File Coverage

lib/Math/Business/BlackScholes/Binaries/Greeks/Delta.pm
Criterion Covered Total %
statement 99 104 95.1
branch 11 16 68.7
condition 2 3 66.6
subroutine 20 20 100.0
pod 0 13 0.0
total 132 156 84.6


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries::Greeks::Delta;
2 1     1   22905 use strict; use warnings;
  1     1   1  
  1         27  
  1         3  
  1         1  
  1         38  
3              
4             our $VERSION = '0.04';
5              
6             =head1 NAME
7              
8             Math::Business::BlackScholes::Binaries::Greeks::Delta
9              
10             =head1 DESCRIPTION
11              
12             Gets the delta for different options, Vanilla and Foreign for all contract types
13              
14             =head1 COMMENTS
15              
16             It is tricky to decide what form to use. Should the delta be with respect to
17             1/$S, or with respect to $S? For the binary bets, whether foreign or domestic
18             we are differentiating with respect to $S.
19              
20             For a vanilla, the correct way should be with respect to 1/$S (so that we know
21             how many units of the domestic currency to hedge), but to keep things standard,
22             we do it with respect to $S.
23              
24             For example take USDJPY vanilla call with premium in USD. Thus this is a vanilla
25             contract on JPY. Thus delta with respect to 1/$S tells us how many units of JPY
26             to hedge, but with respect to $S, there really isn't a meaning and needs to be
27             converted back before interpretation.
28              
29             =cut
30              
31             =head1 SUBROUTINES
32              
33             See L
34              
35             =cut
36              
37 1     1   4 use List::Util qw(max);
  1         8  
  1         90  
38 1     1   419 use Math::CDF qw(pnorm);
  1         3830  
  1         60  
39 1     1   1681 use Math::Trig;
  1         11445  
  1         115  
40 1     1   517 use Math::Business::BlackScholes::Binaries;
  1         2885  
  1         26  
41 1     1   270 use Math::Business::BlackScholes::Binaries::Greeks::Math qw( dgauss );
  1         1  
  1         918  
42              
43             sub vanilla_call {
44 6     6 0 7571 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
45              
46 6         22 my $d1 =
47             ( log( $S / $K ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
48             ( $vol * sqrt($t) );
49              
50 6         34 return exp( ( $mu - $r_q ) * $t ) * pnorm($d1);
51             }
52              
53             sub vanilla_put {
54 6     6 0 7054 my ( $S, $K, $t, $r_q, $mu, $vol ) = @_;
55              
56 6         21 my $d1 =
57             ( log( $S / $K ) + ( $mu + $vol * $vol / 2.0 ) * $t ) /
58             ( $vol * sqrt($t) );
59              
60 6         32 return -exp( ( $mu - $r_q ) * $t ) * pnorm( -$d1 );
61             }
62              
63             sub call {
64 16     16 0 7867 my ( $S, $U, $t, $r_q, $mu, $vol ) = @_;
65              
66 16         86 my $d2 =
67             ( log( $S / $U ) + ( $mu - $vol * $vol / 2.0 ) * $t ) /
68             ( $vol * sqrt($t) );
69              
70 16         65 return exp( -$r_q * $t ) * dgauss($d2) / ( $vol * sqrt($t) * $S );
71             }
72              
73             sub put {
74 16     16 0 7934 my ( $S, $D, $t, $r_q, $mu, $vol ) = @_;
75              
76 16         59 my $d2 =
77             ( log( $S / $D ) + ( $mu - $vol * $vol / 2.0 ) * $t ) /
78             ( $vol * sqrt($t) );
79              
80 16         48 return -exp( -$r_q * $t ) * dgauss($d2) / ( $vol * sqrt($t) * $S );
81             }
82              
83             sub expirymiss {
84 10     10 0 8534 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
85              
86 10         33 return call( $S, $U, $t, $r_q, $mu, $vol ) +
87             put( $S, $D, $t, $r_q, $mu, $vol );
88             }
89              
90             sub expiryrange {
91 5     5 0 6546 my ( $S, $U, $D, $t, $r_q, $mu, $vol ) = @_;
92              
93 5         14 return -1 * expirymiss( $S, $U, $D, $t, $r_q, $mu, $vol );
94             }
95              
96             sub onetouch {
97 13     13 0 9815 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
98              
99             # w = 0, rebate paid at hit.
100             # w = 1, rebate paid at end.
101 13 100       36 if ( not defined $w ) {
102 7         9 $w = 0;
103             }
104              
105 13         24 my $sqrt_t = sqrt($t);
106              
107 13         23 my $theta = ( $mu / $vol ) + ( 0.5 * $vol );
108              
109 13         16 my $theta_ = ( $mu / $vol ) - ( 0.5 * $vol );
110              
111             # Floor v_ squared near zero in case negative interest rates push it negative.
112 13         54 my $v_ = sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $theta_ * $theta_ ) + ( 2 * ( 1 - $w ) * $r_q ) ) );
113              
114 13         70 my $e = ( log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
115              
116 13         30 my $e_ = ( -log( $S / $U ) - ( $vol * $v_ * $t ) ) / ( $vol * $sqrt_t );
117              
118 13 100       27 my $eta = ( $S > $U ) ? 1 : -1;
119              
120 13         97 my $part1 =
121             ( $theta_ + $v_ ) * pnorm( -$eta * $e ) + $eta * dgauss($e) / $sqrt_t;
122 13         45 my $part2 =
123             ( $theta_ - $v_ ) * pnorm( $eta * $e_ ) + $eta * dgauss($e_) / $sqrt_t;
124              
125 13         66 my $delta =
126             ( ( $U / $S )**( ( $theta_ + $v_ ) / $vol ) ) * $part1 +
127             ( ( $U / $S )**( ( $theta_ - $v_ ) / $vol ) ) * $part2;
128              
129 13         34 return -$delta * exp( -$w * $r_q * $t ) / ( $vol * $S );
130             }
131              
132             sub notouch {
133 6     6 0 7572 my ( $S, $U, $t, $r_q, $mu, $vol, $w ) = @_;
134              
135             # No touch bet always pay out at end
136 6         8 $w = 1;
137              
138 6         12 return -1 * onetouch( $S, $U, $t, $r_q, $mu, $vol, $w );
139             }
140              
141             sub upordown {
142 13     13 0 13653 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
143              
144             # $w = 0, paid at hit
145             # $w = 1, paid at end
146 13 100       52 if ( not defined $w ) { $w = 0; }
  7         14  
147              
148 13         48 return ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) +
149             ot_down_ko_up_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
150             }
151              
152             sub x_common_function_pelsser_1997 {
153 104     104 0 178 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta ) = @_;
154              
155 104         106 my $pi = Math::Trig::pi;
156              
157 104         141 my $h = log( $U / $D );
158 104         113 my $x = log( $S / $D );
159              
160             # $eta = 1, onetouch up knockout down
161             # $eta = 0, onetouch down knockout up
162             # This variable used to check stability
163 104 50       201 if ( not defined $eta ) {
164 0         0 die
165             "$0: (x_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.";
166             }
167 104 100       191 if ( $eta == 0 ) { $x = $h - $x; }
  52         56  
168              
169 104         124 my $mu_new = $mu - ( 0.5 * $vol * $vol );
170 104         349 my $mu_dash =
171             sqrt( max( $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU, ( $mu_new * $mu_new ) + ( 2 * $vol * $vol * $r_q * ( 1 - $w ) ) ) );
172              
173 104         104 my $series_part = 0;
174 104         88 my $hyp_part = 0;
175              
176 104         207 my $stability_constant =
177             Math::Business::BlackScholes::Binaries::get_stability_constant_pelsser_1997(
178             $S, $U, $D, $t, $r_q, $mu, $vol, $w, $eta, 2 );
179              
180 104         1340 my $iterations_required =
181             Math::Business::BlackScholes::Binaries::get_min_iterations_pelsser_1997(
182             $S, $U, $D, $t, $r_q, $mu, $vol, $w );
183              
184 104         3988 for ( my $k = 1 ; $k < $iterations_required ; $k++ ) {
185 2280         3130 my $lambda_k_dash = (
186             0.5 * (
187             ( $mu_dash * $mu_dash ) / ( $vol * $vol ) +
188             ( $k * $k * $pi * $pi * $vol * $vol ) / ( $h * $h )
189             )
190             );
191              
192 2280         3078 my $phi =
193             ( $vol * $vol ) /
194             ( $h * $h * $h ) *
195             exp( -$lambda_k_dash * $t ) *
196             $k *
197             $k /
198             $lambda_k_dash;
199              
200 2280         2802 $series_part += $phi * $pi * $pi * cos( $k * $pi * ( $h - $x ) / $h );
201              
202             #
203             # For delta, the stability function is $phi/$S, for gamma it is different,
204             # but we shall ignore for now.
205             #
206 2280 50 66     6113 if ( $k == 1 and ( not( abs( $phi / $S ) < $stability_constant ) ) ) {
207 0         0 die
208             "$0: PELSSER DELTA 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 DELTA stability conditions ($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.";
209             }
210             }
211              
212             # Need to take care when $mu goes to zero
213 104 50       211 if (
214             abs($mu_new) < $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU )
215             {
216 0 0       0 my $sign = ( $mu_new >= 0 ) ? 1 : -1;
217 0         0 $mu_new =
218             $sign * $Math::Business::BlackScholes::Binaries::SMALL_VALUE_MU;
219 0         0 $mu_dash = sqrt(
220             ( $mu_new * $mu_new ) + ( 2 * $vol * $vol * $r_q * ( 1 - $w ) ) );
221             }
222              
223             $hyp_part =
224 104         316 ( $mu_dash / ( $vol * $vol ) ) *
225             ( Math::Trig::cosh( $mu_dash * $x / ( $vol * $vol ) ) /
226             Math::Trig::sinh( $mu_dash * $h / ( $vol * $vol ) ) );
227              
228 104         1393 my $dc_dx = ( $hyp_part + $series_part ) * exp( -$r_q * $t * $w );
229              
230 104         293 return $dc_dx;
231             }
232              
233             sub ot_up_ko_down_pelsser_1997 {
234 13     13 0 37 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
235              
236 13         40 my $mu_new = $mu - ( 0.5 * $vol * $vol );
237 13         43 my $h = log( $U / $D );
238 13         30 my $x = log( $S / $D );
239              
240 13         62 my $dVu_dx = -(
241             ( $mu_new / ( $vol * $vol ) ) *
242             Math::Business::BlackScholes::Binaries::common_function_pelsser_1997(
243             $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1
244             )
245             );
246              
247 13         2476 $dVu_dx +=
248             x_common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 1 );
249 13         43 $dVu_dx *= exp( $mu_new * ( $h - $x ) / ( $vol * $vol ) );
250              
251             # dV/dS = dV/dx * dx/dS = dV/dx * 1/S
252 13         44 return $dVu_dx / $S;
253             }
254              
255             sub ot_down_ko_up_pelsser_1997 {
256 13     13 0 30 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
257              
258 13         35 my $mu_new = $mu - ( 0.5 * $vol * $vol );
259 13         29 my $h = log( $U / $D );
260 13         19 my $x = log( $S / $D );
261              
262 13         46 my $dVl_dx = -(
263             ( $mu_new / ( $vol * $vol ) ) *
264             Math::Business::BlackScholes::Binaries::common_function_pelsser_1997(
265             $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0
266             )
267             );
268              
269 13         2041 $dVl_dx -=
270             x_common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $vol, $w, 0 );
271 13         39 $dVl_dx *= exp( -$mu_new * $x / ( $vol * $vol ) );
272              
273             # dV/dS = dV/dx * dx/dS = dV/dx * 1/S
274 13         45 return $dVl_dx / $S;
275             }
276              
277             sub range {
278 6     6 0 12883 my ( $S, $U, $D, $t, $r_q, $mu, $vol, $w ) = @_;
279              
280             # Range always pay out at end
281 6         13 $w = 1;
282              
283 6         27 return -1 * upordown( $S, $U, $D, $t, $r_q, $mu, $vol, $w );
284             }
285              
286             1;
287