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   35356 use strict;
  2         4  
  2         45  
3 2     2   6 use warnings;
  2         1  
  2         84  
4              
5             our $VERSION = "0.04";
6              
7 2     2   6 use Exporter qw(import export_to_level);
  2         2  
  2         75  
8             our @EXPORT_OK = qw(gbm_sample random_normal);
9              
10 2     2   831 use Math::Random::Secure qw(rand);
  2         189930  
  2         123  
11              
12 2     2   14 use Carp qw(confess);
  2         3  
  2         432  
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 341902 my $rand = shift || \&rand;
54 100000         81613 my ($s, $t) = (0.449871, -0.386595); # Center point
55 100000         66163 my ($a, $b) = (0.19600, 0.25472);
56              
57 100000         59543 my $nv;
58 100000         119410 while (not defined $nv) {
59 136819         175154 my ($u, $v) = ($rand->(), 1.7156 * ($rand->() - 0.5));
60 136819         17142000 my $x = $u - $s;
61 136819         103238 my $y = abs($v) - $t;
62 136819         157363 my $Q = $x**2 + $y * ($a * $y - $b * $x);
63 136819 100       185715 if ($Q >= 0.27597) {
64 37440 100 100     93798 next if ($Q > 0.27846 || $v**2 > -4 * $u**2 * log($u));
65             }
66 100000         149386 $nv = $v / $u;
67             }
68              
69 100000         147814 return $nv;
70             }
71              
72             =back
73              
74             =head2 gbm_sample($price, $vol, $t, $r, $q, $rand)
75              
76             Generates a random sample price of a stock following Geometric Brownian Motion after t years.
77              
78             =over 4
79              
80             =item I<$price>
81              
82             is the value of the stock initially
83              
84             =item I<$vol>
85              
86             is the annual volatility of the stock
87              
88             =item I<$t>
89              
90             is the time elapsed in years
91              
92             =item I<$r>
93              
94             is the annualized drift rate
95              
96             =item I<$q>
97              
98             is the annualized dividend rate
99              
100             =item I<$rand>
101              
102             custom rand generated if not passed will use Math::Random::Secure::rand
103              
104             =back
105              
106             note: all rates are taken as decimals (.06 for 6%)
107              
108             =cut
109              
110             sub gbm_sample {
111 0     0 1   my ($price, $vol, $time, $r, $q, $rand) = @_;
112              
113             confess('All parameters are required to be set: generate_gbm($price, $annualized_vol, $time_in_years, $r_rate, $q_rate)')
114 0 0         if grep { not defined $_ } ($price, $vol, $time, $r, $q);
  0            
115              
116 0           return $price * exp(($r - $q - $vol * $vol / 2) * $time + $vol * sqrt($time) * random_normal($rand));
117             }
118              
119             1;
120              
121             __END__