//! identity instance public IOperator identity(int size) { TridiagonalOperator I = new TridiagonalOperator(new Vector(size - 1, 0.0), // lower diagonal new Vector(size, 1.0), // diagonal new Vector(size - 1, 0.0)); // upper diagonal return(I); }
public IOperator multiply(double a, IOperator o) { TridiagonalOperator D = o as TridiagonalOperator; Vector low = D.lowerDiagonal_ * a, mid = D.diagonal_ * a, high = D.upperDiagonal_ * a; TridiagonalOperator result = new TridiagonalOperator(low, mid, high); return(result); }
public IOperator subtract(IOperator A, IOperator B) { TridiagonalOperator D1 = A as TridiagonalOperator; TridiagonalOperator D2 = B as TridiagonalOperator; Vector low = D1.lowerDiagonal_ - D2.lowerDiagonal_, mid = D1.diagonal_ - D2.diagonal_, high = D1.upperDiagonal_ - D2.upperDiagonal_; TridiagonalOperator result = new TridiagonalOperator(low, mid, high); return(result); }
public void generateOperator(double t, TransformedGrid tg, TridiagonalOperator L) { for (int i = 1; i < tg.size() - 1; i++) { double sigma = diffusion(t, tg.grid(i)); double nu = drift(t, tg.grid(i)); double r = discount(t, tg.grid(i)); double sigma2 = sigma * sigma; double pd = -(sigma2 / tg.dxm(i) - nu) / tg.dx(i); double pu = -(sigma2 / tg.dxp(i) + nu) / tg.dx(i); double pm = sigma2 / (tg.dxm(i) * tg.dxp(i)) + r; L.setMidRow(i, pd, pm, pu); } }
// interface public override void applyBeforeApplying(IOperator o) { TridiagonalOperator L = o as TridiagonalOperator; switch (side_) { case Side.Lower: L.setFirstRow(-1.0, 1.0); break; case Side.Upper: L.setLastRow(-1.0, 1.0); break; default: throw new ArgumentException("unknown side for Neumann boundary condition"); } }
public override void applyBeforeSolving(IOperator o, Vector rhs) { TridiagonalOperator L = o as TridiagonalOperator; switch (side_) { case Side.Lower: L.setFirstRow(-1.0, 1.0); rhs[0] = value_; break; case Side.Upper: L.setLastRow(-1.0, 1.0); rhs[rhs.size() - 1] = value_; break; default: throw new ArgumentException("unknown side for Neumann boundary condition"); } }
public override void update() { Vector tmp = new Vector(size_); List <double> dx = new InitializedList <double>(size_ - 1), S = new InitializedList <double>(size_ - 1); for (int i = 0; i < size_ - 1; ++i) { dx[i] = xBegin_[i + 1] - xBegin_[i]; S[i] = (yBegin_[i + 1] - yBegin_[i]) / dx[i]; } // first derivative approximation if (da_ == CubicInterpolation.DerivativeApprox.Spline) { TridiagonalOperator L = new TridiagonalOperator(size_); for (int i = 1; i < size_ - 1; ++i) { L.setMidRow(i, dx[i], 2.0 * (dx[i] + dx[i - 1]), dx[i - 1]); tmp[i] = 3.0 * (dx[i] * S[i - 1] + dx[i - 1] * S[i]); } // left boundary condition switch (leftType_) { case CubicInterpolation.BoundaryCondition.NotAKnot: // ignoring end condition value L.setFirstRow(dx[1] * (dx[1] + dx[0]), (dx[0] + dx[1]) * (dx[0] + dx[1])); tmp[0] = S[0] * dx[1] * (2.0 * dx[1] + 3.0 * dx[0]) + S[1] * dx[0] * dx[0]; break; case CubicInterpolation.BoundaryCondition.FirstDerivative: L.setFirstRow(1.0, 0.0); tmp[0] = leftValue_; break; case CubicInterpolation.BoundaryCondition.SecondDerivative: L.setFirstRow(2.0, 1.0); tmp[0] = 3.0 * S[0] - leftValue_ * dx[0] / 2.0; break; case CubicInterpolation.BoundaryCondition.Periodic: // ignoring end condition value throw new NotImplementedException("this end condition is not implemented yet"); case CubicInterpolation.BoundaryCondition.Lagrange: L.setFirstRow(1.0, 0.0); tmp[0] = cubicInterpolatingPolynomialDerivative( this.xBegin_[0], this.xBegin_[1], this.xBegin_[2], this.xBegin_[3], this.yBegin_[0], this.yBegin_[1], this.yBegin_[2], this.yBegin_[3], this.xBegin_[0]); break; default: throw new ArgumentException("unknown end condition"); } // right boundary condition switch (rightType_) { case CubicInterpolation.BoundaryCondition.NotAKnot: // ignoring end condition value L.setLastRow(-(dx[size_ - 2] + dx[size_ - 3]) * (dx[size_ - 2] + dx[size_ - 3]), -dx[size_ - 3] * (dx[size_ - 3] + dx[size_ - 2])); tmp[size_ - 1] = -S[size_ - 3] * dx[size_ - 2] * dx[size_ - 2] - S[size_ - 2] * dx[size_ - 3] * (3.0 * dx[size_ - 2] + 2.0 * dx[size_ - 3]); break; case CubicInterpolation.BoundaryCondition.FirstDerivative: L.setLastRow(0.0, 1.0); tmp[size_ - 1] = rightValue_; break; case CubicInterpolation.BoundaryCondition.SecondDerivative: L.setLastRow(1.0, 2.0); tmp[size_ - 1] = 3.0 * S[size_ - 2] + rightValue_ * dx[size_ - 2] / 2.0; break; case CubicInterpolation.BoundaryCondition.Periodic: // ignoring end condition value throw new NotImplementedException("this end condition is not implemented yet"); case CubicInterpolation.BoundaryCondition.Lagrange: L.setLastRow(0.0, 1.0); tmp[size_ - 1] = cubicInterpolatingPolynomialDerivative( this.xBegin_[size_ - 4], this.xBegin_[size_ - 3], this.xBegin_[size_ - 2], this.xBegin_[size_ - 1], this.yBegin_[size_ - 4], this.yBegin_[size_ - 3], this.yBegin_[size_ - 2], this.yBegin_[size_ - 1], this.xBegin_[size_ - 1]); break; default: throw new ArgumentException("unknown end condition"); } // solve the system tmp = L.solveFor(tmp); } else if (da_ == CubicInterpolation.DerivativeApprox.SplineOM1) { Matrix T_ = new Matrix(size_ - 2, size_, 0.0); for (int i = 0; i < size_ - 2; ++i) { T_[i, i] = dx[i] / 6.0; T_[i, i + 1] = (dx[i + 1] + dx[i]) / 3.0; T_[i, i + 2] = dx[i + 1] / 6.0; } Matrix S_ = new Matrix(size_ - 2, size_, 0.0); for (int i = 0; i < size_ - 2; ++i) { S_[i, i] = 1.0 / dx[i]; S_[i, i + 1] = -(1.0 / dx[i + 1] + 1.0 / dx[i]); S_[i, i + 2] = 1.0 / dx[i + 1]; } Matrix Up_ = new Matrix(size_, 2, 0.0); Up_[0, 0] = 1; Up_[size_ - 1, 1] = 1; Matrix Us_ = new Matrix(size_, size_ - 2, 0.0); for (int i = 0; i < size_ - 2; ++i) { Us_[i + 1, i] = 1; } Matrix Z_ = Us_ * Matrix.inverse(T_ * Us_); Matrix I_ = new Matrix(size_, size_, 0.0); for (int i = 0; i < size_; ++i) { I_[i, i] = 1; } Matrix V_ = (I_ - Z_ * T_) * Up_; Matrix W_ = Z_ * S_; Matrix Q_ = new Matrix(size_, size_, 0.0); Q_[0, 0] = 1.0 / (size_ - 1) * dx[0] * dx[0] * dx[0]; Q_[0, 1] = 7.0 / 8 * 1.0 / (size_ - 1) * dx[0] * dx[0] * dx[0]; for (int i = 1; i < size_ - 1; ++i) { Q_[i, i - 1] = 7.0 / 8 * 1.0 / (size_ - 1) * dx[i - 1] * dx[i - 1] * dx[i - 1]; Q_[i, i] = 1.0 / (size_ - 1) * dx[i] * dx[i] * dx[i] + 1.0 / (size_ - 1) * dx[i - 1] * dx[i - 1] * dx[i - 1]; Q_[i, i + 1] = 7.0 / 8 * 1.0 / (size_ - 1) * dx[i] * dx[i] * dx[i]; } Q_[size_ - 1, size_ - 2] = 7.0 / 8 * 1.0 / (size_ - 1) * dx[size_ - 2] * dx[size_ - 2] * dx[size_ - 2]; Q_[size_ - 1, size_ - 1] = 1.0 / (size_ - 1) * dx[size_ - 2] * dx[size_ - 2] * dx[size_ - 2]; Matrix J_ = (I_ - V_ * Matrix.inverse(Matrix.transpose(V_) * Q_ * V_) * Matrix.transpose(V_) * Q_) * W_; Vector Y_ = new Vector(size_); for (int i = 0; i < size_; ++i) { Y_[i] = this.yBegin_[i]; } Vector D_ = J_ * Y_; for (int i = 0; i < size_ - 1; ++i) { tmp[i] = (Y_[i + 1] - Y_[i]) / dx[i] - (2.0 * D_[i] + D_[i + 1]) * dx[i] / 6.0; } tmp[size_ - 1] = tmp[size_ - 2] + D_[size_ - 2] * dx[size_ - 2] + (D_[size_ - 1] - D_[size_ - 2]) * dx[size_ - 2] / 2.0; } else if (da_ == CubicInterpolation.DerivativeApprox.SplineOM2) { Matrix T_ = new Matrix(size_ - 2, size_, 0.0); for (int i = 0; i < size_ - 2; ++i) { T_[i, i] = dx[i] / 6.0; T_[i, i + 1] = (dx[i] + dx[i + 1]) / 3.0; T_[i, i + 2] = dx[i + 1] / 6.0; } Matrix S_ = new Matrix(size_ - 2, size_, 0.0); for (int i = 0; i < size_ - 2; ++i) { S_[i, i] = 1.0 / dx[i]; S_[i, i + 1] = -(1.0 / dx[i + 1] + 1.0 / dx[i]); S_[i, i + 2] = 1.0 / dx[i + 1]; } Matrix Up_ = new Matrix(size_, 2, 0.0); Up_[0, 0] = 1; Up_[size_ - 1, 1] = 1; Matrix Us_ = new Matrix(size_, size_ - 2, 0.0); for (int i = 0; i < size_ - 2; ++i) { Us_[i + 1, i] = 1; } Matrix Z_ = Us_ * Matrix.inverse(T_ * Us_); Matrix I_ = new Matrix(size_, size_, 0.0); for (int i = 0; i < size_; ++i) { I_[i, i] = 1; } Matrix V_ = (I_ - Z_ * T_) * Up_; Matrix W_ = Z_ * S_; Matrix Q_ = new Matrix(size_, size_, 0.0); Q_[0, 0] = 1.0 / (size_ - 1) * dx[0]; Q_[0, 1] = 1.0 / 2 * 1.0 / (size_ - 1) * dx[0]; for (int i = 1; i < size_ - 1; ++i) { Q_[i, i - 1] = 1.0 / 2 * 1.0 / (size_ - 1) * dx[i - 1]; Q_[i, i] = 1.0 / (size_ - 1) * dx[i] + 1.0 / (size_ - 1) * dx[i - 1]; Q_[i, i + 1] = 1.0 / 2 * 1.0 / (size_ - 1) * dx[i]; } Q_[size_ - 1, size_ - 2] = 1.0 / 2 * 1.0 / (size_ - 1) * dx[size_ - 2]; Q_[size_ - 1, size_ - 1] = 1.0 / (size_ - 1) * dx[size_ - 2]; Matrix J_ = (I_ - V_ * Matrix.inverse(Matrix.transpose(V_) * Q_ * V_) * Matrix.transpose(V_) * Q_) * W_; Vector Y_ = new Vector(size_); for (int i = 0; i < size_; ++i) { Y_[i] = this.yBegin_[i]; } Vector D_ = J_ * Y_; for (int i = 0; i < size_ - 1; ++i) { tmp[i] = (Y_[i + 1] - Y_[i]) / dx[i] - (2.0 * D_[i] + D_[i + 1]) * dx[i] / 6.0; } tmp[size_ - 1] = tmp[size_ - 2] + D_[size_ - 2] * dx[size_ - 2] + (D_[size_ - 1] - D_[size_ - 2]) * dx[size_ - 2] / 2.0; } else { // local schemes if (size_ == 2) { tmp[0] = tmp[1] = S[0]; } else { switch (da_) { case CubicInterpolation.DerivativeApprox.FourthOrder: throw new NotImplementedException("FourthOrder not implemented yet"); case CubicInterpolation.DerivativeApprox.Parabolic: // intermediate points for (int i = 1; i < size_ - 1; ++i) { tmp[i] = (dx[i - 1] * S[i] + dx[i] * S[i - 1]) / (dx[i] + dx[i - 1]); } // end points tmp[0] = ((2.0 * dx[0] + dx[1]) * S[0] - dx[0] * S[1]) / (dx[0] + dx[1]); tmp[size_ - 1] = ((2.0 * dx[size_ - 2] + dx[size_ - 3]) * S[size_ - 2] - dx[size_ - 2] * S[size_ - 3]) / (dx[size_ - 2] + dx[size_ - 3]); break; case CubicInterpolation.DerivativeApprox.FritschButland: // intermediate points for (int i = 1; i < size_ - 1; ++i) { double Smin = Math.Min(S[i - 1], S[i]); double Smax = Math.Max(S[i - 1], S[i]); tmp[i] = 3.0 * Smin * Smax / (Smax + 2.0 * Smin); } // end points tmp[0] = ((2.0 * dx[0] + dx[1]) * S[0] - dx[0] * S[1]) / (dx[0] + dx[1]); tmp[size_ - 1] = ((2.0 * dx[size_ - 2] + dx[size_ - 3]) * S[size_ - 2] - dx[size_ - 2] * S[size_ - 3]) / (dx[size_ - 2] + dx[size_ - 3]); break; case CubicInterpolation.DerivativeApprox.Akima: tmp[0] = (Math.Abs(S[1] - S[0]) * 2 * S[0] * S[1] + Math.Abs(2 * S[0] * S[1] - 4 * S[0] * S[0] * S[1]) * S[0]) / (Math.Abs(S[1] - S[0]) + Math.Abs(2 * S[0] * S[1] - 4 * S[0] * S[0] * S[1])); tmp[1] = (Math.Abs(S[2] - S[1]) * S[0] + Math.Abs(S[0] - 2 * S[0] * S[1]) * S[1]) / (Math.Abs(S[2] - S[1]) + Math.Abs(S[0] - 2 * S[0] * S[1])); for (int i = 2; i < size_ - 2; ++i) { if ((S[i - 2].IsEqual(S[i - 1])) && (S[i].IsNotEqual(S[i + 1]))) { tmp[i] = S[i - 1]; } else if ((S[i - 2].IsNotEqual(S[i - 1])) && (S[i].IsEqual(S[i + 1]))) { tmp[i] = S[i]; } else if (S[i].IsEqual(S[i - 1])) { tmp[i] = S[i]; } else if ((S[i - 2].IsEqual(S[i - 1])) && (S[i - 1].IsNotEqual(S[i])) && (S[i].IsEqual(S[i + 1]))) { tmp[i] = (S[i - 1] + S[i]) / 2.0; } else { tmp[i] = (Math.Abs(S[i + 1] - S[i]) * S[i - 1] + Math.Abs(S[i - 1] - S[i - 2]) * S[i]) / (Math.Abs(S[i + 1] - S[i]) + Math.Abs(S[i - 1] - S[i - 2])); } } tmp[size_ - 2] = (Math.Abs(2 * S[size_ - 2] * S[size_ - 3] - S[size_ - 2]) * S[size_ - 3] + Math.Abs(S[size_ - 3] - S[size_ - 4]) * S[size_ - 2]) / (Math.Abs(2 * S[size_ - 2] * S[size_ - 3] - S[size_ - 2]) + Math.Abs(S[size_ - 3] - S[size_ - 4])); tmp[size_ - 1] = (Math.Abs(4 * S[size_ - 2] * S[size_ - 2] * S[size_ - 3] - 2 * S[size_ - 2] * S[size_ - 3]) * S[size_ - 2] + Math.Abs(S[size_ - 2] - S[size_ - 3]) * 2 * S[size_ - 2] * S[size_ - 3]) / (Math.Abs(4 * S[size_ - 2] * S[size_ - 2] * S[size_ - 3] - 2 * S[size_ - 2] * S[size_ - 3]) + Math.Abs(S[size_ - 2] - S[size_ - 3])); break; case CubicInterpolation.DerivativeApprox.Kruger: // intermediate points for (int i = 1; i < size_ - 1; ++i) { if (S[i - 1] * S[i] < 0.0) { // slope changes sign at point tmp[i] = 0.0; } else { // slope will be between the slopes of the adjacent // straight lines and should approach zero if the // slope of either line approaches zero tmp[i] = 2.0 / (1.0 / S[i - 1] + 1.0 / S[i]); } } // end points tmp[0] = (3.0 * S[0] - tmp[1]) / 2.0; tmp[size_ - 1] = (3.0 * S[size_ - 2] - tmp[size_ - 2]) / 2.0; break; case CubicInterpolation.DerivativeApprox.Harmonic: // intermediate points for (int i = 1; i < size_ - 1; ++i) { double w1 = 2 * dx[i] + dx[i - 1]; double w2 = dx[i] + 2 * dx[i - 1]; if (S[i - 1] * S[i] <= 0.0) { // slope changes sign at point tmp[i] = 0.0; } else { // weighted harmonic mean of S[i] and S[i-1] if they // have the same sign; otherwise 0 tmp[i] = (w1 + w2) / (w1 / S[i - 1] + w2 / S[i]); } } // end points [0] tmp[0] = ((2 * dx[0] + dx[1]) * S[0] - dx[0] * S[1]) / (dx[1] + dx[0]); if (tmp[0] * S[0] < 0.0) { tmp[0] = 0; } else if (S[0] * S[1] < 0) { if (Math.Abs(tmp[0]) > Math.Abs(3 * S[0])) { tmp[0] = 3 * S[0]; } } // end points [n-1] tmp[size_ - 1] = ((2 * dx[size_ - 2] + dx[size_ - 3]) * S[size_ - 2] - dx[size_ - 2] * S[size_ - 3]) / (dx[size_ - 3] + dx[size_ - 2]); if (tmp[size_ - 1] * S[size_ - 2] < 0.0) { tmp[size_ - 1] = 0; } else if (S[size_ - 2] * S[size_ - 3] < 0) { if (Math.Abs(tmp[size_ - 1]) > Math.Abs(3 * S[size_ - 2])) { tmp[size_ - 1] = 3 * S[size_ - 2]; } } break; default: throw new ArgumentException("unknown scheme"); } } } monotonicityAdjustments_.Erase(); // Hyman monotonicity constrained filter if (monotonic_) { double correction; double pm, pu, pd, M; for (int i = 0; i < size_; ++i) { if (i == 0) { if (tmp[i] * S[0] > 0.0) { correction = tmp[i] / Math.Abs(tmp[i]) * Math.Min(Math.Abs(tmp[i]), Math.Abs(3.0 * S[0])); } else { correction = 0.0; } if (correction.IsNotEqual(tmp[i])) { tmp[i] = correction; monotonicityAdjustments_[i] = true; } } else if (i == size_ - 1) { if (tmp[i] * S[size_ - 2] > 0.0) { correction = tmp[i] / Math.Abs(tmp[i]) * Math.Min(Math.Abs(tmp[i]), Math.Abs(3.0 * S[size_ - 2])); } else { correction = 0.0; } if (correction.IsNotEqual(tmp[i])) { tmp[i] = correction; monotonicityAdjustments_[i] = true; } } else { pm = (S[i - 1] * dx[i] + S[i] * dx[i - 1]) / (dx[i - 1] + dx[i]); M = 3.0 * Math.Min(Math.Min(Math.Abs(S[i - 1]), Math.Abs(S[i])), Math.Abs(pm)); if (i > 1) { if ((S[i - 1] - S[i - 2]) * (S[i] - S[i - 1]) > 0.0) { pd = (S[i - 1] * (2.0 * dx[i - 1] + dx[i - 2]) - S[i - 2] * dx[i - 1]) / (dx[i - 2] + dx[i - 1]); if (pm * pd > 0.0 && pm * (S[i - 1] - S[i - 2]) > 0.0) { M = Math.Max(M, 1.5 * Math.Min( Math.Abs(pm), Math.Abs(pd))); } } } if (i < size_ - 2) { if ((S[i] - S[i - 1]) * (S[i + 1] - S[i]) > 0.0) { pu = (S[i] * (2.0 * dx[i] + dx[i + 1]) - S[i + 1] * dx[i]) / (dx[i] + dx[i + 1]); if (pm * pu > 0.0 && -pm * (S[i] - S[i - 1]) > 0.0) { M = Math.Max(M, 1.5 * Math.Min( Math.Abs(pm), Math.Abs(pu))); } } } if (tmp[i] * pm > 0.0) { correction = tmp[i] / Math.Abs(tmp[i]) * Math.Min(Math.Abs(tmp[i]), M); } else { correction = 0.0; } if (correction.IsNotEqual(tmp[i])) { tmp[i] = correction; monotonicityAdjustments_[i] = true; } } } } // cubic coefficients for (int i = 0; i < size_ - 1; ++i) { a_[i] = tmp[i]; b_[i] = (3.0 * S[i] - tmp[i + 1] - 2.0 * tmp[i]) / dx[i]; c_[i] = (tmp[i + 1] + tmp[i] - 2.0 * S[i]) / (dx[i] * dx[i]); } primitiveConst_[0] = 0.0; for (int i = 1; i < size_ - 1; ++i) { primitiveConst_[i] = primitiveConst_[i - 1] + dx[i - 1] * (yBegin_[i - 1] + dx[i - 1] * (a_[i - 1] / 2.0 + dx[i - 1] * (b_[i - 1] / 3.0 + dx[i - 1] * c_[i - 1] / 4.0))); } }
protected void initializeOperator() { finiteDifferenceOperator_ = OperatorFactory.getOperator(process_, intrinsicValues_.grid(), getResidualTime(), timeDependent_); }
public override void calculate(IPricingEngineResults r) { OneAssetOption.Results results = r as OneAssetOption.Results; setGridLimits(); initializeInitialCondition(); initializeOperator(); initializeBoundaryConditions(); initializeStepCondition(); List <IOperator> operatorSet = new List <IOperator>(); List <Vector> arraySet = new List <Vector>(); BoundaryConditionSet bcSet = new BoundaryConditionSet(); StepConditionSet <Vector> conditionSet = new StepConditionSet <Vector>(); prices_ = (SampledCurve)intrinsicValues_.Clone(); controlPrices_ = (SampledCurve)intrinsicValues_.Clone(); controlOperator_ = (TridiagonalOperator)finiteDifferenceOperator_.Clone(); controlBCs_[0] = BCs_[0]; controlBCs_[1] = BCs_[1]; operatorSet.Add(finiteDifferenceOperator_); operatorSet.Add(controlOperator_); arraySet.Add(prices_.values()); arraySet.Add(controlPrices_.values()); bcSet.Add(BCs_); bcSet.Add(controlBCs_); conditionSet.Add(stepCondition_); conditionSet.Add(new NullCondition <Vector>()); var model = new FiniteDifferenceModel <ParallelEvolver <CrankNicolson <TridiagonalOperator> > >(operatorSet, bcSet); object temp = arraySet; model.rollback(ref temp, getResidualTime(), 0.0, timeSteps_, conditionSet); arraySet = (List <Vector>)temp; prices_.setValues(arraySet[0]); controlPrices_.setValues(arraySet[1]); StrikedTypePayoff striked_payoff = payoff_ as StrikedTypePayoff; Utils.QL_REQUIRE(striked_payoff != null, () => "non-striked payoff given"); double variance = process_.blackVolatility().link.blackVariance(exerciseDate_, striked_payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(exerciseDate_); double riskFreeDiscount = process_.riskFreeRate().link.discount(exerciseDate_); double spot = process_.stateVariable().link.value(); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(striked_payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results.value = prices_.valueAtCenter() - controlPrices_.valueAtCenter() + black.value(); results.delta = prices_.firstDerivativeAtCenter() - controlPrices_.firstDerivativeAtCenter() + black.delta(spot); results.gamma = prices_.secondDerivativeAtCenter() - controlPrices_.secondDerivativeAtCenter() + black.gamma(spot); results.additionalResults["priceCurve"] = prices_; }