stockcruncher 1.2.0 → 1.3.2
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.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +4 -3
- data/.rubocop.yml +1 -0
- data/CHANGELOG +6 -0
- data/README.md +23 -6
- data/lib/stockcruncher/alphavantage.rb +75 -6
- data/lib/stockcruncher/cli.rb +23 -7
- data/lib/stockcruncher/config.rb +40 -0
- data/lib/stockcruncher/influxdb.rb +48 -66
- data/lib/stockcruncher/stats.rb +74 -0
- data/lib/stockcruncher/version.rb +1 -1
- data/spec/files/SYM.daily +49 -0
- data/spec/files/SYM.quote +10 -0
- data/spec/stockcruncher/cli_spec.rb +9 -8
- data/spec/stockcruncher/stubs/servers_stubs.rb +7 -0
- data/stockcruncher.gemspec +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56cbc10941fcfdc6ccee0273cf5b2f43f4a98c1f3b53b1554a53ea45020c604e
|
4
|
+
data.tar.gz: ce451b4498697f9c6148167771296219c4f83ffdef3f1fbefb4c8a12f7a747e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b3bcfdbe1805e95c4729e33ad26b4ea676d63a5e638150fca0a26d75682a96d6d4d71a16e9458ef75423f82fc4ba0a4f7442e838865ac9d27b57fbb817dbaed
|
7
|
+
data.tar.gz: fbe77bb52c0b0d51d6f480246c9467ca4838f9afd385e573c35342b40ada8973f0f5f52c65d159ae85f332d8e0a38e244cf5149b386dc10872323d5990726451
|
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
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -5,12 +5,14 @@
|
|
5
5
|
[![Gem Version][gem-img]][gem]
|
6
6
|
|
7
7
|
1. [Overview](#overview)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
1. [Description](#description)
|
9
|
+
1. [Setup](#setup)
|
10
|
+
1. [Usage](#usage)
|
11
|
+
1. [Environment variables](#environment-variables)
|
12
|
+
1. [Examples](#examples)
|
13
|
+
1. [Limitations](#limitations)
|
14
|
+
1. [Development](#development)
|
15
|
+
1. [Miscellaneous](#miscellaneous)
|
14
16
|
|
15
17
|
## Overview
|
16
18
|
|
@@ -41,6 +43,17 @@ An interactive help is available with:
|
|
41
43
|
|
42
44
|
$ stockcruncher help [subcommand]
|
43
45
|
|
46
|
+
## Environment variables
|
47
|
+
|
48
|
+
Parameters in /etc/stockcruncher/stockcrunucher.yml can be overloaded with
|
49
|
+
environment variables.
|
50
|
+
Environment variables should match SCR_<COMPONENTID>_<ITEM>.
|
51
|
+
Component ID are as follow.
|
52
|
+
- AlphaVantage: AV
|
53
|
+
- InfluxDB: IDB
|
54
|
+
Items are upcase keys. See template /etc/stockcruncher/stockcrunucher.yml for
|
55
|
+
reference.
|
56
|
+
|
44
57
|
## Examples
|
45
58
|
|
46
59
|
To get daily time serie data of a symbol:
|
@@ -51,6 +64,10 @@ To get last day endpoint data of a symbol:
|
|
51
64
|
|
52
65
|
$ stockcruncher quote AAPL
|
53
66
|
|
67
|
+
To override a parameter with environment variable:
|
68
|
+
|
69
|
+
$ SCR_IDB_HOST=192.168.0.80; stockcruncher quote AAPL
|
70
|
+
|
54
71
|
## Limitations
|
55
72
|
|
56
73
|
Data are currently scraped from AlphaVantage API.
|
@@ -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 +=
|
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 =
|
29
|
-
p +=
|
30
|
-
p +=
|
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
|
data/lib/stockcruncher/cli.rb
CHANGED
@@ -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
|
-
|
52
|
-
StockCruncher::InfluxDB.new(config).export_history(symbol,
|
53
|
-
puts
|
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 =
|
76
|
+
config = StockCruncher::Config.load(opts['config'])
|
61
77
|
cruncher = StockCruncher::AlphaVantage.new(config, opts['insecure'])
|
62
|
-
|
63
|
-
StockCruncher::InfluxDB.new(config).export_last_day(
|
64
|
-
puts
|
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
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'date'
|
5
|
+
require 'json'
|
5
6
|
require 'net/http'
|
6
7
|
|
7
8
|
module StockCruncher
|
@@ -13,88 +14,69 @@ module StockCruncher
|
|
13
14
|
@insecure = insecure
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
prevday = keys[index + 1]
|
21
|
-
next if prevday.nil?
|
22
|
-
|
23
|
-
prevclose = hash[prevday]['close']
|
24
|
-
hash[date] = v.merge(generate_missing_data(v['close'], prevclose))
|
25
|
-
end
|
26
|
-
hash
|
27
|
-
end
|
28
|
-
|
29
|
-
# Method to generate missing data
|
30
|
-
def generate_missing_data(current, previous)
|
31
|
-
{
|
32
|
-
'previousClose' => previous,
|
33
|
-
'change' => change(current.to_f, previous.to_f),
|
34
|
-
'changePercent' => change_percent(current.to_f, previous.to_f)
|
35
|
-
}
|
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
|
36
21
|
end
|
37
22
|
|
38
|
-
# Method to calculate
|
39
|
-
def
|
40
|
-
|
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
|
41
34
|
end
|
42
35
|
|
43
|
-
# Method to
|
44
|
-
def
|
45
|
-
|
36
|
+
# Method to create tags hash containing only symbol
|
37
|
+
def create_tags(symbol)
|
38
|
+
{ 'symbol' => symbol }
|
46
39
|
end
|
47
40
|
|
48
|
-
# Method to
|
49
|
-
def
|
50
|
-
|
41
|
+
# Method to export historical data to database
|
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
|
51
47
|
end
|
52
48
|
|
53
49
|
# Method to export latest data to database
|
54
|
-
def export_last_day(
|
55
|
-
|
56
|
-
|
57
|
-
values = create_hash(*raw.split("\r\n"))
|
58
|
-
values['close'] = values.delete('price')
|
59
|
-
values['changePercent'] = values['changePercent'].delete('%')
|
60
|
-
tags = { 'symbol' => values.delete('symbol') }
|
50
|
+
def export_last_day(values)
|
51
|
+
tags = create_tags(values.delete('symbol'))
|
61
52
|
date = values.delete('latestDay')
|
62
53
|
write('daily', tags, values, date)
|
63
54
|
end
|
64
55
|
|
65
|
-
# Method to export historical data to database
|
66
|
-
def export_history(symbol, raw)
|
67
|
-
raise StandardError, 'No data to export' if raw.match?(/Error Message/)
|
68
|
-
|
69
|
-
tags = { 'symbol' => symbol }
|
70
|
-
timeseries = prepare_daily_timeserie(raw)
|
71
|
-
timeseries = calculate_missing_data(timeseries)
|
72
|
-
timeseries.each_pair do |date, values|
|
73
|
-
write('daily', tags, values, date)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
56
|
# Method to format and array of values into comma separated string
|
78
57
|
def format_values(values)
|
79
|
-
|
80
|
-
values.each_pair do |k, v|
|
81
|
-
string += "#{k}=#{v}"
|
82
|
-
string += ',' unless k == values.keys.last
|
83
|
-
end
|
84
|
-
string
|
58
|
+
values.map { |k, v| "#{k}=#{v}" }.join(',')
|
85
59
|
end
|
86
60
|
|
87
|
-
# Method to
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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]
|
98
80
|
end
|
99
81
|
|
100
82
|
# Method to send http post request
|
@@ -113,7 +95,7 @@ module StockCruncher
|
|
113
95
|
def write(name, tags, values, date)
|
114
96
|
url = "#{@cfg['scheme']}://#{@cfg['host']}:#{@cfg['port']}/write?" \
|
115
97
|
"db=#{@cfg['dbname']}"
|
116
|
-
timestamp = DateTime.parse(date
|
98
|
+
timestamp = DateTime.parse("#{date}T18:00:00").strftime('%s%N')
|
117
99
|
body = "#{name},#{format_values(tags)} #{format_values(values)} " \
|
118
100
|
"#{timestamp}"
|
119
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["vwma#{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
|
@@ -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
|
+
}
|
@@ -1,15 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
3
4
|
require 'spec_helper'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
"100.0000,0.0000,0.0000%\r\n"
|
8
|
-
daily = "timestamp,open,high,low,close,volume\r\n2020-08-03,23.8500,24.6500," \
|
9
|
-
"23.7400,24.5500,3112972\r\n2020-07-31,24.0100,24.5100,23.5900,23.81" \
|
10
|
-
"00,5482485\r\n2020-07-30,24.4700,24.6600,23.5200,23.6600,6466738\r" \
|
11
|
-
"\n2020-07-29,24.7500,25.0300,24.0400,24.5600,4605804\r\n2020-07-28," \
|
12
|
-
"25.9900,26.0700,24.5100,24.7500,6904261\r\n"
|
6
|
+
daily = File.read('spec/files/SYM.daily')
|
7
|
+
quote = File.read('spec/files/SYM.quote')
|
13
8
|
|
14
9
|
describe StockCruncher::CLI do # rubocop:disable Metrics/BlockLength
|
15
10
|
context 'version' do
|
@@ -31,6 +26,12 @@ describe StockCruncher::CLI do # rubocop:disable Metrics/BlockLength
|
|
31
26
|
end
|
32
27
|
end
|
33
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
|
+
|
34
35
|
context 'quote NODATA -c spec/files/stockcruncher.yml' do
|
35
36
|
it 'Should not get any data and should fail.' do
|
36
37
|
expect { start(self) }.to raise_error(SystemExit)
|
@@ -12,6 +12,11 @@ daily = "timestamp,open,high,low,close,volume\r\n2020-08-03,23.8500,24.6500," \
|
|
12
12
|
"\n2020-07-29,24.7500,25.0300,24.0400,24.5600,4605804\r\n2020-07-28," \
|
13
13
|
"25.9900,26.0700,24.5100,24.7500,6904261\r\n"
|
14
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]]}]}]}'
|
15
20
|
|
16
21
|
RSpec.configure do |config|
|
17
22
|
config.before(:each) do
|
@@ -30,6 +35,8 @@ RSpec.configure do |config|
|
|
30
35
|
'function=TIME_SERIES_DAILY&symbol=SYM&apikey=demo' \
|
31
36
|
'&datatype=csv&outputsize=compact')
|
32
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' => {})
|
33
40
|
stub_request(:post, 'http://localhost:8086/write?db=test')
|
34
41
|
.to_return('status' => 204, 'body' => '', 'headers' => {})
|
35
42
|
end
|
data/stockcruncher.gemspec
CHANGED
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.2
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Delaplace
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-20 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.
|
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.
|
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
|