File Coverage

blib/lib/Math/Business/ParabolicSAR.pm
Criterion Covered Total %
statement 81 89 91.0
branch 34 44 77.2
condition 5 15 33.3
subroutine 9 12 75.0
pod 0 8 0.0
total 129 168 76.7


line stmt bran cond sub pod time code
1             package Math::Business::ParabolicSAR;
2              
3 2     2   12586 use strict;
  2         4  
  2         77  
4 2     2   12 use warnings;
  2         3  
  2         70  
5 2     2   10 use Carp;
  2         4  
  2         155  
6             use constant {
7 2         2381 LONG => 7,
8             SHORT => 9,
9             HP => 1,
10             LP => 0,
11 2     2   11 };
  2         3  
12              
13             1;
14              
15 0     0 0 0 sub tag { (shift)->{tag} }
16              
17             sub recommended {
18 2     2 0 1333 my $class = shift;
19 2         9 $class->new(0.02, 0.20);
20             }
21              
22             sub new {
23 2     2 0 3 my $class = shift;
24 2         13 my $this = bless {e=>[], y=>[]}, $class;
25              
26 2 50       9 if( @_ ) {
27 2         5 eval { $this->set_alpha(@_) };
  2         10  
28 2 50       9 croak $@ if $@;
29             }
30              
31 2         6 return $this;
32             }
33              
34             sub set_alpha {
35 2     2 0 3 my $this = shift;
36 2         5 my ($as,$am) = @_;
37              
38 2 50 33     50 croak "set_alpha(as,am) takes two arguments, the alpha start (0
      33        
39             unless 0 < $as and $as < $am and $am < 1;
40              
41 2         21 $this->{as} = $as;
42 2         6 $this->{am} = $am;
43              
44 2         39 $this->{tag} = "PSAR($as,$am)";
45              
46 2         5 return;
47             }
48              
49             sub insert {
50 22     22 0 6449 my $this = shift;
51              
52 22         28 my ($as,$am);
53 22 50 33     135 croak "must set_alpha(as,am) before inserting data" unless defined( $am = $this->{am} ) and defined( $as = $this->{as} );
54              
55 22         28 my ($y_low, $y_high) = @{$this->{y}};
  22         51  
56 22         30 my ($open,$high,$low,$close);
57              
58 0         0 my $S;
59 22         32 my $P = $this->{S};
60 22         27 my $A = $this->{A};
61 22         30 my $e = $this->{e};
62              
63 22         28 my $ls = $this->{ls};
64              
65 22         53 while( defined( my $ar = shift ) ) {
66 137 50 33     1092 croak "arguments to insert must be four tuple (open,high,low,close) with high greater than or equal to low"
      33        
67             unless ref($ar) eq "ARRAY" and @$ar==4 and $ar->[2]<=$ar->[1];
68              
69             # NOTE: we really only use open and close to initialize ...
70 137         322 ($open,$high,$low,$close) = @$ar;
71              
72 137 100       284 if( defined $ls ) {
73             # calculate sar_t
74             # The Encyclopedia of Technical Market Indicators - Page 495
75              
76 135         262 my @oe = @$e;
77 135 100       287 $e->[HP] = $high if $high > $e->[HP]; # the highest point during the trend
78 135 100       278 $e->[LP] = $low if $low < $e->[LP]; # the lowest point during the trend
79              
80 135 100       224 if( $ls == LONG ) {
81 58         93 $S = $P + $A*($e->[HP] - $P); # adjusted upwards from the reset like so
82              
83             # NOTE: many sources say you should flop short/long if you get
84             # inside the price range for the last *two* periods. Amazon,
85             # Yahoo! and stockcharts dont' seem to do it that way.
86              
87 58 100       250 if( $S > $low ) {
    100          
    100          
88 8         13 $ls = SHORT; # new short position
89              
90 8         13 $S = $e->[HP];
91 8         12 $A = $as;
92              
93 8 100       18 $e->[HP] = ($high>$y_high ? $high : $y_high);
94 8 100       47 $e->[LP] = ($low <$y_low ? $low : $y_low );
95              
96             } elsif( $S > $y_low ) {
97 2         7 $S = $y_low;
98              
99             } elsif( $oe[HP] != $e->[HP] ) {
100 17         18 $A += $as;
101 17 50       49 $A = $am if $A > $am;
102             }
103              
104             } else {
105 77         128 $S = $P + $A*($e->[LP] - $P); # adjusted downwards from the reset like so
106              
107             # NOTE: many sources say you should flop short/long if you get
108             # inside the price range for the last *two* periods. Amazon,
109             # Yahoo! and stockcharts dont' seem to do it that way.
110              
111 77 100       267 if( $S < $high ) {
    100          
    100          
112 7         9 $ls = LONG; # new long position
113              
114 7         11 $S = $e->[LP];
115 7         10 $A = $as;
116              
117 7 50       15 $e->[HP] = ($high>$y_high ? $high : $y_high);
118 7 50       19 $e->[LP] = ($low <$y_low ? $low : $y_low );
119              
120             } elsif( $S < $y_high ) {
121 4         8 $S = $y_high;
122              
123             } elsif( $oe[LP] != $e->[LP] ) {
124 22         24 $A += $as;
125 22 50       58 $A = $am if $A > $am;
126             }
127             }
128              
129             } else {
130             # circa 2010: initialize somehow
131             # (never did find a good description of how to initialize this mess.
132             # I think you're supposed to tell it how to start)
133             # this is the only time we use open/close and it's not even in the definition
134             #
135             # 2011-01-03: I did look this up, it's the "SIP" or significant
136             # point. It should be the lowest (or the highest) point we have
137             # from our recent-ish data or "long trade" as he calls it. This'll
138             # do as an approximation of that imo — otherwise we'll have to
139             # start asking for a few days previous trades just to initialize.
140              
141 2         4 $A = $as;
142              
143 2 50       10 if( $open < $close ) {
144 2         5 $ls = LONG;
145 2         4 $S = $low;
146              
147             } else {
148 0         0 $ls = SHORT;
149 0         0 $S = $high;
150             }
151              
152 2         4 $e->[HP] = $high;
153 2         5 $e->[LP] = $low;
154             }
155              
156 137         174 $P = $S;
157              
158 137         388 ($y_low, $y_high) = ($low, $high);
159             }
160              
161             ## DEBUG ## warn "{S}=$S; {A}=$A";
162              
163 22         32 $this->{S} = $S;
164 22         32 $this->{A} = $A;
165 22         32 $this->{ls} = $ls;
166              
167 22         26 @{$this->{y}} = ($y_low, $y_high);
  22         100  
168             }
169              
170             sub query {
171 9     9 0 29 my $this = shift;
172              
173 9         23 $this->{S};
174             }
175              
176             sub long {
177 0     0 0   my $this = shift;
178 0           $this->{ls} == LONG;
179             }
180              
181             sub short {
182 0     0 0   my $this = shift;
183 0           $this->{ls} == SHORT;
184             }
185              
186             __END__