Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/americanpayoffathit.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2004 Ferdinando Ametrano
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
 <https://www.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/pricingengines/americanpayoffathit.hpp>
21
#include <ql/math/distributions/normaldistribution.hpp>
22
23
namespace QuantLib {
24
25
    AmericanPayoffAtHit::AmericanPayoffAtHit(
26
         Real spot, DiscountFactor discount, DiscountFactor dividendDiscount,
27
         Real variance, const ext::shared_ptr<StrikedTypePayoff>& payoff)
28
0
    : spot_(spot), discount_(discount), dividendDiscount_(dividendDiscount),
29
0
      variance_(variance) {
30
31
0
        QL_REQUIRE(spot_>0.0,
32
0
                   "positive spot value required");
33
34
0
        QL_REQUIRE(discount_>0.0,
35
0
                   "positive discount required");
36
37
0
        QL_REQUIRE(dividendDiscount_>0.0,
38
0
                   "positive dividend discount required");
39
40
0
        QL_REQUIRE(variance_>=0.0,
41
0
                   "negative variance not allowed");
42
43
0
        stdDev_ = std::sqrt(variance_);
44
45
0
        Option::Type type   = payoff->optionType();
46
0
        strike_ = payoff->strike();
47
48
49
0
        log_H_S_ = std::log(strike_/spot_);
50
51
0
        Real n_d1, n_d2;
52
0
        if (variance_>=QL_EPSILON) {
53
0
            if (discount_==0.0 && dividendDiscount_==0.0) {
54
0
                mu_     = - 0.5;
55
0
                lambda_ = 0.5;
56
0
            } else if (discount_==0.0) {
57
0
                QL_FAIL("null discount not handled yet");
58
0
            } else {
59
0
                mu_ = std::log(dividendDiscount_/discount_)/variance_ - 0.5;
60
0
                lambda_ = std::sqrt(mu_*mu_-2.0*std::log(discount_)/variance_);
61
0
            }
62
0
            D1_ = log_H_S_/stdDev_ + lambda_*stdDev_;
63
0
            D2_ = D1_ - 2.0*lambda_*stdDev_;
64
0
            CumulativeNormalDistribution f;
65
0
            cum_d1_ = f(D1_);
66
0
            cum_d2_ = f(D2_);
67
0
            n_d1 = f.derivative(D1_);
68
0
            n_d2 = f.derivative(D2_);
69
0
        } else {
70
            // not tested yet
71
0
            mu_ = std::log(dividendDiscount_/discount_)/variance_ - 0.5;
72
0
            lambda_ = std::sqrt(mu_*mu_-2.0*std::log(discount_)/variance_);
73
0
            if (log_H_S_>0) {
74
0
                cum_d1_= 1.0;
75
0
                cum_d2_= 1.0;
76
0
            } else {
77
0
                cum_d1_= 0.0;
78
0
                cum_d2_= 0.0;
79
0
            }
80
0
            n_d1 = 0.0;
81
0
            n_d2 = 0.0;
82
0
        }
83
84
85
0
        switch (type) {
86
            // up-and-in cash-(at-hit)-or-nothing option
87
            // a.k.a. american call with cash-or-nothing payoff
88
0
            case Option::Call:
89
0
                if (strike_>spot_) {
90
0
                    alpha_     = 1.0-cum_d1_;//  N(-d1)
91
0
                    DalphaDd1_ =    -  n_d1;// -n( d1)
92
0
                    beta_      = 1.0-cum_d2_;//  N(-d2)
93
0
                    DbetaDd2_  =    -  n_d2;// -n( d2)
94
0
                } else {
95
0
                    alpha_     = 0.5;
96
0
                    DalphaDd1_ = 0.0;
97
0
                    beta_      = 0.5;
98
0
                    DbetaDd2_  = 0.0;
99
0
                }
100
0
                break;
101
            // down-and-in cash-(at-hit)-or-nothing option
102
            // a.k.a. american put with cash-or-nothing payoff
103
0
            case Option::Put:
104
0
                if (strike_<spot_) {
105
0
                    alpha_     =     cum_d1_;//  N(d1)
106
0
                    DalphaDd1_ =       n_d1;//  n(d1)
107
0
                    beta_      =     cum_d2_;//  N(d2)
108
0
                    DbetaDd2_  =       n_d2;//  n(d2)
109
0
                } else {
110
0
                    alpha_     = 0.5;
111
0
                    DalphaDd1_ = 0.0;
112
0
                    beta_      = 0.5;
113
0
                    DbetaDd2_  = 0.0;
114
0
                }
115
0
                break;
116
0
            default:
117
0
                QL_FAIL("invalid option type");
118
0
         }
119
120
121
0
        muPlusLambda_  = mu_ + lambda_;
122
0
        muMinusLambda_ = mu_ - lambda_;
123
0
        inTheMoney_ = (type==Option::Call && strike_<spot_) ||
124
0
                      (type==Option::Put  && strike_>spot_);
125
126
0
        if (inTheMoney_) {
127
0
            forward_   = 1.0;
128
0
            X_         = 1.0;
129
0
            DXDstrike_ = 0.0;
130
0
        } else {
131
0
            forward_   = std::pow(strike_/spot_, muPlusLambda_);
132
0
            X_         = std::pow(strike_/spot_, muMinusLambda_);
133
//            DXDstrike_ = ......;
134
0
        }
135
136
137
        // Binary Cash-Or-Nothing payoff?
138
0
        ext::shared_ptr<CashOrNothingPayoff> coo =
139
0
            ext::dynamic_pointer_cast<CashOrNothingPayoff>(payoff);
140
0
        if (coo != nullptr) {
141
0
            K_ = coo->cashPayoff();
142
0
            DKDstrike_ = 0.0;
143
0
        }
144
145
        // Binary Asset-Or-Nothing payoff?
146
0
        ext::shared_ptr<AssetOrNothingPayoff> aoo =
147
0
            ext::dynamic_pointer_cast<AssetOrNothingPayoff>(payoff);
148
0
        if (aoo != nullptr) {
149
0
            if (inTheMoney_) {
150
0
                K_ = spot_;
151
0
                DKDstrike_ = 0.0;
152
0
            } else {
153
0
                K_ = aoo->strike();
154
0
                DKDstrike_ = 1.0;
155
0
            }
156
0
        }
157
0
    }
158
159
160
0
    Real AmericanPayoffAtHit::delta() const {
161
0
        Real tempDelta = - spot_ * stdDev_;
162
0
        Real DalphaDs = DalphaDd1_/tempDelta;
163
0
        Real DbetaDs  = DbetaDd2_/tempDelta;
164
165
0
        Real DforwardDs, DXDs;
166
0
        if (inTheMoney_) {
167
0
            DforwardDs = 0.0;
168
0
            DXDs       = 0.0;
169
0
        } else {
170
0
            DforwardDs = -muPlusLambda_  * forward_ / spot_;
171
0
            DXDs       = -muMinusLambda_ * X_       / spot_;
172
0
        }
173
174
0
        return K_ * (
175
0
              DalphaDs * forward_ + alpha_ * DforwardDs
176
0
            + DbetaDs  * X_       + beta_  * DXDs
177
0
            );
178
0
    }
179
180
181
0
    Real AmericanPayoffAtHit::gamma() const {
182
0
        Real tempDelta = - spot_ * stdDev_;
183
0
        Real DalphaDs = DalphaDd1_/tempDelta;
184
0
        Real DbetaDs  = DbetaDd2_/tempDelta;
185
0
        Real D2alphaDs2 = -DalphaDs/spot_*(1-D1_/stdDev_);
186
0
        Real D2betaDs2  = -DbetaDs /spot_*(1-D2_/stdDev_);
187
188
0
        Real DforwardDs, DXDs, D2forwardDs2, D2XDs2;
189
0
        if (inTheMoney_) {
190
0
            DforwardDs = 0.0;
191
0
            DXDs       = 0.0;
192
0
            D2forwardDs2 = 0.0;
193
0
            D2XDs2       = 0.0;
194
0
        } else {
195
0
            DforwardDs = -muPlusLambda_  * forward_ / spot_;
196
0
            DXDs       = -muMinusLambda_ * X_       / spot_;
197
0
            D2forwardDs2 = muPlusLambda_  * forward_ / (spot_*spot_)*(1+muPlusLambda_);
198
0
            D2XDs2       = muMinusLambda_ * X_       / (spot_*spot_)*(1+muMinusLambda_);
199
0
        }
200
201
0
        return K_ * (
202
0
              D2alphaDs2 * forward_   + DalphaDs * DforwardDs
203
0
            + DalphaDs   * DforwardDs + alpha_   * D2forwardDs2
204
0
            + D2betaDs2  * X_         + DbetaDs  * DXDs
205
0
            + DbetaDs    * DXDs       + beta_    * D2XDs2
206
0
            );
207
208
0
    }
209
210
211
0
    Real AmericanPayoffAtHit::rho(Time maturity) const {
212
0
        QL_REQUIRE(maturity>=0.0,
213
0
                   "negative maturity not allowed");
214
215
        // actually D.Dr / T
216
0
        Real DalphaDr = -DalphaDd1_/(lambda_*stdDev_) * (1.0 + mu_);
217
0
        Real DbetaDr  =  DbetaDd2_ /(lambda_*stdDev_) * (1.0 + mu_);
218
0
        Real DforwardDr, DXDr;
219
0
        if (inTheMoney_) {
220
0
            DforwardDr = 0.0;
221
0
            DXDr       = 0.0;
222
0
        } else {
223
0
            DforwardDr = forward_ * (1.0+(1.0+mu_)/lambda_) * log_H_S_ / variance_;
224
0
            DXDr       = X_       * (1.0-(1.0+mu_)/lambda_) * log_H_S_ / variance_;
225
0
        }
226
227
0
        return maturity * K_ * (
228
0
              DalphaDr * forward_
229
0
            + alpha_   * DforwardDr
230
0
            + DbetaDr  * X_
231
0
            + beta_    * DXDr
232
0
            );
233
0
    }
234
235
}
236