Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MCDigitalOption pricer not working #2044

Open
lakshya-aga opened this issue Jul 29, 2024 · 7 comments
Open

MCDigitalOption pricer not working #2044

lakshya-aga opened this issue Jul 29, 2024 · 7 comments

Comments

@lakshya-aga
Copy link

I have 2 issues I am running into while using the example of MC pricing from test suite for binary call/put options.

  1. The premium calculated drops(rises) sharply at strike for put(call) option.
  2. 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.
Copy link

boring-cyborg bot commented Jul 29, 2024

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.

@lballabio
Copy link
Owner

Hi—may you post some code to reproduce the problem? Thanks!

@lakshya-aga
Copy link
Author

lakshya-aga commented Jul 30, 2024

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

@lakshya-aga
Copy link
Author

lakshya-aga commented Jul 30, 2024

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

@lakshya-aga
Copy link
Author

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.

Copy link
Contributor

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.

@github-actions github-actions bot added the stale label Sep 29, 2024
@lballabio lballabio removed the stale label Sep 29, 2024
@lballabio
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants