File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Volga.pm
Criterion Covered Total %
statement 151 155 97.4
branch 12 16 75.0
condition 2 3 66.6
subroutine 21 21 100.0
pod 0 13 0.0
total 186 208 89.4


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Volga;
2 1     1   392 use strict; use warnings;
  1     1   1  
  1         29  
  1         3  
  1         1  
  1         31  
3              
4             our $VERSION = '0.04';
5              
6 1     1   4 use List::Util qw( max );
  1         3  
  1         102  
7 1     1   4 use Math::Business::BlackScholes::Binaries;
  1         1  
  1         18  
8 1     1   3 use Math::Business::BlackScholes::Binaries::Greeks::Vega;
  1         6  
  1         14  
9 1     1   3 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( dgauss );
  1         1  
  1         32  
10 1     1   3 use Math::CDF qw( pnorm );
  1         1  
  1         27  
11 1     1   3 use Math::Trig;
  1         1  
  1         1666  
12              
13             =head1 NAME
14              
15             Math::Business::BlackScholes::Binaries::Greeks::Volga
16              
17             =head1 DESCRIPTION
18              
19             Gets the Volga for different options, Vanilla and Foreign for all our bet types
20              
21             =cut
22              
23             =head1 SUBROUTINES
24              
25             See L
26              
27             =cut
28              
29             sub vanilla_call {
30 12     12 0 2125 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
31              
32 12         38 my $d1 = (log($S / $K) + ($mu + $vol * $vol / 2.0) * $t) / ($vol * sqrt($t));
33 12         13 my $d2 = $d1 - ($vol * sqrt($t));
34              
35 12         20 my $vega = Math::Business::BlackScholes::Binaries::Greeks::Vega::vanilla_call($S, $K, $t, $r_q, $mu, $vol);
36              
37 12         15 my $volga = $vega * $d1 * $d2 / $vol;
38 12         15 return $volga;
39             }
40              
41             sub vanilla_put {
42 6     6 0 1820 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
43              
44             # Same as volga of vanilla call (because vega_vanilla_call = vega_vanilla_put)
45 6         7 return vanilla_call($S, $K, $t, $r_q, $mu, $vol);
46             }
47              
48             sub call {
49 32     32 0 2063 my ($S, $U, $t, $r_q, $mu, $vol) = @_;
50              
51 32         113 my $d1 = (log($S / $U) + ($mu + $vol * $vol / 2.0) * $t) / ($vol * sqrt($t));
52 32         29 my $d2 = $d1 - ($vol * sqrt($t));
53              
54 32         63 my $volga = -dgauss($d2) * exp(-$r_q * $t) / ($vol * $vol) * (-$d1 - $d2 + ($d1 * $d1 * $d2));
55 32         157 return $volga;
56             }
57              
58             sub put {
59 16     16 0 2023 my ($S, $D, $t, $r_q, $mu, $vol) = @_;
60              
61 16         38 return -1 * call($S, $D, $t, $r_q, $mu, $vol);
62             }
63              
64             sub expirymiss {
65 10     10 0 2167 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
66              
67 10         28 return call($S, $U, $t, $r_q, $mu, $vol) + put($S, $D, $t, $r_q, $mu, $vol);
68             }
69              
70             sub expiryrange {
71 5     5 0 1780 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
72              
73 5         15 return -1 * expirymiss($S, $U, $D, $t, $r_q, $mu, $vol);
74             }
75              
76             sub onetouch {
77 13     13 0 2768 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
78 13 100       34 if (not defined $w) {
79 7         7 $w = 0;
80             }
81 13         18 my $sqrt_t = sqrt($t);
82              
83 13         24 my $theta = (($mu) / $vol) + (0.5 * $vol);
84 13         22 my $theta_ = (($mu) / $vol) - (0.5 * $vol);
85              
86             # Floor v_ squared at just above zero in case negative interest rates push it negative.
87 13         42 my $v_ = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $theta_ * $theta_ ) + ( 2 * ( 1 - $w ) * $r_q ) ) );
88              
89 13         34 my $e = (log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
90 13         27 my $e_ = (-log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
91              
92 13 100       27 my $eta = ($S > $U) ? 1 : -1;
93              
94 13         24 my $pa_e = (log($U / $S) / ($vol * $vol * $sqrt_t)) + ($theta_ * $theta) / ($vol * $v_) * $sqrt_t;
95 13         25 my $pa_e_ = (-log($U / $S) / ($vol * $vol * $sqrt_t)) + (($theta_ * $theta) / ($vol * $v_) * $sqrt_t);
96              
97 13         27 my $A = -($theta + $theta_ + ($theta_ * $theta / $v_) + $v_) / ($vol * $vol);
98 13         23 my $A_ = -($theta + $theta_ - ($theta_ * $theta / $v_) - $v_) / ($vol * $vol);
99              
100 13         39 my $g = 1 / ($vol * $vol * $v_) * (-$theta * $theta - $theta_ * $theta_ - $theta * $theta_ + $theta * $theta_ * $theta * $theta_ / ($v_ * $v_));
101              
102 13         23 my $pa_2_e = -2 * log($U / $S) / ($vol * $vol * $vol * $sqrt_t) + $g * $sqrt_t;
103 13         21 my $pa_2_e_ = 2 * log($U / $S) / ($vol * $vol * $vol * $sqrt_t) + $g * $sqrt_t;
104              
105 13         63 my $pa_A = ($theta + $theta_) / ($vol * $vol * $vol) - (2 * $A + $g) / $vol;
106 13         22 my $pa_A_ = ($theta + $theta_) / ($vol * $vol * $vol) - (2 * $A_ - $g) / $vol;
107              
108 13         12 my ($part1, $part2, $subpart_1_1, $subpart_1_2, $subpart_2_1, $subpart_2_2);
109              
110 13         67 $subpart_1_1 = pnorm(-$eta * $e) * log($U / $S) * ($A * $A * log($U / $S) + $pa_A);
111 13         29 $subpart_1_2 = $eta * dgauss($e) * (2 * log($U / $S) * $A * $pa_e - $e * $pa_e * $pa_e + $pa_2_e);
112              
113 13         37 $subpart_2_1 = pnorm($eta * $e_) * log($U / $S) * ($A_ * $A_ * log($U / $S) + $pa_A_);
114 13         20 $subpart_2_2 = $eta * dgauss($e_) * (2 * log($U / $S) * $A_ * $pa_e_ - $e_ * $pa_e_ * $pa_e_ + $pa_2_e_);
115              
116 13         20 $part1 = (($U / $S)**(($theta_ + $v_) / $vol)) * ($subpart_1_1 - $subpart_1_2);
117 13         17 $part2 = (($U / $S)**(($theta_ - $v_) / $vol)) * ($subpart_2_1 + $subpart_2_2);
118              
119 13         27 return exp(-$w * $r_q * $t) * ($part1 + $part2);
120             }
121              
122             sub notouch {
123 6     6 0 1848 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
124              
125             # No touch bet always pay out at end
126 6         7 $w = 1;
127              
128             # Since the value VALUE_NOTOUCH = D(T) - VALUE_ONETOUCH, where D(T)
129             # is the discount from time T, any derivative (other than with
130             # respect to time or discount rate) of the value of notouch
131             # is just the negative of the onetouch derivative.
132 6         15 return (-1 * onetouch($S, $U, $t, $r_q, $mu, $vol, $w));
133             }
134              
135             sub upordown {
136 13     13 0 3487 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
137              
138             # $w = 0, paid at hit
139             # $w = 1, paid at end
140 13 100       36 if (not defined $w) { $w = 0; }
  7         15  
141              
142 13         50 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);
143             }
144              
145             sub w_common_function_pelsser_1997 {
146 26     26 0 47 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta) = @_;
147              
148 26         35 my $pi = Math::Trig::pi;
149              
150 26         46 my $h = log($U / $D);
151 26         36 my $x = log($S / $D);
152              
153             # $eta = 1, onetouch up knockout down
154             # $eta = 0, onetouch down knockout up
155             # This variable used to check stability
156 26 50       60 if (not defined $eta) {
157 0         0 die
158             "$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.";
159             }
160 26 100       57 if ($eta == 0) { $x = $h - $x; }
  13         20  
161              
162 26         54 my $mu_new = $mu - (0.5 * $vol * $vol);
163 26         105 my $mu_dash = sqrt(max($Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU,($mu_new * $mu_new) + (2 * $vol * $vol * $r_q * (1 - $w))));
164              
165 26         40 my $r_dash = $r_q * (1 - $w);
166 26         34 my $omega = ($vol * $vol);
167              
168 26         36 my $series_part = 0;
169 26         25 my $hyp_part = 0;
170              
171 26         66 my $stability_constant = Math::Business::BlackScholes::Binaries::get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1);
172              
173 26         345 my $iterations_required = Math::Business::BlackScholes::Binaries::get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
174              
175 26         1048 for (my $k = 1; $k < $iterations_required; $k++) {
176 570         812 my $lambda_k_dash = (0.5 * (($mu_dash * $mu_dash) / ($vol * $vol) + ($k * $k * $pi * $pi * $vol * $vol) / ($h * $h)));
177              
178             # d{lambda_k}/dw
179 570         782 my $dlambdak_domega = 0.5 * (-($mu_new / $omega) - (($mu_new * $mu_new) / ($omega * $omega)) + (($k * $k * $pi * $pi) / ($h * $h)));
180 570         601 my $d2lambdak_domega2 = 0.5 * ($omega + 2 * $mu_new) / (2 * $omega * $omega);
181 570         517 $d2lambdak_domega2 *= (1 + (2 * $mu_new / $omega));
182              
183 570         561 my $beta_k = exp(-$lambda_k_dash * $t) / $lambda_k_dash;
184              
185             # d{beta_k}/d{lambda_k}
186 570         717 my $dbetak_dlambdak = -exp(-$lambda_k_dash * $t) * (($t * $lambda_k_dash + 1) / ($lambda_k_dash**2));
187 570         1198 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         462 my $dbetak_domega = $dlambdak_domega * $dbetak_dlambdak;
191 570         573 my $d2betak_domega2 = ($dlambdak_domega * $dlambdak_domega * $d2betak_dlambdak2) + ($dbetak_dlambdak * $d2lambdak_domega2);
192              
193 570         681 my $phi = (1.0 / ($h * $h)) * ($omega * $d2betak_domega2 + 2 * $dbetak_domega) * $k;
194              
195 570         713 $series_part += $phi * $pi * sin($k * $pi * ($h - $x) / $h);
196              
197 570 50 66     1507 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         36 my $alpha = $mu_dash / ($vol * $vol);
204 26         70 my $dalpha_domega = -(($mu_new * $omega) + (2 * $mu_new * $mu_new) + (2 * $r_dash * $omega)) / (2 * $alpha * $omega * $omega * $omega);
205              
206 26         54 my $d2alpha_domega2 = $alpha * ($omega**3) * (2 * $mu_new + $omega - 4 * $r_dash);
207 26         78 $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         52 $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         23 my $hyp_part1;
215 26 50       86 if ((Math::Trig::sinh($alpha * $h)**3) == 0) {
216 0         0 $hyp_part1 = 0;
217             } else {
218 26         277 $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         824 $hyp_part1 *= ($dalpha_domega**2) / (2 * (Math::Trig::sinh($alpha * $h)**3));
225             }
226              
227 26         161 my $hyp_part2;
228 26 50       55 if ((Math::Trig::sinh($alpha * $h)**2) == 0) {
229 0         0 $hyp_part2 = 0;
230             } else {
231 26         188 $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         424 $hyp_part = $hyp_part1 + $hyp_part2;
237              
238 26         58 my $d2c_domega2 = ($hyp_part - $series_part) * exp(-$r_q * $w * $t);
239              
240 26         47 return $d2c_domega2;
241             }
242              
243             sub ot_up_ko_down_pelsser_1997 {
244 13     13 0 23 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
245              
246 13         40 my $mu_new = $mu - (0.5 * $vol * $vol);
247 13         34 my $h = log($U / $D);
248 13         26 my $x = log($S / $D);
249 13         18 my $omega = ($vol * $vol);
250              
251 13         40 my $c = Math::Business::BlackScholes::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
252 13         2097 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         47 my $d2c_domega2 = w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
254              
255 13         47 my $Vu = Math::Business::BlackScholes::Binaries::ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
256 13         2216 my $dVu_domega = -((0.5 * $omega + $mu_new) * ($h - $x) / ($omega * $omega)) * $c;
257 13         18 $dVu_domega += $dc_domega;
258 13         30 $dVu_domega *= exp($mu_new * ($h - $x) / $omega);
259              
260 13         104 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         60 return (4 * $vol * $vol * $d2Vu_domega2) + (2 * $dVu_domega);
267             }
268              
269             sub ot_down_ko_up_pelsser_1997 {
270 13     13 0 28 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
271              
272 13         29 my $mu_new = $mu - (0.5 * $vol * $vol);
273 13         24 my $h = log($U / $D);
274 13         23 my $x = log($S / $D);
275 13         23 my $omega = ($vol * $vol);
276              
277 13         34 my $c = Math::Business::BlackScholes::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
278 13         1940 my $dc_domega = Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
279 13         45 my $d2c_domega2 = w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
280              
281 13         51 my $Vl = Math::Business::BlackScholes::Binaries::ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
282 13         2121 my $dVl_domega = ((0.5 * $omega + $mu_new) * $x / ($omega * $omega)) * $c;
283 13         19 $dVl_domega += $dc_domega;
284 13         28 $dVl_domega *= exp(-$mu_new * $x / $omega);
285              
286 13         74 my $d2Vl_domega2 =
287             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * $x * $dVl_domega) -
288             ((($omega + (2 * $mu_new)) / ($omega**3)) * $x * $Vl) +
289             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * $x * exp(-$mu_new * $x / $omega) * $dc_domega) +
290             (exp(-$mu_new * $x / $omega) * $d2c_domega2);
291              
292 13         57 return (4 * $vol * $vol * $d2Vl_domega2) + (2 * $dVl_domega);
293             }
294              
295             sub range {
296 6     6 0 3324 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
297              
298             # Range always pay out at end
299 6         11 $w = 1;
300              
301 6         24 return -1 * upordown($S, $U, $D, $t, $r_q, $mu, $vol, $w);
302             }
303              
304             1;
305