diff --git a/README.md b/README.md index 1265442..eae20ae 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,30 @@ -# Option Pricer -## Overview +# Overview -The Option Pricer project is a C++ library designed to model and price various financial options. It leverages modern C++ practices such as smart pointers, factory methods, and the observer pattern to manage market data and calculate option prices efficiently. The library supports a wide range of option types including vanilla, exotic, and path-dependent options. +The Option Pricer project is a C++ library designed to model and price various financial options. +It complies with SOLID principles, leverages modern C++ features up to C++20 and is built on various design patterns. +The library supports a wide range of option types including vanilla, exotic, and path-dependent options. -## Features +# Features - **Abstract Base Class**: `Option` serves as the base class for all options, ensuring a unified interface for option pricing. - **Vanilla Options**: Supports standard options such as European and American options. -- **Exotic Options**: Supports more complex options such as Digital and Double Digital options. -- **Path-Dependent Options**: Includes options that depend on the path of the underlying asset price, such as Asian options. +- **Exotic Options**: Supports more complex options such as Digital, Double Digital and American Options. +- **Path-Dependent Options**: Includes options that depend on the path of the underlying asset price, such as Asian, Barrier, and Lookback options. - **Market Data Integration**: Utilizes a singleton `MarketData` class to manage and update market data for all options. -- **Payoff Functors**: Implements functor classes to represent the payoff functions of different options. +- **Payoff Functors**: Implements functor classes to represent the payoff functions of different options, including single-strike and double-strike payoffs. - **Factory Methods**: Uses factory methods to ensure that options are always created and managed by `std::shared_ptr`. +- **Monte Carlo Solvers**: Provides a Monte Carlo solver framework supporting both path-independent and path-dependent options, with capabilities for regression-based methods like LSM (Least Squares Monte Carlo). +- **Quasi-Random Number Generators**: Includes support for advanced random number generation techniques such as Sobol and Faure sequences. -## Project Structure +# Installation -```plaintext -├── include -│ ├── option.h -│ ├── vanilla_option.h -│ ├── exotic_option.h -│ ├── market_data.h -│ ├── payoff.h -│ ├── observer.h -├── src -│ ├── option.txt -│ ├── vanilla_option.cpp -│ ├── exotic_option.cpp -│ ├── market_data.cpp -│ ├── payoff.txt -│ ├── observer.cpp -├── tests -│ ├── option_tests.cpp -│ ├── test_market_data.cpp -│ ├── test_payoff.cpp -├── CMakeLists.txt -└── README.md -``` +## Prerequisites +- C++20 or higher +- CMake 3.10 or higher +- A compatible C++ compiler (e.g., g++, clang++) -## Installation - -### Prerequisites - - C++17 or higher - - CMake 3.10 or higher - - A compatible C++ compiler (e.g., g++, clang++) - -### Build Instructions +## Build Instructions 1. Clone the repository: ``` git clone https://github.com/anthonymakarewicz/option_pricer.git @@ -70,74 +48,318 @@ make make test ``` -## Usage -### Creating Options -Options are created using factory methods to ensure they are managed by `std::shared_ptr`.
-Here is an example of creating a VanillaOption (EuropeanOption) using the factory method: +# Project Structure -#### Example: Creating a VanillaOption -The following example demonstrates how to create a VanillaOption (EuropeanOption) using the factory method. -The example also shows how to handle potential exceptions that may be thrown during the creation and use of the Option. +```plaintext +├── src +│ ├── market_data +│ │ ├── interface_market_data.cpp +│ │ ├── market_data.cpp +│ │ ├── market_data_observer.cpp +│ │ └── stock_data.cpp +│ ├── model +│ │ ├── base_model.cpp +│ │ └── gbm_model.cpp +│ ├── option +│ │ ├── base_option.cpp +│ │ ├── interface_option.cpp +│ │ ├── parameter_object.cpp +│ │ ├── path_dependent +│ │ │ ├── american_option.cpp +│ │ │ ├── asian_option.cpp +│ │ │ ├── barrier_option.cpp +│ │ │ ├── base_path_dependent_option.cpp +│ │ │ ├── factory_american_option.cpp +│ │ │ ├── factory_asian_option.cpp +│ │ │ ├── factory_barrier_option.cpp +│ │ │ ├── factory_lookback_option.cpp +│ │ │ └── lookback_option.cpp +│ │ └── single_path +│ │ ├── base_single_path_option.cpp +│ │ ├── digital_option.cpp +│ │ ├── double_digital_option.cpp +│ │ ├── european_option.cpp +│ │ ├── factory_digital_option.cpp +│ │ ├── factory_double_digital_option.cpp +│ │ └── factory_european_option.cpp +│ ├── payoff +│ │ ├── base_payoff.cpp +│ │ ├── double_strikes +│ │ │ ├── base_payoff_double_strikes.cpp +│ │ │ └── payoff_double_digital.cpp +│ │ ├── payoff_floating_strike_lookback.cpp +│ │ └── single_strike +│ │ ├── base_payoff_single_strike.cpp +│ │ ├── payoff_digital.cpp +│ │ └── payoff_vanilla.cpp +│ ├── random +│ │ ├── distribution +│ │ │ ├── base_distribution.cpp +│ │ │ └── standard_normal_distribution.cpp +│ │ └── number_generator +│ │ ├── base_generator.cpp +│ │ ├── base_quasi_random_generator.cpp +│ │ ├── faure_quasi_random_generator.cpp +│ │ ├── random_generator.cpp +│ │ └── sobol_quasi_random_generator.cpp +│ └── solver +│ ├── base_solver.cpp +│ └── monte_carlo +│ ├── base_mc.cpp +│ ├── base_mc_path_dependent.cpp +│ ├── basis_function +│ │ ├── base_basis_function_strategy.cpp +│ │ ├── chebyshev.cpp +│ │ ├── laguerre.cpp +│ │ ├── legendre.cpp +│ │ └── monomial.cpp +│ ├── builder +│ │ ├── base_mc_builder.cpp +│ │ ├── base_mc_builder_path_dependent.cpp +│ │ ├── mc_builder_american.cpp +│ │ ├── mc_builder_asian.cpp +│ │ ├── mc_builder_barrier.cpp +│ │ ├── mc_builder_single_path.cpp +│ │ └── mc_lookback_builder.cpp +│ ├── mc_american.cpp +│ ├── mc_asian.cpp +│ ├── mc_barrier.cpp +│ ├── mc_lookback.cpp +│ ├── mc_single_path.cpp +│ ├── mc_solver.cpp +│ └── regression +│ ├── base_regression_strategy.cpp +│ ├── lasso.cpp +│ ├── least_squares.cpp +│ └── ridge.cpp ``` -#include -#include "vanilla_option.h" -#include "payoff.h" -int main() { - VanillaOptionFactory factory; +# Usage + +## Creating and Pricing Options + +The library allows users to create and price various types of options using both pseudo-random and quasi-random number generators. +Below is an example that demonstrates how to create and price an American put option using Monte Carlo simulation. - try { - // Create a valid Payoff object - auto payoff = std::make_unique(100.0); - // Use the factory to create a VanillaOption - auto option = factory.createOption("AAPL", std::move(payoff), 1.0); - std::cout << *option << std::endl; // Output the option details +### 1. Initializing Market Data and Creating the Option - // Attempt to create an invalid Payoff object - auto invalidPayoff = std::make_unique(90.0, 110.0); - // This will throw an exception since VanillaOption only supports PayoffSingleStrike - auto invalidOption = factory.createOption("AAPL", std::move(invalidPayoff), 1.0); - } catch (const std::invalid_argument& e) { - std::cerr << "Invalid argument: " << e.what() << std::endl; // Handle invalid argument exceptions - } catch (const std::runtime_error& e) { - std::cerr << "Runtime error: " << e.what() << std::endl; // Handle runtime errors - } catch (const std::exception& e) { - std::cerr << "Exception: " << e.what() << std::endl; // Handle all other exceptions - } +```cpp +using namespace OptionPricer; - return 0; -} +std::string ticker = "AAPL"; // Ticker symbol +double T = 1.0; // Time to maturity +double K = 100.0; // Strike price +double S = 100.0; // Current stock price +double sigma = 0.15; // Volatility +double r = 0.03; // Risk-free interest rate +int dim = 52; // Number of time steps +double lambda = 1; // Regularization parameter for Lasso regression + +// Initialize market data +auto marketData = MarketData::getInstance(); +marketData->addStock(ticker, S, sigma); +marketData->setR(r); + +// Set up option parameters +ParameterObject params; +params.setParameter("ticker", ticker); +params.setParameter("T", T); +params.setParameter("K", K); + +// Create American put option using factory +AmericanOptionFactory factory; +std::shared_ptr americanPut = factory.createPutOption(params); ``` -### Market Data Updates -Market data updates are managed by the `MarketData` class. -When market data changes, all registered options are notified and can update their prices accordingly. +### Explanation: +- **Market Data initialization**: The `MarketData` singleton is used to store and manage the current stock price, volatility, and risk-free interest rate for the asset. This data is essential for pricing the option. +- **Option Creation**: The `AmericanOptionFactory` is used to create an American put option based on the specified parameters (ticker, maturity, and strike price). This factory pattern ensures the option is created and managed correctly. + +### 2. Setting Up the Monte Carlo Components +```cpp +int dim = 150; // Number of time steps +int numberBases = 5 // Number of basis functions +// Create Quasi Random Number Generator with Sobol sequences +std::shared_ptr normal = std::make_shared(); +std::shared_ptr generator = std::make_shared(normal, dim); + +// Create the Geometric Brownian Motion model +std::shared_ptr brownianMotion = std::make_shared(ticker, marketData); + +// Create basis function and regularized regression for LSM (Least Squares Monte Carlo) +std::shared_ptr laguerre = std::make_shared(numberBases); + +// Create Monte Carlo pricer through the American builder +AmericanMCBuilder americanBuilder; +std::unique_ptr americanPricer = builder.setOption(americanPut) + .setBasisFunctionStrategy(laguerre) + .setSteps(dim) + .setNumberGenerator(generator).build(); ``` -#include "market_data.h" -#include "vanilla_option.h" -// Example usage -void updateMarketData() { -auto marketData = MarketData::getInstance(); -marketData->updateStockPrice("AAPL", 150.0); -marketData->updateStockSigma("AAPL", 0.25); -} +### Explanation: + +- **American Monte Carlo Builder**: This class leverages the builder design pattern to simplify the creational process for the MCPricer. It provides default implementations for most of the components except option which must be set explicitly. + + +- **Random Number Generator**: A SobolGenerator is used to generate quasi-random numbers, which are more effective for Monte Carlo simulations in certain scenarios due to their low discrepancy. +The default for AmericanMCBuilder is set to RandomNumberGenerator and uses pseudo-random numbers generated from Mersenne Twister. + + +- **Geometric Brownian Motion Model**: The GeometricBrownianMotionModel represents the stochastic process of the underlying asset. +It is the default model for AmericanMCBuilder. + + +- **Basis Function**: The LaguerreBasisFunction is used for the Least Squares Monte Carlo (LSM) method. This method is crucial for pricing American options, which may be exercised before maturity. + +### 3. Running the Monte Carlo Simulation + +```cpp +unsigned long N = 1000000 // Number of simulations +double lambda = 1; // Regularization parameter regularized regression + +// Set up the Monte Carlo solver +MCSolver mcSolver; +mcSolver.setN(N); // Set the number of simulations +mcSolver.setPricer(std::move(americanPricer)); + +// Config 1: Use config from 2. +std::cout << "Config 1, P = " << mcSolver.solve() << "\n"; + +// Config 2: Use Chebyshev polynomials +numberBases = 10; +auto chebychev = std::shared_ptr(numberBases) +auto americanPricer2 = builder.setBasisFunctionStrategy(laguerre).build(); +mcSolver.setPricer(std::move(americanPricer2)); + +std::cout << "Config 2, P = " << mcSolver.solve() << "\n"; + +// Config 3: Use Lasso regression +auto lasso = std::make_shared(lambda); +auto americanPricer3 = builder.setRegressionStrategy(lasso).build(); +mcSolver.setPricer(std::move(americanPricer3)); + +std::cout << "Config 3, P = " << mcSolver.solve() << "\n"; ``` -## Testing -Unit tests are located in the `tests` directory. -To run the tests, use the following command in the build directory: + +# Testing + +## Tests Directory + +This directory contains all the test files and configurations for the Option Pricer project. The tests are organized to ensure modularity, clarity, and ease of maintenance. + +### Directory Structure +```plaintext +tests +. +├── CMakeLists.txt +├── README.md +├── integration +│   ├── CMakeLists.txt +│   └── test_integration_option.cpp +├── test_main.cpp +└── unit + ├── CMakeLists.txt + ├── market_data + │   ├── test_market_data.cpp + │   └── test_stock_data.cpp + ├── option + │   ├── test_base_option.cpp + │   ├── test_european_option.cpp + │   └── test_factory_european_option.cpp + ├── payoff + │   ├── double_strikes + │   │   └── test_payoff_double_digital.cpp + │   └── single_strike + │   ├── test_payoff_digital.cpp + │   ├── test_payoff_vanilla.cpp + │   └── test_support + │   ├── common_payoff_single_strike_tests.h + │   └── fixture_payoff_single_strike.h + └── test_support + └── mock_classes.h ``` -make test + +## Dependencies + +- [Google Test (GTest)](https://github.com/google/googletest): A popular C++ testing framework. +- [Google Mock](https://github.com/google/googletest/tree/main/googlemock): A library for writing and using C++ mock classes. + +## Building and Running Tests + +### Prerequisites +Make sure you have CMake and GTest installed. You can install GTest using a package manager or +through [vcpkg](https://github.com/microsoft/vcpkg). + +### Build Instructions + +1. **Navigate to the project root directory**: + ```sh + cd path/to/your/project + ``` +2. **Create a build directory**: + ```sh + mkdir build && cd build + ``` +3. **Configure the project with CMake**: + ```sh + cmake .. + ``` +4. **Build the project**: + ```sh + cmake --build . # more generic than make and works with any generator + ``` + +### Running tests with Ctest +For automated testing or integration, +you can run the all the tests executable using CTest: +```sh +ctest --output-on-failure +``` + +Alternatively, you can run specific tests: +- **Run only Unit Tests**: +```sh +ctest -R UnitTests --output-on-failure +``` +- **Run only Integration Tests**: +```sh +ctest -R IntegrationTests --output-on-failure ``` -Tests are written using Google Test as testing framework. -Ensure the testing framework is properly configured in the `CMakeLists.txt. +### Running the Custom Tests Target +You can also run all the tests at once by using the custom target **Tests**. +For that purpose, you can build and run the Tests target as follows: +1. **Build the custom Tests target**: +```sh +cmake --build . --target Tests +``` +2. **Run the custom Tests target**: +```sh +ctest -R Tests --output-on-failure +``` + +### Running tests with GTest +For detailed output, running the test using GTest can be a good approach for local +development and debugging. +1. **Go to the build directory**: +```sh +cd build +``` +2. **Run the unit tests executable**: +```sh +./tests/unit/Unit_tests +``` +3. **Run the integration tests executable**: +```sh +./tests/integration/Integration_tests +``` ## Contributing diff --git a/main.cpp b/main.cpp index 82b0b68..8111d31 100644 --- a/main.cpp +++ b/main.cpp @@ -49,7 +49,7 @@ int main() { params.setParameter("K", K); auto normal = std::make_shared(); - auto generator = std::make_shared(normal); + auto generator = std::make_shared(normal, dim); auto brownianMotion = std::make_shared(ticker, marketData); auto laguerre = std::make_shared(4); auto regression = std::make_shared(); diff --git a/simplified_project_struct.txt b/simplified_project_struct.txt index d4574f7..c8920c4 100644 --- a/simplified_project_struct.txt +++ b/simplified_project_struct.txt @@ -1,91 +1,81 @@ -(base) anthony@MacBook-Air-de-anthony-2 include % tree -. -├── market_data -│   ├── interface_market_data.h -│   ├── market_data.h -│   ├── market_data_observer.h -│   └── stock_data.h -├── option -│   ├── base_option.h -│   ├── factory_option.h -│   ├── interface_option.h -│   ├── parameter_object.h -│   ├── path_dependent -│   │   ├── american_option.h -│   │   ├── asian -│   │   │   ├── arithmetic_asian_option.h -│   │   │   ├── base_asian_option.h -│   │   │   ├── factory_arithmetic_asian_option.h -│   │   │   ├── factory_geometric_asian_option.h -│   │   │   └── geometric_asian_option.h -│   │   ├── barrier -│   │   │   ├── base_barrier_option.h -│   │   │   ├── base_single_barrier_option.h -│   │   │   ├── double_barrier.h -│   │   │   ├── down_single_barrier_option.h -│   │   │   ├── factory_up_single_barrier_option.h -│   │   │   ├── knock_behavior.h -│   │   │   └── up_single_barrier_option.h -│   │   ├── base_path_dependent_option.h -│   │   └── factory_american_option.h -│   └── single_path -│   ├── base_single_path_option.h -│   ├── digital_option.h -│   ├── double_digital_option.h -│   ├── european_option.h -│   ├── factory_digital_option.h -│   ├── factory_double_digital_option.h -│   └── factory_european_option.h -├── payoff -│   ├── base_payoff.h -│   ├── double_strikes -│   │   ├── base_payoff_double_strikes.h -│   │   ├── factory_payoff_double_digital.h -│   │   └── payoff_double_digital.h -│   └── single_strike -│   ├── barrier -│   │   └── knock_behavior.h -│   ├── base_payoff_digital.h -│   ├── base_payoff_single_strike.h -│   ├── base_payoff_vanilla.h -│   ├── factory_payoff_digital.h -│   ├── factory_payoff_vanilla.h -│   ├── payoff_digital_call.h -│   ├── payoff_digital_put.h -│   ├── payoff_vanilla_call.h -│   └── payoff_vanilla_put.h -└── solver - -(base) anthony@MacBook-Air-de-anthony-2 tests % tree -. -├── CMakeLists.txt -├── README.md -├── integration -│   ├── CMakeLists.txt -│   └── test_integration_option.cpp -├── test_main.cpp -└── unit - ├── CMakeLists.txt - ├── market_data - │   ├── test_market_data.cpp - │   └── test_stock_data.cpp - ├── option - │   ├── test_base_option.cpp - │   ├── test_european_option.cpp - │   └── test_factory_european_option.cpp - ├── payoff - │   ├── double_strikes - │   │   └── test_payoff_double_digital.cpp - │   └── single_strike - │   ├── test_payoff_digital.cpp - │   ├── test_payoff_digital_put.cpp - │   ├── test_payoff_vanilla.cpp - │   ├── test_payoff_vanilla_put.cpp - │   └── test_support - │   ├── common_payoff_single_strike_tests.h - │   └── fixture_payoff_single_strike.h - └── test_support - └── mock_classes.h - - - +├── src +│   ├── market_data +│   │   ├── interface_market_data.cpp +│   │   ├── market_data.cpp +│   │   ├── market_data_observer.cpp +│   │   └── stock_data.cpp +│   ├── model +│   │   ├── base_model.cpp +│   │   └── gbm_model.cpp +│   ├── option +│   │   ├── base_option.cpp +│   │   ├── interface_option.cpp +│   │   ├── parameter_object.cpp +│   │   ├── path_dependent +│   │   │   ├── american_option.cpp +│   │   │   ├── asian_option.cpp +│   │   │   ├── barrier_option.cpp +│   │   │   ├── base_path_dependent_option.cpp +│   │   │   ├── factory_american_option.cpp +│   │   │   ├── factory_asian_option.cpp +│   │   │   ├── factory_barrier_option.cpp +│   │   │   ├── factory_lookback_option.cpp +│   │   │   └── lookback_option.cpp +│   │   └── single_path +│   │   ├── base_single_path_option.cpp +│   │   ├── digital_option.cpp +│   │   ├── double_digital_option.cpp +│   │   ├── european_option.cpp +│   │   ├── factory_digital_option.cpp +│   │   ├── factory_double_digital_option.cpp +│   │   └── factory_european_option.cpp +│   ├── payoff +│   │   ├── base_payoff.cpp +│   │   ├── double_strikes +│   │   │   ├── base_payoff_double_strikes.cpp +│   │   │   └── payoff_double_digital.cpp +│   │   ├── payoff_floating_strike_lookback.cpp +│   │   └── single_strike +│   │   ├── base_payoff_single_strike.cpp +│   │   ├── payoff_digital.cpp +│   │   └── payoff_vanilla.cpp +│   ├── random +│   │   ├── distribution +│   │   │   ├── base_distribution.cpp +│   │   │   └── standard_normal_distribution.cpp +│   │   └── number_generator +│   │   ├── base_generator.cpp +│   │   ├── base_quasi_random_generator.cpp +│   │   ├── faure_quasi_random_generator.cpp +│   │   ├── random_generator.cpp +│   │   └── sobol_quasi_random_generator.cpp +│   └── solver +│   ├── base_solver.cpp +│   └── monte_carlo +│   ├── base_mc.cpp +│   ├── base_mc_path_dependent.cpp +│   ├── basis_function +│   │   ├── base_basis_function_strategy.cpp +│   │   ├── chebyshev.cpp +│   │   ├── laguerre.cpp +│   │   ├── legendre.cpp +│   │   └── monomial.cpp +│   ├── builder +│   │   ├── base_mc_builder.cpp +│   │   ├── base_mc_builder_path_dependent.cpp +│   │   ├── mc_builder_american.cpp +│   │   ├── mc_builder_asian.cpp +│   │   ├── mc_builder_barrier.cpp +│   │   ├── mc_builder_single_path.cpp +│   │   └── mc_lookback_builder.cpp +│   ├── mc_american.cpp +│   ├── mc_asian.cpp +│   ├── mc_barrier.cpp +│   ├── mc_lookback.cpp +│   ├── mc_single_path.cpp +│   ├── mc_solver.cpp +│   └── regression +│   ├── base_regression_strategy.cpp +│   ├── lasso.cpp +│   ├── least_squares.cpp +│   └── ridge.cpp \ No newline at end of file