File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Vanna.pm
Criterion Covered Total %
statement 131 134 97.7
branch 11 14 78.5
condition 2 3 66.6
subroutine 22 22 100.0
pod 0 13 0.0
total 166 186 89.2


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Vanna;
2 1     1   334 use strict; use warnings;
  1     1   1  
  1         25  
  1         3  
  1         1  
  1         28  
3              
4             our $VERSION = '0.04';
5              
6 1     1   3 use List::Util qw( max );
  1         5  
  1         36  
7 1     1   31 use Math::CDF qw( pnorm );
  1         1  
  1         31  
8 1     1   3 use Math::Trig;
  1         0  
  1         119  
9 1     1   4 use Math::Business::BlackScholes::Binaries;
  1         0  
  1         12  
10 1     1   3 use Math::Business::BlackScholes::Binaries::Greeks::Delta;
  1         19  
  1         14  
11 1     1   254 use Math::Business::BlackScholes::Binaries::Greeks::Vega;
  1         1  
  1         24  
12 1     1   5 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( dgauss );
  1         1  
  1         1281  
13              
14             =head1 NAME
15              
16             Math::Business::BlackScholes::Binaries::Greeks::Vanna
17              
18             =head1 DESCRIPTION
19              
20             Gets the Vanna for different options, Vanilla and Foreign for all our bet types
21              
22             =head1 SUBROUTINES
23              
24             See L
25              
26             =cut
27              
28             sub vanilla_call {
29 12     12 0 2211 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
30              
31 12         36 my $d1 =
32             ( log( $S / $K ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
33             ( $vol * sqrt($t) );
34 12         14 my $d2 = $d1 - ( $vol * sqrt($t) );
35              
36 12         25 my $vega =
37             Math::Business::BlackScholes::Binaries::Greeks::Vega::vanilla_call( $S,
38             $K, $t, $r_q, $mu, $vol );
39 12         16 my $vanna = -$vega * $d2 / ( $S * $vol * sqrt($t) );
40 12         16 return $vanna;
41             }
42              
43             sub vanilla_put {
44 6     6 0 1918 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
45              
46             # Same as vanna of vanilla call (because vega_vanilla_call = vega_vanilla_put)
47 6         9 return vanilla_call( $S, $K, $t, $r_q, $mu, $vol );
48             }
49              
50             sub call {
51 32     32 0 2102 my ( $S, $U, $t, $r_q, $mu, $vol ) = @_;
52              
53 32         115 my $d1 =
54             ( log( $S / $U ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
55             ( $vol * sqrt($t) );
56 32         45 my $d2 = $d1 - ( $vol * sqrt($t) );
57              
58 32         66 my $vanna =
59             -dgauss($d2) *
60             exp( -$r_q * $t ) *
61             ( 1 - $d1 * $d2 ) /
62             ( $S * $vol * $vol * sqrt($t) );
63 32         68 return $vanna;
64             }
65              
66             sub put {
67 16     16 0 2035 my ( $S, $D, $t, $r_q, $mu, $vol ) = @_;
68              
69 16         29 return -1 * call( $S, $D, $t, $r_q, $mu, $vol );
70             }
71              
72             sub expirymiss {
73 10     10 0 2177 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
74              
75 10         26 return call( $S, $U, $t, $r_q, $mu, $vol ) +
76             put( $S, $D, $t, $r_q, $mu, $vol );
77             }
78              
79             sub expiryrange {
80 5     5 0 1569 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
81              
82 5         11 return -1 * expirymiss( $S, $U, $D, $t, $r_q, $mu, $vol );
83             }
84              
85             sub onetouch {
86 13     13 0 2315 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
87              
88 13 100       28 if ( not defined $w ) {
89 7         9 $w = 0;
90             }
91              
92 13         22 my $sqrt_t = sqrt($t);
93              
94 13         29 my $theta = ( ($mu) / $vol ) + ( 0.5 * $vol );
95              
96 13         20 my $theta_ = ( ($mu) / $vol ) - ( 0.5 * $vol );
97              
98             # Floor v_ squared at just above zero in case negative interest rates push it negative.
99 13         43 my $v_ = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $theta_ * $theta_ ) + ( 2 * ( 1 - $w ) * $r_q ) ) );
100              
101 13         34 my $e = ( log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
102 13         31 my $e_ = ( -log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
103              
104 13 100       27 my $eta = ( $S > $U ) ? 1 : -1;
105              
106 13         31 my $pa_e =
107             ( log( $U / $S ) / ( $vol * $vol * $sqrt_t ) ) +
108             ( ( $theta_ * $theta ) / ( $vol * $v_ ) * $sqrt_t );
109 13         32 my $pa_e_ =
110             ( -log( $U / $S ) / ( $vol * $vol * $sqrt_t ) ) +
111             ( ( $theta_ * $theta ) / ( $vol * $v_ ) * $sqrt_t );
112              
113 13         23 my $A =
114             -( $theta + $theta_ + ( $theta_ * $theta / $v_ ) + $v_ ) /
115             ( $vol * $vol );
116 13         21 my $A_ =
117             -( $theta + $theta_ - ( $theta_ * $theta / $v_ ) - $v_ ) /
118             ( $vol * $vol );
119              
120 13         17 my $d_ = ( log( $U / $S ) - $vol * $theta_ * $t ) / ( $vol * $sqrt_t );
121              
122 13         14 my ( $part1, $part2, $subpart_1_1, $subpart_1_2, $subpart_2_1,
123             $subpart_2_2 );
124              
125 13         58 $subpart_1_1 =
126             pnorm( -$eta * $e ) * $A * ( -$vol - ( $theta_ + $v_ ) * log( $U / $S ) );
127 13         31 $subpart_1_2 =
128             $eta *
129             dgauss($e) /
130             $sqrt_t *
131             ( $d_ * $pa_e + $A * log( $U / $S ) - 1.0 / $vol );
132              
133 13         32 $subpart_2_1 =
134             pnorm( $eta * $e_ ) *
135             $A_ *
136             ( -$vol - ( $theta_ - $v_ ) * log( $U / $S ) );
137 13         18 $subpart_2_2 =
138             $eta *
139             dgauss($e_) /
140             $sqrt_t *
141             ( $d_ * $pa_e_ - $A_ * log( $U / $S ) + 1.0 / $vol );
142              
143 13         23 $part1 =
144             ( ( $U / $S )**( ( $theta_ + $v_ ) / $vol ) ) *
145             ( $subpart_1_1 - $subpart_1_2 );
146 13         20 $part2 =
147             ( ( $U / $S )**( ( $theta_ - $v_ ) / $vol ) ) *
148             ( $subpart_2_1 + $subpart_2_2 );
149              
150 13         33 return ( $part1 + $part2 ) * exp( -$w * $r_q * $t ) / ( $vol * $S );
151             }
152              
153             sub notouch {
154 6     6 0 1910 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
155              
156             # No touch bet always pay out at end
157 6         6 $w = 1;
158              
159             # Since the value VALUE_NOTOUCH = D(T) - VALUE_ONETOUCH, where D(T)
160             # is the discount from time T, any derivative (other than with
161             # respect to time or discount rate) of the value of notouch
162             # is just the negative of the onetouch derivative.
163 6         14 return ( -1 * onetouch( $S, $U, $t, $r_q, $mu, $vol, $w ) );
164             }
165              
166             sub upordown {
167 13     13 0 2791 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
168              
169             # $w = 0, paid at hit
170             # $w = 1, paid at end
171 13 100       38 if ( not defined $w ) { $w = 0; }
  7         43  
172              
173 13         53 return ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) +
174             ot_down_ko_up_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
175             }
176              
177             sub xw_common_function_pelsser_1997 {
178 26     26 0 60 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta ) = @_;
179              
180 26         36 my $pi = Math::Trig::pi;
181              
182 26         49 my $h = log( $U / $D );
183 26         34 my $x = log( $S / $D );
184              
185             # $eta = 1, onetouch up knockout down
186             # $eta = 0, onetouch down knockout up
187             # This variable used to check stability
188 26 50       54 if ( not defined $eta ) {
189 0         0 die
190             "$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.";
191             }
192 26 100       51 if ( $eta == 0 ) { $x = $h - $x; }
  13         22  
193              
194 26         41 my $r_dash = $r_q * ( 1 - $w );
195 26         38 my $mu_new = $mu - ( 0.5 * $vol * $vol );
196 26         80 my $mu_dash = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $mu_new * $mu_new ) + ( 2 * $vol * $vol * $r_dash ) ) );
197              
198 26         29 my $omega = ( $vol * $vol );
199              
200 26         29 my $series_part = 0;
201 26         26 my $hyp_part = 0;
202              
203 26         73 my $stability_constant =
204             Math::Business::BlackScholes::Binaries::get_stability_constant_pelsser_1997(
205             $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1 );
206              
207 26         345 my $iterations_required =
208             Math::Business::BlackScholes::Binaries::get_min_iterations_pelsser_1997(
209             $S, $U, $D, $t, $r_q, $mu, $vol, $w );
210              
211 26         1037 for ( my $k = 1 ; $k < $iterations_required ; $k++ ) {
212 570         801 my $lambda_k_dash = (
213             0.5 * (
214             ( $mu_dash * $mu_dash ) / ( $vol * $vol ) +
215             ( $k * $k * $pi * $pi * $vol * $vol ) / ( $h * $h )
216             )
217             );
218              
219             # d{lambda_k}/dw
220 570         824 my $dlambdak_domega =
221             0.5 *
222             ( -( $mu_new / $omega ) -
223             ( ( $mu_new * $mu_new ) / ( $omega * $omega ) ) +
224             ( ( $k * $k * $pi * $pi ) / ( $h * $h ) ) );
225              
226 570         571 my $beta_k = exp( -$lambda_k_dash * $t ) / $lambda_k_dash;
227              
228             # d{beta_k}/d{lambda_k}
229 570         779 my $dbetak_dlambdak =
230             -exp( -$lambda_k_dash * $t ) *
231             ( ( $t * $lambda_k_dash + 1 ) / ( $lambda_k_dash**2 ) );
232              
233             # d{beta_k}/dw
234 570         470 my $dbetak_domega = $dlambdak_domega * $dbetak_dlambdak;
235              
236 570         745 my $phi =
237             ( 1.0 / ( $h * $h * $h ) ) *
238             ( $omega * $dbetak_domega + $beta_k ) *
239             $k *
240             $k;
241              
242 570         731 $series_part += $phi * $pi * $pi * cos( $k * $pi * ( $h - $x ) / $h );
243              
244 570 50 66     1522 if ( $k == 1
245             and ( not( abs( 2 * $vol * $phi / $S ) < $stability_constant ) ) )
246             {
247 0         0 die
248             "$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.";
249             }
250             }
251              
252 26         42 my $alpha = $mu_dash / ( $vol * $vol );
253 26         80 my $dalpha_domega =
254             -( ( $mu_new * $omega ) +
255             ( 2 * $mu_new * $mu_new ) +
256             ( 2 * $r_dash * $omega ) ) /
257             ( 2 * $alpha * $omega * $omega * $omega );
258              
259             # We have to handle the special case where the denominator approaches 0, see our documentation in
260             # quant/Documents/Breakout_bet.tex under the SVN "quant" module.
261 26 50       82 if ( ( Math::Trig::sinh( $alpha * $h )**2 ) == 0 ) {
262 0         0 $hyp_part = 0;
263             }
264             else {
265 26         292 $hyp_part =
266             -( $dalpha_domega * $alpha ) *
267             ( ( ( $h + $x ) * Math::Trig::cosh( $alpha * ( $h - $x ) ) ) +
268             ( ( $h - $x ) * Math::Trig::cosh( $alpha * ( $h + $x ) ) ) ) /
269             ( 2 *
270             Math::Trig::sinh( $alpha * $h ) *
271             Math::Trig::sinh( $alpha * $h ) ) +
272             $dalpha_domega *
273             ( Math::Trig::sinh( $alpha * ( $h - $x ) ) +
274             Math::Trig::sinh( $alpha * ( $h + $x ) ) ) /
275             ( 2 *
276             Math::Trig::sinh( $alpha * $h ) *
277             Math::Trig::sinh( $alpha * $h ) );
278             }
279              
280 26         1024 my $d2c_domegadx = ( $hyp_part + $series_part ) * exp( -$r_q * $w * $t );
281              
282 26         51 return $d2c_domegadx;
283             }
284              
285             sub ot_up_ko_down_pelsser_1997 {
286 13     13 0 31 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
287              
288 13         38 my $mu_new = $mu - ( 0.5 * $vol * $vol );
289 13         36 my $h = log( $U / $D );
290 13         30 my $x = log( $S / $D );
291 13         16 my $omega = ( $vol * $vol );
292              
293 13         43 my $c =
294             Math::Business::BlackScholes::Binaries::common_function_pelsser_1997( $S,
295             $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
296 13         2124 my $dc_domega =
297             Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997(
298             $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
299 13         71 my $dc_dx =
300             Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997(
301             $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
302 13         43 my $d2c_domegadx =
303             xw_common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
304              
305 13         118 my $d2Vu_domegadx =
306             ( ( ( ( 0.5 * $omega ) + $mu_new ) / ( $omega * $omega ) ) *
307             ( 1 + ( $mu_new / $omega ) * ( $h - $x ) ) *
308             exp( ( $mu_new / $omega ) * ( $h - $x ) ) *
309             $c ) -
310             ( ( ( ( 0.5 * $omega ) + $mu_new ) / ( $omega * $omega ) ) *
311             ( $h - $x ) *
312             exp( ( $mu_new / $omega ) * ( $h - $x ) ) *
313             $dc_dx ) -
314             ( ( $mu_new / $omega ) *
315             exp( ( $mu_new / $omega ) * ( $h - $x ) ) *
316             $dc_domega ) +
317             ( exp( ( $mu_new / $omega ) * ( $h - $x ) ) * $d2c_domegadx );
318              
319 13         47 return ( 2 * $vol / $S ) * $d2Vu_domegadx;
320             }
321              
322             sub ot_down_ko_up_pelsser_1997 {
323 13     13 0 30 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
324              
325 13         39 my $mu_new = $mu - ( 0.5 * $vol * $vol );
326 13         20 my $h = log( $U / $D );
327 13         23 my $x = log( $S / $D );
328 13         25 my $omega = ( $vol * $vol );
329              
330 13         31 my $c =
331             Math::Business::BlackScholes::Binaries::common_function_pelsser_1997( $S,
332             $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
333 13         2022 my $dc_domega =
334             Math::Business::BlackScholes::Binaries::Greeks::Vega::w_common_function_pelsser_1997(
335             $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
336 13         43 my $dc_dx =
337             Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997(
338             $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
339 13         31 my $d2c_domegadx =
340             xw_common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
341              
342 13         97 my $d2Vl_domegadx =
343             ( ( ( ( 0.5 * $omega ) + $mu_new ) / ( $omega * $omega ) ) *
344             ( 1 - ( $mu_new / $omega ) * $x ) *
345             exp( -( $mu_new / $omega ) * $x ) *
346             $c ) -
347             ( ( ( ( 0.5 * $omega ) + $mu_new ) / ( $omega * $omega ) ) *
348             $x *
349             exp( -( $mu_new / $omega ) * $x ) *
350             $dc_dx ) -
351             ( ( $mu_new / $omega ) * exp( -( $mu_new / $omega ) * $x ) * $dc_domega )
352             - ( exp( -( $mu_new / $omega ) * $x ) * $d2c_domegadx );
353              
354 13         46 return ( 2 * $vol / $S ) * $d2Vl_domegadx;
355             }
356              
357             sub range {
358 6     6 0 3157 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
359              
360             # Range always pay out at end
361 6         12 $w = 1;
362              
363 6         20 return -1 * upordown( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
364             }
365              
366             1;
367