File Coverage

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


line stmt bran cond sub pod time code
1             package Math::Business::ParabolicSAR;
2              
3 2     2   6875 use strict;
  2         12  
  2         38  
4 2     2   7 use warnings;
  2         2  
  2         35  
5 2     2   7 use Carp;
  2         2  
  2         90  
6             use constant {
7 2         1275 LONG => 7,
8             SHORT => 9,
9             HP => 1,
10             LP => 0,
11 2     2   9 };
  2         2  
12              
13             1;
14              
15 0     0 0 0 sub tag { (shift)->{tag} }
16              
17             sub recommended {
18 2     2 0 341 my $class = shift;
19 2         7 $class->new(0.02, 0.20);
20             }
21              
22             sub new {
23 2     2 0 3 my $class = shift;
24 2         7 my $this = bless {e=>[], y=>[]}, $class;
25              
26 2 50       7 if( @_ ) {
27 2         3 eval { $this->set_alpha(@_) };
  2         7  
28 2 50       5 croak $@ if $@;
29             }
30              
31 2         4 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     19 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         10 $this->{as} = $as;
42 2         6 $this->{am} = $am;
43              
44 2         18 $this->{tag} = "PSAR($as,$am)";
45              
46 2         3 return;
47             }
48              
49             sub insert {
50 22     22 0 1169 my $this = shift;
51              
52 22         25 my ($as,$am);
53 22 50 33     69 croak "must set_alpha(as,am) before inserting data" unless defined( $am = $this->{am} ) and defined( $as = $this->{as} );
54              
55 22         24 my ($y_low, $y_high) = @{$this->{y}};
  22         34  
56 22         41 my ($open,$high,$low,$close);
57              
58 22         0 my $S;
59 22         26 my $P = $this->{S};
60 22         31 my $A = $this->{A};
61 22         24 my $e = $this->{e};
62              
63 22         22 my $ls = $this->{ls};
64              
65 22         31 while( defined( my $ar = shift ) ) {
66 137 50 33     458 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         194 ($open,$high,$low,$close) = @$ar;
71              
72 137 100       146 if( defined $ls ) {
73             # calculate sar_t
74             # The Encyclopedia of Technical Market Indicators - Page 495
75              
76 135         155 my @oe = @$e;
77 135 100       180 $e->[HP] = $high if $high > $e->[HP]; # the highest point during the trend
78 135 100       156 $e->[LP] = $low if $low < $e->[LP]; # the lowest point during the trend
79              
80 135 100       147 if( $ls == LONG ) {
81 58         74 $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       93 if( $S > $low ) {
    100          
    100          
88 8         8 $ls = SHORT; # new short position
89              
90 8         8 $S = $e->[HP];
91 8         6 $A = $as;
92              
93 8 100       16 $e->[HP] = ($high>$y_high ? $high : $y_high);
94 8 100       13 $e->[LP] = ($low <$y_low ? $low : $y_low );
95              
96             } elsif( $S > $y_low ) {
97 2         4 $S = $y_low;
98              
99             } elsif( $oe[HP] != $e->[HP] ) {
100 17         18 $A += $as;
101 17 50       28 $A = $am if $A > $am;
102             }
103              
104             } else {
105 77         84 $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       123 if( $S < $high ) {
    100          
    100          
112 7         7 $ls = LONG; # new long position
113              
114 7         8 $S = $e->[LP];
115 7         7 $A = $as;
116              
117 7 50       8 $e->[HP] = ($high>$y_high ? $high : $y_high);
118 7 50       10 $e->[LP] = ($low <$y_low ? $low : $y_low );
119              
120             } elsif( $S < $y_high ) {
121 4         5 $S = $y_high;
122              
123             } elsif( $oe[LP] != $e->[LP] ) {
124 22         22 $A += $as;
125 22 50       27 $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         3 $A = $as;
142              
143 2 50       11 if( $open < $close ) {
144 2         3 $ls = LONG;
145 2         4 $S = $low;
146              
147             } else {
148 0         0 $ls = SHORT;
149 0         0 $S = $high;
150             }
151              
152 2         5 $e->[HP] = $high;
153 2         3 $e->[LP] = $low;
154             }
155              
156 137         127 $P = $S;
157              
158 137         220 ($y_low, $y_high) = ($low, $high);
159             }
160              
161             ## DEBUG ## warn "{S}=$S; {A}=$A";
162              
163 22         24 $this->{S} = $S;
164 22         23 $this->{A} = $A;
165 22         23 $this->{ls} = $ls;
166              
167 22         26 @{$this->{y}} = ($y_low, $y_high);
  22         57  
168             }
169              
170             sub query {
171 9     9 0 21 my $this = shift;
172              
173 9         12 $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__