public Path(TimeGrid timeGrid, Vector values) { timeGrid_ = timeGrid; values_ = (Vector)values.Clone(); if (values_.empty()) values_ = new Vector(timeGrid_.size()); if (values_.size() != timeGrid_.size()) throw new ApplicationException("different number of times and asset values"); }
public Path(TimeGrid timeGrid, Vector values) { timeGrid_ = timeGrid; values_ = (Vector)values.Clone(); if (values_.empty()) { values_ = new Vector(timeGrid_.size()); } if (values_.size() != timeGrid_.size()) { throw new ApplicationException("different number of times and asset values"); } }
public MultiPathGenerator(StochasticProcess process, TimeGrid times, GSG generator, bool brownianBridge) { brownianBridge_ = brownianBridge; process_ = process; generator_ = generator; next_ = new Sample <IPath>(new MultiPath(process.size(), times), 1.0); Utils.QL_REQUIRE(generator_.dimension() == process.factors() * (times.size() - 1), () => "dimension (" + generator_.dimension() + ") is not equal to (" + process.factors() + " * " + (times.size() - 1) + ") the number of factors " + "times the number of time steps"); Utils.QL_REQUIRE(times.size() > 1, () => "no times given"); }
public LongstaffSchwartzPathPricer(TimeGrid times, IEarlyExercisePathPricer <PathType, double> pathPricer, YieldTermStructure termStructure) { calibrationPhase_ = true; pathPricer_ = pathPricer; coeff_ = new InitializedList <Vector>(times.size() - 1); dF_ = new InitializedList <double>(times.size() - 1); v_ = pathPricer_.basisSystem(); for (int i = 0; i < times.size() - 1; ++i) { dF_[i] = termStructure.discount(times[i + 1]) / termStructure.discount(times[i]); } }
public override Lattice tree(TimeGrid grid) { TermStructureFittingParameter phi = new TermStructureFittingParameter(termStructure()); ShortRateDynamics numericDynamics = new Dynamics(phi, a(), sigma()); TrinomialTree trinomial = new TrinomialTree(numericDynamics.process(), grid); ShortRateTree numericTree = new ShortRateTree(trinomial, numericDynamics, grid); TermStructureFittingParameter.NumericalImpl impl = (TermStructureFittingParameter.NumericalImpl)phi.implementation(); impl.reset(); for (int i=0; i<(grid.size() - 1); i++) { double discountBond = termStructure().link.discount(grid[i+1]); Vector statePrices = numericTree.statePrices(i); int size = numericTree.size(i); double dt = numericTree.timeGrid().dt(i); double dx = trinomial.dx(i); double x = trinomial.underlying(i,0); double value = 0.0; for (int j=0; j<size; j++) { value += statePrices[j]*Math.Exp(-x*dt); x += dx; } value = Math.Log(value/discountBond)/dt; impl.setvalue(grid[i], value); } return numericTree; }
protected override IPathGenerator <IRNG> pathGenerator() { TimeGrid grid = timeGrid(); IRNG gen = new RNG().make_sequence_generator(grid.size() - 1, seed_); return(new PathGenerator <IRNG>(process_, grid, gen, brownianBridge_)); }
public override Lattice tree(TimeGrid grid) { TermStructureFittingParameter phi = new TermStructureFittingParameter(termStructure()); ShortRateDynamics numericDynamics = new Dynamics(phi, a(), sigma()); TrinomialTree trinomial = new TrinomialTree(numericDynamics.process(), grid); ShortRateTree numericTree = new ShortRateTree(trinomial, numericDynamics, grid); TermStructureFittingParameter.NumericalImpl impl = (TermStructureFittingParameter.NumericalImpl)phi.implementation(); impl.reset(); double value = 1.0; double vMin = -50.0; double vMax = 50.0; for (int i = 0; i < (grid.size() - 1); i++) { double discountBond = termStructure().link.discount(grid[i + 1]); double xMin = trinomial.underlying(i, 0); double dx = trinomial.dx(i); Helper finder = new Helper(i, xMin, dx, discountBond, numericTree); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); value = s1d.solve(finder, 1e-7, value, vMin, vMax); impl.setvalue(grid[i], value); } return(numericTree); }
public override Lattice tree( TimeGrid grid) { TermStructureFittingParameter phi= new TermStructureFittingParameter(termStructure()); ShortRateDynamics numericDynamics= new Dynamics(phi, a(), sigma()); TrinomialTree trinomial= new TrinomialTree(numericDynamics.process(), grid); ShortRateTree numericTree = new ShortRateTree(trinomial, numericDynamics, grid); TermStructureFittingParameter.NumericalImpl impl = (TermStructureFittingParameter.NumericalImpl)phi.implementation(); impl.reset(); double value = 1.0; double vMin = -50.0; double vMax = 50.0; for (int i=0; i<(grid.size() - 1); i++) { double discountBond = termStructure().link.discount(grid[i+1]); double xMin = trinomial.underlying(i, 0); double dx = trinomial.dx(i); Helper finder = new Helper(i, xMin, dx, discountBond, numericTree); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); value = s1d.solve(finder, 1e-7, value, vMin, vMax); impl.setvalue(grid[i], value); // vMin = value - 10.0; // vMax = value + 10.0; } return numericTree; }
public override Lattice tree(TimeGrid grid) { TermStructureFittingParameter phi = new TermStructureFittingParameter(termStructure()); ShortRateDynamics numericDynamics = new Dynamics(phi, a(), sigma()); TrinomialTree trinomial = new TrinomialTree(numericDynamics.process(), grid); ShortRateTree numericTree = new ShortRateTree(trinomial, numericDynamics, grid); TermStructureFittingParameter.NumericalImpl impl = (TermStructureFittingParameter.NumericalImpl)phi.implementation(); impl.reset(); for (int i = 0; i < (grid.size() - 1); i++) { double discountBond = termStructure().link.discount(grid[i + 1]); Vector statePrices = numericTree.statePrices(i); int size = numericTree.size(i); double dt = numericTree.timeGrid().dt(i); double dx = trinomial.dx(i); double x = trinomial.underlying(i, 0); double value = 0.0; for (int j = 0; j < size; j++) { value += statePrices[j] * Math.Exp(-x * dt); x += dx; } value = Math.Log(value / discountBond) / dt; impl.setvalue(grid[i], value); } return(numericTree); }
//! Tree build-up + numerical fitting to term-structure public ShortRateTree(TrinomialTree tree, ShortRateDynamics dynamics, TermStructureFittingParameter.NumericalImpl theta, TimeGrid timeGrid) : base(timeGrid, tree.size(1)) { tree_ = tree; dynamics_ = dynamics; theta.reset(); double value = 1.0; double vMin = -100.0; double vMax = 100.0; for (int i = 0; i < (timeGrid.size() - 1); i++) { double discountBond = theta.termStructure().link.discount(t_[i + 1]); Helper finder = new Helper(i, discountBond, theta, this); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); value = s1d.solve(finder, 1e-7, value, vMin, vMax); // vMin = value - 1.0; // vMax = value + 1.0; theta.change(value); } }
public PathGenerator(StochasticProcess process, TimeGrid timeGrid, GSG generator, bool brownianBridge) { brownianBridge_ = brownianBridge; generator_ = generator; dimension_ = generator_.dimension(); timeGrid_ = timeGrid; process_ = process as StochasticProcess1D; next_ = new Sample <IPath>(new Path(timeGrid_), 1.0); temp_ = new InitializedList <double>(dimension_); bb_ = new BrownianBridge(timeGrid_); if (dimension_ != timeGrid_.size() - 1) { throw new Exception("sequence generator dimensionality (" + dimension_ + ") != timeSteps (" + (timeGrid_.size() - 1) + ")"); } }
public LatticeShortRateModelEngine(ShortRateModel model, TimeGrid timeGrid) : base(model) { timeGrid_ = new TimeGrid(timeGrid.Last(), timeGrid.size() - 1 /*timeGrid.dt(1) - timeGrid.dt(0)*/); timeGrid_ = timeGrid; timeSteps_ = 0; lattice_ = this.model_.link.tree(timeGrid); }
public MultiPathGenerator(StochasticProcess process, TimeGrid times, GSG generator, bool brownianBridge) { brownianBridge_ = brownianBridge; process_ = process; generator_ = generator; next_ = new Sample <IPath>(new MultiPath(process.size(), times), 1.0); if (generator_.dimension() != process.factors() * (times.size() - 1)) { throw new Exception("dimension (" + generator_.dimension() + ") is not equal to (" + process.factors() + " * " + (times.size() - 1) + ") the number of factors " + "times the number of time steps"); } if (!(times.size() > 1)) { throw new Exception("no times given"); } }
protected override PathPricer <IPath> pathPricer() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); TimeGrid grid = timeGrid(); List <double> discounts = new InitializedList <double>(grid.size()); for (int i = 0; i < grid.size(); i++) { discounts[i] = process_.riskFreeRate().currentLink().discount(grid[i]); } // do this with template parameters? if (isBiased_) { return(new BiasedBarrierPathPricer(arguments_.barrierType, arguments_.barrier, arguments_.rebate, payoff.optionType(), payoff.strike(), discounts)); } else { IRNG sequenceGen = new RandomSequenceGenerator <MersenneTwisterUniformRng>(grid.size() - 1, 5); return(new BarrierPathPricer(arguments_.barrierType, arguments_.barrier, arguments_.rebate, payoff.optionType(), payoff.strike(), discounts, process_, sequenceGen)); } }
private void pathGenerator(UnderlyingInfo under) { ulong seed = 1; int timeSteps = 365; // int dimensions = this.processArr_.factors(); double t = processArr_.time(maturity); TimeGrid grid = new TimeGrid(t, timeSteps); IRNG rndGenerator = (IRNG)new PseudoRandom().make_sequence_generator(dimensions * (grid.size() - 1), seed); this.pathGenerator_ = new MultiPathGenerator<IRNG>(this.processArr_, grid, rndGenerator, false); }
//! generic times public BrownianBridge(TimeGrid timeGrid) { size_ = timeGrid.size() - 1; t_ = new InitializedList <double>(size_); sqrtdt_ = new InitializedList <double>(size_); sqrtdt_ = new InitializedList <double>(size_); bridgeIndex_ = new InitializedList <int>(size_); leftIndex_ = new InitializedList <int>(size_); rightIndex_ = new InitializedList <int>(size_); leftWeight_ = new InitializedList <double>(size_); rightWeight_ = new InitializedList <double>(size_); stdDev_ = new InitializedList <double>(size_); for (int i = 0; i < size_; ++i) { t_[i] = timeGrid[i + 1]; } initialize(); }
public void testLambdaBootstrapping() { //"Testing caplet LMM lambda bootstrapping..." //SavedSettings backup; double tolerance = 1e-10; double[] lambdaExpected = {14.3010297550, 19.3821411939, 15.9816590141, 15.9953118303, 14.0570815635, 13.5687599894, 12.7477197786, 13.7056638165, 11.6191989567}; LiborForwardModelProcess process = makeProcess(); Matrix covar = process.covariance(0.0, null, 1.0); for (int i=0; i<9; ++i) { double calculated = Math.Sqrt(covar[i+1,i+1]); double expected = lambdaExpected[i]/100; if (Math.Abs(calculated - expected) > tolerance) Assert.Fail("Failed to reproduce expected lambda values" + "\n calculated: " + calculated + "\n expected: " + expected); } LfmCovarianceParameterization param = process.covarParam(); List<double> tmp = process.fixingTimes(); TimeGrid grid= new TimeGrid(tmp.Last(), 14); for (int t=0; t<grid.size(); ++t) { //verifier la presence du null Matrix diff = param.integratedCovariance(grid[t],null) - param.integratedCovariance(grid[t], null); for (int i=0; i<diff.rows(); ++i) { for (int j=0; j<diff.columns(); ++j) { if (Math.Abs(diff[i,j]) > tolerance) { Assert.Fail("Failed to reproduce integrated covariance" + "\n calculated: " + diff[i,j] + "\n expected: " + 0); } } } } }
public void testMonteCarloCapletPricing() { //"Testing caplet LMM Monte-Carlo caplet pricing..." //SavedSettings backup; /* factor loadings are taken from Hull & White article plus extra normalisation to get orthogonal eigenvectors http://www.rotman.utoronto.ca/~amackay/fin/libormktmodel2.pdf */ double[] compValues = {0.85549771, 0.46707264, 0.22353259, 0.91915359, 0.37716089, 0.11360610, 0.96438280, 0.26413316,-0.01412414, 0.97939148, 0.13492952,-0.15028753, 0.95970595,-0.00000000,-0.28100621, 0.97939148,-0.13492952,-0.15028753, 0.96438280,-0.26413316,-0.01412414, 0.91915359,-0.37716089, 0.11360610, 0.85549771,-0.46707264, 0.22353259}; Matrix volaComp=new Matrix(9,3); List<double> lcompValues=new InitializedList<double>(27,0); List<double> ltemp = new InitializedList<double>(3, 0); lcompValues=compValues.ToList(); //std::copy(compValues, compValues+9*3, volaComp.begin()); for (int i = 0; i < 9; i++) { ltemp = lcompValues.GetRange(3*i, 3); for (int j = 0; j < 3; j++) volaComp[i, j] = ltemp[j]; } LiborForwardModelProcess process1 = makeProcess(); LiborForwardModelProcess process2 = makeProcess(volaComp); List<double> tmp = process1.fixingTimes(); TimeGrid grid=new TimeGrid(tmp ,12); List<int> location=new List<int>(); for (int i=0; i < tmp.Count; ++i) { location.Add(grid.index(tmp[i])) ; } // set-up a small Monte-Carlo simulation to price caplets // and ratchet caps using a one- and a three factor libor market model ulong seed = 42; LowDiscrepancy.icInstance = new InverseCumulativeNormal(); IRNG rsg1 = (IRNG)new LowDiscrepancy().make_sequence_generator( process1.factors()*(grid.size()-1), seed); IRNG rsg2 = (IRNG)new LowDiscrepancy().make_sequence_generator( process2.factors()*(grid.size()-1), seed); MultiPathGenerator<IRNG> generator1=new MultiPathGenerator<IRNG> (process1, grid, rsg1, false); MultiPathGenerator<IRNG> generator2=new MultiPathGenerator<IRNG> (process2, grid, rsg2, false); const int nrTrails = 250000; List<GeneralStatistics> stat1 = new InitializedList<GeneralStatistics>(process1.size()); List<GeneralStatistics> stat2 = new InitializedList<GeneralStatistics>(process2.size()); List<GeneralStatistics> stat3 = new InitializedList<GeneralStatistics>(process2.size() - 1); for (int i=0; i<nrTrails; ++i) { Sample<MultiPath> path1 = generator1.next(); Sample<MultiPath> path2 = generator2.next(); List<double> rates1=new InitializedList<double>(len); List<double> rates2 = new InitializedList<double>(len); for (int j=0; j<process1.size(); ++j) { rates1[j] = path1.value[j][location[j]]; rates2[j] = path2.value[j][location[j]]; } List<double> dis1 = process1.discountBond(rates1); List<double> dis2 = process2.discountBond(rates2); for (int k=0; k<process1.size(); ++k) { double accrualPeriod = process1.accrualEndTimes()[k] - process1.accrualStartTimes()[k]; // caplet payoff function, cap rate at 4% double payoff1 = Math.Max(rates1[k] - 0.04, 0.0) * accrualPeriod; double payoff2 = Math.Max(rates2[k] - 0.04, 0.0) * accrualPeriod; stat1[k].add(dis1[k] * payoff1); stat2[k].add(dis2[k] * payoff2); if (k != 0) { // ratchet cap payoff function double payoff3 = Math.Max(rates2[k] - (rates2[k-1]+0.0025), 0.0) * accrualPeriod; stat3[k-1].add(dis2[k] * payoff3); } } } double[] capletNpv = {0.000000000000, 0.000002841629, 0.002533279333, 0.009577143571, 0.017746502618, 0.025216116835, 0.031608230268, 0.036645683881, 0.039792254012, 0.041829864365}; double[] ratchetNpv = {0.0082644895, 0.0082754754, 0.0082159966, 0.0082982822, 0.0083803357, 0.0084366961, 0.0084173270, 0.0081803406, 0.0079533814}; for (int k=0; k < process1.size(); ++k) { double calculated1 = stat1[k].mean(); double tolerance1 = stat1[k].errorEstimate(); double expected = capletNpv[k]; if (Math.Abs(calculated1 - expected) > tolerance1) { Assert.Fail("Failed to reproduce expected caplet NPV" + "\n calculated: " + calculated1 + "\n error int: " + tolerance1 + "\n expected: " + expected); } double calculated2 = stat2[k].mean(); double tolerance2 = stat2[k].errorEstimate(); if (Math.Abs(calculated2 - expected) > tolerance2) { Assert.Fail("Failed to reproduce expected caplet NPV" + "\n calculated: " + calculated2 + "\n error int: " + tolerance2 + "\n expected: " + expected); } if (k != 0) { double calculated3 = stat3[k-1].mean(); double tolerance3 = stat3[k-1].errorEstimate(); expected = ratchetNpv[k-1]; double refError = 1e-5; // 1e-5. error bars of the reference values if (Math.Abs(calculated3 - expected) > tolerance3 + refError) { Assert.Fail("Failed to reproduce expected caplet NPV" + "\n calculated: " + calculated3 + "\n error int: " + tolerance3 + refError + "\n expected: " + expected); } } } }
public int length() { return(timeGrid_.size()); }
protected override IPathGenerator <IRNG> controlPathGenerator() { int dimensions = process_.factors(); TimeGrid grid = this.timeGrid(); IRNG generator = (IRNG) new RNG().make_sequence_generator(dimensions * (grid.size() - 1), this.seed_); HybridHestonHullWhiteProcess process = process_ as HybridHestonHullWhiteProcess; Utils.QL_REQUIRE(process != null, () => "invalid process"); HybridHestonHullWhiteProcess cvProcess = new HybridHestonHullWhiteProcess(process.hestonProcess(), process.hullWhiteProcess(), 0.0, process.discretization()); return(new MultiPathGenerator <IRNG>(cvProcess, grid, generator, false)); }
//! generic times public BrownianBridge(TimeGrid timeGrid) { size_ = timeGrid.size()-1; t_ = new InitializedList<double>(size_); sqrtdt_ = new InitializedList<double>(size_); sqrtdt_ = new InitializedList<double>(size_); bridgeIndex_ = new InitializedList<int>(size_); leftIndex_ = new InitializedList<int>(size_); rightIndex_ = new InitializedList<int>(size_); leftWeight_ = new InitializedList<double>(size_); rightWeight_ = new InitializedList<double>(size_); stdDev_ = new InitializedList<double>(size_); for (int i=0; i<size_; ++i) t_[i] = timeGrid[i+1]; initialize(); }
public void testSwaptionPricing() { //"Testing forward swap and swaption pricing..."); //SavedSettings backup; const int size = 10; const int steps = 8*size; #if QL_USE_INDEXED_COUPON const double tolerance = 1e-6; #else const double tolerance = 1e-12; #endif List<Date> dates = new List<Date>(); List<double> rates = new List<double>(); dates.Add(new Date(4,9,2005)); dates.Add(new Date(4,9,2011)); rates.Add(0.04); rates.Add(0.08); IborIndex index = makeIndex(dates, rates); LiborForwardModelProcess process = new LiborForwardModelProcess(size, index); LmCorrelationModel corrModel = new LmExponentialCorrelationModel(size, 0.5); LmVolatilityModel volaModel = new LmLinearExponentialVolatilityModel(process.fixingTimes(), 0.291, 1.483, 0.116, 0.00001); // set-up pricing engine process.setCovarParam((LfmCovarianceParameterization) new LfmCovarianceProxy(volaModel, corrModel)); // set-up a small Monte-Carlo simulation to price swations List<double> tmp = process.fixingTimes(); TimeGrid grid=new TimeGrid(tmp ,steps); List<int> location=new List<int>(); for (int i=0; i < tmp.Count; ++i) { location.Add(grid.index(tmp[i])) ; } ulong seed=42; const int nrTrails = 5000; LowDiscrepancy.icInstance = new InverseCumulativeNormal(); IRNG rsg = (InverseCumulativeRsg<RandomSequenceGenerator<MersenneTwisterUniformRng> ,InverseCumulativeNormal>) new PseudoRandom().make_sequence_generator(process.factors()*(grid.size()-1),seed); MultiPathGenerator<IRNG> generator=new MultiPathGenerator<IRNG>(process, grid, rsg, false); LiborForwardModel liborModel = new LiborForwardModel(process, volaModel, corrModel); Calendar calendar = index.fixingCalendar(); DayCounter dayCounter = index.forwardingTermStructure().link.dayCounter(); BusinessDayConvention convention = index.businessDayConvention(); Date settlement = index.forwardingTermStructure().link.referenceDate(); SwaptionVolatilityMatrix m = liborModel.getSwaptionVolatilityMatrix(); for (int i=1; i < size; ++i) { for (int j=1; j <= size-i; ++j) { Date fwdStart = settlement + new Period(6*i, TimeUnit.Months); Date fwdMaturity = fwdStart + new Period(6*j, TimeUnit.Months); Schedule schedule =new Schedule(fwdStart, fwdMaturity, index.tenor(), calendar, convention, convention, DateGeneration.Rule.Forward, false); double swapRate = 0.0404; VanillaSwap forwardSwap = new VanillaSwap(VanillaSwap.Type.Receiver, 1.0, schedule, swapRate, dayCounter, schedule, index, 0.0, index.dayCounter()); forwardSwap.setPricingEngine(new DiscountingSwapEngine(index.forwardingTermStructure())); // check forward pricing first double expected = forwardSwap.fairRate(); double calculated = liborModel.S_0(i-1,i+j-1); if (Math.Abs(expected - calculated) > tolerance) Assert.Fail("Failed to reproduce fair forward swap rate" + "\n calculated: " + calculated + "\n expected: " + expected); swapRate = forwardSwap.fairRate(); forwardSwap = new VanillaSwap(VanillaSwap.Type.Receiver, 1.0, schedule, swapRate, dayCounter, schedule, index, 0.0, index.dayCounter()); forwardSwap.setPricingEngine(new DiscountingSwapEngine(index.forwardingTermStructure())); if (i == j && i<=size/2) { IPricingEngine engine = new LfmSwaptionEngine(liborModel, index.forwardingTermStructure()); Exercise exercise = new EuropeanExercise(process.fixingDates()[i]); Swaption swaption = new Swaption(forwardSwap, exercise); swaption.setPricingEngine(engine); GeneralStatistics stat = new GeneralStatistics(); for (int n=0; n<nrTrails; ++n) { Sample<MultiPath> path = (n%2!=0) ? generator.antithetic() : generator.next(); //Sample<MultiPath> path = generator.next(); List<double> rates_ = new InitializedList<double>(size); for (int k=0; k<process.size(); ++k) { rates_[k] = path.value[k][location[i]]; } List<double> dis = process.discountBond(rates_); double npv=0.0; for (int k=i; k < i+j; ++k) { npv += (swapRate - rates_[k]) * ( process.accrualEndTimes()[k] - process.accrualStartTimes()[k])*dis[k]; } stat.add(Math.Max(npv, 0.0)); } if (Math.Abs(swaption.NPV() - stat.mean()) > stat.errorEstimate()*2.35) Assert.Fail("Failed to reproduce swaption npv" + "\n calculated: " + stat.mean() + "\n expected: " + swaption.NPV()); } } } }
public override void calculate() { // this is an european option pricer Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option"); // plain vanilla PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); double v0 = model_.link.v0(); double spotPrice = model_.link.s0(); Utils.QL_REQUIRE(spotPrice > 0.0, () => "negative or null underlying given"); double strike = payoff.strike(); double term = model_.link.riskFreeRate().link.dayCounter().yearFraction( model_.link.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); double riskFreeDiscount = model_.link.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double dividendDiscount = model_.link.dividendYield().link.discount(arguments_.exercise.lastDate()); //average values TimeGrid timeGrid = model_.link.timeGrid(); int n = timeGrid.size() - 1; double kappaAvg = 0.0, thetaAvg = 0.0, sigmaAvg = 0.0, rhoAvg = 0.0; for (int i = 1; i <= n; ++i) { double t = 0.5 * (timeGrid[i - 1] + timeGrid[i]); kappaAvg += model_.link.kappa(t); thetaAvg += model_.link.theta(t); sigmaAvg += model_.link.sigma(t); rhoAvg += model_.link.rho(t); } kappaAvg /= n; thetaAvg /= n; sigmaAvg /= n; rhoAvg /= n; double c_inf = Math.Min(10.0, Math.Max(0.0001, Math.Sqrt(1.0 - Math.Pow(rhoAvg, 2)) / sigmaAvg)) * (v0 + kappaAvg * thetaAvg * term); double p1 = integration_.calculate(c_inf, new Fj_Helper(model_, term, strike, 1).value) / Const.M_PI; double p2 = integration_.calculate(c_inf, new Fj_Helper(model_, term, strike, 2).value) / Const.M_PI; switch (payoff.optionType()) { case Option.Type.Call: results_.value = spotPrice * dividendDiscount * (p1 + 0.5) - strike * riskFreeDiscount * (p2 + 0.5); break; case Option.Type.Put: results_.value = spotPrice * dividendDiscount * (p1 - 0.5) - strike * riskFreeDiscount * (p2 - 0.5); break; default: Utils.QL_FAIL("unknown option type"); break; } }