signal_tools 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []