File Coverage

lib/Math/Business/BlackScholes/Binaries.pm
Criterion Covered Total %
statement 163 168 97.0
branch 47 52 90.3
condition 16 24 66.6
subroutine 24 24 100.0
pod 16 16 100.0
total 266 284 93.6


line stmt bran cond sub pod time code
1             package Math::Business::BlackScholes::Binaries;
2 10     10   550561 use strict;
  10         25  
  10         345  
3 10     10   49 use warnings;
  10         14  
  10         589  
4              
5             our $VERSION = '1.2';
6              
7             my $SMALLTIME = 1 / ( 60 * 60 * 24 * 365 ); # 1 second in years;
8              
9 10     10   61 use List::Util qw(max);
  10         20  
  10         1016  
10 10     10   5123 use Math::CDF qw(pnorm);
  10         28936  
  10         653  
11 10     10   6447 use Math::Trig;
  10         157815  
  10         1694  
12 10     10   5665 use Machine::Epsilon;
  10         3170  
  10         32196  
13              
14             =head1 NAME
15              
16             Math::Business::BlackScholes::Binaries
17              
18             =head1 VERSION
19              
20             Version 1.2
21              
22             =head1 SYNOPSIS
23              
24             use Math::Business::BlackScholes::Binaries;
25              
26             # price of a Call option
27             my $price_call_option = Math::Business::BlackScholes::Binaries::call(
28             1.35, # stock price
29             1.36, # barrier
30             (7/365), # time
31             0.002, # payout currency interest rate (0.05 = 5%)
32             0.001, # quanto drift adjustment (0.05 = 5%)
33             0.11, # volatility (0.3 = 30%)
34             );
35              
36             =head1 DESCRIPTION
37              
38             Prices options using the GBM model, all closed formulas.
39              
40             Important(a): Basically, onetouch, upordown and doubletouch have two cases of
41             payoff either at end or at hit. We treat them differently. We use parameter
42             $w to differ them.
43              
44             $w = 0: payoff at hit time.
45             $w = 1: payoff at end.
46              
47             Our current contracts pay rebate at hit time, so we set $w = 0 by default.
48              
49             Important(b) :Furthermore, for all contracts, we allow a different
50             payout currency (Quantos).
51              
52             Paying domestic currency (JPY if for USDJPY) = correlation coefficient is ZERO.
53             Paying foreign currency (USD if for USDJPY) = correlation coefficient is ONE.
54             Paying another currency = correlation is between negative ONE and positive ONE.
55              
56             See [3] for Quanto formulas and examples
57              
58             =head1 SUBROUTINES
59              
60             =head2 vanilla_call
61              
62             USAGE
63             my $price = vanilla_call($S, $K, $t, $r_q, $mu, $sigma)
64              
65             DESCRIPTION
66             Price of a Vanilla Call
67              
68             =cut
69              
70             sub vanilla_call {
71 2     2 1 1274 my ( $S, $K, $t, $r_q, $mu, $sigma ) = @_;
72              
73 2         11 my $d1 =
74             ( log( $S / $K ) + ( $mu + $sigma * $sigma / 2.0 ) * $t ) /
75             ( $sigma * sqrt($t) );
76 2         6 my $d2 = $d1 - ( $sigma * sqrt($t) );
77              
78             return
79 2         23 exp( -$r_q * $t ) *
80             ( $S * exp( $mu * $t ) * pnorm($d1) - $K * pnorm($d2) );
81             }
82              
83             =head2 vanilla_put
84              
85             USAGE
86             my $price = vanilla_put($S, $K, $t, $r_q, $mu, sigma)
87              
88             DESCRIPTION
89             Price a standard Vanilla Put
90              
91             =cut
92              
93             sub vanilla_put {
94 2     2 1 1273 my ( $S, $K, $t, $r_q, $mu, $sigma ) = @_;
95              
96 2         12 my $d1 =
97             ( log( $S / $K ) + ( $mu + $sigma * $sigma / 2.0 ) * $t ) /
98             ( $sigma * sqrt($t) );
99 2         4 my $d2 = $d1 - ( $sigma * sqrt($t) );
100              
101 2         27 return -1 *
102             exp( -$r_q * $t ) *
103             ( $S * exp( $mu * $t ) * pnorm( -$d1 ) - $K * pnorm( -$d2 ) );
104             }
105              
106             =head2 call
107              
108             USAGE
109             my $price = call($S, $K, $t, $r_q, $mu, $sigma)
110              
111             PARAMS
112             $S => stock price
113             $K => barrier
114             $t => time (1 = 1 year)
115             $r_q => payout currency interest rate (0.05 = 5%)
116             $mu => quanto drift adjustment (0.05 = 5%)
117             $sigma => volatility (0.3 = 30%)
118              
119             DESCRIPTION
120             Price a Call and remove the N(d2) part if the time is too small
121              
122             EXPLANATION
123             The definition of the contract is that if S > K, it gives
124             full payout (1). However the formula DC(T,K) = e^(-rT) N(d2) will not be
125             correct when T->0 and K=S. The value of DC(T,K) for this case will be 0.5.
126            
127             The formula is actually "correct" because when T->0 and S=K, the probability
128             should just be 0.5 that the contract wins, moving up or down is equally
129             likely in that very small amount of time left. Thus the only problem is
130             that the math cannot evaluate at T=0, where divide by 0 error occurs. Thus,
131             we need this check that throws away the N(d2) part (N(d2) will evaluate
132             "wrongly" to 0.5 if S=K).
133              
134             NOTE
135             Note that we have call = - dCall/dStrike
136             pair Foreign/Domestic
137              
138             see [3] for $r_q and $mu for quantos
139              
140             =cut
141              
142             sub call {
143 13     13 1 11508 my ( $S, $K, $t, $r_q, $mu, $sigma ) = @_;
144              
145 13 100       51 if ( $t < $SMALLTIME ) {
146 2 100       13 return ( $S > $K ) ? exp( -$r_q * $t ) : 0;
147             }
148              
149 11         63 return exp( -$r_q * $t ) * pnorm( d2( $S, $K, $t, $r_q, $mu, $sigma ) );
150             }
151              
152             =head2 put
153              
154             USAGE
155             my $price = put($S, $K, $t, $r_q, $mu, $sigma)
156              
157             PARAMS
158             $S => stock price
159             $K => barrier
160             $t => time (1 = 1 year)
161             $r_q => payout currency interest rate (0.05 = 5%)
162             $mu => quanto drift adjustment (0.05 = 5%)
163             $sigma => volatility (0.3 = 30%)
164              
165             DESCRIPTION
166             Price a standard Digital Put
167              
168             =cut
169              
170             sub put {
171 13     13 1 4624 my ( $S, $K, $t, $r_q, $mu, $sigma ) = @_;
172              
173 13 100       44 if ( $t < $SMALLTIME ) {
174 2 100       21 return ( $S < $K ) ? exp( -$r_q * $t ) : 0;
175             }
176              
177             return
178 11         34 exp( -$r_q * $t ) * pnorm( -1 * d2( $S, $K, $t, $r_q, $mu, $sigma ) );
179             }
180              
181             =head2 d2
182              
183             returns the DS term common to many BlackScholes formulae.
184              
185             =cut
186              
187             sub d2 {
188 22     22 1 30 my ( $S, $K, $t, $r_q, $mu, $sigma ) = @_;
189              
190 22         298 return ( log( $S / $K ) + ( $mu - $sigma * $sigma / 2.0 ) * $t ) /
191             ( $sigma * sqrt($t) );
192             }
193              
194             =head2 expirymiss
195              
196             USAGE
197             my $price = expirymiss($S, $U, $D, $t, $r_q, $mu, $sigma)
198              
199             PARAMS
200             $S => stock price
201             $t => time (1 = 1 year)
202             $U => barrier
203             $D => barrier
204             $r_q => payout currency interest rate (0.05 = 5%)
205             $mu => quanto drift adjustment (0.05 = 5%)
206             $sigma => volatility (0.3 = 30%)
207              
208             DESCRIPTION
209             Price an expiry miss contract (1 Call + 1 Put)
210              
211             [3] for $r_q and $mu for quantos
212              
213             =cut
214              
215             sub expirymiss {
216 6     6 1 1278 my ( $S, $U, $D, $t, $r_q, $mu, $sigma ) = @_;
217              
218 6         14 my ($call_price) = call( $S, $U, $t, $r_q, $mu, $sigma );
219 6         15 my ($put_price) = put( $S, $D, $t, $r_q, $mu, $sigma );
220              
221 6         21 return $call_price + $put_price;
222             }
223              
224             =head2 expiryrange
225              
226             USAGE
227             my $price = expiryrange($S, $U, $D, $t, $r_q, $mu, $sigma)
228              
229             PARAMS
230             $S => stock price
231             $U => barrier
232             $D => barrier
233             $t => time (1 = 1 year)
234             $r_q => payout currency interest rate (0.05 = 5%)
235             $mu => quanto drift adjustment (0.05 = 5%)
236             $sigma => volatility (0.3 = 30%)
237              
238             DESCRIPTION
239             Price an Expiry Range contract as Foreign/Domestic.
240              
241             [3] for $r_q and $mu for quantos
242              
243             =cut
244              
245             sub expiryrange {
246 3     3 1 1627 my ( $S, $U, $D, $t, $r_q, $mu, $sigma ) = @_;
247              
248 3         39 return exp( -$r_q * $t ) - expirymiss( $S, $U, $D, $t, $r_q, $mu, $sigma );
249             }
250              
251             =head2 onetouch
252              
253             PARAMS
254             $S => stock price
255             $U => barrier
256             $t => time (1 = 1 year)
257             $r_q => payout currency interest rate (0.05 = 5%)
258             $mu => quanto drift adjustment (0.05 = 5%)
259             $sigma => volatility (0.3 = 30%)
260              
261             [3] for $r_q and $mu for quantos
262              
263             =cut
264              
265             sub onetouch {
266 36     36 1 2632 my ( $S, $U, $t, $r_q, $mu, $sigma, $w ) = @_;
267              
268             # w = 0, rebate paid at hit (good way to remember is that waiting
269             # time to get paid = 0)
270             # w = 1, rebate paid at end.
271              
272             # When the contract already reached it expiry and not yet reach it
273             # settlement time, it is consider an unexpired contract but will come to
274             # here with t=0 and it will caused the formula to die hence set it to the
275             # SMALLTIME which is 1 second
276 36         74 $t = max( $SMALLTIME, $t );
277              
278 36   100     133 $w ||= 0;
279              
280             # eta = -1, one touch up
281             # eta = 1, one touch down
282 36 100       60 my $eta = ( $S < $U ) ? -1 : 1;
283              
284 36         44 my $sqrt_t = sqrt($t);
285              
286 36         48 my $theta = ( ($mu) / $sigma ) + ( 0.5 * $sigma );
287 36         49 my $theta_ = ( ($mu) / $sigma ) - ( 0.5 * $sigma );
288              
289 36         61 my $v_ = sqrt( ( $theta_ * $theta_ ) + ( 2 * ( 1 - $w ) * $r_q ) );
290              
291 36         70 my $e = ( log( $S / $U ) - ( $sigma * $v_ * $t ) ) / ( $sigma * $sqrt_t );
292 36         56 my $e_ = ( -log( $S / $U ) - ( $sigma * $v_ * $t ) ) / ( $sigma * $sqrt_t );
293              
294 36         289 my $price =
295             ( ( $U / $S )**( ( $theta_ + $v_ ) / $sigma ) ) * pnorm( -$eta * $e ) +
296             ( ( $U / $S )**( ( $theta_ - $v_ ) / $sigma ) ) * pnorm( $eta * $e_ );
297              
298 36         97 return exp( -$w * $r_q * $t ) * $price;
299             }
300              
301             =head2 notouch
302              
303             USAGE
304             my $price = notouch($S, $U, $t, $r_q, $mu, $sigma, $w)
305              
306             PARAMS
307             $S => stock price
308             $U => barrier
309             $t => time (1 = 1 year)
310             $r_q => payout currency interest rate (0.05 = 5%)
311             $mu => quanto drift adjustment (0.05 = 5%)
312             $sigma => volatility (0.3 = 30%)
313              
314             DESCRIPTION
315             Price a No touch contract.
316              
317             Payoff with domestic currency
318             Identity:
319             price of notouch = exp(- r t) - price of onetouch(rebate paid at end)
320              
321             [3] for $r_q and $mu for quantos
322              
323             =cut
324              
325             sub notouch {
326 7     7 1 2031 my ( $S, $U, $t, $r_q, $mu, $sigma ) = @_;
327              
328             # No touch contract always pay out at end
329 7         11 my $w = 1;
330              
331 7         27 return exp( -$r_q * $t ) - onetouch( $S, $U, $t, $r_q, $mu, $sigma, $w );
332             }
333              
334             # These variables require 'our' only because they need to be
335             # accessed by a test script.
336             our $MAX_ITERATIONS_UPORDOWN_PELSSER_1997 = 1000;
337             our $MIN_ITERATIONS_UPORDOWN_PELSSER_1997 = 16;
338              
339             #
340             # This variable requires 'our' only because it needs to be
341             # accessed via test script.
342             # Min accuracy. Accurate to 1 dollar for 100,000 notional
343             #
344             our $MIN_ACCURACY_UPORDOWN_PELSSER_1997 = 1.0 / 100000.0;
345             our $SMALL_VALUE_MU = 1e-10;
346              
347             # The smallest (in magnitude) floating-point number which,
348             # when added to the floating-point number 1.0, produces a
349             # floating-point result different from 1.0 is termed the
350             # machine accuracy, e.
351             #
352             # This value is very important for knowing stability to
353             # certain formulas used. e.g. Pelsser formula for UPORDOWN
354             # and RANGE contracts.
355             #
356             my $MACHINE_EPSILON = machine_epsilon();
357              
358             =head2 upordown
359              
360             USAGE
361             my $price = upordown(($S, $U, $D, $t, $r_q, $mu, $sigma, $w))
362              
363             PARAMS
364             $S stock price
365             $U barrier
366             $D barrier
367             $t time (1 = 1 year)
368             $r_q payout currency interest rate (0.05 = 5%)
369             $mu quanto drift adjustment (0.05 = 5%)
370             $sigma volatility (0.3 = 30%)
371              
372             see [3] for $r_q and $mu for quantos
373              
374             DESCRIPTION
375             Price an Up or Down contract
376              
377             =cut
378              
379             sub upordown {
380 15     15 1 4858 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w ) = @_;
381              
382             # When the contract already reached it's expiry and not yet reach it
383             # settlement time, it is considered an unexpired contract but will come to
384             # here with t=0 and it will caused the formula to die hence set it to the
385             # SMALLTIME whiich is 1 second
386 15         40 $t = max( $t, $SMALLTIME );
387              
388             # $w = 0, paid at hit
389             # $w = 1, paid at end
390 15 100       39 if ( not defined $w ) { $w = 0; }
  8         11  
391              
392             # spot is outside [$D, $U] --> contract is expired with full payout,
393             # one barrier is already hit (can happen due to shift markup):
394 15 100 100     75 if ( $S >= $U or $S <= $D ) {
395 4 100       14 return $w ? exp( -$t * $r_q ) : 1;
396             }
397              
398             #
399             # SANITY CHECKS
400             #
401             # For extreme cases, the price will be wrong due the values in the
402             # infinite series getting too large or too small, which causes
403             # roundoff errors in the computer. Thus no matter how many iterations
404             # you make, the errors will never go away.
405             #
406             # For example try this:
407             #
408             # my ($S, $U, $D, $t, $r, $q, $vol, $w)
409             # = (100.00, 118.97, 99.00, 30/365, 0.1, 0.02, 0.01, 1);
410             # $up_price = Math::Business::BlackScholes::Binaries::ot_up_ko_down_pelsser_1997(
411             # $S,$U,$D,$t,$r,$q,$vol,$w);
412             # $down_price= Math::Business::BlackScholes::Binaries::ot_down_ko_up_pelsser_1997(
413             # $S,$U,$D,$t,$r,$q,$vol,$w);
414             #
415             # Thus we put a sanity checks here such that
416             #
417             # CONDITION 1: UPORDOWN[U,D] < ONETOUCH[U] + ONETOUCH[D]
418             # CONDITION 2: UPORDOWN[U,D] > ONETOUCH[U]
419             # CONDITION 3: UPORDOWN[U,D] > ONETOUCH[D]
420             # CONDITION 4: ONETOUCH[U] + ONETOUCH[D] >= $MIN_ACCURACY_UPORDOWN_PELSSER_1997
421             #
422 11         26 my $onetouch_up_prob = onetouch( $S, $U, $t, $r_q, $mu, $sigma, $w );
423 11         26 my $onetouch_down_prob = onetouch( $S, $D, $t, $r_q, $mu, $sigma, $w );
424              
425 11         13 my $upordown_prob;
426              
427 11 100 75     66 if ( $onetouch_up_prob + $onetouch_down_prob <
    100          
428             $MIN_ACCURACY_UPORDOWN_PELSSER_1997 )
429             {
430              
431             # CONDITION 4:
432             # The probability is too small for the Pelsser formula to be correct.
433             # Do this check first to avoid PELSSER stability condition to be
434             # triggered.
435             # Here we assume that the ONETOUCH formula is perfect and never give
436             # wrong values (e.g. negative).
437 1         4 return 0;
438             }
439             elsif ( $onetouch_up_prob xor $onetouch_down_prob ) {
440              
441             # One of our ONETOUCH probabilities is 0.
442             # That means our upordown prob is equivalent to the other one.
443             # Pelsser recompute will either be the same or wrong.
444             # Continuing to assume the ONETOUCH is perfect.
445 4         43 $upordown_prob = max( $onetouch_up_prob, $onetouch_down_prob );
446             }
447             else {
448              
449             # THIS IS THE ONLY PLACE IT SHOULD BE!
450 6         15 $upordown_prob =
451             ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $sigma, $w ) +
452             ot_down_ko_up_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $sigma, $w );
453             }
454              
455             # CONDITION 4:
456             # Now check on the other end, when the contract is too close to payout.
457             # Not really needed to check for payout at hit, because RANGE is
458             # always at end, and thus the value (DISCOUNT - UPORDOWN) is not
459             # evaluated.
460 10 100       26 if ( $w == 1 ) {
461              
462             # Since the difference is already less than the min accuracy,
463             # the value [payout - upordown], which is the RANGE formula
464             # can become negative.
465 5 50       22 if (
466             abs( exp( -$r_q * $t ) - $upordown_prob ) <
467             $MIN_ACCURACY_UPORDOWN_PELSSER_1997 )
468             {
469 0         0 $upordown_prob = exp( -$r_q * $t );
470             }
471             }
472              
473             # CONDITION 1-3
474             # We use hardcoded small value of $SMALL_TOLERANCE, because if we were to increase
475             # the minimum accuracy, and this small value uses that min accuracy, it is
476             # very hard for the conditions to pass.
477 10         9 my $SMALL_TOLERANCE = 0.00001;
478 10 50 33     56 if (
      33        
479             not( $upordown_prob <
480             $onetouch_up_prob + $onetouch_down_prob + $SMALL_TOLERANCE )
481             or not( $upordown_prob + $SMALL_TOLERANCE > $onetouch_up_prob )
482             or not( $upordown_prob + $SMALL_TOLERANCE > $onetouch_down_prob )
483             )
484             {
485 0         0 die "UPORDOWN price sanity checks failed for S=$S, U=$U, "
486             . "D=$D, t=$t, r_q=$r_q, mu=$mu, sigma=$sigma, w=$w. "
487             . "UPORDOWN PROB=$upordown_prob , "
488             . "ONETOUCH_UP PROB=$onetouch_up_prob , "
489             . "ONETOUCH_DOWN PROB=$onetouch_down_prob";
490             }
491              
492 10         24 return $upordown_prob;
493             }
494              
495             =head2 common_function_pelsser_1997
496              
497             USAGE
498             my $c = common_function_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta)
499              
500             DESCRIPTION
501             Return the common function from Pelsser's Paper (1997)
502              
503             =cut
504              
505             sub common_function_pelsser_1997 {
506              
507             # h: normalized high barrier, log(U/L)
508             # x: normalized spot, log(S/L)
509 14     14 1 2462 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta ) = @_;
510              
511 14         18 my $pi = Math::Trig::pi;
512              
513 14         28 my $h = log( $U / $D );
514 14         16 my $x = log( $S / $D );
515              
516             # $eta = 1, onetouch up knockout down
517             # $eta = 0, onetouch down knockout up
518             # This variable used to check stability
519 14 100       29 if ( not defined $eta ) {
520 1         22 die "Wrong usage of this function for S=$S, U=$U, D=$D, "
521             . "t=$t, r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, eta not defined.";
522             }
523 13 100       24 if ( $eta == 0 ) { $x = $h - $x; }
  7         10  
524              
525             # $w = 0, paid at hit
526             # $w = 1, paid at end
527              
528 13         17 my $mu_new = $mu - ( 0.5 * $sigma * $sigma );
529 13         28 my $mu_dash = sqrt(
530             ( $mu_new * $mu_new ) + ( 2 * $sigma * $sigma * $r_q * ( 1 - $w ) ) );
531              
532 13         12 my $series_part = 0;
533 13         9 my $hyp_part = 0;
534              
535             # These constants will determine whether or not this contract can be
536             # evaluated to a predefined accuracy. It is VERY IMPORTANT because
537             # if these conditions are not met, the prices can be complete nonsense!!
538 13         25 my $stability_constant =
539             get_stability_constant_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $sigma,
540             $w, $eta, 1 );
541              
542             # The number of iterations is important when recommending the
543             # range of the upper/lower barriers on our site. If we recommend
544             # a range that is too big and our iteration is too small, the
545             # price will be wrong! We must know the rate of convergence of
546             # the formula used.
547 13         23 my $iterations_required =
548             get_min_iterations_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $sigma, $w );
549              
550 13         29 for ( my $k = 1 ; $k < $iterations_required ; $k++ ) {
551 195         233 my $lambda_k_dash = (
552             0.5 * (
553             ( $mu_dash * $mu_dash ) / ( $sigma * $sigma ) +
554             ( $k * $k * $pi * $pi * $sigma * $sigma ) / ( $h * $h )
555             )
556             );
557              
558 195         231 my $phi =
559             ( $sigma * $sigma ) /
560             ( $h * $h ) *
561             exp( -$lambda_k_dash * $t ) *
562             $k /
563             $lambda_k_dash;
564              
565 195         255 $series_part += $phi * $pi * sin( $k * $pi * ( $h - $x ) / $h );
566              
567             #
568             # Note that greeks may also call this function, and their
569             # stability constant will differ. However, for simplicity
570             # we will not bother (else the code will get messy), and
571             # just use the price stability constant.
572             #
573 195 50 66     485 if ( $k == 1 and ( not( abs($phi) < $stability_constant ) ) ) {
574 0         0 die
575             "PELSSER VALUATION formula for S=$S, U=$U, D=$D, t=$t, r_q=$r_q, "
576             . "mu=$mu, vol=$sigma, w=$w, eta=$eta, cannot be evaluated because"
577             . "PELSSER VALUATION stability conditions ($phi less than "
578             . "$stability_constant) not met. This could be due to barriers "
579             . "too big, volatilities too low, interest/dividend rates too high, "
580             . "or machine accuracy too low. Machine accuracy is "
581             . $MACHINE_EPSILON . ".";
582             }
583             }
584              
585             #
586             # Some math basics: When A -> 0,
587             #
588             # sinh(A) -> 0.5 * [ (1 + A) - (1 - A) ] = 0.5 * 2A = A
589             # cosh(A) -> 0.5 * [ (1 + A) + (1 - A) ] = 0.5 * 2 = 1
590             #
591             # Thus for sinh(A)/sinh(B) when A & B -> 0, we have
592             #
593             # sinh(A) / sinh(B) -> A / B
594             #
595             # Since the check of the spot == lower/upper barrier has been done in the
596             # _upordown subroutine, we can assume that $x and $h will never be 0.
597             # So we only need to check that $mu_dash is too small. Also note that
598             # $mu_dash is always positive.
599             #
600             # For example, even at 0.0001 the error becomes small enough
601             #
602             # 0.0001 - Math::Trig::sinh(0.0001) = -1.66688941837835e-13
603             #
604             # Since h > x, we only check for (mu_dash * h) / (vol * vol)
605             #
606 13 100       26 if ( abs( $mu_dash * $h / ( $sigma * $sigma ) ) < $SMALL_VALUE_MU ) {
607 1         1 $hyp_part = $x / $h;
608             }
609             else {
610 12         29 $hyp_part =
611             Math::Trig::sinh( $mu_dash * $x / ( $sigma * $sigma ) ) /
612             Math::Trig::sinh( $mu_dash * $h / ( $sigma * $sigma ) );
613             }
614              
615 13         160 return ( $hyp_part - $series_part ) * exp( -$r_q * $t * $w );
616             }
617              
618             =head2 get_stability_constant_pelsser_1997
619              
620             USAGE
621             my $constant = get_stability_constant_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta, $p)
622              
623             DESCRIPTION
624             Get the stability constant (Pelsser 1997)
625              
626             =cut
627              
628             sub get_stability_constant_pelsser_1997 {
629 15     15 1 2804 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, $eta, $p ) = @_;
630              
631             # $eta = 1, onetouch up knockout down
632             # $eta = 0, onetouch down knockout up
633              
634 15 100       31 if ( not defined $eta ) {
635 1         24 die "Wrong usage of this function for S=$S, U=$U, D=$D, t=$t, "
636             . "r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, Eta not defined.";
637             }
638              
639             # p is the power of pi
640             # p=1 for price/theta/vega/vanna/volga
641             # p=2 for delta
642             # p=3 for gamma
643 14 50 66     42 if ( $p != 1 and $p != 2 and $p != 3 ) {
      66        
644 1         20 die "Wrong usage of this function for S=$S, U=$U, D=$D, t=$t, "
645             . "r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, Power of PI must "
646             . "be 1, 2 or 3. Given $p.";
647             }
648              
649 13         18 my $h = log( $U / $D );
650 13         13 my $x = log( $S / $D );
651 13         17 my $mu_new = $mu - ( 0.5 * $sigma * $sigma );
652 13         28 my $mu_dash = sqrt(
653             ( $mu_new * $mu_new ) + ( 2 * $sigma * $sigma * $r_q * ( 1 - $w ) ) );
654              
655 13         29 my $numerator = $MIN_ACCURACY_UPORDOWN_PELSSER_1997 *
656             exp( 1.0 - $mu_new * ( ( $eta * $h ) - $x ) / ( $sigma * $sigma ) );
657 13         42 my $denominator =
658             ( exp(1) * ( Math::Trig::pi + $p ) ) +
659             ( max( $mu_new * ( ( $eta * $h ) - $x ), 0.0 ) *
660             Math::Trig::pi /
661             ( $sigma**2 ) );
662 13         23 $denominator *= ( Math::Trig::pi**( $p - 1 ) ) * $MACHINE_EPSILON;
663              
664 13         17 my $stability_condition = $numerator / $denominator;
665              
666 13         20 return $stability_condition;
667             }
668              
669             =head2 ot_up_ko_down_pelsser_1997
670              
671             USAGE
672             my $price = ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w)
673              
674             DESCRIPTION
675             This is V_{RAHU} in paper [5], or ONETOUCH-UP-KNOCKOUT-DOWN,
676             a contract that wins if it touches upper barrier, but expires
677             worthless if it touches the lower barrier first.
678              
679             =cut
680              
681             sub ot_up_ko_down_pelsser_1997 {
682 6     6 1 9 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w ) = @_;
683              
684 6         9 my $mu_new = $mu - ( 0.5 * $sigma * $sigma );
685 6         10 my $h = log( $U / $D );
686 6         7 my $x = log( $S / $D );
687              
688             return
689 6         19 exp( $mu_new * ( $h - $x ) / ( $sigma * $sigma ) ) *
690             common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, 1 );
691             }
692              
693             =head2 ot_down_ko_up_pelsser_1997
694              
695             USAGE
696             my $price = ot_down_ko_up_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w)
697              
698             DESCRIPTION
699             This is V_{RAHL} in paper [5], or ONETOUCH-DOWN-KNOCKOUT-UP,
700             a contract that wins if it touches lower barrier, but expires
701             worthless if it touches the upper barrier first.
702              
703             =cut
704              
705             sub ot_down_ko_up_pelsser_1997 {
706 6     6 1 8 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w ) = @_;
707              
708 6         9 my $mu_new = $mu - ( 0.5 * $sigma * $sigma );
709 6         8 my $h = log( $U / $D );
710 6         6 my $x = log( $S / $D );
711              
712             return
713 6         16 exp( -$mu_new * $x / ( $sigma * $sigma ) ) *
714             common_function_pelsser_1997( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, 0 );
715             }
716              
717             =head2 get_min_iterations_pelsser_1997
718              
719             USAGE
720             my $min = get_min_iterations_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy)
721              
722             DESCRIPTION
723             An estimate of the number of iterations required to achieve a certain
724             level of accuracy in the price.
725              
726             =cut
727              
728             sub get_min_iterations_pelsser_1997 {
729 16     16 1 3142 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy ) = @_;
730              
731 16 100       36 if ( not defined $accuracy ) {
732 14         18 $accuracy = $MIN_ACCURACY_UPORDOWN_PELSSER_1997;
733             }
734              
735 16 100       50 if ( $accuracy > $MIN_ACCURACY_UPORDOWN_PELSSER_1997 ) {
    100          
736 1         4 $accuracy = $MIN_ACCURACY_UPORDOWN_PELSSER_1997;
737             }
738             elsif ( $accuracy <= 0 ) {
739 1         2 $accuracy = $MIN_ACCURACY_UPORDOWN_PELSSER_1997;
740             }
741              
742 16         26 my $h = log( $U / $D );
743 16         22 my $x = log( $S / $D );
744              
745 16         28 my $it_up =
746             _get_min_iterations_ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q, $mu,
747             $sigma, $w, $accuracy );
748 16         30 my $it_down =
749             _get_min_iterations_ot_down_ko_up_pelsser_1997( $S, $U, $D, $t, $r_q, $mu,
750             $sigma, $w, $accuracy );
751              
752 16         26 my $min = max( $it_up, $it_down );
753              
754 16         36 return $min;
755             }
756              
757             =head2 _get_min_iterations_ot_up_ko_down_pelsser_1997
758              
759             USAGE
760             my $k_min = _get_min_iterations_ot_up_ko_down_pelsser_1997($S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy)
761              
762             DESCRIPTION
763             An estimate of the number of iterations required to achieve a certain
764             level of accuracy in the price for ONETOUCH-UP-KNOCKOUT-DOWN.
765              
766             =cut
767              
768             sub _get_min_iterations_ot_up_ko_down_pelsser_1997 {
769 35     35   1175 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy ) = @_;
770              
771 35 100       61 if (!defined $accuracy) {
772 1         10 die "accuracy required";
773             }
774              
775 34         28 my $pi = Math::Trig::pi;
776              
777 34         36 my $h = log( $U / $D );
778 34         33 my $x = log( $S / $D );
779 34         41 my $mu_new = $mu - ( 0.5 * $sigma * $sigma );
780 34         60 my $mu_dash = sqrt(
781             ( $mu_new * $mu_new ) + ( 2 * $sigma * $sigma * $r_q * ( 1 - $w ) ) );
782              
783 34         31 my $A = ( $mu_dash * $mu_dash ) / ( 2 * $sigma * $sigma );
784 34         37 my $B = ( $pi * $pi * $sigma * $sigma ) / ( 2 * $h * $h );
785              
786 34         29 my $delta_dash = $accuracy;
787 34         68 my $delta =
788             $delta_dash *
789             exp( -$mu_new * ( $h - $x ) / ( $sigma * $sigma ) ) *
790             ( ( $h * $h ) / ( $pi * $sigma * $sigma ) );
791              
792             # This can happen when stability condition fails
793 34 100       67 if ( $delta * $B <= 0 ) {
794 1         28 die "(_get_min_iterations_ot_up_ko_down_pelsser_1997) Cannot "
795             . "evaluate minimum iterations because too many iterations "
796             . "required!! delta=$delta, B=$B for input parameters S=$S, "
797             . "U=$U, D=$D, t=$t, r_q=$r_q, mu=$mu, sigma=$sigma, w=$w, "
798             . "accuracy=$accuracy";
799 0         0 return $MAX_ITERATIONS_UPORDOWN_PELSSER_1997;
800             }
801              
802             # Check that condition is satisfied
803 33         64 my $condition = max( exp( -$A * $t ) / ( $B * $delta ), 1 );
804              
805 33         49 my $k_min = log($condition) / ( $B * $t );
806 33         22 $k_min = sqrt($k_min);
807              
808 33 100       51 if ( $k_min < $MIN_ITERATIONS_UPORDOWN_PELSSER_1997 ) {
    50          
809              
810 32         65 return $MIN_ITERATIONS_UPORDOWN_PELSSER_1997;
811             }
812             elsif ( $k_min > $MAX_ITERATIONS_UPORDOWN_PELSSER_1997 ) {
813              
814 1         2 return $MAX_ITERATIONS_UPORDOWN_PELSSER_1997;
815             }
816              
817 0         0 return int($k_min);
818             }
819              
820             =head2 _get_min_iterations_ot_down_ko_up_pelsser_1997
821              
822             USAGE
823              
824             DESCRIPTION
825             An estimate of the number of iterations required to achieve a certain
826             level of accuracy in the price for ONETOUCH-UP-KNOCKOUT-UP.
827              
828             =cut
829              
830             sub _get_min_iterations_ot_down_ko_up_pelsser_1997 {
831 16     16   34 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w, $accuracy ) = @_;
832              
833 16         22 my $h = log( $U / $D );
834 16         16 my $x = log( $S / $D );
835 16         21 my $mu_new = $mu - ( 0.5 * $sigma * $sigma );
836              
837 16         25 $accuracy = $accuracy * exp( $mu_new * $h / ( $sigma * $sigma ) );
838              
839 16         25 return _get_min_iterations_ot_up_ko_down_pelsser_1997( $S, $U, $D, $t, $r_q,
840             $mu, $sigma, $w, $accuracy );
841             }
842              
843             =head2 range
844              
845             USAGE
846             my $price = range($S, $U, $D, $t, $r_q, $mu, $sigma, $w)
847              
848             PARAMS
849             $S stock price
850             $t time (1 = 1 year)
851             $U barrier
852             $D barrier
853             $r_q payout currency interest rate (0.05 = 5%)
854             $mu quanto drift adjustment (0.05 = 5%)
855             $sigma volatility (0.3 = 30%)
856              
857             see [3] for $r_q and $mu for quantos
858              
859             DESCRIPTION
860             Price a range contract.
861              
862             =cut
863              
864             sub range {
865              
866             # payout time $w is only a dummy. range contracts always payout at end.
867 7     7 1 3077 my ( $S, $U, $D, $t, $r_q, $mu, $sigma, $w ) = @_;
868              
869             # range always pay out at end
870 7         12 $w = 1;
871              
872             return
873 7         31 exp( -$r_q * $t ) - upordown( $S, $U, $D, $t, $r_q, $mu, $sigma, $w );
874             }
875              
876             =head1 DEPENDENCIES
877              
878             * Math::CDF
879             * Machine::Epsilon
880              
881             =head1 SOURCE CODE
882              
883             https://github.com/binary-com/perl-math-business-blackscholes-binaries
884              
885             =head1 REFERENCES
886              
887             [1] P.G Zhang [1997], "Exotic Options", World Scientific
888             Another good refernce is Mark rubinstein, Eric Reiner [1991], "Binary Options", RISK 4, pp 75-83
889              
890             [2] Anlong Li [1999], "The pricing of double barrier options and their variations".
891             Advances in Futures and Options, 10, 1999. (paper).
892              
893             [3] Uwe Wystup. FX Options and Strutured Products. Wiley Finance, England, 2006. pp 93-96 (Quantos)
894              
895             [4] Antoon Pelsser, "Pricing Double Barrier Options: An Analytical Approach", Jan 15 1997.
896             http://repub.eur.nl/pub/7807/1997-0152.pdf
897              
898             =head1 AUTHOR
899              
900             binary.com, C<< >>
901              
902             =head1 BUGS
903              
904             Please report any bugs or feature requests to
905             C, or through the web
906             interface at
907             L.
908             I will be notified, and then you'll automatically be notified of progress on
909             your bug as I make changes.
910              
911             =head1 SUPPORT
912              
913             You can find documentation for this module with the perldoc command.
914              
915             perldoc Math::Business::BlackScholes::Binaries
916              
917              
918             You can also look for information at:
919              
920             =over 4
921              
922             =item * RT: CPAN's request tracker (report bugs here)
923              
924             L
925              
926             =item * AnnoCPAN: Annotated CPAN documentation
927              
928             L
929              
930             =item * CPAN Ratings
931              
932             L
933              
934             =item * Search CPAN
935              
936             L
937              
938             =back
939              
940              
941             =head1 LICENSE AND COPYRIGHT
942              
943             Copyright 2014 binary.com.
944              
945             This program is free software; you can redistribute it and/or modify it
946             under the terms of the the Artistic License (2.0). You may obtain a
947             copy of the full license at:
948              
949             L
950              
951             Any use, modification, and distribution of the Standard or Modified
952             Versions is governed by this Artistic License. By using, modifying or
953             distributing the Package, you accept this license. Do not use, modify,
954             or distribute the Package, if you do not accept this license.
955              
956             If your Modified Version has been derived from a Modified Version made
957             by someone other than you, you are nevertheless required to ensure that
958             your Modified Version complies with the requirements of this license.
959              
960             This license does not grant you the right to use any trademark, service
961             mark, tradename, or logo of the Copyright Holder.
962              
963             This license includes the non-exclusive, worldwide, free-of-charge
964             patent license to make, have made, use, offer to sell, sell, import and
965             otherwise transfer the Package with respect to any patent claims
966             licensable by the Copyright Holder that are necessarily infringed by the
967             Package. If you institute patent litigation (including a cross-claim or
968             counterclaim) against any party alleging that the Package constitutes
969             direct or contributory patent infringement, then this Artistic License
970             to you shall terminate on the date that such litigation is filed.
971              
972             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
973             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
974             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
975             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
976             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
977             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
978             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
979             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
980              
981              
982             =cut
983              
984             1;
985