BlackCalculator greeks does not match the formula in wiki

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

BlackCalculator greeks does not match the formula in wiki

SteveGe
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.

Reply | Threaded
Open this post in threaded view
|

Re: BlackCalculator greeks does not match the formula in wiki

Luigi Ballabio
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
Reply | Threaded
Open this post in threaded view
|

Re: BlackCalculator greeks does not match the formula in wiki

SteveGe
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
            );
        }
    }