57 {
58
59 BOOST_TEST_MESSAGE("Testing Commodity APO Analytical Approximation vs MC Pricing");
60
61 SavedSettings backup;
62
63 Date today(5, Feb, 2019);
64 Settings::instance().evaluationDate() = today;
65 Calendar cal = UnitedStates(UnitedStates::Settlement);
66
67
68 std::vector<Date> dates = {today + 1 * Years, today + 5 * Years, today + 10 * Years};
69 std::vector<Real> prices = {100.0, 100.0, 100.0};
70 DayCounter dc = Actual365Fixed();
71 Handle<QuantExt::PriceTermStructure> priceCurve(
73 priceCurve->enableExtrapolation();
74
75
76 Handle<Quote> rateQuote(QuantLib::ext::make_shared<SimpleQuote>(0.01));
77 Handle<YieldTermStructure> discountCurve(QuantLib::ext::make_shared<FlatForward>(today, rateQuote, dc, Compounded, Annual));
78
79
80 Handle<QuantLib::BlackVolTermStructure> vol(QuantLib::ext::make_shared<QuantLib::BlackConstantVol>(today, cal, 0.3, dc));
81
82
83 Real beta = 0.0;
84 QuantLib::ext::shared_ptr<PricingEngine> analyticalEngine =
85 QuantLib::ext::make_shared<CommodityAveragePriceOptionAnalyticalEngine>(discountCurve, vol, beta);
86
87
88 Size samples = 10000;
89 QuantLib::ext::shared_ptr<PricingEngine> mcEngine =
90 QuantLib::ext::make_shared<CommodityAveragePriceOptionMonteCarloEngine>(discountCurve, vol, samples, beta);
91
92
93 Real quantity = 1.0;
94 std::string
name =
"CL";
95 Period term = 1 * Months;
96
97
98 std::vector<ApoTestCase> cases = {{100.0, Option::Call}, {120.0, Option::Call}, {80.0, Option::Call},
99 {100.0, Option::Put}, {120.0, Option::Put}, {80.0, Option::Put}};
100
101 for (Size k = 0; k < cases.size(); ++k) {
102
103 Real strikePrice = cases[k].strikePrice;
104 QuantLib::Option::Type optionType = cases[k].optionType;
105 std::string optionTypeString = (optionType == Option::Call ? "Call" : "Put");
106
107
108 for (Size i = 1; i <= 10; ++i) {
109
110
111 Period startTerm = i * Years;
112 Date startDate = today + startTerm;
113 Date endDate = startDate + term;
114 Date payDate = endDate;
115 QuantLib::ext::shared_ptr<CommoditySpotIndex> index = QuantLib::ext::make_shared<CommoditySpotIndex>(
name, cal, priceCurve);
116 QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> flow =
117 QuantLib::ext::make_shared<CommodityIndexedAverageCashFlow>(quantity, startDate, endDate, payDate, index);
118 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<EuropeanExercise>(endDate);
119
120 QuantLib::ext::shared_ptr<CommodityAveragePriceOption> apo =
121 QuantLib::ext::make_shared<CommodityAveragePriceOption>(flow, exercise, quantity, strikePrice, optionType);
122
123 boost::timer::cpu_timer tan;
124 apo->setPricingEngine(analyticalEngine);
125 Real anPrice = apo->NPV();
126 tan.stop();
127 Real anTime = tan.elapsed().wall * 1e-6;
128
129 boost::timer::cpu_timer tmc;
130 apo->setPricingEngine(mcEngine);
131 Real mcPrice = apo->NPV();
132 Real mcTime = tmc.elapsed().wall * 1e-6;
133
134 Real error1 = 100.0 * fabs(mcPrice - anPrice) / mcPrice;
135 Real error2 = 100.0 * fabs(mcPrice - anPrice) / anPrice;
136
137 BOOST_TEST_MESSAGE(optionTypeString << " " << std::fixed << std::setprecision(2) << strikePrice << " "
138 << startTerm << " Analytical vs MC price: " << std::fixed
139 << std::setprecision(2) << anPrice << " vs " << mcPrice << " diff "
140 << anPrice - mcPrice << " error " << std::max(error1, error2) << "% ("
141 << anTime << " ms, " << mcTime << " ms)");
142 BOOST_CHECK_CLOSE(anPrice, mcPrice, 1.0);
143 }
144 }
145}