File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Vanna.pm
Criterion Covered Total %
statement 130 133 97.7
branch 11 14 78.5
condition 2 3 66.6
subroutine 22 22 100.0
pod 0 13 0.0
total 165 185 89.1


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Vanna;
2 1     1   456 use strict;
  1         2  
  1         28  
3 1     1   5 use warnings;
  1         2  
  1         41  
4              
5             our $VERSION = '0.06'; ## VERSION
6              
7 1     1   6 use List::Util qw( max );
  1         1  
  1         48  
8 1     1   5 use Math::CDF qw( pnorm );
  1         2  
  1         30  
9 1     1   5 use Math::Trig;
  1         2  
  1         210  
10 1     1   8 use Math::Business::BlackScholesMerton::Binaries;
  1         2  
  1         29  
11 1     1   6 use Math::Business::BlackScholes::Binaries::Greeks::Delta;
  1         1  
  1         21  
12 1     1   445 use Math::Business::BlackScholes::Binaries::Greeks::Vega;
  1         2  
  1         35  
13 1     1   6 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( dgauss );
  1         2  
  1         1961  
14              
15             =head1 NAME
16              
17             Math::Business::BlackScholes::Binaries::Greeks::Vanna
18              
19             =head1 DESCRIPTION
20              
21             Gets the Vanna for different options, Vanilla and Foreign for all our bet types
22              
23             =head1 SUBROUTINES
24              
25             See L
26              
27             =cut
28              
29             sub vanilla_call {
30 12     12 0 3727 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
31              
32 12         52 my $d1 = (log($S / $K) + ($mu + $vol * $vol / 2.0) * $t) / ($vol * sqrt($t));
33 12         24 my $d2 = $d1 - ($vol * sqrt($t));
34              
35 12         37 my $vega = Math::Business::BlackScholes::Binaries::Greeks::Vega::vanilla_call($S, $K, $t, $r_q, $mu, $vol);
36 12         26 my $vanna = -$vega * $d2 / ($S * $vol * sqrt($t));
37 12         26 return $vanna;
38             }
39              
40             sub vanilla_put {
41 6     6 0 3645 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
42              
43             # Same as vanna of vanilla call (because vega_vanilla_call = vega_vanilla_put)
44 6         16 return vanilla_call($S, $K, $t, $r_q, $mu, $vol);
45             }
46              
47             sub call {
48 32     32 0 3904 my ($S, $U, $t, $r_q, $mu, $vol) = @_;
49              
50 32         158 my $d1 = (log($S / $U) + ($mu + $vol * $vol / 2.0) * $t) / ($vol * sqrt($t));
51 32         64 my $d2 = $d1 - ($vol * sqrt($t));
52              
53 32         89 my $vanna = -dgauss($d2) * exp(-$r_q * $t) * (1 - $d1 * $d2) / ($S * $vol * $vol * sqrt($t));
54 32         96 return $vanna;
55             }
56              
57             sub put {
58 16     16 0 3983 my ($S, $D, $t, $r_q, $mu, $vol) = @_;
59              
60 16         41 return -1 * call($S, $D, $t, $r_q, $mu, $vol);
61             }
62              
63             sub expirymiss {
64 10     10 0 3961 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
65              
66 10         31 return call($S, $U, $t, $r_q, $mu, $vol) + put($S, $D, $t, $r_q, $mu, $vol);
67             }
68              
69             sub expiryrange {
70 5     5 0 3273 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
71              
72 5         18 return -1 * expirymiss($S, $U, $D, $t, $r_q, $mu, $vol);
73             }
74              
75             sub onetouch {
76 13     13 0 4395 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
77              
78 13 100       43 if (not defined $w) {
79 7         12 $w = 0;
80             }
81              
82 13         26 my $sqrt_t = sqrt($t);
83              
84 13         36 my $theta = (($mu) / $vol) + (0.5 * $vol);
85              
86 13         31 my $theta_ = (($mu) / $vol) - (0.5 * $vol);
87              
88             # Floor v_ squared at just above zero in case negative interest rates push it negative.
89 13         56 my $v_ = sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($theta_ * $theta_) + (2 * (1 - $w) * $r_q)));
90              
91 13         50 my $e = (log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
92 13         36 my $e_ = (-log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
93              
94 13 100       39 my $eta = ($S > $U) ? 1 : -1;
95              
96 13         37 my $pa_e = (log($U / $S) / ($vol * $vol * $sqrt_t)) + (($theta_ * $theta) / ($vol * $v_) * $sqrt_t);
97 13         34 my $pa_e_ = (-log($U / $S) / ($vol * $vol * $sqrt_t)) + (($theta_ * $theta) / ($vol * $v_) * $sqrt_t);
98              
99 13         34 my $A = -($theta + $theta_ + ($theta_ * $theta / $v_) + $v_) / ($vol * $vol);
100 13         34 my $A_ = -($theta + $theta_ - ($theta_ * $theta / $v_) - $v_) / ($vol * $vol);
101              
102 13         32 my $d_ = (log($U / $S) - $vol * $theta_ * $t) / ($vol * $sqrt_t);
103              
104 13         22 my ($part1, $part2, $subpart_1_1, $subpart_1_2, $subpart_2_1, $subpart_2_2);
105              
106 13         82 $subpart_1_1 =
107             pnorm(-$eta * $e) * $A * (-$vol - ($theta_ + $v_) * log($U / $S));
108 13         53 $subpart_1_2 = $eta * dgauss($e) / $sqrt_t * ($d_ * $pa_e + $A * log($U / $S) - 1.0 / $vol);
109              
110 13         53 $subpart_2_1 = pnorm($eta * $e_) * $A_ * (-$vol - ($theta_ - $v_) * log($U / $S));
111 13         35 $subpart_2_2 = $eta * dgauss($e_) / $sqrt_t * ($d_ * $pa_e_ - $A_ * log($U / $S) + 1.0 / $vol);
112              
113 13         37 $part1 = (($U / $S)**(($theta_ + $v_) / $vol)) * ($subpart_1_1 - $subpart_1_2);
114 13         37 $part2 = (($U / $S)**(($theta_ - $v_) / $vol)) * ($subpart_2_1 + $subpart_2_2);
115              
116 13         47 return ($part1 + $part2) * exp(-$w * $r_q * $t) / ($vol * $S);
117             }
118              
119             sub notouch {
120 6     6 0 4016 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
121              
122             # No touch bet always pay out at end
123 6         15 $w = 1;
124              
125             # Since the value VALUE_NOTOUCH = D(T) - VALUE_ONETOUCH, where D(T)
126             # is the discount from time T, any derivative (other than with
127             # respect to time or discount rate) of the value of notouch
128             # is just the negative of the onetouch derivative.
129 6         29 return (-1 * onetouch($S, $U, $t, $r_q, $mu, $vol, $w));
130             }
131              
132             sub upordown {
133 13     13 0 4684 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
134              
135             # $w = 0, paid at hit
136             # $w = 1, paid at end
137 13 100       45 if (not defined $w) { $w = 0; }
  7         17  
138              
139 13         44 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);
140             }
141              
142             sub xw_common_function_pelsser_1997 {
143 26     26 0 79 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta) = @_;
144              
145 26         37 my $pi = Math::Trig::pi;
146              
147 26         43 my $h = log($U / $D);
148 26         42 my $x = log($S / $D);
149              
150             # $eta = 1, onetouch up knockout down
151             # $eta = 0, onetouch down knockout up
152             # This variable used to check stability
153 26 50       62 if (not defined $eta) {
154 0         0 die
155             "$0: (xw_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.";
156             }
157 26 100       54 if ($eta == 0) { $x = $h - $x; }
  13         25  
158              
159 26         40 my $r_dash = $r_q * (1 - $w);
160 26         53 my $mu_new = $mu - (0.5 * $vol * $vol);
161 26         98 my $mu_dash = sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($mu_new * $mu_new) + (2 * $vol * $vol * $r_dash)));
162              
163 26         47 my $omega = ($vol * $vol);
164              
165 26         39 my $series_part = 0;
166 26         39 my $hyp_part = 0;
167              
168 26         70 my $stability_constant =
169             Math::Business::BlackScholesMerton::Binaries::get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1);
170              
171 26         555 my $iterations_required = Math::Business::BlackScholesMerton::Binaries::get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
172              
173 26         2043 for (my $k = 1; $k < $iterations_required; $k++) {
174 570         1117 my $lambda_k_dash = (0.5 * (($mu_dash * $mu_dash) / ($vol * $vol) + ($k * $k * $pi * $pi * $vol * $vol) / ($h * $h)));
175              
176             # d{lambda_k}/dw
177 570         1031 my $dlambdak_domega = 0.5 * (-($mu_new / $omega) - (($mu_new * $mu_new) / ($omega * $omega)) + (($k * $k * $pi * $pi) / ($h * $h)));
178              
179 570         868 my $beta_k = exp(-$lambda_k_dash * $t) / $lambda_k_dash;
180              
181             # d{beta_k}/d{lambda_k}
182 570         971 my $dbetak_dlambdak = -exp(-$lambda_k_dash * $t) * (($t * $lambda_k_dash + 1) / ($lambda_k_dash**2));
183              
184             # d{beta_k}/dw
185 570         725 my $dbetak_domega = $dlambdak_domega * $dbetak_dlambdak;
186              
187 570         945 my $phi = (1.0 / ($h * $h * $h)) * ($omega * $dbetak_domega + $beta_k) * $k * $k;
188              
189 570         949 $series_part += $phi * $pi * $pi * cos($k * $pi * ($h - $x) / $h);
190              
191 570 50 66     1489 if ($k == 1
192             and (not(abs(2 * $vol * $phi / $S) < $stability_constant)))
193             {
194 0         0 die
195             "$0: PELSSER VANNA 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 VANNA stability conditions (2 * $vol * $phi / $S 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.";
196             }
197             }
198              
199 26         58 my $alpha = $mu_dash / ($vol * $vol);
200 26         77 my $dalpha_domega = -(($mu_new * $omega) + (2 * $mu_new * $mu_new) + (2 * $r_dash * $omega)) / (2 * $alpha * $omega * $omega * $omega);
201              
202             # We have to handle the special case where the denominator approaches 0, see our documentation in
203             # quant/Documents/Breakout_bet.tex under the SVN "quant" module.
204 26 50       80 if ((Math::Trig::sinh($alpha * $h)**2) == 0) {
205 0         0 $hyp_part = 0;
206             } else {
207 26         350 $hyp_part =
208             -($dalpha_domega * $alpha) *
209             ((($h + $x) * Math::Trig::cosh($alpha * ($h - $x))) + (($h - $x) * Math::Trig::cosh($alpha * ($h + $x)))) /
210             (2 * Math::Trig::sinh($alpha * $h) * Math::Trig::sinh($alpha * $h)) +
211             $dalpha_domega *
212             (Math::Trig::sinh($alpha * ($h - $x)) + Math::Trig::sinh($alpha * ($h + $x))) /
213             (2 * Math::Trig::sinh($alpha * $h) * Math::Trig::sinh($alpha * $h));
214             }
215              
216 26         1527 my $d2c_domegadx = ($hyp_part + $series_part) * exp(-$r_q * $w * $t);
217              
218 26         58 return $d2c_domegadx;
219             }
220              
221             sub ot_up_ko_down_pelsser_1997 {
222 13     13 0 32 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
223              
224 13         32 my $mu_new = $mu - (0.5 * $vol * $vol);
225 13         37 my $h = log($U / $D);
226 13         29 my $x = log($S / $D);
227 13         22 my $omega = ($vol * $vol);
228              
229 13         52 my $c = Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
230 13         3738 my $dc_domega = Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
231 13         59 my $dc_dx = Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
232 13         45 my $d2c_domegadx = xw_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
233              
234 13         94 my $d2Vu_domegadx =
235             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * (1 + ($mu_new / $omega) * ($h - $x)) * exp(($mu_new / $omega) * ($h - $x)) * $c) -
236             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * ($h - $x) * exp(($mu_new / $omega) * ($h - $x)) * $dc_dx) -
237             (($mu_new / $omega) * exp(($mu_new / $omega) * ($h - $x)) * $dc_domega) +
238             (exp(($mu_new / $omega) * ($h - $x)) * $d2c_domegadx);
239              
240 13         50 return (2 * $vol / $S) * $d2Vu_domegadx;
241             }
242              
243             sub ot_down_ko_up_pelsser_1997 {
244 13     13 0 40 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
245              
246 13         26 my $mu_new = $mu - (0.5 * $vol * $vol);
247 13         25 my $x = log($S / $D);
248 13         22 my $omega = ($vol * $vol);
249              
250 13         38 my $c = Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
251 13         3597 my $dc_domega = Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
252 13         47 my $dc_dx = Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
253 13         34 my $d2c_domegadx = xw_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
254              
255 13         79 my $d2Vl_domegadx =
256             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * (1 - ($mu_new / $omega) * $x) * exp(-($mu_new / $omega) * $x) * $c) -
257             ((((0.5 * $omega) + $mu_new) / ($omega * $omega)) * $x * exp(-($mu_new / $omega) * $x) * $dc_dx) -
258             (($mu_new / $omega) * exp(-($mu_new / $omega) * $x) * $dc_domega) -
259             (exp(-($mu_new / $omega) * $x) * $d2c_domegadx);
260              
261 13         52 return (2 * $vol / $S) * $d2Vl_domegadx;
262             }
263              
264             sub range {
265 6     6 0 4002 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
266              
267             # Range always pay out at end
268 6         13 $w = 1;
269              
270 6         22 return -1 * upordown($S, $U, $D, $t, $r_q, $mu, $vol, $w);
271             }
272              
273             1;
274