eodhd.rb 0.13.8 → 0.14.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e285836b0317c8ee240d0b4d6c97055e8c32bbf529172a611ce9594764827856
4
- data.tar.gz: bb35b66df2ed01e65cb6bcf42368c5d29d4703293713787e90d18f09f4ed5514
3
+ metadata.gz: b6307a343be540bd2dfe7525c68ec8fd741da0eb349280b560fb3d7b7fa969f6
4
+ data.tar.gz: c90e6506bceb847aa12655be1918abecbda01082a86c35bf0fde6ff245b83ff0
5
5
  SHA512:
6
- metadata.gz: ae6d60f234ef3ec12f5a4ad9c483cc9c7b8e1f2256c9a1bbba737aaca7a5e351a7b59c691e9b8ceed46379e90a0ecd64da99e0a6102b37f1b2e21101f7976ec6
7
- data.tar.gz: 8c0f95acbd5025ea3b35c8eeef96aa6ac4e25dc789f4bb2abf1127cc1ec044da2a3677facbcf0b599c873c19aadd89357e8a569126687b190a73e0e387e99d35
6
+ metadata.gz: a36e1cb75cc4fe2ab891ead816b8dc578d31d9f2380e8d3b14f90a170344603e8ad3f3bc36244a60d5239504d3ea73bdec54a518d1cb9571bab4a1567c59d7bf
7
+ data.tar.gz: c1dc54c034e17f1fb725f3e7568c9d28b98b98d7b30944badd6e113336264fdd22e697c70ea706053bab14ad24de58b8215ee0905daba0d55cca42d01cbe0b16
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.13.8'
5
- spec.date = '2025-03-21'
4
+ spec.version = '0.14.1'
5
+ spec.date = '2025-03-23'
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
- class Eodhd
12
- class Error < RuntimeError
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'
@@ -0,0 +1,16 @@
1
+ # Eodhd/Error.rb
2
+ # Eodhd::Error
3
+
4
+ class Eodhd
5
+ class Error < RuntimeError
6
+ attr_reader :code, :message, :body
7
+
8
+ private
9
+
10
+ def initialize(code:, message:, body:)
11
+ @code = code
12
+ @message = message
13
+ @body = body
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,146 @@
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
+ attr_accessor\
106
+ :api_token,
107
+ :asset_class,
108
+ :consumer
109
+
110
+ attr_reader\
111
+ :symbols
112
+
113
+ def symbols=(symbols)
114
+ @symbols = format_symbols(symbols)
115
+ end
116
+
117
+ def run
118
+ Iodine.threads = 1
119
+ Iodine.connect(url: url, handler: Handler.new(symbols: @symbols, consumer: @consumer))
120
+ Iodine.start
121
+ rescue SystemExit, Interrupt
122
+ return
123
+ end
124
+
125
+ private
126
+
127
+ def initialize(api_token:, asset_class:, symbols:, consumer:)
128
+ @api_token = api_token
129
+ @asset_class = asset_class # crypto, forex, us-quote, us
130
+ @symbols = format_symbols(symbols)
131
+ @consumer = consumer
132
+ end
133
+
134
+ def format_symbols(symbols)
135
+ if symbols.is_a?(Array)
136
+ symbols.join(',')
137
+ elsif symbols.is_a?(String)
138
+ symbols
139
+ end
140
+ end
141
+
142
+ def url
143
+ "wss://#{API_HOST}#{self.class.path_prefix}/#{@asset_class}?api_token=#{@api_token}"
144
+ end
145
+ end
146
+ end
data/lib/eodhd.rb CHANGED
@@ -1,60 +1,35 @@
1
1
  # Eodhd.rb
2
2
  # Eodhd
3
3
 
4
- # 20250321
5
- # 0.13.8
4
+ # 20250323
5
+ # 0.14.1
6
6
 
7
- # Changes since 0.12:
8
- # -/0: Add logging.
9
- # 1. + require 'logger'
10
- # 2. + Eodhd::Client.log_filename
11
- # 3. + Eodhd::Client.log_file
12
- # 4. + Eodhd::Client.logger
13
- # 5. + Eodhd::Client#log
14
- # 6. ~ Eodhd::Client#do_request: Call log().
15
- # 0/1: Add args to the log string.
16
- # 7. ~ Eodhd::Client#do_request: Add the args to the log string.
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().
46
- # 7/8. Fix typo.
47
- # 30. ~ Eodhd::Client#exchanges_list: Remove extraneous parenthesis.
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.
17
+ # 0/1: Some fixes for the WebSocketClient interface.
18
+ # 9. ~ Eodhd::WebSocketClient: + attr_accessor :consumer
19
+ # 10. ~ Eodhd::WebSocketClient: /attr_accessor :symbols/attr_reader :symbols/
20
+ # 11. + Eodhd::WebSocketClient#symbols=
48
21
 
49
22
  require_relative 'Eodhd/Client'
50
23
  require_relative 'Eodhd/EodBulkLastDay'
51
24
  require_relative 'Eodhd/EodData'
52
25
  require_relative 'Eodhd/Exchange'
53
26
  require_relative 'Eodhd/ExchangeSymbol'
27
+ require_relative 'Eodhd/WebSocketClient'
54
28
 
55
29
  class Eodhd
56
- def initialize(api_token:)
30
+ def initialize(api_token:, consumer: nil)
57
31
  @api_token = api_token
32
+ @consumer = consumer
58
33
  end
59
34
 
60
35
  def exchanges
@@ -76,4 +51,34 @@ class Eodhd
76
51
  exchange_code ||= exchange.code
77
52
  Eodhd::EodBulkLastDay.all(api_token: @api_token, exchange_code: exchange_code, date: date)
78
53
  end
54
+
55
+ def web_socket(asset_class:, symbols:)
56
+ Eodhd::WebSocketClient.new(
57
+ api_token: @api_token,
58
+ asset_class: asset_class,
59
+ symbols: symbols,
60
+ consumer: @consumer,
61
+ )
62
+ end
63
+
64
+ def stream(asset_class:, symbols:)
65
+ web_socket(asset_class: asset_class, symbols: symbols).run
66
+ end
67
+
68
+ def us_trade_stream(symbols)
69
+ stream(asset_class: 'us', symbols: symbols)
70
+ end
71
+ alias_method :us_stream, :us_trade_stream
72
+
73
+ def us_quote_stream(symbols)
74
+ stream(asset_class: 'us-quote', symbols: symbols)
75
+ end
76
+
77
+ def forex_stream(symbols)
78
+ stream(asset_class: 'forex', symbols: symbols)
79
+ end
80
+
81
+ def crypto_stream(symbols)
82
+ stream(asset_class: 'crypto', symbols: symbols)
83
+ end
79
84
  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.13.8
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-21 00:00:00.000000000 Z
10
+ date: 2025-03-23 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