eodhd.rb 0.13.7 → 0.14.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/eodhd.rb.gemspec +2 -2
- data/lib/Eodhd/Client.rb +4 -14
- data/lib/Eodhd/Error.rb +16 -0
- data/lib/Eodhd/WebSocketClient.rb +139 -0
- data/lib/eodhd.rb +45 -42
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5e7ba5c1bceffbc1bded24d8143fc41d9fe9989f59008b9da914727d1c98296
|
4
|
+
data.tar.gz: ee6da505ccba36b0be5a901aaae1bd142ed418d4bfbb9c7dce38f43ff46e3d7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1d0fab70a78b53a94213297720fef33053edd2c4922ce95b6d536989037eaf907b59886b262db33e2e4d1b4ff3a76a0ec33ee1e017059191b78ade0f9ec9043
|
7
|
+
data.tar.gz: f872f8c9f5b7d289a7ae6469bf6c6bfe851bc9d2c81f8aff010d2fc40984d8aae5b90de8c895d9f929cc4d42cf689138e6ccfc6ea5ea3fafa06c197527582ad5
|
data/eodhd.rb.gemspec
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = 'eodhd.rb'
|
3
3
|
|
4
|
-
spec.version = '0.
|
5
|
-
spec.date = '2025-03-
|
4
|
+
spec.version = '0.14.0'
|
5
|
+
spec.date = '2025-03-21'
|
6
6
|
|
7
7
|
spec.summary = "Access the eodhd.com API with Ruby."
|
8
8
|
spec.description = "Access the eodhd.com API with Ruby."
|
data/lib/Eodhd/Client.rb
CHANGED
@@ -2,25 +2,15 @@
|
|
2
2
|
# Eodhd::Client
|
3
3
|
|
4
4
|
require 'fileutils'
|
5
|
-
require 'Hash/x_www_form_urlencode'
|
6
5
|
gem 'http.rb'
|
7
6
|
require 'http.rb'
|
8
7
|
require 'json'
|
9
8
|
require 'logger'
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
attr_reader :code, :message, :body
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def initialize(code:, message:, body:)
|
18
|
-
@code = code
|
19
|
-
@message = message
|
20
|
-
@body = body
|
21
|
-
end
|
22
|
-
end
|
10
|
+
require_relative './Error'
|
11
|
+
require_relative '../Hash/x_www_form_urlencode'
|
23
12
|
|
13
|
+
class Eodhd
|
24
14
|
class Client
|
25
15
|
|
26
16
|
API_HOST = 'eodhd.com'
|
@@ -52,7 +42,7 @@ class Eodhd
|
|
52
42
|
|
53
43
|
# This endpoint always returns json regardless of what fmt is specified.
|
54
44
|
def exchanges_list
|
55
|
-
response = get(path: '/exchanges-list')
|
45
|
+
response = get(path: '/exchanges-list')
|
56
46
|
handle_response(response)
|
57
47
|
end
|
58
48
|
|
data/lib/Eodhd/Error.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# Eodhd/WebSocketClient.rb
|
2
|
+
# Eodhd::WebSocketClient
|
3
|
+
|
4
|
+
# Notes:
|
5
|
+
# 1. All 4 of 4 WebSocket endpoints which Eodhd provides (See https://eodhd.com/financial-apis/new-real-time-data-api-websockets.) are as folllows...
|
6
|
+
# i. US market trade data: wss://ws.eodhistoricaldata.com/ws/us?api_token=XXX
|
7
|
+
# ii. US market quote data: wss://ws.eodhistoricaldata.com/ws/us-quote?api_token=XXX
|
8
|
+
# iii. Forex: wss://ws.eodhistoricaldata.com/ws/forex?api_token=XXX
|
9
|
+
# iv. Crypto: wss://ws.eodhistoricaldata.com/ws/crypto?api_token=XXX
|
10
|
+
|
11
|
+
require 'iodine'
|
12
|
+
require 'JSON'
|
13
|
+
require 'UnixTime'
|
14
|
+
|
15
|
+
class Eodhd
|
16
|
+
class WebSocketClient
|
17
|
+
API_HOST = 'ws.eodhistoricaldata.com'
|
18
|
+
|
19
|
+
class Handler
|
20
|
+
class << self
|
21
|
+
attr_writer :log_file_path
|
22
|
+
|
23
|
+
def default_log_file_path
|
24
|
+
File.join(%w{~ log eodhd log.txt})
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_file_path
|
28
|
+
File.expand_path(@log_file_path || default_log_file_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def log_file
|
32
|
+
FileUtils.mkdir_p(File.dirname(log_file_path))
|
33
|
+
File.open(log_file_path, File::WRONLY | File::APPEND | File::CREAT)
|
34
|
+
end
|
35
|
+
|
36
|
+
def logger
|
37
|
+
@logger ||= Logger.new(log_file, 'daily')
|
38
|
+
end
|
39
|
+
end # class << self
|
40
|
+
|
41
|
+
def on_open(connection)
|
42
|
+
connection.write({action: 'subscribe', symbols: @symbols}.to_json)
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_message(connection, message)
|
46
|
+
dataframe = handle_response(message)
|
47
|
+
@consumer.call(dataframe)
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_close(connection)
|
51
|
+
Iodine.stop
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def initialize(symbols:, consumer:)
|
57
|
+
@symbols = symbols
|
58
|
+
@consumer = consumer
|
59
|
+
end
|
60
|
+
|
61
|
+
def log_error(code:, message:, body:)
|
62
|
+
log_string = "WebSocketError #{code}\n#{message}\n#{body}"
|
63
|
+
self.class.logger.error(log_string)
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_response(message)
|
67
|
+
if parsed_json = JSON.parse(message)
|
68
|
+
parsed_json
|
69
|
+
else
|
70
|
+
log_error(
|
71
|
+
message: response.message,
|
72
|
+
)
|
73
|
+
raise Eodhd::Error.new(
|
74
|
+
code: 'ws',
|
75
|
+
message: response.message,
|
76
|
+
body: ''
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class << self
|
83
|
+
def path_prefix
|
84
|
+
'/ws'
|
85
|
+
end
|
86
|
+
|
87
|
+
def us_trade(api_token:, symbols:, consumer:)
|
88
|
+
self.class.new(api_token: api_token, asset_class: 'us', symbols: symbols, consumer: consumer)
|
89
|
+
end
|
90
|
+
alias_method :us, :us_trade
|
91
|
+
|
92
|
+
def us_quote(api_token:, symbols:, consumer:)
|
93
|
+
self.class.new(api_token: api_token, asset_class: 'us-quote', symbols: symbols, consumer: consumer)
|
94
|
+
end
|
95
|
+
|
96
|
+
def forex(api_token:, symbols:, consumer:)
|
97
|
+
self.class.new(api_token: api_token, asset_class: 'forex', symbols: symbols, consumer: consumer)
|
98
|
+
end
|
99
|
+
|
100
|
+
def crypto(api_token:, symbols:, consumer:)
|
101
|
+
self.class.new(api_token: api_token, asset_class: 'crypto', symbols: symbols, consumer: consumer)
|
102
|
+
end
|
103
|
+
end # class << self
|
104
|
+
|
105
|
+
def run
|
106
|
+
Iodine.threads = 1
|
107
|
+
Iodine.connect(url: url, handler: Handler.new(symbols: @symbols, consumer: @consumer))
|
108
|
+
Iodine.start
|
109
|
+
rescue SystemExit, Interrupt
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
attr_accessor\
|
116
|
+
:api_token,
|
117
|
+
:asset_class,
|
118
|
+
:symbols
|
119
|
+
|
120
|
+
def initialize(api_token:, asset_class:, symbols:, consumer:)
|
121
|
+
@api_token = api_token
|
122
|
+
@asset_class = asset_class # crypto, forex, us-quote, us
|
123
|
+
@symbols = format_symbols(symbols)
|
124
|
+
@consumer = consumer
|
125
|
+
end
|
126
|
+
|
127
|
+
def format_symbols(symbols)
|
128
|
+
if symbols.is_a?(Array)
|
129
|
+
symbols.join(',')
|
130
|
+
elsif symbols.is_a?(String)
|
131
|
+
symbols
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def url
|
136
|
+
"wss://#{API_HOST}#{self.class.path_prefix}/#{@asset_class}?api_token=#{@api_token}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/lib/eodhd.rb
CHANGED
@@ -1,58 +1,31 @@
|
|
1
1
|
# Eodhd.rb
|
2
2
|
# Eodhd
|
3
3
|
|
4
|
-
#
|
5
|
-
# 0.
|
4
|
+
# 20250321
|
5
|
+
# 0.14.0
|
6
6
|
|
7
|
-
# Changes since 0.
|
8
|
-
# -/0: Add
|
9
|
-
# 1. +
|
10
|
-
# 2. + Eodhd
|
11
|
-
# 3. + Eodhd
|
12
|
-
# 4. + Eodhd
|
13
|
-
# 5. + Eodhd
|
14
|
-
# 6.
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# 1/2: Log '?' and args only if args are present.
|
18
|
-
# 8. ~ Eodhd::Client#log: Construct the log string in the method, constructing a log string with arguments if present.
|
19
|
-
# 9. ~ Eodhd::Client#do_request: Call the new interface for log().
|
20
|
-
# 2/3: Fix logging '?' for when args values are nil.
|
21
|
-
# 10. + Eodhd::Client#log_args? Only need to check for whether the values are nil, since this will still work for empty args also.
|
22
|
-
# 11. ~ Eodhd::Client#log: Use log_args?
|
23
|
-
# 3/4: + eodhd.rb.gemspec, + additional necessary library files
|
24
|
-
# 12. + eodhd.rb.gemspec
|
25
|
-
# 13. ~ Gemfile: Use gemspec.
|
26
|
-
# 14. + lib/Hash/x_www_form_urlencode.rb
|
27
|
-
# 15. + lib/Thoran/Hash/XWwwFormUrlEncode/x_www_form_url_encode.rb
|
28
|
-
# 16. + lib/Thoran/String/UrlEncode/url_encode.rb
|
29
|
-
# 4/5: Create a directory for the log file when running for the first time and also allow specification of the log file path.
|
30
|
-
# 17. ~ Eodhd::Client#log_file: Create the log file directory if necessary.
|
31
|
-
# 18. ~ Eodhd::Client: + attr_writer :log_file_path
|
32
|
-
# 19. + Eodhd::Client#default_log_file_path
|
33
|
-
# 20. ~ Eodhd::Client: /log_filename/log_file_path/
|
34
|
-
# 5/6: Error handling for HTTP responses.
|
35
|
-
# 21. + Eodhd::Client#handle_response
|
36
|
-
# 22. + Eodhd::Client#get
|
37
|
-
# 23. ~ Eodhd::Client#do_request: Accepts a verb and a path now as well as arguments, instead of a request string and arguments.
|
38
|
-
# 23. + Eodhd::Error
|
39
|
-
# 24. ~ Eodhd::Client#log: + verb argument
|
40
|
-
# 25. ~ Eodhd::Client public methods: Use get() and handle_response().
|
41
|
-
# 6/7. Logging for HTTP errors.
|
42
|
-
# 26. ~ Eodhd::Client: /log()/log_request()/
|
43
|
-
# 27. + Eodhd::Client#log_error
|
44
|
-
# 28. ~ Eodhd::Client#do_request: /log/log_request/.
|
45
|
-
# 29. ~ Eodhd::Client#handle_response: Use log_error().
|
7
|
+
# Changes since 0.13:
|
8
|
+
# -/0: Add WebSockets.
|
9
|
+
# 1. + Eodhd::WebSocketClient
|
10
|
+
# 2. + Eodhd#web_socket
|
11
|
+
# 3. + Eodhd#stream
|
12
|
+
# 4. + Eodhd#us_quote_stream
|
13
|
+
# 5. + Eodhd#us_trade_stream
|
14
|
+
# 6. + Eodhd#forex_stream
|
15
|
+
# 7. + Eodhd#crypto_stream
|
16
|
+
# 8. Moved Eodhd::Error from Eodhd::Client to a separate file.
|
46
17
|
|
47
18
|
require_relative 'Eodhd/Client'
|
48
19
|
require_relative 'Eodhd/EodBulkLastDay'
|
49
20
|
require_relative 'Eodhd/EodData'
|
50
21
|
require_relative 'Eodhd/Exchange'
|
51
22
|
require_relative 'Eodhd/ExchangeSymbol'
|
23
|
+
require_relative 'Eodhd/WebSocketClient'
|
52
24
|
|
53
25
|
class Eodhd
|
54
|
-
def initialize(api_token:)
|
26
|
+
def initialize(api_token:, consumer: nil)
|
55
27
|
@api_token = api_token
|
28
|
+
@consumer = consumer
|
56
29
|
end
|
57
30
|
|
58
31
|
def exchanges
|
@@ -74,4 +47,34 @@ class Eodhd
|
|
74
47
|
exchange_code ||= exchange.code
|
75
48
|
Eodhd::EodBulkLastDay.all(api_token: @api_token, exchange_code: exchange_code, date: date)
|
76
49
|
end
|
50
|
+
|
51
|
+
def web_socket(asset_class:, symbols:)
|
52
|
+
Eodhd::WebSocketClient.new(
|
53
|
+
api_token: @api_token,
|
54
|
+
asset_class: asset_class,
|
55
|
+
symbols: symbols,
|
56
|
+
consumer: @consumer,
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def stream(asset_class:, symbols:)
|
61
|
+
web_socket(asset_class: asset_class, symbols: symbols).run
|
62
|
+
end
|
63
|
+
|
64
|
+
def us_trade_stream(symbols)
|
65
|
+
stream(asset_class: 'us', symbols: symbols)
|
66
|
+
end
|
67
|
+
alias_method :us_stream, :us_trade_stream
|
68
|
+
|
69
|
+
def us_quote_stream(symbols)
|
70
|
+
stream(asset_class: 'us-quote', symbols: symbols)
|
71
|
+
end
|
72
|
+
|
73
|
+
def forex_stream(symbols)
|
74
|
+
stream(asset_class: 'forex', symbols: symbols)
|
75
|
+
end
|
76
|
+
|
77
|
+
def crypto_stream(symbols)
|
78
|
+
stream(asset_class: 'crypto', symbols: symbols)
|
79
|
+
end
|
77
80
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eodhd.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- thoran
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-21 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: http.rb
|
@@ -35,8 +35,10 @@ files:
|
|
35
35
|
- lib/Eodhd/Client.rb
|
36
36
|
- lib/Eodhd/EodBulkLastDay.rb
|
37
37
|
- lib/Eodhd/EodData.rb
|
38
|
+
- lib/Eodhd/Error.rb
|
38
39
|
- lib/Eodhd/Exchange.rb
|
39
40
|
- lib/Eodhd/ExchangeSymbol.rb
|
41
|
+
- lib/Eodhd/WebSocketClient.rb
|
40
42
|
- lib/Hash/x_www_form_urlencode.rb
|
41
43
|
- lib/Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb
|
42
44
|
- lib/Thoran/String/UrlEncode/url_encode.rb
|