technical-analysis 0.1.1 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/lib/technical-analysis.rb +0 -0
  3. data/lib/technical_analysis.rb +3 -0
  4. data/lib/technical_analysis/helpers/array_helper.rb +0 -0
  5. data/lib/technical_analysis/helpers/stock_calculation.rb +8 -0
  6. data/lib/technical_analysis/helpers/validation.rb +4 -4
  7. data/lib/technical_analysis/indicators/adi.rb +0 -0
  8. data/lib/technical_analysis/indicators/adtv.rb +0 -0
  9. data/lib/technical_analysis/indicators/adx.rb +1 -1
  10. data/lib/technical_analysis/indicators/ao.rb +0 -0
  11. data/lib/technical_analysis/indicators/atr.rb +0 -0
  12. data/lib/technical_analysis/indicators/bb.rb +0 -0
  13. data/lib/technical_analysis/indicators/cci.rb +0 -0
  14. data/lib/technical_analysis/indicators/cmf.rb +0 -0
  15. data/lib/technical_analysis/indicators/cr.rb +0 -0
  16. data/lib/technical_analysis/indicators/dc.rb +0 -0
  17. data/lib/technical_analysis/indicators/dlr.rb +0 -0
  18. data/lib/technical_analysis/indicators/dpo.rb +0 -0
  19. data/lib/technical_analysis/indicators/dr.rb +0 -0
  20. data/lib/technical_analysis/indicators/ema.rb +103 -0
  21. data/lib/technical_analysis/indicators/eom.rb +0 -0
  22. data/lib/technical_analysis/indicators/fi.rb +0 -0
  23. data/lib/technical_analysis/indicators/ichimoku.rb +0 -0
  24. data/lib/technical_analysis/indicators/indicator.rb +1 -0
  25. data/lib/technical_analysis/indicators/kc.rb +0 -0
  26. data/lib/technical_analysis/indicators/kst.rb +0 -0
  27. data/lib/technical_analysis/indicators/macd.rb +0 -0
  28. data/lib/technical_analysis/indicators/mfi.rb +0 -0
  29. data/lib/technical_analysis/indicators/mi.rb +0 -0
  30. data/lib/technical_analysis/indicators/nvi.rb +0 -0
  31. data/lib/technical_analysis/indicators/obv.rb +0 -0
  32. data/lib/technical_analysis/indicators/obv_mean.rb +0 -0
  33. data/lib/technical_analysis/indicators/rsi.rb +6 -5
  34. data/lib/technical_analysis/indicators/sma.rb +7 -5
  35. data/lib/technical_analysis/indicators/sr.rb +0 -0
  36. data/lib/technical_analysis/indicators/trix.rb +0 -0
  37. data/lib/technical_analysis/indicators/tsi.rb +0 -0
  38. data/lib/technical_analysis/indicators/uo.rb +0 -0
  39. data/lib/technical_analysis/indicators/vi.rb +0 -0
  40. data/lib/technical_analysis/indicators/vpt.rb +0 -0
  41. data/lib/technical_analysis/indicators/vwap.rb +97 -0
  42. data/lib/technical_analysis/indicators/wma.rb +100 -0
  43. data/lib/technical_analysis/indicators/wr.rb +0 -0
  44. data/spec/helpers/array_helper_spec.rb +0 -0
  45. data/spec/helpers/validaton_spec.rb +0 -0
  46. data/spec/spec_helper.rb +2 -2
  47. data/spec/ta_test_data.csv +0 -0
  48. data/spec/technical_analysis/indicators/adi_spec.rb +0 -0
  49. data/spec/technical_analysis/indicators/adtv_spec.rb +0 -0
  50. data/spec/technical_analysis/indicators/adx_spec.rb +0 -0
  51. data/spec/technical_analysis/indicators/ao_spec.rb +0 -0
  52. data/spec/technical_analysis/indicators/atr_spec.rb +0 -0
  53. data/spec/technical_analysis/indicators/bb_spec.rb +0 -0
  54. data/spec/technical_analysis/indicators/cci_spec.rb +0 -0
  55. data/spec/technical_analysis/indicators/cmf_spec.rb +0 -0
  56. data/spec/technical_analysis/indicators/cr_spec.rb +0 -0
  57. data/spec/technical_analysis/indicators/dc_spec.rb +0 -0
  58. data/spec/technical_analysis/indicators/dlr_spec.rb +0 -0
  59. data/spec/technical_analysis/indicators/dpo_spec.rb +0 -0
  60. data/spec/technical_analysis/indicators/dr_spec.rb +0 -0
  61. data/spec/technical_analysis/indicators/ema_spec.rb +115 -0
  62. data/spec/technical_analysis/indicators/eom_spec.rb +0 -0
  63. data/spec/technical_analysis/indicators/fi_spec.rb +0 -0
  64. data/spec/technical_analysis/indicators/ichimoku_spec.rb +0 -0
  65. data/spec/technical_analysis/indicators/indicator_spec.rb +1 -1
  66. data/spec/technical_analysis/indicators/kc_spec.rb +0 -0
  67. data/spec/technical_analysis/indicators/kst_spec.rb +0 -0
  68. data/spec/technical_analysis/indicators/macd_spec.rb +0 -0
  69. data/spec/technical_analysis/indicators/mfi_spec.rb +0 -0
  70. data/spec/technical_analysis/indicators/mi_spec.rb +0 -0
  71. data/spec/technical_analysis/indicators/nvi_spec.rb +0 -0
  72. data/spec/technical_analysis/indicators/obv_mean_spec.rb +0 -0
  73. data/spec/technical_analysis/indicators/obv_spec.rb +0 -0
  74. data/spec/technical_analysis/indicators/rsi_spec.rb +5 -5
  75. data/spec/technical_analysis/indicators/sma_spec.rb +4 -4
  76. data/spec/technical_analysis/indicators/sr_spec.rb +0 -0
  77. data/spec/technical_analysis/indicators/trix_spec.rb +0 -0
  78. data/spec/technical_analysis/indicators/tsi_spec.rb +0 -0
  79. data/spec/technical_analysis/indicators/uo_spec.rb +0 -0
  80. data/spec/technical_analysis/indicators/vi_spec.rb +0 -0
  81. data/spec/technical_analysis/indicators/vpt_spec.rb +0 -0
  82. data/spec/technical_analysis/indicators/vwap_spec.rb +119 -0
  83. data/spec/technical_analysis/indicators/wma_spec.rb +115 -0
  84. data/spec/technical_analysis/indicators/wr_spec.rb +0 -0
  85. metadata +12 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c291a4476c68ca1fd7baf9279188f2caf5daf20d
4
- data.tar.gz: cf2450f8149d4f4b51581332aceb544bc69b85c1
3
+ metadata.gz: 242a71fb8aedb7315b625ded3707bb7c3b1fbf3e
4
+ data.tar.gz: dd8f5b9980aa256206126ba688537f092ba8c72a
5
5
  SHA512:
6
- metadata.gz: f334282776b1b2cc80dd4cb3e0505179b88cfdf99a39df9a995f76198fb33c4ddcf94b94df774e413d4f9fbd0cce52168b83b43e1525aceb76c321f7ab1ddcd4
7
- data.tar.gz: 5d4d2e50b2565290af10635b7306a84544819b28e27fe1a9928ab700b6f1b365d87634134e982d8d7ab9f739d4eb3f91847f77f25a41221cfcdc6ec37f638c59
6
+ metadata.gz: de0cb79b308cbac50018e8ec5f33a6a1c5849454c13d9d65a0f76eedfda85858e6129681ba3c584c2bb14330e30c6af190180d3d151ed731a23dc9e6604d31a5
7
+ data.tar.gz: 240f61c674561c0f2e428bf27cb09cddaf11840796cc8eafab48b56a45de3548894030290f6fdf301d764a27379797a17359245a559fc09d651e18186866480b
File without changes
@@ -19,6 +19,7 @@ require 'technical_analysis/indicators/dc'
19
19
  require 'technical_analysis/indicators/dlr'
20
20
  require 'technical_analysis/indicators/dpo'
21
21
  require 'technical_analysis/indicators/dr'
22
+ require 'technical_analysis/indicators/ema'
22
23
  require 'technical_analysis/indicators/eom'
23
24
  require 'technical_analysis/indicators/fi'
24
25
  require 'technical_analysis/indicators/ichimoku'
@@ -38,4 +39,6 @@ require 'technical_analysis/indicators/tsi'
38
39
  require 'technical_analysis/indicators/uo'
39
40
  require 'technical_analysis/indicators/vi'
40
41
  require 'technical_analysis/indicators/vpt'
42
+ require 'technical_analysis/indicators/vwap'
43
+ require 'technical_analysis/indicators/wma'
41
44
  require 'technical_analysis/indicators/wr'
@@ -21,5 +21,13 @@ module TechnicalAnalysis
21
21
  end
22
22
  end
23
23
 
24
+ def self.wma(data)
25
+ intermediate_values = []
26
+ data.each_with_index do |datum, i|
27
+ intermediate_values << datum * (i + 1)/(data.size * (data.size + 1)/2).to_f
28
+ end
29
+ ArrayHelper.sum(intermediate_values)
30
+ end
31
+
24
32
  end
25
33
  end
@@ -20,10 +20,10 @@ module TechnicalAnalysis
20
20
  return true if (options.keys - valid_options).empty?
21
21
  raise ValidationError.new "Invalid options provided. Valid options are #{valid_options.join(", ")}"
22
22
  end
23
-
24
- def self.validate_date_time_key(data)
25
- unless data.all? { |row| row.keys.include? :date_time }
26
- raise ValidationError.new "Dataset must include date_time field with timestamps"
23
+
24
+ def self.validate_date_time_key(data, date_time_key=:date_time)
25
+ unless data.all? { |row| row.keys.include? date_time_key }
26
+ raise ValidationError.new "Dataset must include '#{date_time_key}' field with timestamps"
27
27
  end
28
28
  end
29
29
 
File without changes
File without changes
@@ -85,7 +85,7 @@ module TechnicalAnalysis
85
85
  if prev_adx.nil?
86
86
  adx = ArrayHelper.average(dx_values)
87
87
  else
88
- adx = ((prev_adx * 13) + dx) / period.to_f
88
+ adx = ((prev_adx * (period - 1)) + dx) / period.to_f
89
89
  end
90
90
 
91
91
  output << AdxValue.new(date_time: v[:date_time], adx: adx, di_pos: di_pos, di_neg: di_neg)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,103 @@
1
+ module TechnicalAnalysis
2
+ # Exponential Moving Average
3
+ class Ema < Indicator
4
+
5
+ # Returns the symbol of the technical indicator
6
+ #
7
+ # @return [String] A string of the symbol of the technical indicator
8
+ def self.indicator_symbol
9
+ "ema"
10
+ end
11
+
12
+ # Returns the name of the technical indicator
13
+ #
14
+ # @return [String] A string of the name of the technical indicator
15
+ def self.indicator_name
16
+ "Exponential Moving Average"
17
+ end
18
+
19
+ # Returns an array of valid keys for options for this technical indicator
20
+ #
21
+ # @return [Array] An array of keys as symbols for valid options for this technical indicator
22
+ def self.valid_options
23
+ %i(period price_key date_time_key)
24
+ end
25
+
26
+ # Validates the provided options for this technical indicator
27
+ #
28
+ # @param options [Hash] The options for the technical indicator to be validated
29
+ #
30
+ # @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
31
+ def self.validate_options(options)
32
+ Validation.validate_options(options, valid_options)
33
+ end
34
+
35
+ # Calculates the minimum number of observations needed to calculate the technical indicator
36
+ #
37
+ # @param options [Hash] The options for the technical indicator
38
+ #
39
+ # @return [Integer] Returns the minimum number of observations needed to calculate the technical
40
+ # indicator based on the options provided
41
+ def self.min_data_size(period: 30, **params)
42
+ period.to_i
43
+ end
44
+
45
+ # Calculates the exponential moving average (EMA) for the data over the given period
46
+ # https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
47
+ #
48
+ # @param data [Array] Array of hashes with keys (:date_time, :value)
49
+ # @param period [Integer] The given period to calculate the EMA
50
+ # @param price_key [Symbol] The hash key for the price data. Default :value
51
+ # @param date_time_key [Symbol] The hash key for the date time data. Default :date_time
52
+ #
53
+ # @return [Array<EmaValue>] An array of EmaValue instances
54
+ def self.calculate(data, period: 30, price_key: :value, date_time_key: :date_time)
55
+ period = period.to_i
56
+ price_key = price_key.to_sym
57
+ date_time_key = date_time_key.to_sym
58
+ Validation.validate_numeric_data(data, price_key)
59
+ Validation.validate_length(data, min_data_size(period: period))
60
+ Validation.validate_date_time_key(data, date_time_key)
61
+
62
+ data = data.sort_by { |row| row[date_time_key] }
63
+
64
+ output = []
65
+ period_values = []
66
+ previous_ema = nil
67
+
68
+ data.each do |v|
69
+ period_values << v[price_key]
70
+ if period_values.size == period
71
+ ema = StockCalculation.ema(v[price_key], period_values, period, previous_ema)
72
+ previous_ema = ema
73
+
74
+ output << EmaValue.new(date_time: v[date_time_key], ema: ema)
75
+ period_values.shift
76
+ end
77
+ end
78
+
79
+ output.sort_by(&:date_time).reverse
80
+ end
81
+
82
+ end
83
+
84
+ # The value class to be returned by calculations
85
+ class EmaValue
86
+
87
+ # @return [String] the date_time of the obversation as it was provided
88
+ attr_accessor :date_time
89
+
90
+ # @return [Float] the ema calculation value
91
+ attr_accessor :ema
92
+
93
+ def initialize(date_time: nil, ema: nil)
94
+ @date_time = date_time
95
+ @ema = ema
96
+ end
97
+
98
+ # @return [Hash] the attributes as a hash
99
+ def to_hash
100
+ { date_time: @date_time, ema: @ema }
101
+ end
102
+ end
103
+ end
File without changes
File without changes
@@ -49,6 +49,7 @@ module TechnicalAnalysis
49
49
  TechnicalAnalysis::Uo,
50
50
  TechnicalAnalysis::Vi,
51
51
  TechnicalAnalysis::Vpt,
52
+ TechnicalAnalysis::Vwap,
52
53
  TechnicalAnalysis::Wr,
53
54
  ]
54
55
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -20,7 +20,7 @@ module TechnicalAnalysis
20
20
  #
21
21
  # @return [Array] An array of keys as symbols for valid options for this technical indicator
22
22
  def self.valid_options
23
- %i(period price_key)
23
+ %i(period price_key date_time_key)
24
24
  end
25
25
 
26
26
  # Validates the provided options for this technical indicator
@@ -48,16 +48,17 @@ module TechnicalAnalysis
48
48
  # @param data [Array] Array of hashes with keys (:date_time, :value)
49
49
  # @param period [Integer] The given period to calculate the RSI
50
50
  # @param price_key [Symbol] The hash key for the price data. Default :value
51
+ # @param date_time_key [Symbol] The hash key for the date time data. Default :date_time
51
52
  #
52
53
  # @return [Array<RsiValue>] An array of RsiValue instances
53
- def self.calculate(data, period: 14, price_key: :value)
54
+ def self.calculate(data, period: 14, price_key: :value, date_time_key: :date_time)
54
55
  period = period.to_i
55
56
  price_key = price_key.to_sym
56
57
  Validation.validate_numeric_data(data, price_key)
57
58
  Validation.validate_length(data, min_data_size(period: period))
58
- Validation.validate_date_time_key(data)
59
+ Validation.validate_date_time_key(data, date_time_key)
59
60
 
60
- data = data.sort_by { |row| row[:date_time] }
61
+ data = data.sort_by { |row| row[date_time_key] }
61
62
 
62
63
  output = []
63
64
  prev_price = data.shift[price_key]
@@ -96,7 +97,7 @@ module TechnicalAnalysis
96
97
  rsi = (100.00 - (100.00 / (1.00 + rs)))
97
98
  end
98
99
 
99
- output << RsiValue.new(date_time: v[:date_time], rsi: rsi)
100
+ output << RsiValue.new(date_time: v[date_time_key], rsi: rsi)
100
101
 
101
102
  prev_avg = { gain: avg_gain, loss: avg_loss }
102
103
  price_changes.shift
@@ -20,7 +20,7 @@ module TechnicalAnalysis
20
20
  #
21
21
  # @return [Array] An array of keys as symbols for valid options for this technical indicator
22
22
  def self.valid_options
23
- %i(period price_key)
23
+ %i(period price_key date_time_key)
24
24
  end
25
25
 
26
26
  # Validates the provided options for this technical indicator
@@ -48,16 +48,18 @@ module TechnicalAnalysis
48
48
  # @param data [Array] Array of hashes with keys (:date_time, :value)
49
49
  # @param period [Integer] The given period to calculate the SMA
50
50
  # @param price_key [Symbol] The hash key for the price data. Default :value
51
+ # @param date_time_key [Symbol] The hash key for the date time data. Default :date_time
51
52
  #
52
53
  # @return [Array<SmaValue>] An array of SmaValue instances
53
- def self.calculate(data, period: 30, price_key: :value)
54
+ def self.calculate(data, period: 30, price_key: :value, date_time_key: :date_time)
54
55
  period = period.to_i
55
56
  price_key = price_key.to_sym
57
+ date_time_key = date_time_key.to_sym
56
58
  Validation.validate_numeric_data(data, price_key)
57
59
  Validation.validate_length(data, min_data_size(period: period))
58
- Validation.validate_date_time_key(data)
60
+ Validation.validate_date_time_key(data, date_time_key)
59
61
 
60
- data = data.sort_by { |row| row[:date_time] }
62
+ data = data.sort_by { |row| row[date_time_key] }
61
63
 
62
64
  output = []
63
65
  period_values = []
@@ -65,7 +67,7 @@ module TechnicalAnalysis
65
67
  data.each do |v|
66
68
  period_values << v[price_key]
67
69
  if period_values.size == period
68
- output << SmaValue.new(date_time: v[:date_time], sma: ArrayHelper.average(period_values))
70
+ output << SmaValue.new(date_time: v[date_time_key], sma: ArrayHelper.average(period_values))
69
71
  period_values.shift
70
72
  end
71
73
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,97 @@
1
+ module TechnicalAnalysis
2
+ # Volume Weighted Average Price
3
+ class Vwap < Indicator
4
+
5
+ # Returns the symbol of the technical indicator
6
+ #
7
+ # @return [String] A string of the symbol of the technical indicator
8
+ def self.indicator_symbol
9
+ "vwap"
10
+ end
11
+
12
+ # Returns the name of the technical indicator
13
+ #
14
+ # @return [String] A string of the name of the technical indicator
15
+ def self.indicator_name
16
+ "Volume Weighted Average Price"
17
+ end
18
+
19
+ # Returns an array of valid keys for options for this technical indicator
20
+ #
21
+ # @return [Array] An array of keys as symbols for valid options for this technical indicator
22
+ def self.valid_options
23
+ []
24
+ end
25
+
26
+ # Validates the provided options for this technical indicator
27
+ #
28
+ # @param options [Hash] The options for the technical indicator to be validated
29
+ #
30
+ # @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
31
+ def self.validate_options(options)
32
+ return true if options == {}
33
+ raise Validation::ValidationError.new "This indicator doesn't accept any options."
34
+ end
35
+
36
+ # Calculates the minimum number of observations needed to calculate the technical indicator
37
+ #
38
+ # @param options [Hash] The options for the technical indicator
39
+ #
40
+ # @return [Integer] Returns the minimum number of observations needed to calculate the technical
41
+ # indicator based on the options provided
42
+ def self.min_data_size(**params)
43
+ 1
44
+ end
45
+
46
+ # Calculates the volume weighted average price (VWAP) for the data
47
+ # https://en.wikipedia.org/wiki/Volume-weighted_average_price
48
+ #
49
+ # @param data [Array] Array of hashes with keys (:date_time, :high, :low, :close, :volume)
50
+ #
51
+ # @return [Array<VwapValue>] An array of VwapValue instances
52
+ def self.calculate(data)
53
+ Validation.validate_numeric_data(data, :high, :low, :close, :volume)
54
+ Validation.validate_length(data, min_data_size)
55
+ Validation.validate_date_time_key(data)
56
+
57
+ data = data.sort_by { |row| row[:date_time] }
58
+
59
+ output = []
60
+ cumm_volume = 0
61
+ cumm_volume_x_typical_price = 0
62
+
63
+ data.each do |v|
64
+ typical_price = StockCalculation.typical_price(v)
65
+ cumm_volume_x_typical_price += v[:volume] * typical_price
66
+ cumm_volume += v[:volume]
67
+ vwap = cumm_volume_x_typical_price.to_f / cumm_volume.to_f
68
+
69
+ output << VwapValue.new(date_time: v[:date_time], vwap: vwap)
70
+ end
71
+
72
+ output.sort_by(&:date_time).reverse
73
+ end
74
+
75
+ end
76
+
77
+ # The value class to be returned by calculations
78
+ class VwapValue
79
+
80
+ # @return [String] the date_time of the obversation as it was provided
81
+ attr_accessor :date_time
82
+
83
+ # @return [Float] the vwap calculation value
84
+ attr_accessor :vwap
85
+
86
+ def initialize(date_time: nil, vwap: nil)
87
+ @date_time = date_time
88
+ @vwap = vwap
89
+ end
90
+
91
+ # @return [Hash] the attributes as a hash
92
+ def to_hash
93
+ { date_time: @date_time, vwap: @vwap }
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,100 @@
1
+ module TechnicalAnalysis
2
+ # Weighted Moving Average
3
+ class Wma < Indicator
4
+
5
+ # Returns the symbol of the technical indicator
6
+ #
7
+ # @return [String] A string of the symbol of the technical indicator
8
+ def self.indicator_symbol
9
+ "wma"
10
+ end
11
+
12
+ # Returns the name of the technical indicator
13
+ #
14
+ # @return [String] A string of the name of the technical indicator
15
+ def self.indicator_name
16
+ "Weighted Moving Average"
17
+ end
18
+
19
+ # Returns an array of valid keys for options for this technical indicator
20
+ #
21
+ # @return [Array] An array of keys as symbols for valid options for this technical indicator
22
+ def self.valid_options
23
+ %i(period price_key date_time_key)
24
+ end
25
+
26
+ # Validates the provided options for this technical indicator
27
+ #
28
+ # @param options [Hash] The options for the technical indicator to be validated
29
+ #
30
+ # @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
31
+ def self.validate_options(options)
32
+ Validation.validate_options(options, valid_options)
33
+ end
34
+
35
+ # Calculates the minimum number of observations needed to calculate the technical indicator
36
+ #
37
+ # @param options [Hash] The options for the technical indicator
38
+ #
39
+ # @return [Integer] Returns the minimum number of observations needed to calculate the technical
40
+ # indicator based on the options provided
41
+ def self.min_data_size(period: 30, **params)
42
+ period.to_i
43
+ end
44
+
45
+ # Calculates the weighted moving average (WMA) for the data over the given period
46
+ # https://en.wikipedia.org/wiki/Moving_average#Weighted_moving_average
47
+ #
48
+ # @param data [Array] Array of hashes with keys (:date_time, :value)
49
+ # @param period [Integer] The given period to calculate the WMA
50
+ # @param price_key [Symbol] The hash key for the price data. Default :value
51
+ # @param date_time_key [Symbol] The hash key for the date time data. Default :date_time
52
+ #
53
+ # @return [Array<WmaValue>] An array of WmaValue instances
54
+ def self.calculate(data, period: 30, price_key: :value, date_time_key: :date_time)
55
+ period = period.to_i
56
+ price_key = price_key.to_sym
57
+ date_time_key = date_time_key.to_sym
58
+ Validation.validate_numeric_data(data, price_key)
59
+ Validation.validate_length(data, min_data_size(period: period))
60
+ Validation.validate_date_time_key(data, date_time_key)
61
+
62
+ data = data.sort_by { |row| row[date_time_key] }
63
+
64
+ output = []
65
+ period_values = []
66
+ previous_wma = nil
67
+
68
+ data.each do |v|
69
+ period_values << v[price_key]
70
+ if period_values.size == period
71
+ wma = StockCalculation.wma(period_values)
72
+ output << WmaValue.new(date_time: v[date_time_key], wma: wma)
73
+ period_values.shift
74
+ end
75
+ end
76
+ output.sort_by(&:date_time).reverse
77
+ end
78
+
79
+ end
80
+
81
+ # The value class to be returned by calculations
82
+ class WmaValue
83
+
84
+ # @return [String] the date_time of the obversation as it was provided
85
+ attr_accessor :date_time
86
+
87
+ # @return [Float] the wma calculation value
88
+ attr_accessor :wma
89
+
90
+ def initialize(date_time: nil, wma: nil)
91
+ @date_time = date_time
92
+ @wma = wma
93
+ end
94
+
95
+ # @return [Hash] the attributes as a hash
96
+ def to_hash
97
+ { date_time: @date_time, wma: @wma }
98
+ end
99
+ end
100
+ end
File without changes
File without changes
File without changes
@@ -6,12 +6,12 @@ class SpecHelper
6
6
  FLOAT_KEYS = [:open, :high, :low, :close].freeze
7
7
  INTEGER_KEYS = [:volume].freeze
8
8
 
9
- def self.get_test_data(*columns)
9
+ def self.get_test_data(*columns, date_time_key: :date_time)
10
10
  @data = CSV.read(TEST_DATA_PATH, headers: true)
11
11
  columns = columns.map(&:to_sym)
12
12
  output = []
13
13
  @data.each do |v|
14
- col_hash = { date_time: v["date_time"] }
14
+ col_hash = { date_time_key => v["date_time"] }
15
15
  columns.each do |col|
16
16
  value = v[col.to_s]
17
17
  value = value.to_f if FLOAT_KEYS.include?(col)
File without changes
@@ -0,0 +1,115 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "EMA" do
6
+ input_data = SpecHelper.get_test_data(:close, date_time_key: :timestep)
7
+ indicator = TechnicalAnalysis::Ema
8
+
9
+ describe 'Exponential Moving Average' do
10
+ it 'Calculates EMA (5 day)' do
11
+ output = indicator.calculate(input_data, period: 5, price_key: :close, date_time_key: :timestep)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :ema=>151.1937160522574},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :ema=>150.1355740783861},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :ema=>149.82836111757913},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :ema=>150.77754167636868},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :ema=>152.03631251455303},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :ema=>156.95946877182956},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :ema=>156.47920315774434},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :ema=>155.8488047366165},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :ema=>155.65820710492477},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :ema=>155.41231065738714},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :ema=>154.53346598608073},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :ema=>158.3851989791211},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :ema=>162.21279846868165},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :ema=>164.90419770302248},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :ema=>166.91129655453372},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :ema=>167.3319448318006},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :ema=>169.02791724770088},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :ema=>170.80187587155132},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :ema=>170.72781380732698},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :ema=>171.54172071099046},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :ema=>172.99758106648568},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :ema=>174.69637159972854},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :ema=>177.7995573995928},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :ema=>179.33933609938921},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :ema=>180.6640041490838},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :ema=>178.58600622362573},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :ema=>178.5890093354386},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :ema=>178.10851400315786},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :ema=>176.69277100473678},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :ema=>177.91915650710516},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :ema=>179.56873476065772},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :ema=>183.20810214098657},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :ema=>186.42215321147984},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :ema=>191.14322981721975},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :ema=>193.7848447258296},
50
+ {:date_time=>"2018-11-15T00:00:00.000Z", :ema=>193.9122670887444},
51
+ {:date_time=>"2018-11-14T00:00:00.000Z", :ema=>195.1634006331166},
52
+ {:date_time=>"2018-11-13T00:00:00.000Z", :ema=>199.34510094967487},
53
+ {:date_time=>"2018-11-12T00:00:00.000Z", :ema=>202.90265142451233},
54
+ {:date_time=>"2018-11-09T00:00:00.000Z", :ema=>207.26897713676848},
55
+ {:date_time=>"2018-11-08T00:00:00.000Z", :ema=>208.66846570515273},
56
+ {:date_time=>"2018-11-07T00:00:00.000Z", :ema=>208.75769855772907},
57
+ {:date_time=>"2018-11-06T00:00:00.000Z", :ema=>208.16154783659363},
58
+ {:date_time=>"2018-11-05T00:00:00.000Z", :ema=>210.35732175489045},
59
+ {:date_time=>"2018-11-02T00:00:00.000Z", :ema=>214.7409826323357},
60
+ {:date_time=>"2018-11-01T00:00:00.000Z", :ema=>218.37147394850354},
61
+ {:date_time=>"2018-10-31T00:00:00.000Z", :ema=>216.44721092275532},
62
+ {:date_time=>"2018-10-30T00:00:00.000Z", :ema=>215.24081638413296},
63
+ {:date_time=>"2018-10-29T00:00:00.000Z", :ema=>216.21122457619944},
64
+ {:date_time=>"2018-10-26T00:00:00.000Z", :ema=>218.19683686429914},
65
+ {:date_time=>"2018-10-25T00:00:00.000Z", :ema=>219.1452552964487},
66
+ {:date_time=>"2018-10-24T00:00:00.000Z", :ema=>218.81788294467307},
67
+ {:date_time=>"2018-10-23T00:00:00.000Z", :ema=>220.6818244170096},
68
+ {:date_time=>"2018-10-22T00:00:00.000Z", :ema=>219.65773662551442},
69
+ {:date_time=>"2018-10-19T00:00:00.000Z", :ema=>219.1616049382716},
70
+ {:date_time=>"2018-10-18T00:00:00.000Z", :ema=>219.0874074074074},
71
+ {:date_time=>"2018-10-17T00:00:00.000Z", :ema=>220.6211111111111},
72
+ {:date_time=>"2018-10-16T00:00:00.000Z", :ema=>220.33666666666667},
73
+ {:date_time=>"2018-10-15T00:00:00.000Z", :ema=>219.43}
74
+ ]
75
+
76
+ expect(normalized_output).to eq(expected_output)
77
+ end
78
+
79
+ it "Throws exception if not enough data" do
80
+ expect {indicator.calculate(input_data, period: input_data.size+1, price_key: :close)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
81
+ end
82
+
83
+ it 'Returns the symbol' do
84
+ indicator_symbol = indicator.indicator_symbol
85
+ expect(indicator_symbol).to eq('ema')
86
+ end
87
+
88
+ it 'Returns the name' do
89
+ indicator_name = indicator.indicator_name
90
+ expect(indicator_name).to eq('Exponential Moving Average')
91
+ end
92
+
93
+ it 'Returns the valid options' do
94
+ valid_options = indicator.valid_options
95
+ expect(valid_options).to eq(%i(period price_key date_time_key))
96
+ end
97
+
98
+ it 'Validates options' do
99
+ valid_options = { period: 22, price_key: :close, date_time_key: :timestep }
100
+ options_validated = indicator.validate_options(valid_options)
101
+ expect(options_validated).to eq(true)
102
+ end
103
+
104
+ it 'Throws exception for invalid options' do
105
+ invalid_options = { test: 10 }
106
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
107
+ end
108
+
109
+ it 'Calculates minimum data size' do
110
+ options = { period: 4 }
111
+ expect(indicator.min_data_size(options)).to eq(4)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -38,7 +38,7 @@ describe 'Indicators' do
38
38
 
39
39
  it 'Calculates valid_options' do
40
40
  valid_options = indicator.calculate('sma', [], :valid_options, { period: 20, price_key: :close })
41
- expect(valid_options).to eq(%i(period price_key))
41
+ expect(valid_options).to eq(%i(period price_key date_time_key))
42
42
  end
43
43
 
44
44
  it 'Calculates validate_options' do
@@ -3,12 +3,12 @@ require 'spec_helper'
3
3
 
4
4
  describe 'Indicators' do
5
5
  describe "RSI" do
6
- input_data = SpecHelper.get_test_data(:close)
6
+ input_data = SpecHelper.get_test_data(:close, date_time_key: :timestep)
7
7
  indicator = TechnicalAnalysis::Rsi
8
8
 
9
9
  describe 'Relative Strength Index' do
10
10
  it 'Calculates RSI (14 day)' do
11
- output = indicator.calculate(input_data, period: 14, price_key: :close)
11
+ output = indicator.calculate(input_data, period: 14, price_key: :close, date_time_key: :timestep)
12
12
  normalized_output = output.map(&:to_hash)
13
13
 
14
14
  expected_output = [
@@ -67,7 +67,7 @@ describe 'Indicators' do
67
67
  end
68
68
 
69
69
  it "Throws exception if not enough data" do
70
- expect {indicator.calculate(input_data, period: input_data.size+2, price_key: :close)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
70
+ expect {indicator.calculate(input_data, period: input_data.size+2, price_key: :close, date_time_key: :time)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
71
71
  end
72
72
 
73
73
  it 'Returns the symbol' do
@@ -82,11 +82,11 @@ describe 'Indicators' do
82
82
 
83
83
  it 'Returns the valid options' do
84
84
  valid_options = indicator.valid_options
85
- expect(valid_options).to eq(%i(period price_key))
85
+ expect(valid_options).to eq(%i(period price_key date_time_key))
86
86
  end
87
87
 
88
88
  it 'Validates options' do
89
- valid_options = { period: 22, price_key: :close }
89
+ valid_options = { period: 22, price_key: :close, date_time_key: :timespec }
90
90
  options_validated = indicator.validate_options(valid_options)
91
91
  expect(options_validated).to eq(true)
92
92
  end
@@ -3,12 +3,12 @@ require 'spec_helper'
3
3
 
4
4
  describe 'Indicators' do
5
5
  describe "SMA" do
6
- input_data = SpecHelper.get_test_data(:close)
6
+ input_data = SpecHelper.get_test_data(:close, date_time_key: :timestep)
7
7
  indicator = TechnicalAnalysis::Sma
8
8
 
9
9
  describe 'Simple Moving Average' do
10
10
  it 'Calculates SMA (5 day)' do
11
- output = indicator.calculate(input_data, period: 5, price_key: :close)
11
+ output = indicator.calculate(input_data, period: 5, price_key: :close, date_time_key: :timestep)
12
12
  normalized_output = output.map(&:to_hash)
13
13
 
14
14
  expected_output = [
@@ -92,11 +92,11 @@ describe 'Indicators' do
92
92
 
93
93
  it 'Returns the valid options' do
94
94
  valid_options = indicator.valid_options
95
- expect(valid_options).to eq(%i(period price_key))
95
+ expect(valid_options).to eq(%i(period price_key date_time_key))
96
96
  end
97
97
 
98
98
  it 'Validates options' do
99
- valid_options = { period: 22, price_key: :close }
99
+ valid_options = { period: 22, price_key: :close, date_time_key: :timestep }
100
100
  options_validated = indicator.validate_options(valid_options)
101
101
  expect(options_validated).to eq(true)
102
102
  end
@@ -0,0 +1,119 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "VWAP" do
6
+ input_data = SpecHelper.get_test_data(:volume, :high, :low, :close)
7
+ indicator = TechnicalAnalysis::Vwap
8
+
9
+ describe 'Volume Weighted Average Price' do
10
+ it 'Calculates VWAP' do
11
+ output = indicator.calculate(input_data)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :vwap=>183.55426863053765},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :vwap=>184.06635227887782},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :vwap=>184.57507590747517},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :vwap=>185.3413093796368},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :vwap=>186.19781421865375},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :vwap=>187.76843731172002},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :vwap=>188.21552447966795},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :vwap=>188.64862275471472},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :vwap=>189.2144975598256},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :vwap=>189.9889456493791},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :vwap=>190.91953458787302},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :vwap=>191.6297166885398},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :vwap=>193.36567531270202},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :vwap=>194.463693818502},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :vwap=>195.21670632642397},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :vwap=>195.7127882800064},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :vwap=>196.3956319251192},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :vwap=>197.03092567150182},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :vwap=>197.47196479514173},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :vwap=>198.00221355940155},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :vwap=>198.7429667210245},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :vwap=>199.85255982037654},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :vwap=>200.57927838438474},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :vwap=>201.28731920049952},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :vwap=>201.8731568533139},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :vwap=>202.34554208942396},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :vwap=>202.95867828112358},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :vwap=>203.60135199869035},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :vwap=>204.38651817163168},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :vwap=>205.30361785858756},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :vwap=>206.36274937523916},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :vwap=>206.94494917055295},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :vwap=>207.6427516507437},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :vwap=>209.27699957144853},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :vwap=>210.05211892678065},
50
+ {:date_time=>"2018-11-15T00:00:00.000Z", :vwap=>210.59952347188045},
51
+ {:date_time=>"2018-11-14T00:00:00.000Z", :vwap=>211.4589551886514},
52
+ {:date_time=>"2018-11-13T00:00:00.000Z", :vwap=>212.75803369499064},
53
+ {:date_time=>"2018-11-12T00:00:00.000Z", :vwap=>213.6551619737374},
54
+ {:date_time=>"2018-11-09T00:00:00.000Z", :vwap=>214.61043573066618},
55
+ {:date_time=>"2018-11-08T00:00:00.000Z", :vwap=>215.00076838969952},
56
+ {:date_time=>"2018-11-07T00:00:00.000Z", :vwap=>215.18761540540189},
57
+ {:date_time=>"2018-11-06T00:00:00.000Z", :vwap=>215.4663555747261},
58
+ {:date_time=>"2018-11-05T00:00:00.000Z", :vwap=>215.93354729003553},
59
+ {:date_time=>"2018-11-02T00:00:00.000Z", :vwap=>217.20680955786048},
60
+ {:date_time=>"2018-11-01T00:00:00.000Z", :vwap=>218.35223508156088},
61
+ {:date_time=>"2018-10-31T00:00:00.000Z", :vwap=>218.1692825392702},
62
+ {:date_time=>"2018-10-30T00:00:00.000Z", :vwap=>218.13783195840017},
63
+ {:date_time=>"2018-10-29T00:00:00.000Z", :vwap=>218.5155747290589},
64
+ {:date_time=>"2018-10-26T00:00:00.000Z", :vwap=>219.05970452474796},
65
+ {:date_time=>"2018-10-25T00:00:00.000Z", :vwap=>219.3440526461074},
66
+ {:date_time=>"2018-10-24T00:00:00.000Z", :vwap=>219.34643675779574},
67
+ {:date_time=>"2018-10-23T00:00:00.000Z", :vwap=>219.4951598586493},
68
+ {:date_time=>"2018-10-22T00:00:00.000Z", :vwap=>219.41092910048724},
69
+ {:date_time=>"2018-10-19T00:00:00.000Z", :vwap=>219.26375336145847},
70
+ {:date_time=>"2018-10-18T00:00:00.000Z", :vwap=>219.25541442468506},
71
+ {:date_time=>"2018-10-17T00:00:00.000Z", :vwap=>219.65735634491844},
72
+ {:date_time=>"2018-10-16T00:00:00.000Z", :vwap=>219.5125052055069},
73
+ {:date_time=>"2018-10-15T00:00:00.000Z", :vwap=>219.34283327445593},
74
+ {:date_time=>"2018-10-12T00:00:00.000Z", :vwap=>219.44169580294155},
75
+ {:date_time=>"2018-10-11T00:00:00.000Z", :vwap=>219.0592293697168},
76
+ {:date_time=>"2018-10-10T00:00:00.000Z", :vwap=>221.89869420553177},
77
+ {:date_time=>"2018-10-09T00:00:00.000Z", :vwap=>225.4620666666667}
78
+ ]
79
+
80
+ expect(normalized_output).to eq(expected_output)
81
+ end
82
+
83
+ it "Throws exception if not enough data" do
84
+ expect {indicator.calculate([])}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
85
+ end
86
+
87
+ it 'Returns the symbol' do
88
+ indicator_symbol = indicator.indicator_symbol
89
+ expect(indicator_symbol).to eq('vwap')
90
+ end
91
+
92
+ it 'Returns the name' do
93
+ indicator_name = indicator.indicator_name
94
+ expect(indicator_name).to eq('Volume Weighted Average Price')
95
+ end
96
+
97
+ it 'Returns the valid options' do
98
+ valid_options = indicator.valid_options
99
+ expect(valid_options).to eq([])
100
+ end
101
+
102
+ it 'Validates options' do
103
+ valid_options = {}
104
+ options_validated = indicator.validate_options(valid_options)
105
+ expect(options_validated).to eq(true)
106
+ end
107
+
108
+ it 'Throws exception for invalid options' do
109
+ invalid_options = { test: 10 }
110
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
111
+ end
112
+
113
+ it 'Calculates minimum data size' do
114
+ options = {}
115
+ expect(indicator.min_data_size(options)).to eq(1)
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,115 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "WMA" do
6
+ input_data = SpecHelper.get_test_data(:close, date_time_key: :timestep)
7
+ indicator = TechnicalAnalysis::Wma
8
+
9
+ describe 'Weighted Moving Average' do
10
+ it 'Calculates WMA (5 day)' do
11
+ output = indicator.calculate(input_data, period: 5, price_key: :close, date_time_key: :timestep)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :wma=>150.13666666666666},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :wma=>148.83666666666667},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :wma=>148.856},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :wma=>150.36866666666666},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :wma=>152.29733333333334},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :wma=>157.248},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :wma=>156.216},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :wma=>154.77666666666667},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :wma=>153.88066666666668},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :wma=>153.3273333333333},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :wma=>153.02733333333333},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :wma=>157.31466666666668},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :wma=>161.28533333333334},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :wma=>164.164},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :wma=>166.23666666666665},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :wma=>166.75333333333333},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :wma=>168.35733333333334},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :wma=>169.64866666666666},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :wma=>169.368},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :wma=>170.21},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :wma=>172.288},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :wma=>174.64133333333334},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :wma=>178.102},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :wma=>179.9006666666667},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :wma=>180.87933333333334},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :wma=>178.468},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :wma=>177.71733333333333},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :wma=>176.45866666666666},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :wma=>174.47266666666667},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :wma=>175.49466666666666},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :wma=>177.65066666666667},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :wma=>181.858},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :wma=>185.23666666666668},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :wma=>189.56533333333334},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :wma=>191.488},
50
+ {:date_time=>"2018-11-15T00:00:00.000Z", :wma=>191.58333333333331},
51
+ {:date_time=>"2018-11-14T00:00:00.000Z", :wma=>193.524},
52
+ {:date_time=>"2018-11-13T00:00:00.000Z", :wma=>198.54466666666667},
53
+ {:date_time=>"2018-11-12T00:00:00.000Z", :wma=>202.52466666666666},
54
+ {:date_time=>"2018-11-09T00:00:00.000Z", :wma=>206.35266666666666},
55
+ {:date_time=>"2018-11-08T00:00:00.000Z", :wma=>206.948},
56
+ {:date_time=>"2018-11-07T00:00:00.000Z", :wma=>207.11866666666668},
57
+ {:date_time=>"2018-11-06T00:00:00.000Z", :wma=>207.39666666666665},
58
+ {:date_time=>"2018-11-05T00:00:00.000Z", :wma=>210.37},
59
+ {:date_time=>"2018-11-02T00:00:00.000Z", :wma=>214.78},
60
+ {:date_time=>"2018-11-01T00:00:00.000Z", :wma=>217.81466666666665},
61
+ {:date_time=>"2018-10-31T00:00:00.000Z", :wma=>215.7746666666667},
62
+ {:date_time=>"2018-10-30T00:00:00.000Z", :wma=>214.60333333333332},
63
+ {:date_time=>"2018-10-29T00:00:00.000Z", :wma=>215.91400000000002},
64
+ {:date_time=>"2018-10-26T00:00:00.000Z", :wma=>218.13866666666667},
65
+ {:date_time=>"2018-10-25T00:00:00.000Z", :wma=>219.21066666666667},
66
+ {:date_time=>"2018-10-24T00:00:00.000Z", :wma=>218.86400000000003},
67
+ {:date_time=>"2018-10-23T00:00:00.000Z", :wma=>220.49400000000003},
68
+ {:date_time=>"2018-10-22T00:00:00.000Z", :wma=>219.53866666666664},
69
+ {:date_time=>"2018-10-19T00:00:00.000Z", :wma=>219.05733333333333},
70
+ {:date_time=>"2018-10-18T00:00:00.000Z", :wma=>219.20933333333335},
71
+ {:date_time=>"2018-10-17T00:00:00.000Z", :wma=>220.35333333333335},
72
+ {:date_time=>"2018-10-16T00:00:00.000Z", :wma=>219.452},
73
+ {:date_time=>"2018-10-15T00:00:00.000Z", :wma=>218.54533333333333}
74
+ ]
75
+
76
+ expect(normalized_output).to eq(expected_output)
77
+ end
78
+
79
+ it "Throws exception if not enough data" do
80
+ expect {indicator.calculate(input_data, period: input_data.size+1, price_key: :close)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
81
+ end
82
+
83
+ it 'Returns the symbol' do
84
+ indicator_symbol = indicator.indicator_symbol
85
+ expect(indicator_symbol).to eq('wma')
86
+ end
87
+
88
+ it 'Returns the name' do
89
+ indicator_name = indicator.indicator_name
90
+ expect(indicator_name).to eq('Weighted Moving Average')
91
+ end
92
+
93
+ it 'Returns the valid options' do
94
+ valid_options = indicator.valid_options
95
+ expect(valid_options).to eq(%i(period price_key date_time_key))
96
+ end
97
+
98
+ it 'Validates options' do
99
+ valid_options = { period: 22, price_key: :close, date_time_key: :timestep }
100
+ options_validated = indicator.validate_options(valid_options)
101
+ expect(options_validated).to eq(true)
102
+ end
103
+
104
+ it 'Throws exception for invalid options' do
105
+ invalid_options = { test: 10 }
106
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
107
+ end
108
+
109
+ it 'Calculates minimum data size' do
110
+ options = { period: 4 }
111
+ expect(indicator.min_data_size(**options)).to eq(4)
112
+ end
113
+ end
114
+ end
115
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: technical-analysis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Intrinio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-06 00:00:00.000000000 Z
11
+ date: 2020-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '12.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '12.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.9.6
61
+ version: 0.9.20
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.9.6
68
+ version: 0.9.20
69
69
  description: A Ruby library for performing technical analysis on stock prices and
70
70
  other data sets.
71
71
  email:
@@ -92,6 +92,7 @@ files:
92
92
  - lib/technical_analysis/indicators/dlr.rb
93
93
  - lib/technical_analysis/indicators/dpo.rb
94
94
  - lib/technical_analysis/indicators/dr.rb
95
+ - lib/technical_analysis/indicators/ema.rb
95
96
  - lib/technical_analysis/indicators/eom.rb
96
97
  - lib/technical_analysis/indicators/fi.rb
97
98
  - lib/technical_analysis/indicators/ichimoku.rb
@@ -112,6 +113,8 @@ files:
112
113
  - lib/technical_analysis/indicators/uo.rb
113
114
  - lib/technical_analysis/indicators/vi.rb
114
115
  - lib/technical_analysis/indicators/vpt.rb
116
+ - lib/technical_analysis/indicators/vwap.rb
117
+ - lib/technical_analysis/indicators/wma.rb
115
118
  - lib/technical_analysis/indicators/wr.rb
116
119
  - spec/helpers/array_helper_spec.rb
117
120
  - spec/helpers/validaton_spec.rb
@@ -130,6 +133,7 @@ files:
130
133
  - spec/technical_analysis/indicators/dlr_spec.rb
131
134
  - spec/technical_analysis/indicators/dpo_spec.rb
132
135
  - spec/technical_analysis/indicators/dr_spec.rb
136
+ - spec/technical_analysis/indicators/ema_spec.rb
133
137
  - spec/technical_analysis/indicators/eom_spec.rb
134
138
  - spec/technical_analysis/indicators/fi_spec.rb
135
139
  - spec/technical_analysis/indicators/ichimoku_spec.rb
@@ -150,6 +154,8 @@ files:
150
154
  - spec/technical_analysis/indicators/uo_spec.rb
151
155
  - spec/technical_analysis/indicators/vi_spec.rb
152
156
  - spec/technical_analysis/indicators/vpt_spec.rb
157
+ - spec/technical_analysis/indicators/vwap_spec.rb
158
+ - spec/technical_analysis/indicators/wma_spec.rb
153
159
  - spec/technical_analysis/indicators/wr_spec.rb
154
160
  homepage: https://github.com/intrinio/technical-analysis
155
161
  licenses: []