Ingestion for UBS data draft
Some checks failed
C++ CI / build (push) Has been cancelled

This commit is contained in:
David Doebel
2026-03-25 21:54:05 +01:00
parent ff30a3e1ce
commit 61df0b425d
9 changed files with 203 additions and 2 deletions

View File

@@ -5,6 +5,9 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "-O3 -march=native") set(CMAKE_CXX_FLAGS "-O3 -march=native")
find_package(Eigen3 REQUIRED) find_package(Eigen3 REQUIRED)
#find_package(PostgreSQL REQUIRED)
#find_package(PkgConfig REQUIRED)
#pkg_check_modules(PQXX REQUIRED IMPORTED_TARGET libpqxx)
add_subdirectory(src) add_subdirectory(src)

View File

@@ -25,9 +25,17 @@ add_library(qengine
Statistics.hpp Statistics.hpp
BlackScholesProcess.cpp BlackScholesProcess.cpp
BlackScholesProcess.hpp BlackScholesProcess.hpp
DBIngest.cpp
DBIngest.hpp
GaussSolver.cpp
GaussSolver.hpp
) )
target_include_directories(qengine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(qengine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(qengine PRIVATE
/opt/homebrew/include
)
target_link_libraries(qengine Eigen3::Eigen) target_link_libraries(qengine Eigen3::Eigen)
target_link_libraries(qengine pqxx pq)

55
src/DBIngest.cpp Normal file
View File

@@ -0,0 +1,55 @@
//
// Created by David Doebel on 13.03.2026.
//
#include "DBIngest.hpp"
#include <iostream>
// Queries
// Query for selecting the volatility surface parameters
std::string vol_surface_query = ""
//
bool DBIngest::connect() {
connection_ = pqxx::connection("dbname=options_db user=quant_user port = 5432 host = localhost password = strong_password" );
if(connection_.is_open()) {
std::cout << "Connected\n";
return true;
}
std::cout << "Not connected\n";
return false;
}
bool DBIngest::disconnect() {
connection_.close();
}
bool DBIngest::update(VolatilitySurface &surface) {
std::string vol_surface_query = "SELECT c.strike, c.expiration_date, q.mid, u.price "
"FROM option_quotes q"
"JOIN option_contracts c "
"ON q.contract_id = c.id "
"JOIN underlying_prices u"
"ON u.underlying_id = c.underlying_id"
"WHERE q.timestamp = ("
"SELECT MAX(timestamp) FROM option_quotes"
")";
pqxx::work work(connection_);
pqxx::result result = work.exec(vol_surface_query);
for (auto row : result) {
std::cout << row[0] << " " << row[1] << " " << row[2] << " " << row[3] << std::endl;
}
}
bool DBIngest::update(YieldCurve &yield_curve) {
}

24
src/DBIngest.hpp Normal file
View File

@@ -0,0 +1,24 @@
//
// Created by David Doebel on 13.03.2026.
//
#ifndef QUANTENGINE_DBINGEST_HPP
#define QUANTENGINE_DBINGEST_HPP
#include <pqxx/pqxx>
#include "VolatilitySurface.hpp"
#include "YieldCurve.hpp"
class DBIngest {
bool connect();
bool disconnect();
bool update(VolatilitySurface& surface);
bool update(YieldCurve& yield_curve);
private:
pqxx::connection connection_;
};
#endif //QUANTENGINE_DBINGEST_HPP

7
src/NewtonSolver.cpp Normal file
View File

@@ -0,0 +1,7 @@
//
// Created by David Doebel on 13.03.2026.
//
#include "NewtonSolver.hpp"

26
src/NewtonSolver.hpp Normal file
View File

@@ -0,0 +1,26 @@
//
// Created by David Doebel on 13.03.2026.
//
#ifndef QUANTENGINE_GAUSSSOLVER_HPP
#define QUANTENGINE_GAUSSSOLVER_HPP
#include <functional>
class NewtonSolver {
template<typename F, typename DFinv, typename T>
bool solve(F&& func, DFinv&& dfinv,T x0 , double rtol, double atol) {
T x = x0;
int i = 0;
T increment;
do {
increment = dfinv(x) * func(x);
x -= increment;
++i;
} while (i < 1000 && std::abs(increment)/ std::abs(x) > rtol && std::abs(increment) > atol);
}
};
#endif //QUANTENGINE_GAUSSSOLVER_HPP

View File

@@ -0,0 +1,4 @@
from fredapi import Fred
fred = Fred(api_key='471be0178bfc20ce10bb93e3fcceee3b')
data = fred.get_series_latest_release('DTB3')
print(data.tail())

View File

@@ -0,0 +1,74 @@
from datetime import datetime, timedelta
import pandas as pd
import yfinance as yf
from sqlalchemy import create_engine
# --- CONFIG ---
TICKERS = ["UBS", "^GSPC"]
DAYS_BACK = 21 # ~3 weeks
TABLE_NAME = "prices"
DB_URI = "postgresql://quant_user:strong_password@localhost:5432/options_db"
def fetch_data(tickers, start_date, end_date):
data = yf.download(
tickers,
start=start_date,
end=end_date,
group_by="ticker",
auto_adjust=True,
progress=False
)
return data
def transform_data(raw_data):
frames = []
for ticker in raw_data.columns.levels[0]:
df = raw_data[ticker].copy()
df["ticker"] = ticker
df = df.reset_index()
# Keep only what we need
df = df[["Date", "ticker", "Close", "Volume"]]
df.rename(columns={
"Date": "date",
"Close": "close",
"Volume": "volume"
}, inplace=True)
# Compute daily returns
df["return"] = df["close"].pct_change()
frames.append(df)
return pd.concat(frames, ignore_index=True)
def load_to_postgres(df, engine):
df.to_sql(
TABLE_NAME,
engine,
if_exists="append",
index=False
)
def main():
end_date = datetime.utcnow()
start_date = end_date - timedelta(days=DAYS_BACK)
raw = fetch_data(TICKERS, start_date, end_date)
df = transform_data(raw)
engine = create_engine(DB_URI)
load_to_postgres(df, engine)
print("Ingestion complete.")
if __name__ == "__main__":
main()