Files
pricing/tests/test_black_scholes.cpp
David Doebel 087a2f0d74 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
2026-04-02 16:30:33 +02:00

80 lines
2.7 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by David Doebel on 06.03.2026.
//
#include <gtest/gtest.h>
#include "BlackScholesClosedFormEngine.hpp"
#include "BlackScholesProcess.hpp"
#include "MonteCarloEngine.hpp"
#include "Instrument.hpp"
#include "Option.hpp"
#include "Payoff.hpp"
#include "stubs/FlatYieldCurve.hpp"
#include "stubs/FlatVolatilitySurface.hpp"
TEST(BlackScholesProcess, ExpectedValue) {
// Market setup (via test stubs): S0=100, r=1%, sigma=20%
const double K = 100.0;
const double T = 1.0;
const int numPaths = 300000; // enough for stable MC estimate
const MarketData marketData(
100.0,
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
auto rng = std::make_shared<MersenneTwister>();
// Pricing engines
auto mcCall = std::make_unique<MonteCarloEngine>(numPaths, std::move(processCall), rng);
auto mcPut = std::make_unique<MonteCarloEngine>(numPaths, std::move(processPut), rng);
// Instruments (European vanilla) with call and put payoffs
Instrument callInstr(T, std::make_unique<CallPayoff>(K), std::move(mcCall));
Instrument putInstr(T, std::make_unique<PutPayoff>(K), std::move(mcPut));
const double callPrice = callInstr.price();
const double putPrice = putInstr.price();
// Ground truth BlackScholes prices provided
const double callGT = 8.4333186901;
const double putGT = 7.4383020650;
// Monte Carlo tolerance
const double tol = 0.10; // 10 cents tolerance
ASSERT_NEAR(callPrice, callGT, tol);
ASSERT_NEAR(putPrice, putGT, tol);
}
TEST(BlackScholesClosedForm, MatchesReference) {
const double K = 100.0;
const double T = 1.0;
const MarketData marketData(
100.0,
std::make_shared<FlatYieldCurve>(0.01),
std::make_shared<FlatVolatilitySurface>(0.2));
auto processCall = std::make_unique<BlackScholesProcess>(marketData);
auto processPut = std::make_unique<BlackScholesProcess>(marketData);
auto analyticCall = std::make_unique<BlackScholesClosedFormEngine>(std::move(processCall));
auto analyticPut = std::make_unique<BlackScholesClosedFormEngine>(std::move(processPut));
Instrument callInstr(T, std::make_unique<CallPayoff>(K), std::move(analyticCall));
Instrument putInstr(T, std::make_unique<PutPayoff>(K), std::move(analyticPut));
const double callGT = 8.4333186901;
const double putGT = 7.4383020650;
ASSERT_NEAR(callInstr.price(), callGT, 1e-9);
ASSERT_NEAR(putInstr.price(), putGT, 1e-9);
}