alphavantagerb 1.0.0

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.
@@ -0,0 +1,70 @@
1
+ module Alphavantage
2
+ class Client
3
+ include HelperFunctions
4
+
5
+ def initialize key:, verbose: false
6
+ check_argument([true, false], verbose, "verbose")
7
+ @apikey = key
8
+ @base_uri = 'https://www.alphavantage.co'
9
+ @verbose = verbose
10
+ end
11
+
12
+ attr_reader :verbose
13
+
14
+ def verbose=(verbose)
15
+ check_argument([true, false], verbose, "verbose")
16
+ @verbose = verbose
17
+ end
18
+
19
+ def request(url)
20
+ send_url = "#{@base_uri}/query?#{url}&apikey=#{@apikey}"
21
+ puts "\n#{send_url}\n" if @verbose
22
+ begin
23
+ response = HTTParty.get(send_url)
24
+ rescue Exception => e
25
+ raise Alphavantage::Error.new message: "Failed request: #{e.message}"
26
+ end
27
+ data = response.body
28
+ begin
29
+ data = JSON.parse(data)
30
+ rescue Exception => e
31
+ raise Alphavantage::Error.new message: "Parsing failed",
32
+ data: data
33
+ end
34
+ if !data["Error Message"].nil?
35
+ raise Alphavantage::Error.new message: data["Error Message"], data: data
36
+ elsif !data["Information"].nil?
37
+ raise Alphavantage::Error.new message: data["Information"], data: data
38
+ end
39
+ return data
40
+ end
41
+
42
+ def download(url, file)
43
+ send_url = "#{@base_uri}/query?#{url}&datatype=csv&apikey=#{@apikey}"
44
+ begin
45
+ puts send_url if @verbose
46
+ uri = URI.parse(send_url)
47
+ uri.open{|csv| IO.copy_stream(csv, file)}
48
+ rescue Exception => e
49
+ raise Alphavantage::Error.new message: "Failed to save the CSV file: #{e.message}"
50
+ end
51
+ return "CSV saved in #{file}"
52
+ end
53
+
54
+ def stock(symbol:, datatype: "json")
55
+ Alphavantage::Stock.new symbol: symbol, key: self, datatype: datatype
56
+ end
57
+
58
+ def exchange(from:, to:)
59
+ Alphavantage::Exchange.new from: from, to: to, key: self
60
+ end
61
+
62
+ def crypto(symbol:, market:, datatype: "json")
63
+ Alphavantage::Crypto.new symbol: symbol, key: self, datatype: datatype, market: market
64
+ end
65
+
66
+ def sector
67
+ Alphavantage::Sector.new key: self
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ module Alphavantage
2
+ class Crypto
3
+ include HelperFunctions
4
+
5
+ def initialize symbol:, datatype: "json", key:, verbose: false, market:
6
+ check_argument([true, false], verbose, "verbose")
7
+ @client = return_client(key, verbose)
8
+ @symbol = symbol
9
+ @market = market
10
+ @datatype = datatype
11
+ end
12
+
13
+ attr_accessor :symbol, :market
14
+ attr_reader :datatype
15
+
16
+ def datatype=(datatype)
17
+ check_argument(["json", "csv"], datatype, "datatype")
18
+ @datatype = datatype
19
+ end
20
+
21
+ def timeseries type: "intraday", market: @market, file: nil, datatype: @datatype
22
+ Alphavantage::Crypto_Timeseries.new type: type, market: market,
23
+ symbol: @symbol, datatype: datatype, file: file, key: @client
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,67 @@
1
+ module Alphavantage
2
+ class Crypto_Timeseries
3
+ include HelperFunctions
4
+
5
+ def initialize type: "intraday", market:, symbol:, datatype: "json", file: nil,
6
+ key:, verbose: false
7
+ check_argument([true, false], verbose, "verbose")
8
+ @client = return_client(key, verbose)
9
+ check_argument(["json", "csv"], datatype, "datatype")
10
+ if datatype == "csv" && file.nil?
11
+ raise Alphavantage::Error.new message: "No file specified where to save the CSV data"
12
+ elsif datatype != "csv" && !file.nil?
13
+ raise Alphavantage::Error.new message: "Hash error: No file necessary"
14
+ end
15
+
16
+ @selected_time_series = which_series(type)
17
+ url = "function=#{@selected_time_series}&symbol=#{symbol}&market=#{market}"
18
+ return @client.download(url, file) if datatype == "csv"
19
+ @hash = @client.request(url)
20
+ metadata = @hash.dig("Meta Data") || {}
21
+ metadata.each do |key, val|
22
+ key_sym = key.downcase.gsub(/[0-9.]/, "").lstrip.gsub(" ", "_").to_sym
23
+ define_singleton_method(key_sym) do
24
+ return val
25
+ end
26
+ end
27
+ @open = []; @high = []; @low = []; @close = []; @volume = [];
28
+ @open_usd = []; @high_usd = []; @low_usd = []; @close_usd = [];
29
+ @market_cap_usd = [];
30
+
31
+ begin
32
+ time_series = @hash.find{|key, val| key.include?("Time Series")}[1]
33
+ rescue Exception => e
34
+ raise Alphavantage::Error.new message: "No Time Series found: #{e.message}",
35
+ data: @hash
36
+ end
37
+
38
+ series = {}
39
+ convert_key = {}
40
+ time_series.values[0].keys.each do |key|
41
+ key_sym = recreate_metadata_key(key)
42
+ series[key_sym] = []
43
+ convert_key[key] = key_sym
44
+ end
45
+ time_series.each do |time, ts_hash|
46
+ ts_hash.each do |key, value|
47
+ series[convert_key[key]] << [time, value]
48
+ end
49
+ end
50
+ series.keys.each do |key_sym|
51
+ define_singleton_method(key_sym) do |*args|
52
+ args ||= []
53
+ return return_series(series[key_sym], args[0])
54
+ end
55
+ end
56
+ end
57
+
58
+ attr_reader :hash
59
+
60
+ def which_series(type)
61
+ check_argument(["intraday", "daily", "weekly", "monthly"], type, "type")
62
+ series = "DIGITAL_CURRENCY_"
63
+ series += type.upcase
64
+ return series
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,9 @@
1
+ module Alphavantage
2
+ class Error < StandardError
3
+ def initialize(message: , data: nil)
4
+ @data = data
5
+ super(message)
6
+ end
7
+ attr_reader :data
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Alphavantage
2
+ class Exchange
3
+ include HelperFunctions
4
+
5
+ def initialize from:, to:, key:, verbose: false
6
+ check_argument([true, false], verbose, "verbose")
7
+ @client = return_client(key, verbose)
8
+ @from = from
9
+ @to = to
10
+ @hash = @client.request("function=CURRENCY_EXCHANGE_RATE&from_currency=#{@from}&to_currency=#{@to}")
11
+ hash = @hash["Realtime Currency Exchange Rate"]
12
+ hash.each do |key, val|
13
+ key_sym = recreate_metadata_key(key)
14
+ define_singleton_method(key_sym) do
15
+ return val
16
+ end
17
+ end
18
+ end
19
+
20
+ attr_reader :hash
21
+
22
+ end
23
+ end
@@ -0,0 +1,135 @@
1
+ module Alphavantage
2
+ class Indicator
3
+ include HelperFunctions
4
+
5
+ def initialize function:, symbol:, interval: "daily", time_period: "60",
6
+ series_type: "close", fastlimit: "0.01", slowlimit: "0.01",
7
+ fastperiod: "12", slowperiod: "26", signalperiod: "9",
8
+ fastmatype: "0", slowmatype: "0", signalmatype: "0",
9
+ fastkperiod: "5", slowkperiod: "3", slowdperiod: "3",
10
+ slowkmatype: "0", slowdmatype: "0", fastdperiod: "3",
11
+ fastdmatype: "0", matype: "0", timeperiod1: "7", timeperiod2: "14",
12
+ timeperiod3: "28", nbdevup: "2", nbdevdn: "2", acceleration: "0.01",
13
+ maximum: "0.20", key:, verbose: false
14
+ check_argument([true, false], verbose, "verbose")
15
+ @client = return_client(key, verbose)
16
+ check_argument(["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA", "KAMA", "T3",
17
+ "RSI","MAMA", "MACD", "MACDEXT", "STOCH", "STOCHF", "STOCHRSI", "WILLR",
18
+ "ADX", "ADXR", "APO", "PPO", "MOM", "BOP", "CCI", "CMO", "ROC", "ROCR",
19
+ "AROON", "AROONOSC", "MFI", "TRIX", "ULTOSC", "DX", "MINUS_DI",
20
+ "PLUS_DI", "MINUS_DM", "PLUS_DM", "BBANDS", "MIDPOINT", "MIDPRICE",
21
+ "SAR", "TRANGE", "ATR", "NATR", "AD", "ADOSC", "OBV", "HT_SINE",
22
+ "HT_TRENDLINE", "HT_TRENDMODE", "HT_DCPERIOD", "HT_DCPHASE",
23
+ "HT_PHASOR"], function, "function")
24
+ url = "function=#{function}&symbol=#{symbol}"
25
+
26
+ if ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA", "KAMA", "T3", "RSI",
27
+ "MAMA", "MACD", "MACDEXT", "STOCH", "STOCHF", "STOCHRSI", "WILLR",
28
+ "ADX", "ADXR", "APO", "PPO", "MOM", "BOP", "CCI", "CMO", "ROC", "ROCR",
29
+ "AROON", "AROONOSC", "MFI", "TRIX", "ULTOSC", "DX", "MINUS_DI",
30
+ "PLUS_DI", "MINUS_DM", "PLUS_DM", "BBANDS", "MIDPOINT", "MIDPRICE",
31
+ "SAR", "TRANGE", "ATR", "NATR", "AD", "ADOSC", "OBV", "HT_SINE",
32
+ "HT_TRENDLINE", "HT_TRENDMODE", "HT_DCPERIOD", "HT_DCPHASE",
33
+ "HT_PHASOR"].include? function
34
+ check_argument(["1min", "5min", "15min", "30min", "60min", "daily", "weekly", "monthly"], interval, "interval")
35
+ url += "&interval=#{interval}"
36
+ end
37
+ if ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA", "KAMA", "T3", "RSI",
38
+ "STOCHRSI", "WILLR", "ADX", "ADXR", "MOM", "CCI", "CMO", "ROC", "ROCR",
39
+ "AROON", "AROONOSC", "MFI", "TRIX", "DX", "MINUS_DI", "PLUS_DI",
40
+ "MINUS_DM", "PLUS_DM", "BBANDS", "MIDPOINT", "MIDPRICE", "ATR",
41
+ "NATR"].include? function
42
+ url += return_int_val(time_period, "time_period", "integer")
43
+ end
44
+ if ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA", "KAMA", "T3", "RSI",
45
+ "MAMA", "MACD", "MACDEXT", "STOCHRSI", "APO", "PPO", "MOM", "ROC",
46
+ "ROCR", "TRIX", "BBANDS", "MIDPOINT", "HT_SINE", "HT_TRENDLINE",
47
+ "HT_TRENDMODE", "HT_DCPERIOD", "HT_DCPHASE", "HT_PHASOR", "CMO"].include? function
48
+ check_argument(["close", "open", "high", "low"], series_type, "series_type")
49
+ url += "&series_type=#{series_type}"
50
+ end
51
+ if ["MAMA"].include? function
52
+ url += return_int_val(fastlimit, "fastlimit", "float")
53
+ url += return_int_val(slowlimit, "slowlimit", "float")
54
+ end
55
+ if ["MACD", "MACDEXT", "APO", "PPO", "ADOSC"].include? function
56
+ url += return_int_val(fastperiod, "fastperiod", "integer")
57
+ url += return_int_val(slowperiod, "slowperiod", "integer")
58
+ end
59
+ if ["MACD", "MACDEXT"].include? function
60
+ url += return_int_val(signalperiod, "signalperiod", "integer")
61
+ end
62
+ if ["MACDEXT"].include? function
63
+ url += return_matype(fastmatype, "fastmatype")
64
+ url += return_matype(slowmatype, "slowmatype")
65
+ url += return_matype(signalmatype, "signalmatype")
66
+ end
67
+ if ["STOCH", "STOCHF", "STOCHRSI"].include? function
68
+ url += return_int_val(fastkperiod, "fastkperiod", "integer")
69
+ url += return_int_val(fastdperiod, "fastdperiod", "integer")
70
+ end
71
+ if ["STOCH"].include? function
72
+ url += return_int_val(slowkperiod, "slowkperiod", "integer")
73
+ url += return_int_val(signalperiod, "signalperiod", "integer")
74
+ url += return_matype(slowkmatype, "slowkmatype")
75
+ url += return_matype(slowdmatype, "slowdmatype")
76
+ end
77
+ if ["STOCH", "STOCHF", "STOCHRSI"].include? function
78
+ url += return_matype(fastdmatype, "fastdmatype")
79
+ end
80
+ if ["APO", "PPO", "BBANDS"].include? function
81
+ url += return_matype(matype, "matype")
82
+ end
83
+ if ["ULTOSC"].include? function
84
+ url += return_int_val(timeperiod1, "timeperiod1", "integer")
85
+ url += return_int_val(timeperiod2, "timeperiod2", "integer")
86
+ url += return_int_val(timeperiod3, "timeperiod3", "integer")
87
+ end
88
+ if ["BBANDS"].include? function
89
+ url += return_int_val(nbdevup, "nbdevup", "integer")
90
+ url += return_int_val(nbdevdn, "nbdevdn", "integer")
91
+ end
92
+ if ["SAR"].include? function
93
+ url += return_int_val(acceleration, "acceleration", "float")
94
+ url += return_int_val(maximum, "maximum", "float")
95
+ end
96
+
97
+ @hash = @client.request(url)
98
+ metadata = @hash.dig("Meta Data") || {}
99
+ metadata.each do |key, val|
100
+ key_sym = recreate_metadata_key(key)
101
+ define_singleton_method(key_sym) do
102
+ return val
103
+ end
104
+ end
105
+
106
+ begin
107
+ time_series = @hash.find{|key, val| key.include?("Technical Analysis")}[1]
108
+ rescue Exception => e
109
+ raise Alphavantage::Error.new message: "No Time Series found: #{e.message}",
110
+ data: @hash
111
+ end
112
+
113
+ series = {}
114
+ convert_key = {}
115
+ time_series.values[0].keys.each do |key|
116
+ key_sym = key.downcase.gsub(/[.\/]/, "").lstrip.gsub(" ", "_").to_sym
117
+ series[key_sym] = []
118
+ convert_key[key] = key_sym
119
+ end
120
+ time_series.each do |time, ts_hash|
121
+ ts_hash.each do |key, value|
122
+ series[convert_key[key]] << [time, value]
123
+ end
124
+ end
125
+ series.keys.each do |key_sym|
126
+ define_singleton_method(key_sym) do |*args|
127
+ args ||= []
128
+ return return_series(series[key_sym], args[0])
129
+ end
130
+ end
131
+ end
132
+
133
+ attr_reader :hash
134
+ end
135
+ end
@@ -0,0 +1,34 @@
1
+ module Alphavantage
2
+ class Sector
3
+ include HelperFunctions
4
+
5
+ def initialize key:, verbose: false
6
+ check_argument([true, false], verbose, "verbose")
7
+ @client = return_client(key, verbose)
8
+ @hash = @client.request("function=SECTOR")
9
+ metadata = @hash.dig("Meta Data") || {}
10
+ metadata.each do |key, val|
11
+ key_sym = key.downcase.gsub(/[0-9.]/, "").lstrip.gsub(" ", "_").to_sym
12
+ define_singleton_method(key_sym) do
13
+ return val
14
+ end
15
+ end
16
+ @hash.each do |key, val|
17
+ next if key == "Meta Data"
18
+ key = key.split(":")[1].lstrip
19
+ key = key.split(" ")
20
+ if key[0].to_i != 0
21
+ key[0] = key[0].to_i.humanize
22
+ end
23
+ key.delete_if{|k| k.include?("(")}
24
+ key = key.join("_")
25
+ key_sym = key.downcase.gsub("-", "_").to_sym
26
+ define_singleton_method(key_sym) do
27
+ return val
28
+ end
29
+ end
30
+ end
31
+
32
+ attr_reader :hash
33
+ end
34
+ end
@@ -0,0 +1,49 @@
1
+ module Alphavantage
2
+ class Stock
3
+ include HelperFunctions
4
+
5
+ def initialize symbol:, datatype: "json", key:, verbose: false
6
+ check_argument([true, false], verbose, "verbose")
7
+ @client = return_client(key, verbose)
8
+ @symbol = symbol
9
+ @datatype = datatype
10
+ end
11
+
12
+ attr_accessor :symbol
13
+ attr_reader :datatype
14
+
15
+ def datatype=(datatype)
16
+ check_argument(["json", "csv"], datatype, "datatype")
17
+ @datatype = datatype
18
+ end
19
+
20
+ def timeseries type: "daily", interval: nil, outputsize: "compact",
21
+ file: nil, datatype: @datatype, adjusted: false
22
+ Alphavantage::Timeseries.new type: type, interval: interval,
23
+ outputsize: outputsize, symbol: @symbol, datatype: datatype, file: file,
24
+ key: @client, adjusted: adjusted
25
+ end
26
+
27
+ def indicator function:, interval: "daily", time_period: "60",
28
+ series_type: "close", fastlimit: "0.01", slowlimit: "0.01",
29
+ fastperiod: "12", slowperiod: "26", signalperiod: "9",
30
+ fastmatype: "0", slowmatype: "0", signalmatype: "0",
31
+ fastkperiod: "5", slowkperiod: "3", slowdperiod: "3",
32
+ slowkmatype: "0", slowdmatype: "0", fastdperiod: "3",
33
+ fastdmatype: "0", matype: "0", timeperiod1: "7", timeperiod2: "14",
34
+ timeperiod3: "28", nbdevup: "2", nbdevdn: "2", acceleration: "0.01",
35
+ maximum: "0.20"
36
+ Alphavantage::Indicator.new function: function, symbol: @symbol,
37
+ interval: interval, time_period: time_period, series_type: series_type,
38
+ fastlimit: fastlimit, slowlimit: slowlimit, fastperiod: fastperiod,
39
+ slowperiod: slowperiod, signalperiod: signalperiod,
40
+ fastmatype: fastmatype, slowmatype: slowmatype,
41
+ signalmatype: signalmatype, fastkperiod: fastkperiod, slowkperiod: slowkperiod,
42
+ slowdperiod: slowdperiod, slowkmatype: slowkmatype, slowdmatype: slowdmatype,
43
+ fastdperiod: fastdperiod, fastdmatype: fastdmatype, matype: matype,
44
+ timeperiod1: timeperiod1, timeperiod2: timeperiod2, timeperiod3: timeperiod3,
45
+ nbdevup: nbdevup, nbdevdn: nbdevdn, acceleration: acceleration,
46
+ maximum: maximum, key: @client
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ module Alphavantage
2
+ class Timeseries
3
+ include HelperFunctions
4
+
5
+ def initialize type: "daily", interval: nil, outputsize: "compact",
6
+ symbol:, datatype: "json", file: nil, key:, verbose: false,
7
+ adjusted: false
8
+ check_argument([true, false], verbose, "verbose")
9
+ @client = return_client(key, verbose)
10
+ if type == "intraday"
11
+ interval ||= "1min"
12
+ check_argument(["1min", "5min", "15min", "30min", "60min"], interval, "interval")
13
+ check_argument([false], adjusted, "adjusted")
14
+ interval = "&interval=#{interval}"
15
+ else
16
+ check_argument([nil], interval, "interval")
17
+ interval = ""
18
+ end
19
+ check_argument(["compact", "full"], outputsize, "outputsize")
20
+ check_argument(["json", "csv"], datatype, "datatype")
21
+ if datatype == "csv" && file.nil?
22
+ raise Alphavantage::Error.new message: "No file specified where to save the CSV ata"
23
+ elsif datatype != "csv" && !file.nil?
24
+ raise Alphavantage::Error.new message: "Hash error: No file necessary"
25
+ end
26
+
27
+ @selected_time_series = which_series(type, adjusted)
28
+ url = "function=#{@selected_time_series}&symbol=#{symbol}#{interval}&outputsize=#{outputsize}"
29
+ return @client.download(url, file) if datatype == "csv"
30
+ @hash = @client.request(url)
31
+ metadata = @hash.dig("Meta Data") || {}
32
+ metadata.each do |key, val|
33
+ key_sym = recreate_metadata_key(key)
34
+ define_singleton_method(key_sym) do
35
+ return val
36
+ end
37
+ end
38
+
39
+ begin
40
+ time_series = @hash.find{|key, val| key.include?("Time Series")}[1]
41
+ rescue Exception => e
42
+ raise Alphavantage::Error.new message: "No Time Series found: #{e.message}",
43
+ data: @hash
44
+ end
45
+
46
+ series = {}
47
+ convert_key = {}
48
+ time_series.values[0].keys.each do |key|
49
+ key_sym = key.downcase.gsub(/[0-9.]/, "").lstrip.gsub(" ", "_").to_sym
50
+ series[key_sym] = []
51
+ convert_key[key] = key_sym
52
+ end
53
+ time_series.each do |time, ts_hash|
54
+ ts_hash.each do |key, value|
55
+ series[convert_key[key]] << [time, value]
56
+ end
57
+ end
58
+ series.keys.each do |key_sym|
59
+ define_singleton_method(key_sym) do |*args|
60
+ args ||= []
61
+ return return_series(series[key_sym], args[0])
62
+ end
63
+ end
64
+ end
65
+
66
+ attr_reader :hash
67
+
68
+ def which_series(type, adjusted)
69
+ check_argument(["intraday", "daily", "weekly", "monthly"], type, "type")
70
+ series = "TIME_SERIES_"
71
+ series += type.upcase
72
+ series += "_ADJUSTED" if adjusted
73
+ return series
74
+ end
75
+ end
76
+ end