line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright 2007, 2009, 2010 Kevin Ryde |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This file is part of Chart. |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Chart is free software; you can redistribute it and/or modify it under the |
6
|
|
|
|
|
|
|
# terms of the GNU General Public License as published by the Free Software |
7
|
|
|
|
|
|
|
# Foundation; either version 3, or (at your option) any later version. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# Chart is distributed in the hope that it will be useful, but WITHOUT ANY |
10
|
|
|
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
11
|
|
|
|
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
12
|
|
|
|
|
|
|
# details. |
13
|
|
|
|
|
|
|
# |
14
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License along |
15
|
|
|
|
|
|
|
# with Chart. If not, see <http://www.gnu.org/licenses/>. |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
package App::Chart::Series::Derived::LaguerreFilter; |
18
|
1
|
|
|
1
|
|
401
|
use 5.010; |
|
1
|
|
|
|
|
3
|
|
19
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
17
|
|
20
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
21
|
1
|
|
|
1
|
|
3
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
22
|
1
|
|
|
1
|
|
4
|
use List::Util qw(min max); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
23
|
1
|
|
|
1
|
|
301
|
use Locale::TextDomain ('App-Chart'); |
|
1
|
|
|
|
|
16950
|
|
|
1
|
|
|
|
|
7
|
|
24
|
|
|
|
|
|
|
|
25
|
1
|
|
|
1
|
|
5154
|
use base 'App::Chart::Series::Indicator'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
350
|
|
26
|
|
|
|
|
|
|
use App::Chart::Series::Calculation; |
27
|
|
|
|
|
|
|
use App::Chart::Series::Derived::EMA; |
28
|
|
|
|
|
|
|
use App::Chart::Series::Derived::EMAx2; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# http://www.mesasoftware.com/technicalpapers.htm |
31
|
|
|
|
|
|
|
# http://www.mesasoftware.com/Papers/TIME%20WARP.pdf |
32
|
|
|
|
|
|
|
# Paper by John Elhers. |
33
|
|
|
|
|
|
|
# |
34
|
|
|
|
|
|
|
# http://www.mesasoftware.com/seminars.htm |
35
|
|
|
|
|
|
|
# http://www.mesasoftware.com/Seminars/TradeStation%20World%2005.pdf |
36
|
|
|
|
|
|
|
# http://www.mesasoftware.com/Seminars/Seminars/TSWorld05.ppt |
37
|
|
|
|
|
|
|
# (View the powerpoint with google.) |
38
|
|
|
|
|
|
|
# Summary by John Ehlers of several of his and other averages. |
39
|
|
|
|
|
|
|
# * A Laguerre filter warps time in the filter coefficients |
40
|
|
|
|
|
|
|
# - Enables extreme smoothing with just a few filter terms |
41
|
|
|
|
|
|
|
# * A NonLinear Laguerre filter measures the difference between the |
42
|
|
|
|
|
|
|
# current price and the last computed filter output. |
43
|
|
|
|
|
|
|
# - Objective is to drive this "error" to zero |
44
|
|
|
|
|
|
|
# - The "error", normalized to the error range over a selected period |
45
|
|
|
|
|
|
|
# is the alpha of the Laguerre filter |
46
|
|
|
|
|
|
|
# |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub longname { __('Laguerre Filter') } |
50
|
|
|
|
|
|
|
sub shortname { __('Laguerre') } |
51
|
|
|
|
|
|
|
sub manual { __p('manual-node','Laguerre Filter') } |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
use constant |
54
|
|
|
|
|
|
|
{ type => 'average', |
55
|
|
|
|
|
|
|
parameter_info => [ { name => __('Alpha'), |
56
|
|
|
|
|
|
|
key => 'laguerre_filter_alpha', |
57
|
|
|
|
|
|
|
type => 'float', |
58
|
|
|
|
|
|
|
minimum => 0, |
59
|
|
|
|
|
|
|
maximum => 1, |
60
|
|
|
|
|
|
|
default => 0.2, |
61
|
|
|
|
|
|
|
decimals => 2, |
62
|
|
|
|
|
|
|
step => 0.1 } ], |
63
|
|
|
|
|
|
|
}; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub new { |
66
|
|
|
|
|
|
|
my ($class, $parent, $alpha) = @_; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
$alpha //= parameter_info()->[0]->{'default'}; |
69
|
|
|
|
|
|
|
($alpha >= 0 && $alpha <= 1.0) || croak "Laguerre Filter bad alpha: $alpha"; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
return $class->SUPER::new |
72
|
|
|
|
|
|
|
(parent => $parent, |
73
|
|
|
|
|
|
|
parameters => [ $alpha ], |
74
|
|
|
|
|
|
|
arrays => { values => [] }, |
75
|
|
|
|
|
|
|
array_aliases => { }); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub proc { |
79
|
|
|
|
|
|
|
my ($class, $alpha) = @_; |
80
|
|
|
|
|
|
|
$alpha = max (0.00001, min (0.99999, $alpha)); |
81
|
|
|
|
|
|
|
my $proc_for_alpha = $class->proc_for_alpha(); |
82
|
|
|
|
|
|
|
return sub { |
83
|
|
|
|
|
|
|
my ($value) = @_; |
84
|
|
|
|
|
|
|
return $proc_for_alpha->($value, $alpha); |
85
|
|
|
|
|
|
|
}; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub proc_for_alpha { |
89
|
|
|
|
|
|
|
my ($class) = @_; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
my $a_sum = 0; |
92
|
|
|
|
|
|
|
my $a_div = 0; |
93
|
|
|
|
|
|
|
my ($a_prev, $a_pdiv); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my $b_sum = 0; |
96
|
|
|
|
|
|
|
my $b_div = 0; |
97
|
|
|
|
|
|
|
my ($b_prev, $b_pdiv); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
my $c_sum = 0; |
100
|
|
|
|
|
|
|
my $c_div = 0; |
101
|
|
|
|
|
|
|
my ($c_prev, $c_pdiv); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
my $d_sum = 0; |
104
|
|
|
|
|
|
|
my $d_div = 0; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
return sub { |
107
|
|
|
|
|
|
|
my ($value, $alpha) = @_; |
108
|
|
|
|
|
|
|
my $f = 1 - $alpha; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
$a_prev = $a_sum; |
111
|
|
|
|
|
|
|
$a_pdiv = $a_div; |
112
|
|
|
|
|
|
|
$a_sum = $value * $alpha + $a_sum * $f; |
113
|
|
|
|
|
|
|
$a_div = $alpha + $a_div * $f; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$b_prev = $b_sum; |
116
|
|
|
|
|
|
|
$b_pdiv = $b_div; |
117
|
|
|
|
|
|
|
$b_sum = $a_prev + ($b_sum - $a_sum) * $f; |
118
|
|
|
|
|
|
|
$b_div = $a_pdiv + ($b_div - $a_div) * $f; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
$c_prev = $c_sum; |
121
|
|
|
|
|
|
|
$c_pdiv = $c_div; |
122
|
|
|
|
|
|
|
$c_sum = $b_prev + ($c_sum - $b_sum) * $f; |
123
|
|
|
|
|
|
|
$c_div = $b_pdiv + ($c_div - $b_div) * $f; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
$d_sum = $c_prev + ($d_sum - $c_sum) * $f; |
126
|
|
|
|
|
|
|
$d_div = $c_pdiv + ($d_div - $c_div) * $f; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
return ($a_sum/$a_div |
129
|
|
|
|
|
|
|
+ 2 * $b_sum/$b_div |
130
|
|
|
|
|
|
|
+ 2 * $c_sum/$c_div |
131
|
|
|
|
|
|
|
+ $d_sum/$d_div) / 6.0; |
132
|
|
|
|
|
|
|
}; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# warmup_count() gives a fixed amount, based on the worst-case EMA alphas |
136
|
|
|
|
|
|
|
# all the slowest possible. It ends up being 1656 which is hugely more than |
137
|
|
|
|
|
|
|
# needed in practice. |
138
|
|
|
|
|
|
|
# |
139
|
|
|
|
|
|
|
# warmup_count_for_position() calculates a value on actual data, working |
140
|
|
|
|
|
|
|
# backwards. In practice it's as little as about 100. |
141
|
|
|
|
|
|
|
# |
142
|
|
|
|
|
|
|
sub warmup_count { |
143
|
|
|
|
|
|
|
my ($self_or_class, $alpha) = @_; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
$alpha = max (0.00001, min (0.99999, $alpha)); |
146
|
|
|
|
|
|
|
my $f = 1 - $alpha; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
return App::Chart::Series::Derived::EMAx2::bsearch_first_true |
149
|
|
|
|
|
|
|
(sub { |
150
|
|
|
|
|
|
|
my ($i) = @_; |
151
|
|
|
|
|
|
|
return (laguerre_omitted($f,$i) |
152
|
|
|
|
|
|
|
<= App::Chart::Series::Derived::EMA::WARMUP_OMITTED_FRACTION); |
153
|
|
|
|
|
|
|
}, |
154
|
|
|
|
|
|
|
App::Chart::Series::Derived::EMA::alpha_to_N($alpha)); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# see devel/ema-omitted.pl |
158
|
|
|
|
|
|
|
sub laguerre_omitted { |
159
|
|
|
|
|
|
|
my ($f, $k) = @_; |
160
|
|
|
|
|
|
|
return |
161
|
|
|
|
|
|
|
$f ** ($k-2) |
162
|
|
|
|
|
|
|
* ((1/36*$k**2 + -1/36)*$k |
163
|
|
|
|
|
|
|
+ ($f # f^($k-1) |
164
|
|
|
|
|
|
|
* ((1/4*$k + 1/4)*$k |
165
|
|
|
|
|
|
|
+ ($f # f^$k |
166
|
|
|
|
|
|
|
* ((((-1/12*$k + -1/6)*$k + 3/4)*$k + 5/6) |
167
|
|
|
|
|
|
|
+ ($f # f^($k+1) |
168
|
|
|
|
|
|
|
* (((-1/2*$k + -1)*$k + 1/2) |
169
|
|
|
|
|
|
|
+ ($f # f^($k+2) |
170
|
|
|
|
|
|
|
* ((((1/12*$k + 1/3)*$k + -5/12)*$k + -2/3) |
171
|
|
|
|
|
|
|
+ ($f # f^($k+3) |
172
|
|
|
|
|
|
|
* (((1/4*$k + 3/4)*$k + 1/2) |
173
|
|
|
|
|
|
|
+ ($f # f^($k+4) |
174
|
|
|
|
|
|
|
* ((((-1/36*$k + -1/6)*$k + -11/36)*$k + -1/6) |
175
|
|
|
|
|
|
|
))))))))))))); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
1; |
179
|
|
|
|
|
|
|
__END__ |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# =head1 NAME |
182
|
|
|
|
|
|
|
# |
183
|
|
|
|
|
|
|
# App::Chart::Series::Derived::LaguerreFilter -- Laguerre Filter moving average |
184
|
|
|
|
|
|
|
# |
185
|
|
|
|
|
|
|
# =head1 SYNOPSIS |
186
|
|
|
|
|
|
|
# |
187
|
|
|
|
|
|
|
# my $series = $parent->LaguerreFilter($alpha); |
188
|
|
|
|
|
|
|
# |
189
|
|
|
|
|
|
|
# =head1 DESCRIPTION |
190
|
|
|
|
|
|
|
# |
191
|
|
|
|
|
|
|
# ... |
192
|
|
|
|
|
|
|
# |
193
|
|
|
|
|
|
|
# =head1 SEE ALSO |
194
|
|
|
|
|
|
|
# |
195
|
|
|
|
|
|
|
# L<App::Chart::Series>, L<App::Chart::Series::Derived::EMA> |
196
|
|
|
|
|
|
|
# |
197
|
|
|
|
|
|
|
# =cut |