File Coverage

blib/lib/Math/Business/LaguerreFilter.pm
Criterion Covered Total %
statement 66 87 75.8
branch 27 42 64.2
condition 4 6 66.6
subroutine 10 14 71.4
pod 0 9 0.0
total 107 158 67.7


line stmt bran cond sub pod time code
1             package Math::Business::LaguerreFilter;
2              
3 2     2   16152 use strict;
  2         6  
  2         88  
4 2     2   11 use warnings;
  2         3  
  2         62  
5 2     2   11 use Carp;
  2         3  
  2         242  
6             use constant {
7 2         1514 ALPHA => 2,
8             LENGTH => 3,
9             F => -2,
10             TAG => -1,
11 2     2   13 };
  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 2     2 0 1111 my $class = shift;
29 2         11 my $this = bless [], $class;
30              
31 2         8 my $alpha = shift;
32 2 50       13 if( defined $alpha ) {
33 2         12 $this->set_alpha( $alpha );
34             }
35              
36 2         5 my $length = shift;
37 2 50       10 if( defined $length ) {
38 0         0 $this->set_adaptive( $length );
39             }
40              
41 2         8 return $this;
42             }
43              
44             sub set_days {
45 0     0 0 0 my $this = shift;
46 0         0 my $arg = 0+shift;
47              
48 0         0 my $alpha = 2/(1+$arg);
49 0         0 eval { $this->set_alpha( $alpha ) };
  0         0  
50 0 0       0 croak "set_days() is basically set_alpha(2/(1+$arg)), which complained: $@" if $@;
51              
52 0         0 $this->{tag} = "LAG($arg)";
53             }
54              
55             sub set_alpha {
56 2     2 0 6 my $this = shift;
57 2         9 my $alpha = 0+shift;
58              
59 2 50 33     32 croak "alpha must be a real between >=0 and <=1" unless $alpha >= 0 and $alpha <= 1;
60 2         26 @$this = (
61             [], # P-hist
62             [], # L0-L4
63             $alpha,
64             0, # adaptive length
65             [], # adaptive diff history
66             undef, # filter
67             undef, # tag
68             );
69              
70 2         11 my $arg = int ( (1/$alpha)*2-1 ); # pretty sure... gah, algebra
71 2         14 $this->[TAG] = "LAG($arg)";
72             }
73              
74             sub set_adaptive {
75 1     1 0 7 my $this = shift;
76 1         2 my $that = int shift;
77              
78 1 50       5 croak "adaptive length must be an non-negative integer" unless $that >= 0;
79 1         4 $this->[LENGTH] = $that;
80             }
81              
82             sub insert {
83 7002     7002 0 440731 my $this = shift;
84 7002         15784 my ($h, $L, $alpha, $length, $diff, $filter) = @$this;
85              
86 7002 50       15901 croak "You must set the number of days before you try to insert" if not defined $alpha;
87 2     2   22 no warnings 'uninitialized';
  2         3  
  2         1212  
88              
89 7002         17299 while( defined( my $P = shift ) ) {
90 7002 50       15671 if( ref $P ) {
91 0 0       0 my @a = eval {@$P}; croak $@ if $@;
  0         0  
  0         0  
92 0         0 my $c = 0+@a;
93 0 0       0 croak "high+low should only be two elements, not c=$c" unless $c == 2;
94 0         0 $P = ($a[0]+$a[1])/$c;
95             }
96              
97 7002 100       13389 if( defined $L->[0] ) {
98             # adapt alpha {{{
99 7000 100 100     30071 if( $length and defined($filter) ) {
100 3497         7266 my $d = abs($P-$filter);
101 3497         7325 push @$diff, $d;
102              
103 3497         5898 my $k = @$diff - $length;
104 3497 100       10464 splice @$diff, 0, $k if $k>0;
105              
106 3497 100       7226 if( $k > 0 ) { # NOTE Ehler really does this, "CurrentBar > Length". See below.
107             # IE, $k will only by >0 when we've moved past the 20th point
108 3477         5354 my $HH = $d;
109 3477         4087 my $LL = $d;
110              
111 3477         6547 for(@$diff) {
112 69540 100       178189 $HH = $_ if $_ > $HH;
113 69540 100       182869 $LL = $_ if $_ < $LL;
114             }
115              
116 3477 50       10029 if( $HH != $LL ) {
117             # Ehler: If CurrentBar > Length and HH - LL <> 0 then alpha = Median(((Diff - LL) / (HH - LL)), 5);
118              
119             # NOTE: wtf is a "5 bar median"? I guess it's this, or
120             # pretty close to it. I imagine Median() runs through
121             # the [] hist for Diff, LL, and HH, but I can't say for
122             # sure without access to the programming language he
123             # uses in the book.
124              
125             # AVG # my $sum = ($diff->[-5]-$LL)/($HH-$LL);
126             # AVG # $sum += ($diff->[$_]-$LL)/($HH-$LL) for (-4 .. -1);
127              
128             # AVG # ($this->[ALPHA] = $alpha = $sum / 5);
129              
130             # NOTE (later): he appears to mean the median (not
131             # average) of a scalar $HH/$LL against the last 5 @diff
132              
133 3477         6240 my @b5 = sort {$a<=>$b}map {(($diff->[$_]-$LL)/($HH-$LL))} -5 .. -1;
  25903         62410  
  17385         60792  
134              
135 3477         13568 $this->[ALPHA] = $alpha = $b5[2];
136             }
137             }
138             }
139             # }}}
140              
141 7000         18450 my $O = [ @$L ];
142              
143             # L0 = alpha*Price + (1 - alpha)*L0[1] = alpha*P + (1-alpha)*O[0]
144              
145 7000         15725 $L->[0] = $alpha*$P + (1-$alpha)*$O->[0];
146              
147             # L1 = (1 - alpha)*L1[1] - (1 - alpha)*L0 + L0[1] = (1 - alpha)*O[1] - (1 - alpha)*L[0] + O[0]
148             # L2 = (1 - alpha)*L2[1] - (1 - alpha)*L1 + L1[1] = (1 - alpha)*O[2] - (1 - alpha)*L[1] + O[1]
149             # L3 = (1 - alpha)*L3[1] - (1 - alpha)*L2 + L2[1] = (1 - alpha)*O[3] - (1 - alpha)*L[2] + O[2]
150              
151 7000 100       23743 $L->[1] = defined($O->[1]) ? (1 - $alpha)*$O->[1] - (1 - $alpha)*$L->[0] + $O->[0] : $O->[0];
152 7000 100       17792 $L->[2] = defined($O->[2]) ? (1 - $alpha)*$O->[2] - (1 - $alpha)*$L->[1] + $O->[1] : $O->[1];
153 7000 100       39691 $L->[3] = defined($O->[3]) ? (1 - $alpha)*$O->[3] - (1 - $alpha)*$L->[2] + $O->[2] : $O->[2];
154              
155             } else {
156 2         12 $L->[0] = $P;
157             }
158             }
159              
160 7002 100       11622 if( 4 == grep {defined $_} @$L ) {
  28002         70678  
161 6996         36328 $this->[F] = ($L->[0] + 2*$L->[1] + 2*$L->[2] + $L->[3])/6;
162             }
163             }
164              
165             sub query {
166 7002     7002 0 36438 my $this = shift;
167              
168 7002         19915 return $this->[F];
169             }
170              
171             __END__