File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Volga.pm
Criterion Covered Total %
statement 149 153 97.3
branch 12 16 75.0
condition 2 3 66.6
subroutine 21 21 100.0
pod 0 13 0.0
total 184 206 89.3


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Volga;
2 1     1   505 use strict;
  1         2  
  1         30  
3 1     1   5 use warnings;
  1         3  
  1         41  
4              
5             our $VERSION = '0.06'; ## VERSION
6              
7 1     1   5 use List::Util qw( max );
  1         2  
  1         60  
8 1     1   7 use Math::Business::BlackScholesMerton::Binaries;
  1         1  
  1         31  
9 1     1   12 use Math::Business::BlackScholes::Binaries::Greeks::Vega;
  1         2  
  1         39  
10 1     1   6 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( dgauss );
  1         1  
  1         57  
11 1     1   7 use Math::CDF qw( pnorm );
  1         1  
  1         42  
12 1     1   6 use Math::Trig;
  1         2  
  1         2240  
13              
14             =head1 NAME
15              
16             Math::Business::BlackScholes::Binaries::Greeks::Volga
17              
18             =head1 DESCRIPTION
19              
20             Gets the Volga for different options, Vanilla and Foreign for all our bet types
21              
22             =cut
23              
24             =head1 SUBROUTINES
25              
26             See L
27              
28             =cut
29              
30             sub vanilla_call {
31 12     12 0 3813 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
32              
33 12         48 my $d1 = (log($S / $K) + ($mu + $vol * $vol / 2.0) * $t) / ($vol * sqrt($t));
34 12         27 my $d2 = $d1 - ($vol * sqrt($t));
35              
36 12         31 my $vega = Math::Business::BlackScholes::Binaries::Greeks::Vega::vanilla_call($S, $K, $t, $r_q, $mu, $vol);
37              
38 12         20 my $volga = $vega * $d1 * $d2 / $vol;
39 12         24 return $volga;
40             }
41              
42             sub vanilla_put {
43 6     6 0 3711 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
44              
45             # Same as volga of vanilla call (because vega_vanilla_call = vega_vanilla_put)
46 6         18 return vanilla_call($S, $K, $t, $r_q, $mu, $vol);
47             }
48              
49             sub call {
50 32     32 0 4150 my ($S, $U, $t, $r_q, $mu, $vol) = @_;
51              
52 32         121 my $d1 = (log($S / $U) + ($mu + $vol * $vol / 2.0) * $t) / ($vol * sqrt($t));
53 32         64 my $d2 = $d1 - ($vol * sqrt($t));
54              
55 32         84 my $volga = -dgauss($d2) * exp(-$r_q * $t) / ($vol * $vol) * (-$d1 - $d2 + ($d1 * $d1 * $d2));
56 32         93 return $volga;
57             }
58              
59             sub put {
60 16     16 0 3688 my ($S, $D, $t, $r_q, $mu, $vol) = @_;
61              
62 16         39 return -1 * call($S, $D, $t, $r_q, $mu, $vol);
63             }
64              
65             sub expirymiss {
66 10     10 0 3646 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
67              
68 10         89 return call($S, $U, $t, $r_q, $mu, $vol) + put($S, $D, $t, $r_q, $mu, $vol);
69             }
70              
71             sub expiryrange {
72 5     5 0 3283 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
73              
74 5         20 return -1 * expirymiss($S, $U, $D, $t, $r_q, $mu, $vol);
75             }
76              
77             sub onetouch {
78 13     13 0 4507 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
79 13 100       40 if (not defined $w) {
80 7         11 $w = 0;
81             }
82 13         27 my $sqrt_t = sqrt($t);
83              
84 13         35 my $theta = (($mu) / $vol) + (0.5 * $vol);
85 13         30 my $theta_ = (($mu) / $vol) - (0.5 * $vol);
86              
87             # Floor v_ squared at just above zero in case negative interest rates push it negative.
88 13         52 my $v_ = sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($theta_ * $theta_) + (2 * (1 - $w) * $r_q)));
89              
90 13         50 my $e = (log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
91 13         38 my $e_ = (-log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
92              
93 13 100       39 my $eta = ($S > $U) ? 1 : -1;
94              
95 13         38 my $pa_e = (log($U / $S) / ($vol * $vol * $sqrt_t)) + ($theta_ * $theta) / ($vol * $v_) * $sqrt_t;
96 13         33 my $pa_e_ = (-log($U / $S) / ($vol * $vol * $sqrt_t)) + (($theta_ * $theta) / ($vol * $v_) * $sqrt_t);
97              
98 13         34 my $A = -($theta + $theta_ + ($theta_ * $theta / $v_) + $v_) / ($vol * $vol);
99 13         32 my $A_ = -($theta + $theta_ - ($theta_ * $theta / $v_) - $v_) / ($vol * $vol);
100              
101 13         40 my $g = 1 / ($vol * $vol * $v_) * (-$theta * $theta - $theta_ * $theta_ - $theta * $theta_ + $theta * $theta_ * $theta * $theta_ / ($v_ * $v_));
102              
103 13         37 my $pa_2_e = -2 * log($U / $S) / ($vol * $vol * $vol * $sqrt_t) + $g * $sqrt_t;
104 13         31 my $pa_2_e_ = 2 * log($U / $S) / ($vol * $vol * $vol * $sqrt_t) + $g * $sqrt_t;
105              
106 13         36 my $pa_A = ($theta + $theta_) / ($vol * $vol * $vol) - (2 * $A + $g) / $vol;
107 13         32 my $pa_A_ = ($theta + $theta_) / ($vol * $vol * $vol) - (2 * $A_ - $g) / $vol;
108              
109 13         21 my ($part1, $part2, $subpart_1_1, $subpart_1_2, $subpart_2_1, $subpart_2_2);
110              
111 13         81 $subpart_1_1 = pnorm(-$eta * $e) * log($U / $S) * ($A * $A * log($U / $S) + $pa_A);
112 13         43 $subpart_1_2 = $eta * dgauss($e) * (2 * log($U / $S) * $A * $pa_e - $e * $pa_e * $pa_e + $pa_2_e);
113              
114 13         57 $subpart_2_1 = pnorm($eta * $e_) * log($U / $S) * ($A_ * $A_ * log($U / $S) + $pa_A_);
115 13         30 $subpart_2_2 = $eta * dgauss($e_) * (2 * log($U / $S) * $A_ * $pa_e_ - $e_ * $pa_e_ * $pa_e_ + $pa_2_e_);
116              
117 13         35 $part1 = (($U / $S)**(($theta_ + $v_) / $vol)) * ($subpart_1_1 - $subpart_1_2);
118 13         34 $part2 = (($U / $S)**(($theta_ - $v_) / $vol)) * ($subpart_2_1 + $subpart_2_2);
119              
120 13         43 return exp(-$w * $r_q * $t) * ($part1 + $part2);
121             }
122              
123             sub notouch {
124 6     6 0 4798 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
125              
126             # No touch bet always pay out at end
127 6         16 $w = 1;
128              
129             # Since the value VALUE_NOTOUCH = D(T) - VALUE_ONETOUCH, where D(T)
130             # is the discount from time T, any derivative (other than with
131             # respect to time or discount rate) of the value of notouch
132             # is just the negative of the onetouch derivative.
133 6         22 return (-1 * onetouch($S, $U, $t, $r_q, $mu, $vol, $w));
134             }
135              
136             sub upordown {
137 13     13 0 5132 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
138              
139             # $w = 0, paid at hit
140             # $w = 1, paid at end
141 13 100       46 if (not defined $w) { $w = 0; }
  7         17  
142              
143 13         52 return ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w) + ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
144             }
145              
146             sub w_common_function_pelsser_1997 {
147 26     26 0 76 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta) = @_;
148              
149 26         44 my $pi = Math::Trig::pi;
150              
151 26         50 my $h = log($U / $D);
152 26         44 my $x = log($S / $D);
153              
154             # $eta = 1, onetouch up knockout down
155             # $eta = 0, onetouch down knockout up
156             # This variable used to check stability
157 26 50       61 if (not defined $eta) {
158 0         0 die
159             "$0: (w_common_function_pelsser_1997) Wrong usage of this function for S=$S, U=$U, D=$D, t=$t, r_q=$r_q, mu=$mu, vol=$vol, w=$w. eta not defined.";
160             }
161 26 100       59 if ($eta == 0) { $x = $h - $x; }
  13         20  
162              
163 26         53 my $mu_new = $mu - (0.5 * $vol * $vol);
164 26         89 my $mu_dash = sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($mu_new * $mu_new) + (2 * $vol * $vol * $r_q * (1 - $w))));
165              
166 26         48 my $r_dash = $r_q * (1 - $w);
167 26         41 my $omega = ($vol * $vol);
168              
169 26         40 my $series_part = 0;
170 26         35 my $hyp_part = 0;
171              
172 26         81 my $stability_constant =
173             Math::Business::BlackScholesMerton::Binaries::get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1);
174              
175 26         603 my $iterations_required = Math::Business::BlackScholesMerton::Binaries::get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
176              
177 26         1982 for (my $k = 1; $k < $iterations_required; $k++) {
178 570         1092 my $lambda_k_dash = (0.5 * (($mu_dash * $mu_dash) / ($vol * $vol) + ($k * $k * $pi * $pi * $vol * $vol) / ($h * $h)));
179              
180             # d{lambda_k}/dw
181 570         1086 my $dlambdak_domega = 0.5 * (-($mu_new / $omega) - (($mu_new * $mu_new) / ($omega * $omega)) + (($k * $k * $pi * $pi) / ($h * $h)));
182 570         886 my $d2lambdak_domega2 = 0.5 * ($omega + 2 * $mu_new) / (2 * $omega * $omega);
183 570         817 $d2lambdak_domega2 *= (1 + (2 * $mu_new / $omega));
184              
185             # d{beta_k}/d{lambda_k}
186 570         1081 my $dbetak_dlambdak = -exp(-$lambda_k_dash * $t) * (($t * $lambda_k_dash + 1) / ($lambda_k_dash**2));
187 570         1319 my $d2betak_dlambdak2 = -($t * $dbetak_dlambdak) + exp(-$lambda_k_dash * $t) * (($t / ($lambda_k_dash**2)) + (2 / ($lambda_k_dash**3)));
188              
189             # d{beta_k}/dw
190 570         781 my $dbetak_domega = $dlambdak_domega * $dbetak_dlambdak;
191 570         859 my $d2betak_domega2 = ($dlambdak_domega * $dlambdak_domega * $d2betak_dlambdak2) + ($dbetak_dlambdak * $d2lambdak_domega2);
192              
193 570         965 my $phi = (1.0 / ($h * $h)) * ($omega * $d2betak_domega2 + 2 * $dbetak_domega) * $k;
194              
195 570         999 $series_part += $phi * $pi * sin($k * $pi * ($h - $x) / $h);
196              
197 570 50 66     1583 if ($k == 1 and (not(abs(4 * $vol * $vol * $phi) < $stability_constant))) {
198 0         0 die
199             "$0: PELSSER VOLGA formula for S=$S, U=$U, D=$D, t=$t, r_q=$r_q, mu=$mu, vol=$vol, w=$w, eta=$eta cannot be evaluated because PELSSER VOLGA stability conditions (4 * $vol * $vol * $phi less than $stability_constant) not met. This could be due to barriers too big, volatilities too low, interest/dividend rates too high, or machine accuracy too low.";
200             }
201             }
202              
203 26         54 my $alpha = $mu_dash / ($vol * $vol);
204 26         66 my $dalpha_domega = -(($mu_new * $omega) + (2 * $mu_new * $mu_new) + (2 * $r_dash * $omega)) / (2 * $alpha * $omega * $omega * $omega);
205              
206 26         73 my $d2alpha_domega2 = $alpha * ($omega**3) * (2 * $mu_new + $omega - 4 * $r_dash);
207 26         112 $d2alpha_domega2 +=
208             (($mu_new * $omega) + (2 * $mu_new * $mu_new) + (2 * $r_dash * $omega)) *
209             ((6 * $alpha * $omega * $omega) + (2 * $omega * $omega * $omega * $dalpha_domega));
210 26         57 $d2alpha_domega2 = $d2alpha_domega2 / (4 * $alpha * $alpha * ($omega**6));
211              
212             # We have to handle the special case where the denominator approaches 0, see our documentation in
213             # quant/Documents/Breakout_bet.tex under the SVN "quant" module.
214 26         36 my $hyp_part1;
215 26 50       94 if ((Math::Trig::sinh($alpha * $h)**3) == 0) {
216 0         0 $hyp_part1 = 0;
217             } else {
218 26         363 $hyp_part1 =
219             Math::Trig::sinh($alpha * $h) *
220             ($h**2 - $x**2) *
221             (Math::Trig::cosh($alpha * ($h - $x)) - Math::Trig::cosh($alpha * ($h + $x))) -
222             (2 * $h * Math::Trig::cosh($alpha * $h)) *
223             ((($h + $x) * Math::Trig::sinh($alpha * ($h - $x))) - (($h - $x) * Math::Trig::sinh($alpha * ($h + $x))));
224 26         1259 $hyp_part1 *= ($dalpha_domega**2) / (2 * (Math::Trig::sinh($alpha * $h)**3));
225             }
226              
227 26         266 my $hyp_part2;
228 26 50       52 if ((Math::Trig::sinh($alpha * $h)**2) == 0) {
229 0         0 $hyp_part2 = 0;
230             } else {
231 26         280 $hyp_part2 =
232             ($d2alpha_domega2 / (2 * (Math::Trig::sinh($alpha * $h)**2))) *
233             (($h + $x) * Math::Trig::sinh($alpha * ($h - $x)) - ($h - $x) * Math::Trig::sinh($alpha * ($h + $x)));
234             }
235              
236 26         676 $hyp_part = $hyp_part1 + $hyp_part2;
237              
238 26         79 my $d2c_domega2 = ($hyp_part - $series_part) * exp(-$r_q * $w * $t);
239              
240 26         63 return $d2c_domega2;
241             }
242              
243             sub ot_up_ko_down_pelsser_1997 {
244 13     13 0 53 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
245              
246 13         37 my $mu_new = $mu - (0.5 * $vol * $vol);
247 13         42 my $h = log($U / $D);
248 13         28 my $x = log($S / $D);
249 13         25 my $omega = ($vol * $vol);
250              
251 13         56 my $c = Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
252 13         3891 my $dc_domega = Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
253 13         51 my $d2c_domega2 = w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
254              
255 13         52 my $Vu = Math::Business::BlackScholesMerton::Binaries::ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
256 13         3801 my $dVu_domega = -((0.5 * $omega + $mu_new) * ($h - $x) / ($omega * $omega)) * $c;
257 13         29 $dVu_domega += $dc_domega;
258 13         34 $dVu_domega *= exp($mu_new * ($h - $x) / $omega);
259              
260 13         77 my $d2Vu_domega2 =
261             -((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * ($h - $x) * $dVu_domega) +
262             ((($omega + (2 * $mu_new)) / ($omega**3)) * ($h - $x) * $Vu) -
263             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * ($h - $x) * exp($mu_new * ($h - $x) / $omega) * $dc_domega) +
264             (exp($mu_new * ($h - $x) / $omega) * $d2c_domega2);
265              
266 13         64 return (4 * $vol * $vol * $d2Vu_domega2) + (2 * $dVu_domega);
267             }
268              
269             sub ot_down_ko_up_pelsser_1997 {
270 13     13 0 37 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
271              
272 13         29 my $mu_new = $mu - (0.5 * $vol * $vol);
273 13         36 my $x = log($S / $D);
274 13         25 my $omega = ($vol * $vol);
275              
276 13         40 my $c = Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
277 13         3626 my $dc_domega = Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
278 13         52 my $d2c_domega2 = w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
279              
280 13         49 my $Vl = Math::Business::BlackScholesMerton::Binaries::ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
281 13         3995 my $dVl_domega = ((0.5 * $omega + $mu_new) * $x / ($omega * $omega)) * $c;
282 13         25 $dVl_domega += $dc_domega;
283 13         36 $dVl_domega *= exp(-$mu_new * $x / $omega);
284              
285 13         64 my $d2Vl_domega2 =
286             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * $x * $dVl_domega) -
287             ((($omega + (2 * $mu_new)) / ($omega**3)) * $x * $Vl) +
288             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * $x * exp(-$mu_new * $x / $omega) * $dc_domega) +
289             (exp(-$mu_new * $x / $omega) * $d2c_domega2);
290              
291 13         57 return (4 * $vol * $vol * $d2Vl_domega2) + (2 * $dVl_domega);
292             }
293              
294             sub range {
295 6     6 0 4152 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
296              
297             # Range always pay out at end
298 6         12 $w = 1;
299              
300 6         22 return -1 * upordown($S, $U, $D, $t, $r_q, $mu, $vol, $w);
301             }
302              
303             1;
304