MonteCarloPricer.cpp
#include "MonteCarloPricer.h"
#include "matlib.h"
#include "CallOption.h"
#include "Executor.h"
using namespace std;
MonteCarloPricer::MonteCarloPricer() :
nScenarios(100000),
nSteps(10),
nTasks(1) {
}
double MonteCarloPricer::price(
const ContinuousTimeOption& option,
const BlackScholesModel& model) const {
auto stocks = option.getStocks();
assert(stocks.size() == 1);
MultiStockModel msm(model);
return price(option, msm);
}
double singleThreadedPrice(
int taskNumber,
int nScenarios,
int nSteps,
const ContinuousTimeOption& option,
const MultiStockModel& model ) {
if (!option.isPathDependent()) {
nSteps = 1;
}
double total = 0.0;
MultiStockModel subModel = model.getSubmodel(
option.getStocks());
long long randSize = subModel.randSize(nScenarios,
nSteps);
mt19937 rng;
rng.discard(randSize*taskNumber);
// We price at most one million scenarios at a time to avoid running out of memory
int batchSize = 1000000/nSteps;
if (batchSize<=0) {
batchSize = 1;
}
int scenariosRemaining = nScenarios;
while (scenariosRemaining>0) {
int thisBatch = batchSize;
if (scenariosRemaining<batchSize) {
thisBatch = scenariosRemaining;
}
MarketSimulation sim = subModel.
generateRiskNeutralPricePaths(
rng,
option.getMaturity(),
thisBatch,
nSteps );
Matrix payoffs = option.payoff( sim );
total+= sumCols( payoffs ).asScalar();
scenariosRemaining-=thisBatch;
}
double mean = total/nScenarios;
double r = model.getRiskFreeRate();
double T = option.getMaturity() - model.getDate();
return exp(-r*T)*mean;
}
class PriceTask : public Task {
public:
/* Amount of random numbers to skip */
int taskNumber;
int nScenarios, nSteps;
const ContinuousTimeOption& option;
const MultiStockModel& model;
/* Output data */
double result;
PriceTask(
int taskNumber,
int nScenarios,
int nSteps,
const ContinuousTimeOption& option,
const MultiStockModel& model)
:
taskNumber(taskNumber),
nScenarios(nScenarios),
nSteps(nSteps),
option(option),
model(model) {
}
void execute() {
result = singleThreadedPrice( taskNumber,
nScenarios, nSteps, option, model);
}
};
/**
* Price the option by Monte Carlo
*/
double MonteCarloPricer::price(
const ContinuousTimeOption& option,
const MultiStockModel& model) const {
ASSERT(nTasks >= 1);
vector< shared_ptr<PriceTask> > tasks;
shared_ptr<Executor> executor =
Executor::newInstance(nTasks);
for (int i = 0; i<nTasks; i++) {
shared_ptr<PriceTask> task(new PriceTask(
i, nScenarios/nTasks,
nSteps, option, model));
tasks.push_back(task);
executor->addTask(task);
}
executor->join();
double total = 0.0;
for (int i = 0; i<nTasks; i++) {
total += tasks[i]->result;
}
return total / nTasks;
}
//////////////////////////////////////
//
// Tests
//
//////////////////////////////////////
static void testPriceCallOption() {
rng("default");
CallOption c;
c.setStrike( 110 );
c.setMaturity( 2 );
BlackScholesModel m;
m.volatility = 0.1;
m.riskFreeRate = 0.05;
m.stockPrice = 100.0;
m.drift = 0.1;
m.date = 1;
MultiStockModel msm(m);
MonteCarloPricer pricer;
pricer.nTasks = 1;
double price = pricer.price( c, m );
double expected = c.price( msm );
pricer.nTasks = 10;
double price2 = pricer.price(c, m);
ASSERT_APPROX_EQUAL( price, expected, 0.1 );
ASSERT_APPROX_EQUAL( price2, price, 0.000001);
}
void testMonteCarloPricer() {
TEST( testPriceCallOption );
}