File Coverage

blib/lib/Math/Business/LaguerreFilter.pm
Criterion Covered Total %
statement 74 95 77.8
branch 29 42 69.0
condition 4 6 66.6
subroutine 11 15 73.3
pod 0 10 0.0
total 118 168 70.2


line stmt bran cond sub pod time code
1             package Math::Business::LaguerreFilter;
2              
3 2     2   32649 use strict;
  2         4  
  2         79  
4 2     2   17 use warnings;
  2         9  
  2         143  
5 2     2   13 use Carp;
  2         6  
  2         169  
6             use constant {
7 2         1609 ALPHA => 2,
8             LENGTH => 3,
9             F => -2,
10             TAG => -1,
11 2     2   11 };
  2         4  
12              
13             1;
14              
15 0     0 0 0 sub tag { (shift)->[TAG] }
16              
17 0     0 0 0 sub recommended { croak "no recommendation" }
18              
19             sub dnew {
20 0     0 0 0 my $class = shift;
21 0 0       0 my $days = int shift; $days = 4 unless $days > 1;
  0         0  
22 0         0 my $this = $class->new(2/(1+$days));
23              
24 0         0 return $this;
25             }
26              
27             sub new {
28 4     4 0 1087 my $class = shift;
29 4         17 my $this = bless [
30             [], # [0] P-hist
31             [], # [1] L0-L4
32             0, # [2] alpha
33             0, # [3] adaptive length
34             [], # [4] adaptive diff history
35             undef, # [5] filter
36             undef, # [6] tag
37             ], $class;
38              
39 4         9 my $alpha = shift;
40 4 100       17 $alpha = 0.5 if not defined $alpha;
41 4         21 $this->set_alpha( $alpha );
42              
43 4         6 my $length = shift;
44 4 100       10 if( defined $length ) {
45 1         5 $this->set_adaptive( $length );
46             }
47              
48 4         11 return $this;
49             }
50              
51             sub reset {
52 6     6 0 11 my $this = shift;
53 6         13 $this->[0] = [];
54 6         9 $this->[1] = [];
55 6         9 $this->[4] = [];
56             return
57 6         12 }
58              
59             sub set_days {
60 0     0 0 0 my $this = shift;
61 0         0 my $arg = 0+shift;
62              
63 0         0 my $alpha = 2/(1+$arg);
64 0         0 eval { $this->set_alpha( $alpha ) };
  0         0  
65 0 0       0 croak "set_days() is basically set_alpha(2/(1+$arg)), which complained: $@" if $@;
66              
67 0         0 $this->[TAG] = "LAG($arg)";
68 0         0 $this->reset
69             }
70              
71             sub set_alpha {
72 4     4 0 8 my $this = shift;
73 4         13 my $alpha = 0+shift;
74              
75 4 50 33     26 croak "alpha must be a real between >=0 and <=1" unless $alpha >= 0 and $alpha <= 1;
76              
77 4         17 $this->[2] = $alpha;
78              
79 4         15 my $arg = int ( (1/$alpha)*2-1 ); # pretty sure... gah, algebra
80              
81 4         11 $this->[TAG] = "LAG($arg)";
82 4         14 $this->reset
83             }
84              
85             sub set_adaptive {
86 2     2 0 10 my $this = shift;
87 2         3 my $that = int shift;
88              
89 2 50       9 croak "adaptive length must be an non-negative integer" unless $that >= 0;
90 2         4 $this->[LENGTH] = $that;
91 2         6 $this->reset
92             }
93              
94             sub insert {
95 7002     7002 0 370529 my $this = shift;
96 7002         16246 my ($h, $L, $alpha, $length, $diff, $filter) = @$this;
97              
98 7002 50       16865 croak "You must set the number of days before you try to insert" if not defined $alpha;
99 2     2   16 no warnings 'uninitialized';
  2         5  
  2         1433  
100              
101 7002         15372 while( defined( my $P = shift ) ) {
102 7002 50       12699 if( ref $P ) {
103 0 0       0 my @a = eval {@$P}; croak $@ if $@;
  0         0  
  0         0  
104 0         0 my $c = 0+@a;
105 0 0       0 croak "high+low should only be two elements, not c=$c" unless $c == 2;
106 0         0 $P = ($a[0]+$a[1])/$c;
107             }
108              
109 7002 100       12697 if( not defined $L->[0] ) {
110              
111 2         15 $L->[0] = $P;
112              
113             } else {
114              
115             # adapt alpha {{{
116 7000 100 100     20888 if( $length and defined($filter) ) {
117 3497         10059 my $d = abs($P-$filter);
118 3497         7675 push @$diff, $d;
119              
120 3497         8001 my $k = @$diff - $length;
121 3497 100       13569 splice @$diff, 0, $k if $k>0;
122              
123 3497 100       7820 if( $k > 0 ) { # NOTE Ehler really does this, "CurrentBar > Length". See below.
124             # IE, $k will only by >0 when we've moved past the 20th point
125 3477         8900 my $HH = $d;
126 3477         5724 my $LL = $d;
127              
128 3477         7142 for(@$diff) {
129 69540 100       155601 $HH = $_ if $_ > $HH;
130 69540 100       191116 $LL = $_ if $_ < $LL;
131             }
132              
133 3477 50       16751 if( $HH != $LL ) {
134             # Ehler: If CurrentBar > Length and HH - LL <> 0 then alpha = Median(((Diff - LL) / (HH - LL)), 5);
135              
136             # NOTE: wtf is a "5 bar median"? I guess it's this, or
137             # pretty close to it. I imagine Median() runs through
138             # the [] hist for Diff, LL, and HH, but I can't say for
139             # sure without access to the programming language he
140             # uses in the book.
141              
142             # AVG # my $sum = ($diff->[-5]-$LL)/($HH-$LL);
143             # AVG # $sum += ($diff->[$_]-$LL)/($HH-$LL) for (-4 .. -1);
144              
145             # AVG # ($this->[ALPHA] = $alpha = $sum / 5);
146              
147             # NOTE (later): he appears to mean the median (not
148             # average) of a scalar $HH/$LL against the last 5 @diff
149              
150 3477         9561 my @b5 = sort {$a<=>$b}map {(($diff->[$_]-$LL)/($HH-$LL))} -5 .. -1;
  25903         67072  
  17385         67209  
151              
152 3477         11606 $this->[ALPHA] = $alpha = $b5[2];
153             }
154             }
155             }
156             # }}}
157              
158 7000         15171 my $O = [ @$L ];
159              
160             # L0 = alpha*Price + (1 - alpha)*L0[1] = alpha*P + (1-alpha)*O[0]
161              
162 7000         17509 $L->[0] = $alpha*$P + (1-$alpha)*$O->[0];
163              
164             # L1 = (1 - alpha)*L1[1] - (1 - alpha)*L0 + L0[1] = (1 - alpha)*O[1] - (1 - alpha)*L[0] + O[0]
165             # L2 = (1 - alpha)*L2[1] - (1 - alpha)*L1 + L1[1] = (1 - alpha)*O[2] - (1 - alpha)*L[1] + O[1]
166             # L3 = (1 - alpha)*L3[1] - (1 - alpha)*L2 + L2[1] = (1 - alpha)*O[3] - (1 - alpha)*L[2] + O[2]
167              
168 7000 100       15633 $L->[1] = defined($O->[1]) ? (1 - $alpha)*$O->[1] - (1 - $alpha)*$L->[0] + $O->[0] : $O->[0];
169 7000 100       15443 $L->[2] = defined($O->[2]) ? (1 - $alpha)*$O->[2] - (1 - $alpha)*$L->[1] + $O->[1] : $O->[1];
170 7000 100       24526 $L->[3] = defined($O->[3]) ? (1 - $alpha)*$O->[3] - (1 - $alpha)*$L->[2] + $O->[2] : $O->[2];
171              
172             }
173             }
174              
175 7002 100       14855 if( 4 == grep {defined $_} @$L ) {
  28002         59866  
176 6996         22460 $this->[F] = ($L->[0] + 2*$L->[1] + 2*$L->[2] + $L->[3])/6;
177             }
178             }
179              
180             sub query {
181 7002     7002 0 24256 my $this = shift;
182              
183 7002         18018 return $this->[F];
184             }
185              
186             __END__