| 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__ |