File Coverage

blib/lib/Math/Business/SM/Stochastic.pm
Criterion Covered Total %
statement 163 202 80.6
branch 32 56 57.1
condition 38 68 55.8
subroutine 22 25 88.0
pod 0 17 0.0
total 255 368 69.2


line stmt bran cond sub pod time code
1             package Math::Business::SM::Stochastic;
2              
3 1     1   3472 use strict;
  1         5  
  1         19  
4 1     1   3 use warnings;
  1         1  
  1         15  
5 1     1   3 use Carp;
  1         2  
  1         31  
6              
7 1     1   285 use Math::Business::SMA;
  1         2  
  1         40  
8              
9             1;
10              
11 0     0 0 0 sub tag { (shift)->{tag} }
12              
13 1     1   4 use constant METHOD_LANE => 0;
  1         1  
  1         79  
14 1     1   4 use constant METHOD_FAST => 1;
  1         1  
  1         28  
15 1     1   3 use constant METHOD_SLOW => 2;
  1         1  
  1         26  
16 1     1   3 use constant METHOD_FULL => 3;
  1         1  
  1         1439  
17              
18 0     0 0 0 sub recommended { my $class = shift; return $class->new(METHOD_LANE,5,3); }
  0         0  
19              
20 1   50 1 0 4 sub method_slow { my $class = shift; return $class->new(METHOD_SLOW,$_[0]||14,$_[1]||3); }
  1   50     3  
21 1   50 1 0 405 sub method_fast { my $class = shift; return $class->new(METHOD_FAST,$_[0]||14,$_[1]||3); }
  1   50     6  
22 1   50 1 0 4 sub method_full { my $class = shift; return $class->new(METHOD_FULL,$_[0]||14,$_[1]||3,$_[2]||3); }
  1   50     5  
      50        
23              
24             sub new {
25 3     3 0 4 my $class = shift;
26 3   50     6 my $meth = shift || METHOD_LANE;
27 3   50     5 my $kp = shift || 5;
28 3   50     5 my $dp = shift || 3;
29 3   100     7 my $xp = shift || 3;
30              
31 3         7 my $this = bless {}, $class;
32              
33 3         4 $this->set_method( $meth );
34 3         6 $this->set_days( $kp );
35 3         6 $this->set_dperiod( $dp );
36 3         6 $this->set_xperiod( $xp );
37              
38 3         6 return $this;
39             }
40              
41             sub set_days {
42 3     3 0 2 my $this = shift;
43 3         3 my $arg = shift;
44              
45 3 50       5 croak "days must be a positive non-zero integer" if $arg <= 0;
46              
47 3         3 $this->{kp} = $arg;
48 3         5 $this->set_tag;
49             }
50              
51             sub set_dperiod {
52 3     3 0 3 my $this = shift;
53 3         2 my $arg = shift;
54              
55 3 50       5 croak "days must be a positive non-zero integer" if $arg <= 0;
56              
57 3         3 $this->{dp} = $arg;
58 3         3 $this->set_tag;
59             }
60              
61             sub set_xperiod {
62 3     3 0 2 my $this = shift;
63 3         4 my $arg = shift;
64              
65 3 50       4 croak "days must be a positive non-zero integer" if $arg <= 0;
66              
67 3         4 $this->{xp} = $arg;
68 3         3 $this->set_tag;
69             }
70              
71             sub set_method {
72 3     3 0 3 my $this = shift;
73 3         4 my $meth = shift;
74              
75 3 50       4 croak "method not known" unless grep {$meth == $_} (METHOD_LANE, METHOD_FAST, METHOD_SLOW, METHOD_FULL);
  12         18  
76              
77 3         7 $this->{m} = $meth;
78              
79 3         3 delete $this->{ksma};
80 3         4 delete $this->{dsma};
81              
82 3         5 $this->set_tag;
83              
84 3         3 return;
85             }
86              
87             sub set_tag {
88 12     12 0 11 my $this = shift;
89              
90 12 100       13 return if grep { not defined } @$this{qw(kp dp xp)};
  36         51  
91              
92 3 100       7 if( $this->{m} == METHOD_FULL ) {
    100          
    50          
93 1         4 $this->{tag} = "FullSTO($this->{kp},$this->{dp},$this->{xp})";
94              
95             } elsif( $this->{m} == METHOD_FAST ) {
96 1         5 $this->{tag} = "FSTO($this->{kp},$this->{dp})";
97              
98             } elsif( $this->{m} == METHOD_SLOW ) {
99 1         11 $this->{tag} = "SSTO($this->{kp},$this->{dp})";
100              
101             } else {
102 0         0 $this->{tag} = "STO($this->{kp},$this->{dp})";
103             }
104             }
105              
106             {
107             my $method = {
108             METHOD_LANE() => \&insert_lane,
109             METHOD_FAST() => \&insert_fast,
110             METHOD_SLOW() => \&insert_slow,
111             METHOD_FULL() => \&insert_full,
112             };
113              
114             sub insert {
115 90     90 0 1815 my $this = shift;
116 90         108 my $m = $method->{$this->{m}};
117              
118 90         119 return $this->$m( @_ );
119             }
120             }
121              
122             # {{{ sub insert_lane
123             sub insert_lane {
124 0     0 0 0 my $this = shift;
125              
126 0   0     0 my $l = ($this->{low_hist} ||= []);
127 0   0     0 my $h = ($this->{high_hist} ||= []);
128 0         0 my $kp = $this->{kp};
129 0         0 my $dp = $this->{dp};
130              
131 0         0 my ($K, $D);
132 0         0 while( defined( my $point = shift ) ) {
133 0 0 0     0 croak "insert takes three tuple (high, low, close)" unless ref $point eq "ARRAY" and @$point == 3;
134 0         0 my ($t_high, $t_low, $t_close) = @$point;
135              
136 0         0 push @$l, $t_low; shift @$l while @$l > $kp;
  0         0  
137 0         0 push @$h, $t_high; shift @$h while @$h > $kp;
  0         0  
138              
139 0 0       0 if( @$l == $kp ) {
140 0 0       0 my $L = $l->[0]; for( 1 .. $#$l ) { $L = $l->[$_] if $l->[$_] < $L };
  0         0  
  0         0  
141 0 0       0 my $H = $h->[0]; for( 1 .. $#$h ) { $H = $h->[$_] if $h->[$_] > $H };
  0         0  
  0         0  
142              
143 0 0       0 if( $H != $L ) {
144 0         0 $K = 100 * ($t_close - $L)/($H-$L);
145             } else {
146             # added 2018-06-10 per Marcel Ebbrecht, to avoid division by
147             # zero we don't really have any notes from Lane on this, so
148             # K=100 seems reasonable; but I admit I have no idea what the
149             # the "correct" behavior might be, if there is one.
150 0         0 $K = 100;
151             }
152              
153 0 0       0 $L = $l->[-$dp]; for( (-$dp+1) .. -1 ) { $L = $l->[$_] if $l->[$_] < $L };
  0         0  
  0         0  
154 0 0       0 $H = $h->[-$dp]; for( (-$dp+1) .. -1 ) { $H = $h->[$_] if $h->[$_] > $H };
  0         0  
  0         0  
155              
156 0 0       0 if( $L != 0 ) {
157 0         0 $D = 100 * $H / $L;
158             } else {
159             # also added 2018-06-10 (see above comment)
160 0         0 $D = 100;
161             }
162             }
163             }
164              
165 0         0 $this->{K} = $K;
166 0         0 $this->{D} = $D;
167              
168 0         0 return;
169             }
170              
171             # }}}
172             # {{{ sub insert_fast
173             sub insert_fast {
174 30     30 0 27 my $this = shift;
175              
176 30   100     51 my $l = ($this->{low_hist} ||= []);
177 30   100     41 my $h = ($this->{high_hist} ||= []);
178 30         31 my $kp = $this->{kp};
179 30         32 my $dp = $this->{dp};
180              
181 30   66     75 my $dsma = ($this->{dsma} ||= Math::Business::SMA->new($dp));
182              
183 30         45 my ($K, $D);
184 30         48 while( defined( my $point = shift ) ) {
185 30 50 33     80 croak "insert takes three tuple (high, low, close)" unless ref $point eq "ARRAY" and @$point == 3;
186 30         45 my ($t_high, $t_low, $t_close) = @$point;
187              
188 30         41 push @$l, $t_low; shift @$l while @$l > $kp;
  30         49  
189 30         46 push @$h, $t_high; shift @$h while @$h > $kp;
  30         45  
190              
191 30 100       51 if( @$l == $kp ) {
192 17 100       21 my $L = $l->[0]; for( 1 .. $#$l ) { $L = $l->[$_] if $l->[$_] < $L };
  17         30  
  221         282  
193 17 100       43 my $H = $h->[0]; for( 1 .. $#$h ) { $H = $h->[$_] if $h->[$_] > $H };
  17         57  
  221         286  
194              
195 17         30 $K = 100 * ($t_close - $L)/($H-$L);
196              
197 17         38 $dsma->insert($K);
198 17         25 $D = $dsma->query;
199             }
200             }
201              
202 30         32 $this->{K} = $K;
203 30         30 $this->{D} = $D;
204              
205 30         47 return;
206             }
207              
208             # }}}
209             # {{{ sub insert_slow
210             sub insert_slow {
211 30     30 0 31 my $this = shift;
212              
213 30   100     46 my $l = ($this->{low_hist} ||= []);
214 30   100     45 my $h = ($this->{high_hist} ||= []);
215 30         32 my $kp = $this->{kp};
216 30         29 my $dp = $this->{dp};
217              
218 30   66     53 my $dsma = ($this->{dsma} ||= Math::Business::SMA->new($dp));
219 30   66     41 my $ksma = ($this->{ksma} ||= Math::Business::SMA->new($dp));
220              
221 30         32 my ($K, $D);
222 30         42 while( defined( my $point = shift ) ) {
223 30 50 33     73 croak "insert takes three tuple (high, low, close)" unless ref $point eq "ARRAY" and @$point == 3;
224 30         44 my ($t_high, $t_low, $t_close) = @$point;
225              
226 30         36 push @$l, $t_low; shift @$l while @$l > $kp;
  30         48  
227 30         33 push @$h, $t_high; shift @$h while @$h > $kp;
  30         47  
228              
229 30 100       47 if( @$l == $kp ) {
230 17 100       19 my $L = $l->[0]; for( 1 .. $#$l ) { $L = $l->[$_] if $l->[$_] < $L };
  17         24  
  221         274  
231 17 100       19 my $H = $h->[0]; for( 1 .. $#$h ) { $H = $h->[$_] if $h->[$_] > $H };
  17         20  
  221         289  
232              
233 17         46 $ksma->insert( 100 * ($t_close - $L)/($H-$L) );
234 17         27 $K = $ksma->query;
235              
236 17         26 $dsma->insert($K);
237 17         20 $D = $dsma->query;
238             }
239             }
240              
241 30         31 $this->{K} = $K;
242 30         31 $this->{D} = $D;
243              
244 30         54 return;
245             }
246              
247             # }}}
248             # {{{ sub insert_full
249             sub insert_full {
250 30     30 0 29 my $this = shift;
251              
252 30   100     54 my $l = ($this->{low_hist} ||= []);
253 30   100     41 my $h = ($this->{high_hist} ||= []);
254 30         33 my $kp = $this->{kp};
255 30         31 my $dp = $this->{dp};
256 30   33     34 my $xp = $this->{xp} || $this->{dp};
257              
258 30   66     55 my $dsma = ($this->{dsma} ||= Math::Business::SMA->new($xp));
259 30   66     45 my $ksma = ($this->{ksma} ||= Math::Business::SMA->new($xp));
260              
261 30         32 my ($K, $D);
262 30         42 while( defined( my $point = shift ) ) {
263 30 50 33     73 croak "insert takes three tuple (high, low, close)" unless ref $point eq "ARRAY" and @$point == 3;
264 30         46 my ($t_high, $t_low, $t_close) = @$point;
265              
266 30         37 push @$l, $t_low; shift @$l while @$l > $kp;
  30         50  
267 30         38 push @$h, $t_high; shift @$h while @$h > $kp;
  30         47  
268              
269 30 100       46 if( @$l == $kp ) {
270 17 100       19 my $L = $l->[0]; for( 1 .. $#$l ) { $L = $l->[$_] if $l->[$_] < $L };
  17         24  
  221         286  
271 17 100       20 my $H = $h->[0]; for( 1 .. $#$h ) { $H = $h->[$_] if $h->[$_] > $H };
  17         21  
  221         293  
272              
273 17         45 $ksma->insert( 100 * ($t_close - $L)/($H-$L) );
274 17         19 $K = $ksma->query;
275              
276 17         34 $dsma->insert($K);
277 17         38 $D = $dsma->query;
278             }
279             }
280              
281 30         32 $this->{K} = $K;
282 30         29 $this->{D} = $D;
283              
284 30         40 return;
285             }
286              
287             # }}}
288              
289             sub query {
290 90     90 0 3881 my $this = shift;
291              
292 90         152 return ($this->{D}, $this->{K});
293             }
294              
295             __END__