Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
analyticcashsettledeuropeanengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 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/event.hpp>
20#include <ql/exercise.hpp>
23
24using QuantLib::close;
25using QuantLib::Date;
26using QuantLib::DiscountFactor;
27using QuantLib::GeneralizedBlackScholesProcess;
28using QuantLib::Handle;
29using QuantLib::Null;
30using QuantLib::PricingEngine;
31using QuantLib::Real;
32using QuantLib::Settings;
33using QuantLib::StrikedTypePayoff;
34using QuantLib::Time;
35using QuantLib::VanillaOption;
36using QuantLib::YieldTermStructure;
37using QuantLib::detail::simple_event;
38
39namespace QuantExt {
40
42 const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& bsp)
43 : underlyingEngine_(bsp), bsp_(bsp) {
44 registerWith(bsp_);
45}
46
47AnalyticCashSettledEuropeanEngine::AnalyticCashSettledEuropeanEngine(
48 const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& bsp, const Handle<YieldTermStructure>& discountCurve)
49 : underlyingEngine_(bsp, discountCurve), bsp_(bsp), discountCurve_(discountCurve) {
50 registerWith(bsp_);
51 registerWith(discountCurve_);
52}
53
54void AnalyticCashSettledEuropeanEngine::calculate() const {
55
56 // Same logic as underlying engine for discount curve.
57 QuantLib::ext::shared_ptr<YieldTermStructure> dts =
58 discountCurve_.empty() ? bsp_->riskFreeRate().currentLink() : discountCurve_.currentLink();
59
60 // Option expiry date.
61 Date expiryDate = arguments_.exercise->lastDate();
62
63 Date today = Settings::instance().evaluationDate();
64 if (expiryDate <= today) {
65 // If expiry has occurred, we attempt to establish the payoff amount, if any, and discount it.
66 Real payoffAmount = 0.0;
67 Real priceAtExercise = 0.0;
68 bool deterministicPayoff = true;
69 if (arguments_.automaticExercise) {
70 // If we have automatic exercise, we base the payoff on the value of the index on the expiry date.
71 QL_REQUIRE(arguments_.underlying, "Expect a valid underlying index when exercise is automatic.");
72 priceAtExercise = arguments_.underlying->fixing(expiryDate);
73 payoffAmount = (*arguments_.payoff)(priceAtExercise);
74 } else if (arguments_.exercised) {
75 // If we have manually exercised, we base the payoff on the value at exercise.
76 QL_REQUIRE(arguments_.priceAtExercise != Null<Real>(), "Expect a valid price at exercise when option "
77 << "has been manually exercised.");
78 priceAtExercise = arguments_.priceAtExercise;
79 payoffAmount = (*arguments_.payoff)(priceAtExercise);
80 } else if (expiryDate == today) {
81 // Expiry date is today, not automatic exercise and hasn't been manually exercised - use spot.
82 priceAtExercise = bsp_->x0();
83 payoffAmount = (*arguments_.payoff)(priceAtExercise);
84 deterministicPayoff = false;
85 }
86
87 if(deterministicPayoff) {
88 results_.delta = 0.0;
89 results_.deltaForward = 0.0;
90 results_.elasticity = 0.0;
91 results_.gamma = 0.0;
92 results_.dividendRho = 0.0;
93 results_.vega = 0.0;
94 }
95
96 // Discount factor to payment date.
97 DiscountFactor df_tp = dts->discount(arguments_.paymentDate);
98 Time delta_tp = dts->timeFromReference(arguments_.paymentDate);
99
100 // Only value, rho and theta are meaningful now.
101 results_.value = df_tp * payoffAmount;
102 results_.rho = -delta_tp * results_.value;
103 results_.theta = 0.0;
104 if (delta_tp > 0.0 && !close(delta_tp, 0.0)) {
105 results_.theta = -std::log(df_tp) / delta_tp * results_.value;
106 }
107 results_.thetaPerDay = results_.theta / 365.0;
108
109 // Populate some additional results.
110 results_.additionalResults["spot"] = bsp_->x0();
111 auto payoff = QuantLib::ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
112 if (payoff)
113 results_.additionalResults["strike"] = payoff->strike();
114 results_.additionalResults["priceAtExercise"] = priceAtExercise;
115 results_.additionalResults["payoffAmount"] = payoffAmount;
116 results_.additionalResults["discountFactor"] = df_tp;
117 results_.additionalResults["timeToExpiry"] = delta_tp;
118
119 } else {
120
121 // If expiry has not occurred, we use the underlying engine and amend the results to account
122 // for the deferred cash payment.
123
124 // Prepare the underlying engine for the valuation.
125 underlyingEngine_.reset();
126 VanillaForwardOption::arguments* underlyingArgs =
127 dynamic_cast<VanillaForwardOption::arguments*>(underlyingEngine_.getArguments());
128 QL_REQUIRE(underlyingArgs, "Underlying engine expected to have vanilla option arguments.");
129 underlyingArgs->exercise = arguments_.exercise;
130 underlyingArgs->payoff = arguments_.payoff;
131
132 // If we have a commodity future index, set the forward date to its expiry to get the right future price
133 // in the Black formula. If not, just use the expiry date => same result as standard QL engine.
134 if (auto cfi = QuantLib::ext::dynamic_pointer_cast<CommodityFuturesIndex>(arguments_.underlying)) {
135 underlyingArgs->forwardDate = cfi->expiryDate();
136 } else {
137 underlyingArgs->forwardDate = expiryDate;
138 }
139 underlyingEngine_.calculate();
140
141 // Discount factor from payment date back to expiry date i.e. P(t_e, t_p) when rates are deterministic.
142 DiscountFactor df_te_tp = dts->discount(arguments_.paymentDate) / dts->discount(expiryDate);
143 Time delta_te_tp = dts->timeFromReference(arguments_.paymentDate) - dts->timeFromReference(expiryDate);
144
145 // Populate this engine's results using the results from the underlying engine.
146 const CashSettledEuropeanOption::results* underlyingResults =
147 dynamic_cast<const CashSettledEuropeanOption::results*>(underlyingEngine_.getResults());
148 QL_REQUIRE(underlyingResults, "Underlying engine expected to have compatible results.");
149
150 results_.value = df_te_tp * underlyingResults->value;
151 results_.delta = df_te_tp * underlyingResults->delta;
152 results_.deltaForward = df_te_tp * underlyingResults->deltaForward;
153 results_.elasticity = underlyingResults->elasticity;
154 results_.gamma = df_te_tp * underlyingResults->gamma;
155 results_.rho = df_te_tp * (underlyingResults->rho - delta_te_tp * underlyingResults->value);
156 results_.dividendRho = df_te_tp * underlyingResults->dividendRho;
157 results_.vega = df_te_tp * underlyingResults->vega;
158 if (underlyingResults->theta != Null<Real>())
159 results_.theta = df_te_tp * underlyingResults->theta;
160 if (underlyingResults->thetaPerDay != Null<Real>())
161 results_.thetaPerDay = df_te_tp * underlyingResults->thetaPerDay;
162 results_.strikeSensitivity = df_te_tp * underlyingResults->strikeSensitivity;
163 results_.itmCashProbability = underlyingResults->itmCashProbability;
164
165 // Take the additional results from the underlying engine and add more.
166 results_.additionalResults = underlyingResults->additionalResults;
167 results_.additionalResults["discountFactorTeTp"] = df_te_tp;
168 }
169
170}
171
172} // namespace QuantExt
pricing engine for cash settled European vanilla options.
const Instrument::results * results_
Definition: cdsoption.cpp:81
AnalyticCashSettledEuropeanEngine(const QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > &bsp)
Arguments for Vanilla Forward Option calculation
commodity index class for holding commodity spot and futures price histories and forwarding.
Swap::arguments * arguments_