signal_tools 0.2.2 → 0.3.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95c229f7b3633f9907032443788386156bbe95c9
4
+ data.tar.gz: 7f8ee54376035b7326a881afc701bf625ea9c678
5
+ SHA512:
6
+ metadata.gz: 9030bd61f15ca7349cfc230698127226703c7dd729685ff4cd3bf32504a779094708bea8a0e40b3c442466953c1dbf357385fddc238e1266fc59995b42310d61
7
+ data.tar.gz: 33f9665391f185da998bf59cf0bb2fae1547ca91dc05de95d0b7063d955af8675d180254714399c30d3d9e48cf64d04d6f0c1ecfb2385e1c7f6f05fa1f9d55c9
@@ -1,11 +1,10 @@
1
1
  == Signal Tools
2
2
 
3
- Signal tools allows you to create technical analysis data for a given stock (like MACD, stochastic, and exponential moving averages).
3
+ Signal tools allows you to generate technical analysis data for a given stock (like MACD, stochastic, and exponential moving averages).
4
4
 
5
5
  == Installation
6
6
 
7
7
  gem install signal_tools
8
- bundle install --without development test
9
8
 
10
9
  == Usage
11
10
 
@@ -33,4 +32,24 @@ Signal tools allows you to create technical analysis data for a given stock (lik
33
32
 
34
33
  == Copyright
35
34
 
36
- Copyright (c) 2010 Matt White.
35
+ Copyright (c) 2015 Matt White.
36
+
37
+ MIT License
38
+
39
+ Permission is hereby granted, free of charge, to any person obtaining a copy
40
+ of this software and associated documentation files (the "Software"), to deal
41
+ in the Software without restriction, including without limitation the rights
42
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
43
+ copies of the Software, and to permit persons to whom the Software is
44
+ furnished to do so, subject to the following conditions:
45
+
46
+ The above copyright notice and this permission notice shall be included in all
47
+ copies or substantial portions of the Software.
48
+
49
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55
+ SOFTWARE.
@@ -1,5 +1,12 @@
1
1
  require "signal_tools/stock_data"
2
2
  require "signal_tools/stock"
3
+ require "signal_tools/technicals/average_directional_index"
4
+ require "signal_tools/technicals/average_true_range"
5
+ require "signal_tools/technicals/common"
6
+ require "signal_tools/technicals/ema"
7
+ require "signal_tools/technicals/fast_stochastic"
8
+ require "signal_tools/technicals/slow_stochastic"
9
+ require "signal_tools/technicals/macd"
3
10
 
4
11
  module SignalTools
5
12
  def self.sum(array)
@@ -1,311 +1,50 @@
1
+ require './lib/signal_tools/technicals/common'
2
+
1
3
  module SignalTools
2
4
  class Stock
3
- Default_Period = 90
4
- EMA_Seed_Days = 10
5
- Slow_K_SMA = 3
6
- ATR_Seed_Days = 14
5
+ include ::SignalTools::Technicals::Common
6
+
7
+ DEFAULT_PERIOD = 90
7
8
 
8
9
  attr_accessor :ticker
9
- attr_reader :stock_data
10
+ attr_reader :dates, :stock_data
10
11
 
11
- def initialize(ticker, from_date=Date.today-Default_Period, to_date=Date.today)
12
+ def initialize(ticker, from_date=Date.today-DEFAULT_PERIOD, to_date=Date.today)
12
13
  from_date = Date.parse(from_date) unless from_date.is_a?(Date)
13
14
  to_date = Date.parse(to_date) unless to_date.is_a?(Date)
14
15
  @ticker = ticker
15
16
  @stock_data = SignalTools::StockData.new(ticker, from_date, to_date)
17
+ @dates = stock_data.dates
16
18
  end
17
19
 
18
- # Takes a period of days over which to average closing prices and returns the exponential moving average for each day.
19
- def ema(period=10)
20
- trim_data_to_range!(ema_points(period, @stock_data.close_prices))
20
+ def ema(period=10, type=:default)
21
+ ema_data = SignalTools::Technicals::EMA.new(stock_data.close_prices, period, type).calculate
22
+ trim_data_to_range(ema_data, dates.size)
21
23
  end
22
24
 
23
25
  def macd(fast=8, slow=17, signal=9)
24
- trim_data_to_range!(macd_points(fast, slow, signal))
25
- end
26
-
27
- def fast_stochastic(k=14, d=5)
28
- trim_data_to_range!(fast_stochastic_points(k, d))
29
- end
30
-
31
- def slow_stochastic(k=14, d=5)
32
- trim_data_to_range!(slow_stochastic_points(k, d))
33
- end
34
-
35
- def atr(period=14)
36
- trim_data_to_range!(average_true_ranges(period))
37
- end
38
-
39
- def adx(period=14)
40
- trim_data_to_range!(average_directional_indexes(period))
41
- end
42
-
43
- def dates
44
- @stock_data.dates
45
- end
46
-
47
- def close_prices
48
- @close_prices = trim_data_to_range(@stock_data.close_prices)
49
- end
50
-
51
- private
52
-
53
- #### EMA methods
54
-
55
- def ema_points(period, data, type=:default)
56
- emas = [default_simple_average(data, EMA_Seed_Days)]
57
- if type == :wilder
58
- data.slice(EMA_Seed_Days..-1).each { |current| emas << calculate_wilder_ema(emas.last, current, period) }
59
- else
60
- data.slice(EMA_Seed_Days..-1).each { |current| emas << calculate_ema(emas.last, current, period) }
61
- end
62
- emas
63
- end
64
-
65
- #Takes current value, previous day's EMA, and number of days. Returns EMA for that day.
66
- def calculate_ema(previous, current, period)
67
- (current - previous) * (2.0 / (period + 1)) + previous
68
- end
69
-
70
- #Uses Wilder's moving average formula.
71
- def calculate_wilder_ema(previous, current, period)
72
- (previous * (period - 1) + current) / period
73
- end
74
-
75
- #Takes a period and array of data and calculates the sum ema over the period specified.
76
- def period_sum_ema(period, data)
77
- raise if data.size <= period
78
- sum_emas = [SignalTools.sum(data[0...period])]
79
- data[(period..-1)].each do |today|
80
- sum_emas << (sum_emas.last - (sum_emas.last / period) + today)
81
- end
82
- sum_emas
83
- end
84
-
85
- #### MACD Methods
86
-
87
- # Takes a period of days for fast, slow, signal, and time period (eg 8,17,9).
88
- def macd_points(fast, slow, signal)
89
- fast_ema_points = ema_points(fast, @stock_data.close_prices)
90
- slow_ema_points = ema_points(slow, @stock_data.close_prices)
91
- macd_and_divergence_points(fast_ema_points, slow_ema_points, signal)
92
- end
93
-
94
- def macd_and_divergence_points(fast_ema_points, slow_ema_points, signal)
95
- macds = differences_between_arrays(fast_ema_points, slow_ema_points)
96
- signal_points = ema_points(signal, macds)
97
- divergences = differences_between_arrays(macds, signal_points)
98
- {:signal_points => signal_points, :divergences => divergences}
99
- end
100
-
101
- # Returns an array with the differences between the first_points and second_points
102
- def differences_between_arrays(first_points, second_points)
103
- SignalTools.truncate_to_shortest!(first_points, second_points)
104
- differences = []
105
- first_points.each_with_index { |fp, index| differences << fp - second_points[index] }
106
- differences
107
- end
108
-
109
- #### Stochastic Methods
110
-
111
- def fast_stochastic_points(k_period, d_period)
112
- k_points = calculate_fast_stochastic_k_points(k_period)
113
- d_points = calculate_d_points(k_points, d_period)
114
- k_d_points(k_points, d_points)
115
- end
116
-
117
- def slow_stochastic_points(k_period, d_period)
118
- fast_points = fast_stochastic_points(k_period, d_period)
119
- k_points = slow_k_points(fast_points[:k])
120
- slow_d_points = calculate_d_points(k_points, d_period)
121
- k_d_points(k_points, slow_d_points)
122
- end
123
-
124
- def calculate_fast_stochastic_k_points(period)
125
- index = 0
126
- points = []
127
- while((index + period) <= @stock_data.close_prices.size)
128
- today_cp = @stock_data.close_prices[index + period - 1]
129
- low_price = get_for_period(@stock_data.low_prices, index, index + period - 1, :min)
130
- high_price = get_for_period(@stock_data.high_prices, index, index + period - 1, :max)
131
- points << (today_cp - low_price) / (high_price - low_price)
132
- index += 1
133
- end
134
- points
135
- end
136
-
137
- def calculate_d_points(k_points, period)
138
- collection_for_array(k_points, period, :average)
139
- end
140
-
141
- def k_d_points(k_points, d_points)
142
- raise unless k_points.size > d_points.size
143
- SignalTools.truncate_to_shortest!(k_points, d_points)
144
- {:k => k_points, :d => d_points}
145
- end
146
-
147
- def slow_k_points(fast_k_points)
148
- collection_for_array(fast_k_points, Slow_K_SMA, :average)
149
- end
150
-
151
- #### True Range Methods
152
-
153
- # Takes a smoothing period and historical data and calculates the average
154
- # true ranges.
155
- def average_true_ranges(period)
156
- trs = true_ranges
157
- atrs = [default_simple_average(trs.slice!(0...ATR_Seed_Days), ATR_Seed_Days)]
158
- trs.each { |tr| atrs << calculate_average_true_range(atrs.last, tr, period) }
159
- atrs
160
- end
161
-
162
- # Takes historical data and computes the true ranges.
163
- def true_ranges
164
- trs = [@stock_data.high_prices.first - @stock_data.low_prices.first]
165
- index = 1
166
- while index < (@stock_data.high_prices.size)
167
- trs << true_range(@stock_data.raw_data[index], @stock_data.raw_data[index-1])
168
- index += 1
169
- end
170
- trs
171
- end
172
-
173
- # Takes today's data and yesterday's data and computes the true range.
174
- def true_range(today, yesterday)
175
- [
176
- today[SignalTools::StockData::Indexes[:high]] - today[SignalTools::StockData::Indexes[:low]],
177
- (yesterday[SignalTools::StockData::Indexes[:close]] - today[SignalTools::StockData::Indexes[:high]]).abs,
178
- (yesterday[SignalTools::StockData::Indexes[:close]] - today[SignalTools::StockData::Indexes[:low]]).abs
179
- ].max
180
- end
181
-
182
- # Takes yesterday's average true range, today's true range, and the smoothing
183
- # period and calculates the day's average true range.
184
- def calculate_average_true_range(yesterday_atr, today_tr, period)
185
- (yesterday_atr * (period - 1) + today_tr) / period
186
- end
187
-
188
- #### Average Directional Index Methods
189
-
190
- def average_directional_indexes(period)
191
- dxs = directional_indexes(plus_directional_index(period), minus_directional_index(period))
192
- adxs = ema_points(period, dxs, :wilder)
193
- adxs
194
- end
195
-
196
- def directional_indexes(plus_dis, minus_dis)
197
- SignalTools.truncate_to_shortest!(plus_dis, minus_dis)
198
- differences, sums = [], []
199
- index = 0
200
- while index < plus_dis.size
201
- differences << (plus_dis[index] - minus_dis[index]).abs
202
- sums << (plus_dis[index] + minus_dis[index])
203
- index += 1
204
- end
205
- quotients(differences, sums)
206
- end
207
-
208
- def plus_directional_index(period)
209
- plus_dms = plus_directional_movement(@stock_data.raw_data)
210
- plus_dm_sums = period_sum_ema(period, plus_dms)
211
- true_range_sums = period_sum_ema(period, true_ranges)
212
- quotients(plus_dm_sums, true_range_sums)
213
- end
214
-
215
- def minus_directional_index(period)
216
- minus_dms = minus_directional_movement(@stock_data.raw_data)
217
- minus_dm_sums = period_sum_ema(period, minus_dms)
218
- true_range_sums = period_sum_ema(period, true_ranges)
219
- quotients(minus_dm_sums, true_range_sums)
220
- end
221
-
222
- def quotients(first, second)
223
- SignalTools.truncate_to_shortest!(first, second)
224
- index = 0
225
- quots = []
226
- while index < first.size
227
- quots << first[index] / second[index]
228
- index += 1
229
- end
230
- quots
231
- end
232
-
233
- def plus_directional_movement(data)
234
- plus_dm = []
235
- data.each_cons(2) do |two_days|
236
- um = up_move(two_days.last, two_days.first)
237
- dm = down_move(two_days.last, two_days.first)
238
- plus_dm << ((um > dm) ? um : 0)
239
- end
240
- plus_dm
241
- end
242
-
243
- def minus_directional_movement(data)
244
- minus_dm = []
245
- data.each_cons(2) do |two_days|
246
- um = up_move(two_days.last, two_days.first)
247
- dm = down_move(two_days.last, two_days.first)
248
- minus_dm << ((dm > um) ? dm : 0)
249
- end
250
- minus_dm
251
- end
252
-
253
- #TODO: Pass in only the high prices to this method
254
- # Up move is today_high - yesterday_high
255
- def up_move(today, yesterday)
256
- diff = today[SignalTools::StockData::Indexes[:high]] - yesterday[SignalTools::StockData::Indexes[:high]]
257
- diff > 0 ? diff : 0
258
- end
259
-
260
- #TODO: Pass in only the low prices to this method
261
- # Down move is yesterday_low - today_low
262
- def down_move(today, yesterday)
263
- diff = yesterday[SignalTools::StockData::Indexes[:low]] - today[SignalTools::StockData::Indexes[:low]]
264
- diff > 0 ? diff : 0
265
- end
266
-
267
- #### Misc Utility Methods
268
-
269
- # Returns only the points specific to the date range given.
270
- def trim_data_to_range!(data)
271
- if data.is_a? Array
272
- data.slice!(0..(-dates.size-1))
273
- elsif data.is_a? Hash
274
- data.each { |k,v| v = v.slice!(0..(-dates.size-1)) }
275
- end
276
- data
26
+ macd_data = SignalTools::Technicals::MACD.new(stock_data.close_prices, fast, slow, signal).calculate
27
+ trim_data_to_range(macd_data, dates.size)
277
28
  end
278
29
 
279
- def trim_data_to_range(data)
280
- data.slice((-dates.size+1)..-1)
30
+ def fast_stochastic(k_period=14, d_period=5)
31
+ stochastic_data = SignalTools::Technicals::FastStochastic.new(stock_data, k_period, d_period).calculate
32
+ trim_data_to_range(stochastic_data, dates.size)
281
33
  end
282
34
 
283
- # Gets the first 0...period of numbers from data and returns a simple average.
284
- def default_simple_average(data, period)
285
- SignalTools.average(data.slice(0...period))
35
+ def slow_stochastic(k_period=14, d_period=5)
36
+ stochastic_data = SignalTools::Technicals::SlowStochastic.new(stock_data, k_period, d_period).calculate
37
+ trim_data_to_range(stochastic_data, dates.size)
286
38
  end
287
39
 
288
- #Runs method for the given slice of the array.
289
- def get_for_period(points, start, finish, method)
290
- case method
291
- when :average
292
- SignalTools.average(points.slice(start..finish))
293
- else
294
- (points.slice(start..finish)).send(method)
295
- end
40
+ def average_true_range(period=14)
41
+ atr_data = SignalTools::Technicals::AverageTrueRange.new(stock_data, period).calculate
42
+ trim_data_to_range(atr_data, dates.size)
296
43
  end
297
44
 
298
- #Returns a collection of values by iterating over an array, slicing it period
299
- # elements at a time and calling method for each slice.
300
- def collection_for_array(points, period, method)
301
- raise unless points.size >= period
302
- collection = []
303
- index = 0
304
- while((index + period - 1) < points.size)
305
- collection << get_for_period(points, index, (index + period - 1), method)
306
- index += 1
307
- end
308
- collection
45
+ def average_directional_index(period=14)
46
+ adx_data = SignalTools::Technicals::AverageDirectionalIndex.new(stock_data, period).calculate
47
+ trim_data_to_range(adx_data, dates.size)
309
48
  end
310
49
  end
311
50
  end
@@ -0,0 +1,106 @@
1
+ require './lib/signal_tools/technicals/true_range'
2
+
3
+ module SignalTools::Technicals
4
+ class AverageDirectionalIndex
5
+ include TrueRange
6
+
7
+ attr_reader :period, :stock_data
8
+
9
+ def initialize(stock_data, period)
10
+ @stock_data = stock_data
11
+ @period = period
12
+ end
13
+
14
+ def calculate
15
+ # trim_data_to_range!(average_directional_indexes(period))
16
+ average_directional_indexes
17
+ end
18
+
19
+ def average_directional_indexes
20
+ dxs = directional_indexes(plus_directional_index, minus_directional_index)
21
+ adxs = EMA.new(dxs, period, :wilder).calculate
22
+ adxs
23
+ end
24
+
25
+ def directional_indexes(plus_dis, minus_dis)
26
+ SignalTools.truncate_to_shortest!(plus_dis, minus_dis)
27
+ differences, sums = [], []
28
+ index = 0
29
+ while index < plus_dis.size
30
+ differences << (plus_dis[index] - minus_dis[index]).abs
31
+ sums << (plus_dis[index] + minus_dis[index])
32
+ index += 1
33
+ end
34
+ quotients(differences, sums)
35
+ end
36
+
37
+ def plus_directional_index
38
+ plus_dms = plus_directional_movement(@stock_data.raw_data)
39
+ plus_dm_sums = period_sum_ema(plus_dms)
40
+ true_range_sums = period_sum_ema(true_ranges(stock_data))
41
+ quotients(plus_dm_sums, true_range_sums)
42
+ end
43
+
44
+ def minus_directional_index
45
+ minus_dms = minus_directional_movement(@stock_data.raw_data)
46
+ minus_dm_sums = period_sum_ema(minus_dms)
47
+ true_range_sums = period_sum_ema(true_ranges(stock_data))
48
+ quotients(minus_dm_sums, true_range_sums)
49
+ end
50
+
51
+ def quotients(first, second)
52
+ SignalTools.truncate_to_shortest!(first, second)
53
+ index = 0
54
+ quots = []
55
+ while index < first.size
56
+ quots << first[index] / second[index]
57
+ index += 1
58
+ end
59
+ quots
60
+ end
61
+
62
+ def plus_directional_movement(data)
63
+ plus_dm = []
64
+ data.each_cons(2) do |two_days|
65
+ um = up_move(two_days.last, two_days.first)
66
+ dm = down_move(two_days.last, two_days.first)
67
+ plus_dm << ((um > dm) ? um : 0)
68
+ end
69
+ plus_dm
70
+ end
71
+
72
+ def minus_directional_movement(data)
73
+ minus_dm = []
74
+ data.each_cons(2) do |two_days|
75
+ um = up_move(two_days.last, two_days.first)
76
+ dm = down_move(two_days.last, two_days.first)
77
+ minus_dm << ((dm > um) ? dm : 0)
78
+ end
79
+ minus_dm
80
+ end
81
+
82
+ #TODO: Pass in only the high prices to this method
83
+ # Up move is today_high - yesterday_high
84
+ def up_move(today, yesterday)
85
+ diff = today[SignalTools::StockData::Indexes[:high]] - yesterday[SignalTools::StockData::Indexes[:high]]
86
+ diff > 0 ? diff : 0
87
+ end
88
+
89
+ #TODO: Pass in only the low prices to this method
90
+ # Down move is yesterday_low - today_low
91
+ def down_move(today, yesterday)
92
+ diff = yesterday[SignalTools::StockData::Indexes[:low]] - today[SignalTools::StockData::Indexes[:low]]
93
+ diff > 0 ? diff : 0
94
+ end
95
+
96
+ #Takes a period and array of data and calculates the sum ema over the period specified.
97
+ def period_sum_ema(data)
98
+ raise if data.size <= period
99
+ sum_emas = [SignalTools.sum(data[0...period])]
100
+ data[(period..-1)].each do |today|
101
+ sum_emas << (sum_emas.last - (sum_emas.last / period) + today)
102
+ end
103
+ sum_emas
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,38 @@
1
+ require './lib/signal_tools/technicals/common'
2
+ require './lib/signal_tools/technicals/true_range'
3
+
4
+ module SignalTools::Technicals
5
+ class AverageTrueRange
6
+ include Common
7
+ include TrueRange
8
+
9
+ DEFAULT_PERIOD = 14
10
+
11
+ attr_reader :period, :stock_data
12
+
13
+ def initialize(stock_data, period)
14
+ @period = period
15
+ @stock_data = stock_data
16
+ end
17
+
18
+ def calculate
19
+ average_true_ranges
20
+ end
21
+
22
+ # Takes a smoothing period and historical data and calculates the average
23
+ # true ranges.
24
+ def average_true_ranges
25
+ trs = true_ranges(stock_data)
26
+ atrs = [default_simple_average(trs.slice!(0...DEFAULT_PERIOD), DEFAULT_PERIOD)]
27
+ trs.each { |tr| atrs << calculate_average_true_range(atrs.last, tr, period) }
28
+ atrs
29
+ end
30
+
31
+ # Takes yesterday's average true range, today's true range, and the smoothing
32
+ # period and calculates the day's average true range.
33
+ def calculate_average_true_range(yesterday_atr, today_tr, period)
34
+ (yesterday_atr * (period - 1) + today_tr) / period
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ require './lib/signal_tools/technicals/common'
2
+
3
+ module SignalTools
4
+ module Technicals
5
+ module Common
6
+ def trim_data_to_range(data, size)
7
+ if data.is_a?(Array)
8
+ data.last(size)
9
+ elsif data.is_a?(Hash)
10
+ data.keys.each { |key| data[key] = data[key].first(size) }
11
+ data
12
+ end
13
+ end
14
+
15
+ # Gets the first 0...period of numbers from data and returns a simple average.
16
+ def default_simple_average(data, period)
17
+ SignalTools.average(data.slice(0...period))
18
+ end
19
+
20
+ #Runs method for the given slice of the array.
21
+ def get_for_period(points, start, finish, method)
22
+ case method
23
+ when :average
24
+ SignalTools.average(points.slice(start..finish))
25
+ else
26
+ (points.slice(start..finish)).send(method)
27
+ end
28
+ end
29
+
30
+ #Returns a collection of values by iterating over an array, slicing it period
31
+ # elements at a time and calling method for each slice.
32
+ def collection_for_array(points, period, method)
33
+ raise unless points.size >= period
34
+ collection = []
35
+ index = 0
36
+ while((index + period - 1) < points.size)
37
+ collection << get_for_period(points, index, (index + period - 1), method)
38
+ index += 1
39
+ end
40
+ collection
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ require './lib/signal_tools/technicals/common'
2
+
3
+ module SignalTools::Technicals
4
+ class EMA
5
+ include Common
6
+
7
+ EMA_DEFAULT = 10
8
+
9
+ attr_reader :data, :period, :type
10
+
11
+ def initialize(data, period, type=:default)
12
+ @data = data
13
+ @period = period
14
+ @type = type
15
+ end
16
+
17
+ def calculate
18
+ ema_points
19
+ end
20
+
21
+ private
22
+
23
+ #TODO: Break Wilder into its own class
24
+ def ema_points
25
+ emas = [default_simple_average(data, EMA_DEFAULT)]
26
+ if type == :wilder
27
+ data.slice(EMA_DEFAULT..-1).each { |current| emas << calculate_wilder_ema(emas.last, current) }
28
+ else
29
+ data.slice(EMA_DEFAULT..-1).each { |current| emas << calculate_ema(emas.last, current) }
30
+ end
31
+ emas
32
+ end
33
+
34
+ #Takes current value, previous day's EMA, and number of days. Returns EMA for that day.
35
+ def calculate_ema(previous, current)
36
+ (current - previous) * (2.0 / (period + 1)) + previous
37
+ end
38
+
39
+ #Uses Wilder's moving average formula.
40
+ def calculate_wilder_ema(previous, current)
41
+ (previous * (period - 1) + current) / period
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,19 @@
1
+ require './lib/signal_tools/technicals/common'
2
+ require './lib/signal_tools/technicals/stochastic'
3
+
4
+ module SignalTools::Technicals
5
+ class FastStochastic
6
+ include Common
7
+ include Stochastic
8
+
9
+ def initialize(stock_data, k_period, d_period)
10
+ @d_period = d_period
11
+ @k_period = k_period
12
+ @stock_data = stock_data
13
+ end
14
+
15
+ def calculate
16
+ fast_stochastic_points
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ module SignalTools
2
+ module Technicals
3
+ class MACD
4
+ attr_reader :fast, :slow, :signal, :data
5
+
6
+ def initialize(data, fast, slow, signal)
7
+ @data = data
8
+ @fast = fast
9
+ @slow = slow
10
+ @signal = signal
11
+ end
12
+
13
+ def calculate
14
+ # trim_data_to_range!(macd_points)
15
+ macd_points
16
+ end
17
+
18
+ # Takes a period of days for fast, slow, signal, and time period (eg 8,17,9).
19
+ def macd_points
20
+ fast_ema_points = SignalTools::Technicals::EMA.new(data, fast).calculate
21
+ slow_ema_points = SignalTools::Technicals::EMA.new(data, slow).calculate
22
+ macd_and_divergence_points(fast_ema_points, slow_ema_points)
23
+ end
24
+
25
+ def macd_and_divergence_points(fast_ema_points, slow_ema_points)
26
+ macds = differences_between_arrays(fast_ema_points, slow_ema_points)
27
+ signal_points = SignalTools::Technicals::EMA.new(macds, signal).calculate
28
+ divergences = differences_between_arrays(macds, signal_points)
29
+ {:signal_points => signal_points, :divergences => divergences}
30
+ end
31
+
32
+ # Returns an array with the differences between the first_points and second_points
33
+ def differences_between_arrays(first_points, second_points)
34
+ SignalTools.truncate_to_shortest!(first_points, second_points)
35
+ differences = []
36
+ first_points.each_with_index { |fp, index| differences << fp - second_points[index] }
37
+ differences
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ require './lib/signal_tools/technicals/stochastic'
2
+
3
+ module SignalTools::Technicals
4
+ class SlowStochastic
5
+ include Stochastic
6
+
7
+ SMA_DEFAULT = 3
8
+
9
+ def initialize(stock_data, k_period, d_period)
10
+ @d_period = d_period
11
+ @k_period = k_period
12
+ @stock_data = stock_data
13
+ end
14
+
15
+ def calculate
16
+ # trim_data_to_range!(slow_stochastic_points(k_period, d_period))
17
+ slow_stochastic_points
18
+ end
19
+
20
+ def slow_stochastic_points
21
+ fast_points = fast_stochastic_points
22
+ k_points = slow_k_points(fast_points[:k])
23
+ slow_d_points = calculate_d_points(k_points, d_period)
24
+ k_d_points(k_points, slow_d_points)
25
+ end
26
+
27
+ def slow_k_points(fast_k_points)
28
+ collection_for_array(fast_k_points, SMA_DEFAULT, :average)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ require './lib/signal_tools/technicals/common'
2
+
3
+ module SignalTools::Technicals::Stochastic
4
+ include ::SignalTools::Technicals::Common
5
+
6
+ attr_reader :d_period, :k_period, :stock_data
7
+
8
+ def calculate_d_points(k_points, period)
9
+ collection_for_array(k_points, period, :average)
10
+ end
11
+
12
+ def k_d_points(k_points, d_points)
13
+ raise unless k_points.size > d_points.size
14
+ SignalTools.truncate_to_shortest!(k_points, d_points)
15
+ {:k => k_points, :d => d_points}
16
+ end
17
+
18
+ def fast_stochastic_points
19
+ k_points = calculate_fast_stochastic_k_points
20
+ d_points = calculate_d_points(k_points, d_period)
21
+ k_d_points(k_points, d_points)
22
+ end
23
+
24
+ def calculate_fast_stochastic_k_points
25
+ index = 0
26
+ points = []
27
+ while((index + k_period) <= stock_data.close_prices.size)
28
+ today_cp = stock_data.close_prices[index + k_period - 1]
29
+ low_price = get_for_period(stock_data.low_prices, index, index + k_period - 1, :min)
30
+ high_price = get_for_period(stock_data.high_prices, index, index + k_period - 1, :max)
31
+ points << (today_cp - low_price) / (high_price - low_price)
32
+ index += 1
33
+ end
34
+ points
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ module SignalTools
2
+ module Technicals
3
+ module TrueRange
4
+ # Takes historical data and computes the true ranges.
5
+ def true_ranges(stock_data)
6
+ trs = [stock_data.high_prices.first - stock_data.low_prices.first]
7
+ index = 1
8
+ while index < (stock_data.high_prices.size)
9
+ trs << true_range(stock_data.raw_data[index], stock_data.raw_data[index-1])
10
+ index += 1
11
+ end
12
+ trs
13
+ end
14
+
15
+ # Takes today's data and yesterday's data and computes the true range.
16
+ def true_range(today, yesterday)
17
+ [
18
+ today[SignalTools::StockData::Indexes[:high]] - today[SignalTools::StockData::Indexes[:low]],
19
+ (yesterday[SignalTools::StockData::Indexes[:close]] - today[SignalTools::StockData::Indexes[:high]]).abs,
20
+ (yesterday[SignalTools::StockData::Indexes[:close]] - today[SignalTools::StockData::Indexes[:low]]).abs
21
+ ].max
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ require './test/test_helper'
2
+
3
+ class TestAverageDirectionalIndex < Minitest::Test
4
+ def setup
5
+ ticker = "TESTING"
6
+ @days = 90
7
+
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @stock = SignalTools::Stock.new(ticker)
10
+ end
11
+
12
+ @stock_data = @stock.stock_data
13
+ end
14
+
15
+ def test_calculate
16
+ assert_equal "0.491321", "%.6f" % SignalTools::Technicals::AverageDirectionalIndex.new(@stock_data, 14).calculate[-1]
17
+ assert_equal "0.496588", "%.6f" % SignalTools::Technicals::AverageDirectionalIndex.new(@stock_data, 14).calculate[-5]
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require './test/test_helper'
2
+
3
+ class TestAverageTrueRange < Minitest::Test
4
+ def setup
5
+ ticker = "TESTING"
6
+ @days = 90
7
+
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @stock = SignalTools::Stock.new(ticker)
10
+ end
11
+
12
+ @stock_data = @stock.stock_data
13
+ end
14
+
15
+ def test_calculate
16
+ assert_equal "3.195750", "%.6f" % SignalTools::Technicals::AverageTrueRange.new(@stock_data, 14).calculate[-1]
17
+ assert_equal "3.438910", "%.6f" % SignalTools::Technicals::AverageTrueRange.new(@stock_data, 14).calculate[-5]
18
+ assert_equal "3.208282", "%.6f" % SignalTools::Technicals::AverageTrueRange.new(@stock_data, 15).calculate[-1]
19
+ assert_equal "3.434397", "%.6f" % SignalTools::Technicals::AverageTrueRange.new(@stock_data, 15).calculate[-5]
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require './test/test_helper'
2
+
3
+ class TestEMA < Minitest::Test
4
+ def setup
5
+ ticker = "TESTING"
6
+ @days = 90
7
+
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @stock = SignalTools::Stock.new(ticker)
10
+ end
11
+
12
+ @close_prices = @stock.stock_data.close_prices
13
+ end
14
+
15
+ def test_calculate
16
+ assert_equal "2.344948", "%.6f" % SignalTools::Technicals::EMA.new(@close_prices, 10, :default).calculate[-1]
17
+ assert_equal "2.736776", "%.6f" % SignalTools::Technicals::EMA.new(@close_prices, 10, :default).calculate[-5]
18
+ assert_equal "2.556322", "%.6f" % SignalTools::Technicals::EMA.new(@close_prices, 25, :default).calculate[-1]
19
+ assert_equal "2.705835", "%.6f" % SignalTools::Technicals::EMA.new(@close_prices, 25, :default).calculate[-5]
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ require './test/test_helper'
2
+
3
+ class TestFastStochastic < Minitest::Test
4
+ def setup
5
+ ticker = "TESTING"
6
+ @days = 90
7
+
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @stock = SignalTools::Stock.new(ticker)
10
+ end
11
+
12
+ @stock_data = @stock.stock_data
13
+ end
14
+
15
+ def test_calculate
16
+ assert_equal "0.057143", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 14, 5).calculate[:k][-1]
17
+ assert_equal "0.571429", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 14, 5).calculate[:k][-5]
18
+ assert_equal "0.314286", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 14, 5).calculate[:d][-1]
19
+ assert_equal "0.314286", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 14, 5).calculate[:d][-5]
20
+
21
+ assert_equal "0.057143", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 12, 3).calculate[:k][-1]
22
+ assert_equal "0.571429", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 12, 3).calculate[:k][-5]
23
+ assert_equal "0.185714", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 12, 3).calculate[:d][-1]
24
+ assert_equal "0.271429", "%.6f" % SignalTools::Technicals::FastStochastic.new(@stock_data, 12, 3).calculate[:d][-5]
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require './test/test_helper'
2
+
3
+ class TestMACD < Minitest::Test
4
+ def setup
5
+ ticker = "TESTING"
6
+ @days = 90
7
+
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @data = SignalTools::Stock.new(ticker)
10
+ end
11
+
12
+ @data = @data.stock_data.close_prices
13
+ end
14
+
15
+ def test_calculate
16
+ assert_equal "-0.034645", "%.6f" % SignalTools::Technicals::MACD.new(@data, 8, 17, 9).calculate[:signal_points][-1]
17
+ assert_equal "-0.018762", "%.6f" % SignalTools::Technicals::MACD.new(@data, 8, 17, 9).calculate[:signal_points][-5]
18
+ assert_equal "-0.195043", "%.6f" % SignalTools::Technicals::MACD.new(@data, 8, 17, 9).calculate[:divergences][-1]
19
+ assert_equal "0.063532", "%.6f" % SignalTools::Technicals::MACD.new(@data, 8, 17, 9).calculate[:divergences][-5]
20
+
21
+ assert_equal "-0.022654", "%.6f" % SignalTools::Technicals::MACD.new(@data, 12, 26, 9).calculate[:signal_points][-1]
22
+ assert_equal "-0.014099", "%.6f" % SignalTools::Technicals::MACD.new(@data, 12, 26, 9).calculate[:signal_points][-5]
23
+ assert_equal "-0.136291", "%.6f" % SignalTools::Technicals::MACD.new(@data, 12, 26, 9).calculate[:divergences][-1]
24
+ assert_equal "0.034219", "%.6f" % SignalTools::Technicals::MACD.new(@data, 12, 26, 9).calculate[:divergences][-5]
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require './test/test_helper'
2
+
3
+ class TestSlowStochastic < Minitest::Test
4
+ def setup
5
+ ticker = "TESTING"
6
+ @days = 90
7
+
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @stock = SignalTools::Stock.new(ticker)
10
+ end
11
+
12
+ @stock_data = @stock.stock_data
13
+ end
14
+
15
+ def test_calculate
16
+ assert_equal "0.185714", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 14, 5).calculate[:k][-1]
17
+ assert_equal "0.271429", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 14, 5).calculate[:k][-5]
18
+ assert_equal "0.314286", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 14, 5).calculate[:d][-1]
19
+ assert_equal "0.314286", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 14, 5).calculate[:d][-5]
20
+
21
+ assert_equal "0.185714", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 12, 3).calculate[:k][-1]
22
+ assert_equal "0.271429", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 12, 3).calculate[:k][-5]
23
+ assert_equal "0.314286", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 12, 3).calculate[:d][-1]
24
+ assert_equal "0.257143", "%.6f" % SignalTools::Technicals::SlowStochastic.new(@stock_data, 12, 3).calculate[:d][-5]
25
+ end
26
+ end
@@ -1,25 +1,21 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'flexmock/test_unit'
1
+ require 'minitest/autorun'
4
2
 
5
3
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
4
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
5
  require 'signal_tools'
8
6
 
9
- class Test::Unit::TestCase
10
- def data_for_tests(period)
11
- repeat = 5
12
- historical_data = []
13
- (0...SignalTools::StockData::Extra_Days+period).each do |i|
14
- seed = i % repeat + 1
15
- historical_data << [
16
- (Date.today-i).to_s,
17
- (seed * 0.8).to_s, #Open
18
- (seed * 1.5).to_s, #High
19
- (seed * 0.5).to_s, #Low
20
- (seed * 0.9).to_s #Close
21
- ]
22
- end
23
- historical_data
7
+ def data_for_tests(period)
8
+ repeat = 5
9
+ historical_data = []
10
+ (0...SignalTools::StockData::Extra_Days+period).each do |i|
11
+ seed = i % repeat + 1
12
+ historical_data << [
13
+ (Date.today-i).to_s,
14
+ (seed * 0.8).to_s, #Open
15
+ (seed * 1.5).to_s, #High
16
+ (seed * 0.5).to_s, #Low
17
+ (seed * 0.9).to_s #Close
18
+ ]
24
19
  end
25
- end
20
+ historical_data
21
+ end
@@ -1,7 +1,6 @@
1
- require 'test_helper'
2
- require 'signal_tools'
1
+ require './test/test_helper'
3
2
 
4
- class TestSignalTools < Test::Unit::TestCase
3
+ class TestSignalTools < Minitest::Test
5
4
  def setup
6
5
  @array1 = [1,2,3,4,5,6,7,8,9,10]
7
6
  end
@@ -1,67 +1,13 @@
1
- require 'test_helper'
2
- require 'signal_tools'
1
+ require './test/test_helper'
3
2
 
4
- class TestStock < Test::Unit::TestCase
3
+ class TestStock < Minitest::Test
5
4
  def setup
6
5
  ticker = "TESTING"
7
6
  @days = 90
8
- flexmock(YahooFinance).should_receive(:get_historical_quotes).with_any_args.and_return(data_for_tests(@days))
9
- @stock = SignalTools::Stock.new(ticker)
10
- end
11
-
12
- def test_ema
13
- assert_equal "2.344948", "%.6f" % @stock.ema[-1]
14
- assert_equal "2.736776", "%.6f" % @stock.ema[-5]
15
- assert_equal "2.556322", "%.6f" % @stock.ema(25)[-1]
16
- assert_equal "2.705835", "%.6f" % @stock.ema(25)[-5]
17
- end
18
-
19
- def test_macd
20
- assert_equal "-0.034645", "%.6f" % @stock.macd[:signal_points][-1]
21
- assert_equal "-0.018762", "%.6f" % @stock.macd[:signal_points][-5]
22
- assert_equal "-0.195043", "%.6f" % @stock.macd[:divergences][-1]
23
- assert_equal "0.063532", "%.6f" % @stock.macd[:divergences][-5]
24
-
25
- assert_equal "-0.022654", "%.6f" % @stock.macd(12, 26, 9)[:signal_points][-1]
26
- assert_equal "-0.014099", "%.6f" % @stock.macd(12, 26, 9)[:signal_points][-5]
27
- assert_equal "-0.136291", "%.6f" % @stock.macd(12, 26, 9)[:divergences][-1]
28
- assert_equal "0.034219", "%.6f" % @stock.macd(12, 26, 9)[:divergences][-5]
29
- end
30
-
31
- def test_fast_stochastic
32
- assert_equal "0.057143", "%.6f" % @stock.fast_stochastic[:k][-1]
33
- assert_equal "0.571429", "%.6f" % @stock.fast_stochastic[:k][-5]
34
- assert_equal "0.314286", "%.6f" % @stock.fast_stochastic[:d][-1]
35
- assert_equal "0.314286", "%.6f" % @stock.fast_stochastic[:d][-5]
36
-
37
- assert_equal "0.057143", "%.6f" % @stock.fast_stochastic(12, 3)[:k][-1]
38
- assert_equal "0.571429", "%.6f" % @stock.fast_stochastic(12, 3)[:k][-5]
39
- assert_equal "0.185714", "%.6f" % @stock.fast_stochastic(12, 3)[:d][-1]
40
- assert_equal "0.271429", "%.6f" % @stock.fast_stochastic(12, 3)[:d][-5]
41
- end
42
-
43
- def test_slow_stochastic
44
- assert_equal "0.185714", "%.6f" % @stock.slow_stochastic[:k][-1]
45
- assert_equal "0.271429", "%.6f" % @stock.slow_stochastic[:k][-5]
46
- assert_equal "0.314286", "%.6f" % @stock.slow_stochastic[:d][-1]
47
- assert_equal "0.314286", "%.6f" % @stock.slow_stochastic[:d][-5]
48
-
49
- assert_equal "0.185714", "%.6f" % @stock.slow_stochastic(12, 3)[:k][-1]
50
- assert_equal "0.271429", "%.6f" % @stock.slow_stochastic(12, 3)[:k][-5]
51
- assert_equal "0.314286", "%.6f" % @stock.slow_stochastic(12, 3)[:d][-1]
52
- assert_equal "0.257143", "%.6f" % @stock.slow_stochastic(12, 3)[:d][-5]
53
- end
54
-
55
- def test_atr
56
- assert_equal "3.195750", "%.6f" % @stock.atr[-1]
57
- assert_equal "3.438910", "%.6f" % @stock.atr[-5]
58
- assert_equal "3.208282", "%.6f" % @stock.atr(15)[-1]
59
- assert_equal "3.434397", "%.6f" % @stock.atr(15)[-5]
60
- end
61
7
 
62
- def test_adx
63
- assert_equal "0.491321", "%.6f" % @stock.adx[-1]
64
- assert_equal "0.496588", "%.6f" % @stock.adx[-5]
8
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
9
+ @stock = SignalTools::Stock.new(ticker)
10
+ end
65
11
  end
66
12
 
67
13
  def test_stock_should_have_correct_number_of_data_elements
@@ -70,7 +16,7 @@ class TestStock < Test::Unit::TestCase
70
16
  assert_equal(@days, @stock.macd[:divergences].size)
71
17
  assert_equal(@days, @stock.fast_stochastic[:k].size)
72
18
  assert_equal(@days, @stock.slow_stochastic[:k].size)
73
- assert_equal(@days, @stock.atr.size)
74
- assert_equal(@days, @stock.adx.size)
19
+ assert_equal(@days, @stock.average_true_range.size)
20
+ assert_equal(@days, @stock.average_directional_index.size)
75
21
  end
76
22
  end
@@ -1,15 +1,16 @@
1
- require 'test_helper'
2
- require 'signal_tools'
1
+ require './test/test_helper'
3
2
 
4
- class TestStockData < Test::Unit::TestCase
3
+ class TestStockData < Minitest::Test
5
4
  def setup
6
5
  ticker = "TESTING"
7
6
  @days = 90
8
7
  @total_days = @days + SignalTools::StockData::Extra_Days
9
8
  @from_date = Date.today - @days
10
9
  @to_date = Date.today
11
- flexmock(YahooFinance).should_receive(:get_historical_quotes).with_any_args.and_return(data_for_tests(@days))
12
- @stock_data = SignalTools::StockData.new(ticker, @from_date, @to_date)
10
+
11
+ YahooFinance.stub(:get_historical_quotes, data_for_tests(@days)) do
12
+ @stock_data = SignalTools::StockData.new(ticker, @from_date, @to_date)
13
+ end
13
14
  end
14
15
 
15
16
  def test_dates
metadata CHANGED
@@ -1,27 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: signal_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
5
- prerelease:
4
+ version: 0.3.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Matt White
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-09-15 00:00:00.000000000Z
11
+ date: 2015-05-28 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: yahoofinance
16
- requirement: &11998940 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: '0'
19
+ version: 1.2.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.0
22
23
  type: :runtime
23
24
  prerelease: false
24
- version_requirements: *11998940
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: minitest
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 5.5.0
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 5.5.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 5.5.0
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 5.5.0
25
53
  description: Gem to create technical analysis data for a given stock (like MACD, stochastic,
26
54
  and exponential moving averages).
27
55
  email: mattw922@gmail.com
@@ -29,39 +57,54 @@ executables: []
29
57
  extensions: []
30
58
  extra_rdoc_files: []
31
59
  files:
60
+ - LICENSE
61
+ - README.rdoc
32
62
  - Rakefile
33
63
  - lib/signal_tools.rb
34
64
  - lib/signal_tools/stock.rb
35
65
  - lib/signal_tools/stock_data.rb
36
- - test/test_tickers
37
- - test/test_stock.rb
66
+ - lib/signal_tools/technicals/average_directional_index.rb
67
+ - lib/signal_tools/technicals/average_true_range.rb
68
+ - lib/signal_tools/technicals/common.rb
69
+ - lib/signal_tools/technicals/ema.rb
70
+ - lib/signal_tools/technicals/fast_stochastic.rb
71
+ - lib/signal_tools/technicals/macd.rb
72
+ - lib/signal_tools/technicals/slow_stochastic.rb
73
+ - lib/signal_tools/technicals/stochastic.rb
74
+ - lib/signal_tools/technicals/true_range.rb
75
+ - test/lib/signal_tools/technicals/test_average_directional_index.rb
76
+ - test/lib/signal_tools/technicals/test_average_true_range.rb
77
+ - test/lib/signal_tools/technicals/test_ema.rb
78
+ - test/lib/signal_tools/technicals/test_fast_stochastic.rb
79
+ - test/lib/signal_tools/technicals/test_macd.rb
80
+ - test/lib/signal_tools/technicals/test_slow_stochastic.rb
81
+ - test/test_helper.rb
38
82
  - test/test_signal_tools.rb
83
+ - test/test_stock.rb
39
84
  - test/test_stock_data.rb
40
- - test/test_helper.rb
41
- - README.rdoc
42
- - LICENSE
85
+ - test/test_tickers
43
86
  homepage: http://github.com/whitethunder/signal_tools
44
- licenses: []
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
45
90
  post_install_message:
46
91
  rdoc_options: []
47
92
  require_paths:
48
93
  - lib
49
94
  required_ruby_version: !ruby/object:Gem::Requirement
50
- none: false
51
95
  requirements:
52
- - - ! '>='
96
+ - - ">="
53
97
  - !ruby/object:Gem::Version
54
98
  version: '0'
55
99
  required_rubygems_version: !ruby/object:Gem::Requirement
56
- none: false
57
100
  requirements:
58
- - - ! '>='
101
+ - - ">="
59
102
  - !ruby/object:Gem::Version
60
103
  version: '0'
61
104
  requirements: []
62
105
  rubyforge_project:
63
- rubygems_version: 1.8.8
106
+ rubygems_version: 2.4.5
64
107
  signing_key:
65
- specification_version: 3
108
+ specification_version: 4
66
109
  summary: Create technical analysis data for a given stock.
67
110
  test_files: []