File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Theta.pm
Criterion Covered Total %
statement 106 109 97.2
branch 13 16 81.2
condition 4 6 66.6
subroutine 20 20 100.0
pod 0 13 0.0
total 143 164 87.2


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Theta;
2 1     1   433 use strict; use warnings;
  1     1   2  
  1         28  
  1         3  
  1         0  
  1         32  
3              
4             our $VERSION = '0.04';
5              
6             =head1 NAME
7              
8             Math::Business::BlackScholes::Binaries::Greeks::Theta
9              
10             =head1 DESCRIPTION
11              
12             Gets the Theta 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   3 use List::Util qw(max);
  1         1  
  1         44  
23 1     1   3 use Math::Trig;
  1         1  
  1         116  
24 1     1   3 use Math::CDF qw(pnorm);
  1         3  
  1         24  
25 1     1   3 use Math::Business::BlackScholes::Binaries;
  1         1  
  1         13  
26 1     1   2 use Math::Business::BlackScholes::Binaries::Greeks::Math qw(dgauss);
  1         2  
  1         1032  
27              
28             sub vanilla_call {
29 6     6 0 1896 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
30              
31 6         26 my $d1 =
32             ( log( $S / $K ) + ($mu) * $t ) / ( $vol * sqrt($t) ) +
33             0.5 * $vol * sqrt($t);
34 6         7 my $d2 = $d1 - $vol * sqrt($t);
35              
36 6         17 my $theta =
37             -( $vol * $S * exp( ( $mu - $r_q ) * $t ) * dgauss($d1) ) /
38             ( 2 * sqrt($t) ) +
39             ( ( $r_q - $mu ) * $S * exp( ( $mu - $r_q ) * $t ) * pnorm($d1) ) -
40             ( $r_q * $K * exp( -$r_q * $t ) * pnorm($d2) );
41              
42 6         10 return $theta;
43             }
44              
45             sub vanilla_put {
46 6     6 0 1849 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
47              
48 6         17 my $d1 =
49             ( log( $S / $K ) + ($mu) * $t ) / ( $vol * sqrt($t) ) +
50             0.5 * $vol * sqrt($t);
51 6         7 my $d2 = $d1 - $vol * sqrt($t);
52              
53 6         20 my $theta =
54             -( $vol * $S * exp( ( $mu - $r_q ) * $t ) * dgauss( -$d1 ) ) /
55             ( 2 * sqrt($t) ) -
56             ( ( $r_q - $mu ) * $S * exp( ( $mu - $r_q ) * $t ) * pnorm( -$d1 ) ) +
57             ( $r_q * $K * exp( -$r_q * $t ) * pnorm( -$d2 ) );
58              
59 6         7 return $theta;
60             }
61              
62             sub call {
63 16     16 0 2046 my ( $S, $U, $t, $r_q, $mu, $vol ) = @_;
64              
65 16         93 my $d1 =
66             ( log( $S / $U ) + ($mu) * $t ) / ( $vol * sqrt($t) ) +
67             0.5 * $vol * sqrt($t);
68 16         22 my $d2 = $d1 - $vol * sqrt($t);
69              
70 16         157 my $theta =
71             $r_q * pnorm($d2) +
72             dgauss($d2) * $d1 / ( 2 * $t ) -
73             dgauss($d2) * ($mu) / ( $vol * sqrt($t) );
74              
75 16         48 return $theta * exp( -$r_q * $t );
76             }
77              
78             sub put {
79 16     16 0 2088 my ( $S, $D, $t, $r_q, $mu, $vol ) = @_;
80              
81 16         61 my $d1 =
82             ( log( $S / $D ) + ($mu) * $t ) / ( $vol * sqrt($t) ) +
83             0.5 * $vol * sqrt($t);
84 16         20 my $d2 = $d1 - $vol * sqrt($t);
85              
86 16         90 my $theta =
87             $r_q * pnorm( -$d2 ) -
88             dgauss($d2) * $d1 / ( 2 * $t ) +
89             dgauss($d2) * ($mu) / ( $vol * sqrt($t) );
90              
91 16         36 return $theta * exp( -$r_q * $t );
92             }
93              
94             sub expirymiss {
95 10     10 0 2477 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
96              
97 10         29 return call( $S, $U, $t, $r_q, $mu, $vol ) +
98             put( $S, $D, $t, $r_q, $mu, $vol );
99             }
100              
101             sub expiryrange {
102 5     5 0 1617 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
103              
104 5         23 return $r_q * exp( -$r_q * $t ) -
105             expirymiss( $S, $U, $D, $t, $r_q, $mu, $vol );
106             }
107              
108             sub onetouch {
109 13     13 0 2448 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
110 13 100       32 if ( not defined $w ) {
111 7         8 $w = 0;
112             }
113              
114 13         17 my $sqrt_t = sqrt($t);
115              
116 13         21 my $theta = ( ($mu) / $vol ) + ( 0.5 * $vol );
117 13         18 my $theta_ = ( ($mu) / $vol ) - ( 0.5 * $vol );
118              
119             # Floor v_ squared at zero in case negative interest rates push it negative.
120 13         41 my $v_ = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $theta_ * $theta_ ) + ( 2 * ( 1 - $w ) * $r_q ) ) );
121              
122 13         32 my $e = ( log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
123              
124 13 100       28 my $eta = ( $S > $U ) ? 1 : -1;
125              
126 13         47 my $part1 =
127             $w * $r_q *
128             Math::Business::BlackScholes::Binaries::onetouch( $S, $U, $t, $r_q, $mu,
129             $vol, $w );
130 13         315 my $part2 =
131             $eta *
132             exp( -$w * $r_q * $t ) /
133             ( $vol * ( $t**1.5 ) ) *
134             ( ( $U / $S )**( ( $theta_ + $v_ ) / $vol ) ) *
135             dgauss($e) *
136             log( $U / $S );
137              
138 13         14 my $theta_onetouch = $part1 + $part2;
139              
140 13         27 return $theta_onetouch;
141             }
142              
143             sub notouch {
144 6     6 0 1857 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
145              
146             # No touch bet always pay out at end
147 6         6 $w = 1;
148              
149 6         17 return $r_q * exp( -$r_q * $t ) -
150             onetouch( $S, $U, $t, $r_q, $mu, $vol, $w );
151             }
152              
153             sub upordown {
154 13     13 0 3875 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
155              
156 13 100 66     86 if ( ( $S >= $U ) || ( $S <= $D ) ) { return 0; }
  3         9  
157              
158             # $w = 0, paid at hit
159             # $w = 1, paid at end
160 10 100       56 if ( not defined $w ) { $w = 0; }
  5         10  
161              
162 10         33 return ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) +
163             ot_down_ko_up_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
164             }
165              
166             sub common_function_pelsser_1997 {
167 20     20 0 38 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta ) = @_;
168              
169 20         30 my $pi = Math::Trig::pi;
170              
171 20         25 my $h = log( $U / $D );
172 20         32 my $x = log( $S / $D );
173              
174             # $eta = 1, onetouch up knockout down
175             # $eta = 0, onetouch down knockout up
176             # This variable used to check stability
177 20 50       43 if ( not defined $eta ) {
178 0         0 die
179             "$0: (common_function_pelsser_1997) Wrong usage of this function for S=$S, U=$U, D=$D, t=$t, r=$r_q, mu=$mu, vol=$vol, w=$w. eta not defined.";
180             }
181 20 100       46 if ( $eta == 0 ) { $x = $h - $x; }
  10         14  
182              
183 20         31 my $mu_ = $mu - ( 0.5 * $vol * $vol );
184 20         81 my $mu_dash =
185             sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $mu_ * $mu_ ) + ( 2 * $vol * $vol * $r_q * ( 1 - $w ) ) ) );
186              
187 20         22 my $hyp_part = 0;
188 20         20 my $series_part = 0;
189              
190 20         60 my $stability_constant =
191             Math::Business::BlackScholes::Binaries::get_stability_constant_pelsser_1997(
192             $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1 );
193              
194 20         275 my $iterations_required =
195             Math::Business::BlackScholes::Binaries::get_min_iterations_pelsser_1997(
196             $S, $U, $D, $t, $r_q, $mu, $vol, $w );
197              
198 20         830 for ( my $k = 1 ; $k < $iterations_required ; $k++ ) {
199 480         670 my $lambda_k_dash = (
200             0.5 * (
201             ( $mu_dash * $mu_dash ) / ( $vol * $vol ) +
202             ( $k * $k * $pi * $pi * $vol * $vol ) / ( $h * $h )
203             )
204             );
205              
206 480         783 my $phi =
207             ( $vol * $vol ) / ( $h * $h ) * ( 1 + ( $r_q * $w / $lambda_k_dash ) )
208             * exp( -( $r_q * $w + $lambda_k_dash ) * $t )
209             * $k;
210              
211 480         569 $series_part += $phi * $pi * sin( $k * $pi * ( $h - $x ) / $h );
212              
213 480 50 66     1208 if ( $k == 1 and ( not( abs($phi) < $stability_constant ) ) ) {
214 0         0 die
215             "$0: PELSSER THETA formula for S=$S, U=$U, D=$D, t=$t, r=$r_q, mu=$mu, vol=$vol, w=$w, eta=$eta cannot be evaluated because PELSSER THETA stability conditions ($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.";
216             }
217             }
218              
219             # We have to handle the special case where the denominator approaches 0, see our documentation in
220             # quant/Documents/Breakout_bet.tex under the SVN "quant" module.
221 20 50       72 if ( ( Math::Trig::sinh( $mu_dash * $h / ( $vol * $vol ) ) ) == 0 ) {
222 0         0 $hyp_part = -( $r_q * $w ) * exp( -$r_q * $w * $t ) * ( $x / $h );
223             }
224             else {
225 20         221 $hyp_part =
226             -( $r_q * $w ) *
227             exp( -$r_q * $w * $t ) *
228             Math::Trig::sinh( $mu_dash * $x / ( $vol * $vol ) ) /
229             Math::Trig::sinh( $mu_dash * $h / ( $vol * $vol ) );
230             }
231              
232 20         227 my $dc_dT = ( $hyp_part + $series_part );
233              
234 20         30 return $dc_dT;
235             }
236              
237             sub ot_up_ko_down_pelsser_1997 {
238 10     10 0 21 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
239              
240 10         29 my $mu_ = $mu - ( 0.5 * $vol * $vol );
241 10         25 my $h = log( $U / $D );
242 10         16 my $x = log( $S / $D );
243              
244 10         29 my $dc_dT =
245             common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
246              
247 10         30 my $dVu_dT = -exp( ( $mu_ / ( $vol * $vol ) ) * ( $h - $x ) ) * $dc_dT;
248 10         36 return $dVu_dT;
249             }
250              
251             sub ot_down_ko_up_pelsser_1997 {
252 10     10 0 25 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
253              
254 10         23 my $mu_ = $mu - ( 0.5 * $vol * $vol );
255 10         19 my $h = log( $U / $D );
256 10         19 my $x = log( $S / $D );
257              
258 10         26 my $dc_dT =
259             common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
260              
261 10         36 my $dVl_dT = -exp( -( $mu_ / ( $vol * $vol ) ) * $x ) * $dc_dT;
262 10         30 return $dVl_dT;
263             }
264              
265             sub range {
266 6     6 0 3981 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
267              
268             # Range always pay out at end
269 6         10 $w = 1;
270              
271 6         34 return $r_q * exp( -$r_q * $t ) -
272             upordown( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
273             }
274              
275             1;
276