File Coverage

lib/Math/Business/BlackScholes/Binaries.pm
Criterion Covered Total %
statement 18 161 11.1
branch 0 52 0.0
condition 0 24 0.0
subroutine 6 24 25.0
pod 16 16 100.0
total 40 277 14.4


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