File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Gamma.pm
Criterion Covered Total %
statement 120 125 96.0
branch 11 16 68.7
condition 2 3 66.6
subroutine 20 20 100.0
pod 0 13 0.0
total 153 177 86.4


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Gamma;
2 1     1   459 use strict;
  1         2  
  1         29  
3 1     1   4 use warnings;
  1         2  
  1         46  
4              
5             our $VERSION = '0.06'; ## VERSION
6              
7             =head1 NAME
8              
9             Math::Business::BlackScholes::Binaries::Greeks::Gamma
10              
11             =head1 DESCRIPTION
12              
13             Gets the gamma 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         63  
24 1     1   6 use Math::CDF qw( pnorm );
  1         2  
  1         46  
25 1     1   6 use Math::Trig;
  1         2  
  1         175  
26 1     1   11 use Math::Business::BlackScholesMerton::Binaries;
  1         3  
  1         42  
27 1     1   7 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( ddgauss dgauss );
  1         2  
  1         1866  
28              
29             sub vanilla_call {
30 12     12 0 3934 my ($S0, $Strike, $t, $r_q, $mu, $vol) = @_;
31              
32 12         54 my $d1 = (log($S0 / $Strike) + ($mu + (($vol**2) / 2)) * $t) / ($vol * sqrt($t));
33              
34 12         37 my $gamma =
35             dgauss($d1) * exp(($mu - $r_q) * $t) / ($S0 * $vol * sqrt($t));
36              
37 12         28 return $gamma;
38             }
39              
40             sub vanilla_put {
41 6     6 0 3835 return vanilla_call(@_);
42             }
43              
44             sub call {
45 16     16 0 4012 my ($S, $U, $t, $r_q, $mu, $vol) = @_;
46              
47 16         59 my $v = $mu - ($vol**2) / 2;
48 16         44 my $log_value = log($U / $S);
49              
50 16         30 my $da = -1 / $S;
51 16         31 my $dda = 1 / ($S * $S);
52              
53 16         41 my $q = ($log_value - $v * $t) / ($vol * sqrt($t));
54 16         30 my $dq = $da / ($vol * sqrt($t));
55 16         29 my $ddq = $dda / ($vol * sqrt($t));
56              
57 16         65 my $gamma =
58             -exp(-$r_q * $t) * (ddgauss($q) * $dq * $dq + dgauss($q) * $ddq);
59              
60 16         49 return $gamma;
61             }
62              
63             sub put {
64 16     16 0 4211 my ($S, $D, $t, $r_q, $mu, $vol) = @_;
65              
66 16         45 my $v = $mu - ($vol**2) / 2;
67 16         38 my $log_value = log($D / $S);
68 16         32 my $db = -1 / $S;
69 16         31 my $ddb = 1 / ($S * $S);
70              
71 16         33 my $q = ($log_value - $v * $t) / ($vol * sqrt($t));
72 16         35 my $dq = $db / ($vol * sqrt($t));
73 16         33 my $ddq = $ddb / ($vol * sqrt($t));
74              
75 16         51 my $gamma =
76             exp(-$r_q * $t) * (ddgauss($q) * $dq * $dq + dgauss($q) * $ddq);
77              
78 16         49 return $gamma;
79             }
80              
81             sub expirymiss {
82 10     10 0 4308 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
83              
84 10         35 return call($S, $U, $t, $r_q, $mu, $vol) + put($S, $D, $t, $r_q, $mu, $vol);
85             }
86              
87             sub expiryrange {
88 5     5 0 3336 my ($S, $U, $D, $t, $r_q, $mu, $vol) = @_;
89              
90 5         18 return -1 * expirymiss($S, $U, $D, $t, $r_q, $mu, $vol);
91             }
92              
93             sub onetouch {
94 13     13 0 4597 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
95 13 100       35 if (not defined $w) {
96 7         12 $w = 0;
97             }
98              
99 13         32 my $sqrt_t = sqrt($t);
100              
101 13         38 my $theta_ = (($mu) / $vol) - (0.5 * $vol);
102              
103             # Floor v_ squared near zero in case negative interest rates push it negative.
104 13         54 my $v_ = sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($theta_ * $theta_) + (2 * (1 - $w) * $r_q)));
105              
106 13         52 my $e = (log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
107              
108 13         31 my $e_ = (-log($S / $U) - ($vol * $v_ * $t)) / ($vol * $sqrt_t);
109              
110 13 100       39 my $eta = ($S > $U) ? 1 : -1;
111              
112 13         92 my $part1 = (($U / $S)**(($theta_ + $v_) / $vol)) * pnorm(-$eta * $e) * ($r_q * (1 - $w) + ($mu) * ($theta_ + $v_) / $vol);
113 13         56 my $part2 = (($U / $S)**(($theta_ - $v_) / $vol)) * pnorm($eta * $e_) * ($r_q * (1 - $w) + ($mu) * ($theta_ - $v_) / $vol);
114 13         52 my $part3 = $eta * (($U / $S)**(($theta_ + $v_) / $vol)) * dgauss($e) * (-$e_ * 0.5 / $t + ($mu) / ($vol * $sqrt_t));
115 13         38 my $part4 = $eta * (($U / $S)**(($theta_ - $v_) / $vol)) * dgauss($e_) * ($e * 0.5 / $t + ($mu) / ($vol * $sqrt_t));
116              
117 13         20 my $gamma = $part1 + $part2 + $part3 + $part4;
118 13         43 return $gamma * 2 * exp(-$w * $r_q * $t) / ($vol * $vol * $S * $S);
119             }
120              
121             sub notouch {
122 6     6 0 4033 my ($S, $U, $t, $r_q, $mu, $vol, $w) = @_;
123              
124             # No touch bet always pay out at end
125 6         11 $w = 1;
126              
127 6         18 return -1 * onetouch($S, $U, $t, $r_q, $mu, $vol, $w);
128             }
129              
130             sub upordown {
131 13     13 0 4881 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
132              
133             # $w = 0, paid at hit
134             # $w = 1, paid at end
135 13 100       46 if (not defined $w) { $w = 0; }
  7         15  
136              
137 13         47 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);
138             }
139              
140             sub xx_common_function_pelsser_1997 {
141 26     26 0 74 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta) = @_;
142              
143 26         46 my $pi = Math::Trig::pi;
144              
145 26         51 my $h = log($U / $D);
146 26         46 my $x = log($S / $D);
147              
148             # $eta = 1, onetouch up knockout down
149             # $eta = 0, onetouch down knockout up
150             # This variable used to check stability
151 26 50       77 if (not defined $eta) {
152 0         0 die
153             "$0: (xx_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.";
154             }
155 26 100       60 if ($eta == 0) { $x = $h - $x; }
  13         19  
156              
157 26         52 my $mu_ = $mu - (0.5 * $vol * $vol);
158 26         98 my $mu_dash =
159             sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($mu_ * $mu_) + (2 * $vol * $vol * $r_q * (1 - $w))));
160              
161 26         49 my $series_part = 0;
162 26         40 my $hyp_part = 0;
163              
164 26         75 my $stability_constant =
165             Math::Business::BlackScholesMerton::Binaries::get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 3);
166              
167 26         536 my $iterations_required = Math::Business::BlackScholesMerton::Binaries::get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w);
168              
169 26         1955 for (my $k = 1; $k < $iterations_required; $k++) {
170 570         1101 my $lambda_k_dash = (0.5 * (($mu_dash * $mu_dash) / ($vol * $vol) + ($k * $k * $pi * $pi * $vol * $vol) / ($h * $h)));
171              
172 570         1117 my $phi = ($vol * $vol) / ($h**4) * exp(-$lambda_k_dash * $t) * ($k**3) / $lambda_k_dash;
173              
174 570         1070 $series_part += $phi * ($pi**3) * sin($k * $pi * ($h - $x) / $h);
175              
176 570 50 66     1409 if ($k == 1
177             and (not(abs($phi / ($S**2)) < $stability_constant)))
178             {
179 0         0 die
180             "$0: PELSSER GAMMA 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 GAMMA stability conditions ($phi / ($S * $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.";
181             }
182             }
183              
184             # Need to take care when $mu goes to zero
185 26 50       65 if (abs($mu_) < $Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU) {
186 0 0       0 my $sign = ($mu_ >= 0) ? 1 : -1;
187 0         0 $mu_ = $sign * $Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU;
188 0         0 $mu_dash =
189             sqrt(max($Math::Business::BlackScholesMerton::Binaries::SMALL_VALUE_MU, ($mu_ * $mu_) + (2 * $vol * $vol * $r_q * (1 - $w))));
190             }
191              
192 26         110 $hyp_part = (($mu_dash**2) / ($vol**4)) * (Math::Trig::sinh($mu_dash * $x / ($vol * $vol)) / Math::Trig::sinh($mu_dash * $h / ($vol * $vol)));
193              
194 26         515 my $d2c_dwdx = ($hyp_part + $series_part) * exp(-$r_q * $t * $w);
195              
196 26         55 return $d2c_dwdx;
197             }
198              
199             sub ot_up_ko_down_pelsser_1997 {
200 13     13 0 44 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
201              
202 13         33 my $mu_ = $mu - (0.5 * $vol * $vol);
203 13         38 my $h = log($U / $D);
204 13         30 my $x = log($S / $D);
205              
206 13         52 my $c = Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
207 13         3954 my $dc_dx = Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
208 13         62 my $d2c_dx2 = xx_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
209              
210 13         46 my $dVu_dx =
211             -(
212             ($mu_ / ($vol * $vol)) * Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1));
213 13         3571 $dVu_dx += Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 1);
214 13         38 $dVu_dx *= exp($mu_ * ($h - $x) / ($vol * $vol));
215              
216 13         69 my $d2Vu_dx2 =
217             ((($mu_**2) / ($vol**4)) * exp(($mu_ / ($vol * $vol)) * ($h - $x)) * $c) -
218             (2 * ($mu_ / ($vol**2)) * exp(($mu_ / ($vol * $vol)) * ($h - $x)) * $dc_dx) +
219             (exp(($mu_ / ($vol**2)) * ($h - $x)) * $d2c_dx2);
220              
221 13         55 return (1 / ($S**2)) * ($d2Vu_dx2 - $dVu_dx);
222             }
223              
224             sub ot_down_ko_up_pelsser_1997 {
225 13     13 0 41 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
226              
227 13         29 my $mu_ = $mu - (0.5 * $vol * $vol);
228 13         25 my $x = log($S / $D);
229              
230 13         35 my $c = Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
231 13         3489 my $dc_dx = Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
232 13         41 my $d2c_dx2 = xx_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
233              
234 13         50 my $dVl_dx =
235             -(
236             ($mu_ / ($vol * $vol)) * Math::Business::BlackScholesMerton::Binaries::common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0));
237 13         3561 $dVl_dx -= Math::Business::BlackScholes::Binaries::Greeks::Delta::x_common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $vol, $w, 0);
238 13         45 $dVl_dx *= exp(-$mu_ * $x / ($vol * $vol));
239              
240 13         94 my $d2Vl_dx2 =
241             ((($mu_**2) / ($vol**4)) * exp(-($mu_ / ($vol * $vol)) * $x) * $c) +
242             (2 * ($mu_ / ($vol**2)) * exp(-($mu_ / ($vol * $vol)) * $x) * $dc_dx) +
243             (exp(-($mu_ / ($vol**2)) * $x) * $d2c_dx2);
244              
245 13         56 return (1 / ($S**2)) * ($d2Vl_dx2 - $dVl_dx);
246             }
247              
248             sub range {
249 6     6 0 4271 my ($S, $U, $D, $t, $r_q, $mu, $vol, $w) = @_;
250              
251             # Range always pay out at end
252 6         14 $w = 1;
253              
254 6         22 return -1 * upordown($S, $U, $D, $t, $r_q, $mu, $vol, $w);
255             }
256              
257             1;
258