17 #include <boost/test/unit_test.hpp>
18 #include <oret/toplevelfixture.hpp>
20 #include <boost/algorithm/string/replace.hpp>
21 #include <boost/make_shared.hpp>
23 #include <ql/currencies/america.hpp>
24 #include <ql/instruments/asianoption.hpp>
25 #include <ql/math/interpolations/linearinterpolation.hpp>
26 #include <ql/pricingengines/blackformula.hpp>
27 #include <ql/termstructures/volatility/equityfx/blackvariancecurve.hpp>
28 #include <ql/termstructures/yield/flatforward.hpp>
29 #include <ql/time/daycounters/actual360.hpp>
42using namespace boost::unit_test_framework;
43using namespace boost::algorithm;
52 TestMarket(
const Real spot,
const Date& expiry,
const Rate riskFreeRate,
const Rate convenienceYield,
53 const Volatility flatVolatility)
56 asof_ = Date(01, Feb, 2021);
58 DayCounter dayCounter = Actual360();
61 Handle<YieldTermStructure> discount(QuantLib::ext::make_shared<FlatForward>(asof_, riskFreeRate, dayCounter));
65 vector<Date> dates = {
asof_, expiry};
66 vector<Real> prices = {
67 spot, spot * std::exp((riskFreeRate - convenienceYield) * dayCounter.yearFraction(asof_, expiry))};
69 asof_, dates, prices, dayCounter, USDCurrency()));
70 Handle<CommodityIndex> commIdx(QuantLib::ext::make_shared<CommoditySpotIndex>(
"ALU_USD", NullCalendar(), priceCurve));
74 Handle<BlackVolTermStructure> volatility(
75 QuantLib::ext::make_shared<BlackConstantVol>(asof_, TARGET(), flatVolatility, dayCounter));
80 struct DiscreteAsianTestData {
84 Rate convenienceYield;
95 BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
97 BOOST_AUTO_TEST_SUITE(CommodityAsianOptionTests)
101 BOOST_TEST_MESSAGE(
"Testing commodity Asian option trade building with constant vol term structure");
115 std::vector<DiscreteAsianTestData> asians = {
116 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 2, 0.13, 1.3942835683},
117 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 4, 0.13, 1.5852442983},
118 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 8, 0.13, 1.66970673},
119 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 12, 0.13, 1.6980019214},
120 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 26, 0.13, 1.7255070456},
121 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 52, 0.13, 1.7401553533},
122 {Option::Put, 90.0, 87.0, 0.06, 0.025, 0.0, 11.0 / 12.0, 100, 0.13, 1.7478303712},
123 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 2, 0.13, 1.8496053697},
124 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 4, 0.13, 2.0111495205},
125 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 8, 0.13, 2.0852138818},
126 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 12, 0.13, 2.1105094397},
127 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 26, 0.13, 2.1346526695},
128 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 52, 0.13, 2.147489651},
129 {Option::Put, 90.0, 87.0, 0.06, 0.025, 1.0 / 12.0, 11.0 / 12.0, 100, 0.13, 2.154728109},
130 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 2, 0.13, 2.63315092584},
131 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 4, 0.13, 2.76723962361},
132 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 8, 0.13, 2.83124836881},
133 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 12, 0.13, 2.84290301412},
134 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 26, 0.13, 2.88179560417},
135 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 52, 0.13, 2.88447044543},
136 {Option::Put, 90.0, 87.0, 0.06, 0.025, 3.0 / 12.0, 11.0 / 12.0, 100, 0.13, 2.89985329603}};
138 Date asof = Date(01, Feb, 2021);
140 QuantLib::ext::shared_ptr<EngineFactory> engineFactory;
141 QuantLib::ext::shared_ptr<Market> market;
143 for (
const auto& a : asians) {
144 Time deltaT = a.length / (a.fixings - 1);
146 vector<Date> fixingDates(a.fixings);
147 vector<std::string> strFixingDates(a.fixings);
148 for (Size i = 0; i < a.fixings; ++i) {
149 fixingDates[i] = (asof +
static_cast<Integer
>((a.firstFixing + i * deltaT) * 360 + 0.5));
150 strFixingDates[i] =
to_string(fixingDates[i]);
152 expiry = fixingDates[a.fixings - 1];
154 ScheduleDates scheduleDates(
"NullCalendar",
"",
"", strFixingDates);
157 market = QuantLib::ext::make_shared<TestMarket>(a.spot, expiry, a.riskFreeRate, a.convenienceYield, a.volatility);
158 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
159 std::string productName =
"CommodityAsianOptionArithmeticPrice";
160 engineData->model(productName) =
"BlackScholesMerton";
161 engineData->engine(productName) =
"MCDiscreteArithmeticAPEngine";
162 engineData->engineParameters(productName) = {{
"ProcessType",
"Discrete"}, {
"BrownianBridge",
"True"},
163 {
"AntitheticVariate",
"False"}, {
"ControlVariate",
"True"},
164 {
"RequiredSamples",
"2047"}, {
"Seed",
"0"}};
165 engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
168 Settings::instance().evaluationDate() = market->asofDate();
172 OptionData optionData(
"Long",
to_string(a.type),
"European",
true, {to_string(expiry)},
"Cash",
"",
173 premiumData, vector<Real>(), vector<Real>(),
"",
"",
"", vector<string>(),
174 vector<string>(),
"",
"",
"",
"Asian",
"Arithmetic", boost::none, boost::none,
177 QuantLib::ext::shared_ptr<CommodityAsianOption> asianOption = QuantLib::ext::make_shared<CommodityAsianOption>(
178 env,
"CommodityAsianOption", 1.0,
TradeStrike(a.strike,
"USD"), optionData, scheduleData,
179 QuantLib::ext::make_shared<CommodityUnderlying>(
"ALU_USD", 1.0,
"Spot", 0, 0,
""), Date(),
"USD");
180 BOOST_CHECK_NO_THROW(asianOption->build(engineFactory));
183 QuantLib::ext::shared_ptr<Instrument> qlInstrument = asianOption->instrument()->qlInstrument();
185 QuantLib::ext::shared_ptr<DiscreteAveragingAsianOption> discreteAsian =
186 QuantLib::ext::dynamic_pointer_cast<DiscreteAveragingAsianOption>(qlInstrument);
188 BOOST_CHECK(discreteAsian);
189 BOOST_CHECK_EQUAL(discreteAsian->exercise()->type(), Exercise::Type::European);
190 BOOST_CHECK_EQUAL(discreteAsian->exercise()->dates().size(), 1);
191 BOOST_CHECK_EQUAL(discreteAsian->exercise()->dates()[0], expiry);
193 QuantLib::ext::shared_ptr<TypePayoff> payoff = QuantLib::ext::dynamic_pointer_cast<TypePayoff>(discreteAsian->payoff());
195 BOOST_CHECK_EQUAL(payoff->optionType(), a.type);
197 Real expectedPrice = a.expectedNPV;
200 BOOST_CHECK_SMALL(asianOption->instrument()->NPV() - expectedPrice, 2e-2);
207 BOOST_TEST_MESSAGE(
"Testing parsing of commodity Asian option trade from XML");
211 tradeXml.append(
"<Portfolio>");
212 tradeXml.append(
" <Trade id=\"CommodityAsianOption_Alu\">");
213 tradeXml.append(
" <TradeType>CommodityAsianOption</TradeType>");
214 tradeXml.append(
" <Envelope>");
215 tradeXml.append(
" <CounterParty>CPTY_A</CounterParty>");
216 tradeXml.append(
" <NettingSetId>CPTY_A</NettingSetId>");
217 tradeXml.append(
" <AdditionalFields/>");
218 tradeXml.append(
" </Envelope>");
219 tradeXml.append(
" <CommodityAsianOptionData>");
220 tradeXml.append(
" <OptionData>");
221 tradeXml.append(
" <LongShort>Long</LongShort>");
222 tradeXml.append(
" <OptionType>Call</OptionType>");
223 tradeXml.append(
" <Style>European</Style>");
224 tradeXml.append(
" <Settlement>Cash</Settlement>");
225 tradeXml.append(
" <PayOffAtExpiry>false</PayOffAtExpiry>");
226 tradeXml.append(
" <PayoffType>Asian</PayoffType>");
227 tradeXml.append(
" <PayoffType2>Arithmetic</PayoffType2>");
228 tradeXml.append(
" <ExerciseDates>");
229 tradeXml.append(
" <ExerciseDate>2021-02-26</ExerciseDate>");
230 tradeXml.append(
" </ExerciseDates>");
231 tradeXml.append(
" </OptionData>");
232 tradeXml.append(
" <ObservationDates>");
233 tradeXml.append(
" <Dates>");
234 tradeXml.append(
" <Dates>");
235 tradeXml.append(
" <Date>2021-02-01</Date>");
236 tradeXml.append(
" <Date>2021-02-02</Date>");
237 tradeXml.append(
" <Date>2021-02-03</Date>");
238 tradeXml.append(
" <Date>2021-02-04</Date>");
239 tradeXml.append(
" <Date>2021-02-05</Date>");
240 tradeXml.append(
" <Date>2021-02-08</Date>");
241 tradeXml.append(
" <Date>2021-02-09</Date>");
242 tradeXml.append(
" <Date>2021-02-10</Date>");
243 tradeXml.append(
" <Date>2021-02-11</Date>");
244 tradeXml.append(
" <Date>2021-02-12</Date>");
245 tradeXml.append(
" <Date>2021-02-15</Date>");
246 tradeXml.append(
" <Date>2021-02-16</Date>");
247 tradeXml.append(
" <Date>2021-02-17</Date>");
248 tradeXml.append(
" <Date>2021-02-18</Date>");
249 tradeXml.append(
" <Date>2021-02-19</Date>");
250 tradeXml.append(
" <Date>2021-02-22</Date>");
251 tradeXml.append(
" <Date>2021-02-23</Date>");
252 tradeXml.append(
" <Date>2021-02-24</Date>");
253 tradeXml.append(
" <Date>2021-02-25</Date>");
254 tradeXml.append(
" <Date>2021-02-26</Date>");
255 tradeXml.append(
" </Dates>");
256 tradeXml.append(
" </Dates>");
257 tradeXml.append(
" </ObservationDates>");
258 tradeXml.append(
" <Underlying>");
259 tradeXml.append(
" <Type>Commodity</Type>");
260 tradeXml.append(
" <Name>ALU_USD</Name>");
261 tradeXml.append(
" </Underlying>");
262 tradeXml.append(
" <IsFuturePrice>false</IsFuturePrice>");
263 tradeXml.append(
" <Currency>USD</Currency>");
264 tradeXml.append(
" <Strike>2270</Strike>");
265 tradeXml.append(
" <Quantity>1</Quantity>");
266 tradeXml.append(
" </CommodityAsianOptionData>");
267 tradeXml.append(
" </Trade>");
268 tradeXml.append(
"</Portfolio>");
275 QuantLib::ext::shared_ptr<Trade> trade = portfolio.
trades().begin()->second;
276 QuantLib::ext::shared_ptr<CommodityAsianOption> option =
277 QuantLib::ext::dynamic_pointer_cast<ore::data::CommodityAsianOption>(trade);
278 BOOST_CHECK(option !=
nullptr);
282 BOOST_CHECK_EQUAL(option->tradeType(),
"CommodityAsianOption");
283 BOOST_CHECK_EQUAL(option->id(),
"CommodityAsianOption_Alu");
285 BOOST_CHECK_EQUAL(option->payCurrency(),
"USD");
286 BOOST_CHECK_EQUAL(option->strike().value(), 2270);
287 BOOST_CHECK_EQUAL(option->quantity(), 1);
288 BOOST_CHECK_EQUAL(option->option().longShort(),
"Long");
289 BOOST_CHECK_EQUAL(option->option().callPut(),
"Call");
290 BOOST_CHECK_EQUAL(option->option().style(),
"European");
291 BOOST_CHECK_EQUAL(option->option().exerciseDates().size(), 1);
292 BOOST_CHECK_EQUAL(option->option().exerciseDates()[0],
"2021-02-26");
293 BOOST_CHECK(option->observationDates().hasData());
295 BOOST_CHECK_EQUAL(option->option().payoffType(),
"Asian");
296 BOOST_CHECK_EQUAL(option->option().payoffType2(),
"Arithmetic");
299 BOOST_AUTO_TEST_SUITE_END()
301 BOOST_AUTO_TEST_SUITE_END()
Asian option representation.
Serializable object holding generic trade data, reporting dimensions.
static const string defaultConfiguration
Default configuration label.
map< tuple< string, YieldCurveType, string >, Handle< YieldTermStructure > > yieldCurves_
map< pair< string, string >, QuantLib::Handle< QuantExt::CommodityIndex > > commodityIndices_
map< pair< string, string >, QuantLib::Handle< QuantLib::BlackVolTermStructure > > commodityVols_
Serializable object holding option data.
const std::map< std::string, QuantLib::ext::shared_ptr< Trade > > & trades() const
Return the map tradeId -> trade.
Serializable object holding premium data.
Serializable schedule data.
Serializable object holding schedule Dates data.
void fromXMLString(const std::string &xml)
Parse from XML string.
Engine builder for commodity Asian options.
An implementation of the Market class that stores the required objects in maps.
std::string to_string(const LocationInfo &l)
trade option data model and serialization
BOOST_AUTO_TEST_CASE(testCommodityAsianOptionTradeBuilding)
string conversion utilities