technical-analysis 0.1.0 → 0.2.3

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/lib/technical-analysis.rb +0 -0
  3. data/lib/technical_analysis.rb +2 -0
  4. data/lib/technical_analysis/helpers/array_helper.rb +0 -0
  5. data/lib/technical_analysis/helpers/stock_calculation.rb +0 -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 +0 -0
  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/wr.rb +0 -0
  43. data/spec/helpers/array_helper_spec.rb +0 -0
  44. data/spec/helpers/validaton_spec.rb +0 -0
  45. data/spec/spec_helper.rb +2 -2
  46. data/spec/ta_test_data.csv +0 -0
  47. data/spec/technical_analysis/indicators/adi_spec.rb +0 -0
  48. data/spec/technical_analysis/indicators/adtv_spec.rb +0 -0
  49. data/spec/technical_analysis/indicators/adx_spec.rb +0 -0
  50. data/spec/technical_analysis/indicators/ao_spec.rb +0 -0
  51. data/spec/technical_analysis/indicators/atr_spec.rb +0 -0
  52. data/spec/technical_analysis/indicators/bb_spec.rb +0 -0
  53. data/spec/technical_analysis/indicators/cci_spec.rb +0 -0
  54. data/spec/technical_analysis/indicators/cmf_spec.rb +0 -0
  55. data/spec/technical_analysis/indicators/cr_spec.rb +0 -0
  56. data/spec/technical_analysis/indicators/dc_spec.rb +0 -0
  57. data/spec/technical_analysis/indicators/dlr_spec.rb +0 -0
  58. data/spec/technical_analysis/indicators/dpo_spec.rb +0 -0
  59. data/spec/technical_analysis/indicators/dr_spec.rb +0 -0
  60. data/spec/technical_analysis/indicators/ema_spec.rb +115 -0
  61. data/spec/technical_analysis/indicators/eom_spec.rb +0 -0
  62. data/spec/technical_analysis/indicators/fi_spec.rb +0 -0
  63. data/spec/technical_analysis/indicators/ichimoku_spec.rb +0 -0
  64. data/spec/technical_analysis/indicators/indicator_spec.rb +1 -1
  65. data/spec/technical_analysis/indicators/kc_spec.rb +0 -0
  66. data/spec/technical_analysis/indicators/kst_spec.rb +0 -0
  67. data/spec/technical_analysis/indicators/macd_spec.rb +0 -0
  68. data/spec/technical_analysis/indicators/mfi_spec.rb +0 -0
  69. data/spec/technical_analysis/indicators/mi_spec.rb +0 -0
  70. data/spec/technical_analysis/indicators/nvi_spec.rb +0 -0
  71. data/spec/technical_analysis/indicators/obv_mean_spec.rb +0 -0
  72. data/spec/technical_analysis/indicators/obv_spec.rb +0 -0
  73. data/spec/technical_analysis/indicators/rsi_spec.rb +0 -0
  74. data/spec/technical_analysis/indicators/sma_spec.rb +4 -4
  75. data/spec/technical_analysis/indicators/sr_spec.rb +0 -0
  76. data/spec/technical_analysis/indicators/trix_spec.rb +0 -0
  77. data/spec/technical_analysis/indicators/tsi_spec.rb +0 -0
  78. data/spec/technical_analysis/indicators/uo_spec.rb +0 -0
  79. data/spec/technical_analysis/indicators/vi_spec.rb +0 -0
  80. data/spec/technical_analysis/indicators/vpt_spec.rb +0 -0
  81. data/spec/technical_analysis/indicators/vwap_spec.rb +119 -0
  82. data/spec/technical_analysis/indicators/wr_spec.rb +0 -0
  83. metadata +14 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f408c4203ba656b1aa458266b8ed6ec656f91f2e
4
- data.tar.gz: 18c7d6c4465b9ddbf6627508672e3a8f3295a597
3
+ metadata.gz: d638cd93a0c547b5882077a74f23b899632901b1
4
+ data.tar.gz: 222ea519ae143925f661d8917fc44d25abc571cd
5
5
  SHA512:
6
- metadata.gz: dcb6a96e644cc93c7f48866aa067c6cac7ca7c226cfeb3a71bd4a1789f258dee846b19c774a95560001cff93a8e6c693d4386a3247ea7aebb404beb37121f7bb
7
- data.tar.gz: 42e5e7bbfedb0f48a92b5107edfa2fda26c616cdb67bd91984e53fbd47f145fb50323da63268b34722202c71029d75fabfd94dba0136497baf508e874289c877
6
+ metadata.gz: 151f837f5c897ea12cdfe3f4edaf70b10be35a76080d001555ad4c9f0db1f9cee8ecdad78cb2182170f8f7328cd8ffc011b5de3c09d432391f517e55175cba3c
7
+ data.tar.gz: f59019103e1a6aa43c3f9ef4e83601c2af9882591f4458faf5012f48b69571aabb6112b2a1d7f12c35d4d60c824ac6138e46d1732ea1a969367c6bbaec9b7460
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,5 @@ 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'
41
43
  require 'technical_analysis/indicators/wr'
@@ -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
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,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
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 "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
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.0
4
+ version: 0.2.3
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-03 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,15 +58,16 @@ 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
69
- description: Intrinio Technical Analysis
68
+ version: 0.9.20
69
+ description: A Ruby library for performing technical analysis on stock prices and
70
+ other data sets.
70
71
  email:
71
72
  - admin@intrinio.com
72
73
  executables: []
@@ -91,6 +92,7 @@ files:
91
92
  - lib/technical_analysis/indicators/dlr.rb
92
93
  - lib/technical_analysis/indicators/dpo.rb
93
94
  - lib/technical_analysis/indicators/dr.rb
95
+ - lib/technical_analysis/indicators/ema.rb
94
96
  - lib/technical_analysis/indicators/eom.rb
95
97
  - lib/technical_analysis/indicators/fi.rb
96
98
  - lib/technical_analysis/indicators/ichimoku.rb
@@ -111,6 +113,7 @@ files:
111
113
  - lib/technical_analysis/indicators/uo.rb
112
114
  - lib/technical_analysis/indicators/vi.rb
113
115
  - lib/technical_analysis/indicators/vpt.rb
116
+ - lib/technical_analysis/indicators/vwap.rb
114
117
  - lib/technical_analysis/indicators/wr.rb
115
118
  - spec/helpers/array_helper_spec.rb
116
119
  - spec/helpers/validaton_spec.rb
@@ -129,6 +132,7 @@ files:
129
132
  - spec/technical_analysis/indicators/dlr_spec.rb
130
133
  - spec/technical_analysis/indicators/dpo_spec.rb
131
134
  - spec/technical_analysis/indicators/dr_spec.rb
135
+ - spec/technical_analysis/indicators/ema_spec.rb
132
136
  - spec/technical_analysis/indicators/eom_spec.rb
133
137
  - spec/technical_analysis/indicators/fi_spec.rb
134
138
  - spec/technical_analysis/indicators/ichimoku_spec.rb
@@ -149,6 +153,7 @@ files:
149
153
  - spec/technical_analysis/indicators/uo_spec.rb
150
154
  - spec/technical_analysis/indicators/vi_spec.rb
151
155
  - spec/technical_analysis/indicators/vpt_spec.rb
156
+ - spec/technical_analysis/indicators/vwap_spec.rb
152
157
  - spec/technical_analysis/indicators/wr_spec.rb
153
158
  homepage: https://github.com/intrinio/technical-analysis
154
159
  licenses: []
@@ -173,5 +178,6 @@ rubyforge_project:
173
178
  rubygems_version: 2.5.2.2
174
179
  signing_key:
175
180
  specification_version: 4
176
- summary: Intrinio Technical Analysis
181
+ summary: A Ruby library for performing technical analysis on stock prices and other
182
+ data sets.
177
183
  test_files: []