Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
aposurface.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <ql/exercise.hpp>
24
25using namespace QuantLib;
26using std::vector;
27
28namespace QuantExt {
29
30ApoFutureSurface::ApoFutureSurface(const Date& referenceDate, const vector<Real>& moneynessLevels,
31 const QuantLib::ext::shared_ptr<CommodityIndex>& index,
32 const Handle<PriceTermStructure>& pts, const Handle<YieldTermStructure>& yts,
33 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& expCalc,
34 const Handle<BlackVolTermStructure>& baseVts,
35 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& baseExpCalc, Real beta,
36 bool flatStrikeExtrapolation, const boost::optional<Period>& maxTenor)
37 : BlackVolatilityTermStructure(referenceDate, baseVts->calendar(), baseVts->businessDayConvention(),
38 baseVts->dayCounter()),
39 index_(index), baseExpCalc_(baseExpCalc), vols_(moneynessLevels.size()) {
40
41 // Checks.
42 QL_REQUIRE(!pts.empty(), "The price term structure should not be empty.");
43 QL_REQUIRE(!yts.empty(), "The yield term structure should not be empty.");
44 QL_REQUIRE(expCalc, "The expiry calculator should not be null.");
45 QL_REQUIRE(!baseVts.empty(), "The base volatility term structure should not be empty.");
46 QL_REQUIRE(!index_->priceCurve().empty(), "The commodity index should have a base price curve.");
47 QL_REQUIRE(baseExpCalc_, "The base expiry calculator should not be null.");
48
49 // Register with dependent term structures.
50 registerWith(pts);
51 registerWith(yts);
52 registerWith(baseVts);
53
54 // Determine the maximum expiry of the APO surface that we will build
55 Date maxDate;
56 if (maxTenor) {
57 // If maxTenor is provided, use it.
58 maxDate = referenceDate + *maxTenor;
59 } else {
60 maxDate = baseVts->maxDate();
61 if (maxDate == Date::maxDate() || maxDate == Date()) {
62 maxDate = pts->maxDate();
63 QL_REQUIRE(maxDate != Date::maxDate() && maxDate != Date(),
64 "Could not determine a maximum date for the ApoFutureSurface");
65 }
66 }
67 QL_REQUIRE(maxDate > referenceDate, "Expected the max date, " << io::iso_date(maxDate)
68 << ", to be greater than the reference date, "
69 << io::iso_date(referenceDate) << ".");
70
71 // Get the start and end dates of each APO that will be used to create the APO surface in the expiry direction.
72 // Here, we use the expiry calculator, expCalc. This expiry calculator will generally be from the corresponding
73 // averaging future contracts. We will be using this APO surface to price these averaging futures as standard
74 // non-averaging commodity options. The averaging future contract expiry will equal the APO expiry.
75 apoDates_ = { expCalc->priorExpiry(true, referenceDate) };
76 vector<Time> apoTimes;
77 while (apoDates_.back() < maxDate) {
78 apoDates_.push_back(expCalc->nextExpiry(false, apoDates_.back()));
79 apoTimes.push_back(timeFromReference(apoDates_.back()));
80 }
81
82 // Spot quote based on the price curve and adapted yield term structure
83 Handle<Quote> spot(QuantLib::ext::make_shared<DerivedPriceQuote>(pts));
84 Handle<YieldTermStructure> pyts =
85 Handle<YieldTermStructure>(QuantLib::ext::make_shared<PriceTermStructureAdapter>(*pts, *yts));
86 pyts->enableExtrapolation();
87
88 // Hard-code this to false.
89 bool stickyStrike = false;
90
91 // Initialise the matrix of quotes for use in vts_. These will be populated/updated in performCalculations by
92 // pricing all of the APOs and extracting the volatility from each of them. Rows are moneyness levels and columns
93 // are expiry times in the matrix of quotes that are fed to vts_'s ctor.
94 vector<vector<Handle<Quote> > > vols(moneynessLevels.size());
95 for (Size i = 0; i < moneynessLevels.size(); i++) {
96 for (Size j = 0; j < apoTimes.size(); j++) {
97 vols_[i].push_back(QuantLib::ext::make_shared<SimpleQuote>(0.0));
98 vols[i].push_back(Handle<Quote>(vols_[i].back()));
99 }
100 }
101
102 // Initialise the underlying helping volatility structure.
103 vts_ = QuantLib::ext::make_shared<BlackVarianceSurfaceMoneynessForward>(calendar_, spot, apoTimes, moneynessLevels, vols,
104 baseVts->dayCounter(), pyts, yts, stickyStrike,
105 flatStrikeExtrapolation);
106
107 vts_->enableExtrapolation();
108
109 // Initialise the engine for performing the APO valuations.
110 apoEngine_ = QuantLib::ext::make_shared<CommodityAveragePriceOptionAnalyticalEngine>(yts, baseVts, beta);
111}
112
113Date ApoFutureSurface::maxDate() const { return vts_->maxDate(); }
114
115Real ApoFutureSurface::minStrike() const { return vts_->minStrike(); }
116
117Real ApoFutureSurface::maxStrike() const { return vts_->maxStrike(); }
118
119void ApoFutureSurface::accept(AcyclicVisitor& v) {
120 if (auto v1 = dynamic_cast<Visitor<ApoFutureSurface>*>(&v))
121 v1->visit(*this);
122 else
123 QL_FAIL("Not an ApoFutureSurface visitor");
124}
125
127 TermStructure::update();
128 LazyObject::update();
129}
130
132
133 // Quantity is 1.0 everywhere below.
134 Real quantity = 1.0;
135
136 for (Size j = 1; j < apoDates_.size(); j++) {
137
138 // Set up the APO cashflow with from apoDates_[j-1] to apoDates_[j]
139 auto cf =
140 QuantLib::ext::make_shared<CommodityIndexedAverageCashFlow>(quantity, apoDates_[j - 1], apoDates_[j], apoDates_[j],
141 index_, Calendar(), 0.0, 1.0, true, 0, 0, baseExpCalc_);
142
143 // The APO cashflow forward rate is the amount
144 Real forward = cf->amount();
145
146 // Some of the sigmas will be Null<Real>() in the first APO period if the accrued is already greater than the
147 // strike. So store the sigmas in a vector in the first step and then update the quotes as a second step.
148 // idx will point to the first element in sigmas not equal to Null<Real>().
149 vector<Real> sigmas(vts_->moneyness().size());
150 for (Size i = 0; i < vts_->moneyness().size(); i++) {
151
152 // "exercise date" is just the last date of the APO cashflow.
153 auto exercise = QuantLib::ext::make_shared<EuropeanExercise>(apoDates_[j]);
154
155 // Apply moneyness to forward rate to get current strike
156 Real strike = vts_->moneyness()[i] * forward;
157
158 // Create the APO and call NPV() to trigger population of results.
159 CommodityAveragePriceOption apo(cf, exercise, 1.0, strike, Option::Call);
160 apo.setPricingEngine(apoEngine_);
161 apo.NPV();
162
163 auto it = apo.additionalResults().find("sigma");
164 if (it != apo.additionalResults().end())
165 sigmas[i] = boost::any_cast<Real>(it->second);
166 else
167 sigmas[i] = Null<Real>();
168 }
169
170 QL_REQUIRE(sigmas.back() != Null<Real>(), "All of the sigmas are null.");
171
172 for (Size i = vts_->moneyness().size(); i > 0; i--) {
173
174 // Know already that the last sigma is not null.
175 if (sigmas[i - 1] == Null<Real>())
176 sigmas[i - 1] = sigmas[i];
177
178 // Update the quote
179 vols_[i - 1][j - 1]->setValue(sigmas[i - 1]);
180 }
181 }
182}
183
184const QuantLib::ext::shared_ptr<BlackVarianceSurfaceMoneyness>& ApoFutureSurface::vts() const { return vts_; }
185
186Volatility ApoFutureSurface::blackVolImpl(Time t, Real strike) const {
187 calculate();
188 return vts_->blackVol(t, strike, true);
189}
190
191} // namespace QuantExt
Average future price option surface derived from future option surface.
std::vector< std::vector< QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > > > vols_
This will keep a handle on the APO vol quotes that are calculated.
Definition: aposurface.hpp:97
void performCalculations() const override
Definition: aposurface.cpp:131
QuantLib::Volatility blackVolImpl(QuantLib::Time t, QuantLib::Real strike) const override
Definition: aposurface.cpp:186
void accept(QuantLib::AcyclicVisitor &v) override
Definition: aposurface.cpp:119
QuantLib::ext::shared_ptr< BlackVarianceSurfaceMoneyness > vts_
The surface that is created to do the work.
Definition: aposurface.hpp:100
QuantLib::Real minStrike() const override
Definition: aposurface.cpp:115
const QuantLib::ext::shared_ptr< BlackVarianceSurfaceMoneyness > & vts() const
Definition: aposurface.cpp:184
QuantLib::ext::shared_ptr< CommodityIndex > index_
Definition: aposurface.hpp:90
QuantLib::Date maxDate() const override
Definition: aposurface.cpp:113
std::vector< QuantLib::Date > apoDates_
The APO schedule dates.
Definition: aposurface.hpp:94
ApoFutureSurface(const QuantLib::Date &referenceDate, const std::vector< QuantLib::Real > &moneynessLevels, const QuantLib::ext::shared_ptr< CommodityIndex > &index, const QuantLib::Handle< PriceTermStructure > &pts, const QuantLib::Handle< QuantLib::YieldTermStructure > &yts, const QuantLib::ext::shared_ptr< FutureExpiryCalculator > &expCalc, const QuantLib::Handle< QuantLib::BlackVolTermStructure > &baseVts, const QuantLib::ext::shared_ptr< FutureExpiryCalculator > &baseExpCalc, QuantLib::Real beta=0.0, bool flatStrikeExtrapolation=true, const boost::optional< QuantLib::Period > &maxTenor=boost::none)
Definition: aposurface.cpp:30
QuantLib::Real maxStrike() const override
Definition: aposurface.cpp:117
QuantLib::ext::shared_ptr< CommodityAveragePriceOptionBaseEngine > apoEngine_
The engine for valuing the APOs.
Definition: aposurface.hpp:103
QuantLib::ext::shared_ptr< FutureExpiryCalculator > baseExpCalc_
Definition: aposurface.hpp:91
Commodity Average Price Option.
Swaption class.
Cash flow dependent on the average commodity spot price or future's settlement price over a period....
PriceTermStructure adapter.