File Coverage

blib/lib/Math/Business/BlackScholesMerton/NonBinaries.pm
Criterion Covered Total %
statement 70 74 94.5
branch 4 4 100.0
condition n/a
subroutine 15 17 88.2
pod 10 10 100.0
total 99 105 94.2


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholesMerton::NonBinaries;
2              
3 3     3   179023 use strict;
  3         15  
  3         63  
4 3     3   12 use warnings;
  3         4  
  3         82  
5              
6 3     3   18 use List::Util qw(min max);
  3         5  
  3         189  
7 3     3   744 use Math::CDF qw(pnorm);
  3         4323  
  3         2876  
8              
9             our $VERSION = '1.24'; ## VERSION
10              
11             =head1 NAME
12              
13             Math::Business::BlackScholesMerton::NonBinaries
14              
15             =head1 SYNOPSIS
16              
17             use Math::Business::BlackScholesMerton::NonBinaries;
18              
19             # price of a Call spread option
20             my $price_call_option = Math::Business::BlackScholesMerton::NonBinaries::vanilla_call(
21             1.35, # stock price
22             1.34, # barrier
23             (7/365), # time
24             0.002, # payout currency interest rate (0.05 = 5%)
25             0.001, # quanto drift adjustment (0.05 = 5%)
26             0.11, # volatility (0.3 = 30%)
27             );
28              
29             =head1 DESCRIPTION
30              
31             Contains non-binary option pricing formula.
32              
33             =cut
34              
35             =head2 vanilla_call
36              
37             USAGE
38             my $price = vanilla_call($S, $K, $t, $r_q, $mu, $sigma);
39              
40             DESCRIPTION
41             Price of a Vanilla Call
42              
43             =cut
44              
45             sub vanilla_call {
46 2     2 1 753 my ($S, $K, $t, $r_q, $mu, $sigma) = @_;
47              
48 2         8 my $d1 = (log($S / $K) + ($mu + $sigma * $sigma / 2.0) * $t) / ($sigma * sqrt($t));
49 2         3 my $d2 = $d1 - ($sigma * sqrt($t));
50              
51 2         10 return exp(-$r_q * $t) * ($S * exp($mu * $t) * pnorm($d1) - $K * pnorm($d2));
52             }
53              
54             =head2 vanilla_put
55              
56             USAGE
57             my $price = vanilla_put($S, $K, $t, $r_q, $mu, sigma)
58              
59             DESCRIPTION
60             Price a standard Vanilla Put
61              
62             =cut
63              
64             sub vanilla_put {
65 2     2 1 787 my ($S, $K, $t, $r_q, $mu, $sigma) = @_;
66              
67 2         6 my $d1 = (log($S / $K) + ($mu + $sigma * $sigma / 2.0) * $t) / ($sigma * sqrt($t));
68 2         3 my $d2 = $d1 - ($sigma * sqrt($t));
69              
70 2         18 return -1 * exp(-$r_q * $t) * ($S * exp($mu * $t) * pnorm(-$d1) - $K * pnorm(-$d2));
71             }
72              
73             =head2 lbfloatcall
74              
75             USAGE
76             my $price = lbfloatcall($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min)
77              
78             DESCRIPTION
79             Price of a Lookback Float Call
80              
81             =cut
82              
83             sub lbfloatcall {
84 3     3 1 1926 my ($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min) = @_;
85              
86 3         5 $S_max = undef;
87 3         6 my $d1 = _d1_function($S, $S_min, $t, $r_q, $mu, $sigma);
88 3         6 my $d2 = $d1 - ($sigma * sqrt($t));
89              
90 3         55 my $value = exp(-$r_q * $t) * ($S * exp($mu * $t) * pnorm($d1) - $S_min * pnorm($d2) + _l_min($S, $S_min, $t, $r_q, $mu, $sigma));
91              
92 3         9 return $value;
93             }
94              
95             =head2 lbfloatput
96              
97             USAGE
98             my $price = lbfloatcall($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min)
99              
100             DESCRIPTION
101             Price of a Lookback Float Put
102              
103             =cut
104              
105             sub lbfloatput { # Floating Strike Put
106 3     3 1 1366 my ($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min) = @_;
107              
108 3         5 $S_min = undef;
109 3         7 my $d1 = _d1_function($S, $S_max, $t, $r_q, $mu, $sigma);
110 3         7 my $d2 = $d1 - ($sigma * sqrt($t));
111              
112 3         32 my $value = exp(-$r_q * $t) * ($S_max * pnorm(-$d2) - $S * exp($mu * $t) * pnorm(-$d1) + _l_max($S, $S_max, $t, $r_q, $mu, $sigma));
113              
114 3         7 return $value;
115             }
116              
117             =head2 lbfixedcall
118              
119             USAGE
120             my $price = lbfixedcall($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min)
121              
122             DESCRIPTION
123             Price of a Lookback Fixed Call
124              
125             =cut
126              
127             sub lbfixedcall {
128 2     2 1 1676 my ($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min) = @_;
129              
130 2         3 $S_min = undef;
131 2         10 my $K_max = max($S_max, $K);
132 2         6 my $d1 = _d1_function($S, $K_max, $t, $r_q, $mu, $sigma);
133 2         5 my $d2 = $d1 - ($sigma * sqrt($t));
134              
135 2         46 my $value =
136             exp(-$r_q * $t) * (max($S_max - $K, 0.0) + $S * exp($mu * $t) * pnorm($d1) - $K_max * pnorm($d2) + _l_max($S, $K_max, $t, $r_q, $mu, $sigma));
137              
138 2         5 return $value;
139             }
140              
141             =head2 lbfixedput
142              
143             USAGE
144             my $price = lbfixedput($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min)
145              
146             DESCRIPTION
147             Price of a Lookback Fixed Put
148              
149             =cut
150              
151             sub lbfixedput {
152 2     2 1 1347 my ($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min) = @_;
153              
154 2         11 $S_max = undef;
155 2         9 my $K_min = min($S_min, $K);
156 2         5 my $d1 = _d1_function($S, $K_min, $t, $r_q, $mu, $sigma);
157 2         5 my $d2 = $d1 - ($sigma * sqrt($t));
158              
159 2         23 my $value = exp(-$r_q * $t) *
160             (max($K - $S_min, 0.0) + $K_min * pnorm(-$d2) - $S * exp($mu * $t) * pnorm(-$d1) + _l_min($S, $K_min, $t, $r_q, $mu, $sigma));
161              
162 2         4 return $value;
163             }
164              
165             =head2 lbhighlow
166              
167             USAGE
168             my $price = lbhighlow($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min)
169              
170             DESCRIPTION
171             Price of a Lookback High Low
172              
173             =cut
174              
175             sub lbhighlow {
176 1     1 1 473 my ($S, $K, $t, $r_q, $mu, $sigma, $S_max, $S_min) = @_;
177              
178 1         3 my $value = lbfloatcall($S, $S_min, $t, $r_q, $mu, $sigma, $S_max, $S_min) + lbfloatput($S, $S_max, $t, $r_q, $mu, $sigma, $S_max, $S_min);
179              
180 1         2 return $value;
181             }
182              
183             =head2 _d1_function
184              
185             returns the d1 term common to many BlackScholesMerton formulae.
186              
187             =cut
188              
189             sub _d1_function {
190 20     20   37 my ($S, $K, $t, $r_q, $mu, $sigma) = @_;
191              
192 20         63 my $value = (log($S / $K) + ($mu + $sigma * $sigma * 0.5) * $t) / ($sigma * sqrt($t));
193              
194 20         32 return $value;
195             }
196              
197             =head2 _l_max
198              
199             This is a common function use to calculate the lookbacks options price. See [5] for details.
200              
201             =cut
202              
203             sub _l_max {
204 5     5   16 my ($S, $K, $t, $r_q, $mu, $sigma) = @_;
205              
206 5         10 my $d1 = _d1_function($S, $K, $t, $r_q, $mu, $sigma);
207 5         7 my $value;
208              
209 5 100       12 if ($mu) {
210 3         19 $value =
211             $S *
212             ($sigma**2) /
213             (2.0 * $mu) *
214             (-($S / $K)**(-2.0 * $mu / ($sigma**2)) * pnorm($d1 - 2.0 * $mu / $sigma * sqrt($t)) + exp($mu * $t) * pnorm($d1));
215             } else {
216 2         5 $value = $S * ($sigma * sqrt($t)) * (dnorm($d1) + $d1 * pnorm($d1));
217             }
218              
219 5         11 return $value;
220             }
221              
222             =head2 _l_min
223              
224             This is a common function use to calculate the lookbacks options price. See [5] for details.
225              
226             =cut
227              
228             sub _l_min {
229 5     5   16 my ($S, $K, $t, $r_q, $mu, $sigma) = @_;
230              
231 5         13 my $d1 = _d1_function($S, $K, $t, $r_q, $mu, $sigma);
232 5         7 my $value;
233              
234 5 100       11 if ($mu) {
235 3         17 $value =
236             $S *
237             ($sigma**2) /
238             (2.0 * $mu) *
239             (($S / $K)**(-2.0 * $mu / ($sigma**2)) * pnorm(-$d1 + 2.0 * $mu / $sigma * sqrt($t)) - exp($mu * $t) * pnorm(-$d1));
240             } else {
241 2         5 $value = $S * ($sigma * sqrt($t)) * (dnorm($d1) + $d1 * (pnorm($d1) - 1));
242             }
243              
244 5         10 return $value;
245             }
246              
247             =head2 dnorm
248              
249             Standard normal density function
250              
251             =cut
252              
253             sub dnorm { # Standard normal density function
254 4     4 1 6 my $x = shift;
255 4         5 my $pi = 3.14159265359;
256              
257 4         16 my $value = exp(-$x**2 / 2) / sqrt(2.0 * $pi);
258              
259 4         11 return $value;
260             }
261              
262             =head2 callspread
263              
264             USAGE
265             my $price = callspread($S, $U, $D, $t, $r_q, $mu, $sigmaU, $sigmaD);
266              
267             DESCRIPTION
268             Price of a CALL SPREAD
269              
270             =cut
271              
272             sub callspread {
273 0     0 1   my ($S, $U, $D, $t, $r_q, $mu, $sigmaU, $sigmaD) = @_;
274              
275 0           return vanilla_call($S, $D, $t, $r_q, $mu, $sigmaD) - vanilla_call($S, $U, $t, $r_q, $mu, $sigmaU);
276             }
277              
278             =head2 putspread
279              
280             USAGE
281             my $price = putspread($S, $U, $D, $t, $r_q, $mu, $sigmaU, $sigmaD);
282              
283             DESCRIPTION
284             Price of a PUT SPREAD
285              
286             =cut
287              
288             sub putspread {
289 0     0 1   my ($S, $U, $D, $t, $r_q, $mu, $sigmaU, $sigmaD) = @_;
290              
291 0           return vanilla_put($S, $U, $t, $r_q, $mu, $sigmaU) - vanilla_put($S, $D, $t, $r_q, $mu, $sigmaD);
292             }
293              
294             1;