Restructure C++ core into cpp module and package bindings.

Move the pricing engine sources out of src/ into cpp/, add the closed-form engine and pybind wiring, and align tests/build targets with the new project layout.

Made-with: Cursor
This commit is contained in:
David Doebel
2026-04-02 16:30:33 +02:00
parent 61df0b425d
commit 087a2f0d74
53 changed files with 803 additions and 195 deletions

View File

@@ -1,23 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#include "BlackScholesProcess.hpp"
double BlackScholesProcess::drift(double t, double s) {
double r = this->data().yield_curve().zeroRate(t);
return r * s;
}
double BlackScholesProcess::diffusion(double t, double s) {
double sigma = this->data().volatility_surface().sigma(s,t);
return sigma*s;
}
double BlackScholesProcess::step(double t, double s, double dt, double dW) {
double r = this->data().yield_curve().zeroRate(t);
double sigma = this->data().volatility_surface().sigma(s,t);
return s*exp((r-0.5*sigma*sigma)*dt + sigma*sqrt(dt)*dW);
}

View File

@@ -1,23 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#ifndef QUANTENGINE_BLACKSCHOLESPROCESS_HPP
#define QUANTENGINE_BLACKSCHOLESPROCESS_HPP
#include "StochasticProcess.hpp"
class BlackScholesProcess : public StochasticProcess{
public:
explicit BlackScholesProcess(MarketData data) : StochasticProcess(std::move(data)){}
double drift(double t, double s) override;
double diffusion(double t, double s) override;
double step(double t, double s, double dt, double dW) override;
};
#endif //QUANTENGINE_BLACKSCHOLESPROCESS_HPP

View File

@@ -1,41 +0,0 @@
add_library(qengine
Instrument.cpp
Instrument.hpp
Payoff.cpp
Payoff.hpp
Option.cpp
Option.hpp
PricingEngine.cpp
PricingEngine.hpp
MonteCarloEngine.cpp
MonteCarloEngine.hpp
StochasticProcess.cpp
StochasticProcess.hpp
Exercise.cpp
Exercise.hpp
MarketData.cpp
MarketData.hpp
YieldCurve.cpp
YieldCurve.hpp
VolatilitySurface.cpp
VolatilitySurface.hpp
RandomGenerator.cpp
RandomGenerator.hpp
Statistics.cpp
Statistics.hpp
BlackScholesProcess.cpp
BlackScholesProcess.hpp
DBIngest.cpp
DBIngest.hpp
GaussSolver.cpp
GaussSolver.hpp
)
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 pqxx pq)

View File

@@ -1,55 +0,0 @@
//
// 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) {
}

View File

@@ -1,24 +0,0 @@
//
// 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

View File

@@ -1,5 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "Exercise.hpp"

View File

@@ -1,53 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_EXERCISE_HPP
#define QUANTENGINE_EXERCISE_HPP
#include <vector>
class Exercise {
public:
Exercise() = default;
virtual ~Exercise() = default;
enum class Type {
European,
American,
Bermudan
};
virtual Type type() const = 0;
protected:
std::vector<double> exercise_times_;
};
class EuropeanExercise : public Exercise {
EuropeanExercise() : type_(Type::European) {};
EuropeanExercise(double maturity) : type_(Type::European){
exercise_times_.push_back(maturity);
}
~EuropeanExercise() override = default;
[[nodiscard]] Type type() const override {
return type_;
}
private:
Type type_;
};
class AmericanExercise : public Exercise{
AmericanExercise() : type_(Type::American) {};
AmericanExercise(double maturity) : type_(Type::American) {
exercise_times_.push_back(0);
exercise_times_.push_back(maturity);
}
[[nodiscard]] Type type() const override {
return type_;
}
private:
Type type_;
};
#endif //QUANTENGINE_EXERCISE_HPP

View File

@@ -1,17 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "Instrument.hpp"
Instrument::Instrument(double maturity, std::unique_ptr<Payoff> payoff,
std::unique_ptr<PricingEngine> engine) : maturity_(maturity), payoff_(std::move(payoff)), engine_
(std::move(engine)){
}
double Instrument::price() const {
return engine_->calculate(*this);
}

View File

@@ -1,34 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_INSTRUMENT_HPP
#define QUANTENGINE_INSTRUMENT_HPP
#include "Payoff.hpp"
#include "PricingEngine.hpp"
#include <memory>
class PricingEngine;
class Instrument {
public:
Instrument() = default;
Instrument(double maturity, std::unique_ptr<Payoff> payoff, std::unique_ptr<PricingEngine> engine);
double price() const;
[[nodiscard]] double maturity() const {
return maturity_;
}
[[nodiscard]] Payoff& payoff() const {
return *payoff_;
}
protected:
double maturity_;
std::unique_ptr<Payoff> payoff_;
std::unique_ptr<PricingEngine> engine_;
};
#endif //QUANTENGINE_INSTRUMENT_HPP

View File

@@ -1,9 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#include "MarketData.hpp"
double MarketData::spot() const { return spot_; }
const YieldCurve& MarketData::yield_curve() const { return *yield_curve_; }
const VolatilitySurface& MarketData::volatility_surface() const { return *volatility_surface_; }

View File

@@ -1,33 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#ifndef QUANTENGINE_MARKETDATA_HPP
#define QUANTENGINE_MARKETDATA_HPP
#include "YieldCurve.hpp"
#include "VolatilitySurface.hpp"
#include <memory>
class MarketData {
public:
MarketData() = delete;
MarketData(double spot, std::shared_ptr<const YieldCurve> yield_curve,
std::shared_ptr<const VolatilitySurface> volatility_surface)
: spot_(spot),
yield_curve_(std::move(yield_curve)),
volatility_surface_(std::move(volatility_surface)) {
}
double spot() const;
const YieldCurve& yield_curve() const;
const VolatilitySurface& volatility_surface() const;
private:
double spot_;
std::shared_ptr<const YieldCurve> yield_curve_;
std::shared_ptr<const VolatilitySurface> volatility_surface_;
};
#endif //QUANTENGINE_MARKETDATA_HPP

View File

@@ -1,24 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "MonteCarloEngine.hpp"
#include <iostream>
#include "Instrument.hpp"
#include "Statistics.hpp"
double MonteCarloEngine::calculate(const Instrument &instrument) const {
// parameters
double T = instrument.maturity();
double spot = process_->data().spot();
Statistics stats;
auto rNumbers = rng_->nextGaussianVector(numPaths_);
std::vector<double> payoffs(numPaths_);
for (std::size_t i = 0; i < numPaths_; ++i) {
double terminalPrice = process_->step(0.0,spot,T,rNumbers[i]);
double payoff = instrument.payoff()(terminalPrice);
stats.dump(payoff);
}
return stats.mean() * process_->data().yield_curve().discount(T);
}

View File

@@ -1,23 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_MONTECARLOENGINE_HPP
#define QUANTENGINE_MONTECARLOENGINE_HPP
#include "PricingEngine.hpp"
#include "RandomGenerator.hpp"
class MonteCarloEngine : public PricingEngine{
public:
MonteCarloEngine() = default;
MonteCarloEngine(int numPaths, std::unique_ptr<StochasticProcess> process, std::shared_ptr<RandomGenerator> rng):
numPaths_(numPaths), PricingEngine(std::move(process)), rng_(std::move(rng)) {}
double calculate(const Instrument& instrument) const override;
private:
int numPaths_;
std::shared_ptr<RandomGenerator> rng_;
};
#endif //QUANTENGINE_MONTECARLOENGINE_HPP

View File

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

View File

@@ -1,26 +0,0 @@
//
// 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

@@ -1,10 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "Option.hpp"
Option::Option(double maturity, std::unique_ptr<Exercise> exercise, std::unique_ptr<Payoff> payoff,
std::unique_ptr<PricingEngine> engine) : Instrument(maturity, std::move(payoff),
std::move(engine)), exercise_(std::move(exercise)){
}

View File

@@ -1,33 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_OPTION_HPP
#define QUANTENGINE_OPTION_HPP
#include "Instrument.hpp"
#include "Exercise.hpp"
class Option : public Instrument{
public:
Option() = default;
virtual ~Option() = default;
Option(double maturity, std::unique_ptr<Exercise> exercise,
std::unique_ptr<Payoff> payoff, std::unique_ptr<PricingEngine> engine);
[[nodiscard]] Exercise& exercise() const {
return *exercise_;
}
protected:
std::unique_ptr<Exercise> exercise_;
};
class VanillaOption : public Option {
public:
using Option::Option;
};
#endif //QUANTENGINE_OPTION_HPP

View File

@@ -1,18 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "Payoff.hpp"
#include <algorithm>
double CallPayoff::operator()(double S) {
return std::max(0., S - strike_);
}
double PutPayoff::operator()(double S) {
return std::max(0., strike_ - S);
}
double DigitalPayoff::operator()(double S) {
return S > strike_ ? 1. : 0.;
}

View File

@@ -1,51 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_PAYOFF_HPP
#define QUANTENGINE_PAYOFF_HPP
class Payoff {
public:
Payoff() = default;
virtual ~Payoff() = default;
virtual double operator()(double S) = 0;
virtual double strike() = 0;
};
class CallPayoff : public Payoff {
public:
CallPayoff() = default;
CallPayoff(double strike) : strike_(strike) {}
double operator()(double S) override;
double strike() override {return strike_;}
private:
double strike_;
};
class PutPayoff : public Payoff {
public:
PutPayoff() = default;
PutPayoff(double strike) : strike_(strike) {}
double operator()(double S) override;
double strike() override {return strike_;}
private:
double strike_;
};
class DigitalPayoff : public Payoff {
public:
DigitalPayoff() = default;
DigitalPayoff(double strike) : strike_(strike) {}
double operator()(double S) override;
double strike() override {return strike_;}
private:
double strike_;
};
#endif //QUANTENGINE_PAYOFF_HPP

View File

@@ -1,5 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "PricingEngine.hpp"

View File

@@ -1,26 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_PRICINGENGINE_HPP
#define QUANTENGINE_PRICINGENGINE_HPP
#include <memory>
#include "StochasticProcess.hpp"
class Instrument;
class PricingEngine {
public:
PricingEngine() = default;
PricingEngine(std::unique_ptr<StochasticProcess> process) : process_(std::move(process)){}
virtual ~PricingEngine() = default;
virtual double calculate(const Instrument& instrument) const = 0;
protected:
std::unique_ptr<StochasticProcess> process_;
};
#endif //QUANTENGINE_PRICINGENGINE_HPP

View File

@@ -1,18 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#include "RandomGenerator.hpp"
double MersenneTwister::nextGaussian() {
return distr_(generator_);
}
std::vector<double> MersenneTwister::nextGaussianVector(std::size_t n) {
std::vector<double> v(n);
for (auto& e : v) {
e = nextGaussian();
}
return v;
}

View File

@@ -1,28 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#ifndef QUANTENGINE_RANDOMGENERATOR_HPP
#define QUANTENGINE_RANDOMGENERATOR_HPP
#include <random>
class RandomGenerator {
public:
RandomGenerator() = default;
virtual ~RandomGenerator() = default;
virtual double nextGaussian() = 0;
virtual std::vector<double> nextGaussianVector(std::size_t n) = 0;
};
class MersenneTwister : public RandomGenerator {
public:
MersenneTwister() = default;
double nextGaussian() override;
std::vector<double> nextGaussianVector(std::size_t n) override;
private:
std::mt19937 generator_;
std::normal_distribution<> distr_ {0.0, 1.0};
};
#endif //QUANTENGINE_RANDOMGENERATOR_HPP

View File

@@ -1,52 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#include "Statistics.hpp"
void Statistics::dump(double value) {
for (std::size_t i = 0; i < 3; ++i) {
moments_[i] += std::pow(value, i+1);
}
++n;
max_ = std::max(max_, value);
min_ = std::min(min_, value);
}
void Statistics::clear() {
n = 0;
moments_ = {0.,0.,0.};
}
double Statistics::mean() {
return moments_[0]/n;
}
double Statistics::variance() {
return moments_[1]/n - std::pow(mean(), 2);
}
double Statistics::standardDeviation() {
return std::sqrt(variance());
}
double Statistics::skewness() {
return moments_[2]/std::pow(n, 3);
}
double Statistics::max() {
return max_;
}
double Statistics::min() {
return min_;
}
double Statistics::sum() {
return moments_[0];
}
double Statistics::count() {
return n;
}

View File

@@ -1,30 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#ifndef QUANTENGINE_STATISTICS_HPP
#define QUANTENGINE_STATISTICS_HPP
#include <vector>
class Statistics {
public:
Statistics() : moments_({0., 0., 0.}), n(0), max_(0.), min_(0.) {}
void dump(double value);
void clear();
double mean();
double variance();
double standardDeviation();
double skewness();
double max();
double min();
double sum();
double count();
private:
std::vector<double> moments_;
std::size_t n;
double max_, min_;
};
#endif //QUANTENGINE_STATISTICS_HPP

View File

@@ -1,5 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#include "StochasticProcess.hpp"

View File

@@ -1,28 +0,0 @@
//
// Created by David Doebel on 05.03.2026.
//
#ifndef QUANTENGINE_STOCHASTICPROCESS_HPP
#define QUANTENGINE_STOCHASTICPROCESS_HPP
#include "MarketData.hpp"
#include <memory>
class StochasticProcess {
public:
StochasticProcess() = delete;
explicit StochasticProcess(MarketData data) : data_(std::move(data)){}
virtual ~StochasticProcess() = default;
virtual double drift(double t, double s) = 0;
virtual double diffusion(double t, double s) = 0;
virtual double step(double t, double s, double dt, double dW) = 0;
const MarketData& data() const {return data_;}
private:
MarketData data_;
};
#endif //QUANTENGINE_STOCHASTICPROCESS_HPP

View File

@@ -1,5 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#include "VolatilitySurface.hpp"

View File

@@ -1,18 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#ifndef QUANTENGINE_VOLATILITYSURFACE_HPP
#define QUANTENGINE_VOLATILITYSURFACE_HPP
class VolatilitySurface {
public:
virtual ~VolatilitySurface() = default;
virtual double sigma(double K, double T) const = 0;
private:
};
#endif //QUANTENGINE_VOLATILITYSURFACE_HPP

View File

@@ -1,5 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#include "YieldCurve.hpp"

View File

@@ -1,37 +0,0 @@
//
// Created by David Doebel on 06.03.2026.
//
#ifndef QUANTENGINE_YIELDCURVE_HPP
#define QUANTENGINE_YIELDCURVE_HPP
class YieldCurve {
public:
YieldCurve() = default;
YieldCurve(const YieldCurve &other) {
}
YieldCurve(YieldCurve &&other) noexcept {
}
YieldCurve & operator=(const YieldCurve &other) {
if (this == &other)
return *this;
return *this;
}
YieldCurve & operator=(YieldCurve &&other) noexcept {
if (this == &other)
return *this;
return *this;
}
virtual ~YieldCurve() = default;
virtual double discount(double t) const = 0;
virtual double zeroRate(double t) const = 0;
};
#endif //QUANTENGINE_YIELDCURVE_HPP