File Coverage

blib/lib/Math/Random/Normal/Leva.pm
Criterion Covered Total %
statement 28 32 87.5
branch 4 6 66.6
condition 4 5 80.0
subroutine 6 7 85.7
pod 2 2 100.0
total 44 52 84.6


line stmt bran cond sub pod time code
1             package Math::Random::Normal::Leva;
2 2     2   43069 use strict;
  2         3  
  2         57  
3 2     2   8 use warnings;
  2         2  
  2         87  
4              
5             our $VERSION = "0.03";
6              
7 2     2   8 use Exporter qw(import export_to_level);
  2         6  
  2         89  
8             our @EXPORT_OK = qw(gbm_sample random_normal);
9              
10 2     2   1056 use Math::Random::Secure qw(rand);
  2         231872  
  2         156  
11              
12 2     2   15 use Carp qw(confess);
  2         3  
  2         611  
13              
14             =head1 NAME
15              
16             Math::Random::Normal::Leva - generate normally distributed PRN using Leva method
17              
18             =head1 VERSION
19              
20             This document describes Math::Random::Normal::Leva version 0.02
21              
22             =head1 SYNOPSIS
23              
24             use Math::Random::Normal::Leva;
25             my @normal = map { random_normal() } 1..1000;
26              
27             =head1 DESCRIPTION
28              
29             Generates normally distributed pseudorandom numbers using algorithm described
30             in the paper "A Fast Normal Random Number Generator", Joseph L. Leva, 1992
31             (L)
32              
33             =head1 FUNCTIONS
34              
35             =cut
36              
37             =head2 random_normal($rand)
38              
39             Returns a random number sampled from the normal distribution.
40              
41             =over 4
42              
43             =item I<$rand>
44              
45             is the value of the stock initially
46              
47             =cut
48              
49             # This algorithm comes from the paper
50             # "A Fast Normal Random Number Generator" (Leva, 1992)
51              
52             sub random_normal {
53 100000   50 100000 1 405325 my $rand = shift || \&rand;
54 100000         91713 my ( $s, $t ) = ( 0.449871, -0.386595 ); # Center point
55 100000         78507 my ( $a, $b ) = ( 0.19600, 0.25472 );
56              
57 100000         71222 my $nv;
58 100000         148798 while ( not defined $nv ) {
59 137289         197450 my ( $u, $v ) = ( $rand->(), 1.7156 * ( $rand->() - 0.5 ) );
60 137289         20775602 my $x = $u - $s;
61 137289         137594 my $y = abs($v) - $t;
62 137289         189379 my $Q = $x**2 + $y * ( $a * $y - $b * $x );
63 137289 100       214919 if ( $Q >= 0.27597 ) {
64 37895 100 100     111481 next if ( $Q > 0.27846 || $v**2 > -4 * $u**2 * log($u) );
65             }
66 100000         186471 $nv = $v / $u;
67             }
68              
69 100000         185375 return $nv;
70             }
71              
72             =head2 gbm_sample($price, $vol, $t, $r, $q, $rand)
73              
74             Generates a random sample price of a stock following Geometric Brownian Motion after t years.
75              
76             =over 4
77              
78             =item I<$price>
79              
80             is the value of the stock initially
81              
82             =item I<$vol>
83              
84             is the annual volatility of the stock
85              
86             =item I<$t>
87              
88             is the time elapsed in years
89              
90             =item I<$r>
91              
92             is the annualized drift rate
93              
94             =item I<$q>
95              
96             is the annualized dividend rate
97              
98             =item I<$rand>
99              
100             custom rand generated if not passed will use Math::Random::Secure::rand
101              
102             =back
103              
104             note: all rates are taken as decimals (.06 for 6%)
105              
106             =cut
107              
108             sub gbm_sample {
109 0     0 1   my ( $price, $vol, $time, $r, $q, $rand ) = @_;
110              
111             confess(
112             'All parameters are required to be set: generate_gbm($price, $annualized_vol, $time_in_years, $r_rate, $q_rate)'
113 0 0         ) if grep { not defined $_ } ( $price, $vol, $time, $r, $q );
  0            
114              
115 0           return $price *
116             exp( ( $r - $q - $vol * $vol / 2 ) * $time + $vol * sqrt($time) * random_normal($rand) );
117             }
118              
119             1;
120              
121             __END__