advanced_math 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Manifest.txt CHANGED
@@ -1,8 +1,13 @@
1
- advanced_math-0.0.4.gem
2
1
  advanced_math.gemspec
2
+ gem_publish.sh
3
3
  History.txt
4
+ lib/advanced_math/rsi.rb
5
+ lib/advanced_math/sma.rb
4
6
  lib/advanced_math.rb
5
7
  Manifest.txt
6
8
  PostInstall.txt
7
9
  README.rdoc
10
+ test/rsi.pl
11
+ test/RSI.pm
12
+ test/test_rsi.rb
8
13
  test/test_simple_moving_average.rb
@@ -1,16 +1,37 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
1
4
  # -*- encoding: utf-8 -*-
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = %q{advanced_math}
5
- s.version = "0.0.5"
8
+ s.version = "0.0.7"
6
9
 
7
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
11
  s.authors = [%q{Glenn Nagel}]
9
12
  s.date = %q{2011-08-28}
10
13
  s.description = %q{A simple gem for advanced and financial math calcualtions.}
11
14
  s.email = [%q{glenn@mercury-wireless.com}]
12
- s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt"]
13
- s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "advanced_math-0.0.4.gem", "advanced_math.gemspec", "lib/advanced_math.rb", "test/test_simple_moving_average.rb"]
15
+ s.extra_rdoc_files = [
16
+ "History.txt",
17
+ "Manifest.txt",
18
+ "PostInstall.txt"
19
+ ]
20
+ s.files = [
21
+ "History.txt",
22
+ "Manifest.txt",
23
+ "PostInstall.txt",
24
+ "README.rdoc",
25
+ "advanced_math.gemspec",
26
+ "gem_publish.sh",
27
+ "lib/advanced_math.rb",
28
+ "lib/advanced_math/rsi.rb",
29
+ "lib/advanced_math/sma.rb",
30
+ "test/RSI.pm",
31
+ "test/rsi.pl",
32
+ "test/test_rsi.rb",
33
+ "test/test_simple_moving_average.rb"
34
+ ]
14
35
  s.homepage = %q{https://github.com/gnagel/mercury-wireless-public/tree/master/ruby/advanced_math}
15
36
  s.post_install_message = %q{PostInstall.txt}
16
37
  s.rdoc_options = [%q{--main}, %q{README.rdoc}]
@@ -18,7 +39,7 @@ Gem::Specification.new do |s|
18
39
  s.rubyforge_project = %q{advanced_math}
19
40
  s.rubygems_version = %q{1.8.6}
20
41
  s.summary = %q{A simple gem for advanced and financial math calcualtions.}
21
- s.test_files = [%q{test/test_simple_moving_average.rb}]
42
+ s.test_files = [%q{test/test_rsi.rb}, %q{test/test_simple_moving_average.rb}]
22
43
 
23
44
  if s.respond_to? :specification_version then
24
45
  s.specification_version = 3
@@ -32,3 +53,4 @@ Gem::Specification.new do |s|
32
53
  s.add_dependency(%q<hoe>, ["~> 2.9"])
33
54
  end
34
55
  end
56
+
data/gem_publish.sh ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ rm *.gem;
4
+ rake gemspec:generate &&
5
+ gem build ./*gemspec &&
6
+ gem push ./*gemspec
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + "/sma.rb"
2
+
3
+ module AdvancedMath
4
+ ###
5
+ # Relative Strength Index (RSI) calculator
6
+ # Created: 2011-08-28
7
+ # Author: G Nagel
8
+ # Company: Mercury Wireless Software LLC
9
+ # Source: http://en.wikipedia.org/wiki/Relative_Strength_Index#Cutler.27s_RSI
10
+ # Source: http://www.aspenres.com/Documents/AspenGraphics4.0/Aspen_Graphics_4.htm#CutlersRSI.htm
11
+ # Source: http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:relative_strength_index_rsi
12
+ ###
13
+ class RelativeStrengthIndex
14
+ attr_accessor :values_up, :values_down, :range, :last_value
15
+ attr_accessor :verbose
16
+ ###
17
+ # Initialize the members
18
+ ###
19
+ def initialize(range)
20
+ raise ArgumentError, "Range is nil" unless (range)
21
+ raise ArgumentError, "Range must be > 1" unless range.to_i > 1
22
+
23
+ @verbose = false
24
+ @values_up = []
25
+ @values_down = []
26
+ @range = range.to_i()
27
+ @last_value = nil
28
+ end
29
+
30
+ ###
31
+ # Add a value to the list.
32
+ # If the list is < @range, then return nil.
33
+ # Otherwise compute the RSI and return the value.
34
+ ###
35
+ def add(value)
36
+ raise ArgumentError, "Value is nil" unless (value)
37
+ puts "==================" if verbose()
38
+ puts "value = #{value}" if verbose()
39
+
40
+ if (nil == @last_value)
41
+ @last_value = value.to_f()
42
+ puts "Setting intial last_value = #{@last_value}" if verbose()
43
+ return nil
44
+ end
45
+
46
+ @values_up.shift() if (range() == @values_up.length())
47
+ @values_down.shift() if (range() == @values_down.length())
48
+
49
+ diff = value.to_f - @last_value
50
+ @last_value = value.to_f()
51
+ if (0 > diff)
52
+ @values_up << 0
53
+ @values_down << diff.abs()
54
+ elsif (0 < diff)
55
+ @values_up << diff
56
+ @values_down << 0
57
+ else
58
+ @values_up << 0
59
+ @values_down << 0
60
+ end
61
+ puts "diff = #{diff}, values_up = #{@values_up.inspect()}, values_down = #{@values_down.inspect()}" if verbose()
62
+
63
+ if (@values_up.length() < range())
64
+ puts "#{@values_up.length()} < #{range()}" if verbose()
65
+ return nil
66
+ end
67
+ if (@values_up.length() > range())
68
+ puts "#{@values_up.length()} > #{range()}" if verbose()
69
+ @values_up.shift()
70
+ @values_down.shift()
71
+ else
72
+ puts "#{@values_up.length()} == #{range()}" if verbose()
73
+ end
74
+
75
+ sum = {:up => 0, :down => 0, :i_up => 0, :i_down => 0}
76
+ @values_up.each() { |value| sum[:up] = sum[:up] + value }
77
+ @values_down.each() { |value| sum[:down] = sum[:down] + value }
78
+
79
+ rs = (0 == sum[:down]) ? 100.0 : (sum[:up] / sum[:down])
80
+ puts "rs = #{rs}" if verbose()
81
+
82
+ rsi = 100.0 - (100.0 / (1.0 + rs))
83
+ puts "rsi = #{rsi}" if verbose()
84
+ return rsi
85
+ end
86
+
87
+ ###
88
+ # Compute the RSI values for an array
89
+ # Return an array with the RSI values
90
+ ###
91
+ def add_array(values)
92
+ raise ArgumentError, "Value is nil" unless (values);
93
+ raise ArgumentError, "Value is not an array" unless values.kind_of?(Array);
94
+
95
+ output = []
96
+
97
+ # Calculate the sum of the array
98
+ values.each() { |value| output << add(value) }
99
+
100
+ # Return the RSI array
101
+ return output
102
+ end
103
+ end
104
+
105
+ ###
106
+ # RSI is just an alias to RelativeStrengthIndex
107
+ ###
108
+ class RSI < RelativeStrengthIndex
109
+ def initialize(range)
110
+ super(range)
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,74 @@
1
+ module AdvancedMath
2
+ # Simple Moving Average (SMA) calculator
3
+ # Created: 2011-06-24
4
+ # Author: G Nagel
5
+ # Company: Mercury Wireless Software LLC
6
+ class SimpleMovingAverage
7
+ ###
8
+ # Initialize the members:
9
+ # range:
10
+ # number of values to average
11
+ # sum:
12
+ # current sum of all values in the array
13
+ # values:
14
+ # array of values used as temporary storage
15
+ ###
16
+ def initialize(range)
17
+ raise ArgumentError, "Range is nil" unless (range);
18
+ raise ArgumentError, "Range must be >= 1" unless range.to_i >= 1;
19
+ @range = range.to_i;
20
+ @sum = 0;
21
+ @values = Array.new();
22
+ end
23
+
24
+ ###
25
+ # Add a value to the list.
26
+ # If the list is < @range, then return nil.
27
+ # Otherwise compute the SMA and return the value.
28
+ ###
29
+ def add(value)
30
+ raise ArgumentError, "Value is nil" unless (value);
31
+
32
+ # add the value to the end of the array.
33
+ @values.push(value);
34
+
35
+ # Calculate the sum of the array
36
+ @sum += value.to_f;
37
+
38
+ # Is the array less than the range?
39
+ return nil if (@values.length() < @range)
40
+
41
+ # Is the array larger than the range?
42
+ @sum -= @values.shift.to_f() if (@values.length() > @range)
43
+
44
+ # Compute the average
45
+ return @sum.to_f / @range.to_f;
46
+ end
47
+
48
+ ###
49
+ # Compute the SMA values for an array
50
+ # Return an array with the SMA values
51
+ ###
52
+ def add_array(values)
53
+ raise ArgumentError, "Value is nil" unless (values);
54
+ raise ArgumentError, "Value is not an array" unless values.kind_of?(Array);
55
+
56
+ output = []
57
+
58
+ # Calculate the sum of the array
59
+ values.each() { |value| output << add(value) }
60
+
61
+ # Return the SMA array
62
+ return output
63
+ end
64
+ end
65
+
66
+ ###
67
+ # SMA is just an alias to SimpleMovingAverage
68
+ ###
69
+ class SMA < SimpleMovingAverage
70
+ def initialize(range)
71
+ super(range)
72
+ end
73
+ end
74
+ end
data/lib/advanced_math.rb CHANGED
@@ -1,59 +1,6 @@
1
-
2
1
  module AdvancedMath
3
- VERSION = '0.0.5'
4
-
5
-
6
- # Simple Moving Average (SMA) calculator
7
- # Created: 2011-06-24
8
- # Author: G Nagel
9
- # Company: Mercury Wireless Software LLC
10
- class SimpleMovingAverage
11
- ###
12
- # Initialize the members:
13
- # range:
14
- # number of values to average
15
- # sum:
16
- # current sum of all values in the array
17
- # values:
18
- # array of values used as temporary storage
19
- ###
20
- def initialize(range)
21
- raise ArgumentError, "Range is nil" unless (range);
22
- raise ArgumentError, "Range must be >= 1" unless range.to_i >= 1;
23
- @range = range.to_i;
24
- @sum = 0;
25
- @values = Array.new();
26
- end
27
-
28
- ###
29
- # Add a value to the list.
30
- # If the list is < @range, then return nil.
31
- # Otherwise compute the SMA and return the value.
32
- ###
33
- def add(value)
34
- raise ArgumentError, "Value is nil" unless (value);
35
-
36
- # add the value to the end of the array.
37
- @values.push(value);
38
-
39
- # Calculate the sum of the array
40
- @sum += value.to_f;
41
-
42
- # Is the array less than the range?
43
- return nil if (@values.length() < @range)
44
-
45
- # Is the array larger than the range?
46
- @sum -= @values.shift.to_f() if (@values.length() > @range)
47
-
48
- # Compute the average
49
- return @sum.to_f / @range.to_f;
50
- end
51
- end
52
-
53
- ###
54
- # SMA is just an alias to SimpleMovingAverage
55
- ###
56
- class SMA < SimpleMovingAverage
57
- end
58
-
2
+ VERSION = '0.0.7'
59
3
  end
4
+
5
+ require File.dirname(__FILE__) + "/advanced_math/sma.rb"
6
+ require File.dirname(__FILE__) + "/advanced_math/rsi.rb"
data/test/RSI.pm ADDED
@@ -0,0 +1,247 @@
1
+ package Math::Business::RSI;
2
+
3
+ use strict;
4
+ use warnings;
5
+ use Carp;
6
+
7
+ our $VERSION = 2.5; # local revision: b
8
+
9
+ use Math::Business::SMA;
10
+ use Math::Business::EMA;
11
+
12
+ 1;
13
+
14
+ sub recommended {
15
+ my $class = shift;
16
+
17
+ $class->new(14);
18
+ }
19
+
20
+ sub new {
21
+ my $class = shift;
22
+ my $this = bless {
23
+ U => Math::Business::EMA->new,
24
+ D => Math::Business::EMA->new,
25
+ RSI => undef,
26
+ cy => undef,
27
+ }, $class;
28
+
29
+ my $alpha = shift;
30
+ if( defined $alpha ) {
31
+ $this->set_alpha( $alpha );
32
+ }
33
+
34
+ return $this;
35
+ }
36
+
37
+ sub set_alpha {
38
+ my $this = shift;
39
+ my $alpha = shift;
40
+
41
+ # NOTE: this alpha is different than you might think ... it's really inverse alpha
42
+ # Wilder uses alpha=14 instead of alpha=(1/14) like you might expect
43
+
44
+ my $days = 2*$alpha - 1; # so days is 2*$alpha-1 instead of the expected 2*(1/$alpha)-1
45
+
46
+ eval { $this->set_days( $days ) };
47
+ croak "set_alpha() is basically set_days(2*$alpha-1), which complained: $@" if $@;
48
+ }
49
+
50
+ sub set_standard {
51
+ my $this = shift;
52
+ my $rm = ref $this->{U};
53
+
54
+ if( $rm =~ m/SMA/ ) {
55
+ $this->{U} = Math::Business::EMA->new;
56
+ $this->{D} = Math::Business::EMA->new;
57
+
58
+ if( my $d = $this->{days} ) {
59
+ $this->set_days($d);
60
+ }
61
+ }
62
+ }
63
+
64
+ sub set_cutler {
65
+ my $this = shift;
66
+ my $rm = ref $this->{U};
67
+
68
+ if( $rm =~ m/EMA/ ) {
69
+ $this->{U} = Math::Business::SMA->new;
70
+ $this->{D} = Math::Business::SMA->new;
71
+
72
+ if( my $d = $this->{days} ) {
73
+ $this->set_days($d);
74
+ }
75
+ }
76
+ }
77
+
78
+ sub set_days {
79
+ my $this = shift;
80
+ my $arg = int(shift);
81
+
82
+ croak "days must be a positive non-zero integer" if $arg <= 0;
83
+
84
+ $this->{U}->set_days($this->{days} = $arg);
85
+ $this->{D}->set_days($arg);
86
+ delete $this->{cy};
87
+ delete $this->{RSI};
88
+ }
89
+
90
+ sub insert {
91
+ my $this = shift;
92
+ my $close_yesterday = $this->{cy};
93
+
94
+ my $EMA_U = $this->{U};
95
+ my $EMA_D = $this->{D};
96
+
97
+ croak "You must set the number of days before you try to insert" if not $this->{days};
98
+ while( defined( my $close_today = shift ) ) {
99
+ if( defined $close_yesterday ) {
100
+ my $delta = $close_today - $close_yesterday;
101
+
102
+ my ($U,$D) = (0,0);
103
+ if( $delta > 0 ) {
104
+ $U = $delta;
105
+ $D = 0;
106
+
107
+ } elsif( $delta < 0 ) {
108
+ $U = 0;
109
+ $D = abs $delta;
110
+ }
111
+
112
+ $EMA_U->insert($U);
113
+ $EMA_D->insert($D);
114
+ }
115
+
116
+ if( defined(my $eu = $EMA_U->query) ) {
117
+ my $ed = $EMA_D->query;
118
+ my $rs = (($ed == 0) ? 100 : $eu/$ed ); # NOTE: This is by definition apparently.
119
+
120
+ $this->{RSI} = 100 - 100/(1+$rs);
121
+ }
122
+
123
+ $close_yesterday = $close_today;
124
+ }
125
+
126
+ $this->{cy} = $close_yesterday;
127
+ }
128
+
129
+ sub query {
130
+ my $this = shift;
131
+
132
+ return $this->{RSI};
133
+ }
134
+
135
+ __END__
136
+
137
+ =head1 NAME
138
+
139
+ Math::Business::RSI - Technical Analysis: Relative Strength Index
140
+
141
+ =head1 SYNOPSIS
142
+
143
+ use Math::Business::RSI;
144
+
145
+ my $rsi = new Math::Business::RSI;
146
+ $rsi->set_alpha(14); # issues a set days of 2*14-1
147
+ $rsi->set_days(27); # equivilent to set_alpha(14)
148
+
149
+ # equivelent to set_days(27)/set_alpha(14):
150
+ my $rsi = new Math::Business::RSI(14);
151
+
152
+ # or to just get the recommended model ... set_alpha(14)
153
+ my $rsi = Math::Business::RSI->recommended;
154
+
155
+ my @closing_values = qw(
156
+ 3 4 4 5 6 5 6 5 5 5 5
157
+ 6 6 6 6 7 7 7 8 8 8 8
158
+ );
159
+
160
+ # choose one:
161
+ $rsi->insert( @closing_values );
162
+ $rsi->insert( $_ ) for @closing_values;
163
+
164
+ if( defined(my $q = $rsi->query) ) {
165
+ print "RSI: $q.\n";
166
+
167
+ } else {
168
+ print "RSI: n/a.\n";
169
+ }
170
+
171
+ =head1 RESEARCHER
172
+
173
+ The RSI was designed by J. Welles Wilder Jr in 1978.
174
+
175
+ According to Wilder, a security is "overbought" it the RSI reaches an upper
176
+ bound of 70 and is "oversold" when it moves below 30. Some sources also
177
+ use thresholds of 80 and 20.
178
+
179
+ Therefore, moving above the upper threshold is a selling signal, whlie moving
180
+ below the lower threshold is a signal to buy.
181
+
182
+ Oddly, RSI(14) uses a "smoothing period" of 14 days -- referring to an alpha of
183
+ 1/14. This means the EMA[N]u/EMA[N]d has N set to 27. This also means the
184
+ alpha is upside of other alpha you might see. RSI(14) actually uses an alpha
185
+ of ~0.0714, but set_alpha() takes the inverse to make C<$rsi->set_alpha(14)>
186
+ work.
187
+
188
+ If all of the above seems really confusing, no worries: RSI(14) means
189
+ C<set_alpha(14)> (or C<new(14)> and is equivelent to C<set_days(27)>.
190
+
191
+ =head2 Cutler
192
+
193
+ There are differing schools of thought on how to calculate this and how
194
+ important it is to stick to precisely the formula Wilder used. Cutler used
195
+ simple moving averages instead of exponential moving averages.
196
+
197
+ You can switch between Wilder and Cutler mode with these:
198
+
199
+ $rsi->set_cutler; # for simple moving averages
200
+ $rsi->set_standard; # for exponential moving averages
201
+
202
+ WARNING: Both of these clear out the value queue! If you need to track
203
+ both, you'll need two objects.
204
+
205
+ =head1 THANKS
206
+
207
+ Todd Litteken C<< <cl@xganon.com> >>
208
+
209
+ Amit Dutt C<< <amit_dutt@hotmail.com> >>
210
+
211
+ =head1 AUTHOR
212
+
213
+ Paul Miller C<< <jettero@cpan.org> >>
214
+
215
+ I am using this software in my own projects... If you find bugs, please please
216
+ please let me know.
217
+
218
+ I normally hang out on #perl on freenode, so you can try to get immediate
219
+ gratification there if you like. L<irc://irc.freenode.net/perl>
220
+
221
+ There is also a mailing list with very light traffic that you might want to
222
+ join: L<http://groups.google.com/group/stockmonkey/>.
223
+
224
+ =head1 COPYRIGHT
225
+
226
+ Copyright (c) 2010 Paul Miller
227
+
228
+ =head1 LICENSE
229
+
230
+ This module is free software. You can redistribute it and/or
231
+ modify it under the terms of the Artistic License 2.0.
232
+
233
+ This program is distributed in the hope that it will be useful,
234
+ but without any warranty; without even the implied warranty of
235
+ merchantability or fitness for a particular purpose.
236
+
237
+ [This software may have had previous licenses, of which the current maintainer
238
+ is completely unaware. If this is so, it is possible the above license is
239
+ incorrect or invalid.]
240
+
241
+ =head1 SEE ALSO
242
+
243
+ perl(1), L<Math::Business::StockMonkey>, L<Math::Business::StockMonkey::FAQ>, L<Math::Business::StockMonkey::CookBook>
244
+
245
+ L<http://en.wikipedia.org/wiki/Relative_Strength_Index>
246
+
247
+ =cut
data/test/rsi.pl ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env perl
2
+ use Math::Business::RSI;
3
+
4
+ my $rsi = new Math::Business::RSI(shift @ARGV);
5
+ $rsi->set_cutler;
6
+ $rsi->insert( $_ ) for @ARGV;
7
+ $rsi = int($rsi->query);
8
+ print "$rsi\n";
data/test/test_rsi.rb ADDED
@@ -0,0 +1,127 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + "/../lib/advanced_math.rb"
3
+
4
+ module AdvancedMath
5
+ class RelativeStrengthIndexTest < Test::Unit::TestCase
6
+ # Verify the constructor raises an ArgumentError if the "range" is invalid
7
+ def test_sma_initialize_nil
8
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(nil) }
9
+ end
10
+
11
+ # Verify the constructor raises an ArgumentError if the "range" is invalid
12
+ def test_sma_initialize_negative
13
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(-1) }
14
+ (-1...-1000).each do |i|
15
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(i) }
16
+ end
17
+ end
18
+
19
+ # Verify the constructor raises an ArgumentError if the "range" is invalid
20
+ def test_sma_initialize_zero
21
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(0) }
22
+ end
23
+
24
+ # Verify the constructor raises an ArgumentError if the "range" is invalid
25
+ def test_sma_initialize_one
26
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(1) }
27
+ end
28
+
29
+ # Verify the constructor doesn't raise an exception if the value is positive
30
+ def test_sma_initialize_positive
31
+ (2...1000).each do |i|
32
+ assert_nothing_raised(ArgumentError) { RelativeStrengthIndex.new(i) }
33
+ end
34
+ end
35
+
36
+ # Verify "add" raises an ArgumentError if the "value" is invalid
37
+ def test_sma_add_nil
38
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(1).add(nil) }
39
+ end
40
+
41
+ # # Verify SMA of 1, always returns the same value
42
+ # def test_sma_add_0
43
+ # sma = RelativeStrengthIndex.new(2)
44
+ # assert_nil(value = sma.add(0))
45
+ # assert_nil(value = sma.add(0))
46
+ # assert_not_nil(value = sma.add(0))
47
+ # assert_equal(50, value)
48
+ # (0...1000).each { |i| assert_equal(50, sma.add(0)) }
49
+ # end
50
+
51
+ def test_sma_add_range
52
+ sma = RelativeStrengthIndex.new(2)
53
+ (0...1000).each do |i|
54
+ value = sma.add(i)
55
+ if (i < 2)
56
+ assert_nil(value, "#{i}")
57
+ else
58
+ assert_not_nil(value, "#{i}")
59
+ assert_equal(99, sma.add(i).to_i(), "#{i}")
60
+ end
61
+ end
62
+ end
63
+
64
+ def RelativeStrengthIndexTest.rsi_values(period, values)
65
+ cmd = "#{File.dirname(__FILE__)}/rsi.pl #{period} #{values.join(" ")}"
66
+ cmd = `#{cmd}`.strip()
67
+ return cmd.to_i()
68
+ end
69
+
70
+ def RelativeStrengthIndexTest.rsi_range(period, prefix, range)
71
+ values = []
72
+ prefix.each() { |i| values << i }
73
+ range.each() { |i| values << i }
74
+ return RelativeStrengthIndexTest.rsi_values(period, values)
75
+ end
76
+
77
+ # Verify SMA of 2, always returns the same value - 0.5
78
+ def test_sma_add_2
79
+ sma = RelativeStrengthIndex.new(2)
80
+ assert_equal(nil, sma.add(0))
81
+ assert_equal(nil, sma.add(0))
82
+ assert_equal(99, sma.add(0).to_i())
83
+ (1...1000).each do |i|
84
+ value = sma.add(i).to_i()
85
+ cmp = RelativeStrengthIndexTest.rsi_range(sma.range(), [0, 0, 0], Range.new(0, i+1))
86
+ assert_equal(cmp, value, "#{i}")
87
+ assert_equal(99, value, "#{i}")
88
+ end
89
+
90
+ # (0..1000).to_a().each() do |i|
91
+ # sma = RelativeStrengthIndex.new(2)
92
+ # value = sma.add(i)
93
+ # assert_nil(value, i)
94
+ #
95
+ # value = sma.add(i+1)
96
+ # assert_not_nil(value = sma.add(i+1), i)
97
+ # assert_equal(99, sma.add(i).to_i(), "#{i}")
98
+ # end
99
+ end
100
+
101
+ def test_sma_array
102
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(2).add_array(nil) }
103
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(2).add_array("") }
104
+ assert_raise(ArgumentError) { RelativeStrengthIndex.new(2).add_array(1) }
105
+ assert_nothing_raised(ArgumentError) { RelativeStrengthIndex.new(2).add_array(Array.new) }
106
+ assert_not_nil( RelativeStrengthIndex.new(2).add_array(Array.new) )
107
+ assert_not_nil( RelativeStrengthIndex.new(2).add_array([]) )
108
+ assert_equal( 0, RelativeStrengthIndex.new(2).add_array([]).length() )
109
+ assert_equal( 1, RelativeStrengthIndex.new(2).add_array([1]).length() )
110
+ assert_equal( 2, RelativeStrengthIndex.new(2).add_array((1..2).to_a).length() )
111
+ assert_equal( 1000, RelativeStrengthIndex.new(2).add_array((1..1000).to_a).length() )
112
+
113
+ values = RelativeStrengthIndex.new(2).add_array((0..1000).to_a)
114
+ assert_nil(values[0])
115
+ assert_nil(values[1])
116
+ 2.upto(values.length() -1) do |i|
117
+ assert_not_nil(values[i])
118
+ assert_equal(99, values[i].to_i())
119
+ end
120
+
121
+ values = RelativeStrengthIndex.new(14).add_array((0..14000).to_a)
122
+ values.slice!(0, 14).each() { |value| assert_nil(value) }
123
+
124
+ values.each() { |value| assert_not_nil(value); assert_equal(99, value.to_i()) }
125
+ end
126
+ end
127
+ end
@@ -5,48 +5,88 @@ module AdvancedMath
5
5
  class SimpleMovingAverageTest < Test::Unit::TestCase
6
6
  # Verify the constructor raises an ArgumentError if the "range" is invalid
7
7
  def test_sma_initialize_nil
8
- assert_raise(ArgumentError) { SimpleMovingAverage.new(nil); }
8
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(nil) }
9
9
  end
10
10
 
11
11
  # Verify the constructor raises an ArgumentError if the "range" is invalid
12
12
  def test_sma_initialize_negative
13
- assert_raise(ArgumentError) { SimpleMovingAverage.new(-1); }
13
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(-1) }
14
14
  (-1...-1000).each do |i|
15
- assert_raise(ArgumentError) { SimpleMovingAverage.new(i); }
15
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(i) }
16
16
  end
17
17
  end
18
18
 
19
19
  # Verify the constructor raises an ArgumentError if the "range" is invalid
20
20
  def test_sma_initialize_zero
21
- assert_raise(ArgumentError) { SimpleMovingAverage.new(0); }
21
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(0) }
22
22
  end
23
23
 
24
24
  # Verify the constructor doesn't raise an exception if the value is positive
25
25
  def test_sma_initialize_positive
26
26
  (1...1000).each do |i|
27
- assert_nothing_raised(ArgumentError) { SimpleMovingAverage.new(i); }
27
+ assert_nothing_raised(ArgumentError) { SimpleMovingAverage.new(i) }
28
28
  end
29
29
  end
30
30
 
31
31
  # Verify "add" raises an ArgumentError if the "value" is invalid
32
32
  def test_sma_add_nil
33
- assert_raise(ArgumentError) { SimpleMovingAverage.new(1).add(nil); }
33
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(1).add(nil) }
34
34
  end
35
35
 
36
36
  # Verify SMA of 1, always returns the same value
37
37
  def test_sma_add_1
38
- sma = SimpleMovingAverage.new(1);
38
+ sma = SimpleMovingAverage.new(1)
39
39
  (1...1000).each do |i|
40
- assert_equal(i, sma.add(i));
40
+ assert_equal(i, sma.add(i))
41
41
  end
42
42
  end
43
43
 
44
44
  # Verify SMA of 2, always returns the same value - 0.5
45
45
  def test_sma_add_2
46
- sma = SimpleMovingAverage.new(2);
47
- assert_equal(nil, sma.add(1));
46
+ sma = SimpleMovingAverage.new(2)
47
+ assert_equal(nil, sma.add(1))
48
48
  (2...1000).each do |i|
49
- assert_equal(i - 0.5, sma.add(i));
49
+ assert_equal(i - 0.5, sma.add(i))
50
+ end
51
+
52
+ (0..1000).to_a().each() do |i|
53
+ sma = SimpleMovingAverage.new(2)
54
+ assert_nil(value = sma.add(i))
55
+ assert_not_nil(value = sma.add(i+1))
56
+ assert_equal(i+0.5, value)
57
+ end
58
+ end
59
+
60
+ def test_sma_array
61
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(2).add_array(nil) }
62
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(2).add_array("") }
63
+ assert_raise(ArgumentError) { SimpleMovingAverage.new(2).add_array(1) }
64
+ assert_nothing_raised(ArgumentError) { SimpleMovingAverage.new(2).add_array(Array.new) }
65
+ assert_not_nil( SimpleMovingAverage.new(2).add_array(Array.new) )
66
+ assert_not_nil( SimpleMovingAverage.new(2).add_array([]) )
67
+ assert_equal( 0, SimpleMovingAverage.new(2).add_array([]).length() )
68
+ assert_equal( 1, SimpleMovingAverage.new(2).add_array([1]).length() )
69
+ assert_equal( 2, SimpleMovingAverage.new(2).add_array((1..2).to_a).length() )
70
+ assert_equal( 1000, SimpleMovingAverage.new(2).add_array((1..1000).to_a).length() )
71
+
72
+ values = SimpleMovingAverage.new(2).add_array((0..1000).to_a)
73
+ assert_nil(values[0])
74
+ 1.upto(values.length() -1) do |i|
75
+ assert_not_nil(values[i])
76
+ assert_equal(i - 0.5, values[i])
77
+ end
78
+
79
+ values = SimpleMovingAverage.new(14).add_array((0..14000).to_a)
80
+ 0.upto(12) { |i| assert_nil(values[i], "#{i}") }
81
+
82
+ 13.upto(values.length() -1) do |i|
83
+ assert_not_nil(values[i], "#{i}")
84
+
85
+ sum = 0.0
86
+ Range.new(i-13, i).each() { |value| sum = sum + value.to_f}
87
+ sum = sum / 14
88
+
89
+ assert_equal(sum, values[i], "#{i}")
50
90
  end
51
91
  end
52
92
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: advanced_math
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Glenn Nagel
@@ -48,9 +48,14 @@ files:
48
48
  - Manifest.txt
49
49
  - PostInstall.txt
50
50
  - README.rdoc
51
- - advanced_math-0.0.4.gem
52
51
  - advanced_math.gemspec
52
+ - gem_publish.sh
53
53
  - lib/advanced_math.rb
54
+ - lib/advanced_math/rsi.rb
55
+ - lib/advanced_math/sma.rb
56
+ - test/RSI.pm
57
+ - test/rsi.pl
58
+ - test/test_rsi.rb
54
59
  - test/test_simple_moving_average.rb
55
60
  homepage: https://github.com/gnagel/mercury-wireless-public/tree/master/ruby/advanced_math
56
61
  licenses: []
@@ -87,4 +92,5 @@ signing_key:
87
92
  specification_version: 3
88
93
  summary: A simple gem for advanced and financial math calcualtions.
89
94
  test_files:
95
+ - test/test_rsi.rb
90
96
  - test/test_simple_moving_average.rb
Binary file