line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::Business::BlackScholesMerton::Binaries; |
2
|
11
|
|
|
11
|
|
1322168
|
use strict; |
|
11
|
|
|
|
|
138
|
|
|
11
|
|
|
|
|
343
|
|
3
|
11
|
|
|
11
|
|
62
|
use warnings; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
507
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '1.25'; ## VERSION |
6
|
|
|
|
|
|
|
|
7
|
11
|
|
|
11
|
|
77
|
use constant PI => 3.14159265359; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
975
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
my $SMALLTIME = 1 / (60 * 60 * 24 * 365); # 1 second in years; |
10
|
|
|
|
|
|
|
|
11
|
11
|
|
|
11
|
|
116
|
use List::Util qw(max); |
|
11
|
|
|
|
|
35
|
|
|
11
|
|
|
|
|
1301
|
|
12
|
11
|
|
|
11
|
|
5425
|
use Math::CDF qw(pnorm); |
|
11
|
|
|
|
|
32526
|
|
|
11
|
|
|
|
|
728
|
|
13
|
11
|
|
|
11
|
|
5677
|
use POSIX qw(ceil); |
|
11
|
|
|
|
|
71640
|
|
|
11
|
|
|
|
|
60
|
|
14
|
11
|
|
|
11
|
|
21968
|
use Math::Trig; |
|
11
|
|
|
|
|
154096
|
|
|
11
|
|
|
|
|
1784
|
|
15
|
11
|
|
|
11
|
|
5413
|
use Machine::Epsilon; |
|
11
|
|
|
|
|
4077
|
|
|
11
|
|
|
|
|
31177
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# ABSTRACT: Algorithm of Math::Business::BlackScholesMerton::Binaries |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 NAME |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
Math::Business::BlackScholesMerton::Binaries |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
use Math::Business::BlackScholesMerton::Binaries; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# price of a Call option |
28
|
|
|
|
|
|
|
my $price_call_option = Math::Business::BlackScholesMerton::Binaries::call( |
29
|
|
|
|
|
|
|
1.35, # stock price |
30
|
|
|
|
|
|
|
1.36, # barrier |
31
|
|
|
|
|
|
|
(7/365), # time |
32
|
|
|
|
|
|
|
0.002, # payout currency interest rate (0.05 = 5%) |
33
|
|
|
|
|
|
|
0.001, # quanto drift adjustment (0.05 = 5%) |
34
|
|
|
|
|
|
|
0.11, # volatility (0.3 = 30%) |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 DESCRIPTION |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Prices options using the GBM model, all closed formulas. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Important(a): Basically, onetouch, upordown and doubletouch have two cases of |
42
|
|
|
|
|
|
|
payoff either at end or at hit. We treat them differently. We use parameter |
43
|
|
|
|
|
|
|
$w to differ them. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
$w = 0: payoff at hit time. |
46
|
|
|
|
|
|
|
$w = 1: payoff at end. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Our current contracts pay rebate at hit time, so we set $w = 0 by default. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Important(b) :Furthermore, for all contracts, we allow a different |
51
|
|
|
|
|
|
|
payout currency (Quantos). |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Paying domestic currency (JPY if for USDJPY) = correlation coefficient is ZERO. |
54
|
|
|
|
|
|
|
Paying foreign currency (USD if for USDJPY) = correlation coefficient is ONE. |
55
|
|
|
|
|
|
|
Paying another currency = correlation is between negative ONE and positive ONE. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
See [3] for Quanto formulas and examples |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head1 SUBROUTINES |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head2 call |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
USAGE |
64
|
|
|
|
|
|
|
my $price = call($S, $K, $t, $r_q, $mu, $sigma) |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
PARAMS |
67
|
|
|
|
|
|
|
$S => stock price |
68
|
|
|
|
|
|
|
$K => barrier |
69
|
|
|
|
|
|
|
$t => time (1 = 1 year) |
70
|
|
|
|
|
|
|
$r_q => payout currency interest rate (0.05 = 5%) |
71
|
|
|
|
|
|
|
$mu => quanto drift adjustment (0.05 = 5%) |
72
|
|
|
|
|
|
|
$sigma => volatility (0.3 = 30%) |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
DESCRIPTION |
75
|
|
|
|
|
|
|
Price a Call and remove the N(d2) part if the time is too small |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
EXPLANATION |
78
|
|
|
|
|
|
|
The definition of the contract is that if S > K, it gives |
79
|
|
|
|
|
|
|
full payout (1). However the formula DC(T,K) = e**(-rT) N(d2) will not be |
80
|
|
|
|
|
|
|
correct when T->0 and K=S. The value of DC(T,K) for this case will be 0.5. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
The formula is actually "correct" because when T->0 and S=K, the probability |
83
|
|
|
|
|
|
|
should just be 0.5 that the contract wins, moving up or down is equally |
84
|
|
|
|
|
|
|
likely in that very small amount of time left. Thus the only problem is |
85
|
|
|
|
|
|
|
that the math cannot evaluate at T=0, where divide by 0 error occurs. Thus, |
86
|
|
|
|
|
|
|
we need this check that throws away the N(d2) part (N(d2) will evaluate |
87
|
|
|
|
|
|
|
"wrongly" to 0.5 if S=K). |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
NOTE |
90
|
|
|
|
|
|
|
Note that we have call = - dCall/dStrike |
91
|
|
|
|
|
|
|
pair Foreign/Domestic |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
see [3] for $r_q and $mu for quantos |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=cut |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub call { |
98
|
13
|
|
|
13
|
1
|
10661
|
my ($S, $K, $t, $r_q, $mu, $sigma) = @_; |
99
|
|
|
|
|
|
|
|
100
|
13
|
100
|
|
|
|
48
|
if ($t < $SMALLTIME) { |
101
|
2
|
100
|
|
|
|
12
|
return ($S > $K) ? exp(-$r_q * $t) : 0; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
11
|
|
|
|
|
53
|
return exp(-$r_q * $t) * pnorm(d2($S, $K, $t, $r_q, $mu, $sigma)); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head2 put |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
USAGE |
110
|
|
|
|
|
|
|
my $price = put($S, $K, $t, $r_q, $mu, $sigma) |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
PARAMS |
113
|
|
|
|
|
|
|
$S => stock price |
114
|
|
|
|
|
|
|
$K => barrier |
115
|
|
|
|
|
|
|
$t => time (1 = 1 year) |
116
|
|
|
|
|
|
|
$r_q => payout currency interest rate (0.05 = 5%) |
117
|
|
|
|
|
|
|
$mu => quanto drift adjustment (0.05 = 5%) |
118
|
|
|
|
|
|
|
$sigma => volatility (0.3 = 30%) |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
DESCRIPTION |
121
|
|
|
|
|
|
|
Price a standard Digital Put |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=cut |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub put { |
126
|
13
|
|
|
13
|
1
|
3419
|
my ($S, $K, $t, $r_q, $mu, $sigma) = @_; |
127
|
|
|
|
|
|
|
|
128
|
13
|
100
|
|
|
|
43
|
if ($t < $SMALLTIME) { |
129
|
2
|
100
|
|
|
|
18
|
return ($S < $K) ? exp(-$r_q * $t) : 0; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
11
|
|
|
|
|
34
|
return exp(-$r_q * $t) * pnorm(-1 * d2($S, $K, $t, $r_q, $mu, $sigma)); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head2 d2 |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
returns the DS term common to many BlackScholesMerton formulae. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=cut |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub d2 { |
142
|
22
|
|
|
22
|
1
|
83
|
my ($S, $K, $t, undef, $mu, $sigma) = @_; |
143
|
|
|
|
|
|
|
|
144
|
22
|
|
|
|
|
257
|
return (log($S / $K) + ($mu - $sigma * $sigma / 2.0) * $t) / ($sigma * sqrt($t)); |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head2 expirymiss |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
USAGE |
150
|
|
|
|
|
|
|
my $price = expirymiss($S, $U, $D, $t, $r_q, $mu, $sigma) |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
PARAMS |
153
|
|
|
|
|
|
|
$S => stock price |
154
|
|
|
|
|
|
|
$t => time (1 = 1 year) |
155
|
|
|
|
|
|
|
$U => barrier |
156
|
|
|
|
|
|
|
$D => barrier |
157
|
|
|
|
|
|
|
$r_q => payout currency interest rate (0.05 = 5%) |
158
|
|
|
|
|
|
|
$mu => quanto drift adjustment (0.05 = 5%) |
159
|
|
|
|
|
|
|
$sigma => volatility (0.3 = 30%) |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
DESCRIPTION |
162
|
|
|
|
|
|
|
Price an expiry miss contract (1 Call + 1 Put) |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
[3] for $r_q and $mu for quantos |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=cut |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub expirymiss { |
169
|
6
|
|
|
6
|
1
|
1071
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma) = @_; |
170
|
|
|
|
|
|
|
|
171
|
6
|
|
|
|
|
16
|
my ($call_price) = call($S, $U, $t, $r_q, $mu, $sigma); |
172
|
6
|
|
|
|
|
16
|
my ($put_price) = put($S, $D, $t, $r_q, $mu, $sigma); |
173
|
|
|
|
|
|
|
|
174
|
6
|
|
|
|
|
18
|
return $call_price + $put_price; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=head2 expiryrange |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
USAGE |
180
|
|
|
|
|
|
|
my $price = expiryrange($S, $U, $D, $t, $r_q, $mu, $sigma) |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
PARAMS |
183
|
|
|
|
|
|
|
$S => stock price |
184
|
|
|
|
|
|
|
$U => barrier |
185
|
|
|
|
|
|
|
$D => barrier |
186
|
|
|
|
|
|
|
$t => time (1 = 1 year) |
187
|
|
|
|
|
|
|
$r_q => payout currency interest rate (0.05 = 5%) |
188
|
|
|
|
|
|
|
$mu => quanto drift adjustment (0.05 = 5%) |
189
|
|
|
|
|
|
|
$sigma => volatility (0.3 = 30%) |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
DESCRIPTION |
192
|
|
|
|
|
|
|
Price an Expiry Range contract as Foreign/Domestic. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
[3] for $r_q and $mu for quantos |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub expiryrange { |
199
|
3
|
|
|
3
|
1
|
1414
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma) = @_; |
200
|
|
|
|
|
|
|
|
201
|
3
|
|
|
|
|
15
|
return exp(-$r_q * $t) - expirymiss($S, $U, $D, $t, $r_q, $mu, $sigma); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 onetouch |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
PARAMS |
207
|
|
|
|
|
|
|
$S => stock price |
208
|
|
|
|
|
|
|
$U => barrier |
209
|
|
|
|
|
|
|
$t => time (1 = 1 year) |
210
|
|
|
|
|
|
|
$r_q => payout currency interest rate (0.05 = 5%) |
211
|
|
|
|
|
|
|
$mu => quanto drift adjustment (0.05 = 5%) |
212
|
|
|
|
|
|
|
$sigma => volatility (0.3 = 30%) |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
[3] for $r_q and $mu for quantos |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=cut |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub onetouch { |
219
|
39
|
|
|
39
|
1
|
4892
|
my ($S, $U, $t, $r_q, $mu, $sigma, $w) = @_; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# w = 0, rebate paid at hit (good way to remember is that waiting |
222
|
|
|
|
|
|
|
# time to get paid = 0) |
223
|
|
|
|
|
|
|
# w = 1, rebate paid at end. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# When the contract already reached it expiry and not yet reach it |
226
|
|
|
|
|
|
|
# settlement time, it is consider an unexpired contract but will come to |
227
|
|
|
|
|
|
|
# here with t=0 and it will caused the formula to die hence set it to the |
228
|
|
|
|
|
|
|
# SMALLTIME which is 1 second |
229
|
39
|
|
|
|
|
86
|
$t = max($SMALLTIME, $t); |
230
|
|
|
|
|
|
|
|
231
|
39
|
|
100
|
|
|
125
|
$w ||= 0; |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# eta = -1, one touch up |
234
|
|
|
|
|
|
|
# eta = 1, one touch down |
235
|
39
|
100
|
|
|
|
98
|
my $eta = ($S < $U) ? -1 : 1; |
236
|
|
|
|
|
|
|
|
237
|
39
|
|
|
|
|
60
|
my $sqrt_t = sqrt($t); |
238
|
|
|
|
|
|
|
|
239
|
39
|
|
|
|
|
76
|
my $theta_ = (($mu) / $sigma) - (0.5 * $sigma); |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Floor v_ squared at zero in case negative interest rates push it negative. |
242
|
|
|
|
|
|
|
# See: Barrier Options under Negative Rates in Black-Scholes (Le Floc’h and Pruell, 2014) |
243
|
39
|
|
|
|
|
102
|
my $v_ = sqrt(max(0, ($theta_ * $theta_) + (2 * (1 - $w) * $r_q))); |
244
|
|
|
|
|
|
|
|
245
|
39
|
|
|
|
|
131
|
my $e = (log($S / $U) - ($sigma * $v_ * $t)) / ($sigma * $sqrt_t); |
246
|
39
|
|
|
|
|
76
|
my $e_ = (-log($S / $U) - ($sigma * $v_ * $t)) / ($sigma * $sqrt_t); |
247
|
|
|
|
|
|
|
|
248
|
39
|
|
|
|
|
300
|
my $price = (($U / $S)**(($theta_ + $v_) / $sigma)) * pnorm(-$eta * $e) + (($U / $S)**(($theta_ - $v_) / $sigma)) * pnorm($eta * $e_); |
249
|
|
|
|
|
|
|
|
250
|
39
|
|
|
|
|
128
|
return exp(-$w * $r_q * $t) * $price; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=head2 notouch |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
USAGE |
256
|
|
|
|
|
|
|
my $price = notouch($S, $U, $t, $r_q, $mu, $sigma, $w) |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
PARAMS |
259
|
|
|
|
|
|
|
$S => stock price |
260
|
|
|
|
|
|
|
$U => barrier |
261
|
|
|
|
|
|
|
$t => time (1 = 1 year) |
262
|
|
|
|
|
|
|
$r_q => payout currency interest rate (0.05 = 5%) |
263
|
|
|
|
|
|
|
$mu => quanto drift adjustment (0.05 = 5%) |
264
|
|
|
|
|
|
|
$sigma => volatility (0.3 = 30%) |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
DESCRIPTION |
267
|
|
|
|
|
|
|
Price a No touch contract. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Payoff with domestic currency |
270
|
|
|
|
|
|
|
Identity: |
271
|
|
|
|
|
|
|
price of notouch = exp(- r t) - price of onetouch(rebate paid at end) |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
[3] for $r_q and $mu for quantos |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=cut |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub notouch { |
278
|
7
|
|
|
7
|
1
|
1684
|
my ($S, $U, $t, $r_q, $mu, $sigma) = @_; |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# No touch contract always pay out at end |
281
|
7
|
|
|
|
|
11
|
my $w = 1; |
282
|
|
|
|
|
|
|
|
283
|
7
|
|
|
|
|
25
|
return exp(-$r_q * $t) - onetouch($S, $U, $t, $r_q, $mu, $sigma, $w); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# These variables require 'our' only because they need to be |
287
|
|
|
|
|
|
|
# accessed by a test script. |
288
|
|
|
|
|
|
|
our $MAX_ITERATIONS_UPORDOWN_PELSSER_1997 = 1000; |
289
|
|
|
|
|
|
|
our $MIN_ITERATIONS_UPORDOWN_PELSSER_1997 = 16; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# |
292
|
|
|
|
|
|
|
# This variable requires 'our' only because it needs to be |
293
|
|
|
|
|
|
|
# accessed via test script. |
294
|
|
|
|
|
|
|
# Min accuracy. Accurate to 1 dollar for 100,000 notional |
295
|
|
|
|
|
|
|
# |
296
|
|
|
|
|
|
|
our $MIN_ACCURACY_UPORDOWN_PELSSER_1997 = 1.0 / 100000.0; |
297
|
|
|
|
|
|
|
our $SMALL_VALUE_MU = 1e-10; |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# The smallest (in magnitude) floating-point number which, |
300
|
|
|
|
|
|
|
# when added to the floating-point number 1.0, produces a |
301
|
|
|
|
|
|
|
# floating-point result different from 1.0 is termed the |
302
|
|
|
|
|
|
|
# machine accuracy, e. |
303
|
|
|
|
|
|
|
# |
304
|
|
|
|
|
|
|
# This value is very important for knowing stability to |
305
|
|
|
|
|
|
|
# certain formulas used. e.g. Pelsser formula for UPORDOWN |
306
|
|
|
|
|
|
|
# and RANGE contracts. |
307
|
|
|
|
|
|
|
# |
308
|
|
|
|
|
|
|
my $MACHINE_EPSILON = machine_epsilon(); |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head2 upordown |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
USAGE |
313
|
|
|
|
|
|
|
my $price = upordown(($S, $U, $D, $t, $r_q, $mu, $sigma, $w)) |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
PARAMS |
316
|
|
|
|
|
|
|
$S stock price |
317
|
|
|
|
|
|
|
$U barrier |
318
|
|
|
|
|
|
|
$D barrier |
319
|
|
|
|
|
|
|
$t time (1 = 1 year) |
320
|
|
|
|
|
|
|
$r_q payout currency interest rate (0.05 = 5%) |
321
|
|
|
|
|
|
|
$mu quanto drift adjustment (0.05 = 5%) |
322
|
|
|
|
|
|
|
$sigma volatility (0.3 = 30%) |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
see [3] for $r_q and $mu for quantos |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
DESCRIPTION |
327
|
|
|
|
|
|
|
Price an Up or Down contract |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=cut |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub upordown { |
332
|
16
|
|
|
16
|
1
|
6754
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w) = @_; |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# When the contract already reached it's expiry and not yet reach it |
335
|
|
|
|
|
|
|
# settlement time, it is considered an unexpired contract but will come to |
336
|
|
|
|
|
|
|
# here with t=0 and it will caused the formula to die hence set it to the |
337
|
|
|
|
|
|
|
# SMALLTIME whiich is 1 second |
338
|
16
|
|
|
|
|
49
|
$t = max($t, $SMALLTIME); |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# $w = 0, paid at hit |
341
|
|
|
|
|
|
|
# $w = 1, paid at end |
342
|
16
|
100
|
|
|
|
47
|
if (not defined $w) { $w = 0; } |
|
8
|
|
|
|
|
26
|
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# spot is outside [$D, $U] --> contract is expired with full payout, |
345
|
|
|
|
|
|
|
# one barrier is already hit (can happen due to shift markup): |
346
|
16
|
100
|
100
|
|
|
85
|
if ($S >= $U or $S <= $D) { |
347
|
4
|
100
|
|
|
|
25
|
return $w ? exp(-$t * $r_q) : 1; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# |
351
|
|
|
|
|
|
|
# SANITY CHECKS |
352
|
|
|
|
|
|
|
# |
353
|
|
|
|
|
|
|
# For extreme cases, the price will be wrong due the values in the |
354
|
|
|
|
|
|
|
# infinite series getting too large or too small, which causes |
355
|
|
|
|
|
|
|
# roundoff errors in the computer. Thus no matter how many iterations |
356
|
|
|
|
|
|
|
# you make, the errors will never go away. |
357
|
|
|
|
|
|
|
# |
358
|
|
|
|
|
|
|
# For example try this: |
359
|
|
|
|
|
|
|
# |
360
|
|
|
|
|
|
|
# my ($S, $U, $D, $t, $r, $q, $vol, $w) |
361
|
|
|
|
|
|
|
# = (100.00, 118.97, 99.00, 30/365, 0.1, 0.02, 0.01, 1); |
362
|
|
|
|
|
|
|
# $up_price = Math::Business::BlackScholesMerton::Binaries::ot_up_ko_down_pelsser_1997( |
363
|
|
|
|
|
|
|
# $S,$U,$D,$t,$r,$q,$vol,$w); |
364
|
|
|
|
|
|
|
# $down_price= Math::Business::BlackScholesMerton::Binaries::ot_down_ko_up_pelsser_1997( |
365
|
|
|
|
|
|
|
# $S,$U,$D,$t,$r,$q,$vol,$w); |
366
|
|
|
|
|
|
|
# |
367
|
|
|
|
|
|
|
# Thus we put a sanity checks here such that |
368
|
|
|
|
|
|
|
# |
369
|
|
|
|
|
|
|
# CONDITION 1: UPORDOWN[U,D] < ONETOUCH[U] + ONETOUCH[D] |
370
|
|
|
|
|
|
|
# CONDITION 2: UPORDOWN[U,D] > ONETOUCH[U] |
371
|
|
|
|
|
|
|
# CONDITION 3: UPORDOWN[U,D] > ONETOUCH[D] |
372
|
|
|
|
|
|
|
# CONDITION 4: ONETOUCH[U] + ONETOUCH[D] >= $MIN_ACCURACY_UPORDOWN_PELSSER_1997 |
373
|
|
|
|
|
|
|
# |
374
|
12
|
|
|
|
|
62
|
my $onetouch_up_prob = onetouch($S, $U, $t, $r_q, $mu, $sigma, $w); |
375
|
12
|
|
|
|
|
39
|
my $onetouch_down_prob = onetouch($S, $D, $t, $r_q, $mu, $sigma, $w); |
376
|
|
|
|
|
|
|
|
377
|
12
|
|
|
|
|
26
|
my $upordown_prob; |
378
|
|
|
|
|
|
|
|
379
|
12
|
100
|
75
|
|
|
96
|
if ($onetouch_up_prob + $onetouch_down_prob < $MIN_ACCURACY_UPORDOWN_PELSSER_1997) { |
|
|
100
|
|
|
|
|
|
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# CONDITION 4: |
382
|
|
|
|
|
|
|
# The probability is too small for the Pelsser formula to be correct. |
383
|
|
|
|
|
|
|
# Do this check first to avoid PELSSER stability condition to be |
384
|
|
|
|
|
|
|
# triggered. |
385
|
|
|
|
|
|
|
# Here we assume that the ONETOUCH formula is perfect and never give |
386
|
|
|
|
|
|
|
# wrong values (e.g. negative). |
387
|
1
|
|
|
|
|
4
|
return 0; |
388
|
|
|
|
|
|
|
} elsif ($onetouch_up_prob xor $onetouch_down_prob) { |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# One of our ONETOUCH probabilities is 0. |
391
|
|
|
|
|
|
|
# That means our upordown prob is equivalent to the other one. |
392
|
|
|
|
|
|
|
# Pelsser recompute will either be the same or wrong. |
393
|
|
|
|
|
|
|
# Continuing to assume the ONETOUCH is perfect. |
394
|
4
|
|
|
|
|
13
|
$upordown_prob = max($onetouch_up_prob, $onetouch_down_prob); |
395
|
|
|
|
|
|
|
} else { |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# THIS IS THE ONLY PLACE IT SHOULD BE! |
398
|
7
|
|
|
|
|
27
|
$upordown_prob = |
399
|
|
|
|
|
|
|
ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w) + ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w); |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# CONDITION 4: |
403
|
|
|
|
|
|
|
# Now check on the other end, when the contract is too close to payout. |
404
|
|
|
|
|
|
|
# Not really needed to check for payout at hit, because RANGE is |
405
|
|
|
|
|
|
|
# always at end, and thus the value (DISCOUNT - UPORDOWN) is not |
406
|
|
|
|
|
|
|
# evaluated. |
407
|
11
|
100
|
|
|
|
36
|
if ($w == 1) { |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# Since the difference is already less than the min accuracy, |
410
|
|
|
|
|
|
|
# the value [payout - upordown], which is the RANGE formula |
411
|
|
|
|
|
|
|
# can become negative. |
412
|
5
|
50
|
|
|
|
22
|
if (abs(exp(-$r_q * $t) - $upordown_prob) < $MIN_ACCURACY_UPORDOWN_PELSSER_1997) { |
413
|
0
|
|
|
|
|
0
|
$upordown_prob = exp(-$r_q * $t); |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# CONDITION 1-3 |
418
|
|
|
|
|
|
|
# We use hardcoded small value of $SMALL_TOLERANCE, because if we were to increase |
419
|
|
|
|
|
|
|
# the minimum accuracy, and this small value uses that min accuracy, it is |
420
|
|
|
|
|
|
|
# very hard for the conditions to pass. |
421
|
11
|
|
|
|
|
18
|
my $SMALL_TOLERANCE = 0.00001; |
422
|
11
|
50
|
33
|
|
|
100
|
if ( not($upordown_prob < $onetouch_up_prob + $onetouch_down_prob + $SMALL_TOLERANCE) |
|
|
|
33
|
|
|
|
|
423
|
|
|
|
|
|
|
or not($upordown_prob + $SMALL_TOLERANCE > $onetouch_up_prob) |
424
|
|
|
|
|
|
|
or not($upordown_prob + $SMALL_TOLERANCE > $onetouch_down_prob)) |
425
|
|
|
|
|
|
|
{ |
426
|
0
|
|
|
|
|
0
|
die "UPORDOWN price sanity checks failed for S=$S, U=$U, " |
427
|
|
|
|
|
|
|
. "D=$D, t=$t, r_q=$r_q, mu=$mu, sigma=$sigma, w=$w. " |
428
|
|
|
|
|
|
|
. "UPORDOWN PROB=$upordown_prob , " |
429
|
|
|
|
|
|
|
. "ONETOUCH_UP PROB=$onetouch_up_prob , " |
430
|
|
|
|
|
|
|
. "ONETOUCH_DOWN PROB=$onetouch_down_prob"; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
11
|
|
|
|
|
47
|
return $upordown_prob; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head2 common_function_pelsser_1997 |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
USAGE |
439
|
|
|
|
|
|
|
my $c = common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta) |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
DESCRIPTION |
442
|
|
|
|
|
|
|
Return the common function from Pelsser's Paper (1997) |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=cut |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub common_function_pelsser_1997 { |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# h: normalized high barrier, log(U/L) |
449
|
|
|
|
|
|
|
# x: normalized spot, log(S/L) |
450
|
16
|
|
|
16
|
1
|
2999
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta) = @_; |
451
|
|
|
|
|
|
|
|
452
|
16
|
|
|
|
|
27
|
my $pi = Math::Trig::pi; |
453
|
|
|
|
|
|
|
|
454
|
16
|
|
|
|
|
36
|
my $h = log($U / $D); |
455
|
16
|
|
|
|
|
35
|
my $x = log($S / $D); |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# $eta = 1, onetouch up knockout down |
458
|
|
|
|
|
|
|
# $eta = 0, onetouch down knockout up |
459
|
|
|
|
|
|
|
# This variable used to check stability |
460
|
16
|
100
|
|
|
|
42
|
if (not defined $eta) { |
461
|
1
|
|
|
|
|
36
|
die "Wrong usage of this function for S=$S, U=$U, D=$D, " . "t=$t, r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, eta not defined."; |
462
|
|
|
|
|
|
|
} |
463
|
15
|
100
|
|
|
|
46
|
if ($eta == 0) { $x = $h - $x; } |
|
8
|
|
|
|
|
28
|
|
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# $w = 0, paid at hit |
466
|
|
|
|
|
|
|
# $w = 1, paid at end |
467
|
|
|
|
|
|
|
|
468
|
15
|
|
|
|
|
31
|
my $mu_new = $mu - (0.5 * $sigma * $sigma); |
469
|
15
|
|
|
|
|
59
|
my $mu_dash = sqrt(max(0, ($mu_new * $mu_new) + (2 * $sigma * $sigma * $r_q * (1 - $w)))); |
470
|
|
|
|
|
|
|
|
471
|
15
|
|
|
|
|
20
|
my $series_part = 0; |
472
|
15
|
|
|
|
|
20
|
my $hyp_part = 0; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# These constants will determine whether or not this contract can be |
475
|
|
|
|
|
|
|
# evaluated to a predefined accuracy. It is VERY IMPORTANT because |
476
|
|
|
|
|
|
|
# if these conditions are not met, the prices can be complete nonsense!! |
477
|
15
|
|
|
|
|
35
|
my $stability_constant = get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta, 1); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# The number of iterations is important when recommending the |
480
|
|
|
|
|
|
|
# range of the upper/lower barriers on our site. If we recommend |
481
|
|
|
|
|
|
|
# a range that is too big and our iteration is too small, the |
482
|
|
|
|
|
|
|
# price will be wrong! We must know the rate of convergence of |
483
|
|
|
|
|
|
|
# the formula used. |
484
|
15
|
|
|
|
|
32
|
my $iterations_required = get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w); |
485
|
|
|
|
|
|
|
|
486
|
15
|
|
|
|
|
47
|
for (my $k = 1; $k < $iterations_required; $k++) { |
487
|
253
|
|
|
|
|
404
|
my $lambda_k_dash = (0.5 * (($mu_dash * $mu_dash) / ($sigma * $sigma) + ($k * $k * $pi * $pi * $sigma * $sigma) / ($h * $h))); |
488
|
|
|
|
|
|
|
|
489
|
253
|
|
|
|
|
457
|
my $phi = ($sigma * $sigma) / ($h * $h) * exp(-$lambda_k_dash * $t) * $k / $lambda_k_dash; |
490
|
|
|
|
|
|
|
|
491
|
253
|
|
|
|
|
482
|
$series_part += $phi * $pi * sin($k * $pi * ($h - $x) / $h); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# |
494
|
|
|
|
|
|
|
# Note that greeks may also call this function, and their |
495
|
|
|
|
|
|
|
# stability constant will differ. However, for simplicity |
496
|
|
|
|
|
|
|
# we will not bother (else the code will get messy), and |
497
|
|
|
|
|
|
|
# just use the price stability constant. |
498
|
|
|
|
|
|
|
# |
499
|
253
|
50
|
66
|
|
|
585
|
if ($k == 1 and (not(abs($phi) < $stability_constant))) { |
500
|
0
|
|
|
|
|
0
|
die "PELSSER VALUATION formula for S=$S, U=$U, D=$D, t=$t, r_q=$r_q, " |
501
|
|
|
|
|
|
|
. "mu=$mu, vol=$sigma, w=$w, eta=$eta, cannot be evaluated because" |
502
|
|
|
|
|
|
|
. "PELSSER VALUATION stability conditions ($phi less than " |
503
|
|
|
|
|
|
|
. "$stability_constant) not met. This could be due to barriers " |
504
|
|
|
|
|
|
|
. "too big, volatilities too low, interest/dividend rates too high, " |
505
|
|
|
|
|
|
|
. "or machine accuracy too low. Machine accuracy is " |
506
|
|
|
|
|
|
|
. $MACHINE_EPSILON . "."; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# |
511
|
|
|
|
|
|
|
# Some math basics: When A -> 0, |
512
|
|
|
|
|
|
|
# |
513
|
|
|
|
|
|
|
# sinh(A) -> 0.5 * [ (1 + A) - (1 - A) ] = 0.5 * 2A = A |
514
|
|
|
|
|
|
|
# cosh(A) -> 0.5 * [ (1 + A) + (1 - A) ] = 0.5 * 2 = 1 |
515
|
|
|
|
|
|
|
# |
516
|
|
|
|
|
|
|
# Thus for sinh(A)/sinh(B) when A & B -> 0, we have |
517
|
|
|
|
|
|
|
# |
518
|
|
|
|
|
|
|
# sinh(A) / sinh(B) -> A / B |
519
|
|
|
|
|
|
|
# |
520
|
|
|
|
|
|
|
# Since the check of the spot == lower/upper barrier has been done in the |
521
|
|
|
|
|
|
|
# _upordown subroutine, we can assume that $x and $h will never be 0. |
522
|
|
|
|
|
|
|
# So we only need to check that $mu_dash is too small. Also note that |
523
|
|
|
|
|
|
|
# $mu_dash is always positive. |
524
|
|
|
|
|
|
|
# |
525
|
|
|
|
|
|
|
# For example, even at 0.0001 the error becomes small enough |
526
|
|
|
|
|
|
|
# |
527
|
|
|
|
|
|
|
# 0.0001 - Math::Trig::sinh(0.0001) = -1.66688941837835e-13 |
528
|
|
|
|
|
|
|
# |
529
|
|
|
|
|
|
|
# Since h > x, we only check for (mu_dash * h) / (vol * vol) |
530
|
|
|
|
|
|
|
# |
531
|
15
|
100
|
|
|
|
48
|
if (abs($mu_dash * $h / ($sigma * $sigma)) < $SMALL_VALUE_MU) { |
532
|
3
|
|
|
|
|
6
|
$hyp_part = $x / $h; |
533
|
|
|
|
|
|
|
} else { |
534
|
12
|
|
|
|
|
38
|
$hyp_part = Math::Trig::sinh($mu_dash * $x / ($sigma * $sigma)) / Math::Trig::sinh($mu_dash * $h / ($sigma * $sigma)); |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|
537
|
15
|
|
|
|
|
285
|
return ($hyp_part - $series_part) * exp(-$r_q * $t * $w); |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head2 get_stability_constant_pelsser_1997 |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
USAGE |
543
|
|
|
|
|
|
|
my $constant = get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta, $p) |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
DESCRIPTION |
546
|
|
|
|
|
|
|
Get the stability constant (Pelsser 1997) |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=cut |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
sub get_stability_constant_pelsser_1997 { |
551
|
17
|
|
|
17
|
1
|
3279
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta, $p) = @_; |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# $eta = 1, onetouch up knockout down |
554
|
|
|
|
|
|
|
# $eta = 0, onetouch down knockout up |
555
|
|
|
|
|
|
|
|
556
|
17
|
100
|
|
|
|
41
|
if (not defined $eta) { |
557
|
1
|
|
|
|
|
44
|
die "Wrong usage of this function for S=$S, U=$U, D=$D, t=$t, " . "r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, Eta not defined."; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# p is the power of pi |
561
|
|
|
|
|
|
|
# p=1 for price/theta/vega/vanna/volga |
562
|
|
|
|
|
|
|
# p=2 for delta |
563
|
|
|
|
|
|
|
# p=3 for gamma |
564
|
16
|
50
|
66
|
|
|
64
|
if ($p != 1 and $p != 2 and $p != 3) { |
|
|
|
66
|
|
|
|
|
565
|
1
|
|
|
|
|
26
|
die "Wrong usage of this function for S=$S, U=$U, D=$D, t=$t, " |
566
|
|
|
|
|
|
|
. "r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, Power of PI must " |
567
|
|
|
|
|
|
|
. "be 1, 2 or 3. Given $p."; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
15
|
|
|
|
|
31
|
my $h = log($U / $D); |
571
|
15
|
|
|
|
|
19
|
my $x = log($S / $D); |
572
|
15
|
|
|
|
|
24
|
my $mu_new = $mu - (0.5 * $sigma * $sigma); |
573
|
|
|
|
|
|
|
|
574
|
15
|
|
|
|
|
43
|
my $numerator = $MIN_ACCURACY_UPORDOWN_PELSSER_1997 * exp(1.0 - $mu_new * (($eta * $h) - $x) / ($sigma * $sigma)); |
575
|
15
|
|
|
|
|
62
|
my $denominator = (exp(1) * (Math::Trig::pi + $p)) + (max($mu_new * (($eta * $h) - $x), 0.0) * Math::Trig::pi / ($sigma**2)); |
576
|
15
|
|
|
|
|
37
|
$denominator *= (Math::Trig::pi**($p - 1)) * $MACHINE_EPSILON; |
577
|
|
|
|
|
|
|
|
578
|
15
|
|
|
|
|
24
|
my $stability_condition = $numerator / $denominator; |
579
|
|
|
|
|
|
|
|
580
|
15
|
|
|
|
|
26
|
return $stability_condition; |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=head2 ot_up_ko_down_pelsser_1997 |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
USAGE |
586
|
|
|
|
|
|
|
my $price = ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w) |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
DESCRIPTION |
589
|
|
|
|
|
|
|
This is V_{RAHU} in paper [5], or ONETOUCH-UP-KNOCKOUT-DOWN, |
590
|
|
|
|
|
|
|
a contract that wins if it touches upper barrier, but expires |
591
|
|
|
|
|
|
|
worthless if it touches the lower barrier first. |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=cut |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
sub ot_up_ko_down_pelsser_1997 { |
596
|
7
|
|
|
7
|
1
|
24
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w) = @_; |
597
|
|
|
|
|
|
|
|
598
|
7
|
|
|
|
|
16
|
my $mu_new = $mu - (0.5 * $sigma * $sigma); |
599
|
7
|
|
|
|
|
14
|
my $h = log($U / $D); |
600
|
7
|
|
|
|
|
17
|
my $x = log($S / $D); |
601
|
|
|
|
|
|
|
|
602
|
7
|
|
|
|
|
30
|
return exp($mu_new * ($h - $x) / ($sigma * $sigma)) * common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, 1); |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head2 ot_down_ko_up_pelsser_1997 |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
USAGE |
608
|
|
|
|
|
|
|
my $price = ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w) |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
DESCRIPTION |
611
|
|
|
|
|
|
|
This is V_{RAHL} in paper [5], or ONETOUCH-DOWN-KNOCKOUT-UP, |
612
|
|
|
|
|
|
|
a contract that wins if it touches lower barrier, but expires |
613
|
|
|
|
|
|
|
worthless if it touches the upper barrier first. |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=cut |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub ot_down_ko_up_pelsser_1997 { |
618
|
7
|
|
|
7
|
1
|
27
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w) = @_; |
619
|
|
|
|
|
|
|
|
620
|
7
|
|
|
|
|
30
|
my $mu_new = $mu - (0.5 * $sigma * $sigma); |
621
|
7
|
|
|
|
|
15
|
my $x = log($S / $D); |
622
|
|
|
|
|
|
|
|
623
|
7
|
|
|
|
|
33
|
return exp(-$mu_new * $x / ($sigma * $sigma)) * common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, 0); |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head2 get_min_iterations_pelsser_1997 |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
USAGE |
629
|
|
|
|
|
|
|
my $min = get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy) |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
DESCRIPTION |
632
|
|
|
|
|
|
|
An estimate of the number of iterations required to achieve a certain |
633
|
|
|
|
|
|
|
level of accuracy in the price. |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
=cut |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
sub get_min_iterations_pelsser_1997 { |
638
|
18
|
|
|
18
|
1
|
3306
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy) = @_; |
639
|
|
|
|
|
|
|
|
640
|
18
|
100
|
|
|
|
44
|
if (not defined $accuracy) { |
641
|
16
|
|
|
|
|
37
|
$accuracy = $MIN_ACCURACY_UPORDOWN_PELSSER_1997; |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
|
644
|
18
|
100
|
|
|
|
89
|
if ($accuracy > $MIN_ACCURACY_UPORDOWN_PELSSER_1997) { |
|
|
100
|
|
|
|
|
|
645
|
1
|
|
|
|
|
2
|
$accuracy = $MIN_ACCURACY_UPORDOWN_PELSSER_1997; |
646
|
|
|
|
|
|
|
} elsif ($accuracy <= 0) { |
647
|
1
|
|
|
|
|
2
|
$accuracy = $MIN_ACCURACY_UPORDOWN_PELSSER_1997; |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
18
|
|
|
|
|
57
|
my $it_up = _get_min_iterations_ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy); |
651
|
18
|
|
|
|
|
59
|
my $it_down = _get_min_iterations_ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy); |
652
|
|
|
|
|
|
|
|
653
|
18
|
|
|
|
|
51
|
my $min = max($it_up, $it_down); |
654
|
|
|
|
|
|
|
|
655
|
18
|
|
|
|
|
35
|
return $min; |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=head2 _get_min_iterations_ot_up_ko_down_pelsser_1997 |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
USAGE |
661
|
|
|
|
|
|
|
my $k_min = _get_min_iterations_ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy) |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
DESCRIPTION |
664
|
|
|
|
|
|
|
An estimate of the number of iterations required to achieve a certain |
665
|
|
|
|
|
|
|
level of accuracy in the price for ONETOUCH-UP-KNOCKOUT-DOWN. |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=cut |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
sub _get_min_iterations_ot_up_ko_down_pelsser_1997 { |
670
|
39
|
|
|
39
|
|
1117
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy) = @_; |
671
|
|
|
|
|
|
|
|
672
|
39
|
100
|
|
|
|
81
|
if (!defined $accuracy) { |
673
|
1
|
|
|
|
|
12
|
die "accuracy required"; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
38
|
|
|
|
|
53
|
my $pi = Math::Trig::pi; |
677
|
|
|
|
|
|
|
|
678
|
38
|
|
|
|
|
63
|
my $h = log($U / $D); |
679
|
38
|
|
|
|
|
54
|
my $x = log($S / $D); |
680
|
38
|
|
|
|
|
59
|
my $mu_new = $mu - (0.5 * $sigma * $sigma); |
681
|
38
|
|
|
|
|
117
|
my $mu_dash = sqrt(max(0, ($mu_new * $mu_new) + (2 * $sigma * $sigma * $r_q * (1 - $w)))); |
682
|
|
|
|
|
|
|
|
683
|
38
|
|
|
|
|
75
|
my $A = ($mu_dash * $mu_dash) / (2 * $sigma * $sigma); |
684
|
38
|
|
|
|
|
63
|
my $B = ($pi * $pi * $sigma * $sigma) / (2 * $h * $h); |
685
|
|
|
|
|
|
|
|
686
|
38
|
|
|
|
|
50
|
my $delta_dash = $accuracy; |
687
|
38
|
|
|
|
|
83
|
my $delta = $delta_dash * exp(-$mu_new * ($h - $x) / ($sigma * $sigma)) * (($h * $h) / ($pi * $sigma * $sigma)); |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
# This can happen when stability condition fails |
690
|
38
|
100
|
|
|
|
82
|
if ($delta * $B <= 0) { |
691
|
1
|
|
|
|
|
32
|
die "(_get_min_iterations_ot_up_ko_down_pelsser_1997) Cannot " |
692
|
|
|
|
|
|
|
. "evaluate minimum iterations because too many iterations " |
693
|
|
|
|
|
|
|
. "required!! delta=$delta, B=$B for input parameters S=$S, " |
694
|
|
|
|
|
|
|
. "U=$U, D=$D, t=$t, r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, " |
695
|
|
|
|
|
|
|
. "accuracy=$accuracy"; |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# Check that condition is satisfied |
699
|
37
|
|
|
|
|
81
|
my $condition = max(exp(-$A * $t) / ($B * $delta), 1); |
700
|
|
|
|
|
|
|
|
701
|
37
|
|
|
|
|
85
|
my $k_min = log($condition) / ($B * $t); |
702
|
37
|
|
|
|
|
53
|
$k_min = sqrt($k_min); |
703
|
|
|
|
|
|
|
|
704
|
37
|
100
|
|
|
|
85
|
if ($k_min < $MIN_ITERATIONS_UPORDOWN_PELSSER_1997) { |
|
|
100
|
|
|
|
|
|
705
|
|
|
|
|
|
|
|
706
|
32
|
|
|
|
|
75
|
return $MIN_ITERATIONS_UPORDOWN_PELSSER_1997; |
707
|
|
|
|
|
|
|
} elsif ($k_min > $MAX_ITERATIONS_UPORDOWN_PELSSER_1997) { |
708
|
|
|
|
|
|
|
|
709
|
1
|
|
|
|
|
3
|
return $MAX_ITERATIONS_UPORDOWN_PELSSER_1997; |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
4
|
|
|
|
|
10
|
return int($k_min); |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=head2 _get_min_iterations_ot_down_ko_up_pelsser_1997 |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
USAGE |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
DESCRIPTION |
720
|
|
|
|
|
|
|
An estimate of the number of iterations required to achieve a certain |
721
|
|
|
|
|
|
|
level of accuracy in the price for ONETOUCH-UP-KNOCKOUT-UP. |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=cut |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
sub _get_min_iterations_ot_down_ko_up_pelsser_1997 { |
726
|
18
|
|
|
18
|
|
43
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy) = @_; |
727
|
|
|
|
|
|
|
|
728
|
18
|
|
|
|
|
30
|
my $h = log($U / $D); |
729
|
18
|
|
|
|
|
40
|
my $mu_new = $mu - (0.5 * $sigma * $sigma); |
730
|
|
|
|
|
|
|
|
731
|
18
|
|
|
|
|
32
|
$accuracy = $accuracy * exp($mu_new * $h / ($sigma * $sigma)); |
732
|
|
|
|
|
|
|
|
733
|
18
|
|
|
|
|
39
|
return _get_min_iterations_ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy); |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head2 range |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
USAGE |
739
|
|
|
|
|
|
|
my $price = range($S, $U, $D, $t, $r_q, $mu, $sigma, $w) |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
PARAMS |
742
|
|
|
|
|
|
|
$S stock price |
743
|
|
|
|
|
|
|
$t time (1 = 1 year) |
744
|
|
|
|
|
|
|
$U barrier |
745
|
|
|
|
|
|
|
$D barrier |
746
|
|
|
|
|
|
|
$r_q payout currency interest rate (0.05 = 5%) |
747
|
|
|
|
|
|
|
$mu quanto drift adjustment (0.05 = 5%) |
748
|
|
|
|
|
|
|
$sigma volatility (0.3 = 30%) |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
see [3] for $r_q and $mu for quantos |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
DESCRIPTION |
753
|
|
|
|
|
|
|
Price a range contract. |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=cut |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
sub range { |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
# payout time $w is only a dummy. range contracts always payout at end. |
760
|
7
|
|
|
7
|
1
|
3525
|
my ($S, $U, $D, $t, $r_q, $mu, $sigma, $w) = @_; |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
# range always pay out at end |
763
|
7
|
|
|
|
|
11
|
$w = 1; |
764
|
|
|
|
|
|
|
|
765
|
7
|
|
|
|
|
31
|
return exp(-$r_q * $t) - upordown($S, $U, $D, $t, $r_q, $mu, $sigma, $w); |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
=head2 americanknockout |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
American Binary Knockout |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Description of parameters: |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
$S - spot |
775
|
|
|
|
|
|
|
$H1 - lower barrier |
776
|
|
|
|
|
|
|
$H2 - upper barrier |
777
|
|
|
|
|
|
|
$K - payout strike |
778
|
|
|
|
|
|
|
$tiy - time in years |
779
|
|
|
|
|
|
|
$sigma - volatility |
780
|
|
|
|
|
|
|
$mu - mean |
781
|
|
|
|
|
|
|
$r - interest rate |
782
|
|
|
|
|
|
|
$type - 'c' for buy or 'p' for sell |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
Reference: |
785
|
|
|
|
|
|
|
http://www.phy.cuhk.edu.hk/~cflo/Finance/chohoi/afe.pdf |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
=cut |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
sub americanknockout { |
790
|
4
|
|
|
4
|
1
|
5870
|
my ($S, $H2, $H1, $K, $tiy, $sigma, $mu, $type) = @_; |
791
|
|
|
|
|
|
|
|
792
|
4
|
100
|
|
|
|
16
|
if ($type eq 'c') { |
793
|
3
|
|
|
|
|
9
|
($H1, $H2) = ($H2, $H1); |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
|
796
|
4
|
|
|
|
|
29
|
my $k1 = 2 * $mu / $sigma**2; |
797
|
4
|
|
|
|
|
13
|
my $alpha = -0.5 * ($k1 - 1); |
798
|
4
|
|
|
|
|
16
|
my $beta = -0.25 * ($k1 - 1)**2 - 2 * $mu / $sigma**2; |
799
|
4
|
|
|
|
|
13
|
my $L = log($H2 / $H1); |
800
|
4
|
|
|
|
|
52
|
my $n = ceil(sqrt(((-2 * log($MACHINE_EPSILON) / $tiy) - ($mu / $sigma)**2) / ((PI * $sigma / $L)**2))); |
801
|
|
|
|
|
|
|
|
802
|
4
|
|
|
|
|
11
|
my $z = 0; |
803
|
4
|
|
|
|
|
18
|
for (my $i = 1; $i <= $n; $i++) { |
804
|
123
|
|
|
|
|
167
|
my $zeta = $i * PI / $L; |
805
|
123
|
|
|
|
|
391
|
$z += |
806
|
|
|
|
|
|
|
(2 / ($i * PI)) * |
807
|
|
|
|
|
|
|
(($beta - ($zeta**2) * exp(-0.5 * $tiy * ($sigma**2) * ($zeta**2 - $beta))) / ($zeta**2 - $beta)) * |
808
|
|
|
|
|
|
|
sin($zeta * log($S / $H1)); |
809
|
|
|
|
|
|
|
} |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
# For smaller durations or barriers farther apart, the value could converge to a small negative value |
812
|
4
|
|
|
|
|
46
|
return max(0, $K * (($S / $H1)**$alpha) * ($z + (1 - log($S / $H1) / $L))); |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
1; |
816
|
|
|
|
|
|
|
|