File Coverage

blib/lib/Math/Business/DMI.pm
Criterion Covered Total %
statement 88 96 91.6
branch 21 26 80.7
condition 9 12 75.0
subroutine 9 12 75.0
pod 0 8 0.0
total 127 154 82.4


line stmt bran cond sub pod time code
1             package Math::Business::DMI;
2              
3 3     3   28350 use strict;
  3         8  
  3         114  
4 3     3   13 use warnings;
  3         3  
  3         161  
5 3     3   12 use Carp;
  3         6  
  3         159  
6              
7 3     3   1537 use Math::Business::ATR;
  3         7  
  3         3010  
8              
9             1;
10              
11 0     0 0 0 sub tag { (shift)->{tag} }
12              
13             sub recommended {
14 4     4 0 480186 my $class = shift;
15              
16 4         31 $class->new(14);
17             }
18              
19             sub new {
20 4     4 0 8 my $class = shift;
21 4         34 my $this = bless {
22             ATR => new Math::Business::ATR,
23             }, $class;
24              
25 4         9 my $days = shift;
26 4 50       23 if( defined $days ) {
27 4         18 $this->set_days( $days );
28             }
29              
30 4         39 return $this;
31             }
32              
33             sub set_days {
34 4     4 0 8 my $this = shift;
35 4         12 my $arg = int(shift);
36              
37 4 50       13 croak "days must be a positive non-zero integer" if $arg <= 0;
38              
39 4         54 $this->{ATR}->set_days($arg);
40 4         9 $this->{days} = $arg;
41 4         10 $this->{R} = ($arg-1)/$arg;
42 4         23 $this->{R1} = 1/$arg;
43              
44 4         14 $this->{tag} = "DMI($this->{days})";
45             }
46              
47             sub insert {
48 1109     1109 0 49219 my $this = shift;
49              
50 1109         1791 my $y_point = $this->{y};
51 1109         2314 while( defined( my $point = shift ) ) {
52 1112 50 33     4804 croak "insert takes three tuple (high, low, close)" unless ref $point eq "ARRAY" and @$point == 3;
53 1112         2122 my ($t_high, $t_low, $t_close) = @$point;
54              
55 1112 100       12491 if( defined $y_point ) {
56 1108         1730 my $atr = $this->{ATR};
57 1108         4125 $atr->insert($point);
58              
59 1108         1953 my ($y_high, $y_low, $y_close) = @$y_point;
60              
61 1108         1786 my ($PDM, $MDM) = (0,0);
62 1108         5543 my $A = $t_high - $y_high;
63 1108         1431 my $B = $y_low - $t_low;
64              
65 1108 100 100     6767 if( $A > 0 and $A > $B ) {
    100 66        
66 468         648 $PDM = $A;
67 468         636 $MDM = 0;
68              
69             } elsif( $B > 0 and $B > $A ) {
70 412         541 $PDM = 0;
71 412         538 $MDM = $B;
72             }
73              
74 1108 100       2163 if( defined(my $pdm = $this->{aPDM}) ) {
75 1063         1570 my $mdm = $this->{aMDM};
76              
77 1063         1521 my $R = $this->{R};
78 1063         1590 my $R1 = $this->{R1};
79              
80 1063         4428 my $aPDM = $this->{aPDM} = $R * $pdm + $R1 * $PDM;
81 1063         2600 my $aMDM = $this->{aMDM} = $R * $mdm + $R1 * $MDM;
82              
83 1063         12261 my $ATR = $atr->query;
84              
85 1063 50       1935 if( $ATR == 0 ) {
86 0         0 my $DX = $this->{PDI} = $this->{MDI} = 0;
87 0         0 $this->{ADX} = $R * $this->{ADX} + $R1 * $DX;
88              
89             } else {
90 1063         1831 my $PDI = $this->{PDI} = $aPDM / $ATR;
91 1063         1660 my $MDI = $this->{MDI} = $aMDM / $ATR;
92              
93 1063         1633 my $DI = abs( $PDI - $MDI );
94 1063 100       1985 my $DX = $DI ? $DI / ($PDI + $MDI) : 0;
95             # 0/0 is indeterminent form, but I think 0 makes sense
96              
97 1063         2410 $this->{ADX} = $R * $this->{ADX} + $R1 * $DX;
98             }
99              
100             } else {
101 45         55 my $p;
102 45         116 my $N = $this->{days};
103 45 100 100     163 if( ref($p = $this->{_p}) and (@$p >= $N-1) ) {
104 3         8 my $psum = 0;
105 3         24 $psum += $_ for @$p;
106 3         23 $psum += $PDM;
107              
108 3         8 my $m = $this->{_m};
109 3         8 my $msum = 0;
110 3         17 $msum += $_ for @$m;
111 3         9 $msum += $MDM;
112              
113 3         11 my $aPDM = $this->{aPDM} = $psum / $N;
114 3         25 my $aMDM = $this->{aMDM} = $msum / $N;
115              
116 3         15 my $ATR = $atr->query;
117 3 50       15 if( $ATR == 0 ) {
118 0         0 $this->{PDI} = $this->{MDI} = $this->{ADX} = 0;
119              
120             } else {
121 3         11 my $PDI = $this->{PDI} = $aPDM / $ATR;
122 3         8 my $MDI = $this->{MDI} = $aMDM / $ATR;
123              
124 3         9 my $DI = abs( $PDI - $MDI );
125 3 100       28 my $DX = $DI ? $DI / ($PDI + $MDI) : 0;
126             # 0/0 is indeterminent form, but I think 0 makes sense
127              
128 3         9 $this->{ADX} = $DX; # is this right? No idea... I assume this is well documented in his book
129             }
130              
131 3         6 delete $this->{_p};
132 3         10 delete $this->{_m};
133              
134             } else {
135 42         59 push @{$this->{_p}}, $PDM;
  42         108  
136 42         62 push @{$this->{_m}}, $MDM;
  42         92  
137             }
138             }
139             }
140              
141 1112         2516 $y_point = $point;
142             }
143              
144 1109         2240 $this->{y} = $y_point;
145             }
146              
147 0     0 0 0 sub query_pdi { my $this = shift; return $this->{PDI}; }
  0         0  
148 0     0 0 0 sub query_mdi { my $this = shift; return $this->{MDI}; }
  0         0  
149              
150             sub query {
151 2017     2017 0 5572 my $this = shift;
152              
153 2017 100       5141 return ($this->{PDI}, $this->{MDI}, $this->{ADX}) if wantarray;
154 1009         2837 return $this->{ADX};
155             }
156              
157             __END__