market_data 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/Gemfile +5 -1
- data/Gemfile.lock +13 -1
- data/README.md +34 -14
- data/lib/market_data/constants.rb +13 -0
- data/lib/market_data/errors.rb +18 -1
- data/lib/market_data/mappers.rb +67 -0
- data/lib/market_data/models.rb +19 -0
- data/lib/market_data/quotes.rb +50 -25
- data/lib/market_data/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '082a3b709ed32b745807906d0324a3b839a90c95e55bc199b564db75518d1fbc'
|
4
|
+
data.tar.gz: dab55b3c50200dbf92e071dc0bd3eab5b8d712928f82d045c92a80a5f09b0979
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64fca38c4e459cb4244f845ad4137939249c353b2e91922cc0bb416a2b1129871916c80bf0d44e3a71e8c48b797de2a27f378192a790286aed9d2f985345c556
|
7
|
+
data.tar.gz: 97b5508677cbff6418254771f78e68eb48c0ee66c067274663a40ad2c6cd00af3355dac9b93bd812e90290f2fb1c10af2d0b6ba3aa447bcc760f43024ccc8dc9
|
data/CHANGELOG.md
CHANGED
@@ -7,4 +7,16 @@
|
|
7
7
|
## [0.1.1] - 2024-09-12
|
8
8
|
|
9
9
|
- Fix handling of snapshot parameter in bulk quotes method
|
10
|
-
- Rename fetch method to quote on Quote submodule. Quotes methods are no longer wrapped in parent module.
|
10
|
+
- Rename fetch method to quote on Quote submodule. Quotes methods are no longer wrapped in parent module.
|
11
|
+
|
12
|
+
## [0.1.2] - 2024-09-24
|
13
|
+
|
14
|
+
- Change validations for bulk_quotes method
|
15
|
+
- Add `token` method to `Client` class
|
16
|
+
|
17
|
+
## [0.2.0] - 2024-09-30
|
18
|
+
|
19
|
+
- Internal rework of code. New modules for models, mappers, constants and errors
|
20
|
+
- Introduced unit tests for almost all modules.
|
21
|
+
- Introduced coverage. Currently at 85%
|
22
|
+
- Add functionality for [Candles](https://www.marketdata.app/docs/api/stocks/candles) and [Bulk Candles](https://www.marketdata.app/docs/api/stocks/bulkcandles) endpoints
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
market_data (0.1.
|
4
|
+
market_data (0.1.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
+
docile (1.4.1)
|
9
10
|
minitest (5.16.3)
|
11
|
+
mocha (2.4.5)
|
12
|
+
ruby2_keywords (>= 0.0.5)
|
10
13
|
rake (13.0.6)
|
14
|
+
ruby2_keywords (0.0.5)
|
15
|
+
simplecov (0.22.0)
|
16
|
+
docile (~> 1.1)
|
17
|
+
simplecov-html (~> 0.11)
|
18
|
+
simplecov_json_formatter (~> 0.1)
|
19
|
+
simplecov-html (0.13.1)
|
20
|
+
simplecov_json_formatter (0.1.4)
|
11
21
|
|
12
22
|
PLATFORMS
|
13
23
|
arm64-darwin-23
|
@@ -15,7 +25,9 @@ PLATFORMS
|
|
15
25
|
DEPENDENCIES
|
16
26
|
market_data!
|
17
27
|
minitest (~> 5.0)
|
28
|
+
mocha
|
18
29
|
rake (~> 13.0)
|
30
|
+
simplecov
|
19
31
|
|
20
32
|
BUNDLED WITH
|
21
33
|
2.4.1
|
data/README.md
CHANGED
@@ -1,34 +1,54 @@
|
|
1
1
|
# MarketData
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/market_data`. To experiment with that code, run `bin/console` for an interactive prompt.
|
3
|
+
A Ruby wrapper for the [MarketData API](https://www.marketdata.app/docs/api).
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
|
-
|
7
|
+
$ gem install market-data
|
8
|
+
$ bundle install
|
9
|
+
|
10
|
+
## Usage
|
10
11
|
|
11
|
-
|
12
|
+
$ client = MarketData.Client.new "YOUR_API_TOKEN"
|
13
|
+
$ client.quote("AAPL")
|
14
|
+
$ => <struct MarketData::Models::Quote symbol="AAPL", ask=231.42, askSize=2, bid=231.4, .....
|
12
15
|
|
13
|
-
|
16
|
+
## ROADMAP
|
14
17
|
|
15
|
-
|
18
|
+
The following is an ordered list of next expected developments, based on the endpoints present in the [docs](https://www.marketdata.app/docs/api)
|
16
19
|
|
17
|
-
|
20
|
+
From Stocks endpoints:
|
21
|
+
- [X] Stocks
|
22
|
+
- [X] Bulk Stocks
|
23
|
+
- [X] Candles
|
24
|
+
- [X] Bulk Candles
|
25
|
+
- [X] Support for optional parameters for Bulk Candles
|
26
|
+
- [] Earnings
|
27
|
+
|
28
|
+
From Markets endpoints:
|
29
|
+
- [] Status
|
30
|
+
|
31
|
+
From Indices endpoints:
|
32
|
+
- [] Quotes
|
33
|
+
- [] Candles
|
34
|
+
|
35
|
+
From Stocks endpoints:
|
36
|
+
- [] Support for optional parameters for Candles
|
37
|
+
- [] Support for optional parameters for Bulk Candles
|
18
38
|
|
19
|
-
## Usage
|
20
39
|
|
21
|
-
|
40
|
+
## Tests
|
22
41
|
|
23
|
-
|
42
|
+
Run tests with
|
24
43
|
|
25
|
-
|
44
|
+
$ rake
|
26
45
|
|
27
|
-
To
|
46
|
+
To run tests and check coverage
|
28
47
|
|
48
|
+
$ rake && open coverage/index.html
|
29
49
|
## Contributing
|
30
50
|
|
31
|
-
|
51
|
+
Leave an issue or contact me at sebagonz91@gmail.com
|
32
52
|
|
33
53
|
## License
|
34
54
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MarketData
|
2
|
+
module Constants
|
3
|
+
SECOND = 1
|
4
|
+
MINUTE = 60
|
5
|
+
HOUR = MINUTE * 60
|
6
|
+
DAY = HOUR * 24
|
7
|
+
WEEK = DAY * 7
|
8
|
+
YEAR = DAY * 365
|
9
|
+
|
10
|
+
QUOTE_FIELDS = [:symbol, :ask, :askSize, :bid, :bidSize, :mid, :last, :change, :changepct, :volume, :updated, :high52, :low52]
|
11
|
+
CANDLE_FIELDS = [:symbol, :open, :high, :low, :close, :volume, :time]
|
12
|
+
end
|
13
|
+
end
|
data/lib/market_data/errors.rb
CHANGED
@@ -1,15 +1,32 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module MarketData
|
2
4
|
|
3
5
|
class ClientError < StandardError;end
|
4
6
|
class UnauthorizedError < ClientError; end
|
7
|
+
class RateLimitedError < ClientError; end
|
5
8
|
class BadParameterError < ClientError; end
|
9
|
+
class NotFoundError < ClientError; end
|
6
10
|
|
7
11
|
module Errors
|
8
12
|
def handle_error e
|
9
13
|
er = e.io
|
14
|
+
parsed_info = JSON.parse(er.string)
|
10
15
|
case er.status[0]
|
16
|
+
when "400"
|
17
|
+
if parsed_info["s"] == "error"
|
18
|
+
raise BadParameterError.new(parsed_info["errmsg"])
|
19
|
+
end
|
20
|
+
raise BadParameterError
|
21
|
+
when "404"
|
22
|
+
if parsed_info["s"] == "no_data"
|
23
|
+
raise NotFoundError.new("no candle information was found for the request")
|
24
|
+
end
|
25
|
+
raise NotFoundError
|
11
26
|
when "401"
|
12
|
-
raise UnauthorizedError
|
27
|
+
raise UnauthorizedError.new(parsed_info["errmsg"])
|
28
|
+
when "429"
|
29
|
+
raise RateLimitedError
|
13
30
|
else
|
14
31
|
raise e
|
15
32
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'market_data/models'
|
2
|
+
|
3
|
+
module MarketData
|
4
|
+
module Mappers
|
5
|
+
include MarketData::Models
|
6
|
+
|
7
|
+
SYMBOL_RESPONSE_KEY = "symbol"
|
8
|
+
STATUS_RESPONSE_KEY = "s"
|
9
|
+
|
10
|
+
def map_quote response, i=0
|
11
|
+
Quote.new(
|
12
|
+
symbol: response["symbol"][i],
|
13
|
+
ask: response["ask"][i],
|
14
|
+
askSize: response["askSize"][i],
|
15
|
+
bid: response["bid"][i],
|
16
|
+
bidSize: response["bidSize"][i],
|
17
|
+
mid: response["mid"][i],
|
18
|
+
last: response["last"][i],
|
19
|
+
change: response["change"][i],
|
20
|
+
changepct: response["changepct"][i],
|
21
|
+
volume: response["volume"][i],
|
22
|
+
updated: response["updated"][i],
|
23
|
+
high52: response.fetch("high52", nil),
|
24
|
+
low52: response.fetch("low52", nil),
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def map_bulk_quotes response
|
29
|
+
h = Hash.new
|
30
|
+
size = response[SYMBOL_RESPONSE_KEY].size
|
31
|
+
for i in 0..(size - 1) do
|
32
|
+
qquote = map_quote(response, i)
|
33
|
+
h[response[SYMBOL_RESPONSE_KEY][i]] = !qquote.blank? ? qquote : nil
|
34
|
+
end
|
35
|
+
h
|
36
|
+
end
|
37
|
+
|
38
|
+
def map_candles response, symbol
|
39
|
+
ar = []
|
40
|
+
size = response["o"].size
|
41
|
+
|
42
|
+
for i in 0..(size - 1) do
|
43
|
+
ar << Candle.new(
|
44
|
+
open: response["o"][i],
|
45
|
+
high: response["h"][i],
|
46
|
+
low: response["l"][i],
|
47
|
+
close: response["c"][i],
|
48
|
+
volume: response["v"][i],
|
49
|
+
time: response["t"][i],
|
50
|
+
symbol: symbol
|
51
|
+
)
|
52
|
+
end
|
53
|
+
ar
|
54
|
+
end
|
55
|
+
|
56
|
+
def map_bulk_candles response
|
57
|
+
h = Hash.new
|
58
|
+
size = response[SYMBOL_RESPONSE_KEY].size
|
59
|
+
|
60
|
+
for i in 0..(size - 1) do
|
61
|
+
candle = Candle.new(symbol: response["symbol"][i], open: response["o"][i], high: response["h"][i], low: response["l"][i], close: response["c"][i], volume: response["v"][i], time: response["t"][i])
|
62
|
+
h[response[SYMBOL_RESPONSE_KEY][i]] = !candle.blank? ? candle : nil
|
63
|
+
end
|
64
|
+
h
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'market_data/constants'
|
2
|
+
|
3
|
+
module MarketData
|
4
|
+
module Models
|
5
|
+
include MarketData::Constants
|
6
|
+
|
7
|
+
Quote = Struct.new(*Constants::QUOTE_FIELDS) do
|
8
|
+
def blank?
|
9
|
+
(QUOTE_FIELDS - [:symbol]).all? { |mmethod| self[mmethod].nil?}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Candle = Struct.new(*Constants::CANDLE_FIELDS) do
|
14
|
+
def blank?
|
15
|
+
(CANDLE_FIELDS - [:symbol]).all? { |mmethod| self[mmethod].nil?}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/market_data/quotes.rb
CHANGED
@@ -1,32 +1,25 @@
|
|
1
1
|
require 'market_data/conn'
|
2
2
|
require 'market_data/errors'
|
3
|
+
require 'market_data/mappers'
|
3
4
|
|
4
5
|
module MarketData
|
5
6
|
module Quotes
|
7
|
+
include MarketData::Mappers
|
6
8
|
include MarketData::Errors
|
7
9
|
include MarketData::Conn
|
8
10
|
|
9
|
-
SYMBOL_RESPONSE_KEY = "symbol"
|
10
|
-
STATUS_RESPONSE_KEY = "s"
|
11
|
-
|
12
|
-
QUOTE_FIELDS = [:symbol, :ask, :askSize, :bid, :bidSize, :mid, :last, :change, :changepct, :volume, :updated, :high52, :low52]
|
13
11
|
@@single = "/v1/stocks/quotes/"
|
14
12
|
@@bulk = "/v1/stocks/bulkquotes/"
|
13
|
+
@@candles = "/v1/stocks/candles/"
|
14
|
+
@@bulk_candles = "/v1/stocks/bulkcandles/"
|
15
15
|
|
16
|
-
# Quote is the struct to hold ticker request information
|
17
|
-
Quote = Struct.new(*QUOTE_FIELDS) do
|
18
|
-
def blank?
|
19
|
-
(QUOTE_FIELDS - [:symbol]).all? { |mmethod| self[mmethod].nil?}
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
16
|
def quote(symbol, w52 = false)
|
24
17
|
path_hash = { host: MarketData.base_host, path: @@single + symbol }
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
18
|
+
if w52
|
19
|
+
path_hash[:query] = URI.encode_www_form({"52week" => true })
|
20
|
+
end
|
21
|
+
res = do_connect(get_uri path_hash)
|
22
|
+
map_quote(res)
|
30
23
|
end
|
31
24
|
|
32
25
|
def bulk_quotes(symbols, snapshot = false)
|
@@ -36,7 +29,7 @@ module MarketData
|
|
36
29
|
if snapshot
|
37
30
|
query_hash[:snapshot] = true
|
38
31
|
else
|
39
|
-
|
32
|
+
if !symbols.is_a?(Array) || symbols.size < 1
|
40
33
|
raise BadParameterError.new("symbols must be a non-empty list")
|
41
34
|
end
|
42
35
|
query_hash = { symbols: symbols.join(",") }
|
@@ -45,17 +38,49 @@ module MarketData
|
|
45
38
|
path_hash[:query] = URI.encode_www_form(query_hash)
|
46
39
|
|
47
40
|
res = do_connect(get_uri path_hash)
|
48
|
-
|
41
|
+
map_bulk_quotes res
|
42
|
+
end
|
43
|
+
|
44
|
+
def candles(symbol, opts = {})
|
45
|
+
defaults = {resolution: "D", from: nil, to: Time.now.utc.to_i, countback: nil}
|
46
|
+
opts = defaults.merge(opts)
|
47
|
+
|
48
|
+
query_hash = {to: opts[:to]}
|
49
|
+
|
50
|
+
# TODO Move method validations into own class
|
51
|
+
# TODO check to is either iso8601 or unix
|
52
|
+
if opts[:from].nil? && opts[:countback].nil?
|
53
|
+
raise BadParameterError.new("either :from or :countback must be supplied")
|
54
|
+
end
|
55
|
+
|
56
|
+
if opts[:from].nil?
|
57
|
+
query_hash[:countback] = opts[:countback]
|
58
|
+
else
|
59
|
+
query_hash[:from] = opts[:from]
|
60
|
+
end
|
61
|
+
|
62
|
+
path_hash = { host: MarketData.base_host, path: @@candles + opts[:resolution] + "/" + symbol }
|
63
|
+
path_hash[:query] = URI.encode_www_form(query_hash)
|
64
|
+
|
65
|
+
res = do_connect(get_uri path_hash)
|
66
|
+
map_candles res, symbol
|
49
67
|
end
|
50
68
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
for i in 0..(size - 1) do
|
55
|
-
qquote = Quote.new(*quotes.except(STATUS_RESPONSE_KEY).values.map { |ar| ar[i] })
|
56
|
-
h[quotes[SYMBOL_RESPONSE_KEY][i]] = !qquote.blank? ? qquote : nil
|
69
|
+
def bulk_candles(symbols, resolution = "D")
|
70
|
+
unless resolution == "daily" || resolution == "1D" || resolution == "D"
|
71
|
+
raise BadParameterError.new("only daily resolution is allowed for this endpoint")
|
57
72
|
end
|
58
|
-
|
73
|
+
path_hash = { host: MarketData.base_host, path: @@bulk_candles + resolution + "/" }
|
74
|
+
|
75
|
+
if !symbols.is_a?(Array) || symbols.size < 1
|
76
|
+
raise BadParameterError.new("symbols must be a non-empty list")
|
77
|
+
end
|
78
|
+
query_hash = { symbols: symbols.join(",") }
|
79
|
+
|
80
|
+
path_hash[:query] = URI.encode_www_form(query_hash)
|
81
|
+
|
82
|
+
res = do_connect(get_uri path_hash)
|
83
|
+
map_bulk_candles res
|
59
84
|
end
|
60
85
|
end
|
61
86
|
end
|
data/lib/market_data/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: market_data
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastián González
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Ruby client for the MarketData API.
|
14
14
|
email:
|
@@ -26,7 +26,10 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- lib/market_data.rb
|
28
28
|
- lib/market_data/conn.rb
|
29
|
+
- lib/market_data/constants.rb
|
29
30
|
- lib/market_data/errors.rb
|
31
|
+
- lib/market_data/mappers.rb
|
32
|
+
- lib/market_data/models.rb
|
30
33
|
- lib/market_data/quotes.rb
|
31
34
|
- lib/market_data/version.rb
|
32
35
|
- market_data.gemspec
|