eodhd 0.19.5

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +195 -0
  3. data/Gemfile +3 -0
  4. data/README.md +160 -0
  5. data/Rakefile +28 -0
  6. data/eodhd.gemspec +52 -0
  7. data/lib/Eodhd/Client.rb +143 -0
  8. data/lib/Eodhd/DefaultLogger.rb +30 -0
  9. data/lib/Eodhd/EodBulkLastDay.rb +56 -0
  10. data/lib/Eodhd/EodData.rb +56 -0
  11. data/lib/Eodhd/Error.rb +16 -0
  12. data/lib/Eodhd/Exchange.rb +50 -0
  13. data/lib/Eodhd/ExchangeSymbol.rb +50 -0
  14. data/lib/Eodhd/Fundamentals/AnalystRatings.rb +29 -0
  15. data/lib/Eodhd/Fundamentals/ESGScores.rb +37 -0
  16. data/lib/Eodhd/Fundamentals/Earnings/AnnualEntry.rb +21 -0
  17. data/lib/Eodhd/Fundamentals/Earnings/HistoryEntry.rb +33 -0
  18. data/lib/Eodhd/Fundamentals/Earnings/TrendEntry.rb +65 -0
  19. data/lib/Eodhd/Fundamentals/Earnings.rb +30 -0
  20. data/lib/Eodhd/Fundamentals/Financials/BalanceSheet/Entry.rb +147 -0
  21. data/lib/Eodhd/Fundamentals/Financials/BalanceSheet.rb +28 -0
  22. data/lib/Eodhd/Fundamentals/Financials/CashFlow/Entry.rb +83 -0
  23. data/lib/Eodhd/Fundamentals/Financials/CashFlow.rb +28 -0
  24. data/lib/Eodhd/Fundamentals/Financials/IncomeStatement/Entry.rb +87 -0
  25. data/lib/Eodhd/Fundamentals/Financials/IncomeStatement.rb +28 -0
  26. data/lib/Eodhd/Fundamentals/Financials.rb +25 -0
  27. data/lib/Eodhd/Fundamentals/General.rb +85 -0
  28. data/lib/Eodhd/Fundamentals/Highlights.rb +63 -0
  29. data/lib/Eodhd/Fundamentals/Holders/Fund.rb +31 -0
  30. data/lib/Eodhd/Fundamentals/Holders/Institution.rb +31 -0
  31. data/lib/Eodhd/Fundamentals/Holders.rb +27 -0
  32. data/lib/Eodhd/Fundamentals/InsiderTransaction.rb +35 -0
  33. data/lib/Eodhd/Fundamentals/OutstandingShares/Entry.rb +25 -0
  34. data/lib/Eodhd/Fundamentals/OutstandingShares.rb +26 -0
  35. data/lib/Eodhd/Fundamentals/SharesStats.rb +33 -0
  36. data/lib/Eodhd/Fundamentals/SplitsDividends.rb +31 -0
  37. data/lib/Eodhd/Fundamentals/Technicals.rb +33 -0
  38. data/lib/Eodhd/Fundamentals/Valuation.rb +29 -0
  39. data/lib/Eodhd/Fundamentals.rb +92 -0
  40. data/lib/Eodhd/Intraday.rb +76 -0
  41. data/lib/Eodhd/VERSION.rb +3 -0
  42. data/lib/Eodhd/Validations.rb +78 -0
  43. data/lib/Eodhd/WebSocketClient.rb +130 -0
  44. data/lib/Hash/x_www_form_urlencode.rb +7 -0
  45. data/lib/Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb +27 -0
  46. data/lib/Thoran/String/Urlencode/urlencode.rb +29 -0
  47. data/lib/eodhd.rb +78 -0
  48. data/test/Eodhd/Client_test.rb +111 -0
  49. data/test/Eodhd/Fundamentals_test.rb +163 -0
  50. data/test/Eodhd/VERSION_test.rb +20 -0
  51. data/test/Eodhd/Validations_test.rb +135 -0
  52. data/test/Eodhd/WebSocketClient_test.rb +99 -0
  53. data/test/Eodhd_test.rb +73 -0
  54. data/test/helper.rb +17 -0
  55. metadata +218 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cf8b67ed05dcf07f838df21a530d42c07824fd548ce5956f0425356c6e20ab9b
4
+ data.tar.gz: 107375d103ae2afebd038183c256c7c4d387d47287bbcf524ef5afe058c0de24
5
+ SHA512:
6
+ metadata.gz: e84643904cbe85f5d7b67961927b071e9966020677f6c48bd3c5070e4e47bcd90c18ac72cb1ed9f0d5c6158978624109136a2a3935a2373c2f8208862ee05980
7
+ data.tar.gz: 2a6f1e38654db71f172aa88e40ef96e3e55a3f98762930648c0b24986c6f0714cb480503386920c31eb3ee7a3d5c018fefb95b9fb15307806aa11f0bc79a10e4
data/CHANGELOG ADDED
@@ -0,0 +1,195 @@
1
+ # CHANGELOG
2
+
3
+ ## 20260915
4
+
5
+ 0.19.5: + eodhd.gemspec, so that the gem resolves under both names.
6
+
7
+ 1. + eodhd.gemspec: eodhd.rb.gemspec with /eodhd.rb/eodhd/ in spec.name and in spec.files, upon measurand 0.1.4's pattern.
8
+ 2. ~ Gemfile: /gemspec/gemspec name: 'eodhd.rb'/, bundler refusing to parse a Gemfile whose bare gemspec directive finds more than one gemspec to choose from.
9
+ 3. ~ Eodhd::VERSION: /0.19.4/0.19.5/
10
+
11
+
12
+ ## 20260910
13
+
14
+ 0.19.4: ~ lib/Eodhd.rb: + require_relative './Eodhd/VERSION'; + test/Eodhd/VERSION_test.rb
15
+
16
+ 1. ~ lib/Eodhd.rb: + `require_relative './Eodhd/VERSION'`, last among the requires. The file has been present and unreached, so `require 'eodhd'` left Eodhd::VERSION undefined and the gemspec was the only thing loading it.
17
+ 2. + test/Eodhd/VERSION_test.rb: that Eodhd::VERSION is a string, that it is three numbers separated by dots, and that it matches the newest entry in the changelog. Modelled on moby's, which is where the convention comes from.
18
+ 3. The test loads the library rather than VERSION.rb, which is the whole of its value: run against v0.19.3 it fails with "uninitialized constant Eodhd::VERSION". Nine libraries were in that state and not one of them carried this test, which is how it went unseen.
19
+ 4. ~ Eodhd::VERSION: /0.19.3/0.19.4/
20
+
21
+
22
+ ## 20260821
23
+
24
+ 0.19.3: Rewrite the CHANGELOG in the form changelogger writes.
25
+
26
+ 1. ~ CHANGELOG: The Keep a Changelog form, `## [version] - date, summary` over `- ` bullets, --> the form the rest of these gems use: a `## YYYYMMDD` heading per date, newest first, holding each of that date's versions as a summary line over numbered items. No item was reworded, dropped or reordered, and all 111 are still here.
27
+ 2. ~ CHANGELOG: 20260719 carried three headings, for 0.18.1, 0.19.0 and 0.19.1, so one day's work read as three; 20251005 carried two, for 0.16.0 and 0.17.0. One heading each now, the versions under them in the same order they were in.
28
+ 3. ~ CHANGELOG: The version summaries are the commit subjects, which three of them had not been: 0.19.2 read "populate the .gitignore" against a commit saying `~ .gitignore: + current default contents`, 0.19.1 likewise, and 0.19.0 differed in case. Every other repo here keeps the two identical, which is what makes a summary checkable against something.
29
+ 4. ~ CHANGELOG: /# eodhd.rb\/CHANGELOG/# CHANGELOG/, the file's own name being no part of what it records.
30
+ 5. ~ Eodhd/VERSION: /0.19.2/0.19.3/
31
+
32
+ 0.19.2: ~ .gitignore: + current default contents
33
+
34
+ 1. ~ .gitignore: + current default contents. The file held only its filename comment, having been committed that way.
35
+ 2. ~ Eodhd/VERSION: /0.19.1/0.19.2/
36
+
37
+
38
+ ## 20260719
39
+
40
+ 0.19.1: ~ README.md: + a blank line before each code fence.
41
+
42
+ 1. ~ README.md: + a blank line before each code fence, without which the highlighting does not render.
43
+ 2. ~ Eodhd/VERSION: /0.19.0/0.19.1/
44
+
45
+ 0.19.0: Complete the fundamentals model wrapping.
46
+
47
+ 1. + Eodhd::Fundamentals::Financials::BalanceSheet::Entry: Typed entry for a balance sheet period (64 fields).
48
+ 2. + Eodhd::Fundamentals::Financials::IncomeStatement::Entry: Typed entry for an income statement period (34 fields).
49
+ 3. + Eodhd::Fundamentals::Financials::CashFlow::Entry: Typed entry for a cash flow period (32 fields).
50
+ 4. + Eodhd::Fundamentals::Earnings::AnnualEntry
51
+ 5. + Eodhd::Fundamentals::Earnings::HistoryEntry
52
+ 6. + Eodhd::Fundamentals::Earnings::TrendEntry
53
+ 7. + Eodhd::Fundamentals::Holders::Institution: Typed entry for an institutional holder.
54
+ 8. + Eodhd::Fundamentals::Holders::Fund: Typed entry for a fund holder.
55
+ 9. + Eodhd::Fundamentals::InsiderTransaction: Typed entry for an insider transaction.
56
+ 10. + Eodhd::Fundamentals::OutstandingShares::Entry: Typed entry for an outstanding shares data point.
57
+ 11. + Eodhd::Fundamentals::OutstandingShares: Wraps annual and quarterly OutstandingShares::Entry arrays.
58
+ 12. + Eodhd::Fundamentals::Financials::BalanceSheet
59
+ 13. + Eodhd::Fundamentals::Financials::IncomeStatement
60
+ 14. + Eodhd::Fundamentals::Financials::CashFlow
61
+ 15. ~ Eodhd::Fundamentals::Financials: Build BalanceSheet/IncomeStatement/CashFlow instead of the shared Statement.
62
+ 16. ~ Eodhd::Fundamentals::Financials::BalanceSheet#yearly, #quarterly: Now sorted arrays of typed entries, was raw hashes.
63
+ 17. ~ Eodhd::Fundamentals::Earnings#annual, #history, #trend: Now sorted arrays of typed entries, was raw hashes.
64
+ 18. ~ Eodhd::Fundamentals::Holders#institutions, #funds: Now arrays of typed entries, was raw hashes.
65
+ 19. ~ Eodhd::Fundamentals#insider_transactions: Now a sorted array of InsiderTransaction, was a raw hash.
66
+ 20. ~ Eodhd::Fundamentals#outstanding_shares: Now an OutstandingShares object, was a raw hash.
67
+ 21. + test/Eodhd/Fundamentals_test.rb: Cover the newly wrapped entry types and collections.
68
+ 22. - Eodhd::Fundamentals::Financials::Statement: Replaced by the three per-statement classes.
69
+ 23. ~ Eodhd/VERSION: /0.18.1/0.19.0/
70
+
71
+ 0.18.1: urlencode helper naming consistency
72
+
73
+ 1. /Thoran::String::UrlEncode#url_encode/Thoran::String::Urlencode#urlencode/ (url_encode kept as an alias).
74
+ 2. /Thoran::Hash::XWwwFormUrlEncode#x_www_form_url_encode/Thoran::Hash::XWwwFormUrlencode#x_www_form_urlencode/ (x_www_form_url_encode kept as an alias).
75
+ 3. ~ lib/Hash/x_www_form_urlencode.rb: Renamed urlencode require path.
76
+ 4. ~ eodhd.rb.gemspec: - spec.date (RubyGems stamps the build date); tidy spec.require_paths placement.
77
+ 5. ~ Eodhd/VERSION: /0.18.0/0.18.1/
78
+
79
+
80
+ ## 20260320
81
+
82
+ 0.18.0: + fundamentals endpoint
83
+
84
+ 1. + Eodhd::Fundamentals: Model for fundamental data with typed sub-objects.
85
+ 2. + Eodhd::Fundamentals::General
86
+ 3. + Eodhd::Fundamentals::Highlights
87
+ 4. + Eodhd::Fundamentals::Valuation
88
+ 5. + Eodhd::Fundamentals::SharesStats
89
+ 6. + Eodhd::Fundamentals::Technicals
90
+ 7. + Eodhd::Fundamentals::SplitsDividends
91
+ 8. + Eodhd::Fundamentals::AnalystRatings
92
+ 9. + Eodhd::Fundamentals::Holders
93
+ 10. + Eodhd::Fundamentals::ESGScores
94
+ 11. + Eodhd::Fundamentals::Earnings
95
+ 12. + Eodhd::Fundamentals::Financials
96
+ 13. + Eodhd::Fundamentals::Financials::Statement
97
+ 14. + Eodhd::Client#fundamentals: Fetch fundamental data for a symbol.
98
+ 15. + Eodhd#fundamentals: Facade method for fundamental data.
99
+ 16. + test/Eodhd/Fundamentals_test.rb
100
+ 17. ~ test/Eodhd/Client_test.rb: + tests for fundamentals endpoint.
101
+ 18. ~ test/Eodhd_test.rb: + test for fundamentals facade method.
102
+ 19. ~ test/helper.rb: + require 'minitest/mock' for minitest 6 compatibility.
103
+ 20. ~ Eodhd: Move initialize, web_socket, stream to private.
104
+ 21. ~ Eodhd: + safe navigation (&.) on exchange and exchange_symbol across all methods.
105
+ 22. ~ Eodhd#intraday: + exchange_symbol argument.
106
+ 23. ~ Eodhd#intraday: - default exchange_code of 'US'
107
+ 24. ~ test/: Reorganise test files into test/Eodhd/ subdirectory.
108
+ 25. - test/test_all.rb: Previously replaced by Rakefile.
109
+ 26. /CHANGELOG.txt/CHANGELOG/
110
+ 27. + LICENSE: MIT License.
111
+ 28. ~ README.md: Rewrite with intraday, fundamentals, and WebSocket documentation.
112
+
113
+
114
+ ## 20251005
115
+
116
+ 0.17.0: + intraday endpoint
117
+
118
+ 1. + Eodhd::Intraday: Model for intraday data.
119
+ 2. + Eodhd::Client#intraday: Fetch intraday data for US stocks.
120
+ 3. + Eodhd#intraday: Facade method for intraday data.
121
+ 4. + Eodhd::Validations#validate_interval: Validation of intraday intervals (1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w, 1mo).
122
+ 5. ~ Eodhd::Validations#validate_arguments: + interval argument.
123
+ 6. ~ test/validations_test.rb: + tests for validate_interval.
124
+ 7. ~ test/client_test.rb: + tests for intraday endpoint.
125
+ 8. ~ test/eodhd_test.rb: + tests for intraday facade method.
126
+
127
+ 0.16.0: + automated tests, + DefaultLogger, fix WebSocket bugs, improve validations
128
+
129
+ 1. + Eodhd::DefaultLogger: A shared logger for Client and WebSocketClient.
130
+ 2. ~ Eodhd::Client: Use DefaultLogger.
131
+ 3. ~ Eodhd::Client#initialize: + logger, use_default_logger arguments.
132
+ 4. + Eodhd::Client#use_logging?
133
+ 5. ~ Eodhd::Validations: Move validate_arguments() from Client to Validations.
134
+ 6. ~ Eodhd::Validations: Remove ! from all validation methods (validate_exchange_code, validate_symbol, etc).
135
+ 7. ~ Eodhd::WebSocketClient: Use DefaultLogger, add logger attribute.
136
+ 8. ~ Eodhd::WebSocketClient#initialize: + logger, use_default_logger arguments.
137
+ 9. + Eodhd::WebSocketClient#use_logging?: Check if logging is enabled.
138
+ 10. ~ Eodhd::WebSocketClient::Handler: Remove duplicate logger code.
139
+ 11. ~ Eodhd::WebSocketClient::Handler#initialize: + logger argument.
140
+ 12. ~ Eodhd::WebSocketClient::Handler#handle_response: Fix JSON parsing with proper StandardError rescue.
141
+ 13. ~ Eodhd::WebSocketClient::Handler#log_error: Use instance logger with use_logging? check.
142
+ 14. + test/validations_test.rb: Comprehensive validation tests with edge cases.
143
+ 15. + test/client_test.rb: REST client tests with VCR for all endpoints.
144
+ 16. + test/eodhd_test.rb: Facade delegation tests.
145
+ 17. + test/web_socket_client_test.rb: WebSocket client tests with stubbed Iodine.
146
+ 18. + test/helper.rb: VCR configuration with WebMock.
147
+ 19. ~ eodhd.gemspec: Add iodine as runtime dependency.
148
+ 20. ~ .gitignore: + Gemfile.lock.
149
+
150
+
151
+ ## 20250916
152
+
153
+ 0.15.0: Add validations, response logging, a version constant, and CHANGELOG.
154
+
155
+ 1. + Eodhd::Validations
156
+ 2. + Eodhd::Client#validate_arguments
157
+ 3. ~ Eodhd::Client#exchange_symbol_list: Use validate_arguments().
158
+ 4. ~ Eodhd::Client#eod_data: Use validate_arguments().
159
+ 5. ~ Eodhd::Client#eod_bulk_last_day: Use validate_arguments().
160
+ 6. + Eodhd::Client#log_response
161
+ 7. ~ Eodhd::Client#do_request: Call log_request() after construction of args.
162
+ 8. ~ Eodhd::Client#handle_response: Use log_response().
163
+ 9. + Eodhd::VERSION
164
+ 10. ~ eodhd.gemspec: Use Eodhd::VERSION and update the date.
165
+ 11. + CHANGELOG.txt
166
+
167
+
168
+ ## 20250326
169
+
170
+ 0.14.2: Remove reference to currently unused library UnixTime.
171
+
172
+ 1. ~ Eodhd::WebSocketClient: - require 'UnixTime'
173
+
174
+
175
+ ## 20250323
176
+
177
+ 0.14.1: Some fixes for the WebSocketClient interface.
178
+
179
+ 1. ~ Eodhd::WebSocketClient: + attr_accessor :consumer
180
+ 2. ~ Eodhd::WebSocketClient: /attr_accessor :symbols/attr_reader :symbols/
181
+ 3. + Eodhd::WebSocketClient#symbols=
182
+
183
+
184
+ ## 20250321
185
+
186
+ 0.14.0: Add WebSockets.
187
+
188
+ 1. + Eodhd::WebSocketClient
189
+ 2. + Eodhd#web_socket
190
+ 3. + Eodhd#stream
191
+ 4. + Eodhd#us_quote_stream
192
+ 5. + Eodhd#us_trade_stream
193
+ 6. + Eodhd#forex_stream
194
+ 7. + Eodhd#crypto_stream
195
+ 8. ~ Eodhd::Error: lib/Eodhd/Client.rb --> lib/Eodhd/Error.rb
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec name: 'eodhd.rb'
data/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # eodhd.rb
2
+
3
+ ## Description
4
+
5
+ Access the eodhd.com API with Ruby.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'eodhd.rb'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install eodhd.rb
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Setup
30
+
31
+ ```ruby
32
+ api_token = 'api_token'
33
+ eodhd = Eodhd.new(api_token: api_token)
34
+ ```
35
+
36
+ ### List of Exchanges
37
+
38
+ ```ruby
39
+ eodhd.exchanges
40
+ ```
41
+
42
+ ### List of Exchange Symbols
43
+
44
+ ```ruby
45
+ exchange = eodhd.exchanges.first
46
+
47
+ eodhd.exchange_symbols(exchange: exchange)
48
+ # OR
49
+ eodhd.exchange_symbols(exchange_code: exchange.code)
50
+ ```
51
+
52
+ ### Retrieve EOD Data For One Symbol
53
+
54
+ ```ruby
55
+ exchange = eodhd.exchanges.first
56
+ exchange_symbol = eodhd.exchange_symbols(exchange: exchange).first
57
+
58
+ eodhd.eod_data(exchange: exchange, exchange_symbol: exchange_symbol)
59
+ # OR
60
+ eodhd.eod_data(exchange_code: exchange.code, exchange_symbol: exchange_symbol)
61
+ # OR
62
+ eodhd.eod_data(exchange: exchange, symbol: exchange_symbol.code)
63
+ # OR
64
+ eodhd.eod_data(exchange_code: exchange.code, symbol: exchange_symbol.code)
65
+ ```
66
+
67
+ ### Retrieve EOD Data For Multiple Symbols For One Date
68
+
69
+ ```ruby
70
+ exchange = eodhd.exchanges.first
71
+
72
+ eodhd.eod_bulk_last_day(exchange: exchange, date: Date.today)
73
+ # OR
74
+ eodhd.eod_bulk_last_day(exchange_code: exchange.code, date: Date.today)
75
+ ```
76
+
77
+ ### Retrieve Intraday Data
78
+
79
+ ```ruby
80
+ eodhd.intraday(symbol: 'AAPL', exchange_code: 'US', interval: '5m')
81
+ # OR with date range (from and to are Unix timestamps)
82
+ eodhd.intraday(symbol: 'AAPL', exchange_code: 'US', interval: '1h', from: 1696118400, to: 1696550400)
83
+ ```
84
+
85
+ ### Retrieve Fundamental Data
86
+
87
+ ```ruby
88
+ fd = eodhd.fundamentals(exchange_code: 'US', symbol: 'AAPL')
89
+
90
+ fd.general
91
+ fd.general.name
92
+ fd.general.sector
93
+ fd.general.industry
94
+
95
+ fd.highlights
96
+ fd.highlights.market_capitalization
97
+ fd.highlights.pe_ratio
98
+ fd.highlights.dividend_yield
99
+ fd.highlights.earnings_share
100
+
101
+ fd.valuation
102
+ fd.valuation.trailing_pe
103
+ fd.valuation.forward_pe
104
+ fd.valuation.price_book_mrq
105
+
106
+ fd.shares_stats
107
+ fd.shares_stats.shares_outstanding
108
+ fd.shares_stats.shares_float
109
+
110
+ fd.financials
111
+ fd.financials.income_statement
112
+ fd.financials.income_statement.quarterly
113
+ fd.financials.income_statement.yearly
114
+ fd.financials.balance_sheet.quarterly
115
+ fd.financials.cash_flow.quarterly
116
+
117
+ fd.earnings
118
+ fd.earnings.history
119
+ fd.earnings.trend
120
+ fd.earnings.annual
121
+ ```
122
+
123
+ #### Use the filter parameter to request only specific sections
124
+
125
+ ```ruby
126
+ eodhd.fundamentals(exchange_code: 'US', symbol: 'AAPL', filter: 'General')
127
+ eodhd.fundamentals(exchange_code: 'US', symbol: 'AAPL', filter: 'Financials::Income_Statement::quarterly')
128
+ ```
129
+
130
+ ### WebSockets
131
+
132
+ #### Streaming requires a callable consumer (a proc, lambda, or any object responding to `call`) to handle incoming data frames
133
+
134
+ ```ruby
135
+ consumer = ->(data){puts data}
136
+ eodhd = Eodhd.new(api_token: api_token, consumer: consumer)
137
+
138
+ eodhd.us_trade_stream('AAPL,MSFT')
139
+ eodhd.us_quote_stream('AAPL,MSFT')
140
+ eodhd.forex_stream('EURUSD')
141
+ eodhd.crypto_stream('BTC-USD')
142
+ ```
143
+
144
+ #### Or use the general interface
145
+
146
+ ```ruby
147
+ eodhd.stream(asset_class: 'us', symbols: 'AAPL,MSFT')
148
+ ```
149
+
150
+ ## License
151
+
152
+ MIT License. See LICENSE file for details.
153
+
154
+ ## Contributing
155
+
156
+ 1. Fork it ( https://github.com/thoran/eodhd.rb/fork )
157
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
158
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
159
+ 4. Push to the branch (`git push origin my-new-feature`)
160
+ 5. Create a new pull request
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.libs << "lib"
5
+ t.test_files = FileList["test/**/*_test.rb"]
6
+ t.warning = true
7
+ end
8
+
9
+ task default: :test
10
+
11
+ namespace :cassettes do
12
+ CASSETTE_DIR = File.expand_path("test/fixtures/vcr_cassettes", __dir__)
13
+
14
+ desc "Delete all VCR cassettes"
15
+ task :clear do
16
+ if Dir.exist?(CASSETTE_DIR)
17
+ FileUtils.rm_rf(Dir[File.join(CASSETTE_DIR, "**", "*")])
18
+ puts "Cleared cassettes in #{CASSETTE_DIR}"
19
+ else
20
+ puts "Cassette dir not found: #{CASSETTE_DIR}"
21
+ end
22
+ end
23
+
24
+ desc "Show cassette directory path"
25
+ task :path do
26
+ puts CASSETTE_DIR
27
+ end
28
+ end
data/eodhd.gemspec ADDED
@@ -0,0 +1,52 @@
1
+ require_relative './lib/Eodhd/VERSION'
2
+
3
+ class Gem::Specification
4
+ def dependencies=(gems)
5
+ gems.each{|gem| add_dependency(*gem)}
6
+ end
7
+
8
+ def development_dependencies=(gems)
9
+ gems.each{|gem| add_development_dependency(*gem)}
10
+ end
11
+ end
12
+
13
+ Gem::Specification.new do |spec|
14
+ spec.name = 'eodhd'
15
+ spec.version = Eodhd::VERSION
16
+
17
+ spec.summary = "Access the eodhd.com API with Ruby."
18
+ spec.description = "Access the eodhd.com API with Ruby."
19
+
20
+ spec.author = 'thoran'
21
+ spec.email = 'code@thoran.com'
22
+ spec.homepage = 'http://github.com/thoran/eodhd.rb'
23
+ spec.license = 'MIT'
24
+
25
+ spec.require_paths = ['lib']
26
+ spec.required_ruby_version = '>= 2.7'
27
+
28
+ spec.files = [
29
+ 'CHANGELOG',
30
+ 'eodhd.gemspec',
31
+ 'Gemfile',
32
+ Dir['lib/**/*.rb'],
33
+ 'Rakefile',
34
+ 'README.md',
35
+ Dir['test/**/*.rb']
36
+ ].flatten
37
+
38
+ spec.dependencies = %w{
39
+ http.rb
40
+ iodine
41
+ }
42
+
43
+ spec.development_dependencies = %w{
44
+ rake
45
+ minitest
46
+ minitest-mock
47
+ minitest-spec-context
48
+ webmock
49
+ vcr
50
+ simplecov
51
+ }
52
+ end
@@ -0,0 +1,143 @@
1
+ # Eodhd/Client.rb
2
+ # Eodhd::Client
3
+
4
+ gem 'http.rb'
5
+
6
+ require 'http.rb'
7
+ require 'json'
8
+
9
+ require_relative './DefaultLogger'
10
+ require_relative './Error'
11
+ require_relative '../Hash/x_www_form_urlencode'
12
+ require_relative './Validations'
13
+
14
+ class Eodhd
15
+ class Client
16
+
17
+ API_HOST = 'eodhd.com'
18
+
19
+ class << self
20
+ def path_prefix
21
+ '/api'
22
+ end
23
+ end # class << self
24
+
25
+ include Validations
26
+
27
+ # This endpoint always returns json regardless of what fmt is specified.
28
+ def exchanges_list
29
+ response = get(path: '/exchanges-list')
30
+ handle_response(response)
31
+ end
32
+
33
+ def exchange_symbol_list(exchange_code:)
34
+ validate_arguments(exchange_code: exchange_code)
35
+ response = get(path: "/exchange-symbol-list/#{exchange_code}")
36
+ handle_response(response)
37
+ end
38
+
39
+ def eod_data(exchange_id:, symbol:, period:, from: nil, to: nil)
40
+ validate_arguments(
41
+ exchange_id: exchange_id,
42
+ symbol: symbol,
43
+ period: period,
44
+ from: from,
45
+ to: to
46
+ )
47
+ args = {period: period, from: from, to: to}.compact
48
+ response = get(path: "/eod/#{symbol}.#{exchange_id}", args: args)
49
+ handle_response(response)
50
+ end
51
+
52
+ def eod_bulk_last_day(exchange_id:, date:)
53
+ validate_arguments(exchange_id: exchange_id, date: date)
54
+ args = {date: date}
55
+ response = get(path: "/eod-bulk-last-day/#{exchange_id}", args: args)
56
+ handle_response(response)
57
+ end
58
+
59
+ def intraday(exchange_code:, symbol:, interval:, from: nil, to: nil)
60
+ validate_arguments(
61
+ exchange_code: exchange_code,
62
+ symbol: symbol,
63
+ interval: interval,
64
+ from: from,
65
+ to: to
66
+ )
67
+ args = {interval: interval, from: from, to: to}.compact
68
+ response = get(path: "/intraday/#{symbol}.#{exchange_code}", args: args)
69
+ handle_response(response)
70
+ end
71
+
72
+ def fundamentals(exchange_code:, symbol:, filter: nil)
73
+ validate_arguments(exchange_code: exchange_code, symbol: symbol)
74
+ args = {filter: filter}.compact
75
+ response = get(path: "/fundamentals/#{symbol}.#{exchange_code}", args: args)
76
+ handle_response(response)
77
+ end
78
+
79
+ attr_accessor\
80
+ :api_token,
81
+ :logger
82
+
83
+ private
84
+
85
+ def initialize(api_token: nil, logger: nil, use_default_logger: false)
86
+ @api_token = api_token
87
+ @logger = use_default_logger ? DefaultLogger.logger : logger
88
+ end
89
+
90
+ def request_string(path)
91
+ "https://#{API_HOST}#{self.class.path_prefix}#{path}"
92
+ end
93
+
94
+ def log_args?(args)
95
+ !args.values.all?(&:nil?)
96
+ end
97
+
98
+ def log_request(verb:, request_string:, args:)
99
+ log_string = "#{verb} #{request_string}"
100
+ if log_args?(args)
101
+ log_string << "?#{args.x_www_form_urlencode}"
102
+ end
103
+ @logger.info(log_string)
104
+ end
105
+
106
+ def log_response(code:, message:, body:)
107
+ log_string = "#{code}\n#{message}\n#{body}"
108
+ @logger.info(log_string)
109
+ end
110
+
111
+ def log_error(code:, message:, body:)
112
+ log_string = "#{code}\n#{message}\n#{body}"
113
+ @logger.error(log_string)
114
+ end
115
+
116
+ def do_request(verb:, path:, args: {})
117
+ api_token = args[:api_token] || @api_token
118
+ fmt = args[:fmt] || 'json'
119
+ args.merge!(api_token: api_token, fmt: fmt)
120
+ log_request(verb: verb, request_string: request_string(path), args: args) if use_logging?
121
+ HTTP.send(verb.to_s.downcase, request_string(path), args)
122
+ end
123
+
124
+ def get(path:, args: {})
125
+ do_request(verb: 'GET', path: path, args: args)
126
+ end
127
+
128
+ def handle_response(response)
129
+ if response.success?
130
+ parsed_body = JSON.parse(response.body)
131
+ log_response(code: response.code, message: response.message, body: response.body) if use_logging?
132
+ parsed_body
133
+ else
134
+ log_error(code: response.code, message: response.message, body: response.body) if use_logging?
135
+ raise Eodhd::Error.new(code: response.code, message: response.message, body: response.body)
136
+ end
137
+ end
138
+
139
+ def use_logging?
140
+ !@logger.nil?
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,30 @@
1
+ # Eodhd/DefaultLogger.rb
2
+ # Eodhd::DefaultLogger
3
+
4
+ require 'fileutils'
5
+ require 'logger'
6
+
7
+ class Eodhd
8
+ class DefaultLogger
9
+ class << self
10
+ attr_writer :log_filepath
11
+
12
+ def default_log_filepath
13
+ File.join(%w{~ log eodhd log.txt})
14
+ end
15
+
16
+ def log_filepath
17
+ File.expand_path(@log_filepath || default_log_filepath)
18
+ end
19
+
20
+ def log_file
21
+ FileUtils.mkdir_p(File.dirname(log_filepath))
22
+ File.open(log_filepath, File::WRONLY | File::APPEND | File::CREAT)
23
+ end
24
+
25
+ def logger
26
+ @logger ||= Logger.new(log_file, 'daily')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ # Eodhd/EodBulkLastDay.rb
2
+ # Eodhd::EodBulkLastDay
3
+
4
+ class Eodhd
5
+ class EodBulkLastDay
6
+ class << self
7
+ def all(client: nil, api_token: nil, exchange_code:, date:)
8
+ load(client: client, api_token: api_token, exchange_code: exchange_code, date:)
9
+ end
10
+
11
+ private
12
+
13
+ def load(client: nil, api_token: nil, exchange_code:, date:)
14
+ client ||= Client.new(api_token: api_token)
15
+ client.eod_bulk_last_day(exchange_id: exchange_code, date: date).collect do |eod_bulk_last_day|
16
+ self.new(
17
+ code: eod_bulk_last_day['code'],
18
+ exchange_short_name: eod_bulk_last_day['exchange_short_name'],
19
+ date: eod_bulk_last_day['date'],
20
+ open: eod_bulk_last_day['open'],
21
+ high: eod_bulk_last_day['high'],
22
+ low: eod_bulk_last_day['low'],
23
+ close: eod_bulk_last_day['close'],
24
+ adjusted_close: eod_bulk_last_day['adjusted_close'],
25
+ volume: eod_bulk_last_day['volume']
26
+ )
27
+ end
28
+ end
29
+ end # class << self
30
+
31
+ attr_reader\
32
+ :code,
33
+ :exchange_short_name,
34
+ :date,
35
+ :open,
36
+ :high,
37
+ :low,
38
+ :close,
39
+ :adjusted_close,
40
+ :volume
41
+
42
+ private
43
+
44
+ def initialize(code:, exchange_short_name:, date:, open:, high:, low:, close:, adjusted_close:, volume:)
45
+ @code = code
46
+ @exchange_short_name = exchange_short_name
47
+ @date = date
48
+ @open = open
49
+ @high = high
50
+ @low = low
51
+ @close = close
52
+ @adjusted_close = adjusted_close
53
+ @volume = volume
54
+ end
55
+ end
56
+ end