This commit is contained in:
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_FLAGS "-O3 -march=native")
|
||||
|
||||
find_package(Eigen3 REQUIRED)
|
||||
#find_package(PostgreSQL REQUIRED)
|
||||
#find_package(PkgConfig REQUIRED)
|
||||
#pkg_check_modules(PQXX REQUIRED IMPORTED_TARGET libpqxx)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
@@ -25,9 +25,17 @@ add_library(qengine
|
||||
Statistics.hpp
|
||||
BlackScholesProcess.cpp
|
||||
BlackScholesProcess.hpp
|
||||
DBIngest.cpp
|
||||
DBIngest.hpp
|
||||
GaussSolver.cpp
|
||||
GaussSolver.hpp
|
||||
|
||||
|
||||
)
|
||||
|
||||
target_include_directories(qengine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(qengine Eigen3::Eigen)
|
||||
target_include_directories(qengine PRIVATE
|
||||
/opt/homebrew/include
|
||||
)
|
||||
target_link_libraries(qengine Eigen3::Eigen)
|
||||
target_link_libraries(qengine pqxx pq)
|
||||
55
src/DBIngest.cpp
Normal file
55
src/DBIngest.cpp
Normal 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
24
src/DBIngest.hpp
Normal 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
7
src/NewtonSolver.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
//
|
||||
// Created by David Doebel on 13.03.2026.
|
||||
//
|
||||
|
||||
#include "NewtonSolver.hpp"
|
||||
|
||||
|
||||
26
src/NewtonSolver.hpp
Normal file
26
src/NewtonSolver.hpp
Normal 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
|
||||
4
src/data/ingestion/fred_data_ingestion.py
Normal file
4
src/data/ingestion/fred_data_ingestion.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from fredapi import Fred
|
||||
fred = Fred(api_key='471be0178bfc20ce10bb93e3fcceee3b')
|
||||
data = fred.get_series_latest_release('DTB3')
|
||||
print(data.tail())
|
||||
74
src/data/ingestion/ingest_ubs_comparison.py
Normal file
74
src/data/ingestion/ingest_ubs_comparison.py
Normal 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()
|
||||
Reference in New Issue
Block a user