File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Theta.pm
Criterion Covered Total %
statement 104 107 97.2
branch 13 16 81.2
condition 4 6 66.6
subroutine 20 20 100.0
pod 0 13 0.0
total 141 162 87.0


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Theta;
2 1     1   462 use strict;
  1         2  
  1         37  
3 1     1   5 use warnings;
  1         6  
  1         46  
4              
5             our $VERSION = '0.06'; ## VERSION
6              
7             =head1 NAME
8              
9             Math::Business::BlackScholes::Binaries::Greeks::Theta
10              
11             =head1 DESCRIPTION
12              
13             Gets the Theta for different options, Vanilla and Foreign for all our bet types
14              
15             =cut
16              
17             =head1 SUBROUTINES
18              
19             See L
20              
21             =cut
22              
23 1     1   5 use List::Util qw(max);
  1         2  
  1         55  
24 1     1   7 use Math::Trig;
  1         2  
  1         173  
25 1     1   6 use Math::CDF qw(pnorm);
  1         2  
  1         36  
26 1     1   6 use Math::Business::BlackScholesMerton::Binaries;
  1         1  
  1         31  
27 1     1   6 use Math::Business::BlackScholes::Binaries::Greeks::Math qw(dgauss);
  1         1  
  1         1571  
28              
29             sub vanilla_call {
30 6     6 0 3742 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
31              
32 6         26 my $d1 = (log($S / $K) + ($mu) * $t) / ($vol * sqrt($t)) + 0.5 * $vol * sqrt($t);
33 6         11 my $d2 = $d1 - $vol * sqrt($t);
34              
35 6         24 my $theta =
36             -($vol * $S * exp(($mu - $r_q) * $t) * dgauss($d1)) / (2 * sqrt($t)) +
37             (($r_q - $mu) * $S * exp(($mu - $r_q) * $t) * pnorm($d1)) -
38             ($r_q * $K * exp(-$r_q * $t) * pnorm($d2));
39              
40 6         19 return $theta;
41             }
42              
43             sub vanilla_put {
44 6     6 0 3725 my ($S, $K, $t, $r_q, $mu, $vol) = @_;
45              
46 6         28 my $d1 = (log($S / $K) + ($mu) * $t) / ($vol * sqrt($t)) + 0.5 * $vol * sqrt($t);
47 6         14 my $d2 = $d1 - $vol * sqrt($t);
48              
49 6         22 my $theta =
50             -($vol * $S * exp(($mu - $r_q) * $t) * dgauss(-$d1)) / (2 * sqrt($t)) -
51             (($r_q - $mu) * $S * exp(($mu - $r_q) * $t) * pnorm(-$d1)) +
52             ($r_q * $K * exp(-$r_q * $t) * pnorm(-$d2));
53              
54 6         14 return $theta;
55             }
56              
57             sub call {
58 16     16 0 3955 my ($S, $U, $t, $r_q, $mu, $vol) = @_;
59              
60 16         81 my $d1 = (log($S / $U) + ($mu) * $t) / ($vol * sqrt($t)) + 0.5 * $vol * sqrt($t);
61 16         37 my $d2 = $d1 - $vol * sqrt($t);
62              
63 16         132 my $theta = $r_q * pnorm($d2) + dgauss($d2) * $d1 / (2 * $t) - dgauss($d2) * ($mu) / ($vol * sqrt($t));
64              
65 16         56 return $theta * exp(-$r_q * $t);
66             }
67              
68             sub put {
69 16     16 0 4076 my ($S, $D, $t, $r_q, $mu, $vol) = @_;
70              
71 16         62 my $d1 = (log($S / $D) + ($mu) * $t) / ($vol * sqrt($t)) + 0.5 * $vol * sqrt($t);
72 16         32 my $d2 = $d1 - $vol * sqrt($t);
73              
74 16         88 my $theta = $r_q * pnorm(-$d2) - dgauss($d2) * $d1 / (2 * $t) + dgauss($d2) * ($mu) / ($vol * sqrt($t));
75              
76 16         55 return $theta * exp(-$r_q * $t);
77             }
78              
79             sub expirymiss {
80 10     10 0 4469 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
81              
82 10         34 return call($S, $U, $t, $r_q, $mu, $vol) + put($S, $D, $t, $r_q, $mu, $vol);
83             }
84              
85             sub expiryrange {
86 5     5 0 3517 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
87              
88 5         25 return $r_q * exp(-$r_q * $t) - expirymiss($S, $U, $D, $t, $r_q, $mu, $vol);
89             }
90              
91             sub onetouch {
92 13     13 0 4428 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
93 13 100       43 if (not defined $w) {
94 7         12 $w = 0;
95             }
96              
97 13         22 my $sqrt_t = sqrt($t);
98              
99 13         33 my $theta_ = (($mu) / $vol) - (0.5 * $vol);
100              
101             # Floor v_ squared at zero in case negative interest rates push it negative.
102 13         52 my $v_ = sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($theta_ * $theta_) + (2 * (1 - $w) * $r_q)));
103              
104 13         47 my $e = (log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
105              
106 13 100       40 my $eta = ($S > $U) ? 1 : -1;
107              
108 13         61 my $part1 = $w * $r_q * Math::Business::BlackScholesMerton::Binaries::onetouch($S, $U, $t, $r_q, $mu, $vol, $w);
109 13         421 my $part2 = $eta * exp(-$w * $r_q * $t) / ($vol * ($t**1.5)) * (($U / $S)**(($theta_ + $v_) / $vol)) * dgauss($e) * log($U / $S);
110              
111 13         23 my $theta_onetouch = $part1 + $part2;
112              
113 13         32 return $theta_onetouch;
114             }
115              
116             sub notouch {
117 6     6 0 4437 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
118              
119             # No touch bet always pay out at end
120 6         26 $w = 1;
121              
122 6         33 return $r_q * exp(-$r_q * $t) - onetouch($S, $U, $t, $r_q, $mu, $vol, $w);
123             }
124              
125             sub upordown {
126 13     13 0 4919 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
127              
128 13 100 66     77 if (($S >= $U) || ($S <= $D)) { return 0; }
  3         12  
129              
130             # $w = 0, paid at hit
131             # $w = 1, paid at end
132 10 100       36 if (not defined $w) { $w = 0; }
  5         9  
133              
134 10         38 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);
135             }
136              
137             sub common_function_pelsser_1997 {
138 20     20 0 55 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta) = @_;
139              
140 20         29 my $pi = Math::Trig::pi;
141              
142 20         33 my $h = log($U / $D);
143 20         29 my $x = log($S / $D);
144              
145             # $eta = 1, onetouch up knockout down
146             # $eta = 0, onetouch down knockout up
147             # This variable used to check stability
148 20 50       45 if (not defined $eta) {
149 0         0 die
150             "$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.";
151             }
152 20 100       39 if ($eta == 0) { $x = $h - $x; }
  10         17  
153              
154 20         39 my $mu_ = $mu - (0.5 * $vol * $vol);
155 20         76 my $mu_dash =
156             sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($mu_ * $mu_) + (2 * $vol * $vol * $r_q * (1 - $w))));
157              
158 20         37 my $hyp_part = 0;
159 20         29 my $series_part = 0;
160              
161 20         65 my $stability_constant =
162             Math::Business::BlackScholesMerton::Binaries::get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 1);
163              
164 20         434 my $iterations_required = Math::Business::BlackScholesMerton::Binaries::get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
165              
166 20         1560 for (my $k = 1; $k < $iterations_required; $k++) {
167 480         861 my $lambda_k_dash = (0.5 * (($mu_dash * $mu_dash) / ($vol * $vol) + ($k * $k * $pi * $pi * $vol * $vol) / ($h * $h)));
168              
169 480         951 my $phi = ($vol * $vol) / ($h * $h) * (1 + ($r_q * $w / $lambda_k_dash)) * exp(-($r_q * $w + $lambda_k_dash) * $t) * $k;
170              
171 480         765 $series_part += $phi * $pi * sin($k * $pi * ($h - $x) / $h);
172              
173 480 50 66     1148 if ($k == 1 and (not(abs($phi) < $stability_constant))) {
174 0         0 die
175             "$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.";
176             }
177             }
178              
179             # We have to handle the special case where the denominator approaches 0, see our documentation in
180             # quant/Documents/Breakout_bet.tex under the SVN "quant" module.
181 20 50       83 if ((Math::Trig::sinh($mu_dash * $h / ($vol * $vol))) == 0) {
182 0         0 $hyp_part = -($r_q * $w) * exp(-$r_q * $w * $t) * ($x / $h);
183             } else {
184 20         284 $hyp_part =
185             -($r_q * $w) * exp(-$r_q * $w * $t) * Math::Trig::sinh($mu_dash * $x / ($vol * $vol)) / Math::Trig::sinh($mu_dash * $h / ($vol * $vol));
186             }
187              
188 20         362 my $dc_dT = ($hyp_part + $series_part);
189              
190 20         36 return $dc_dT;
191             }
192              
193             sub ot_up_ko_down_pelsser_1997 {
194 10     10 0 33 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
195              
196 10         24 my $mu_ = $mu - (0.5 * $vol * $vol);
197 10         25 my $h = log($U / $D);
198 10         21 my $x = log($S / $D);
199              
200 10         31 my $dc_dT = common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
201              
202 10         28 my $dVu_dT = -exp(($mu_ / ($vol * $vol)) * ($h - $x)) * $dc_dT;
203 10         37 return $dVu_dT;
204             }
205              
206             sub ot_down_ko_up_pelsser_1997 {
207 10     10 0 31 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
208              
209 10         22 my $mu_ = $mu - (0.5 * $vol * $vol);
210 10         20 my $x = log($S / $D);
211              
212 10         21 my $dc_dT = common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
213              
214 10         27 my $dVl_dT = -exp(-($mu_ / ($vol * $vol)) * $x) * $dc_dT;
215 10         29 return $dVl_dT;
216             }
217              
218             sub range {
219 6     6 0 4185 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
220              
221             # Range always pay out at end
222 6         14 $w = 1;
223              
224 6         30 return $r_q * exp(-$r_q * $t) - upordown($S, $U, $D, $t, $r_q, $mu, $vol, $w);
225             }
226              
227             1;
228