Adapt Yield Curve and Volatility Surface and Market Data, to be better compatible with unit test.
Some checks failed
C++ CI / build (push) Has been cancelled
Some checks failed
C++ CI / build (push) Has been cancelled
This commit is contained in:
@@ -24,9 +24,8 @@ FetchContent_MakeAvailable(googletest)
|
|||||||
add_executable(qengine_tests
|
add_executable(qengine_tests
|
||||||
tests/test_black_scholes.cpp
|
tests/test_black_scholes.cpp
|
||||||
tests/stubs/FlatYieldCurve.cpp
|
tests/stubs/FlatYieldCurve.cpp
|
||||||
tests/stubs/FlatVolatilitySurface.cpp
|
tests/stubs/FlatVolatilitySurface.cpp)
|
||||||
tests/stubs/FakeMarketData.cpp)
|
|
||||||
|
|
||||||
target_link_libraries(qengine_tests qengine GTest::gtest_main)
|
target_link_libraries(qengine_tests qengine GTest::gtest_main)
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
gtest_discover_tests(qengine_tests)
|
gtest_discover_tests(qengine_tests)
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
class BlackScholesProcess : public StochasticProcess{
|
class BlackScholesProcess : public StochasticProcess{
|
||||||
public:
|
public:
|
||||||
BlackScholesProcess() = default;
|
explicit BlackScholesProcess(MarketData data) : StochasticProcess(std::move(data)){}
|
||||||
BlackScholesProcess(std::unique_ptr<MarketData> data) : StochasticProcess(std::move(data)){}
|
|
||||||
|
|
||||||
double drift(double t, double s) override;
|
double drift(double t, double s) override;
|
||||||
|
|
||||||
@@ -21,4 +20,4 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //QUANTENGINE_BLACKSCHOLESPROCESS_HPP
|
#endif //QUANTENGINE_BLACKSCHOLESPROCESS_HPP
|
||||||
|
|||||||
@@ -5,5 +5,5 @@
|
|||||||
#include "MarketData.hpp"
|
#include "MarketData.hpp"
|
||||||
|
|
||||||
double MarketData::spot() const { return spot_; }
|
double MarketData::spot() const { return spot_; }
|
||||||
YieldCurve& MarketData::yield_curve() { return *yield_curve_; }
|
const YieldCurve& MarketData::yield_curve() const { return *yield_curve_; }
|
||||||
VolatilitySurface& MarketData::volatility_surface() { return *volatility_surface_; }
|
const VolatilitySurface& MarketData::volatility_surface() const { return *volatility_surface_; }
|
||||||
|
|||||||
@@ -10,24 +10,24 @@
|
|||||||
|
|
||||||
class MarketData {
|
class MarketData {
|
||||||
public:
|
public:
|
||||||
MarketData() = default;
|
MarketData() = delete;
|
||||||
|
|
||||||
MarketData(double spot, std::unique_ptr<YieldCurve> yield_curve,
|
MarketData(double spot, std::shared_ptr<const YieldCurve> yield_curve,
|
||||||
std::unique_ptr<VolatilitySurface> volatility_surface)
|
std::shared_ptr<const VolatilitySurface> volatility_surface)
|
||||||
: spot_(spot),
|
: spot_(spot),
|
||||||
yield_curve_(std::move(yield_curve)),
|
yield_curve_(std::move(yield_curve)),
|
||||||
volatility_surface_(std::move(volatility_surface)) {
|
volatility_surface_(std::move(volatility_surface)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
double spot() const;
|
double spot() const;
|
||||||
YieldCurve& yield_curve();
|
const YieldCurve& yield_curve() const;
|
||||||
VolatilitySurface& volatility_surface();
|
const VolatilitySurface& volatility_surface() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double spot_;
|
double spot_;
|
||||||
std::unique_ptr<YieldCurve> yield_curve_;
|
std::shared_ptr<const YieldCurve> yield_curve_;
|
||||||
std::unique_ptr<VolatilitySurface> volatility_surface_;
|
std::shared_ptr<const VolatilitySurface> volatility_surface_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //QUANTENGINE_MARKETDATA_HPP
|
#endif //QUANTENGINE_MARKETDATA_HPP
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
class Statistics {
|
class Statistics {
|
||||||
public:
|
public:
|
||||||
Statistics() : moments_({0., 0., 0.}), max_(0.), min_(0.) {}
|
Statistics() : moments_({0., 0., 0.}), n(0), max_(0.), min_(0.) {}
|
||||||
void dump(double value);
|
void dump(double value);
|
||||||
void clear();
|
void clear();
|
||||||
double mean();
|
double mean();
|
||||||
@@ -27,4 +27,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //QUANTENGINE_STATISTICS_HPP
|
#endif //QUANTENGINE_STATISTICS_HPP
|
||||||
|
|||||||
@@ -9,20 +9,20 @@
|
|||||||
|
|
||||||
class StochasticProcess {
|
class StochasticProcess {
|
||||||
public:
|
public:
|
||||||
StochasticProcess() = default;
|
StochasticProcess() = delete;
|
||||||
StochasticProcess(std::unique_ptr<MarketData> data) : data_(std::move(data)){}
|
explicit StochasticProcess(MarketData data) : data_(std::move(data)){}
|
||||||
|
|
||||||
virtual ~StochasticProcess() = default;
|
virtual ~StochasticProcess() = default;
|
||||||
virtual double drift(double t, double s) = 0;
|
virtual double drift(double t, double s) = 0;
|
||||||
virtual double diffusion(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;
|
virtual double step(double t, double s, double dt, double dW) = 0;
|
||||||
MarketData& data() const {return *data_;}
|
const MarketData& data() const {return data_;}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<MarketData> data_;
|
MarketData data_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //QUANTENGINE_STOCHASTICPROCESS_HPP
|
#endif //QUANTENGINE_STOCHASTICPROCESS_HPP
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
class VolatilitySurface {
|
class VolatilitySurface {
|
||||||
public:
|
public:
|
||||||
virtual ~VolatilitySurface() = default;
|
virtual ~VolatilitySurface() = default;
|
||||||
virtual double sigma(double K, double T) = 0;
|
virtual double sigma(double K, double T) const = 0;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //QUANTENGINE_VOLATILITYSURFACE_HPP
|
#endif //QUANTENGINE_VOLATILITYSURFACE_HPP
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
virtual ~YieldCurve() = default;
|
virtual ~YieldCurve() = default;
|
||||||
virtual double discount(double t) = 0;
|
virtual double discount(double t) const = 0;
|
||||||
virtual double zeroRate(double t) = 0;
|
virtual double zeroRate(double t) const = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //QUANTENGINE_YIELDCURVE_HPP
|
#endif //QUANTENGINE_YIELDCURVE_HPP
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// Minimal TU to satisfy CMake for test stubs
|
|
||||||
#include "FakeMarketData.hpp"
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by David Doebel on 07.03.2026.
|
|
||||||
//
|
|
||||||
#ifndef QUANTENGINE_FAKEMARKETDATA_HPP
|
|
||||||
#define QUANTENGINE_FAKEMARKETDATA_HPP
|
|
||||||
#include "MarketData.hpp"
|
|
||||||
#include "FlatYieldCurve.hpp"
|
|
||||||
#include "FlatVolatilitySurface.hpp"
|
|
||||||
|
|
||||||
class FakeMarketData : public MarketData {
|
|
||||||
public:
|
|
||||||
FakeMarketData() = default;
|
|
||||||
|
|
||||||
FakeMarketData(const FakeMarketData &other)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FakeMarketData(FakeMarketData &&other) noexcept
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FakeMarketData & operator=(const FakeMarketData &other) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
FakeMarketData & operator=(FakeMarketData &&other) noexcept {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
double spot() const {return 100.0;}
|
|
||||||
YieldCurve& yield_curve(){return *yieldCurve_; };
|
|
||||||
VolatilitySurface& volatility_surface(){return *volatilitySurface_; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<FlatYieldCurve> yieldCurve_ = std::make_unique<FlatYieldCurve>();
|
|
||||||
std::unique_ptr<FlatVolatilitySurface> volatilitySurface_ = std::make_unique<FlatVolatilitySurface>();
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
@@ -6,6 +6,12 @@
|
|||||||
#include "VolatilitySurface.hpp"
|
#include "VolatilitySurface.hpp"
|
||||||
|
|
||||||
class FlatVolatilitySurface : public VolatilitySurface {
|
class FlatVolatilitySurface : public VolatilitySurface {
|
||||||
double sigma(double K, double T) {return 0.2;}
|
public:
|
||||||
|
explicit FlatVolatilitySurface(double sigma = 0.2) : sigma_(sigma) {}
|
||||||
|
|
||||||
|
double sigma(double K, double T) const override {return sigma_;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double sigma_;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,10 +7,12 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
class FlatYieldCurve : public YieldCurve{
|
class FlatYieldCurve : public YieldCurve{
|
||||||
|
public:
|
||||||
|
explicit FlatYieldCurve(double rate = 0.01) : rate_(rate) {}
|
||||||
|
|
||||||
double discount(double t) override {return std::exp(-rate_ * t); };
|
double discount(double t) const override {return std::exp(-rate_ * t); };
|
||||||
double zeroRate(double t) override {return rate_; }
|
double zeroRate(double t) const override {return rate_; }
|
||||||
private:
|
private:
|
||||||
double rate_ = 0.01;
|
double rate_ = 0.01;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include "stubs/FlatYieldCurve.hpp"
|
#include "stubs/FlatYieldCurve.hpp"
|
||||||
#include "stubs/FlatVolatilitySurface.hpp"
|
#include "stubs/FlatVolatilitySurface.hpp"
|
||||||
#include "stubs/FakeMarketData.hpp"
|
|
||||||
|
|
||||||
TEST(BlackScholesProcess, ExpectedValue) {
|
TEST(BlackScholesProcess, ExpectedValue) {
|
||||||
// Market setup (via test stubs): S0=100, r=1%, sigma=20%
|
// Market setup (via test stubs): S0=100, r=1%, sigma=20%
|
||||||
@@ -19,9 +18,14 @@ TEST(BlackScholesProcess, ExpectedValue) {
|
|||||||
const double T = 1.0;
|
const double T = 1.0;
|
||||||
const int numPaths = 300000; // enough for stable MC estimate
|
const int numPaths = 300000; // enough for stable MC estimate
|
||||||
|
|
||||||
// Build Black-Scholes process with fake flat market data
|
const MarketData marketData(
|
||||||
auto processCall = std::make_unique<BlackScholesProcess>(std::make_unique<FakeMarketData>());
|
100.0,
|
||||||
auto processPut = std::make_unique<BlackScholesProcess>(std::make_unique<FakeMarketData>());
|
std::make_shared<FlatYieldCurve>(0.01),
|
||||||
|
std::make_shared<FlatVolatilitySurface>(0.2));
|
||||||
|
|
||||||
|
// Build Black-Scholes process from an immutable market snapshot
|
||||||
|
auto processCall = std::make_unique<BlackScholesProcess>(marketData);
|
||||||
|
auto processPut = std::make_unique<BlackScholesProcess>(marketData);
|
||||||
|
|
||||||
// RNG shared between engines is fine
|
// RNG shared between engines is fine
|
||||||
auto rng = std::make_shared<MersenneTwister>();
|
auto rng = std::make_shared<MersenneTwister>();
|
||||||
@@ -38,12 +42,12 @@ TEST(BlackScholesProcess, ExpectedValue) {
|
|||||||
const double putPrice = putInstr.price();
|
const double putPrice = putInstr.price();
|
||||||
|
|
||||||
// Ground truth Black–Scholes prices provided
|
// Ground truth Black–Scholes prices provided
|
||||||
const double callGT = 10.450583572;
|
const double callGT = 8.4333186901;
|
||||||
const double putGT = 5.573526022;
|
const double putGT = 7.4383020650;
|
||||||
|
|
||||||
// Monte Carlo tolerance
|
// Monte Carlo tolerance
|
||||||
const double tol = 0.10; // 10 cents tolerance
|
const double tol = 0.10; // 10 cents tolerance
|
||||||
|
|
||||||
ASSERT_NEAR(callPrice, callGT, tol);
|
ASSERT_NEAR(callPrice, callGT, tol);
|
||||||
ASSERT_NEAR(putPrice, putGT, tol);
|
ASSERT_NEAR(putPrice, putGT, tol);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user