eodhd.rb 0.17.0 → 0.18.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 +4 -4
- data/CHANGELOG +101 -0
- data/README.md +86 -14
- data/Rakefile +28 -0
- data/eodhd.rb.gemspec +30 -15
- data/lib/Eodhd/Client.rb +7 -0
- data/lib/Eodhd/EodBulkLastDay.rb +2 -0
- data/lib/Eodhd/EodData.rb +2 -0
- data/lib/Eodhd/Exchange.rb +2 -0
- data/lib/Eodhd/ExchangeSymbol.rb +2 -0
- data/lib/Eodhd/Fundamentals/AnalystRatings.rb +29 -0
- data/lib/Eodhd/Fundamentals/ESGScores.rb +37 -0
- data/lib/Eodhd/Fundamentals/Earnings.rb +21 -0
- data/lib/Eodhd/Fundamentals/Financials/Statement.rb +21 -0
- data/lib/Eodhd/Fundamentals/Financials.rb +23 -0
- data/lib/Eodhd/Fundamentals/General.rb +85 -0
- data/lib/Eodhd/Fundamentals/Highlights.rb +63 -0
- data/lib/Eodhd/Fundamentals/Holders.rb +19 -0
- data/lib/Eodhd/Fundamentals/SharesStats.rb +33 -0
- data/lib/Eodhd/Fundamentals/SplitsDividends.rb +31 -0
- data/lib/Eodhd/Fundamentals/Technicals.rb +33 -0
- data/lib/Eodhd/Fundamentals/Valuation.rb +29 -0
- data/lib/Eodhd/Fundamentals.rb +85 -0
- data/lib/Eodhd/VERSION.rb +1 -1
- data/lib/Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb +14 -13
- data/lib/Thoran/String/Urlencode/urlencode.rb +29 -0
- data/lib/{Eodhd.rb → eodhd.rb} +27 -17
- data/test/{client_test.rb → Eodhd/Client_test.rb} +38 -14
- data/test/Eodhd/Fundamentals_test.rb +78 -0
- data/test/{validations_test.rb → Eodhd/Validations_test.rb} +4 -2
- data/test/{web_socket_client_test.rb → Eodhd/WebSocketClient_test.rb} +9 -7
- data/test/{eodhd_test.rb → Eodhd_test.rb} +12 -1
- data/test/helper.rb +3 -0
- metadata +41 -12
- data/lib/Thoran/String/UrlEncode/url_encode.rb +0 -26
- data/test/test_all.rb +0 -5
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Eodhd/Fundamentals/Holders.rb
|
|
2
|
+
# Eodhd::Fundamentals::Holders
|
|
3
|
+
|
|
4
|
+
class Eodhd
|
|
5
|
+
class Fundamentals
|
|
6
|
+
class Holders
|
|
7
|
+
attr_reader\
|
|
8
|
+
:institutions,
|
|
9
|
+
:funds
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def initialize(data)
|
|
14
|
+
@institutions = data['Institutions']
|
|
15
|
+
@funds = data['Funds']
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Eodhd/Fundamentals/SharesStats.rb
|
|
2
|
+
# Eodhd::Fundamentals::SharesStats
|
|
3
|
+
|
|
4
|
+
class Eodhd
|
|
5
|
+
class Fundamentals
|
|
6
|
+
class SharesStats
|
|
7
|
+
attr_reader\
|
|
8
|
+
:shares_outstanding,
|
|
9
|
+
:shares_float,
|
|
10
|
+
:percent_insiders,
|
|
11
|
+
:percent_institutions,
|
|
12
|
+
:shares_short,
|
|
13
|
+
:shares_short_prior_month,
|
|
14
|
+
:short_ratio,
|
|
15
|
+
:short_percent_outstanding,
|
|
16
|
+
:short_percent_float
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def initialize(data)
|
|
21
|
+
@shares_outstanding = data['SharesOutstanding']
|
|
22
|
+
@shares_float = data['SharesFloat']
|
|
23
|
+
@percent_insiders = data['PercentInsiders']
|
|
24
|
+
@percent_institutions = data['PercentInstitutions']
|
|
25
|
+
@shares_short = data['SharesShort']
|
|
26
|
+
@shares_short_prior_month = data['SharesShortPriorMonth']
|
|
27
|
+
@short_ratio = data['ShortRatio']
|
|
28
|
+
@short_percent_outstanding = data['ShortPercentOutstanding']
|
|
29
|
+
@short_percent_float = data['ShortPercentFloat']
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Eodhd/Fundamentals/SplitsDividends.rb
|
|
2
|
+
# Eodhd::Fundamentals::SplitsDividends
|
|
3
|
+
|
|
4
|
+
class Eodhd
|
|
5
|
+
class Fundamentals
|
|
6
|
+
class SplitsDividends
|
|
7
|
+
attr_reader\
|
|
8
|
+
:forward_annual_dividend_rate,
|
|
9
|
+
:forward_annual_dividend_yield,
|
|
10
|
+
:payout_ratio,
|
|
11
|
+
:dividend_date,
|
|
12
|
+
:ex_dividend_date,
|
|
13
|
+
:last_split_factor,
|
|
14
|
+
:last_split_date,
|
|
15
|
+
:number_dividends_by_year
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def initialize(data)
|
|
20
|
+
@forward_annual_dividend_rate = data['ForwardAnnualDividendRate']
|
|
21
|
+
@forward_annual_dividend_yield = data['ForwardAnnualDividendYield']
|
|
22
|
+
@payout_ratio = data['PayoutRatio']
|
|
23
|
+
@dividend_date = data['DividendDate']
|
|
24
|
+
@ex_dividend_date = data['ExDividendDate']
|
|
25
|
+
@last_split_factor = data['LastSplitFactor']
|
|
26
|
+
@last_split_date = data['LastSplitDate']
|
|
27
|
+
@number_dividends_by_year = data['NumberDividendsByYear']
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Eodhd/Fundamentals/Technicals.rb
|
|
2
|
+
# Eodhd::Fundamentals::Technicals
|
|
3
|
+
|
|
4
|
+
class Eodhd
|
|
5
|
+
class Fundamentals
|
|
6
|
+
class Technicals
|
|
7
|
+
attr_reader\
|
|
8
|
+
:beta,
|
|
9
|
+
:fifty_two_week_high,
|
|
10
|
+
:fifty_two_week_low,
|
|
11
|
+
:fifty_day_ma,
|
|
12
|
+
:two_hundred_day_ma,
|
|
13
|
+
:shares_short,
|
|
14
|
+
:shares_short_prior_month,
|
|
15
|
+
:short_ratio,
|
|
16
|
+
:short_percent
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def initialize(data)
|
|
21
|
+
@beta = data['Beta']
|
|
22
|
+
@fifty_two_week_high = data['52WeekHigh']
|
|
23
|
+
@fifty_two_week_low = data['52WeekLow']
|
|
24
|
+
@fifty_day_ma = data['50DayMA']
|
|
25
|
+
@two_hundred_day_ma = data['200DayMA']
|
|
26
|
+
@shares_short = data['SharesShort']
|
|
27
|
+
@shares_short_prior_month = data['SharesShortPriorMonth']
|
|
28
|
+
@short_ratio = data['ShortRatio']
|
|
29
|
+
@short_percent = data['ShortPercent']
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Eodhd/Fundamentals/Valuation.rb
|
|
2
|
+
# Eodhd::Fundamentals::Valuation
|
|
3
|
+
|
|
4
|
+
class Eodhd
|
|
5
|
+
class Fundamentals
|
|
6
|
+
class Valuation
|
|
7
|
+
attr_reader\
|
|
8
|
+
:trailing_pe,
|
|
9
|
+
:forward_pe,
|
|
10
|
+
:price_sales_ttm,
|
|
11
|
+
:price_book_mrq,
|
|
12
|
+
:enterprise_value,
|
|
13
|
+
:enterprise_value_revenue,
|
|
14
|
+
:enterprise_value_ebitda
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def initialize(data)
|
|
19
|
+
@trailing_pe = data['TrailingPE']
|
|
20
|
+
@forward_pe = data['ForwardPE']
|
|
21
|
+
@price_sales_ttm = data['PriceSalesTTM']
|
|
22
|
+
@price_book_mrq = data['PriceBookMRQ']
|
|
23
|
+
@enterprise_value = data['EnterpriseValue']
|
|
24
|
+
@enterprise_value_revenue = data['EnterpriseValueRevenue']
|
|
25
|
+
@enterprise_value_ebitda = data['EnterpriseValueEbitda']
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Eodhd/Fundamentals.rb
|
|
2
|
+
# Eodhd::Fundamentals
|
|
3
|
+
|
|
4
|
+
require_relative './Fundamentals/AnalystRatings'
|
|
5
|
+
require_relative './Fundamentals/Earnings'
|
|
6
|
+
require_relative './Fundamentals/ESGScores'
|
|
7
|
+
require_relative './Fundamentals/Financials'
|
|
8
|
+
require_relative './Fundamentals/General'
|
|
9
|
+
require_relative './Fundamentals/Highlights'
|
|
10
|
+
require_relative './Fundamentals/Holders'
|
|
11
|
+
require_relative './Fundamentals/SharesStats'
|
|
12
|
+
require_relative './Fundamentals/SplitsDividends'
|
|
13
|
+
require_relative './Fundamentals/Technicals'
|
|
14
|
+
require_relative './Fundamentals/Valuation'
|
|
15
|
+
|
|
16
|
+
class Eodhd
|
|
17
|
+
class Fundamentals
|
|
18
|
+
class << self
|
|
19
|
+
def all(client: nil, api_token: nil, exchange_code:, symbol:, filter: nil)
|
|
20
|
+
load(client: client, api_token: api_token, exchange_code: exchange_code, symbol: symbol, filter: filter)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def load(client: nil, api_token: nil, exchange_code:, symbol:, filter:)
|
|
26
|
+
client ||= Client.new(api_token: api_token)
|
|
27
|
+
response = client.fundamentals(exchange_code: exchange_code, symbol: symbol, filter: filter)
|
|
28
|
+
self.new(
|
|
29
|
+
exchange_code: exchange_code,
|
|
30
|
+
symbol: symbol,
|
|
31
|
+
general: response['General'],
|
|
32
|
+
highlights: response['Highlights'],
|
|
33
|
+
valuation: response['Valuation'],
|
|
34
|
+
shares_stats: response['SharesStats'],
|
|
35
|
+
technicals: response['Technicals'],
|
|
36
|
+
splits_dividends: response['SplitsDividends'],
|
|
37
|
+
analyst_ratings: response['AnalystRatings'],
|
|
38
|
+
holders: response['Holders'],
|
|
39
|
+
insider_transactions: response['InsiderTransactions'],
|
|
40
|
+
esg_scores: response['ESGScores'],
|
|
41
|
+
outstanding_shares: response['outstandingShares'],
|
|
42
|
+
earnings: response['Earnings'],
|
|
43
|
+
financials: response['Financials']
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end # class << self
|
|
47
|
+
|
|
48
|
+
attr_reader\
|
|
49
|
+
:exchange_code,
|
|
50
|
+
:symbol,
|
|
51
|
+
:general,
|
|
52
|
+
:highlights,
|
|
53
|
+
:valuation,
|
|
54
|
+
:shares_stats,
|
|
55
|
+
:technicals,
|
|
56
|
+
:splits_dividends,
|
|
57
|
+
:analyst_ratings,
|
|
58
|
+
:holders,
|
|
59
|
+
:insider_transactions,
|
|
60
|
+
:esg_scores,
|
|
61
|
+
:outstanding_shares,
|
|
62
|
+
:earnings,
|
|
63
|
+
:financials
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def initialize(exchange_code:, symbol:, general:, highlights:, valuation:, shares_stats:, technicals:, splits_dividends:, analyst_ratings:, holders:, insider_transactions:, esg_scores:, outstanding_shares:, earnings:, financials:)
|
|
68
|
+
@exchange_code = exchange_code
|
|
69
|
+
@symbol = symbol
|
|
70
|
+
@general = General.new(general) if general
|
|
71
|
+
@highlights = Highlights.new(highlights) if highlights
|
|
72
|
+
@valuation = Valuation.new(valuation) if valuation
|
|
73
|
+
@shares_stats = SharesStats.new(shares_stats) if shares_stats
|
|
74
|
+
@technicals = Technicals.new(technicals) if technicals
|
|
75
|
+
@splits_dividends = SplitsDividends.new(splits_dividends) if splits_dividends
|
|
76
|
+
@analyst_ratings = AnalystRatings.new(analyst_ratings) if analyst_ratings
|
|
77
|
+
@holders = Holders.new(holders) if holders
|
|
78
|
+
@insider_transactions = insider_transactions
|
|
79
|
+
@esg_scores = ESGScores.new(esg_scores) if esg_scores
|
|
80
|
+
@outstanding_shares = outstanding_shares
|
|
81
|
+
@earnings = Earnings.new(earnings) if earnings
|
|
82
|
+
@financials = Financials.new(financials) if financials
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/Eodhd/VERSION.rb
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
# Thoran/Hash/
|
|
2
|
-
# Thoran::Hash::
|
|
1
|
+
# Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb
|
|
2
|
+
# Thoran::Hash::XWwwFormUrlencode#x_www_form_urlencode
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
# 0.
|
|
4
|
+
# 20260713
|
|
5
|
+
# 0.3.0
|
|
6
6
|
|
|
7
|
-
# Changes since 0.
|
|
8
|
-
# -/0: (The class name and the snake case name are consistent now.)
|
|
9
|
-
# 1. /
|
|
7
|
+
# Changes since 0.2:
|
|
8
|
+
# -/0: (The class name and the snake case name really are consistent now.)
|
|
9
|
+
# 1. /XWwwFormUrlEncode/XWwwFormUrlencode/
|
|
10
|
+
# 2. /url_encode/urlencode/
|
|
10
11
|
|
|
11
|
-
require 'Thoran/String/
|
|
12
|
+
require 'Thoran/String/Urlencode/urlencode'
|
|
12
13
|
|
|
13
14
|
module Thoran
|
|
14
15
|
module Hash
|
|
15
|
-
module
|
|
16
|
+
module XWwwFormUrlencode
|
|
16
17
|
|
|
17
|
-
def
|
|
18
|
-
inject([]){|a,e| a << "#{e.first.to_s.
|
|
18
|
+
def x_www_form_urlencode(joiner = '&')
|
|
19
|
+
inject([]){|a,e| a << "#{e.first.to_s.urlencode}=#{e.last.to_s.urlencode}" unless e.last.nil?; a}.join(joiner)
|
|
19
20
|
end
|
|
20
|
-
alias_method :
|
|
21
|
+
alias_method :x_www_form_url_encode, :x_www_form_urlencode
|
|
21
22
|
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
Hash.send(:include, Thoran::Hash::
|
|
27
|
+
Hash.send(:include, Thoran::Hash::XWwwFormUrlencode)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Thoran/String/Urlencode/urlencode.rb
|
|
2
|
+
# Thoran::String::Urlencode#urlencode
|
|
3
|
+
|
|
4
|
+
# 20260713
|
|
5
|
+
# 0.4.0
|
|
6
|
+
|
|
7
|
+
# Acknowledgements: I've simply ripped off and refashioned the code from the CGI module!...
|
|
8
|
+
|
|
9
|
+
# Changes since 0.3:
|
|
10
|
+
# -/0: More consistent naming.
|
|
11
|
+
# 1. /UrlEncode/Urlencode/
|
|
12
|
+
# 2. /url_encode/urlencode/
|
|
13
|
+
|
|
14
|
+
module Thoran
|
|
15
|
+
module String
|
|
16
|
+
module Urlencode
|
|
17
|
+
|
|
18
|
+
def urlencode
|
|
19
|
+
self.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
|
|
20
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
|
21
|
+
end.tr(' ', '+')
|
|
22
|
+
end
|
|
23
|
+
alias_method :url_encode, :urlencode
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
String.send(:include, Thoran::String::Urlencode)
|
data/lib/{Eodhd.rb → eodhd.rb}
RENAMED
|
@@ -7,45 +7,40 @@ require_relative './Eodhd/EodData'
|
|
|
7
7
|
require_relative './Eodhd/Exchange'
|
|
8
8
|
require_relative './Eodhd/ExchangeSymbol'
|
|
9
9
|
require_relative './Eodhd/Intraday'
|
|
10
|
+
require_relative './Eodhd/Fundamentals'
|
|
10
11
|
require_relative './Eodhd/WebSocketClient'
|
|
11
12
|
|
|
12
13
|
class Eodhd
|
|
13
|
-
def initialize(api_token:, consumer: nil)
|
|
14
|
-
@api_token = api_token
|
|
15
|
-
@consumer = consumer
|
|
16
|
-
end
|
|
17
|
-
|
|
18
14
|
def exchanges
|
|
19
15
|
Eodhd::Exchange.all(api_token: @api_token)
|
|
20
16
|
end
|
|
21
17
|
|
|
22
18
|
def exchange_symbols(exchange: nil, exchange_code: nil)
|
|
23
|
-
exchange_code ||= exchange
|
|
19
|
+
exchange_code ||= exchange&.code
|
|
24
20
|
Eodhd::ExchangeSymbol.all(api_token: @api_token, exchange_code: exchange_code)
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
def eod_data(exchange: nil, exchange_code: nil, exchange_symbol: nil, symbol: nil, period: nil, from: nil, to: nil)
|
|
28
|
-
exchange_code ||= exchange
|
|
29
|
-
symbol ||= exchange_symbol
|
|
24
|
+
exchange_code ||= exchange&.code
|
|
25
|
+
symbol ||= exchange_symbol&.code
|
|
30
26
|
Eodhd::EodData.all(api_token: @api_token, exchange_code: exchange_code, symbol: symbol, period: period, from: from, to: to)
|
|
31
27
|
end
|
|
32
28
|
|
|
33
29
|
def eod_bulk_last_day(exchange: nil, exchange_code: nil, date:)
|
|
34
|
-
exchange_code ||= exchange
|
|
30
|
+
exchange_code ||= exchange&.code
|
|
35
31
|
Eodhd::EodBulkLastDay.all(api_token: @api_token, exchange_code: exchange_code, date: date)
|
|
36
32
|
end
|
|
37
33
|
|
|
38
|
-
def intraday(exchange: nil, exchange_code: nil, symbol
|
|
39
|
-
exchange_code ||= exchange&.code
|
|
34
|
+
def intraday(exchange: nil, exchange_code: nil, exchange_symbol: nil, symbol: nil, interval:, from: nil, to: nil)
|
|
35
|
+
exchange_code ||= exchange&.code
|
|
36
|
+
symbol ||= exchange_symbol&.code
|
|
40
37
|
Eodhd::Intraday.all(api_token: @api_token, exchange_code: exchange_code, symbol: symbol, interval: interval, from: from, to: to)
|
|
41
38
|
end
|
|
42
39
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def stream(asset_class:, symbols:)
|
|
48
|
-
web_socket(asset_class: asset_class, symbols: symbols).run
|
|
40
|
+
def fundamentals(exchange: nil, exchange_code: nil, exchange_symbol: nil, symbol: nil, filter: nil)
|
|
41
|
+
exchange_code ||= exchange&.code
|
|
42
|
+
symbol ||= exchange_symbol&.code
|
|
43
|
+
Eodhd::Fundamentals.all(api_token: @api_token, exchange_code: exchange_code, symbol: symbol, filter: filter)
|
|
49
44
|
end
|
|
50
45
|
|
|
51
46
|
def us_trade_stream(symbols)
|
|
@@ -64,4 +59,19 @@ class Eodhd
|
|
|
64
59
|
def crypto_stream(symbols)
|
|
65
60
|
stream(asset_class: 'crypto', symbols: symbols)
|
|
66
61
|
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def initialize(api_token:, consumer: nil)
|
|
66
|
+
@api_token = api_token
|
|
67
|
+
@consumer = consumer
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def web_socket(asset_class:, symbols:)
|
|
71
|
+
Eodhd::WebSocketClient.new(api_token: @api_token, asset_class: asset_class, symbols: symbols, consumer: @consumer)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def stream(asset_class:, symbols:)
|
|
75
|
+
web_socket(asset_class: asset_class, symbols: symbols).run
|
|
76
|
+
end
|
|
67
77
|
end
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
# Eodhd/Client_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
2
4
|
|
|
3
5
|
describe Eodhd::Client do
|
|
4
6
|
let(:api_token){ENV.fetch('EODHD_API_TOKEN', '<API_TOKEN>')}
|
|
5
|
-
|
|
6
7
|
let(:client){Eodhd::Client.new(api_token: api_token)}
|
|
7
8
|
|
|
8
9
|
describe "#exchanges_list" do
|
|
9
|
-
it "
|
|
10
|
+
it "returns parsed JSON array" do
|
|
10
11
|
VCR.use_cassette("client_exchanges_list") do
|
|
11
12
|
result = client.exchanges_list
|
|
12
13
|
_(result).must_be_kind_of(Array)
|
|
@@ -22,7 +23,7 @@ describe Eodhd::Client do
|
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
describe "#exchange_symbol_list" do
|
|
25
|
-
it "
|
|
26
|
+
it "returns parsed JSON array" do
|
|
26
27
|
VCR.use_cassette("client_exchange_symbol_list") do
|
|
27
28
|
result = client.exchange_symbol_list(exchange_code: 'AU')
|
|
28
29
|
_(result).must_be_kind_of(Array)
|
|
@@ -38,7 +39,7 @@ describe Eodhd::Client do
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
describe "#eod_data" do
|
|
41
|
-
it "
|
|
42
|
+
it "returns parsed JSON array" do
|
|
42
43
|
VCR.use_cassette('client_eod_data') do
|
|
43
44
|
result = client.eod_data(exchange_id: 'AU', symbol: 'BHP', period: 'd')
|
|
44
45
|
_(result).must_be_kind_of(Array)
|
|
@@ -53,8 +54,24 @@ describe Eodhd::Client do
|
|
|
53
54
|
end
|
|
54
55
|
end
|
|
55
56
|
|
|
57
|
+
describe "#eod_bulk_last_day" do
|
|
58
|
+
it "returns parsed JSON array" do
|
|
59
|
+
VCR.use_cassette("client_eod_bulk_last_day") do
|
|
60
|
+
result = client.eod_bulk_last_day(exchange_id: 'AU', date: "2024-09-30")
|
|
61
|
+
_(result).must_be_kind_of(Array)
|
|
62
|
+
_(result.first.keys).must_equal(%w{code exchange_short_name date open high low close adjusted_close volume})
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "raises Eodhd::Error on failure" do
|
|
67
|
+
VCR.use_cassette('client_eod_bulk_last_day_401_error') do
|
|
68
|
+
_{client.eod_bulk_last_day(exchange_id: 'AU', date: "2024-09-30")}.must_raise(Eodhd::Error)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
56
73
|
describe "#intraday" do
|
|
57
|
-
it "
|
|
74
|
+
it "returns parsed JSON array" do
|
|
58
75
|
VCR.use_cassette('client_intraday') do
|
|
59
76
|
result = client.intraday(exchange_code: 'US', symbol: 'AAPL', interval: '5m')
|
|
60
77
|
_(result).must_be_kind_of(Array)
|
|
@@ -69,18 +86,25 @@ describe Eodhd::Client do
|
|
|
69
86
|
end
|
|
70
87
|
end
|
|
71
88
|
|
|
72
|
-
describe "#
|
|
73
|
-
it "
|
|
74
|
-
VCR.use_cassette(
|
|
75
|
-
result = client.
|
|
76
|
-
_(result).must_be_kind_of(
|
|
77
|
-
_(result
|
|
89
|
+
describe "#fundamentals" do
|
|
90
|
+
it "returns parsed JSON hash" do
|
|
91
|
+
VCR.use_cassette('client_fundamentals') do
|
|
92
|
+
result = client.fundamentals(exchange_code: 'US', symbol: 'AAPL')
|
|
93
|
+
_(result).must_be_kind_of(Hash)
|
|
94
|
+
_(result['General']).wont_be_nil
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "supports filter parameter" do
|
|
99
|
+
VCR.use_cassette('client_fundamentals_filtered') do
|
|
100
|
+
result = client.fundamentals(exchange_code: 'US', symbol: 'AAPL', filter: 'General')
|
|
101
|
+
_(result).must_be_kind_of(Hash)
|
|
78
102
|
end
|
|
79
103
|
end
|
|
80
104
|
|
|
81
105
|
it "raises Eodhd::Error on failure" do
|
|
82
|
-
VCR.use_cassette('
|
|
83
|
-
_{client.
|
|
106
|
+
VCR.use_cassette('client_fundamentals_401_error') do
|
|
107
|
+
_{client.fundamentals(exchange_code: 'US', symbol: 'AAPL')}.must_raise(Eodhd::Error)
|
|
84
108
|
end
|
|
85
109
|
end
|
|
86
110
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Eodhd/Fundamentals_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
4
|
+
|
|
5
|
+
describe Eodhd::Fundamentals do
|
|
6
|
+
let(:api_token){ENV.fetch('EODHD_API_TOKEN', '<API_TOKEN>')}
|
|
7
|
+
let(:fd){Eodhd::Fundamentals.all(api_token: api_token, exchange_code: 'US', symbol: 'AAPL')}
|
|
8
|
+
|
|
9
|
+
describe ".all" do
|
|
10
|
+
it "returns an Eodhd::Fundamentals object" do
|
|
11
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
12
|
+
_(fd).must_be_kind_of(Eodhd::Fundamentals)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "stores exchange_code and symbol" do
|
|
17
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
18
|
+
_(fd.exchange_code).must_equal('US')
|
|
19
|
+
_(fd.symbol).must_equal('AAPL')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "returns General as an Eodhd::Fundamentals::General" do
|
|
24
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
25
|
+
_(fd.general).must_be_kind_of(Eodhd::Fundamentals::General)
|
|
26
|
+
_(fd.general.code).must_equal('AAPL')
|
|
27
|
+
_(fd.general.name).wont_be_nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "returns Highlights as an Eodhd::Fundamentals::Highlights" do
|
|
32
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
33
|
+
_(fd.highlights).must_be_kind_of(Eodhd::Fundamentals::Highlights)
|
|
34
|
+
_(fd.highlights.market_capitalization).wont_be_nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "returns Valuation as an Eodhd::Fundamentals::Valuation" do
|
|
39
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
40
|
+
_(fd.valuation).must_be_kind_of(Eodhd::Fundamentals::Valuation)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "returns Financials as an Eodhd::Fundamentals::Financials" do
|
|
45
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
46
|
+
_(fd.financials).must_be_kind_of(Eodhd::Fundamentals::Financials)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "returns Financials with income_statement, balance_sheet, and cash_flow" do
|
|
51
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
52
|
+
_(fd.financials.income_statement).must_be_kind_of(Eodhd::Fundamentals::Financials::Statement)
|
|
53
|
+
_(fd.financials.balance_sheet).must_be_kind_of(Eodhd::Fundamentals::Financials::Statement)
|
|
54
|
+
_(fd.financials.cash_flow).must_be_kind_of(Eodhd::Fundamentals::Financials::Statement)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "returns quarterly and yearly data as hashes on financial statements" do
|
|
59
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
60
|
+
_(fd.financials.income_statement.quarterly).must_be_kind_of(Hash)
|
|
61
|
+
_(fd.financials.income_statement.yearly).must_be_kind_of(Hash)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "returns Earnings as an Eodhd::Fundamentals::Earnings" do
|
|
66
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
67
|
+
_(fd.earnings).must_be_kind_of(Eodhd::Fundamentals::Earnings)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "returns SharesStats as an Eodhd::Fundamentals::SharesStats" do
|
|
72
|
+
VCR.use_cassette('fundamentals_aapl') do
|
|
73
|
+
_(fd.shares_stats).must_be_kind_of(Eodhd::Fundamentals::SharesStats)
|
|
74
|
+
_(fd.shares_stats.shares_outstanding).wont_be_nil
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
# Eodhd/Validations_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
2
4
|
|
|
3
5
|
class DummyIncludingValidations
|
|
4
6
|
include Eodhd::Validations
|
|
5
7
|
end
|
|
6
8
|
|
|
7
|
-
describe
|
|
9
|
+
describe Eodhd::Validations do
|
|
8
10
|
let(:v){DummyIncludingValidations.new}
|
|
9
11
|
|
|
10
12
|
describe "#validate_arguments" do
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# Eodhd/WebSocketClient_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
2
4
|
|
|
3
5
|
describe Eodhd::WebSocketClient do
|
|
4
6
|
let(:api_token){ENV.fetch('EODHD_API_TOKEN', '<API_TOKEN>')}
|
|
@@ -53,10 +55,10 @@ describe Eodhd::WebSocketClient do
|
|
|
53
55
|
describe "#run" do
|
|
54
56
|
it "configures Iodine and starts connection" do
|
|
55
57
|
client = Eodhd::WebSocketClient.new(api_token: api_token, asset_class: 'us', symbols: 'AAPL', consumer: consumer)
|
|
56
|
-
|
|
58
|
+
|
|
57
59
|
iodine_connect_called = false
|
|
58
60
|
iodine_start_called = false
|
|
59
|
-
|
|
61
|
+
|
|
60
62
|
Iodine.stub(:threads=, nil) do
|
|
61
63
|
Iodine.stub(:connect, ->(opts){iodine_connect_called = true}) do
|
|
62
64
|
Iodine.stub(:start, ->{iodine_start_called = true}) do
|
|
@@ -64,7 +66,7 @@ describe Eodhd::WebSocketClient do
|
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
|
-
|
|
69
|
+
|
|
68
70
|
_(iodine_connect_called).must_equal(true)
|
|
69
71
|
_(iodine_start_called).must_equal(true)
|
|
70
72
|
end
|
|
@@ -76,10 +78,10 @@ describe Eodhd::WebSocketClient do
|
|
|
76
78
|
received_data = nil
|
|
77
79
|
test_consumer = ->(data){received_data = data}
|
|
78
80
|
handler = Eodhd::WebSocketClient::Handler.new(symbols: 'AAPL', consumer: test_consumer, logger: nil)
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
message = '{"symbol":"AAPL","price":150.0}'
|
|
81
83
|
handler.on_message(nil, message)
|
|
82
|
-
|
|
84
|
+
|
|
83
85
|
_(received_data).must_be_kind_of(Hash)
|
|
84
86
|
_(received_data['symbol']).must_equal('AAPL')
|
|
85
87
|
_(received_data['price']).must_equal(150.0)
|
|
@@ -88,7 +90,7 @@ describe Eodhd::WebSocketClient do
|
|
|
88
90
|
it "raises Eodhd::Error on invalid JSON" do
|
|
89
91
|
test_consumer = ->(data){}
|
|
90
92
|
handler = Eodhd::WebSocketClient::Handler.new(symbols: 'AAPL', consumer: test_consumer, logger: nil)
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
invalid_message = '{invalid json'
|
|
93
95
|
_{handler.on_message(nil, invalid_message)}.must_raise(Eodhd::Error)
|
|
94
96
|
end
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
# Eodhd_test.rb
|
|
2
|
+
|
|
1
3
|
require_relative "helper"
|
|
2
4
|
|
|
3
5
|
describe Eodhd do
|
|
4
6
|
let(:api_token){ENV.fetch('EODHD_API_TOKEN', '<API_TOKEN>')}
|
|
5
|
-
|
|
6
7
|
let(:eodhd){Eodhd.new(api_token: api_token)}
|
|
7
8
|
|
|
8
9
|
describe "#exchanges" do
|
|
@@ -59,4 +60,14 @@ describe Eodhd do
|
|
|
59
60
|
end
|
|
60
61
|
end
|
|
61
62
|
end
|
|
63
|
+
|
|
64
|
+
describe "#fundamentals" do
|
|
65
|
+
it "delegates to Eodhd::Fundamentals.all" do
|
|
66
|
+
VCR.use_cassette('eodhd_fundamentals') do
|
|
67
|
+
fundamental_data = eodhd.fundamentals(exchange_code: 'US', symbol: 'AAPL')
|
|
68
|
+
_(fundamental_data).must_be_kind_of(Eodhd::Fundamentals)
|
|
69
|
+
_(fundamental_data.general).must_be_kind_of(Eodhd::Fundamentals::General)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
62
73
|
end
|