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