Based on payoff, option, mcstatistics and random numbers classes, we now have basic components to build option pricing applications. This post applies template pattern to price path-dependent options. The framwork
1. generation of the stock price path;
2. generation of cash-flows given a stock price path;
3. discounting and summing of cash-flows for a given path;
4. averaging of the prices over all the paths.
We need a production class, a random number class, a statistics gathering class, and a independent path generating class, which generates stochastic process according to different assumption, say geometric brownian motion, jump diffusion process and stochastic volatility process. The communication among different classes is given by
1. the path generator asks the product what times it needs spot for, and it passes back an array.
2. the accounting part of the engine asks the product what cash-flow times are possible, and it passes back an array. The engine then computes all the possible discount factors.
3. the accounting part of the engine asks the product the maximum number of cash flows it can generate, and sets up a vector of that size.
4. for each path, the engine gets an array of spot values from the path generator.
5. The array of spot values is passed into the product, whch passes back the number of cash-flows, and puts their values into the vector.
6. The cash-flows are discounted appropriately and summed, and the total value is passed into the statistics gatherer.
7. After all the looping is done, the final results are obtained from the statistics gatherer.
#ifndef PATHDEPENDENT_HPP_INCLUDED
#define PATHDEPENDENT_HPP_INCLUDED
#include<vector>
#include<payoffbridge.hpp>
using namespace std;
class CashFlow {
public:
double amount;
unsigned long index;
CashFlow(double amount_, unsigned long index_):amount(amount_),index(index_){}
};
class PathDependent{
public:
PathDependent(const vector<double>& LookAtTimes_) : LookAtTimes(LookAtTimes_){}
const vector<double> getLookAtTimes() const { return LookAtTimes; }
virtual unsigned long MaxNumberOfCashFlows() const = 0;
virtual vector<double> PossibleCashFlowTimes() const = 0;
virtual unsigned long CashFlows(const vector<double>& spots, vector<CashFlow>& cashflows) const = 0;
virtual PathDependent* clone() const = 0;
virtual ~PathDependent(){}
private:
vector<double> LookAtTimes;
};
class PathDependentAsian : public PathDependent {
/*
Asian option, the payoff is difference between the mean value of price time series and strike.
Since the option is European type, there is only one cash flow needed to be calcuated, at the
delivery time.
*/
public:
PathDependentAsian(const vector<double>& LookAtTimes_, double deliverytime_, const PayOffBridge& payoff_)
:PathDependent(LookAtTimes_), deliverytime(deliverytime_), payoff(payoff_), numberoftimes(LookAtTimes_.size()){}
virtual PathDependent* clone() const {
return new PathDependentAsian(*this);
}
virtual unsigned long CashFlows(const vector<double>& spots, vector<CashFlow>& cashflows) const {
double sum = 0.0;
for (unsigned long i=0; i<spots.size(); i++){
sum += spots[i];
}
double mean = sum/numberoftimes;
cashflows[0].index = deliverytime;
cashflows[0].amount = payoff(mean);
return 1;
}
virtual unsigned long MaxNumberOfCashFlows() const {
return 1;
}
virtual vector<double> PossibleCashFlowTimes() const{
vector<double> temp(1);
temp[0] = deliverytime;
return temp;
}
private:
double deliverytime;
PayOffBridge payoff;
unsigned long numberoftimes;
};
#endif // PATHDEPENDENT_HPP_INCLUDED
PathDependent class has several functions, most important ones are PossibleCashFlowTimes, and CashFlows. PossibleCashFlowTimes() generates time index for calculating discount factors and cash flows. CashFlow() calculates cash flows based
on the internal payoff structure.
Then we define ExoticEngine using template pattern
#ifndef EXOTICENGINE_HPP_INCLUDED
#define EXOTICENGINE_HPP_INCLUDED
#include<./wrapper.hpp>
#include<./pathdependent.hpp>
#include<./statisticsmc.hpp>
#include<vector>
using namespace std;
class ExoticEngine {
public:
ExoticEngine(const Wrapper<PathDependent>& product_, double r_):
product(product_), r(r_), discounts(product_->PossibleCashFlowTimes()){
for(unsigned long i=0; i<discounts.size(); i++){
discounts[i] = exp(-r*discounts[i]);
}
cashflows.resize(product_->MaxNumberOfCashFlows());
}
virtual ~ExoticEngine(){}
virtual void getPath(vector<double>& spots) = 0; // choice of stochastic process and model
double doPath(const vector<double>& spots) const{
product->CashFlows(spots, cashflows);
double value = 0.0;
for (unsigned long i=0; i<cashflows.size(); i++){
value += cashflows[i].amount*discounts[i];
}
return value;
}
void simulation(StatisticsMC& gather, unsigned long numberpaths){
vector<double> spots(product->getLookAtTimes().size());
double thisValue;
for(unsigned long i=0; i<numberpaths; i++){
getPath(spots);
thisValue = doPath(spots);
gather.DumpOneResult(thisValue);
}
}
private:
Wrapper<PathDependent> product;
double r; //interest rate
vector<double> discounts;
mutable vector<CashFlow> cashflows;
};
class ExoticBSEngine : public ExoticEngine {
public:
ExoticBSEngine(const Wrapper<PathDependent>& product_, double r, double d, double vol, const Wrapper<RandomBase>& generator_, double spot)
:ExoticEngine(product_, r), generator(generator_){
vector<double> times(product_->getLookAtTimes());
numberoftimes = times.size();
generator->setDimension(numberoftimes);
drifts.resize(numberoftimes);
standarddeviations.resize(numberoftimes);
logspot = log(spot);
variates.resize(numberoftimes);
double thisvariance = vol*vol*times[0];
drifts[0] = (r-d)*times[0] - 0.5*thisvariance;
standarddeviations[0] = sqrt(thisvariance);
for(unsigned long i=1; i<numberoftimes; i++){
thisvariance = vol*vol*(times[i]-times[i-1]);
drifts[i] = (r-d)*(times[i]-times[i-1])-0.5*thisvariance;
standarddeviations[i] = sqrt(thisvariance);
}
}
virtual void getPath(vector<double>& spots){
generator->getGaussians(variates);
double currentlogspot = logspot;
for(unsigned long i=0; i<numberoftimes; i++){
currentlogspot += drifts[i];
currentlogspot += standarddeviations[i]*variates[i];
spots[i] = exp(currentlogspot);
}
}
virtual ~ExoticBSEngine(){}
private:
Wrapper<RandomBase> generator;
vector<double> drifts;
vector<double> standarddeviations;
double logspot;
unsigned long numberoftimes;
vector<double> variates;
};
#endif // EXOTICENGINE_HPP_INCLUDED
The most important one in ExoticEngine class is simulation(), which calls getPath(), doPath(). getPath() wll generate one realization of stock price dynamics based on the stochastic model we assume, then doPath() will calculate discount cash flows, and sum
them to get the option price, the result is passed to statistics gathering tool for further usage.
We have defined the random generator class, therefore, combining them together, the main function is
#include <iostream>
#include <cmath>
#include <./montecarlo.hpp>
#include <./payoff.hpp>
#include <./statisticsmc.hpp>
#include <./vanillaoption.hpp>
#include <./wrapper.hpp>
#include <./pathdependent.hpp>
#include <./ExoticEngine.hpp>
int main(){
// set parameters
double spot = 15.0;
double expiry = 1.0;
double strike = 15.0;
double volatility = 0.4;
double r = 0.01;
double d = 0.0;
unsigned long NumberPaths = 20000;
PayOffCall payoffcall(strike);
StatisticsMean gather;
ConvergenceTable ct(gather);
double numberofdates = 252;
std::vector<double> times(numberofdates);
for(unsigned long i=0; i<numberofdates; i++){
times[i] = (i+1.0)*expiry/numberofdates;
}
PathDependentAsian option(times, expiry, payoffcall);
RandomParkMiller generator(numberofdates);
AntiThetic atgenerator(generator);
// calculate option price and generate statistics
ExoticBSEngine engine(option, r, d, volatility, atgenerator, spot);
engine.simulation(ct, NumberPaths);
// print results
std::vector<std::vector<double>> results = ct.GetResultsSoFar();
std::cout << "For Call Option Price: " << std::endl;
for (unsigned long i=0; i<results.size(); i++){
for (unsigned long j=0; j<results[i].size(); j++){
std::cout << results[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
Output