QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
blackdeltacalculator.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2010 Dimitri Reiswich
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include <ql/experimental/fx/blackdeltacalculator.hpp>
21
22namespace QuantLib {
23
25 Option::Type ot,
27 Real spot,
28 DiscountFactor dDiscount, // domestic discount
29 DiscountFactor fDiscount, // foreign discount
30 Real stdDev):
31 dt_(dt), ot_(ot),
32 dDiscount_(dDiscount), fDiscount_(fDiscount),
33 stdDev_(stdDev), spot_(spot),
34 forward_(spot*fDiscount/dDiscount), phi_(Integer(ot)) {
35
36 QL_REQUIRE(spot_>0.0,
37 "positive spot value required: " <<
38 spot_ << " not allowed");
39 QL_REQUIRE(dDiscount_>0.0,
40 "positive domestic discount factor required: " <<
41 dDiscount_ << " not allowed");
42 QL_REQUIRE(fDiscount_>0.0,
43 "positive foreign discount factor required: " <<
44 fDiscount_ << " not allowed");
45 QL_REQUIRE(stdDev_>=0.0,
46 "non-negative standard deviation required: "
47 << stdDev_ << " not allowed");
48
49 fExpPos_ =forward_*std::exp(0.5*stdDev_*stdDev_);
50 fExpNeg_ =forward_*std::exp(-0.5*stdDev_*stdDev_);
51 }
52
53
55
56 QL_REQUIRE(strike >=0.0,
57 "positive strike value required: " <<
58 strike << " not allowed");
59
60 Real res=0.0;
61
62 switch(dt_){
64 res=phi_*fDiscount_*cumD1(strike);
65 break;
66
68 res=phi_*cumD1(strike);
69 break;
70
72 res=phi_*fDiscount_*cumD2(strike)*strike/forward_;
73 break;
74
76 res=phi_*cumD2(strike)*strike/forward_;
77 break;
78
79 default:
80 QL_FAIL("invalid delta type");
81 }
82 return res;
83 }
84
86 return(strikeFromDelta(delta, dt_));
87 }
88
91 const{
92 Real res=0.0;
93 Real arg=0.0;
95
96 QL_REQUIRE(delta*phi_>=0.0, "Option type and delta are incoherent.");
97
98 switch (dt) {
100 QL_REQUIRE(std::fabs(delta)<=fDiscount_,
101 "Spot delta out of range.");
102
103 arg=-phi_*f(phi_*delta/fDiscount_)*stdDev_+0.5*stdDev_*stdDev_;
104 res=forward_*std::exp(arg);
105 break;
106
108 QL_REQUIRE(std::fabs(delta)<=1.0,
109 "Forward delta out of range.");
110
111 arg=-phi_*f(phi_*delta)*stdDev_+0.5*stdDev_*stdDev_;
112 res=forward_*std::exp(arg);
113 break;
114
117 // This has to be solved numerically. One of the
118 // problems is that the premium adjusted call delta is
119 // not monotonic in strike, such that two solutions
120 // might occur. The one right to the max of the delta is
121 // considered to be the correct strike. Some proper
122 // interval bounds for the strike need to be chosen, the
123 // numerics can otherwise be very unreliable and
124 // unstable. I've chosen Brent over Newton, since the
125 // interval can be specified explicitly and we can not
126 // run into the area on the left of the maximum. The
127 // put delta doesn't have this property and can be
128 // solved without any problems, but also numerically.
129
131 ot_, dt , spot_,dDiscount_, fDiscount_, stdDev_,delta);
132
133 Brent solver;
134 solver.setMaxEvaluations(1000);
135 Real accuracy = 1.0e-10;
136
137 Real rightLimit=0.0;
138 Real leftLimit=0.0;
139
140 // Strike of not premium adjusted is always to the right of premium adjusted
141 if (dt==DeltaVolQuote::PaSpot) {
142 rightLimit=strikeFromDelta(delta, DeltaVolQuote::Spot);
143 } else {
144 rightLimit=strikeFromDelta(delta, DeltaVolQuote::Fwd);
145 }
146
147 if (phi_<0) { // if put
148 res=solver.solve(f, accuracy, rightLimit, 0.0, spot_*100.0);
149 break;
150 } else {
151
152 // find out the left limit which is the strike
153 // corresponding to the value where premium adjusted
154 // deltas have their maximum.
155
158
159 leftLimit=solver.solve(g, accuracy, rightLimit*0.5,
160 0.0, rightLimit);
161
162 Real guess=leftLimit+(rightLimit-leftLimit)*0.5;
163
164 res=solver.solve(f, accuracy, guess, leftLimit, rightLimit);
165 } // end if phi<0 else
166
167 break;
168 }
169
170 default:
171 QL_FAIL("invalid delta type");
172 }
173
174 return res;
175 }
176
178
179 Real res=0.0;
180
181 switch(atmT) {
183 res=spot_;
184 break;
185
188 res=fExpPos_;
189 } else {
190 res=fExpNeg_;
191 }
192 break;
193
195 res=forward_;
196 break;
197
199 res=fExpPos_;
200 break;
201
203 QL_REQUIRE(dt_==DeltaVolQuote::Fwd,
204 "|PutDelta|=CallDelta=0.50 only possible for forward delta.");
205 res=fExpPos_;
206 break;
207
208 default:
209 QL_FAIL("invalid atm type");
210 }
211
212 return res;
213 }
214
215
217
218 Real d1_=0.0;
219 Real cum_d1_pos_ = 1.0; // N(d1)
220 Real cum_d1_neg_ = 0.0; // N(-d1)
221
223
224 if (stdDev_>=QL_EPSILON) {
225 if(strike>0) {
226 d1_ = std::log(forward_/strike)/stdDev_ + 0.5*stdDev_;
227 return f(phi_*d1_);
228 }
229 } else {
230 if (forward_<strike) {
231 cum_d1_pos_ = 0.0;
232 cum_d1_neg_ = 1.0;
233 } else if(forward_==strike){
234 d1_ = 0.5*stdDev_;
235 return f(phi_*d1_);
236 }
237 }
238
239 if (phi_>0) { // if Call
240 return cum_d1_pos_;
241 } else {
242 return cum_d1_neg_;
243 }
244 }
245
246
248
249 Real d1_=0.0;
250 Real n_d1_ = 0.0; // n(d1)
251
252 if (stdDev_>=QL_EPSILON){
253 if(strike>0){
254 d1_ = std::log(forward_/strike)/stdDev_ + 0.5*stdDev_;
256 n_d1_ = f.derivative(d1_);
257 }
258 }
259
260 return n_d1_;
261 }
262
263
265
266 Real d2_=0.0;
267 Real cum_d2_pos_= 1.0; // N(d2)
268 Real cum_d2_neg_= 0.0; // N(-d2)
269
271
272 if (stdDev_>=QL_EPSILON){
273
274 if(strike>0){
275 d2_ = std::log(forward_/strike)/stdDev_ - 0.5*stdDev_;
276 return f(phi_*d2_);
277 }
278
279 } else {
280
281 if (forward_<strike) {
282 cum_d2_pos_= 0.0;
283 cum_d2_neg_= 1.0;
284 } else if (forward_==strike) {
285 d2_ = -0.5*stdDev_;
286 return(f(phi_*d2_));
287 }
288
289 }
290
291 if (phi_>0) { // if Call
292 return cum_d2_pos_;
293 } else {
294 return cum_d2_neg_;
295 }
296 }
297
298
300
301 Real d2_=0.0;
302 Real n_d2_= 0.0; // n(d2)
303
304 if (stdDev_>=QL_EPSILON){
305 if(strike>0){
306 d2_ = std::log(forward_/strike)/stdDev_ - 0.5*stdDev_;
308 n_d2_ = f.derivative(d2_);
309 }
310 }
311
312 return n_d2_;
313 }
314
315
317 dt_=dt;
318 }
319
321 ot_=ot;
323 }
324
325
326 // helper classes
327
329 Option::Type ot,
331 Real spot,
332 DiscountFactor dDiscount, // domestic discount
333 DiscountFactor fDiscount, // foreign discount
334 Real stdDev,
335 Real delta):
336 bdc_(ot,dt,spot,dDiscount,fDiscount,stdDev), delta_(delta) {}
337
338
340 return bdc_.deltaFromStrike(strike)-delta_;
341 }
342
343
345 Option::Type ot,
347 Real spot,
348 DiscountFactor dDiscount, // domestic discount
349 DiscountFactor fDiscount, // foreign discount
350 Real stdDev):
351 bdc_(ot,dt,spot,dDiscount,fDiscount,stdDev), stdDev_(stdDev) {}
352
354 return bdc_.cumD2(strike)*stdDev_ - bdc_.nD2(strike);
355 }
356
357}
Real atmStrike(DeltaVolQuote::AtmType atmT) const
Real strikeFromDelta(Real delta) const
Real deltaFromStrike(Real strike) const
void setDeltaType(DeltaVolQuote::DeltaType dt)
BlackDeltaCalculator(Option::Type ot, DeltaVolQuote::DeltaType dt, Real spot, DiscountFactor dDiscount, DiscountFactor fDiscount, Real stdDev)
BlackDeltaPremiumAdjustedMaxStrikeClass(Option::Type ot, DeltaVolQuote::DeltaType dt, Real spot, DiscountFactor dDiscount, DiscountFactor fDiscount, Real stdDev)
BlackDeltaPremiumAdjustedSolverClass(Option::Type ot, DeltaVolQuote::DeltaType dt, Real spot, DiscountFactor dDiscount, DiscountFactor fDiscount, Real stdDev, Real delta)
Brent 1-D solver
Definition: brent.hpp:37
Cumulative normal distribution function.
Inverse cumulative normal distribution function.
void setMaxEvaluations(Size evaluations)
Definition: solver1d.hpp:238
Real solve(const F &f, Real accuracy, Real guess, Real step) const
Definition: solver1d.hpp:84
#define QL_EPSILON
Definition: qldefines.hpp:178
QL_REAL Real
real number
Definition: types.hpp:50
Real DiscountFactor
discount factor between dates
Definition: types.hpp:66
QL_INTEGER Integer
integer number
Definition: types.hpp:35
Definition: any.hpp:35