stockcruncher 1.1.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51887b6440cf8d3958f52c05d5daa9e77c0bada0e385445ec6bcb6553fe9c16d
4
- data.tar.gz: 66ade1c3623130c5e5588ceca005bddb201b176d1d81ffbb0cabd41568b1692c
3
+ metadata.gz: 6c5f9d2cc2dba8ad0fbd9882cebcda893500e0f029610107004481b0b236d6b9
4
+ data.tar.gz: 65815caea89d856ef4e4c76365df166bdb971302a95f6665d657c0ba6420e671
5
5
  SHA512:
6
- metadata.gz: 0bbdd83e653eadf37c57b6cf5e2bf3234c94ccf74d59ffcd21f995d74260a7673da8a79564f37b4b69715fb5f295d0e1bcf2465fee119a84965cbcc333d0f0bc
7
- data.tar.gz: 979223a1ee0cba78f9d482d22f3025c17fb0506c487d237b94b5855a7843ef3eca610fd89fcbd0a41c66325a1f9e86786e0b56703d666fbca8d5417dd3906321
6
+ metadata.gz: 19b7f5eafd15cbcc5713a6729e17c3a2ae5dd4c471f0d77c7becb4ba10be08c6c492f4469b7ab4a93858215a406d95fced52cde98cffd0644c081ce331b43e9a
7
+ data.tar.gz: c73d7c0caa396f0cf1b06f88f74e2e1cdbd0c7b7fa1739e4b72df4e5852b16e847a46d09071699db51bc6ed07a85e51766c8259221214d62e845ed15331bc980
data/.gitlab-ci.yml CHANGED
@@ -1,9 +1,11 @@
1
+
1
2
  image: ruby:2.4
2
3
 
4
+ before_script:
5
+ - bundle install
6
+
3
7
  rubocop:
4
8
  stage: test
5
- before_script:
6
- - gem install --silent rubocop
7
9
  script:
8
10
  - rubocop
9
11
 
@@ -25,7 +27,6 @@ git_history:
25
27
  rspec:
26
28
  stage: test
27
29
  script:
28
- - bundle install
29
30
  - rspec
30
31
  artifacts:
31
32
  paths:
data/.rubocop.yml CHANGED
@@ -4,3 +4,4 @@ AllCops:
4
4
  DisplayStyleGuide: true
5
5
  DisplayCopNames: false
6
6
  TargetRubyVersion: 2.4
7
+ NewCops: disable
data/CHANGELOG CHANGED
@@ -11,3 +11,20 @@ Changelog
11
11
 
12
12
  - Add InfluxDB as database.
13
13
  - WIP for daily time serie.
14
+
15
+ 1.1.1
16
+ -----
17
+
18
+ - Rescue error on no data
19
+
20
+ 1.2.0
21
+ -----
22
+
23
+ - daily subcommand now recalculates missing data
24
+ - daily subcommand also writes data to database
25
+
26
+ 1.2.1
27
+ -----
28
+
29
+ - Refactor to preprocess data in source class
30
+ - Refactor to print pretty json instead of comma separated striing
data/lib/stockcruncher.rb CHANGED
@@ -10,6 +10,9 @@ module StockCruncher
10
10
 
11
11
  def start(args = ARGV)
12
12
  StockCruncher::CLI.start(args)
13
+ rescue StandardError => e
14
+ warn e.message
15
+ exit 1
13
16
  end
14
17
  end
15
18
  end
@@ -8,12 +8,40 @@ module StockCruncher
8
8
  class AlphaVantage < Cruncher
9
9
  API_URL = 'https://www.alphavantage.co/query?'
10
10
 
11
+ # Method to calculate missing data (previousClose, change, changePercent)
12
+ def calculate_missing_data(hash)
13
+ keys = hash.keys
14
+ hash.each_with_index do |(date, v), index|
15
+ prevday = keys[index + 1]
16
+ next if prevday.nil?
17
+
18
+ prevclose = hash[prevday]['close']
19
+ hash[date] = v.merge(generate_missing_data(v['close'], prevclose))
20
+ end
21
+ hash
22
+ end
23
+
24
+ # Method to calculate change difference
25
+ def change(value, base)
26
+ (value - base).round(4).to_s
27
+ end
28
+
29
+ # Method to calculate percentage of change
30
+ def change_percent(value, base)
31
+ ((value / base - 1) * 100).round(4).to_s
32
+ end
33
+
34
+ # Method to create a new hash from two arrays of keys and values
35
+ def create_hash(descriptions, values)
36
+ descriptions.split(',').zip(values.split(',')).to_h
37
+ end
38
+
11
39
  # Main method to crunch data.
12
40
  def crunch_daily(symbol, fullsize)
13
41
  url = API_URL + parameters(symbol, 'TIME_SERIES_DAILY')
14
- url += '&datatype=csv&outputsize=' + (fullsize ? 'full' : 'compact')
42
+ url += "&datatype=csv&outputsize=#{fullsize ? 'full' : 'compact'}"
15
43
  res = request(url)
16
- res.body
44
+ transform_daily(res.body)
17
45
  end
18
46
 
19
47
  # Main method to crunch data.
@@ -21,14 +49,55 @@ module StockCruncher
21
49
  url = API_URL + parameters(symbol, 'GLOBAL_QUOTE')
22
50
  url += '&datatype=csv'
23
51
  res = request(url)
24
- res.body
52
+ transform_quote(res.body)
25
53
  end
26
54
 
55
+ # Method to generate missing data
56
+ def generate_missing_data(current, previous)
57
+ {
58
+ 'previousClose' => previous,
59
+ 'change' => change(current.to_f, previous.to_f),
60
+ 'changePercent' => change_percent(current.to_f, previous.to_f)
61
+ }
62
+ end
63
+
64
+ # Set parameters of api call
27
65
  def parameters(symbol, serie)
28
- p = 'function=' + serie
29
- p += '&symbol=' + symbol
30
- p += '&apikey=' + @config[self.class.name.split('::').last]['apikey']
66
+ p = "function=#{serie}"
67
+ p += "&symbol=#{symbol}"
68
+ p += "&apikey=#{@config[self.class.name.split('::').last]['apikey']}"
31
69
  p
32
70
  end
71
+
72
+ # Method to transform raw data to constructed hash
73
+ def prepare_daily_timeserie(data)
74
+ lines = data.split("\r\n")
75
+ desc = lines.shift.split(',').drop(1)
76
+ hash = {}
77
+ lines.each do |line|
78
+ values = line.split(',')
79
+ date = values.shift
80
+ hash[date] = desc.zip(values).to_h
81
+ end
82
+ hash
83
+ end
84
+
85
+ # Method to transform daily result to nested hash
86
+ def transform_daily(rawdata)
87
+ raise StandardError, 'No data' if rawdata.match?(/Error Message/)
88
+
89
+ values = prepare_daily_timeserie(rawdata)
90
+ calculate_missing_data(values)
91
+ end
92
+
93
+ # Method to transform quote result to hash
94
+ def transform_quote(rawdata)
95
+ raise StandardError, 'No data' if rawdata.match?(/{}/)
96
+
97
+ values = create_hash(*rawdata.split("\r\n"))
98
+ values['close'] = values.delete('price')
99
+ values['changePercent'] = values['changePercent'].delete('%')
100
+ values
101
+ end
33
102
  end
34
103
  end
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'json'
4
5
  require 'thor'
5
6
  require 'yaml'
6
7
 
@@ -48,20 +49,35 @@ module StockCruncher
48
49
  opts = options.dup
49
50
  config = YAML.load_file(opts['config'])
50
51
  cruncher = StockCruncher::AlphaVantage.new(config, opts['insecure'])
51
- raw_data = cruncher.crunch_daily(symbol, opts['full'])
52
- # TODO: export to DB here
53
- puts raw_data unless opts['quiet']
52
+ data = cruncher.crunch_daily(symbol, opts['full'])
53
+ StockCruncher::InfluxDB.new(config).export_history(symbol, data)
54
+ puts JSON.pretty_generate(data) unless opts['quiet']
55
+ end
56
+
57
+ desc('movingaverages SYMBOL [options]',
58
+ 'Calculate and export moving averages for requested symbol.')
59
+ option(
60
+ :all,
61
+ aliases: ['-a'],
62
+ type: :boolean,
63
+ default: false,
64
+ desc: 'Recalculate all MA historical values.'
65
+ )
66
+ def movingaverages(symbol)
67
+ opts = options.dup
68
+ config = YAML.load_file(opts['config'])
69
+ StockCruncher::InfluxDB.new(config).moving_averages(symbol, opts['all'])
54
70
  end
55
71
 
56
72
  desc('quote SYMBOL [options]',
57
73
  'Crunch SYMBOL stock market data for last day quote.')
58
74
  def quote(symbol)
59
75
  opts = options.dup
60
- config = YAML.load_file(opts['config'])
76
+ config = StockCruncher::Config.load(opts['config'])
61
77
  cruncher = StockCruncher::AlphaVantage.new(config, opts['insecure'])
62
- raw_data = cruncher.crunch_quote(symbol)
63
- puts raw_data unless opts['quiet']
64
- StockCruncher::InfluxDB.new(config).export_last_day(raw_data)
78
+ data = cruncher.crunch_quote(symbol)
79
+ StockCruncher::InfluxDB.new(config).export_last_day(data)
80
+ puts JSON.pretty_generate(data) unless opts['quiet']
65
81
  end
66
82
  end
67
83
  end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaml'
5
+
6
+ module StockCruncher
7
+ # this is a module to load configuration file and environment variable
8
+ module Config
9
+ module_function
10
+
11
+ # Load config file and override with env variables
12
+ def load(file)
13
+ config = YAML.load_file(file)
14
+ overload_alphavantage(config)
15
+ overload_influxdb(config)
16
+ end
17
+
18
+ def overload(config, prefix, component, items)
19
+ items.each do |key|
20
+ var = "#{prefix}#{key.upcase}"
21
+ config[component][key] = ENV[var] unless ENV[var].nil?
22
+ end
23
+ config
24
+ end
25
+
26
+ def overload_alphavantage(config)
27
+ prefix = 'SCR_AV_'
28
+ component = 'AlphaVantage'
29
+ items = %w[apikey]
30
+ overload(config, prefix, component, items)
31
+ end
32
+
33
+ def overload_influxdb(config)
34
+ prefix = 'SCR_IDB_'
35
+ component = 'InfluxDB'
36
+ items = %w[scheme host port user password dbname]
37
+ overload(config, prefix, component, items)
38
+ end
39
+ end
40
+ end
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'date'
5
+ require 'json'
4
6
  require 'net/http'
5
7
 
6
8
  module StockCruncher
@@ -12,30 +14,69 @@ module StockCruncher
12
14
  @insecure = insecure
13
15
  end
14
16
 
15
- # Method to export latest data to database
16
- def export_last_day(raw)
17
- (desc, line) = raw.split("\r\n")
18
- values = desc.split(',').zip(line.split(',')).to_h
19
- values['close'] = values.delete('price')
20
- values['changePercent'] = values['changePercent'].delete('%')
21
- tags = { 'symbol' => values.delete('symbol') }
22
- date = values.delete('latestDay')
23
- write('daily', tags, values, date)
17
+ def get_daily_values(symbol, fullsize)
18
+ values = %w[close change changePercent volume]
19
+ data = query('daily', symbol, values, fullsize)
20
+ data['columns'].zip(data['values'].transpose).to_h
21
+ end
22
+
23
+ # Method to calculate moving averages based on last day values
24
+ def moving_averages(symbol, fullsize)
25
+ series = get_daily_values(symbol, fullsize)
26
+ tags = create_tags(symbol)
27
+ series['close'].each_index do |i|
28
+ date = series['time'][i]
29
+ serie = series['close'][i, 201]
30
+ weights = series['volume'][i, 201]
31
+ write_moving_averages(tags, serie, weights, date)
32
+ break unless fullsize
33
+ end
34
+ end
35
+
36
+ # Method to create tags hash containing only symbol
37
+ def create_tags(symbol)
38
+ { 'symbol' => symbol }
24
39
  end
25
40
 
26
41
  # Method to export historical data to database
27
- def export_history(raw)
28
- # TODO: export whole history and calculated missing values
42
+ def export_history(symbol, timeseries)
43
+ tags = create_tags(symbol)
44
+ timeseries.each_pair do |date, values|
45
+ write('daily', tags, values, date)
46
+ end
47
+ end
48
+
49
+ # Method to export latest data to database
50
+ def export_last_day(values)
51
+ tags = create_tags(values.delete('symbol'))
52
+ date = values.delete('latestDay')
53
+ write('daily', tags, values, date)
29
54
  end
30
55
 
31
56
  # Method to format and array of values into comma separated string
32
57
  def format_values(values)
33
- string = ''
34
- values.each_pair do |k, v|
35
- string += "#{k}=#{v}"
36
- string += ',' unless k == values.keys.last
37
- end
38
- string
58
+ values.map { |k, v| "#{k}=#{v}" }.join(',')
59
+ end
60
+
61
+ # Method to calculate all statistics
62
+ def write_moving_averages(tags, serie, weights, date)
63
+ write('ema', tags, StockCruncher::Stats.list_ema(serie), date)
64
+ write('lwma', tags, StockCruncher::Stats.list_lwma(serie), date)
65
+ write('sma', tags, StockCruncher::Stats.list_sma(serie), date)
66
+ write('vwma', tags, StockCruncher::Stats.list_vwma(serie, weights), date)
67
+ end
68
+
69
+ # Method to query data in bucket
70
+ def query(name, symbol, values, full)
71
+ url = "#{@cfg['scheme']}://#{@cfg['host']}:#{@cfg['port']}/query?" \
72
+ "db=#{@cfg['dbname']}"
73
+ size = full ? '' : 'LIMIT 201'
74
+ body = "q=SELECT #{values.join(',')} FROM #{name} " \
75
+ "WHERE symbol = '#{symbol}' ORDER BY time DESC #{size}"
76
+ data = JSON.parse(request(url, body).body)['results'][0]['series']
77
+ raise StandardError, 'No data' if data.nil?
78
+
79
+ data[0]
39
80
  end
40
81
 
41
82
  # Method to send http post request
@@ -54,7 +95,7 @@ module StockCruncher
54
95
  def write(name, tags, values, date)
55
96
  url = "#{@cfg['scheme']}://#{@cfg['host']}:#{@cfg['port']}/write?" \
56
97
  "db=#{@cfg['dbname']}"
57
- timestamp = DateTime.parse(date + 'T18:00:00').strftime('%s%N')
98
+ timestamp = DateTime.parse("#{date}T18:00:00").strftime('%s%N')
58
99
  body = "#{name},#{format_values(tags)} #{format_values(values)} " \
59
100
  "#{timestamp}"
60
101
  request(url, body)
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/ruby
2
+ # frozen_string_literal: true
3
+
4
+ module StockCruncher
5
+ # this is a module with various statistic calculation methods
6
+ module Stats
7
+ extend self
8
+
9
+ RANGES = [5, 10, 20, 30, 50, 100, 200].freeze
10
+
11
+ # Calculate multiple range of exponential moving average
12
+ def list_ema(values)
13
+ h = {}
14
+ RANGES.each do |n|
15
+ next if values.size < n + 1
16
+
17
+ h["ema#{n}"] = ema(values[0, n + 1])
18
+ end
19
+ h
20
+ end
21
+
22
+ # Calculate multiple range of linearly weighted moving average
23
+ def list_lwma(values)
24
+ h = {}
25
+ RANGES.each do |n|
26
+ next if values.size < n
27
+
28
+ weights = (1..n).to_a.reverse
29
+ h["lwma#{n}"] = sma(values[0, n], weights)
30
+ end
31
+ h
32
+ end
33
+
34
+ # Calculate multiple range of simple moving average
35
+ def list_sma(values)
36
+ h = {}
37
+ RANGES.each do |n|
38
+ next if values.size < n
39
+
40
+ h["sma#{n}"] = sma(values[0, n])
41
+ end
42
+ h
43
+ end
44
+
45
+ # Calculate multiple range of volume weighted moving average
46
+ def list_vwma(values, volumes)
47
+ h = {}
48
+ RANGES.each do |n|
49
+ next if values.size < n
50
+
51
+ h["lwma#{n}"] = sma(values[0, n], volumes[0, n])
52
+ end
53
+ h
54
+ end
55
+
56
+ private
57
+
58
+ # Calculate exponential moving average
59
+ def ema(array, factor = 2, weights = nil)
60
+ f = factor.to_f / array.size
61
+ n = array.size - 1
62
+ tsma = sma(array[0, n], weights)
63
+ ysma = sma(array[1, n], weights)
64
+ (tsma * f + ysma * (1 - f)).round(4)
65
+ end
66
+
67
+ # Calculate simple moving average
68
+ def sma(array, weights = nil)
69
+ factor = weights.nil? ? Array.new(array.size, 1) : weights
70
+ dividend = array.each_with_index.map { |v, i| v * factor[i] }
71
+ (dividend.sum.to_f / factor.sum).round(4)
72
+ end
73
+ end
74
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module StockCruncher
5
- VERSION = '1.1.0'
5
+ VERSION = '1.3.1'
6
6
  end
@@ -0,0 +1,49 @@
1
+ {
2
+ "2020-08-03": {
3
+ "open": "23.8500",
4
+ "high": "24.6500",
5
+ "low": "23.7400",
6
+ "close": "24.5500",
7
+ "volume": "3112972",
8
+ "previousClose": "23.8100",
9
+ "change": "0.74",
10
+ "changePercent": "3.1079"
11
+ },
12
+ "2020-07-31": {
13
+ "open": "24.0100",
14
+ "high": "24.5100",
15
+ "low": "23.5900",
16
+ "close": "23.8100",
17
+ "volume": "5482485",
18
+ "previousClose": "23.6600",
19
+ "change": "0.15",
20
+ "changePercent": "0.634"
21
+ },
22
+ "2020-07-30": {
23
+ "open": "24.4700",
24
+ "high": "24.6600",
25
+ "low": "23.5200",
26
+ "close": "23.6600",
27
+ "volume": "6466738",
28
+ "previousClose": "24.5600",
29
+ "change": "-0.9",
30
+ "changePercent": "-3.6645"
31
+ },
32
+ "2020-07-29": {
33
+ "open": "24.7500",
34
+ "high": "25.0300",
35
+ "low": "24.0400",
36
+ "close": "24.5600",
37
+ "volume": "4605804",
38
+ "previousClose": "24.7500",
39
+ "change": "-0.19",
40
+ "changePercent": "-0.7677"
41
+ },
42
+ "2020-07-28": {
43
+ "open": "25.9900",
44
+ "high": "26.0700",
45
+ "low": "24.5100",
46
+ "close": "24.7500",
47
+ "volume": "6904261"
48
+ }
49
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "open": "100.0000",
3
+ "high": "100.1000",
4
+ "low": "99.9000",
5
+ "volume": "4",
6
+ "previousClose": "100.0000",
7
+ "change": "0.0000",
8
+ "changePercent": "0.0000",
9
+ "close": "100.0000"
10
+ }
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'spec_helper'
4
5
 
5
- quote = 'symbol,open,high,low,price,volume,latestDay,previousClose,change,ch' \
6
- "angePercent\r\nSYM,100.0000,100.1000,99.9000,100.0000,4,2020-07-30," \
7
- "100.0000,0.0000,0.0000%\r\n"
6
+ daily = File.read('spec/files/SYM.daily')
7
+ quote = File.read('spec/files/SYM.quote')
8
8
 
9
- describe StockCruncher::CLI do
9
+ describe StockCruncher::CLI do # rubocop:disable Metrics/BlockLength
10
10
  context 'version' do
11
11
  it 'prints the version.' do
12
12
  out = "StockCruncher version #{StockCruncher::VERSION}\n"
@@ -14,9 +14,27 @@ describe StockCruncher::CLI do
14
14
  end
15
15
  end
16
16
 
17
+ context 'daily NODATA -c spec/files/stockcruncher.yml' do
18
+ it 'Should not get any data and should fail.' do
19
+ expect { start(self) }.to raise_error(SystemExit)
20
+ end
21
+ end
22
+
17
23
  context 'daily SYM -c spec/files/stockcruncher.yml' do
18
24
  it 'Get the daily time serie for SYM.' do
19
- expect { start(self) }.to output("{}\n").to_stdout
25
+ expect { start(self) }.to output(daily).to_stdout
26
+ end
27
+ end
28
+
29
+ context 'movingaverages SYM -c spec/files/stockcruncher.yml' do
30
+ it 'Get the daily time serie for SYM.' do
31
+ expect { start(self) }.to output('').to_stdout
32
+ end
33
+ end
34
+
35
+ context 'quote NODATA -c spec/files/stockcruncher.yml' do
36
+ it 'Should not get any data and should fail.' do
37
+ expect { start(self) }.to raise_error(SystemExit)
20
38
  end
21
39
  end
22
40
 
@@ -5,17 +5,38 @@ require 'spec_helper'
5
5
  quote = 'symbol,open,high,low,price,volume,latestDay,previousClose,change,ch' \
6
6
  "angePercent\r\nSYM,100.0000,100.1000,99.9000,100.0000,4,2020-07-30," \
7
7
  "100.0000,0.0000,0.0000%\r\n"
8
+ q_err = '{}'
9
+ daily = "timestamp,open,high,low,close,volume\r\n2020-08-03,23.8500,24.6500," \
10
+ "23.7400,24.5500,3112972\r\n2020-07-31,24.0100,24.5100,23.5900,23.81" \
11
+ "00,5482485\r\n2020-07-30,24.4700,24.6600,23.5200,23.6600,6466738\r" \
12
+ "\n2020-07-29,24.7500,25.0300,24.0400,24.5600,4605804\r\n2020-07-28," \
13
+ "25.9900,26.0700,24.5100,24.7500,6904261\r\n"
14
+ d_err = "{\n \"Error Message\": \"Invalid API call.\"\n}"
15
+ mvavg = '{"results":[{"statement_id":0,"series":[{"name":"daily","columns":[' \
16
+ '"time","close","change","changePercent","volume"],"values":[["2017-' \
17
+ '03-01T18:00:00Z",1,0,0,1],["2017-03-02T18:00:00Z",1,0,0,1],["2017-0' \
18
+ '3-03T18:00:00Z",1,0,0,1],["2017-03-04T18:00:00Z",1,0,0,1],["2017-03' \
19
+ '-05T18:00:00Z",1,0,0,1],["2017-03-06T18:00:00Z",1,0,0,1]]}]}]}'
8
20
 
9
21
  RSpec.configure do |config|
10
22
  config.before(:each) do
11
23
  # requests an API without extra arguments
24
+ stub_request(:get, 'https://www.alphavantage.co/query?' \
25
+ 'function=GLOBAL_QUOTE&symbol=NODATA&apikey=demo&datatype=csv')
26
+ .to_return('status' => 200, 'body' => q_err, 'headers' => {})
12
27
  stub_request(:get, 'https://www.alphavantage.co/query?' \
13
28
  'function=GLOBAL_QUOTE&symbol=SYM&apikey=demo&datatype=csv')
14
29
  .to_return('status' => 200, 'body' => quote, 'headers' => {})
30
+ stub_request(:get, 'https://www.alphavantage.co/query?' \
31
+ 'function=TIME_SERIES_DAILY&symbol=NODATA&apikey=demo' \
32
+ '&datatype=csv&outputsize=compact')
33
+ .to_return('status' => 200, 'body' => d_err, 'headers' => {})
15
34
  stub_request(:get, 'https://www.alphavantage.co/query?' \
16
35
  'function=TIME_SERIES_DAILY&symbol=SYM&apikey=demo' \
17
36
  '&datatype=csv&outputsize=compact')
18
- .to_return('status' => 200, 'body' => '{}', 'headers' => {})
37
+ .to_return('status' => 200, 'body' => daily, 'headers' => {})
38
+ stub_request(:post, 'http://localhost:8086/query?db=test')
39
+ .to_return('status' => 204, 'body' => mvavg, 'headers' => {})
19
40
  stub_request(:post, 'http://localhost:8086/write?db=test')
20
41
  .to_return('status' => 204, 'body' => '', 'headers' => {})
21
42
  end
@@ -8,7 +8,7 @@ dev_deps = {
8
8
  'bundler' => '~> 1.17',
9
9
  'rspec' => '~> 3.9',
10
10
  'rake' => '~> 11.3',
11
- 'rubocop' => '0.88',
11
+ 'rubocop' => '0.90',
12
12
  'webmock' => '~> 2.0.3',
13
13
  'simplecov' => '~> 0.18'
14
14
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stockcruncher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Delaplace
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-31 00:00:00.000000000 Z
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: '0.88'
61
+ version: '0.90'
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.88'
68
+ version: '0.90'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: webmock
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -129,9 +129,13 @@ files:
129
129
  - lib/stockcruncher.rb
130
130
  - lib/stockcruncher/alphavantage.rb
131
131
  - lib/stockcruncher/cli.rb
132
+ - lib/stockcruncher/config.rb
132
133
  - lib/stockcruncher/cruncher.rb
133
134
  - lib/stockcruncher/influxdb.rb
135
+ - lib/stockcruncher/stats.rb
134
136
  - lib/stockcruncher/version.rb
137
+ - spec/files/SYM.daily
138
+ - spec/files/SYM.quote
135
139
  - spec/files/stockcruncher.yml
136
140
  - spec/spec_helper.rb
137
141
  - spec/stockcruncher/cli_spec.rb