public override void calculate() { DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); Calendar volcal = process_.blackVolatility().link.calendar(); double s0 = process_.stateVariable().link.value(); Utils.QL_REQUIRE(s0 > 0.0, () => "negative or null underlying given"); double v = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), s0); Date maturityDate = arguments_.exercise.lastDate(); double r = process_.riskFreeRate().link.zeroRate(maturityDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).value(); double q = process_.dividendYield().link.zeroRate(maturityDate, divdc, Compounding.Continuous, Frequency.NoFrequency).value(); Date referenceDate = process_.riskFreeRate().link.referenceDate(); // binomial trees with constant coefficient Handle <YieldTermStructure> flatRiskFree = new Handle <YieldTermStructure>(new FlatForward(referenceDate, r, rfdc)); Handle <YieldTermStructure> flatDividends = new Handle <YieldTermStructure>(new FlatForward(referenceDate, q, divdc)); Handle <BlackVolTermStructure> flatVol = new Handle <BlackVolTermStructure>(new BlackConstantVol(referenceDate, volcal, v, voldc)); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); double maturity = rfdc.yearFraction(referenceDate, maturityDate); StochasticProcess1D bs = new GeneralizedBlackScholesProcess(process_.stateVariable(), flatDividends, flatRiskFree, flatVol); // correct timesteps to ensure a (local) minimum, using Boyle and Lau // approach. See Journal of Derivatives, 1/1994, // "Bumping up against the barrier with the binomial method" // Note: this approach works only for CoxRossRubinstein lattices, so // is disabled if T is not a CoxRossRubinstein or derived from it. int optimum_steps = timeSteps_; if (maxTimeSteps_ > timeSteps_ && s0 > 0 && arguments_.barrier > 0) // boost::is_base_of<CoxRossRubinstein, T>::value && { double divisor; if (s0 > arguments_.barrier) { divisor = Math.Pow(Math.Log(s0 / arguments_.barrier.Value), 2); } else { divisor = Math.Pow(Math.Log(arguments_.barrier.Value / s0), 2); } if (!Utils.close(divisor, 0)) { for (int i = 1; i < timeSteps_; ++i) { int optimum = (int)((i * i * v * v * maturity) / divisor); if (timeSteps_ < optimum) { optimum_steps = optimum; break; // found first minimum with iterations>=timesteps } } } if (optimum_steps > maxTimeSteps_) { optimum_steps = maxTimeSteps_; // too high, limit } } TimeGrid grid = new TimeGrid(maturity, optimum_steps); ITree tree = getTree_(bs, maturity, optimum_steps, payoff.strike()); BlackScholesLattice <ITree> lattice = new BlackScholesLattice <ITree>(tree, r, maturity, optimum_steps); DiscretizedAsset option = getAsset_(arguments_, process_, grid); option.initialize(lattice, maturity); // Partial derivatives calculated from various points in the // binomial tree // (see J.C.Hull, "Options, Futures and other derivatives", 6th edition, pp 397/398) // Rollback to third-last step, and get underlying prices (s2) & // option values (p2) at this point option.rollback(grid[2]); Vector va2 = new Vector(option.values()); Utils.QL_REQUIRE(va2.size() == 3, () => "Expect 3 nodes in grid at second step"); double p2u = va2[2]; // up double p2m = va2[1]; // mid double p2d = va2[0]; // down (low) double s2u = lattice.underlying(2, 2); // up price double s2m = lattice.underlying(2, 1); // middle price double s2d = lattice.underlying(2, 0); // down (low) price // calculate gamma by taking the first derivate of the two deltas double delta2u = (p2u - p2m) / (s2u - s2m); double delta2d = (p2m - p2d) / (s2m - s2d); double gamma = (delta2u - delta2d) / ((s2u - s2d) / 2); // Rollback to second-last step, and get option values (p1) at // this point option.rollback(grid[1]); Vector va = new Vector(option.values()); Utils.QL_REQUIRE(va.size() == 2, () => "Expect 2 nodes in grid at first step"); double p1u = va[1]; double p1d = va[0]; double s1u = lattice.underlying(1, 1); // up (high) price double s1d = lattice.underlying(1, 0); // down (low) price double delta = (p1u - p1d) / (s1u - s1d); // Finally, rollback to t=0 option.rollback(0.0); double p0 = option.presentValue(); // Store results results_.value = p0; results_.delta = delta; results_.gamma = gamma; // theta can be approximated by calculating the numerical derivative // between mid value at third-last step and at t0. The underlying price // is the same, only time varies. results_.theta = (p2m - p0) / grid[2]; }
public override void calculate() { DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); Calendar volcal = process_.blackVolatility().link.calendar(); double s0 = process_.stateVariable().link.value(); if (!(s0 > 0.0)) { throw new ApplicationException("negative or null underlying given"); } double v = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), s0); Date maturityDate = arguments_.exercise.lastDate(); double r = process_.riskFreeRate().link.zeroRate(maturityDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate(); double q = process_.dividendYield().link.zeroRate(maturityDate, divdc, Compounding.Continuous, Frequency.NoFrequency).rate(); Date referenceDate = process_.riskFreeRate().link.referenceDate(); // binomial trees with constant coefficient var flatRiskFree = new Handle <YieldTermStructure>(new FlatForward(referenceDate, r, rfdc)); var flatDividends = new Handle <YieldTermStructure>(new FlatForward(referenceDate, q, divdc)); var flatVol = new Handle <BlackVolTermStructure>(new BlackConstantVol(referenceDate, volcal, v, voldc)); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; if (payoff == null) { throw new ApplicationException("non-plain payoff given"); } double maturity = rfdc.yearFraction(referenceDate, maturityDate); StochasticProcess1D bs = new GeneralizedBlackScholesProcess(process_.stateVariable(), flatDividends, flatRiskFree, flatVol); TimeGrid grid = new TimeGrid(maturity, timeSteps_); T tree = new T().factory(bs, maturity, timeSteps_, payoff.strike()); BlackScholesLattice <T> lattice = new BlackScholesLattice <T>(tree, r, maturity, timeSteps_); DiscretizedVanillaOption option = new DiscretizedVanillaOption(arguments_, process_, grid); option.initialize(lattice, maturity); // Partial derivatives calculated from various points in the // binomial tree (Odegaard) // Rollback to third-last step, and get underlying price (s2) & // option values (p2) at this point option.rollback(grid[2]); Vector va2 = new Vector(option.values()); if (!(va2.size() == 3)) { throw new ApplicationException("Expect 3 nodes in grid at second step"); } double p2h = va2[2]; // high-price double s2 = lattice.underlying(2, 2); // high price // Rollback to second-last step, and get option value (p1) at // this point option.rollback(grid[1]); Vector va = new Vector(option.values()); if (!(va.size() == 2)) { throw new ApplicationException("Expect 2 nodes in grid at first step"); } double p1 = va[1]; // Finally, rollback to t=0 option.rollback(0.0); double p0 = option.presentValue(); double s1 = lattice.underlying(1, 1); // Calculate partial derivatives double delta0 = (p1 - p0) / (s1 - s0); // dp/ds double delta1 = (p2h - p1) / (s2 - s1); // dp/ds // Store results results_.value = p0; results_.delta = delta0; results_.gamma = 2.0 * (delta1 - delta0) / (s2 - s0); //d(delta)/ds results_.theta = Utils.blackScholesTheta(process_, results_.value.GetValueOrDefault(), results_.delta.GetValueOrDefault(), results_.gamma.GetValueOrDefault()); }
public override void calculate() { DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); Calendar volcal = process_.blackVolatility().link.calendar(); double s0 = process_.stateVariable().link.value(); Utils.QL_REQUIRE(s0 > 0.0, () => "negative or null underlying given"); double v = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), s0); Date maturityDate = arguments_.exercise.lastDate(); double r = process_.riskFreeRate().link.zeroRate(maturityDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).value(); double q = process_.dividendYield().link.zeroRate(maturityDate, divdc, Compounding.Continuous, Frequency.NoFrequency).value(); Date referenceDate = process_.riskFreeRate().link.referenceDate(); // binomial trees with constant coefficient Handle <YieldTermStructure> flatRiskFree = new Handle <YieldTermStructure>(new FlatForward(referenceDate, r, rfdc)); Handle <YieldTermStructure> flatDividends = new Handle <YieldTermStructure>(new FlatForward(referenceDate, q, divdc)); Handle <BlackVolTermStructure> flatVol = new Handle <BlackVolTermStructure>(new BlackConstantVol(referenceDate, volcal, v, voldc)); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); double maturity = rfdc.yearFraction(referenceDate, maturityDate); StochasticProcess1D bs = new GeneralizedBlackScholesProcess(process_.stateVariable(), flatDividends, flatRiskFree, flatVol); TimeGrid grid = new TimeGrid(maturity, timeSteps_); ITree tree = getTree_(bs, maturity, timeSteps_, payoff.strike()); BlackScholesLattice <ITree> lattice = new BlackScholesLattice <ITree>(tree, r, maturity, timeSteps_); DiscretizedAsset option = getAsset_(arguments_, process_, grid); option.initialize(lattice, maturity); // Partial derivatives calculated from various points in the // binomial tree // (see J.C.Hull, "Options, Futures and other derivatives", 6th edition, pp 397/398) // Rollback to third-last step, and get underlying prices (s2) & // option values (p2) at this point option.rollback(grid[2]); Vector va2 = new Vector(option.values()); Utils.QL_REQUIRE(va2.size() == 3, () => "Expect 3 nodes in grid at second step"); double p2u = va2[2]; // up double p2m = va2[1]; // mid double p2d = va2[0]; // down (low) double s2u = lattice.underlying(2, 2); // up price double s2m = lattice.underlying(2, 1); // middle price double s2d = lattice.underlying(2, 0); // down (low) price // calculate gamma by taking the first derivate of the two deltas double delta2u = (p2u - p2m) / (s2u - s2m); double delta2d = (p2m - p2d) / (s2m - s2d); double gamma = (delta2u - delta2d) / ((s2u - s2d) / 2); // Rollback to second-last step, and get option values (p1) at // this point option.rollback(grid[1]); Vector va = new Vector(option.values()); Utils.QL_REQUIRE(va.size() == 2, () => "Expect 2 nodes in grid at first step"); double p1u = va[1]; double p1d = va[0]; double s1u = lattice.underlying(1, 1); // up (high) price double s1d = lattice.underlying(1, 0); // down (low) price double delta = (p1u - p1d) / (s1u - s1d); // Finally, rollback to t=0 option.rollback(0.0); double p0 = option.presentValue(); // Store results results_.value = p0; results_.delta = delta; results_.gamma = gamma; // theta can be approximated by calculating the numerical derivative // between mid value at third-last step and at t0. The underlying price // is the same, only time varies. results_.theta = (p2m - p0) / grid[2]; }