void StatisticsTest::testIncrementalStatistics() { BOOST_TEST_MESSAGE("Testing incremental statistics..."); // With QuantLib 1.7 IncrementalStatistics was changed to // a wrapper to the boost accumulator library. This is // a test of the new implementation against results from // the old one. MersenneTwisterUniformRng mt(42); IncrementalStatistics stat; for (Size i = 0; i < 500000; ++i) { Real x = 2.0 * (mt.nextReal() - 0.5) * 1234.0; Real w = mt.nextReal(); stat.add(x, w); } TEST_INC_STAT(stat.samples(), 500000); TEST_INC_STAT(stat.weightSum(), 2.5003623600676749e+05); TEST_INC_STAT(stat.mean(), 4.9122325964293845e-01); TEST_INC_STAT(stat.variance(), 5.0706503959683329e+05); TEST_INC_STAT(stat.standardDeviation(), 7.1208499464378076e+02); TEST_INC_STAT(stat.errorEstimate(), 1.0070402569876076e+00); TEST_INC_STAT(stat.skewness(), -1.7360169326720038e-03); TEST_INC_STAT(stat.kurtosis(), -1.1990742562085395e+00); TEST_INC_STAT(stat.min(), -1.2339945045639761e+03); TEST_INC_STAT(stat.max(), 1.2339958308008499e+03); TEST_INC_STAT(stat.downsideVariance(), 5.0786776146975247e+05); TEST_INC_STAT(stat.downsideDeviation(), 7.1264841364431061e+02); // This is a test for numerical stability, // where the old implementation fails InverseCumulativeRng<MersenneTwisterUniformRng,InverseCumulativeNormal> normal_gen(mt); IncrementalStatistics stat2; for (Size i = 0; i < 500000; ++i) { Real x = normal_gen.next().value * 1E-1 + 1E8; Real w = 1.0; stat2.add(x, w); } Real tol = 1E-5; if(std::fabs( stat2.variance() - 1E-2 ) > tol) BOOST_ERROR("variance (" << stat2.variance() << ") out of expected range " << 1E-2 << " +- " << tol); }
// evaluate fitting error on benchmark with parameter set Real HestonPriceBenchmark(Handle<Quote> &s0, std::vector<CALIB> &c, std::vector<RD> &rd, std::vector<double> p, double sdCutOff) { Date settlementDate(24, January, 2011); SavedSettings backup; Settings::instance().evaluationDate() = settlementDate; Real theta = p[0]; Real kappa = p[1]; Real sigma = p[2]; Real rho = p[3]; Real v0 = p[4]; std::cout << "Theta: " << theta << "\nKappa: " << kappa << "\nsigma: " << sigma << "\nrho: " << rho << "\nv0: " << v0 << std::endl; DayCounter dayCounter = Actual365Fixed(); Calendar calendar = TARGET(); std::vector<Date> dates; std::vector<Rate> rates; std::vector<Rate> div; // this is indispensable: expiry in Heston helper is computed from YC settlement date dates.push_back(settlementDate); rates.push_back(rd[0].R); div.push_back(rd[0].D); for (Size i = 0; i < rd.size(); ++i) { Integer days = 365*rd[i].T; dates.push_back(settlementDate + days); rates.push_back(rd[i].R); div.push_back(rd[i].D); } Date dtLast = settlementDate + ((int) 365*rd[rd.size()-1].T); std::cout << "Last curve date: " << dtLast << std::endl; Integer days = (rd[rd.size()-1].T+1)*365; dates.push_back(settlementDate + days); rates.push_back(rd[rd.size()-1].R); div.push_back(rd[rd.size()-1].D); std::cout << "Heston model Benchmark Pricing: TS..." << std::endl; Handle<YieldTermStructure> riskFreeTS( boost::shared_ptr<YieldTermStructure>( new ZeroCurve(dates, rates, dayCounter))); Handle<YieldTermStructure> dividendTS( boost::shared_ptr<YieldTermStructure>( new ZeroCurve(dates, div, dayCounter))); Volatility vol = 2.0; Handle<BlackVolTermStructure> volTS( boost::shared_ptr<BlackVolTermStructure>( new BlackConstantVol(settlementDate, calendar, vol, dayCounter))); boost::shared_ptr<BlackScholesMertonProcess> BSprocess( new BlackScholesMertonProcess(s0, dividendTS, riskFreeTS, volTS)); // boost::shared_ptr<PricingEngine> BSengine( // new AnalyticEuropeanEngine(BSprocess)); boost::shared_ptr<HestonProcess> process(new HestonProcess( riskFreeTS, dividendTS, s0, v0, kappa, theta, sigma, rho)); boost::shared_ptr<PricingEngine> engine(new AnalyticHestonEngine( boost::shared_ptr<HestonModel>(new HestonModel(process)), 64)); std::cout << "Heston model Benchmark Pricing: engine set ..." << std::endl; IncrementalStatistics errorStat; vector<Real> error; ofstream myfile; Volatility ivol; Real errorBid; Real errorAsk; // myfile.open ("dump.csv", ios::out|ios::trunc); // this is the default // myfile << "T, dm, y, CP, PB, Calc, PA, Vega, iVol, ErrorBid, ErrorAsk" << std::endl; for (Size i = 0; i < c.size(); ++i) { Integer days = 365*c[i].T; //std::cout << "Expiry dates: " << dtExpiry << std::endl; Period maturity((int)((days+3)/7.), Weeks); // round to weeks Date dtExpiry = calendar.advance(riskFreeTS->referenceDate(), maturity); if (i==0) std::cout << dtExpiry << std::endl; // Time tau = riskFreeTS->dayCounter().yearFraction( // riskFreeTS->referenceDate(), dtExpiry); boost::shared_ptr<StrikedTypePayoff> payoff( new PlainVanillaPayoff(Option::Call, c[i].K)); boost::shared_ptr<Exercise> exercise(new EuropeanExercise(dtExpiry)); VanillaOption option(payoff, exercise); option.setPricingEngine(engine); Real calculated = option.NPV(); // option.setPricingEngine(BSengine); if (calculated >0) { try { ivol = option.impliedVolatility(calculated, BSprocess, 1.e-6, 1000, .1, 10); errorBid = std::max((c[i].VB-ivol), 0.0)*100; errorAsk = std::max((ivol-c[i].VA), 0.0)*100; } catch (...) { errorBid = 0.0; errorAsk = 0.0; }; } else { vol = 0.0; errorAsk = 0.0; errorBid = 0.0; }; error.push_back(errorBid+errorAsk); // myfile << c[i].T << "," << dtExpiry << "," << c[i].CP << "," << c[i].PB << "," << calculated << "," << c[i].PA << "," << c[i].Vega << "," << ivol << "," << errorBid << "," << errorAsk << std::endl; } // remove outliers and calculate sse errorStat.addSequence(error.begin(), error.end()); Real sse = errorStat.standardDeviation(); for (Size i=0; i<error.size(); ++i) if (abs(error[i]) > sdCutOff*sse) error[i] = 0.0; errorStat.reset(); errorStat.addSequence(error.begin(), error.end()); sse = errorStat.standardDeviation(); return sse; }