Hi all,
I am confused with the BlackCalculator. It is different from the formula in wiki. http://en.wikipedia.org/wiki/Greeks_(finance) i.e Delta is simply N(d). but the below code is so complex. the same for theta and gamma. I am wondering because I am not able to match the thetaPerDay with bloomberg. So I am not sure whether there are bug in the theta formula. but without understand the implementation, I am not able to modify it. Or is Bloomberg number simply wrong ? Real BlackCalculator::delta(Real spot) const { QL_REQUIRE(spot > 0.0, "positive spot value required: " << spot << " not allowed"); Real DforwardDs = forward_ / spot; Real temp = stdDev_*spot; Real DalphaDs = DalphaDd1_/temp; Real DbetaDs = DbetaDd2_/temp; Real temp2 = DalphaDs * forward_ + alpha_ * DforwardDs +DbetaDs * x_ + beta_ * DxDs_; return discount_ * temp2; } @Test public void theta() { double tightTolerance = 0.01; double strike = 6.2686; double spot = 6.2271; double forward = 6.2686; double time = maturity(186); double volatility = 2.5 / 100; double domesticDiscount = discountFactor(4.8, 184, 365.0); // 4.8% 186d RawFxOption rawFxOption = new RawFxOption(Put, strike, spot, forward, time, volatility, domesticDiscount); System.out.println(rawFxOption.price()); System.out.println(rawFxOption.decay()); System.out.println(rawFxOption.vega()); System.out.println(rawFxOption.gamma()); System.out.println(rawFxOption.rho()); //Assert.assertEquals(10719.47, rawFxOption.price() * 1E6, tightTolerance); } private double maturity(double days) { return days / 365.0; } private double discountFactor(double ratePercent, int days, double daysOfYear) { return 1.0 / (1 + ratePercent / 100.0 * days / daysOfYear); } Above code produce -0.00104534018 for theta per Year whereas bloomberg has -110.92 CNYper day for 1M USD 0.04357539169831881 -0.0011045340186808972 0.017429694068779016 0.2197069705587712 -1.5705728003534443 Please help me. Thanks in advance. |
Hello,
apologies for the delay. The code is indeed complex (personally I'm thinking it's *too* complex), but that's because it is generalized to a number of different payoffs. Let's match the code to the formulas in the wiki: the value is written in the code as discount * (forward * alpha + x * beta). Now: - forward is S exp((r-q)*T); - discount is exp(-rT); - x is the strike K; - alpha for a Call option is N(d1); - beta is -N(d2). Putting all those together, you get S exp(-qT) N(d1) - K exp(-rT) N(d2) which is the one you get in the wiki. To get the delta, you have to derive with respect to S. Now, the formula written as above is misleading, because it seems like you should just derive the first term, getting exp(-qT) N(d1), and discard the second term because S doesn't appear. But that's not correct, because d1 and d2 depend on S and you have to keep that into account. (To convince yourself of this, note that S doesn't appear in the delta exp(-qT) N(d1). If you were to take the derivative as above, the gamma would be null. But it's not, because d1 depends on S.) The code takes the dependencies into account, and in the general case calculates the derivative of discount * (forward * alpha + x * beta) as discount * (dForward/dS * alpha + forward * dAlpha/dS + dx/dS * beta + x * dBeta/dS) following the usual derivation rules. In the case of the plain call/put payoff, it just so happen that most terms cancel out and you're left with exp(-qT) N(d1). (The calculation is spelled out in full, for instance, in <http://www.econ-pol.unisi.it/fm10/greeksBS.pdf>). Now, for your theta calculation: it's difficult to say what's going wrong without knowing what happens inside the RawFxOption class you've written. But to begin with, I'd look at the calculation you're performing in the discountFactor() function. The rates in the Black-Scholes formula are continuously compounded, therefore the discount is exp(-rT) and not 1/(1+rT) as you coded. Making this change might take you closer to the Bloomberg value. Later, Luigi On Thu, Aug 28, 2014 at 11:51 AM, SteveGe <[hidden email]> wrote: > Hi all, > > I am confused with the BlackCalculator. It is different from the formula in > wiki. > http://en.wikipedia.org/wiki/Greeks_(finance) > > i.e Delta is simply N(d). > but the below code is so complex. the same for theta and gamma. I am > wondering because I am not able to match the thetaPerDay with bloomberg. So > I am not sure whether there are bug in the theta formula. > but without understand the implementation, I am not able to modify it. > Or is Bloomberg number simply wrong ? > Real BlackCalculator::delta(Real spot) const { > > QL_REQUIRE(spot > 0.0, "positive spot value required: " << > spot << " not allowed"); > > Real DforwardDs = forward_ / spot; > > Real temp = stdDev_*spot; > Real DalphaDs = DalphaDd1_/temp; > Real DbetaDs = DbetaDd2_/temp; > Real temp2 = DalphaDs * forward_ + alpha_ * DforwardDs > +DbetaDs * x_ + beta_ * DxDs_; > > return discount_ * temp2; > } > > > > @Test > public void theta() { > double tightTolerance = 0.01; > double strike = 6.2686; > double spot = 6.2271; > double forward = 6.2686; > double time = maturity(186); > double volatility = 2.5 / 100; > double domesticDiscount = discountFactor(4.8, 184, 365.0); // 4.8% > 186d > RawFxOption rawFxOption = new RawFxOption(Put, strike, spot, > forward, time, volatility, domesticDiscount); > System.out.println(rawFxOption.price()); > System.out.println(rawFxOption.decay()); > System.out.println(rawFxOption.vega()); > System.out.println(rawFxOption.gamma()); > System.out.println(rawFxOption.rho()); > //Assert.assertEquals(10719.47, rawFxOption.price() * 1E6, > tightTolerance); > } > > private double maturity(double days) { > return days / 365.0; > } > > private double discountFactor(double ratePercent, int days, double > daysOfYear) { > return 1.0 / (1 + ratePercent / 100.0 * days / daysOfYear); > } > > Above code produce -0.00104534018 for theta per Year whereas bloomberg has > -110.92 CNYper day for 1M USD > 0.04357539169831881 > -0.0011045340186808972 > 0.017429694068779016 > 0.2197069705587712 > -1.5705728003534443 > > Please help me. > > Thanks in advance. > > > > > > -- > View this message in context: http://quantlib.10058.n7.nabble.com/BlackCalculator-greeks-does-not-match-the-formula-in-wiki-tp15802.html > Sent from the quantlib-users mailing list archive at Nabble.com. > > ------------------------------------------------------------------------------ > Slashdot TV. > Video for Nerds. Stuff that matters. > http://tv.slashdot.org/ > _______________________________________________ > QuantLib-users mailing list > [hidden email] > https://lists.sourceforge.net/lists/listinfo/quantlib-users -- <https://implementingquantlib.blogspot.com> <https://twitter.com/lballabio> ------------------------------------------------------------------------------ Slashdot TV. Video for Nerds. Stuff that Matters. http://pubads.g.doubleclick.net/gampad/clk?id=160591471&iu=/4140/ostg.clktrk _______________________________________________ QuantLib-users mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/quantlib-users |
Thanks for the reply. I agree the implementation match with the wiki.
There is not much going on for RawFXOption class. it just wrap the BlackCalculator. the discountFactor won't make big difference, and all other greek value match perfect, only the theta way off. I notice Quantlib per day is divide by 365, not sure what value bloomberg divide. public class RawFxOption { public final Option.Type type; public final double strike; public final double spot; public final double forward; public final double maturity; public final double volatility; public final double domesticDiscount; private BlackCalculator blackCalculator; public RawFxOption(Option.Type type, double strike, double spot, double forward, double maturity, double volatility, double domesticDiscount) { this.type = type; this.strike = strike; this.spot = spot; this.forward = forward; this.maturity = maturity; this.volatility = volatility; this.domesticDiscount = domesticDiscount; } public RawFxOption inverse() { Option.Type inverseType = type == (Call) ? Put : Call; return new RawFxOption(inverseType, 1.0 / strike, 1.0 / spot, 1.0 / forward, maturity, volatility, foreignDiscount()); } public double price() { createCalculator(); return blackCalculator.value(); } public double delta() { createCalculator(); return blackCalculator.delta(spot); } public double forwardDelta() { return delta() / foreignDiscount(); } public double gamma() { createCalculator(); return blackCalculator.gamma(spot) * spot / 100.0; } public double vega() { createCalculator(); return blackCalculator.vega(maturity) / 100.0; } public double rho() { createCalculator(); return blackCalculator.rho(maturity); } public double phi() { createCalculator(); return blackCalculator.dividendRho(maturity); } public double thetaPerDay() { createCalculator(); return blackCalculator.thetaPerDay(spot,maturity); } public double decay() { createCalculator(); return blackCalculator.theta(spot, maturity); } public double breakEven() { if (type == Option.Type.Call) { return strike - price(); } return strike - price(); } private double foreignDiscount() { return forward / spot * domesticDiscount; } private void createCalculator() { if (blackCalculator == null) { blackCalculator = new BlackCalculator(new PlainVanillaPayoff(type, strike), forward, volatility * Math.sqrt(maturity), domesticDiscount ); } } |
Free forum by Nabble | Edit this page |