File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Vega.pm
Criterion Covered Total %
statement 121 124 97.5
branch 11 14 78.5
condition 2 3 66.6
subroutine 20 20 100.0
pod 0 13 0.0
total 154 174 88.5


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Vega;
2 1     1   3 use strict; use warnings;
  1     1   1  
  1         22  
  1         3  
  1         0  
  1         29  
3              
4             our $VERSION = '0.04';
5              
6             =head1 NAME
7              
8             Math::Business::BlackScholes::Binaries::Greeks::Vega
9              
10             =head1 DESCRIPTION
11              
12             Gets the Vega for different options, Vanilla and Foreign for all our bet types
13              
14             =cut
15              
16             =head1 SUBROUTINES
17              
18             See L
19              
20             =cut
21              
22 1     1   2 use List::Util qw( max );
  1         1  
  1         40  
23 1     1   10 use Math::CDF qw( pnorm );
  1         1  
  1         24  
24 1     1   2 use Math::Trig;
  1         2  
  1         114  
25 1     1   5 use Math::Business::BlackScholes::Binaries;
  1         0  
  1         16  
26 1     1   4 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( dgauss );
  1         0  
  1         1395  
27              
28             sub vanilla_call {
29 36     36 0 2050 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
30              
31 36         69 my $d1 =
32             ( log( $S / $K ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
33             ( $vol * sqrt($t) );
34 36         84 my $vega = $S * sqrt($t) * exp( ( $mu - $r_q ) * $t ) * dgauss($d1);
35 36         45 return $vega;
36             }
37              
38             sub vanilla_put {
39 6     6 0 1883 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
40              
41             # Same as vega of vanilla call
42 6         9 return vanilla_call( $S, $K, $t, $r_q, $mu, $vol );
43             }
44              
45             sub call {
46 16     16 0 2051 my ( $S, $U, $t, $r_q, $mu, $vol ) = @_;
47              
48 16         73 my $d1 =
49             ( log( $S / $U ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
50             ( $vol * sqrt($t) );
51 16         23 my $d2 = $d1 - $vol * sqrt($t);
52 16         49 my $vega = -exp( -$r_q * $t ) * dgauss($d2) * $d1 / $vol;
53 16         38 return $vega;
54             }
55              
56             sub put {
57 16     16 0 2003 my ( $S, $D, $t, $r_q, $mu, $vol ) = @_;
58              
59 16         59 my $d1 =
60             ( log( $S / $D ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
61             ( $vol * sqrt($t) );
62 16         24 my $d2 = $d1 - $vol * sqrt($t);
63 16         42 my $vega = exp( -$r_q * $t ) * dgauss($d2) * $d1 / $vol;
64 16         25 return $vega;
65             }
66              
67             sub expirymiss {
68 10     10 0 2244 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
69              
70 10         29 return call( $S, $U, $t, $r_q, $mu, $vol ) +
71             put( $S, $D, $t, $r_q, $mu, $vol );
72             }
73              
74             sub expiryrange {
75 5     5 0 1614 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
76              
77 5         14 return -1 * expirymiss( $S, $U, $D, $t, $r_q, $mu, $vol );
78             }
79              
80             sub onetouch {
81 13     13 0 2367 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
82              
83 13 100       36 if ( not defined $w ) {
84 7         9 $w = 0;
85             }
86              
87 13         17 my $sqrt_t = sqrt($t);
88              
89 13         28 my $theta = ( $mu / $vol ) + ( 0.5 * $vol );
90              
91 13         17 my $theta_ = ( $mu / $vol ) - ( 0.5 * $vol );
92              
93             # Floor v_ squared at just above zero in case negative interest rates push it negative.
94 13         52 my $v_ = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $theta_ * $theta_ ) + ( 2 * ( 1 - $w ) * $r_q ) ) );
95              
96 13         37 my $e = ( log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
97              
98 13         25 my $e_ = ( -log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
99              
100 13 100       32 my $eta = ( $S > $U ) ? 1 : -1;
101              
102 13         36 my $pa_e =
103             ( log( $U / $S ) / ( $vol * $vol * $sqrt_t ) ) +
104             ( ( $theta_ * $theta ) / ( $vol * $v_ ) * $sqrt_t );
105 13         27 my $pa_e_ =
106             ( -log( $U / $S ) / ( $vol * $vol * $sqrt_t ) ) +
107             ( ( $theta_ * $theta ) / ( $vol * $v_ ) * $sqrt_t );
108 13         24 my $A =
109             -( $theta + $theta_ + ( $theta_ * $theta / $v_ ) + $v_ ) /
110             ( $vol * $vol );
111 13         24 my $A_ =
112             -( $theta + $theta_ - ( $theta_ * $theta / $v_ ) - $v_ ) /
113             ( $vol * $vol );
114              
115 13         78 my $part1 =
116             pnorm( -$eta * $e ) * $A * log( $U / $S ) - $eta * dgauss($e) * $pa_e;
117 13         52 my $part2 =
118             pnorm( $eta * $e_ ) * $A_ * log( $U / $S ) + $eta * dgauss($e_) * $pa_e_;
119 13         51 my $vega =
120             ( ( $U / $S )**( ( $theta_ + $v_ ) / $vol ) ) * $part1 +
121             ( ( $U / $S )**( ( $theta_ - $v_ ) / $vol ) ) * $part2;
122              
123 13         31 return $vega * exp( -$w * $r_q * $t );
124             }
125              
126             sub notouch {
127 6     6 0 1950 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
128              
129             # No touch bet always pay out at end
130 6         7 $w = 1;
131              
132 6         16 return -1 * onetouch( $S, $U, $t, $r_q, $mu, $vol, $w );
133             }
134              
135             sub upordown {
136 13     13 0 4197 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       43 if ( not defined $w ) { $w = 0; }
  7         12  
141              
142 13         44 return ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) +
143             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 78     78 0 146 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta ) = @_;
148              
149 78         79 my $pi = Math::Trig::pi;
150              
151 78         100 my $h = log( $U / $D );
152 78         99 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 78 50       178 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 78 100       142 if ( $eta == 0 ) { $x = $h - $x; }
  39         40  
162              
163 78         104 my $r_dash = $r_q * ( 1 - $w );
164 78         104 my $mu_new = $mu - ( 0.5 * $vol * $vol );
165 78         201 my $mu_dash = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $mu_new * $mu_new ) + ( 2 * $vol * $vol * $r_dash ) ) );
166              
167 78         90 my $omega = ( $vol * $vol );
168              
169 78         69 my $series_part = 0;
170 78         72 my $hyp_part = 0;
171              
172 78         152 my $stability_constant =
173             Math::Business::BlackScholes::Binaries::get_stability_constant_pelsser_1997(
174             $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1 );
175              
176 78         965 my $iterations_required =
177             Math::Business::BlackScholes::Binaries::get_min_iterations_pelsser_1997(
178             $S, $U, $D, $t, $r_q, $mu, $vol, $w );
179              
180 78         3142 for ( my $k = 1 ; $k < $iterations_required ; $k++ ) {
181 1710         2226 my $lambda_k_dash = (
182             0.5 * (
183             ( $mu_dash * $mu_dash ) / $omega +
184             ( $k * $k * $pi * $pi * $vol * $vol ) / ( $h * $h )
185             )
186             );
187              
188             # d{lambda_k}/dw
189 1710         2223 my $dlambdak_domega =
190             0.5 *
191             ( -( $mu_new / $omega ) -
192             ( ( $mu_new * $mu_new ) / ( $omega * $omega ) ) +
193             ( ( $k * $k * $pi * $pi ) / ( $h * $h ) ) );
194              
195 1710         1721 my $beta_k = exp( -$lambda_k_dash * $t ) / $lambda_k_dash;
196              
197             # d{beta_k}/d{lambda_k}
198 1710         2081 my $dbetak_dlambdak =
199             -exp( -$lambda_k_dash * $t ) *
200             ( ( $t * $lambda_k_dash + 1 ) / ( $lambda_k_dash**2 ) );
201              
202             # d{beta_k}/dw
203 1710         1285 my $dbetak_domega = $dlambdak_domega * $dbetak_dlambdak;
204              
205 1710         1990 my $phi =
206             ( 1.0 / ( $h * $h ) ) * ( $omega * $dbetak_domega + $beta_k ) * $k;
207              
208 1710         2026 $series_part += $phi * $pi * sin( $k * $pi * ( $h - $x ) / $h );
209              
210             #
211             # For vega, the stability function is 2* $vol * $phi, for volga/vanna it is different,
212             # but we shall ignore for now.
213             #
214 1710 50 66     4506 if ( $k == 1
215             and ( not( abs( 2 * $vol * $phi ) < $stability_constant ) ) )
216             {
217 0         0 die
218             "$0: PELSSER VEGA 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 VEGA stability conditions (2 * $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.";
219             }
220             }
221              
222 78         110 my $alpha = $mu_dash / ( $vol * $vol );
223 78         157 my $dalpha_domega =
224             -( ( $mu_new * $omega ) +
225             ( 2 * $mu_new * $mu_new ) +
226             ( 2 * $r_dash * $omega ) ) /
227             ( 2 * $alpha * $omega * $omega * $omega );
228              
229             # We have to handle the special case where the denominator approaches 0, see our documentation in
230             # quant/Documents/Breakout_bet.tex under the SVN "quant" module.
231 78 50       231 if ( ( Math::Trig::sinh( $alpha * $h )**2 ) == 0 ) {
232 0         0 $hyp_part = 0;
233             }
234             else {
235 78         737 $hyp_part =
236             ( $dalpha_domega / ( 2 * ( Math::Trig::sinh( $alpha * $h )**2 ) ) ) *
237             ( ( $h + $x ) * Math::Trig::sinh( $alpha * ( $h - $x ) ) -
238             ( $h - $x ) * Math::Trig::sinh( $alpha * ( $h + $x ) ) );
239             }
240              
241 78         1267 my $dc_domega = ( $hyp_part - $series_part ) * exp( -$r_q * $w * $t );
242              
243 78         165 return $dc_domega;
244             }
245              
246             sub ot_up_ko_down_pelsser_1997 {
247 13     13 0 48 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
248              
249 13         34 my $mu_new = $mu - ( 0.5 * $vol * $vol );
250 13         38 my $h = log( $U / $D );
251 13         20 my $x = log( $S / $D );
252 13         18 my $omega = ( $vol * $vol );
253              
254 13         49 my $c =
255             Math::Business::BlackScholes::Binaries::common_function_pelsser_1997( $S,
256             $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
257 13         2134 my $dc_domega =
258             w_common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
259              
260 13         53 my $dVu_domega =
261             -( ( 0.5 * $omega + $mu_new ) * ( $h - $x ) / ( $omega * $omega ) ) * $c;
262 13         20 $dVu_domega += $dc_domega;
263 13         28 $dVu_domega *= exp( $mu_new * ( $h - $x ) / $omega );
264              
265 13         53 return $dVu_domega * ( 2 * $vol );
266             }
267              
268             sub ot_down_ko_up_pelsser_1997 {
269 13     13 0 34 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
270              
271 13         34 my $mu_new = $mu - ( 0.5 * $vol * $vol );
272 13         27 my $h = log( $U / $D );
273 13         19 my $x = log( $S / $D );
274 13         29 my $omega = ( $vol * $vol );
275              
276 13         37 my $c =
277             Math::Business::BlackScholes::Binaries::common_function_pelsser_1997( $S,
278             $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
279 13         1991 my $dc_domega =
280             w_common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
281              
282 13         36 my $dVl_domega =
283             ( ( 0.5 * $omega + $mu_new ) * $x / ( $omega * $omega ) ) * $c;
284 13         14 $dVl_domega += $dc_domega;
285 13         23 $dVl_domega *= exp( -$mu_new * $x / $omega );
286              
287 13         51 return $dVl_domega * ( 2 * $vol );
288             }
289              
290             sub range {
291 6     6 0 3586 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
292              
293             # Range always pay out at end
294 6         8 $w = 1;
295              
296 6         21 return -1 * upordown( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
297             }
298              
299             1;
300