QuantLib
QuantLib copied to clipboard
MCDigitalOption pricer not working
I have 2 issues I am running into while using the example of MC pricing from test suite for binary call/put options.
- The premium calculated drops(rises) sharply at strike for put(call) option.
- The NPV changes the discount factor when I change number of timesteps. For example r=0.04 with a guaranteed payoff of $1 and 100 time steps in simulation would give an answer of ~0.9996 while with 1 time step would give ~0.96 when both have same time to expiry 1 year. Any help or insight is appreciated, thanks.
Thanks for posting! It might take a while before we look at your issue, so don't worry if there seems to be no feedback. We'll get to it.
Hi—may you post some code to reproduce the problem? Thanks!
Sure! Here is my function call parameter list
std::string alib_type = "c"; // Example: "C" for call option
Real underlying_fwd = 200; // current underlying forward price
Real strike = 100;
Real time_payments = 1;
Real time_exp = 1; // time to expiration (in years)
Real vol = 0.20;
Real discount_rate = 0.04;
Size timeSteps = 365;
Size numberOfPaths = 10000;
string sen_type = "p";
// Derived variables
if (sen_type == "P" || sen_type == "p")
{
Real npv = MCPricer(alib_type, underlying_fwd, strike, time_exp, vol, discount_rate, timeSteps, numberOfPaths, sen_type);
cout << underlying_fwd << " " << npv << endl;
// return npv;
}
and my function:
Real MCPricer(std::string alib_type, Real underlying_fwd, Real strike, Real time_exp, Real vol, Real discount_rate, Size timeSteps, Size numberOfPaths, string sen_type)
{
Date today = Date().todaysDate();
Date maturityDate = today + Integer(time_exp * 365);
// Option payoff setup
Settings::instance().evaluationDate() = today;
ext::shared_ptr<StrikedTypePayoff> binaryOptPayoff;
if ((alib_type == "C") || (alib_type == "c"))
{
binaryOptPayoff =
boost::shared_ptr<CashOrNothingPayoff>(new CashOrNothingPayoff(Option::Type::Call, strike, 1.0));
}
else if ((alib_type == "P") || (alib_type == "p"))
{
binaryOptPayoff =
boost::shared_ptr<CashOrNothingPayoff>(new CashOrNothingPayoff(Option::Type::Put, strike, 1.0));
}
// Market data setup
Handle<Quote> underlyingQuote(ext::make_shared<SimpleQuote>(underlying_fwd));
Handle<YieldTermStructure> riskFreeRateCurve(ext::make_shared<FlatForward>(today, discount_rate, Actual365Fixed()));
Handle<BlackVolTermStructure> volatilityCurve(ext::make_shared<BlackConstantVol>(today, NullCalendar(), vol, Actual365Fixed()));
Handle<YieldTermStructure> qTermStructure(ext::make_shared<FlatForward>(today, 0.0, Actual365Fixed()));
ext::shared_ptr<BlackScholesMertonProcess> stochProcess(new BlackScholesMertonProcess(Handle<Quote>(underlyingQuote),
Handle<YieldTermStructure>(qTermStructure),
Handle<YieldTermStructure>(riskFreeRateCurve),
Handle<BlackVolTermStructure>(volatilityCurve)));
// Monte Carlo simulation setup
ext::shared_ptr<PricingEngine> mcengine1 = MakeMCDigitalEngine<LowDiscrepancy>(stochProcess)
.withBrownianBridge()
.withSteps(1)
.withSamples(numberOfPaths)
.withSeed(1);
ext::shared_ptr<PricingEngine> mcengine100 = MakeMCDigitalEngine<LowDiscrepancy>(stochProcess)
.withBrownianBridge()
.withSteps(100)
.withSamples(numberOfPaths)
.withSeed(1);
ext::shared_ptr<Exercise> americanExercise(new AmericanExercise(maturityDate - 1, maturityDate));
VanillaOption americanOption(binaryOptPayoff, americanExercise);
americanOption.setPricingEngine(mcengine1);
cout<<"With 1 steps "<<americanOption.NPV()<<endl;
americanOption.setPricingEngine(mcengine100);
cout<<"With 100 steps "<<americanOption.NPV()<<endl;
return americanOption.NPV();
return 0;
}
This will help you see the problem for different discounting with time steps
std::string alib_type = "c"; // Example: "C" for call option
Real underlying_fwd = 80; // current underlying forward price
Real strike = 100;
Real time_payments = 1;
Real time_exp = 1; // time to expiration (in years)
Real vol = 0.20;
Real discount_rate = 0.04;
Size timeSteps = 1;
Size numberOfPaths = 10000;
string sen_type = "p";
// Derived variables
for(; underlying_fwd<120; underlying_fwd++)
if (sen_type == "P" || sen_type == "p")
{
Real npv = MCPricer(alib_type, underlying_fwd, strike, time_exp, vol, discount_rate, timeSteps, numberOfPaths, sen_type);
cout << underlying_fwd << " " << npv << endl;
// return npv;
}
for reproducing the sharp change issue, please use this. You will get these premium values for each underlying price
90 0.622111
91 0.659774
92 0.695188
93 0.732924
94 0.772462
95 0.809621
96 0.847283
97 0.887357
98 0.926265
99 0.962766
100 0.9996
101 0.9996
102 0.9996
103 0.9996
104 0.9996
105 0.9996
106 0.9996
107 0.9996
108 0.9996
109 0.9996
110 0.9996
I have found the problem with the discounting. I had not set the flag for payoffAtExpiry to true in the constructor call for American Exercise. Premium values however, are still an issue.
This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.
Sorry for the long silence. What do you mean by "sharp" change? Do you think it drops too much? A value of 0.62 when the underlying is 90 means that the probability to touch the barrier at 100 within one year is around 62%, which looks more or less correct given your data and, for instance, the formulas at https://quant.stackexchange.com/questions/235.
If, instead, you mean that there's a discontinuity because it is flat above the strike and decreasing below the strike, that's because above the strike the option is exercised immediately for a constant payoff.
I might have misinterpreted you, though, so let me know if I'm answering the wrong question.
This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.
This issue was automatically closed because it has been stalled for two weeks with no further activity.