Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/pricingengines/americanpayoffatexpiry.cpp
Line
Count
Source (jump to first uncovered line)
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/americanpayoffatexpiry.hpp>
21
#include <ql/math/distributions/normaldistribution.hpp>
22
23
namespace QuantLib {
24
25
    AmericanPayoffAtExpiry::AmericanPayoffAtExpiry(
26
         Real spot, DiscountFactor discount, DiscountFactor dividendDiscount,
27
         Real variance, const ext::shared_ptr<StrikedTypePayoff>& payoff,
28
         bool knock_in)
29
0
    : spot_(spot), discount_(discount), dividendDiscount_(dividendDiscount),
30
0
      variance_(variance), knock_in_(knock_in) {
31
32
0
        QL_REQUIRE(spot_>0.0,
33
0
                   "positive spot value required");
34
35
0
        QL_REQUIRE(discount_>0.0,
36
0
                   "positive discount required");
37
38
0
        QL_REQUIRE(dividendDiscount_>0.0,
39
0
                   "positive dividend discount required");
40
41
0
        QL_REQUIRE(variance_>=0.0,
42
0
                   "negative variance not allowed");
43
44
0
        stdDev_ = std::sqrt(variance_);
45
46
0
        Option::Type type   = payoff->optionType();
47
0
        strike_ = payoff->strike();
48
0
        forward_ = spot_ * dividendDiscount_ / discount_;
49
50
0
        mu_ = std::log(dividendDiscount_/discount_)/variance_ - 0.5;
51
52
        // binary cash-or-nothing payoff?
53
0
        ext::shared_ptr<CashOrNothingPayoff> coo =
54
0
            ext::dynamic_pointer_cast<CashOrNothingPayoff>(payoff);
55
0
        if (coo != nullptr) {
56
0
            K_ = coo->cashPayoff();
57
0
        }
58
59
        // binary asset-or-nothing payoff?
60
0
        ext::shared_ptr<AssetOrNothingPayoff> aoo =
61
0
            ext::dynamic_pointer_cast<AssetOrNothingPayoff>(payoff);
62
0
        if (aoo != nullptr) {
63
0
            K_ = forward_;
64
0
            mu_ += 1.0;
65
0
        }
66
67
68
0
        log_H_S_ = std::log(strike_/spot_);
69
0
        Real log_S_H_ = std::log(spot_/strike_);
70
71
0
        double eta;
72
0
        double phi;
73
0
        switch (type) {
74
0
            case Option::Call:
75
0
                if (knock_in_) {
76
                   // up-and-in cash-(at-expiry)-or-nothing option
77
                   // a.k.a. american call with cash-or-nothing payoff
78
0
                   eta = -1.0;
79
0
                   phi =  1.0;
80
0
                } else {
81
                   // up-and-out cash-(at-expiry)-or-nothing option
82
0
           eta = -1.0;
83
0
           phi = -1.0;
84
0
                }
85
0
                break;
86
0
            case Option::Put:
87
0
               if (knock_in_) {
88
                   // down-and-in cash-(at-expiry)-or-nothing option
89
                   // a.k.a. american put with cash-or-nothing payoff
90
0
                   eta =  1.0;
91
0
                   phi = -1.0;
92
0
                } else {
93
                   // down-and-out cash-(at-expiry)-or-nothing option
94
0
           eta =  1.0;
95
0
           phi =  1.0;
96
0
                }
97
0
                break;
98
0
            default:
99
0
                QL_FAIL("invalid option type");
100
0
         }
101
102
103
0
        if (variance_>=QL_EPSILON) {
104
0
            D1_ = phi*(log_S_H_/stdDev_ + mu_*stdDev_);
105
0
            D2_ = eta*(log_H_S_/stdDev_ + mu_*stdDev_);
106
107
0
            CumulativeNormalDistribution f;
108
0
            cum_d1_ = f(D1_);
109
0
            cum_d2_ = f(D2_);
110
0
            n_d1_ = f.derivative(D1_);
111
0
            n_d2_ = f.derivative(D2_);
112
0
        } else {
113
0
            if (log_S_H_ * phi >0)
114
0
                cum_d1_= 1.0;
115
0
            else
116
0
                cum_d1_= 0.0;
117
0
            if (log_H_S_ * eta >0)
118
0
                cum_d2_= 1.0;
119
0
            else
120
0
                cum_d2_= 0.0;
121
0
            n_d1_ = 0.0;
122
0
            n_d2_ = 0.0;
123
0
        }
124
125
126
0
        switch (type) {
127
0
            case Option::Call:
128
0
                if (strike_<=spot_) {
129
0
                    if (knock_in_) {
130
                        // up-and-in cash-(at-expiry)-or-nothing option
131
                        // a.k.a. american call with cash-or-nothing payoff
132
0
                        cum_d1_     = 0.5;
133
0
                        cum_d2_     = 0.5;
134
0
                    } else {
135
                        // up-and-out cash-(at-expiry)-or-nothing option
136
                        // already knocked out
137
0
                        cum_d1_     = 0.0;
138
0
                        cum_d2_     = 0.0;
139
0
                    }
140
0
                    n_d1_       = 0.0;
141
0
                    n_d2_       = 0.0;
142
0
                }
143
0
                break;
144
0
            case Option::Put:
145
0
        if (strike_>=spot_) {
146
0
                    if (knock_in_) {
147
              // down-and-in cash-(at-expiry)-or-nothing option
148
              // a.k.a. american put with cash-or-nothing payoff
149
0
                        cum_d1_     = 0.5;
150
0
                        cum_d2_     = 0.5;
151
0
                    } else {
152
              // down-and-out cash-(at-expiry)-or-nothing option
153
                        // already knocked out
154
0
                        cum_d1_     = 0.0;
155
0
                        cum_d2_     = 0.0;
156
0
                    }
157
0
                    n_d1_       = 0.0;
158
0
                    n_d2_       = 0.0;
159
0
                }
160
0
                break;
161
0
            default:
162
0
                QL_FAIL("invalid option type");
163
0
         }
164
165
166
0
        inTheMoney_ = (type==Option::Call && strike_<spot_) ||
167
0
                      (type==Option::Put  && strike_>spot_);
168
0
        if (inTheMoney_) {
169
0
            X_ = 1.0;
170
0
            Y_ = 1.0;
171
0
        } else {
172
0
            X_ = 1.0;
173
0
            if (cum_d2_ == 0.0)
174
0
                Y_ = 0.0; // check needed on some extreme cases
175
0
            else
176
0
                Y_ = std::pow(Real(strike_/spot_), Real(2.0*mu_));
177
0
        }
178
0
        if (!knock_in_)
179
0
           Y_ *= -1.0; 
180
0
    }
181
182
}
183