Mersenne-Twister 19937. Initial flat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. ATM strike 3%.
No predictor corrector, no brownian bridge. 131072 simulations. No displaced diffusion, no
stochastic volatility.
Diff = BS-MC
Abbreviations : BS :=Black-Scholes (closed form), DD :=Displaced Diffusion (closed form), MC :=Monte
Carlo.
Mersenne-Twister 19937. Initial flat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. ATM strike 3%.
Predictor corrector + brownian bridge. 131072 simulations. No displaced diffusion, no stochastic
volatility.
Diff = BS-MC
Mersenne-Twister 19937. Initial flat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. ATM strike 3%.
Predictor corrector + brownian bridge. 131072 simulations. Displaced diffusion q=0.7, no stochastic
volatility.
Diff=DD-MC
Mersenne-Twister 19937. Initial flat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. strike 6%.
Predictor corrector + brownian bridge. 131072 simulations. Displaced diffusion q=0.7, no stochastic
volatility.
Diff=DD-MC
Mersenne-Twister 19937. Initial flat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. strike 6%.
Predictor corrector + brownian bridge. 131072 simulations. No Displaced diffusion, stochastic
volatility = 150%, kappa=1.
Diff=Heston-MC
Mersenne-Twister 19937. Initial flat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. strike 6%.
Predictor corrector + brownian bridge. 131072 simulations. Displaced diffusion q=0.7, stochastic
volatility = 150%, kappa=1.
MC only.
#include "stdafx.h"
#include "LMMTest.h"
#include <boost/make_shared.hpp>
namespace QLib
{
using namespace MonteCarloEngine;
void LMMTest()
{
int n = 65536;
size_t nCaplets = 39;
double strike = 0.06;
double skew = 1.0;
double volatility = 0.2;
bool useStochVol = true;
bool usepredcorr = true;
bool useSobol = false;
bool usebrownianbridge = true;
double kappaSV = 1.0;
double sigmaSV = 1.5;
size_t startIdx = 1;
int nFactors = 1;
double deterministicBasis = 0.001 * 0;
/*std::cout << "Enter nSimus: ";
std::cin >> n;
std::cout << "Enter nCaplets: ";
std::cin >> nCaplets;
std::cout << "Enter startIdx: ";
std::cin >> startIdx;
std::cout << "Enter strike: ";
std::cin >> strike;
std::cout << "Enter skew: ";
std::cin >> skew;
std::cout << "Enter volatility: ";
std::cin >> volatility;
std::cout << "Enter useSV: ";
std::cin >> useStochVol;
std::cout << "Enter usePC: ";
std::cin >> usepredcorr;
std::cout << "Enter useBB: ";
std::cin >> usebrownianbridge;
std::cout << "Enter kappaSV: ";
std::cin >> kappaSV;
std::cout << "Enter sigmaSV: ";
std::cin >> sigmaSV;*/
Date base_date(2015, 8, 18);
// TO FIX
boost::shared_ptr<MarketData> market = CreateTestMarket(base_date);
Currency base_ccy("USD");
CurveType type(base_ccy);
Date pricing_date = base_date;
std::string tenor = "3M";
size_t nTimeSteps = 40;
// int nFactors = 1;
double rho = 0.0;
double lambda = 0.1;
boost::shared_ptr<const TimeStructure> lts = boost::shared_ptr<const
TimeStructure>(new TimeStructure(base_date, tenor, nTimeSteps));
boost::shared_ptr<const LMMCorrelationStructure> corr_model =
boost::shared_ptr<const LMMCorrelationStructure>(new LMMCorrelationStructure(rho, lambda,
nFactors, lts));
boost::shared_ptr<const LMMVolatilityStructure> vol_model =
boost::shared_ptr<const LMMVolatilityStructure>(new LMMVolatilityStructure(volatility,
lts, skew));
boost::shared_ptr<const LMMCovarianceStructure> cov_model =
boost::shared_ptr<LMMCovarianceStructure>(new LMMCovarianceStructure(vol_model,
corr_model));
boost::shared_ptr<const LMMStochasticVolatilityStructure> stoch_vol_model;
if (useStochVol)
{
double theta = 1.0;
double kappa = kappaSV;
double sigma = sigmaSV;
stoch_vol_model =
boost::shared_ptr<LMMStochasticVolatilityStructure>(new
LMMStochasticVolatilityStructure(theta, kappa, sigma, lts));
}
std::string rc_name = "usd_dfcurve";
std::string rc_ccy = "USD";
DateTime pricing_datetime = date_to_datetime(pricing_date);
size_t n_dates = nTimeSteps + 1;
std::vector<DateTime> rc_dates(n_dates);
std::vector<double> dfs(n_dates);
rc_dates[0] = date_to_datetime(base_date);
dfs[0] = 1.0;
double dpy = 365.0;
double rate = 0.03;
for (size_t i = 1; i < n_dates; i++)
{
rc_dates[i] = date_to_datetime(AddTenor(lts->getDate(i - 1), tenor));
double days = DateTimeDiff(rc_dates[i], rc_dates[i - 1]);
dfs[i] = dfs[i - 1] / (1.0 + (rate)* days / dpy);
}
std::string interp = "PieceWiseConstant";
Enum::Interpolation::Enum interp_type = Enum::Interpolation::which(interp);
// TO FIX
std::string interp_vals = "LinearRates";
Enum::IRInterpolationData::Enum interp_what =
Enum::IRInterpolationData::which(interp_vals);
TimeCalculator time_calculator;
boost::shared_ptr<const YieldCurve> df_curve = boost::shared_ptr<const
YieldCurve>(
new RateCurve(
rc_name,
rc_ccy,
pricing_datetime,
rc_dates,
dfs,
interp_type,
interp_what,
time_calculator)
);
LMM core_model(
pricing_date,
base_ccy,
df_curve,
df_curve,
cov_model,
stoch_vol_model,
deterministicBasis
);
boost::shared_ptr<LMM> lmm_ptr = boost::make_shared<LMM>(core_model);
LiborMarketModelMC::config_type config;
config.core_model_ptr_ = lmm_ptr;
config.use_brownian_bridge_ = usebrownianbridge;
config.use_predictor_corrector_ = usepredcorr;
config.use_stoch_vol_ = useStochVol;
config.base_ccy = base_ccy;
boost::gregorian::date_duration start = lts->getDate(startIdx) - lts-
>getDate(0);
std::vector<Date> opt_mat;
Date startDate = base_date + DateDuration(start);
Date exerciseDate = startDate - DateDuration(2);
opt_mat.push_back(exerciseDate);
size_t idx = lts->getTimeIndex(startDate);
for (size_t i = 1; i < nCaplets; i++)
{
boost::gregorian::date_duration diff = lts->getDate(idx + i) - lts-
>getDate(idx + i - 1);
double days = diff.days();
startDate += DateDuration((long)days);
exerciseDate = startDate - DateDuration(2);
opt_mat.push_back(exerciseDate);
}
double notional = 1.0;
std::vector<Payoff> payoffs;
payoffs.push_back(IRCap::create(base_ccy, opt_mat, tenor, notional, strike,
true));
Controller<LiborMarketModelMC, Payoff, AccumulatorPVConvergence,
PathAccumulatorPV> controller;
controller.configure(config, payoffs.begin(), payoffs.end(), *market);
if (useSobol)
controller.use_sobol();
Controller<LiborMarketModelMC, Payoff, AccumulatorPVConvergence,
PathAccumulatorPV>::return_type results = controller.run(n);
for (size_t i = 0; i < results.size(); ++i) print_conv_table(results[i]);
// analytical price
TimeCalculator timeCalc;
std::vector<double> hestonParams;
hestonParams.push_back(1.0 * volatility * volatility); // v0
hestonParams.push_back(kappaSV);
hestonParams.push_back(1.0 * volatility * volatility); // theta SV
hestonParams.push_back(sigmaSV * volatility);
hestonParams.push_back(0.0); // rho
XHeston xheston(pricing_datetime, timeCalc, hestonParams);
double analytic_call = 0.0;
double analytic_call_heston = 0.0;
double bs_call = 0.0;
for (size_t i = 0; i < opt_mat.size(); i++)
{
boost::gregorian::date_duration diff = lts->getDate(i + idx + 1) -
lts->getDate(i + idx);
double days = diff.days();
Date sd = opt_mat[i] + DateDuration(2);
Date pd = sd + DateDuration((long)days);
double P_Tsd = df_curve->df(date_to_datetime(sd));
double P_Tpd = df_curve->df(date_to_datetime(pd));
double dcf = YearFraction(sd, AddTenor(sd, tenor));
double fwd_rate = (P_Tsd / P_Tpd - 1.0) / dcf;
analytic_call += ShiftedLogNormal::price(true, P_Tpd, fwd_rate,
strike, YearFraction(base_date, opt_mat[i]), volatility, skew); // *dcf;
bs_call += BlackScholes::price(true, P_Tpd, fwd_rate, strike,
YearFraction(base_date, opt_mat[i]), volatility); // *dcf;
analytic_call_heston += P_Tpd * fwd_rate * xheston.vanilla(true,
YearFraction(base_date, opt_mat[i]), strike / fwd_rate); // *dcf;
}
std::cout << "Market value DD " << analytic_call << std::endl;
std::cout << "Market value BS " << bs_call << std::endl;
std::cout << "Market value Heston " << analytic_call_heston << std::endl;
double diff = analytic_call_heston - results[0].results_[0].first;
std::cout << "Difference" << diff << std::endl;
}
}
/*
LMMProcess
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include "../DiscretisationScheme/LMMSchemeBase.h"
#include "../DiscretisationScheme/DiscretisationSchemeBase.h"
#include "BrownianIncrements.h"
#include "../../../Utils/Matrix.h"
#include "../../../Utils/Vector.h"
#include "../../../Core/Exception.h"
#include "BrownianBridge.h"
namespace QLib
{
namespace MonteCarloEngine
{
class LMMProcess
{
public:
typedef Matrix result_type;
typedef boost::shared_ptr<const LMMSchemeBase> lmm_scheme_ptr_type;
typedef boost::shared_ptr<const DiscretisationSchemeBase>
lmm_stoch_vol_scheme_ptr_type;
result_type create_result() const
{
return result_type(size1(), size2());
}
struct buffer_type
{
Vector bridged_variates;
Matrix increments;
};
buffer_type create_buffer() const
{
buffer_type buffer;
buffer.increments.resize(inc_func_.size1(),
inc_func_.size2());
buffer.bridged_variates.resize(input_size());
return buffer;
}
LMMProcess(const Vector& initialStates,
const CovarianceStructure& cov_struct, // just to initialise
the B_Bridge
size_t nFactors,
const Vector& time_grid,
const std::vector<size_t>& obs_idx,
bool use_brownian_bridge = false,
bool use_stoch_vol = false)
: initialStates_(initialStates),
nLibors_(initialStates_.size()),
inc_func_(nFactors, time_grid),
obs_idx_(obs_idx),
use_brownian_bridge_(use_brownian_bridge),
bridge_(cov_struct),
use_stoch_vol_(use_stoch_vol),
nFactors_(inc_func_.size2())
{
}
Matrix & operator() (const Vector& input, Matrix& output,
buffer_type& buffer) const;
void set_main_scheme(lmm_scheme_ptr_type scheme) { main_scheme_ =
scheme; }
void set_stoch_vol_scheme(lmm_stoch_vol_scheme_ptr_type scheme) {
stoch_vol_scheme_ = scheme; }
bool use_stoch_vol() const { return use_stoch_vol_; }
size_t input_size() const { return inc_func_.input_size(); }
size_t size1() const { return inc_func_.size1(); }
size_t size2() const { return nLibors_; }
private:
Vector initialStates_;
size_t nLibors_;
BrownianIncrements inc_func_;
std::vector<size_t> obs_idx_;
lmm_scheme_ptr_type main_scheme_;
lmm_stoch_vol_scheme_ptr_type stoch_vol_scheme_;
bool use_brownian_bridge_;
BrownianBridgeMultiD bridge_;
bool use_stoch_vol_;
int nFactors_;
};
}
}
/*
LMMProcess class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#include "stdafx.h"
#include "LMMProcess.h"
namespace QLib
{
namespace MonteCarloEngine
{
Matrix & LMMProcess::operator() (const Vector& input, Matrix& output,
buffer_type& buffer) const
{
QLIB_ASSERT((output.size1() == size1()) && (output.size2() ==
size2()), "blabla");
size_t nLibors = initialStates_.size();
size_t nTimes = inc_func_.size1();
Matrix liborStates(nTimes, nLibors);
size_t nVolFactors, nLmmFactors;
if (use_stoch_vol_)
{
nLmmFactors = nFactors_ - 1;
nVolFactors = 1;
}
else
{
nLmmFactors = nFactors_;
nVolFactors = 0;
}
Vector bmIncrements(nLmmFactors + nVolFactors);
Vector lmmBmIncrements(nLmmFactors);
double variance = 1.0;
Vector varianceAtTimeIndex(1);
varianceAtTimeIndex[0] = variance;
Vector volBmIncrements(1);
if (use_brownian_bridge_)
bridge_.generate_path(input,buffer.bridged_variates);
for (size_t timeIndex = 0; timeIndex < nTimes; timeIndex++)
{
if (use_brownian_bridge_)
inc_func_(buffer.bridged_variates, bmIncrements,
timeIndex);
else
inc_func_(input, bmIncrements, timeIndex);
for (size_t i = 0; i < nLmmFactors; i++)
lmmBmIncrements[i] = bmIncrements[i];
for (size_t j = 0; j < nVolFactors; j++)
volBmIncrements[j] = bmIncrements[nLmmFactors + j];
if (timeIndex != 0)
{
if (use_stoch_vol_)
{
varianceAtTimeIndex = stoch_vol_scheme_-
>evolve(varianceAtTimeIndex,volBmIncrements,timeIndex);
variance = varianceAtTimeIndex[0];
}
row(liborStates, timeIndex) = main_scheme_-
>evolve(row(liborStates, timeIndex - 1), lmmBmIncrements, inc_func_.time_inc(timeIndex),
inc_func_.time_grid(timeIndex), initialStates_, variance);
}
else
row(liborStates, 0) = initialStates_;
row(output, timeIndex) = row(liborStates, timeIndex);
}
return output;
}
}
}
/*
CovarianceStructure
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include "../../../Utils/Vector.h"
#include "../../../Utils/Matrix.h"
#include "../../../Core/Exception.h"
namespace QLib
{
namespace MonteCarloEngine
{
class CovarianceStructure
{
public:
CovarianceStructure(size_t dimension = 0,
size_t steps = 0)
: dimension_(dimension), covariances_(steps)
{
for (size_t i = 0; i < steps; ++i)
covariances_[i] = IdentityMatrix(dimension);
}
~CovarianceStructure() {}
void set_covariance(size_t step, const Matrix& covariance)
{
QLIB_ASSERT(step < steps(), "blabla");
QLIB_ASSERT(covariance.size1() == dimension_ &&
covariance.size2() == dimension_, "blabla");
covariances_[step] = covariance;
}
Matrix const & covariance(size_t i) const { return covariances_[i]; }
std::vector<Matrix> const& covariances() const { return covariances_;
}
size_t dimension()const { return dimension_; }
size_t steps() const { return covariances_.size(); }
private:
size_t dimension_;
std::vector<Matrix> covariances_;
};
}
}
/*
BrownianIncrements
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#pragma once
#include "../../../Utils/Vector.h"
#include <boost/shared_ptr.hpp>
#include "../../../Core/Exception.h"
namespace QLib
{
namespace MonteCarloEngine
{
class BrownianIncrements
{
public:
BrownianIncrements(size_t nFactorsTotal, const Vector& time_grid) :
nFactorsTotal_(nFactorsTotal), time_grid_(time_grid) {}
// size_t input_size() const { return time_grid_.size(); } // CHECK
size_t input_size() const { return size1() * size2(); }
size_t input_dimension() const { return nFactorsTotal_; }
size_t size1() const { return time_grid_.size(); }
size_t size2() const { return nFactorsTotal_; } // make stuff cleaner
later
Vector& operator()(Vector const& input, Vector& output, std::size_t
i) const;
double time_inc(size_t i) const
{
if (i == 0)
return time_grid_[0];
else
return time_grid_[i] - time_grid_[i - 1];
}
double time_grid(size_t i) const { return time_grid_[i]; }
private:
size_t nFactorsTotal_;
Vector time_grid_;
};
// TO FIX
inline Vector& BrownianIncrements::operator() (const Vector& input, Vector&
output, size_t i) const
{
QLIB_ASSERT(output.size() == size2(),"blabla");
VectorConstRef in(input, boost::numeric::ublas::range(i *
nFactorsTotal_, (i + 1) * nFactorsTotal_));
output = in; // prod(sample_struct.root(i), in); // don't use this!
too long and useless
return output;
}
}
}
/*
LiborMarketModelMC class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#include "stdafx.h"
#include <boost/pointer_cast.hpp>
#include "LiborMarketModelMC.h"
#include "../Observation/AdjZCBondObservation.h"
#include "../../../Model/LMM/LMMCovarianceStructure.h"
#include "../../../Core/Exception.h"
const double num_days = 365.25;
namespace QLib
{
namespace MonteCarloEngine
{
LiborMarketModelMC::LiborMarketModelMC(const config_type& config, const
std::vector<PrimeRequest>& requests) : config_(config), parser_(requests), dates_(0)
{
for (std::set<Currency>::const_iterator it =
parser_.currencies().begin(); it != parser_.currencies().end(); it++)
QLIB_ASSERT((*it)==config_.base_ccy, "blabla")
boost::shared_ptr<LMM> lmm_ptr = config_.core_model_ptr_;
nFactors_ = lmm_ptr->numOfFactors();
}
boost::shared_ptr<LMMSimulation>
LiborMarketModelMC::calibrate(const MarketData& mkt, const Sampler&
sampler) const
{
boost::shared_ptr<LMM> lmm_ptr = config_.core_model_ptr_;
int nLibors = lmm_ptr->numOfLibors();
Vector initialStates(nLibors);
const std::vector<double>& initSt = config_.core_model_ptr_-
>getInitialDates();
for (int i = 0; i < nLibors; i++)
initialStates[i] = initSt[i];
for (std::map<Currency, size_t>::const_iterator it =
ccy_factor.begin(); it != ccy_factor.end(); ++it)
QLIB_ASSERT(!((*it).first == config_.base_ccy), "blabla");
const boost::shared_ptr<const LMMCovarianceStructure>&
lmmCovarianceStructure = lmm_ptr->getLMMCovarianceStructure();
std::vector<Date> firstDate = lmmCovarianceStructure->getDates();
std::vector<Date> datesUnion(firstDate.size());
std::vector<Date>::iterator itDates;
std::vector<Date> lmm_grid = firstDate;
std::vector<std::size_t> obs_idx;
std::set<Date>::const_iterator obs_date_it =
parser_.observationDates().begin();
while ((obs_date_it != parser_.observationDates().end()) &&
(*obs_date_it <= datetime_to_date(mkt.getPricingDate())))
++obs_date_it;
datesUnion.resize(firstDate.size() + std::distance(obs_date_it,
parser_.observationDates().end()));
itDates = std::set_union (firstDate.begin(), firstDate.end(),
obs_date_it, parser_.observationDates().end(), datesUnion.begin());
datesUnion.resize(itDates - datesUnion.begin());
std::vector<Date>::iterator mc_grid_it = datesUnion.begin();
size_t obs_pos = 0;
for (std::set<Date>::const_iterator it = obs_date_it; it !=
parser_.observationDates().end(); it++)
{
while ((mc_grid_it != datesUnion.end()) && (*mc_grid_it <
*it))
{
++mc_grid_it;
++obs_pos;
}
obs_idx.push_back(obs_pos);
}
std::vector<double> mc_time_grid(datesUnion.size());
Vector mc_time_grid_vec(datesUnion.size());
for (size_t i = 0; i < datesUnion.size(); i++)
{
boost::gregorian::days dd = datesUnion[i] -
datetime_to_date(mkt.getPricingDate());
mc_time_grid[i] = dd.days() / num_days;
mc_time_grid_vec[i] = mc_time_grid[i];
}
std::map<Date, size_t> date_to_step;
size_t step = 0;
for (std::vector<Date>::iterator it = datesUnion.begin(); it !=
datesUnion.end(); ++it)
date_to_step.insert(std::make_pair(*it, step++));
size_t nFactorsTotal = config_.use_stoch_vol_ ? nFactors_ + 1 :
nFactors_;
CovarianceStructure cov_struct(nFactorsTotal, mc_time_grid.size());
for (size_t i = 0; i < mc_time_grid.size(); ++i)
{
double dt = (i == 0) ? 1.0 : mc_time_grid[i] - mc_time_grid[i
- 1];
Matrix cov(cov_struct.dimension(), cov_struct.dimension(),
0.0);
for (size_t j = 0; j < nFactorsTotal; ++j)
{
cov(j, j) = dt;
for (size_t k = 0; k < j; ++k)
cov(j, k) = cov(k, j) = 0.0;
}
cov_struct.set_covariance(i, cov);
}
// create Process
process_type lmm_process(initialStates, cov_struct, nFactorsTotal,
mc_time_grid_vec, obs_idx, config_.use_brownian_bridge_, config_.use_stoch_vol_);
// create schemes
lmm_scheme_ptr_type lmm_scheme =
LMMSchemeFactory::getInstance().createScheme("LogEuler", lmmCovarianceStructure,
nFactors_, config_.use_stoch_vol_, config_.use_predictor_corrector_);
lmm_process.set_main_scheme(lmm_scheme);
bool use_stoch_vol = config_.use_stoch_vol_;
if (use_stoch_vol)
{
const boost::shared_ptr<const
LMMStochasticVolatilityStructure>& lmmSVStructure = lmm_ptr-
>getLMMStochasticVolatilityStructure();
std::vector<double> kappa = lmmSVStructure->getKappa();
std::vector<double> theta = lmmSVStructure->getTheta();
std::vector<double> sigma = lmmSVStructure->getSigma();
boost::shared_ptr<const TimeStructure> ts = lmmSVStructure-
>getTimeStructure();
lmm_stoch_vol_scheme_ptr_type lmm_sv_scheme =
CIRSchemeFactory::getInstance().createScheme("QE", kappa, theta, sigma, ts, datesUnion);
lmm_process.set_stoch_vol_scheme(lmm_sv_scheme);
}
boost::shared_ptr<simulation_type> simulation(new
simulation_type(lmm_process, sampler.nbr_req()));
for (AtomicParser::df_map_type::const_iterator it =
parser_.dfRequests().begin(); it != parser_.dfRequests().end(); ++it)
{
const Date obs_date = (*it).first.first;
if (obs_date > datetime_to_date(mkt.getPricingDate()))
{
simulation->handle_int_.set((*it).second, simulation-
>atomic_observations_.size());
const Date sett_date = (*it).first.second.date();
Currency ccy =
(*it).first.second.curveType().currency();
CurveType::curve_types crv_type =
(*it).first.second.curveType().type();
size_t timeStep = date_to_step.find(obs_date)->second;
size_t index = 0;
std::vector<Date> mc_grid_past;
size_t n_past_dates = timeStep + 2;
for (size_t i = 0; i < n_past_dates; ++i)
{
mc_grid_past.push_back(datesUnion[i]);
}
boost::shared_ptr<Observation<Matrix>> obs(new
AdjZCBondObservation(crv_type, mc_grid_past, sett_date, timeStep, index,
boost::static_pointer_cast<IRBaseModel>(config_.core_model_ptr_)));
simulation->atomic_observations_.push_back(obs);
}
}
sampler.get_derived_observations(simulation->derived_observations_,
simulation-
>atomic_observations_.size(),
simulation->handle_int_,
mkt);
return simulation;
}
}
}
/*
LMMSchemeFactory class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#include "stdafx.h"
#include "LMMSchemeFactory.h"
#include <utility>
namespace QLib
{
namespace MonteCarloEngine
{
LMMSchemeFactory::LMMSchemeFactory()
{
scheme_container_.insert(std::pair<std::string,
create_scheme_func>("LogEuler", LMMSchemeHelper<LMMLogEulerScheme>::Create));
}
LMMSchemeFactory & LMMSchemeFactory::getInstance()
{
static LMMSchemeFactory theFactory;
return theFactory;
}
boost::shared_ptr<LMMSchemeBase> LMMSchemeFactory::createScheme(const
std::string& scheme_id
, const boost::shared_ptr<const LMMCovarianceStructure>&
lmm_covariance_structure
, size_t dimension, bool use_stoch_vol = false, bool
use_pred_corr = false)
{
std::map<std::string, create_scheme_func>::const_iterator scheme_idx
= scheme_container_.find(scheme_id);
if (scheme_idx == scheme_container_.end())
QLIB_THROW("blabla");
return (*scheme_idx).second(lmm_covariance_structure, dimension,
use_stoch_vol, use_pred_corr);
}
}
}
#include "stdafx.h"
#include "LMMLogEulerScheme.h"
namespace QLib
{
namespace MonteCarloEngine
{
/*Vector LMMLogEulerScheme::evolve(const Vector& previousLiborsStates, const
Vector& bmIncrements, double dt, double simDate, const Vector& initialLibors, double
varianceAtTimeIndex) const
{
QLIB_ASSERT(bmIncrements.size() == factors_dimension(), "blabla");
int ltsTimeIndex = liborsTimeStructure_->getTimeIndex(simDate);
double deltaT = liborsTimeStructure_->getTime(ltsTimeIndex + 1) -
liborsTimeStructure_->getTime(ltsTimeIndex);
double stochasticVarianceTermDrift = 1.0;
double stochasticVolatilityTermDiffusion = 1.0;
if (useStochasticVol_)
{
stochasticVarianceTermDrift *= 0.5 * (previousVariance_ +
varianceAtTimeIndex);
stochasticVolatilityTermDiffusion *=
std::sqrt(stochasticVarianceTermDrift);
}
previousVariance_ = varianceAtTimeIndex;
getLogShiftedLiborsDrift(ltsTimeIndex, previousLiborsStates,
initialLibors, deltaT);
for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++)
{
double driftOfLogShiftedLibor = drift_[liborIndex];
double initialLibor = initialLibors[liborIndex];
if (driftOfLogShiftedLibor != driftOfLogShiftedLibor)
{
nextLiborsStates_[liborIndex] =
std::numeric_limits<double>::quiet_NaN();
increments_[liborIndex] =
std::numeric_limits<double>::quiet_NaN();
}
else
{
lmmCovarianceStructure_->getFactorLoadings(simDate,
liborIndex, previousLiborsStates, factorLoadings_);
double diffusionOfLogShiftedLibor = 0.0;
for (int factorIndex = 0; factorIndex < nLiborsFactors_;
factorIndex++)
{
double factorLoading =
factorLoadings_[factorIndex];
double brownianIncrement =
bmIncrements[factorIndex] * std::sqrt(dt);
diffusionOfLogShiftedLibor += factorLoading *
brownianIncrement;
}
double increment = diffusionOfLogShiftedLibor *
stochasticVolatilityTermDiffusion + dt * driftOfLogShiftedLibor *
stochasticVarianceTermDrift;
increments_[liborIndex] = increment;
double skew = lmmCovarianceStructure_-
>getSkew(ltsTimeIndex, liborIndex);
nextLiborsStates_[liborIndex] =
transform(previousLiborsStates[liborIndex], initialLibor, skew, increment);
}
}
if (usePredictorCorrector_)
{
driftPreviousStates_ = drift_;
getLogShiftedLiborsDrift(ltsTimeIndex, nextLiborsStates_,
initialLibors, deltaT);
for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++)
{
double driftOfLogShiftedLiborPreviousStates =
driftPreviousStates_[liborIndex];
double driftOfLogShiftedLiborsNextStates =
drift_[liborIndex];
double initialLibor = initialLibors[liborIndex];
double adjustment = (driftOfLogShiftedLiborsNextStates -
driftOfLogShiftedLiborPreviousStates) * 0.5 * dt * stochasticVarianceTermDrift;
double increment = increments_[liborIndex] + adjustment;
double skew = lmmCovarianceStructure_-
>getSkew(ltsTimeIndex, liborIndex);
nextLiborsStates_[liborIndex] =
transform(previousLiborsStates[liborIndex], initialLibor, skew, increment);
}
}
return nextLiborsStates_;
} // LMMLogEulerScheme::evolve*/
// new evolve: no impact on regular pillars dates
Vector LMMLogEulerScheme::evolve(const Vector& previousLiborsStates, const
Vector& bmIncrements, double dt, double simDate, const Vector& initialLibors, double
varianceAtTimeIndex) const
{
QLIB_ASSERT(bmIncrements.size() == factors_dimension(), "blabla");
int ltsTimeIndex = liborsTimeStructure_->getTimeIndex(simDate);
double deltaT = liborsTimeStructure_->getTime(ltsTimeIndex + 1) -
liborsTimeStructure_->getTime(ltsTimeIndex);
double stochasticVarianceTermDrift = 1.0;
double stochasticVolatilityTermDiffusion = 1.0;
if (useStochasticVol_)
{
stochasticVarianceTermDrift *= 0.5 * (previousVariance_ +
varianceAtTimeIndex);
stochasticVolatilityTermDiffusion *=
std::sqrt(stochasticVarianceTermDrift);
}
previousVariance_ = varianceAtTimeIndex;
getLogShiftedLiborsDrift(ltsTimeIndex, previousLiborsStates,
initialLibors, deltaT);
for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++)
{
double driftOfLogShiftedLibor = drift_[liborIndex];
double initialLibor = initialLibors[liborIndex];
if (driftOfLogShiftedLibor != driftOfLogShiftedLibor)
{
nextLiborsStates_[liborIndex] =
std::numeric_limits<double>::quiet_NaN();
increments_[liborIndex] =
std::numeric_limits<double>::quiet_NaN();
}
else
{
if (liborIndex == ltsTimeIndex)
{
int appliedLiborIndex = (liborIndex == nLibors_ -
1) ? liborIndex : liborIndex + 1;
double diffusionOfLogShiftedLibor = 0.0;
double vol = lmmCovarianceStructure_-
>getVol(liborIndex, appliedLiborIndex);
double skew = lmmCovarianceStructure_-
>getSkew(liborIndex, appliedLiborIndex);
lmmCovarianceStructure_-
>getVolWeightedFactorLoadings(vol, liborIndex, previousLiborsStates, factorLoadings_);
for (int factorIndex = 0; factorIndex <
nLiborsFactors_; factorIndex++)
{
double factorLoading =
factorLoadings_[factorIndex];
double brownianIncrement =
bmIncrements[factorIndex] * std::sqrt(dt);
diffusionOfLogShiftedLibor +=
factorLoading * brownianIncrement;
}
double increment = diffusionOfLogShiftedLibor *
stochasticVolatilityTermDiffusion + dt * driftOfLogShiftedLibor *
stochasticVarianceTermDrift;
increments_[liborIndex] = increment;
// double skew = lmmCovarianceStructure_-
>getSkew(ltsTimeIndex, liborIndex);
nextLiborsStates_[liborIndex] =
transform(previousLiborsStates[liborIndex], initialLibor, skew, increment);
}
else
{
lmmCovarianceStructure_-
>getFactorLoadings(simDate, liborIndex, previousLiborsStates, factorLoadings_);
double diffusionOfLogShiftedLibor = 0.0;
for (int factorIndex = 0; factorIndex <
nLiborsFactors_; factorIndex++)
{
double factorLoading =
factorLoadings_[factorIndex];
double brownianIncrement =
bmIncrements[factorIndex] * std::sqrt(dt);
diffusionOfLogShiftedLibor +=
factorLoading * brownianIncrement;
}
double increment = diffusionOfLogShiftedLibor *
stochasticVolatilityTermDiffusion + dt * driftOfLogShiftedLibor *
stochasticVarianceTermDrift;
increments_[liborIndex] = increment;
double skew = lmmCovarianceStructure_-
>getSkew(ltsTimeIndex, liborIndex);
nextLiborsStates_[liborIndex] =
transform(previousLiborsStates[liborIndex], initialLibor, skew, increment);
}
}
}
if (usePredictorCorrector_)
{
driftPreviousStates_ = drift_;
getLogShiftedLiborsDrift(ltsTimeIndex, nextLiborsStates_,
initialLibors, deltaT);
for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++)
{
double driftOfLogShiftedLiborPreviousStates =
driftPreviousStates_[liborIndex];
double driftOfLogShiftedLiborsNextStates =
drift_[liborIndex];
double initialLibor = initialLibors[liborIndex];
double adjustment = (driftOfLogShiftedLiborsNextStates -
driftOfLogShiftedLiborPreviousStates) * 0.5 * dt * stochasticVarianceTermDrift;
double increment = increments_[liborIndex] + adjustment;
double skew = lmmCovarianceStructure_-
>getSkew(ltsTimeIndex, liborIndex);
nextLiborsStates_[liborIndex] =
transform(previousLiborsStates[liborIndex], initialLibor, skew, increment);
}
}
return nextLiborsStates_;
} // LMMLogEulerScheme::evolve
/*void LMMLogEulerScheme::getLogShiftedLiborsDrift(int ltsTimeIndex, const
Vector &liborsAtTimeIndex, const Vector& initialLibors, double deltaT) const
{
int firstLiborIndex = ltsTimeIndex + 1;
for (int liborIndex = 0; liborIndex < firstLiborIndex; ++liborIndex)
drift_[liborIndex] = std::numeric_limits<double>::quiet_NaN();
for (int liborIndex = firstLiborIndex; liborIndex < nLibors_;
++liborIndex)
drift_[liborIndex] = 0.0;
for (int factorIndex = 0; factorIndex < nLiborsFactors_;
++factorIndex)
covarianceTerms_[factorIndex] = 0.0;
for (int liborIndex = firstLiborIndex; liborIndex < nLibors_;
liborIndex++)
{
double libor = liborsAtTimeIndex[liborIndex];
double initialLibor = initialLibors[liborIndex];
double skew = lmmCovarianceStructure_->getSkew(ltsTimeIndex,
liborIndex);
double temp = deltaT * (skew * libor + (1 - skew) *
initialLibor) / (1.0 + deltaT * libor) / skew;
lmmCovarianceStructure_->getFactorLoadings(ltsTimeIndex,
liborIndex, liborsAtTimeIndex, factorLoadings_);
for (int factorIndex = 0; factorIndex < nLiborsFactors_;
factorIndex++)
{
covarianceTerms_[factorIndex] += temp *
factorLoadings_[factorIndex];
drift_[liborIndex] += covarianceTerms_[factorIndex] *
factorLoadings_[factorIndex];
}
double variance = lmmCovarianceStructure_-
>getCovariance(ltsTimeIndex, liborIndex, liborIndex, liborsAtTimeIndex, factorLoadings1_,
factorLoadings2_);
drift_[liborIndex] -= 0.5 * variance;
}
} // LMMLogEulerScheme::getLogShiftedLiborsDrift*/
// new getLSLD: no impact on relular pillars dates
void LMMLogEulerScheme::getLogShiftedLiborsDrift(int ltsTimeIndex, const
Vector &liborsAtTimeIndex, const Vector& initialLibors, double deltaT) const
{
int firstLiborIndex = ltsTimeIndex + 1;
for (int liborIndex = 0; liborIndex < firstLiborIndex; ++liborIndex)
drift_[liborIndex] = std::numeric_limits<double>::quiet_NaN();
for (int liborIndex = ltsTimeIndex; liborIndex < nLibors_;
++liborIndex)
drift_[liborIndex] = 0.0;
for (int factorIndex = 0; factorIndex < nLiborsFactors_;
++factorIndex)
covarianceTerms_[factorIndex] = 0.0;
for (int liborIndex = firstLiborIndex; liborIndex < nLibors_;
liborIndex++)
{
double libor = liborsAtTimeIndex[liborIndex];
double initialLibor = initialLibors[liborIndex];
double skew = lmmCovarianceStructure_->getSkew(ltsTimeIndex,
liborIndex);
double temp = deltaT * (skew * libor + (1 - skew) *
initialLibor) / (1.0 + deltaT * libor) / skew;
lmmCovarianceStructure_->getFactorLoadings(ltsTimeIndex,
liborIndex, liborsAtTimeIndex, factorLoadings_);
for (int factorIndex = 0; factorIndex < nLiborsFactors_;
factorIndex++)
{
covarianceTerms_[factorIndex] += temp *
factorLoadings_[factorIndex];
drift_[liborIndex] += covarianceTerms_[factorIndex] *
factorLoadings_[factorIndex];
}
double variance = lmmCovarianceStructure_-
>getCovariance(ltsTimeIndex, liborIndex, liborIndex, liborsAtTimeIndex, factorLoadings1_,
factorLoadings2_);
drift_[liborIndex] -= 0.5 * variance;
// drift_[liborIndex];
if (liborIndex == firstLiborIndex)
drift_[firstLiborIndex - 1] = drift_[liborIndex];
}
} // LMMLogEulerScheme::getLogShiftedLiborsDrift
double LMMLogEulerScheme::transform(double previousLibor, double
initialLibor, double skew, double increment) const
{
double libor;
libor = (skew * previousLibor + (1 - skew) * initialLibor) *
std::exp(increment);
libor -= (1 - skew) * initialLibor;
libor /= skew;
return libor;
}
}
}
/*
CirQEScheme class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#include "stdafx.h"
#include "CirQEScheme.h"
namespace QLib
{
namespace MonteCarloEngine
{
const double to_year_frac = 1.0 / 365;
const double QE_psi = 1.5;
CirQEScheme::CirQEScheme(const std::vector<double>& kappa,
const std::vector<double>& theta,
const std::vector<double>& sigma,
const boost::shared_ptr<const TimeStructure>& ts,
const std::vector<Date>& time_grid)
{
size_t nSteps = ts->getSize();
QLIB_ASSERT(kappa.size() == nSteps+1 && theta.size() == nSteps+1 &&
sigma.size() == nSteps+1, "blabla");
ts_ = ts;
time_grid_ = time_grid;
theta_ = theta;
sigma_ = sigma;
kappa_ = kappa;
}
Vector CirQEScheme::evolve(const Vector& previousValueV, const Vector&
bmIncrementsV, size_t timeIndex) const
{
Date simDate = time_grid_[timeIndex];
int tsTimeIndex = ts_->getTimeIndex(simDate);
double previousValue = previousValueV[0];
double bmIncrement = bmIncrementsV[0];
double dt = nbDays(time_grid_[timeIndex-1], time_grid_[timeIndex]) *
to_year_frac;
//double sqrtdt = std::sqrt(dt);
//bmIncrement *= sqrtdt;
double theta = theta_[tsTimeIndex];
double kappa = kappa_[tsTimeIndex];
double sigma = sigma_[tsTimeIndex];
double tmp = std::exp(-kappa * dt);
double m = theta + (previousValue - theta) * tmp;
double s2 = previousValue * sigma * sigma * tmp / kappa * (1 - tmp) +
theta * sigma * sigma / (2 * kappa) * (1 - tmp) * (1 - tmp);
double psi = s2 / (m*m);
Vector variance(1);
if (psi <= QE_psi)
{
double b = std::sqrt(2 / psi - 1 + std::sqrt(2 / psi * (2 /
psi - 1)));
double a = m / (1 + b*b);
variance[0] = a * (b + bmIncrement) * (b + bmIncrement);
}
else
{
double p = (psi - 1) * (psi + 1);
double beta = (1 - p) / m;
double u = boost::math::cdf(norm_,bmIncrement);
variance[0] = (u <= p) ? 0 : 1 / beta * std::log((1-p)/(1-u));
}
return variance;
}
}
}
/*
VolatilityModel class.
Factor reduction performed using Eigen library.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#pragma once
#include "../../Utils/Vector.h"
#include "../../DateTime/TimeStructure.h"
#include <boostshared_ptr.hpp>
namespace QLib
{
class LMMVolatilityStructure
{
public:
LMMVolatilityStructure(double volatility, const boost::shared_ptr<const
TimeStructure>& lts, double skew = 0.0);
double getVolatilityLogSpace(int timeIdx, int liborIdx) const;
double getSkew(size_t timeIdx, size_t liborIdx) const { return skew_; }
const boost::shared_ptr<const TimeStructure>& getLiborsTimeStructure() const
{ return liborsTimeStructure_; }
private:
double volatility_;
double skew_;
boost::shared_ptr<const TimeStructure> liborsTimeStructure_;
};
/*
LMMStochasticVolatilityModel class.
Factor reduction performed using Eigen library.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#pragma once
#include "../../Utils/Vector.h"
#include "../../DateTime/TimeStructure.h"
#include <boostshared_ptr.hpp>
namespace QLib
{
class LMMStochasticVolatilityStructure
{
public:
LMMStochasticVolatilityStructure(double theta, double kappa, double sigma,
const boost::shared_ptr<const TimeStructure>& lts);
const std::vector<double>& getKappa() const { return kappa_; }
const std::vector<double>& getTheta() const { return theta_; }
const std::vector<double>& getSigma() const { return sigma_; }
const boost::shared_ptr<const TimeStructure>& getTimeStructure() const {
return timeStructure_; }
private:
std::vector<double> kappa_;
std::vector<double> theta_;
std::vector<double> sigma_;
boost::shared_ptr<const TimeStructure> timeStructure_;
};
}
/*
CovarianceModel class.
Built from a volatility model along with a correlation model.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#include "stdafx.h"
#include "LMMCovarianceStructure.h"
namespace QLib
{
LMMCovarianceStructure::LMMCovarianceStructure(const boost::shared_ptr<const
LMMVolatilityStructure>& vs, const boost::shared_ptr<const LMMCorrelationStructure>& cs)
{
volatilityStructure_ = vs;
correlationStructure_ = cs;
boost::shared_ptr<const TimeStructure> lts_vol = vs-
>getLiborsTimeStructure();
boost::shared_ptr<const TimeStructure> lts_corr = cs-
>getLiborsTimeStructure();
QLIB_ASSERT(lts_vol == lts_corr, "blabla");
liborsTimeStructure_ = lts_vol;
nFactors_ = cs->numOfFactors();
}
void LMMCovarianceStructure::getFactorLoadings(int timeIdx, int liborIdx, const
Vector& liborsAtTimeIndex, Vector& factorLoadings) const
{
int fls = factorLoadings.size();
QLIB_ASSERT(fls == nFactors_, "blabla");
double volatilityLS = volatilityStructure_->getVolatilityLogSpace(timeIdx,
liborIdx);
for (int factorIdx = 0; factorIdx < nFactors_; factorIdx++)
factorLoadings[factorIdx] = volatilityLS * correlationStructure_-
>getFactorLoading(liborIdx, factorIdx);
}
void LMMCovarianceStructure::getFactorLoadings(double time, int liborIdx, const
Vector& liborsAtTimeIndex, Vector& factorLoadings) const
{
Date date_0 = liborsTimeStructure_->getDate(0);
Date date = date_0 + DateDuration(static_cast<long>(365.0 * time));
int timeIndex = liborsTimeStructure_->getTimeIndex(date);
getFactorLoadings(timeIndex, liborIdx, liborsAtTimeIndex, factorLoadings);
}
double LMMCovarianceStructure::getCovariance(int timeIdx, int liborIdx_1, int
liborIdx_2, const Vector& liborsAtTimeIndex, Vector& factorLoadings_1, Vector&
factorLoadings_2) const
{
int fls1 = factorLoadings_1.size();
int fls2 = factorLoadings_2.size();
QLIB_ASSERT(fls1 == fls2 == nFactors_, "blabla");
getFactorLoadings(timeIdx, liborIdx_1, liborsAtTimeIndex, factorLoadings_1);
getFactorLoadings(timeIdx, liborIdx_2, liborsAtTimeIndex, factorLoadings_2);
double result = 0.0;
for (size_t i = 0; i < factorLoadings_1.size(); i++)
result += factorLoadings_1[i] * factorLoadings_2[i];
return result;
}
double LMMCovarianceStructure::getVol(int timeIndex, int liborIndex) const
{
return volatilityStructure_->getVolatilityLogSpace(timeIndex, liborIndex);
}
void LMMCovarianceStructure::getVolWeightedFactorLoadings(double vol, int
liborIndex, const Vector& liborsAtTimeIndex, Vector& factorLoadings) const
{
int fls = factorLoadings.size();
QLIB_ASSERT(fls == nFactors_, "blabla");
for (int factorIndex = 0; factorIndex < nFactors_; factorIndex++)
factorLoadings[factorIndex] = vol * correlationStructure_-
>getFactorLoading(liborIndex, factorIndex);
}
}
/*
CorrelationModel class.
Factor reduction performed using Eigen library.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#include "stdafx.h"
#include "LMMCorrelationStructure.h"
// #include "../../Math/LinearAlgebra/SpectralDecomposition.h"
#include <cmath>
namespace QLib
{
LMMCorrelationStructure::LMMCorrelationStructure(double rho, double lambda, int
nFactors, const boost::shared_ptr<const TimeStructure>& lts)
{
rho_ = rho;
lambda_ = lambda;
nFactors_ = nFactors;
liborsTimeStructure_ = lts;
int nLibors = lts->getSize();
correlationMatrix_.resize(nLibors, nLibors);
factorMatrix_.resize(nLibors, nFactors);
init();
}
void LMMCorrelationStructure::init()
{
int n = liborsTimeStructure_->getSize();
for (int i = 0; i < n; ++i)
{
correlationMatrix_(i, i) = 1.0;
for (int j = i + 1; j < n; ++j)
{
double ti = liborsTimeStructure_->getTime(i);
double tj = liborsTimeStructure_->getTime(j);
double correl = rho_ + (1 - rho_)*std::exp(-lambda_ *
std::abs(ti - tj));
correlationMatrix_(i, j) = correl;
correlationMatrix_(j, i) = correl;
}
}
doFactorReduction();
}
void LMMCorrelationStructure::doFactorReduction()
{
// TO FIX
int n = liborsTimeStructure_->getSize();
for (int i = 0; i < n; i++)
factorMatrix_(i, 0) = 1;
/*Matrix eigenValues, eigenVectors;
LinearAlgebra::getEigenSystem(correlationMatrix_, eigenVectors,
eigenValues);
int n = eigenValues.size1();
for (int col = 0; col < n; col++)
{
QLIB_ASSERT(eigenValues(col, 0) >= 0, "blabla");
double sqrtLambda = std::sqrt(eigenValues(col, 0));
for (int row = 0; row < n; row++)
eigenVectors(row, col) *= sqrtLambda;
}
for (int col = 0; col < nFactors_; col++)
for (int row = 0; row < n; row++)
factorMatrix_(row, col) = eigenVectors(row, n - 1 - col);
for (int row = 0; row < n; row++)
{
double norm = 0.0;
for (int col = 0; col < nFactors_; col++)
norm += factorMatrix_(row, col) * factorMatrix_(row, col);
norm = std::sqrt(norm);
for (int col = 0; col < nFactors_; col++)
factorMatrix_(row, col) /= norm;
}*/
}
double LMMCorrelationStructure::getFactorLoading(int liborIndex, int factorIndex)
const
{
double factorLoading = factorMatrix_(liborIndex, factorIndex);
return factorLoading;
}
}
/*
LiborMarketModel class.
Built from a covariance model. Does not contain the Monte-Carlo parameters information.
Does not contain the liborProcess neither.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#include "stdafx.h"
#include <cmath>
#include "../LMM/LiborMarketModel.h"
#include <boost/range/algorithm_ext/is_sorted.hpp>
#include "../../Math/Interpolation.h"
namespace QLib
{
void LMM::initialize(const std::vector<Date> & dates, const std::vector<double>&
vols)
{
QLIB_ASSERT(pricingDate_ == dates[0], "blabla");
QLIB_ASSERT(boost::is_sorted(dates), "blabla");
LMM::precompute();
}
void LMM::precompute()
{
size_t nLibors = numOfLibors();
DateTime todayDate = indexCurve_->valueDate();
for (size_t liborIndex = 0; liborIndex < nLibors; liborIndex++)
{
double startTime = liborsTimeStructure_->getTime(liborIndex) * 365.0;
DateTime startDate = DateTimeAddDiff(startTime, todayDate);
double deltaT = liborsTimeStructure_->getTimeStep(liborIndex) *
365.0;
DateTime endDate = DateTimeAddDiff(startTime + deltaT, todayDate);
double df = indexCurve_->df(startDate, endDate);
double initialForwardRate = (1.0 / df - 1.0) / deltaT * 365.0;
initialStates_.push_back(initialForwardRate);
}
}
double LMM::zcb(CurveType::curve_types crv_type, const Date& t, const Date& T, const
Vector& states) const
{
QLIB_ASSERT(t <= T, "blabla");
if (t == T)
return 1.0;
double lastLiborState = crv_type == CurveType::discount ?
states[states.size() - 1] : states[states.size() - 1] + deterministicBasis_;
Date firstDate = liborsTimeStructure_->getDate(0);
double tyf = YearFraction(firstDate, t);
double Tyf = YearFraction(firstDate, T);
int firstLiborIndex = liborsTimeStructure_->getTimeIndex(t);
bool is_tALiborDate = true;
if (liborsTimeStructure_->getDate(firstLiborIndex) < t)
{
is_tALiborDate = false;
firstLiborIndex += 1;
}
int lastLiborIndex = liborsTimeStructure_->getTimeIndex(T);
if (liborsTimeStructure_->getDate(lastLiborIndex) == T)
lastLiborIndex -= 1;
double zcb = 1.0;
for (int liborIndex = firstLiborIndex; liborIndex < lastLiborIndex;
liborIndex++)
{
double libor = CurveType::discount ? states[liborIndex] :
states[liborIndex] + deterministicBasis_;
zcb *= 1.0 / (1.0 + liborsTimeStructure_->getTimeStep(liborIndex)*
libor);
}
double libor = states[firstLiborIndex];
if (libor != libor)
libor = lastLiborState;
if (!is_tALiborDate)
{
double tstart1 = liborsTimeStructure_->getTime(firstLiborIndex - 1);
double tstart2 = liborsTimeStructure_->getTime(firstLiborIndex);
double period = tstart2 - tstart1;
zcb *= 1.0 / (1.0 + period * libor);
period = tyf - tstart1;
zcb *= (1.0 + period * libor);
}
if (firstLiborIndex > lastLiborIndex)
{
double tend1 = liborsTimeStructure_->getTime(lastLiborIndex);
double tend2 = liborsTimeStructure_->getTime(lastLiborIndex+1);
libor = lastLiborState;
double period = tend2 - tend1;
zcb *= (1.0 + period * libor);
period = Tyf - tend1;
zcb /= (1.0 + period * libor);
}
else
{
double t1 = liborsTimeStructure_->getTime(lastLiborIndex);
libor = CurveType::discount ? states[lastLiborIndex] :
states[lastLiborIndex] + deterministicBasis_;
double period = Tyf - t1;
zcb *= 1.0 / (1.0 + period * libor);
}
return zcb;
}
/*
TimeStructure class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
Framework inspired from finmath (Java library)
http://finmath.net/
*/
#include "stdafx.h"
#include "TimeStructure.h"
namespace QLib
{
TimeStructure::TimeStructure(const Date& initialDate, const std::string& tenor,
size_t nTimeSteps)
{
dates_.push_back(initialDate);
Date lastDate = initialDate;
for (size_t timeIndex = 0; timeIndex < nTimeSteps; timeIndex++)
{
Date nD = AddTenor(lastDate, tenor);
Date nextDate = nD;
dates_.push_back(nextDate);
lastDate = nD;
}
}
size_t TimeStructure::getTimeIndex(const Date& date) const
{
std::vector<Date>::const_iterator mIt = std::find(dates_.begin(),
dates_.end(), date);
if (mIt != dates_.end())
return mIt - dates_.begin();
else
{
std::vector<Date>::const_iterator it =
std::lower_bound(dates_.begin(), dates_.end(), date);
if (it == dates_.begin())
return 0;
else if (it == dates_.end())
return dates_.size() - 1;
else
return it - dates_.begin() - 1;
}
}
size_t TimeStructure::getTimeIndex(double time) const
{
Date d1 = dates_[0];
Date d2 = d1 + DateDuration(static_cast<long>(time * 365.0));
return getTimeIndex(d2);
}
double TimeStructure::getTimeStep(size_t timeIndex) const
{
QLIB_ASSERT(timeIndex >= 0 && timeIndex < dates_.size(), "timeIndex must be
between 0 and size of time line");
Date d1 = dates_[timeIndex];
Date d2 = dates_[timeIndex + 1];
double timeStep = YearFraction(d1, d2);
return timeStep;
}
// TO FIX
double TimeStructure::getTime(size_t timeIndex) const
{
QLIB_ASSERT(timeIndex >= 0 && timeIndex < dates_.size(), "timeIndex must be
between 0 and size of time line");
Date d1 = dates_[0];
Date d2 = dates_[timeIndex];
double time = YearFraction(d1, d2);
return time;
}
Date TimeStructure::getDate(size_t timeIndex) const
{
QLIB_ASSERT(timeIndex >= 0 && timeIndex < dates_.size(), "timeIndex must be
between 0 and size of time line");
Date date = dates_[timeIndex];
return date;
}
}
#include "stdafx.h"
#include "AdjZCBondObservation.h"
namespace QLib
{
namespace MonteCarloEngine
{
double AdjZCBondObservation::operator() (Matrix const& m) const
{
std::size_t n_dates = sim_dates_.size();
double numeraire = 1.0;
size_t nLibors = m.size2();
Vector states(nLibors + 1);
states[nLibors] = std::numeric_limits<double>::quiet_NaN();
const boost::shared_ptr<const TimeStructure>& lts = core_model_ptr_-
>getLiborTimeStructure();
const std::vector<Date> & libor_dates = lts->getDates();
size_t idxStepLS = lts->getTimeIndex(sim_dates_[step_]);
Date d = lts->getDate(idxStepLS);
size_t idx = sdts_.getTimeIndex(d);
numeraire *= 1.0 / core_model_ptr_->df(crv_type_, libor_dates[0],
libor_dates[1]);
double lastLiborState = m(idx, index_ * nLibors + idxStepLS);
states[nLibors] = lastLiborState;
if (n_dates > 2)
{
numeraire = 1.0;
for (size_t i = 0; i < idxStepLS + 1; i++)
{
Date d = lts->getDate(i);
size_t idx = sdts_.getTimeIndex(d);
for (size_t liborIndex = 0; liborIndex < nLibors;
liborIndex++)
states[liborIndex] = m(idx, index_ * nLibors +
liborIndex);
numeraire = numeraire * (1.0 / core_model_ptr_-
>zcb(crv_type_, libor_dates[i], libor_dates[i + 1], states));
}
for (size_t liborIndex = 0; liborIndex < nLibors;
liborIndex++)
states[liborIndex] = m(step_, index_ * nLibors +
liborIndex);
numeraire *= core_model_ptr_->zcb(crv_type_,
sim_dates_[step_], libor_dates[idxStepLS + 1], states);
}
return core_model_ptr_->zcb(crv_type_, sim_dates_[step_], T_, states)
/ numeraire;
}
}
}
/*
SwapRateObservable class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#include "stdafx.h"
#include "../../../MarketData/MarketData.h"
#include "SwapRateObservable.h"
#include "../Observation/SwapRateObservation.h"
#include "DiscountFactorObservable.h"
#include "Sampler.h"
#include "../../../Core/Exception.h"
namespace QLib
{
namespace MonteCarloEngine
{
SwapRateObservable::SwapRateObservable(CurveType const& curve, const Date&
start, std::string swapTenor) : start_(start), swapTenor_(swapTenor)
{
size_t n = swapTenor.length();
size_t nmr = boost::lexical_cast<size_t>(swapTenor.substr(0, n - 1));
nPeriods_ = nmr * 4;
Date sd = start_;
for (size_t i = 0; i < nPeriods_; i++)
{
Date nextDate = AddTenor(sd, "3M");
dcfs_[i] = YearFraction(sd, AddTenor(sd, "3M"));
sd = nextDate;
}
}
SwapRateObservable::~SwapRateObservable() {}
std::string SwapRateObservable::description() const
{
std::stringstream ss;
ss << curve_.currency().code() << "-" << swapTenor_;
return ss.str();
}
ObservableImpl::obs_ref_type
SwapRateObservable::requestPrimeReferences(Sampler & sampler, Date date) const
{
// Date start = date + DateDuration(2);
QLIB_ASSERT(date <= start_, "blabla");
obs_ref_type references(nPeriods_+1);
references[0] = sampler.makeRequest(date, new
DiscountFactorObservable(curve_, start_));
Date sd = start_;
for (size_t i = 0; i < nPeriods_; i++)
{
Date nextDate = AddTenor(sd, "3M");
references[i+1] = sampler.makeRequest(date, new
DiscountFactorObservable(curve_, nextDate));
sd = nextDate;
}
return references;
}
boost::shared_ptr < Observation<Vector>>
SwapRateObservable::observation(ObservableImpl::obs_ref_type refs) const
{
return boost::shared_ptr < Observation<Vector>>(new
SwapRateObservation(refs, dcfs_));
}
double SwapRateObservable::fixing(const Date& date, const MarketData&
market) const
{
/*Date d = AddTenor(start_, tenor_);
std::stringstream oss;
oss << "IR.FIXING." << curve_.currency().code() << "." << tenor_;
return market.getFixing(oss.str(), date);*/
return 0.0;
}
}
}
/*
IRCap class.
Author: Xavier Charvet
charvetx@yahoo.fr
Copyright LC Software 2015
*/
#include "stdafx.h"
#include "IRSwaption.h"
#include "../Observable/Sampler.h"
#include "../Observation/HandleInterface.h"
#include "../Observable/DiscountFactorObservable.h"
#include "../Observable/SwapRateObservable.h"
#include "../Observable/Observable.h"
namespace QLib
{
namespace MonteCarloEngine
{
// two ways of doing the request: either we use the swap rate observable
(one cash flow) or we use the forward Libor Observables
// second option is multi-curve ready but we start with the first one for
convenience
CashFlows IRSwaption::request(Sampler & sampler)
{
CashFlows flows;
flows.resize(1);
rate_h_.resize(nPeriods_);
size_t idx = 0;
Date startDate = exerciseDate_ + DateDuration(2);
for (size_t idx = 0; idx < nPeriods_; idx++)
{
Date paymentDate = AddTenor(startDate, "3M");
rate_h_[idx] = sampler.makeRequest(exerciseDate_,
Observable(new DiscountFactorObservable(curve_, paymentDate)));
dcfs_[idx] = YearFraction(startDate, paymentDate);
startDate = paymentDate;
++idx;
}
rate_h_[nPeriods_] = sampler.makeRequest(exerciseDate_,
Observable(new SwapRateObservable(curve_, startDate, swapTenor_)));
return flows;
}
IRSwaption::calculator_type IRSwaption::create_calculator(const
HandleInterface& hi) const
{
std::vector<ObservationHandle> obs_h(rate_h_.size());
for (size_t i = 0; i < rate_h_.size(); i++)
obs_h[i] = hi(rate_h_[i]);
return IRSwaptionCalculator(obs_h, notional_, strike_, dcfs_);
}
}
}

Libor Market Model

  • 1.
    Mersenne-Twister 19937. Initialflat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. ATM strike 3%. No predictor corrector, no brownian bridge. 131072 simulations. No displaced diffusion, no stochastic volatility. Diff = BS-MC Abbreviations : BS :=Black-Scholes (closed form), DD :=Displaced Diffusion (closed form), MC :=Monte Carlo.
  • 2.
    Mersenne-Twister 19937. Initialflat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. ATM strike 3%. Predictor corrector + brownian bridge. 131072 simulations. No displaced diffusion, no stochastic volatility. Diff = BS-MC
  • 3.
    Mersenne-Twister 19937. Initialflat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. ATM strike 3%. Predictor corrector + brownian bridge. 131072 simulations. Displaced diffusion q=0.7, no stochastic volatility. Diff=DD-MC
  • 4.
    Mersenne-Twister 19937. Initialflat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. strike 6%. Predictor corrector + brownian bridge. 131072 simulations. Displaced diffusion q=0.7, no stochastic volatility. Diff=DD-MC
  • 5.
    Mersenne-Twister 19937. Initialflat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. strike 6%. Predictor corrector + brownian bridge. 131072 simulations. No Displaced diffusion, stochastic volatility = 150%, kappa=1. Diff=Heston-MC
  • 6.
    Mersenne-Twister 19937. Initialflat 3M forward rates 3%. LNVol 20%. Cap 10Y/3M. strike 6%. Predictor corrector + brownian bridge. 131072 simulations. Displaced diffusion q=0.7, stochastic volatility = 150%, kappa=1. MC only.
  • 7.
    #include "stdafx.h" #include "LMMTest.h" #include<boost/make_shared.hpp> namespace QLib { using namespace MonteCarloEngine; void LMMTest() { int n = 65536; size_t nCaplets = 39; double strike = 0.06; double skew = 1.0; double volatility = 0.2; bool useStochVol = true; bool usepredcorr = true; bool useSobol = false; bool usebrownianbridge = true; double kappaSV = 1.0; double sigmaSV = 1.5; size_t startIdx = 1; int nFactors = 1; double deterministicBasis = 0.001 * 0; /*std::cout << "Enter nSimus: "; std::cin >> n; std::cout << "Enter nCaplets: "; std::cin >> nCaplets; std::cout << "Enter startIdx: "; std::cin >> startIdx; std::cout << "Enter strike: "; std::cin >> strike; std::cout << "Enter skew: "; std::cin >> skew; std::cout << "Enter volatility: "; std::cin >> volatility; std::cout << "Enter useSV: "; std::cin >> useStochVol; std::cout << "Enter usePC: "; std::cin >> usepredcorr; std::cout << "Enter useBB: "; std::cin >> usebrownianbridge; std::cout << "Enter kappaSV: "; std::cin >> kappaSV; std::cout << "Enter sigmaSV: "; std::cin >> sigmaSV;*/ Date base_date(2015, 8, 18); // TO FIX boost::shared_ptr<MarketData> market = CreateTestMarket(base_date); Currency base_ccy("USD"); CurveType type(base_ccy);
  • 8.
    Date pricing_date =base_date; std::string tenor = "3M"; size_t nTimeSteps = 40; // int nFactors = 1; double rho = 0.0; double lambda = 0.1; boost::shared_ptr<const TimeStructure> lts = boost::shared_ptr<const TimeStructure>(new TimeStructure(base_date, tenor, nTimeSteps)); boost::shared_ptr<const LMMCorrelationStructure> corr_model = boost::shared_ptr<const LMMCorrelationStructure>(new LMMCorrelationStructure(rho, lambda, nFactors, lts)); boost::shared_ptr<const LMMVolatilityStructure> vol_model = boost::shared_ptr<const LMMVolatilityStructure>(new LMMVolatilityStructure(volatility, lts, skew)); boost::shared_ptr<const LMMCovarianceStructure> cov_model = boost::shared_ptr<LMMCovarianceStructure>(new LMMCovarianceStructure(vol_model, corr_model)); boost::shared_ptr<const LMMStochasticVolatilityStructure> stoch_vol_model; if (useStochVol) { double theta = 1.0; double kappa = kappaSV; double sigma = sigmaSV; stoch_vol_model = boost::shared_ptr<LMMStochasticVolatilityStructure>(new LMMStochasticVolatilityStructure(theta, kappa, sigma, lts)); } std::string rc_name = "usd_dfcurve"; std::string rc_ccy = "USD"; DateTime pricing_datetime = date_to_datetime(pricing_date); size_t n_dates = nTimeSteps + 1; std::vector<DateTime> rc_dates(n_dates); std::vector<double> dfs(n_dates); rc_dates[0] = date_to_datetime(base_date); dfs[0] = 1.0; double dpy = 365.0; double rate = 0.03; for (size_t i = 1; i < n_dates; i++) { rc_dates[i] = date_to_datetime(AddTenor(lts->getDate(i - 1), tenor)); double days = DateTimeDiff(rc_dates[i], rc_dates[i - 1]); dfs[i] = dfs[i - 1] / (1.0 + (rate)* days / dpy); } std::string interp = "PieceWiseConstant"; Enum::Interpolation::Enum interp_type = Enum::Interpolation::which(interp); // TO FIX std::string interp_vals = "LinearRates";
  • 9.
    Enum::IRInterpolationData::Enum interp_what = Enum::IRInterpolationData::which(interp_vals); TimeCalculatortime_calculator; boost::shared_ptr<const YieldCurve> df_curve = boost::shared_ptr<const YieldCurve>( new RateCurve( rc_name, rc_ccy, pricing_datetime, rc_dates, dfs, interp_type, interp_what, time_calculator) ); LMM core_model( pricing_date, base_ccy, df_curve, df_curve, cov_model, stoch_vol_model, deterministicBasis ); boost::shared_ptr<LMM> lmm_ptr = boost::make_shared<LMM>(core_model); LiborMarketModelMC::config_type config; config.core_model_ptr_ = lmm_ptr; config.use_brownian_bridge_ = usebrownianbridge; config.use_predictor_corrector_ = usepredcorr; config.use_stoch_vol_ = useStochVol; config.base_ccy = base_ccy; boost::gregorian::date_duration start = lts->getDate(startIdx) - lts- >getDate(0); std::vector<Date> opt_mat; Date startDate = base_date + DateDuration(start); Date exerciseDate = startDate - DateDuration(2); opt_mat.push_back(exerciseDate); size_t idx = lts->getTimeIndex(startDate); for (size_t i = 1; i < nCaplets; i++) { boost::gregorian::date_duration diff = lts->getDate(idx + i) - lts- >getDate(idx + i - 1); double days = diff.days(); startDate += DateDuration((long)days); exerciseDate = startDate - DateDuration(2); opt_mat.push_back(exerciseDate); }
  • 10.
    double notional =1.0; std::vector<Payoff> payoffs; payoffs.push_back(IRCap::create(base_ccy, opt_mat, tenor, notional, strike, true)); Controller<LiborMarketModelMC, Payoff, AccumulatorPVConvergence, PathAccumulatorPV> controller; controller.configure(config, payoffs.begin(), payoffs.end(), *market); if (useSobol) controller.use_sobol(); Controller<LiborMarketModelMC, Payoff, AccumulatorPVConvergence, PathAccumulatorPV>::return_type results = controller.run(n); for (size_t i = 0; i < results.size(); ++i) print_conv_table(results[i]); // analytical price TimeCalculator timeCalc; std::vector<double> hestonParams; hestonParams.push_back(1.0 * volatility * volatility); // v0 hestonParams.push_back(kappaSV); hestonParams.push_back(1.0 * volatility * volatility); // theta SV hestonParams.push_back(sigmaSV * volatility); hestonParams.push_back(0.0); // rho XHeston xheston(pricing_datetime, timeCalc, hestonParams); double analytic_call = 0.0; double analytic_call_heston = 0.0; double bs_call = 0.0; for (size_t i = 0; i < opt_mat.size(); i++) { boost::gregorian::date_duration diff = lts->getDate(i + idx + 1) - lts->getDate(i + idx); double days = diff.days(); Date sd = opt_mat[i] + DateDuration(2); Date pd = sd + DateDuration((long)days); double P_Tsd = df_curve->df(date_to_datetime(sd)); double P_Tpd = df_curve->df(date_to_datetime(pd)); double dcf = YearFraction(sd, AddTenor(sd, tenor)); double fwd_rate = (P_Tsd / P_Tpd - 1.0) / dcf; analytic_call += ShiftedLogNormal::price(true, P_Tpd, fwd_rate, strike, YearFraction(base_date, opt_mat[i]), volatility, skew); // *dcf; bs_call += BlackScholes::price(true, P_Tpd, fwd_rate, strike, YearFraction(base_date, opt_mat[i]), volatility); // *dcf; analytic_call_heston += P_Tpd * fwd_rate * xheston.vanilla(true, YearFraction(base_date, opt_mat[i]), strike / fwd_rate); // *dcf; } std::cout << "Market value DD " << analytic_call << std::endl; std::cout << "Market value BS " << bs_call << std::endl;
  • 11.
    std::cout << "Marketvalue Heston " << analytic_call_heston << std::endl; double diff = analytic_call_heston - results[0].results_[0].first; std::cout << "Difference" << diff << std::endl; } } /* LMMProcess charvetx@yahoo.fr Copyright LC Software 2015 */ #pragma once #include <boost/shared_ptr.hpp> #include "../DiscretisationScheme/LMMSchemeBase.h" #include "../DiscretisationScheme/DiscretisationSchemeBase.h" #include "BrownianIncrements.h" #include "../../../Utils/Matrix.h" #include "../../../Utils/Vector.h" #include "../../../Core/Exception.h" #include "BrownianBridge.h" namespace QLib { namespace MonteCarloEngine { class LMMProcess { public: typedef Matrix result_type; typedef boost::shared_ptr<const LMMSchemeBase> lmm_scheme_ptr_type; typedef boost::shared_ptr<const DiscretisationSchemeBase> lmm_stoch_vol_scheme_ptr_type; result_type create_result() const { return result_type(size1(), size2()); } struct buffer_type
  • 12.
    { Vector bridged_variates; Matrix increments; }; buffer_typecreate_buffer() const { buffer_type buffer; buffer.increments.resize(inc_func_.size1(), inc_func_.size2()); buffer.bridged_variates.resize(input_size()); return buffer; } LMMProcess(const Vector& initialStates, const CovarianceStructure& cov_struct, // just to initialise the B_Bridge size_t nFactors, const Vector& time_grid, const std::vector<size_t>& obs_idx, bool use_brownian_bridge = false, bool use_stoch_vol = false) : initialStates_(initialStates), nLibors_(initialStates_.size()), inc_func_(nFactors, time_grid), obs_idx_(obs_idx), use_brownian_bridge_(use_brownian_bridge), bridge_(cov_struct), use_stoch_vol_(use_stoch_vol), nFactors_(inc_func_.size2()) { } Matrix & operator() (const Vector& input, Matrix& output, buffer_type& buffer) const; void set_main_scheme(lmm_scheme_ptr_type scheme) { main_scheme_ = scheme; } void set_stoch_vol_scheme(lmm_stoch_vol_scheme_ptr_type scheme) { stoch_vol_scheme_ = scheme; } bool use_stoch_vol() const { return use_stoch_vol_; } size_t input_size() const { return inc_func_.input_size(); } size_t size1() const { return inc_func_.size1(); } size_t size2() const { return nLibors_; } private: Vector initialStates_; size_t nLibors_; BrownianIncrements inc_func_; std::vector<size_t> obs_idx_;
  • 13.
    lmm_scheme_ptr_type main_scheme_; lmm_stoch_vol_scheme_ptr_type stoch_vol_scheme_; booluse_brownian_bridge_; BrownianBridgeMultiD bridge_; bool use_stoch_vol_; int nFactors_; }; } } /* LMMProcess class. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 */ #include "stdafx.h" #include "LMMProcess.h" namespace QLib { namespace MonteCarloEngine { Matrix & LMMProcess::operator() (const Vector& input, Matrix& output, buffer_type& buffer) const { QLIB_ASSERT((output.size1() == size1()) && (output.size2() == size2()), "blabla"); size_t nLibors = initialStates_.size(); size_t nTimes = inc_func_.size1(); Matrix liborStates(nTimes, nLibors); size_t nVolFactors, nLmmFactors; if (use_stoch_vol_) { nLmmFactors = nFactors_ - 1; nVolFactors = 1; } else { nLmmFactors = nFactors_; nVolFactors = 0; }
  • 14.
    Vector bmIncrements(nLmmFactors +nVolFactors); Vector lmmBmIncrements(nLmmFactors); double variance = 1.0; Vector varianceAtTimeIndex(1); varianceAtTimeIndex[0] = variance; Vector volBmIncrements(1); if (use_brownian_bridge_) bridge_.generate_path(input,buffer.bridged_variates); for (size_t timeIndex = 0; timeIndex < nTimes; timeIndex++) { if (use_brownian_bridge_) inc_func_(buffer.bridged_variates, bmIncrements, timeIndex); else inc_func_(input, bmIncrements, timeIndex); for (size_t i = 0; i < nLmmFactors; i++) lmmBmIncrements[i] = bmIncrements[i]; for (size_t j = 0; j < nVolFactors; j++) volBmIncrements[j] = bmIncrements[nLmmFactors + j]; if (timeIndex != 0) { if (use_stoch_vol_) { varianceAtTimeIndex = stoch_vol_scheme_- >evolve(varianceAtTimeIndex,volBmIncrements,timeIndex); variance = varianceAtTimeIndex[0]; } row(liborStates, timeIndex) = main_scheme_- >evolve(row(liborStates, timeIndex - 1), lmmBmIncrements, inc_func_.time_inc(timeIndex), inc_func_.time_grid(timeIndex), initialStates_, variance); } else row(liborStates, 0) = initialStates_; row(output, timeIndex) = row(liborStates, timeIndex); } return output; } } } /* CovarianceStructure charvetx@yahoo.fr
  • 15.
    Copyright LC Software2015 */ #pragma once #include <boost/shared_ptr.hpp> #include "../../../Utils/Vector.h" #include "../../../Utils/Matrix.h" #include "../../../Core/Exception.h" namespace QLib { namespace MonteCarloEngine { class CovarianceStructure { public: CovarianceStructure(size_t dimension = 0, size_t steps = 0) : dimension_(dimension), covariances_(steps) { for (size_t i = 0; i < steps; ++i) covariances_[i] = IdentityMatrix(dimension); } ~CovarianceStructure() {} void set_covariance(size_t step, const Matrix& covariance) { QLIB_ASSERT(step < steps(), "blabla"); QLIB_ASSERT(covariance.size1() == dimension_ && covariance.size2() == dimension_, "blabla"); covariances_[step] = covariance; } Matrix const & covariance(size_t i) const { return covariances_[i]; } std::vector<Matrix> const& covariances() const { return covariances_; } size_t dimension()const { return dimension_; } size_t steps() const { return covariances_.size(); } private: size_t dimension_; std::vector<Matrix> covariances_; };
  • 16.
    } } /* BrownianIncrements charvetx@yahoo.fr Copyright LC Software2015 */ #pragma once #include "../../../Utils/Vector.h" #include <boost/shared_ptr.hpp> #include "../../../Core/Exception.h" namespace QLib { namespace MonteCarloEngine { class BrownianIncrements { public: BrownianIncrements(size_t nFactorsTotal, const Vector& time_grid) : nFactorsTotal_(nFactorsTotal), time_grid_(time_grid) {} // size_t input_size() const { return time_grid_.size(); } // CHECK size_t input_size() const { return size1() * size2(); } size_t input_dimension() const { return nFactorsTotal_; } size_t size1() const { return time_grid_.size(); } size_t size2() const { return nFactorsTotal_; } // make stuff cleaner later Vector& operator()(Vector const& input, Vector& output, std::size_t i) const; double time_inc(size_t i) const { if (i == 0) return time_grid_[0]; else return time_grid_[i] - time_grid_[i - 1]; } double time_grid(size_t i) const { return time_grid_[i]; } private: size_t nFactorsTotal_; Vector time_grid_; };
  • 17.
    // TO FIX inlineVector& BrownianIncrements::operator() (const Vector& input, Vector& output, size_t i) const { QLIB_ASSERT(output.size() == size2(),"blabla"); VectorConstRef in(input, boost::numeric::ublas::range(i * nFactorsTotal_, (i + 1) * nFactorsTotal_)); output = in; // prod(sample_struct.root(i), in); // don't use this! too long and useless return output; } } } /* LiborMarketModelMC class. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 */ #include "stdafx.h" #include <boost/pointer_cast.hpp> #include "LiborMarketModelMC.h" #include "../Observation/AdjZCBondObservation.h" #include "../../../Model/LMM/LMMCovarianceStructure.h" #include "../../../Core/Exception.h" const double num_days = 365.25; namespace QLib { namespace MonteCarloEngine { LiborMarketModelMC::LiborMarketModelMC(const config_type& config, const std::vector<PrimeRequest>& requests) : config_(config), parser_(requests), dates_(0) { for (std::set<Currency>::const_iterator it = parser_.currencies().begin(); it != parser_.currencies().end(); it++) QLIB_ASSERT((*it)==config_.base_ccy, "blabla") boost::shared_ptr<LMM> lmm_ptr = config_.core_model_ptr_; nFactors_ = lmm_ptr->numOfFactors(); } boost::shared_ptr<LMMSimulation> LiborMarketModelMC::calibrate(const MarketData& mkt, const Sampler& sampler) const
  • 18.
    { boost::shared_ptr<LMM> lmm_ptr =config_.core_model_ptr_; int nLibors = lmm_ptr->numOfLibors(); Vector initialStates(nLibors); const std::vector<double>& initSt = config_.core_model_ptr_- >getInitialDates(); for (int i = 0; i < nLibors; i++) initialStates[i] = initSt[i]; for (std::map<Currency, size_t>::const_iterator it = ccy_factor.begin(); it != ccy_factor.end(); ++it) QLIB_ASSERT(!((*it).first == config_.base_ccy), "blabla"); const boost::shared_ptr<const LMMCovarianceStructure>& lmmCovarianceStructure = lmm_ptr->getLMMCovarianceStructure(); std::vector<Date> firstDate = lmmCovarianceStructure->getDates(); std::vector<Date> datesUnion(firstDate.size()); std::vector<Date>::iterator itDates; std::vector<Date> lmm_grid = firstDate; std::vector<std::size_t> obs_idx; std::set<Date>::const_iterator obs_date_it = parser_.observationDates().begin(); while ((obs_date_it != parser_.observationDates().end()) && (*obs_date_it <= datetime_to_date(mkt.getPricingDate()))) ++obs_date_it; datesUnion.resize(firstDate.size() + std::distance(obs_date_it, parser_.observationDates().end())); itDates = std::set_union (firstDate.begin(), firstDate.end(), obs_date_it, parser_.observationDates().end(), datesUnion.begin()); datesUnion.resize(itDates - datesUnion.begin()); std::vector<Date>::iterator mc_grid_it = datesUnion.begin(); size_t obs_pos = 0; for (std::set<Date>::const_iterator it = obs_date_it; it != parser_.observationDates().end(); it++) { while ((mc_grid_it != datesUnion.end()) && (*mc_grid_it < *it)) { ++mc_grid_it; ++obs_pos; } obs_idx.push_back(obs_pos); } std::vector<double> mc_time_grid(datesUnion.size()); Vector mc_time_grid_vec(datesUnion.size()); for (size_t i = 0; i < datesUnion.size(); i++) {
  • 19.
    boost::gregorian::days dd =datesUnion[i] - datetime_to_date(mkt.getPricingDate()); mc_time_grid[i] = dd.days() / num_days; mc_time_grid_vec[i] = mc_time_grid[i]; } std::map<Date, size_t> date_to_step; size_t step = 0; for (std::vector<Date>::iterator it = datesUnion.begin(); it != datesUnion.end(); ++it) date_to_step.insert(std::make_pair(*it, step++)); size_t nFactorsTotal = config_.use_stoch_vol_ ? nFactors_ + 1 : nFactors_; CovarianceStructure cov_struct(nFactorsTotal, mc_time_grid.size()); for (size_t i = 0; i < mc_time_grid.size(); ++i) { double dt = (i == 0) ? 1.0 : mc_time_grid[i] - mc_time_grid[i - 1]; Matrix cov(cov_struct.dimension(), cov_struct.dimension(), 0.0); for (size_t j = 0; j < nFactorsTotal; ++j) { cov(j, j) = dt; for (size_t k = 0; k < j; ++k) cov(j, k) = cov(k, j) = 0.0; } cov_struct.set_covariance(i, cov); } // create Process process_type lmm_process(initialStates, cov_struct, nFactorsTotal, mc_time_grid_vec, obs_idx, config_.use_brownian_bridge_, config_.use_stoch_vol_); // create schemes lmm_scheme_ptr_type lmm_scheme = LMMSchemeFactory::getInstance().createScheme("LogEuler", lmmCovarianceStructure, nFactors_, config_.use_stoch_vol_, config_.use_predictor_corrector_); lmm_process.set_main_scheme(lmm_scheme); bool use_stoch_vol = config_.use_stoch_vol_; if (use_stoch_vol) { const boost::shared_ptr<const LMMStochasticVolatilityStructure>& lmmSVStructure = lmm_ptr- >getLMMStochasticVolatilityStructure(); std::vector<double> kappa = lmmSVStructure->getKappa(); std::vector<double> theta = lmmSVStructure->getTheta(); std::vector<double> sigma = lmmSVStructure->getSigma(); boost::shared_ptr<const TimeStructure> ts = lmmSVStructure- >getTimeStructure();
  • 20.
    lmm_stoch_vol_scheme_ptr_type lmm_sv_scheme = CIRSchemeFactory::getInstance().createScheme("QE",kappa, theta, sigma, ts, datesUnion); lmm_process.set_stoch_vol_scheme(lmm_sv_scheme); } boost::shared_ptr<simulation_type> simulation(new simulation_type(lmm_process, sampler.nbr_req())); for (AtomicParser::df_map_type::const_iterator it = parser_.dfRequests().begin(); it != parser_.dfRequests().end(); ++it) { const Date obs_date = (*it).first.first; if (obs_date > datetime_to_date(mkt.getPricingDate())) { simulation->handle_int_.set((*it).second, simulation- >atomic_observations_.size()); const Date sett_date = (*it).first.second.date(); Currency ccy = (*it).first.second.curveType().currency(); CurveType::curve_types crv_type = (*it).first.second.curveType().type(); size_t timeStep = date_to_step.find(obs_date)->second; size_t index = 0; std::vector<Date> mc_grid_past; size_t n_past_dates = timeStep + 2; for (size_t i = 0; i < n_past_dates; ++i) { mc_grid_past.push_back(datesUnion[i]); } boost::shared_ptr<Observation<Matrix>> obs(new AdjZCBondObservation(crv_type, mc_grid_past, sett_date, timeStep, index, boost::static_pointer_cast<IRBaseModel>(config_.core_model_ptr_))); simulation->atomic_observations_.push_back(obs); } } sampler.get_derived_observations(simulation->derived_observations_, simulation- >atomic_observations_.size(), simulation->handle_int_, mkt); return simulation; } } }
  • 21.
    /* LMMSchemeFactory class. Author: XavierCharvet charvetx@yahoo.fr Copyright LC Software 2015 */ #include "stdafx.h" #include "LMMSchemeFactory.h" #include <utility> namespace QLib { namespace MonteCarloEngine { LMMSchemeFactory::LMMSchemeFactory() { scheme_container_.insert(std::pair<std::string, create_scheme_func>("LogEuler", LMMSchemeHelper<LMMLogEulerScheme>::Create)); } LMMSchemeFactory & LMMSchemeFactory::getInstance() { static LMMSchemeFactory theFactory; return theFactory; } boost::shared_ptr<LMMSchemeBase> LMMSchemeFactory::createScheme(const std::string& scheme_id , const boost::shared_ptr<const LMMCovarianceStructure>& lmm_covariance_structure , size_t dimension, bool use_stoch_vol = false, bool use_pred_corr = false) { std::map<std::string, create_scheme_func>::const_iterator scheme_idx = scheme_container_.find(scheme_id); if (scheme_idx == scheme_container_.end()) QLIB_THROW("blabla"); return (*scheme_idx).second(lmm_covariance_structure, dimension, use_stoch_vol, use_pred_corr); } } }
  • 22.
    #include "stdafx.h" #include "LMMLogEulerScheme.h" namespaceQLib { namespace MonteCarloEngine { /*Vector LMMLogEulerScheme::evolve(const Vector& previousLiborsStates, const Vector& bmIncrements, double dt, double simDate, const Vector& initialLibors, double varianceAtTimeIndex) const { QLIB_ASSERT(bmIncrements.size() == factors_dimension(), "blabla"); int ltsTimeIndex = liborsTimeStructure_->getTimeIndex(simDate); double deltaT = liborsTimeStructure_->getTime(ltsTimeIndex + 1) - liborsTimeStructure_->getTime(ltsTimeIndex); double stochasticVarianceTermDrift = 1.0; double stochasticVolatilityTermDiffusion = 1.0; if (useStochasticVol_) { stochasticVarianceTermDrift *= 0.5 * (previousVariance_ + varianceAtTimeIndex); stochasticVolatilityTermDiffusion *= std::sqrt(stochasticVarianceTermDrift); } previousVariance_ = varianceAtTimeIndex; getLogShiftedLiborsDrift(ltsTimeIndex, previousLiborsStates, initialLibors, deltaT); for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++) { double driftOfLogShiftedLibor = drift_[liborIndex]; double initialLibor = initialLibors[liborIndex]; if (driftOfLogShiftedLibor != driftOfLogShiftedLibor) { nextLiborsStates_[liborIndex] = std::numeric_limits<double>::quiet_NaN(); increments_[liborIndex] = std::numeric_limits<double>::quiet_NaN(); } else { lmmCovarianceStructure_->getFactorLoadings(simDate, liborIndex, previousLiborsStates, factorLoadings_); double diffusionOfLogShiftedLibor = 0.0; for (int factorIndex = 0; factorIndex < nLiborsFactors_; factorIndex++) { double factorLoading = factorLoadings_[factorIndex];
  • 23.
    double brownianIncrement = bmIncrements[factorIndex]* std::sqrt(dt); diffusionOfLogShiftedLibor += factorLoading * brownianIncrement; } double increment = diffusionOfLogShiftedLibor * stochasticVolatilityTermDiffusion + dt * driftOfLogShiftedLibor * stochasticVarianceTermDrift; increments_[liborIndex] = increment; double skew = lmmCovarianceStructure_- >getSkew(ltsTimeIndex, liborIndex); nextLiborsStates_[liborIndex] = transform(previousLiborsStates[liborIndex], initialLibor, skew, increment); } } if (usePredictorCorrector_) { driftPreviousStates_ = drift_; getLogShiftedLiborsDrift(ltsTimeIndex, nextLiborsStates_, initialLibors, deltaT); for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++) { double driftOfLogShiftedLiborPreviousStates = driftPreviousStates_[liborIndex]; double driftOfLogShiftedLiborsNextStates = drift_[liborIndex]; double initialLibor = initialLibors[liborIndex]; double adjustment = (driftOfLogShiftedLiborsNextStates - driftOfLogShiftedLiborPreviousStates) * 0.5 * dt * stochasticVarianceTermDrift; double increment = increments_[liborIndex] + adjustment; double skew = lmmCovarianceStructure_- >getSkew(ltsTimeIndex, liborIndex); nextLiborsStates_[liborIndex] = transform(previousLiborsStates[liborIndex], initialLibor, skew, increment); } } return nextLiborsStates_; } // LMMLogEulerScheme::evolve*/ // new evolve: no impact on regular pillars dates Vector LMMLogEulerScheme::evolve(const Vector& previousLiborsStates, const Vector& bmIncrements, double dt, double simDate, const Vector& initialLibors, double varianceAtTimeIndex) const { QLIB_ASSERT(bmIncrements.size() == factors_dimension(), "blabla");
  • 24.
    int ltsTimeIndex =liborsTimeStructure_->getTimeIndex(simDate); double deltaT = liborsTimeStructure_->getTime(ltsTimeIndex + 1) - liborsTimeStructure_->getTime(ltsTimeIndex); double stochasticVarianceTermDrift = 1.0; double stochasticVolatilityTermDiffusion = 1.0; if (useStochasticVol_) { stochasticVarianceTermDrift *= 0.5 * (previousVariance_ + varianceAtTimeIndex); stochasticVolatilityTermDiffusion *= std::sqrt(stochasticVarianceTermDrift); } previousVariance_ = varianceAtTimeIndex; getLogShiftedLiborsDrift(ltsTimeIndex, previousLiborsStates, initialLibors, deltaT); for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++) { double driftOfLogShiftedLibor = drift_[liborIndex]; double initialLibor = initialLibors[liborIndex]; if (driftOfLogShiftedLibor != driftOfLogShiftedLibor) { nextLiborsStates_[liborIndex] = std::numeric_limits<double>::quiet_NaN(); increments_[liborIndex] = std::numeric_limits<double>::quiet_NaN(); } else { if (liborIndex == ltsTimeIndex) { int appliedLiborIndex = (liborIndex == nLibors_ - 1) ? liborIndex : liborIndex + 1; double diffusionOfLogShiftedLibor = 0.0; double vol = lmmCovarianceStructure_- >getVol(liborIndex, appliedLiborIndex); double skew = lmmCovarianceStructure_- >getSkew(liborIndex, appliedLiborIndex); lmmCovarianceStructure_- >getVolWeightedFactorLoadings(vol, liborIndex, previousLiborsStates, factorLoadings_); for (int factorIndex = 0; factorIndex < nLiborsFactors_; factorIndex++) { double factorLoading = factorLoadings_[factorIndex]; double brownianIncrement = bmIncrements[factorIndex] * std::sqrt(dt); diffusionOfLogShiftedLibor += factorLoading * brownianIncrement; }
  • 25.
    double increment =diffusionOfLogShiftedLibor * stochasticVolatilityTermDiffusion + dt * driftOfLogShiftedLibor * stochasticVarianceTermDrift; increments_[liborIndex] = increment; // double skew = lmmCovarianceStructure_- >getSkew(ltsTimeIndex, liborIndex); nextLiborsStates_[liborIndex] = transform(previousLiborsStates[liborIndex], initialLibor, skew, increment); } else { lmmCovarianceStructure_- >getFactorLoadings(simDate, liborIndex, previousLiborsStates, factorLoadings_); double diffusionOfLogShiftedLibor = 0.0; for (int factorIndex = 0; factorIndex < nLiborsFactors_; factorIndex++) { double factorLoading = factorLoadings_[factorIndex]; double brownianIncrement = bmIncrements[factorIndex] * std::sqrt(dt); diffusionOfLogShiftedLibor += factorLoading * brownianIncrement; } double increment = diffusionOfLogShiftedLibor * stochasticVolatilityTermDiffusion + dt * driftOfLogShiftedLibor * stochasticVarianceTermDrift; increments_[liborIndex] = increment; double skew = lmmCovarianceStructure_- >getSkew(ltsTimeIndex, liborIndex); nextLiborsStates_[liborIndex] = transform(previousLiborsStates[liborIndex], initialLibor, skew, increment); } } } if (usePredictorCorrector_) { driftPreviousStates_ = drift_; getLogShiftedLiborsDrift(ltsTimeIndex, nextLiborsStates_, initialLibors, deltaT); for (int liborIndex = 0; liborIndex < nLibors_; liborIndex++) { double driftOfLogShiftedLiborPreviousStates = driftPreviousStates_[liborIndex]; double driftOfLogShiftedLiborsNextStates = drift_[liborIndex]; double initialLibor = initialLibors[liborIndex]; double adjustment = (driftOfLogShiftedLiborsNextStates - driftOfLogShiftedLiborPreviousStates) * 0.5 * dt * stochasticVarianceTermDrift; double increment = increments_[liborIndex] + adjustment;
  • 26.
    double skew =lmmCovarianceStructure_- >getSkew(ltsTimeIndex, liborIndex); nextLiborsStates_[liborIndex] = transform(previousLiborsStates[liborIndex], initialLibor, skew, increment); } } return nextLiborsStates_; } // LMMLogEulerScheme::evolve /*void LMMLogEulerScheme::getLogShiftedLiborsDrift(int ltsTimeIndex, const Vector &liborsAtTimeIndex, const Vector& initialLibors, double deltaT) const { int firstLiborIndex = ltsTimeIndex + 1; for (int liborIndex = 0; liborIndex < firstLiborIndex; ++liborIndex) drift_[liborIndex] = std::numeric_limits<double>::quiet_NaN(); for (int liborIndex = firstLiborIndex; liborIndex < nLibors_; ++liborIndex) drift_[liborIndex] = 0.0; for (int factorIndex = 0; factorIndex < nLiborsFactors_; ++factorIndex) covarianceTerms_[factorIndex] = 0.0; for (int liborIndex = firstLiborIndex; liborIndex < nLibors_; liborIndex++) { double libor = liborsAtTimeIndex[liborIndex]; double initialLibor = initialLibors[liborIndex]; double skew = lmmCovarianceStructure_->getSkew(ltsTimeIndex, liborIndex); double temp = deltaT * (skew * libor + (1 - skew) * initialLibor) / (1.0 + deltaT * libor) / skew; lmmCovarianceStructure_->getFactorLoadings(ltsTimeIndex, liborIndex, liborsAtTimeIndex, factorLoadings_); for (int factorIndex = 0; factorIndex < nLiborsFactors_; factorIndex++) { covarianceTerms_[factorIndex] += temp * factorLoadings_[factorIndex]; drift_[liborIndex] += covarianceTerms_[factorIndex] * factorLoadings_[factorIndex]; } double variance = lmmCovarianceStructure_- >getCovariance(ltsTimeIndex, liborIndex, liborIndex, liborsAtTimeIndex, factorLoadings1_, factorLoadings2_); drift_[liborIndex] -= 0.5 * variance;
  • 27.
    } } // LMMLogEulerScheme::getLogShiftedLiborsDrift*/ //new getLSLD: no impact on relular pillars dates void LMMLogEulerScheme::getLogShiftedLiborsDrift(int ltsTimeIndex, const Vector &liborsAtTimeIndex, const Vector& initialLibors, double deltaT) const { int firstLiborIndex = ltsTimeIndex + 1; for (int liborIndex = 0; liborIndex < firstLiborIndex; ++liborIndex) drift_[liborIndex] = std::numeric_limits<double>::quiet_NaN(); for (int liborIndex = ltsTimeIndex; liborIndex < nLibors_; ++liborIndex) drift_[liborIndex] = 0.0; for (int factorIndex = 0; factorIndex < nLiborsFactors_; ++factorIndex) covarianceTerms_[factorIndex] = 0.0; for (int liborIndex = firstLiborIndex; liborIndex < nLibors_; liborIndex++) { double libor = liborsAtTimeIndex[liborIndex]; double initialLibor = initialLibors[liborIndex]; double skew = lmmCovarianceStructure_->getSkew(ltsTimeIndex, liborIndex); double temp = deltaT * (skew * libor + (1 - skew) * initialLibor) / (1.0 + deltaT * libor) / skew; lmmCovarianceStructure_->getFactorLoadings(ltsTimeIndex, liborIndex, liborsAtTimeIndex, factorLoadings_); for (int factorIndex = 0; factorIndex < nLiborsFactors_; factorIndex++) { covarianceTerms_[factorIndex] += temp * factorLoadings_[factorIndex]; drift_[liborIndex] += covarianceTerms_[factorIndex] * factorLoadings_[factorIndex]; } double variance = lmmCovarianceStructure_- >getCovariance(ltsTimeIndex, liborIndex, liborIndex, liborsAtTimeIndex, factorLoadings1_, factorLoadings2_); drift_[liborIndex] -= 0.5 * variance; // drift_[liborIndex]; if (liborIndex == firstLiborIndex) drift_[firstLiborIndex - 1] = drift_[liborIndex]; } } // LMMLogEulerScheme::getLogShiftedLiborsDrift
  • 28.
    double LMMLogEulerScheme::transform(double previousLibor,double initialLibor, double skew, double increment) const { double libor; libor = (skew * previousLibor + (1 - skew) * initialLibor) * std::exp(increment); libor -= (1 - skew) * initialLibor; libor /= skew; return libor; } } } /* CirQEScheme class. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 Framework inspired from finmath (Java library) http://finmath.net/ */ #include "stdafx.h" #include "CirQEScheme.h" namespace QLib { namespace MonteCarloEngine { const double to_year_frac = 1.0 / 365; const double QE_psi = 1.5; CirQEScheme::CirQEScheme(const std::vector<double>& kappa, const std::vector<double>& theta, const std::vector<double>& sigma, const boost::shared_ptr<const TimeStructure>& ts, const std::vector<Date>& time_grid) { size_t nSteps = ts->getSize(); QLIB_ASSERT(kappa.size() == nSteps+1 && theta.size() == nSteps+1 && sigma.size() == nSteps+1, "blabla"); ts_ = ts;
  • 29.
    time_grid_ = time_grid; theta_= theta; sigma_ = sigma; kappa_ = kappa; } Vector CirQEScheme::evolve(const Vector& previousValueV, const Vector& bmIncrementsV, size_t timeIndex) const { Date simDate = time_grid_[timeIndex]; int tsTimeIndex = ts_->getTimeIndex(simDate); double previousValue = previousValueV[0]; double bmIncrement = bmIncrementsV[0]; double dt = nbDays(time_grid_[timeIndex-1], time_grid_[timeIndex]) * to_year_frac; //double sqrtdt = std::sqrt(dt); //bmIncrement *= sqrtdt; double theta = theta_[tsTimeIndex]; double kappa = kappa_[tsTimeIndex]; double sigma = sigma_[tsTimeIndex]; double tmp = std::exp(-kappa * dt); double m = theta + (previousValue - theta) * tmp; double s2 = previousValue * sigma * sigma * tmp / kappa * (1 - tmp) + theta * sigma * sigma / (2 * kappa) * (1 - tmp) * (1 - tmp); double psi = s2 / (m*m); Vector variance(1); if (psi <= QE_psi) { double b = std::sqrt(2 / psi - 1 + std::sqrt(2 / psi * (2 / psi - 1))); double a = m / (1 + b*b); variance[0] = a * (b + bmIncrement) * (b + bmIncrement); } else { double p = (psi - 1) * (psi + 1); double beta = (1 - p) / m; double u = boost::math::cdf(norm_,bmIncrement); variance[0] = (u <= p) ? 0 : 1 / beta * std::log((1-p)/(1-u)); } return variance; } } } /*
  • 30.
    VolatilityModel class. Factor reductionperformed using Eigen library. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 Framework inspired from finmath (Java library) http://finmath.net/ */ #pragma once #include "../../Utils/Vector.h" #include "../../DateTime/TimeStructure.h" #include <boostshared_ptr.hpp> namespace QLib { class LMMVolatilityStructure { public: LMMVolatilityStructure(double volatility, const boost::shared_ptr<const TimeStructure>& lts, double skew = 0.0); double getVolatilityLogSpace(int timeIdx, int liborIdx) const; double getSkew(size_t timeIdx, size_t liborIdx) const { return skew_; } const boost::shared_ptr<const TimeStructure>& getLiborsTimeStructure() const { return liborsTimeStructure_; } private: double volatility_; double skew_; boost::shared_ptr<const TimeStructure> liborsTimeStructure_; }; /* LMMStochasticVolatilityModel class. Factor reduction performed using Eigen library. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 Framework inspired from finmath (Java library) http://finmath.net/
  • 31.
    */ #pragma once #include "../../Utils/Vector.h" #include"../../DateTime/TimeStructure.h" #include <boostshared_ptr.hpp> namespace QLib { class LMMStochasticVolatilityStructure { public: LMMStochasticVolatilityStructure(double theta, double kappa, double sigma, const boost::shared_ptr<const TimeStructure>& lts); const std::vector<double>& getKappa() const { return kappa_; } const std::vector<double>& getTheta() const { return theta_; } const std::vector<double>& getSigma() const { return sigma_; } const boost::shared_ptr<const TimeStructure>& getTimeStructure() const { return timeStructure_; } private: std::vector<double> kappa_; std::vector<double> theta_; std::vector<double> sigma_; boost::shared_ptr<const TimeStructure> timeStructure_; }; } /* CovarianceModel class. Built from a volatility model along with a correlation model. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015
  • 32.
    Framework inspired fromfinmath (Java library) http://finmath.net/ */ #include "stdafx.h" #include "LMMCovarianceStructure.h" namespace QLib { LMMCovarianceStructure::LMMCovarianceStructure(const boost::shared_ptr<const LMMVolatilityStructure>& vs, const boost::shared_ptr<const LMMCorrelationStructure>& cs) { volatilityStructure_ = vs; correlationStructure_ = cs; boost::shared_ptr<const TimeStructure> lts_vol = vs- >getLiborsTimeStructure(); boost::shared_ptr<const TimeStructure> lts_corr = cs- >getLiborsTimeStructure(); QLIB_ASSERT(lts_vol == lts_corr, "blabla"); liborsTimeStructure_ = lts_vol; nFactors_ = cs->numOfFactors(); } void LMMCovarianceStructure::getFactorLoadings(int timeIdx, int liborIdx, const Vector& liborsAtTimeIndex, Vector& factorLoadings) const { int fls = factorLoadings.size(); QLIB_ASSERT(fls == nFactors_, "blabla"); double volatilityLS = volatilityStructure_->getVolatilityLogSpace(timeIdx, liborIdx); for (int factorIdx = 0; factorIdx < nFactors_; factorIdx++) factorLoadings[factorIdx] = volatilityLS * correlationStructure_- >getFactorLoading(liborIdx, factorIdx); } void LMMCovarianceStructure::getFactorLoadings(double time, int liborIdx, const Vector& liborsAtTimeIndex, Vector& factorLoadings) const { Date date_0 = liborsTimeStructure_->getDate(0); Date date = date_0 + DateDuration(static_cast<long>(365.0 * time)); int timeIndex = liborsTimeStructure_->getTimeIndex(date); getFactorLoadings(timeIndex, liborIdx, liborsAtTimeIndex, factorLoadings); } double LMMCovarianceStructure::getCovariance(int timeIdx, int liborIdx_1, int liborIdx_2, const Vector& liborsAtTimeIndex, Vector& factorLoadings_1, Vector& factorLoadings_2) const { int fls1 = factorLoadings_1.size(); int fls2 = factorLoadings_2.size();
  • 33.
    QLIB_ASSERT(fls1 == fls2== nFactors_, "blabla"); getFactorLoadings(timeIdx, liborIdx_1, liborsAtTimeIndex, factorLoadings_1); getFactorLoadings(timeIdx, liborIdx_2, liborsAtTimeIndex, factorLoadings_2); double result = 0.0; for (size_t i = 0; i < factorLoadings_1.size(); i++) result += factorLoadings_1[i] * factorLoadings_2[i]; return result; } double LMMCovarianceStructure::getVol(int timeIndex, int liborIndex) const { return volatilityStructure_->getVolatilityLogSpace(timeIndex, liborIndex); } void LMMCovarianceStructure::getVolWeightedFactorLoadings(double vol, int liborIndex, const Vector& liborsAtTimeIndex, Vector& factorLoadings) const { int fls = factorLoadings.size(); QLIB_ASSERT(fls == nFactors_, "blabla"); for (int factorIndex = 0; factorIndex < nFactors_; factorIndex++) factorLoadings[factorIndex] = vol * correlationStructure_- >getFactorLoading(liborIndex, factorIndex); } } /* CorrelationModel class. Factor reduction performed using Eigen library. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 Framework inspired from finmath (Java library) http://finmath.net/ */ #include "stdafx.h" #include "LMMCorrelationStructure.h" // #include "../../Math/LinearAlgebra/SpectralDecomposition.h" #include <cmath>
  • 34.
    namespace QLib { LMMCorrelationStructure::LMMCorrelationStructure(double rho,double lambda, int nFactors, const boost::shared_ptr<const TimeStructure>& lts) { rho_ = rho; lambda_ = lambda; nFactors_ = nFactors; liborsTimeStructure_ = lts; int nLibors = lts->getSize(); correlationMatrix_.resize(nLibors, nLibors); factorMatrix_.resize(nLibors, nFactors); init(); } void LMMCorrelationStructure::init() { int n = liborsTimeStructure_->getSize(); for (int i = 0; i < n; ++i) { correlationMatrix_(i, i) = 1.0; for (int j = i + 1; j < n; ++j) { double ti = liborsTimeStructure_->getTime(i); double tj = liborsTimeStructure_->getTime(j); double correl = rho_ + (1 - rho_)*std::exp(-lambda_ * std::abs(ti - tj)); correlationMatrix_(i, j) = correl; correlationMatrix_(j, i) = correl; } } doFactorReduction(); } void LMMCorrelationStructure::doFactorReduction() { // TO FIX int n = liborsTimeStructure_->getSize(); for (int i = 0; i < n; i++) factorMatrix_(i, 0) = 1; /*Matrix eigenValues, eigenVectors; LinearAlgebra::getEigenSystem(correlationMatrix_, eigenVectors, eigenValues); int n = eigenValues.size1(); for (int col = 0; col < n; col++) { QLIB_ASSERT(eigenValues(col, 0) >= 0, "blabla"); double sqrtLambda = std::sqrt(eigenValues(col, 0)); for (int row = 0; row < n; row++) eigenVectors(row, col) *= sqrtLambda; } for (int col = 0; col < nFactors_; col++)
  • 35.
    for (int row= 0; row < n; row++) factorMatrix_(row, col) = eigenVectors(row, n - 1 - col); for (int row = 0; row < n; row++) { double norm = 0.0; for (int col = 0; col < nFactors_; col++) norm += factorMatrix_(row, col) * factorMatrix_(row, col); norm = std::sqrt(norm); for (int col = 0; col < nFactors_; col++) factorMatrix_(row, col) /= norm; }*/ } double LMMCorrelationStructure::getFactorLoading(int liborIndex, int factorIndex) const { double factorLoading = factorMatrix_(liborIndex, factorIndex); return factorLoading; } } /* LiborMarketModel class. Built from a covariance model. Does not contain the Monte-Carlo parameters information. Does not contain the liborProcess neither. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 Framework inspired from finmath (Java library) http://finmath.net/ */ #include "stdafx.h" #include <cmath> #include "../LMM/LiborMarketModel.h" #include <boost/range/algorithm_ext/is_sorted.hpp> #include "../../Math/Interpolation.h" namespace QLib { void LMM::initialize(const std::vector<Date> & dates, const std::vector<double>& vols) { QLIB_ASSERT(pricingDate_ == dates[0], "blabla");
  • 36.
    QLIB_ASSERT(boost::is_sorted(dates), "blabla"); LMM::precompute(); } void LMM::precompute() { size_tnLibors = numOfLibors(); DateTime todayDate = indexCurve_->valueDate(); for (size_t liborIndex = 0; liborIndex < nLibors; liborIndex++) { double startTime = liborsTimeStructure_->getTime(liborIndex) * 365.0; DateTime startDate = DateTimeAddDiff(startTime, todayDate); double deltaT = liborsTimeStructure_->getTimeStep(liborIndex) * 365.0; DateTime endDate = DateTimeAddDiff(startTime + deltaT, todayDate); double df = indexCurve_->df(startDate, endDate); double initialForwardRate = (1.0 / df - 1.0) / deltaT * 365.0; initialStates_.push_back(initialForwardRate); } } double LMM::zcb(CurveType::curve_types crv_type, const Date& t, const Date& T, const Vector& states) const { QLIB_ASSERT(t <= T, "blabla"); if (t == T) return 1.0; double lastLiborState = crv_type == CurveType::discount ? states[states.size() - 1] : states[states.size() - 1] + deterministicBasis_; Date firstDate = liborsTimeStructure_->getDate(0); double tyf = YearFraction(firstDate, t); double Tyf = YearFraction(firstDate, T); int firstLiborIndex = liborsTimeStructure_->getTimeIndex(t); bool is_tALiborDate = true; if (liborsTimeStructure_->getDate(firstLiborIndex) < t) { is_tALiborDate = false; firstLiborIndex += 1; } int lastLiborIndex = liborsTimeStructure_->getTimeIndex(T); if (liborsTimeStructure_->getDate(lastLiborIndex) == T) lastLiborIndex -= 1; double zcb = 1.0; for (int liborIndex = firstLiborIndex; liborIndex < lastLiborIndex; liborIndex++) {
  • 37.
    double libor =CurveType::discount ? states[liborIndex] : states[liborIndex] + deterministicBasis_; zcb *= 1.0 / (1.0 + liborsTimeStructure_->getTimeStep(liborIndex)* libor); } double libor = states[firstLiborIndex]; if (libor != libor) libor = lastLiborState; if (!is_tALiborDate) { double tstart1 = liborsTimeStructure_->getTime(firstLiborIndex - 1); double tstart2 = liborsTimeStructure_->getTime(firstLiborIndex); double period = tstart2 - tstart1; zcb *= 1.0 / (1.0 + period * libor); period = tyf - tstart1; zcb *= (1.0 + period * libor); } if (firstLiborIndex > lastLiborIndex) { double tend1 = liborsTimeStructure_->getTime(lastLiborIndex); double tend2 = liborsTimeStructure_->getTime(lastLiborIndex+1); libor = lastLiborState; double period = tend2 - tend1; zcb *= (1.0 + period * libor); period = Tyf - tend1; zcb /= (1.0 + period * libor); } else { double t1 = liborsTimeStructure_->getTime(lastLiborIndex); libor = CurveType::discount ? states[lastLiborIndex] : states[lastLiborIndex] + deterministicBasis_; double period = Tyf - t1; zcb *= 1.0 / (1.0 + period * libor); } return zcb; } /* TimeStructure class. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 Framework inspired from finmath (Java library) http://finmath.net/ */
  • 38.
    #include "stdafx.h" #include "TimeStructure.h" namespaceQLib { TimeStructure::TimeStructure(const Date& initialDate, const std::string& tenor, size_t nTimeSteps) { dates_.push_back(initialDate); Date lastDate = initialDate; for (size_t timeIndex = 0; timeIndex < nTimeSteps; timeIndex++) { Date nD = AddTenor(lastDate, tenor); Date nextDate = nD; dates_.push_back(nextDate); lastDate = nD; } } size_t TimeStructure::getTimeIndex(const Date& date) const { std::vector<Date>::const_iterator mIt = std::find(dates_.begin(), dates_.end(), date); if (mIt != dates_.end()) return mIt - dates_.begin(); else { std::vector<Date>::const_iterator it = std::lower_bound(dates_.begin(), dates_.end(), date); if (it == dates_.begin()) return 0; else if (it == dates_.end()) return dates_.size() - 1; else return it - dates_.begin() - 1; } } size_t TimeStructure::getTimeIndex(double time) const { Date d1 = dates_[0]; Date d2 = d1 + DateDuration(static_cast<long>(time * 365.0)); return getTimeIndex(d2); } double TimeStructure::getTimeStep(size_t timeIndex) const { QLIB_ASSERT(timeIndex >= 0 && timeIndex < dates_.size(), "timeIndex must be between 0 and size of time line"); Date d1 = dates_[timeIndex]; Date d2 = dates_[timeIndex + 1]; double timeStep = YearFraction(d1, d2); return timeStep; }
  • 39.
    // TO FIX doubleTimeStructure::getTime(size_t timeIndex) const { QLIB_ASSERT(timeIndex >= 0 && timeIndex < dates_.size(), "timeIndex must be between 0 and size of time line"); Date d1 = dates_[0]; Date d2 = dates_[timeIndex]; double time = YearFraction(d1, d2); return time; } Date TimeStructure::getDate(size_t timeIndex) const { QLIB_ASSERT(timeIndex >= 0 && timeIndex < dates_.size(), "timeIndex must be between 0 and size of time line"); Date date = dates_[timeIndex]; return date; } } #include "stdafx.h" #include "AdjZCBondObservation.h" namespace QLib { namespace MonteCarloEngine { double AdjZCBondObservation::operator() (Matrix const& m) const { std::size_t n_dates = sim_dates_.size(); double numeraire = 1.0; size_t nLibors = m.size2(); Vector states(nLibors + 1); states[nLibors] = std::numeric_limits<double>::quiet_NaN(); const boost::shared_ptr<const TimeStructure>& lts = core_model_ptr_- >getLiborTimeStructure(); const std::vector<Date> & libor_dates = lts->getDates(); size_t idxStepLS = lts->getTimeIndex(sim_dates_[step_]); Date d = lts->getDate(idxStepLS); size_t idx = sdts_.getTimeIndex(d); numeraire *= 1.0 / core_model_ptr_->df(crv_type_, libor_dates[0], libor_dates[1]);
  • 40.
    double lastLiborState =m(idx, index_ * nLibors + idxStepLS); states[nLibors] = lastLiborState; if (n_dates > 2) { numeraire = 1.0; for (size_t i = 0; i < idxStepLS + 1; i++) { Date d = lts->getDate(i); size_t idx = sdts_.getTimeIndex(d); for (size_t liborIndex = 0; liborIndex < nLibors; liborIndex++) states[liborIndex] = m(idx, index_ * nLibors + liborIndex); numeraire = numeraire * (1.0 / core_model_ptr_- >zcb(crv_type_, libor_dates[i], libor_dates[i + 1], states)); } for (size_t liborIndex = 0; liborIndex < nLibors; liborIndex++) states[liborIndex] = m(step_, index_ * nLibors + liborIndex); numeraire *= core_model_ptr_->zcb(crv_type_, sim_dates_[step_], libor_dates[idxStepLS + 1], states); } return core_model_ptr_->zcb(crv_type_, sim_dates_[step_], T_, states) / numeraire; } } } /* SwapRateObservable class. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 */ #include "stdafx.h" #include "../../../MarketData/MarketData.h" #include "SwapRateObservable.h" #include "../Observation/SwapRateObservation.h" #include "DiscountFactorObservable.h" #include "Sampler.h" #include "../../../Core/Exception.h"
  • 41.
    namespace QLib { namespace MonteCarloEngine { SwapRateObservable::SwapRateObservable(CurveTypeconst& curve, const Date& start, std::string swapTenor) : start_(start), swapTenor_(swapTenor) { size_t n = swapTenor.length(); size_t nmr = boost::lexical_cast<size_t>(swapTenor.substr(0, n - 1)); nPeriods_ = nmr * 4; Date sd = start_; for (size_t i = 0; i < nPeriods_; i++) { Date nextDate = AddTenor(sd, "3M"); dcfs_[i] = YearFraction(sd, AddTenor(sd, "3M")); sd = nextDate; } } SwapRateObservable::~SwapRateObservable() {} std::string SwapRateObservable::description() const { std::stringstream ss; ss << curve_.currency().code() << "-" << swapTenor_; return ss.str(); } ObservableImpl::obs_ref_type SwapRateObservable::requestPrimeReferences(Sampler & sampler, Date date) const { // Date start = date + DateDuration(2); QLIB_ASSERT(date <= start_, "blabla"); obs_ref_type references(nPeriods_+1); references[0] = sampler.makeRequest(date, new DiscountFactorObservable(curve_, start_)); Date sd = start_; for (size_t i = 0; i < nPeriods_; i++) { Date nextDate = AddTenor(sd, "3M"); references[i+1] = sampler.makeRequest(date, new DiscountFactorObservable(curve_, nextDate)); sd = nextDate; } return references; } boost::shared_ptr < Observation<Vector>> SwapRateObservable::observation(ObservableImpl::obs_ref_type refs) const { return boost::shared_ptr < Observation<Vector>>(new SwapRateObservation(refs, dcfs_)); } double SwapRateObservable::fixing(const Date& date, const MarketData& market) const
  • 42.
    { /*Date d =AddTenor(start_, tenor_); std::stringstream oss; oss << "IR.FIXING." << curve_.currency().code() << "." << tenor_; return market.getFixing(oss.str(), date);*/ return 0.0; } } } /* IRCap class. Author: Xavier Charvet charvetx@yahoo.fr Copyright LC Software 2015 */ #include "stdafx.h" #include "IRSwaption.h" #include "../Observable/Sampler.h" #include "../Observation/HandleInterface.h" #include "../Observable/DiscountFactorObservable.h" #include "../Observable/SwapRateObservable.h" #include "../Observable/Observable.h" namespace QLib { namespace MonteCarloEngine { // two ways of doing the request: either we use the swap rate observable (one cash flow) or we use the forward Libor Observables // second option is multi-curve ready but we start with the first one for convenience CashFlows IRSwaption::request(Sampler & sampler) { CashFlows flows; flows.resize(1); rate_h_.resize(nPeriods_); size_t idx = 0; Date startDate = exerciseDate_ + DateDuration(2); for (size_t idx = 0; idx < nPeriods_; idx++) { Date paymentDate = AddTenor(startDate, "3M"); rate_h_[idx] = sampler.makeRequest(exerciseDate_, Observable(new DiscountFactorObservable(curve_, paymentDate)));
  • 43.
    dcfs_[idx] = YearFraction(startDate,paymentDate); startDate = paymentDate; ++idx; } rate_h_[nPeriods_] = sampler.makeRequest(exerciseDate_, Observable(new SwapRateObservable(curve_, startDate, swapTenor_))); return flows; } IRSwaption::calculator_type IRSwaption::create_calculator(const HandleInterface& hi) const { std::vector<ObservationHandle> obs_h(rate_h_.size()); for (size_t i = 0; i < rate_h_.size(); i++) obs_h[i] = hi(rate_h_[i]); return IRSwaptionCalculator(obs_h, notional_, strike_, dcfs_); } } }