Skip to content

Commit f6beadf

Browse files
authored
Merge pull request ranaroussi#1716 from ranaroussi/dev
sync dev -> main
2 parents 5b1605b + 7da64b6 commit f6beadf

File tree

6 files changed

+247
-238
lines changed

6 files changed

+247
-238
lines changed

meta.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{% set name = "yfinance" %}
2-
{% set version = "0.2.30" %}
2+
{% set version = "0.2.31b1" %}
33

44
package:
55
name: "{{ name|lower }}"

test_yfinance.py

Lines changed: 0 additions & 73 deletions
This file was deleted.

tests/ticker.py

Lines changed: 115 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,54 @@
99
1010
"""
1111
import pandas as pd
12-
import numpy as np
1312

1413
from .context import yfinance as yf
1514
from .context import session_gbl
15+
from yfinance.exceptions import YFNotImplementedError
16+
1617

1718
import unittest
1819
import requests_cache
19-
20+
from typing import Union, Any
21+
22+
ticker_attributes = (
23+
("major_holders", pd.DataFrame),
24+
("institutional_holders", pd.DataFrame),
25+
("mutualfund_holders", pd.DataFrame),
26+
("splits", pd.Series),
27+
("actions", pd.DataFrame),
28+
("shares", pd.DataFrame),
29+
("info", dict),
30+
("calendar", pd.DataFrame),
31+
("recommendations", Union[pd.DataFrame, dict]),
32+
("earnings", pd.DataFrame),
33+
("quarterly_earnings", pd.DataFrame),
34+
("recommendations_summary", Union[pd.DataFrame, dict]),
35+
("quarterly_cashflow", pd.DataFrame),
36+
("cashflow", pd.DataFrame),
37+
("quarterly_balance_sheet", pd.DataFrame),
38+
("balance_sheet", pd.DataFrame),
39+
("quarterly_income_stmt", pd.DataFrame),
40+
("income_stmt", pd.DataFrame),
41+
("analyst_price_target", pd.DataFrame),
42+
("revenue_forecasts", pd.DataFrame),
43+
("sustainability", pd.DataFrame),
44+
("options", tuple),
45+
("news", Any),
46+
("earnings_trend", pd.DataFrame),
47+
("earnings_dates", pd.DataFrame),
48+
("earnings_forecasts", pd.DataFrame),
49+
)
50+
51+
def assert_attribute_type(testClass: unittest.TestCase, instance, attribute_name, expected_type):
52+
try:
53+
attribute = getattr(instance, attribute_name)
54+
if attribute is not None and expected_type is not Any:
55+
testClass.assertEqual(type(attribute), expected_type)
56+
except Exception:
57+
testClass.assertRaises(
58+
YFNotImplementedError, lambda: getattr(instance, attribute_name)
59+
)
2060

2161
class TestTicker(unittest.TestCase):
2262
session = None
@@ -61,39 +101,9 @@ def test_badTicker(self):
61101
for k in dat.fast_info:
62102
dat.fast_info[k]
63103

64-
dat.isin
65-
dat.major_holders
66-
dat.institutional_holders
67-
dat.mutualfund_holders
68-
dat.dividends
69-
dat.splits
70-
dat.actions
71-
dat.get_shares_full()
72-
dat.options
73-
dat.news
74-
dat.earnings_dates
75-
76-
dat.income_stmt
77-
dat.quarterly_income_stmt
78-
dat.balance_sheet
79-
dat.quarterly_balance_sheet
80-
dat.cashflow
81-
dat.quarterly_cashflow
82-
83-
# These haven't been ported Yahoo API
84-
# dat.shares
85-
# dat.info
86-
# dat.calendar
87-
# dat.recommendations
88-
# dat.earnings
89-
# dat.quarterly_earnings
90-
# dat.recommendations_summary
91-
# dat.analyst_price_target
92-
# dat.revenue_forecasts
93-
# dat.sustainability
94-
# dat.earnings_trend
95-
# dat.earnings_forecasts
96-
104+
for attribute_name, attribute_type in ticker_attributes:
105+
assert_attribute_type(self, dat, attribute_name, attribute_type)
106+
97107
def test_goodTicker(self):
98108
# that yfinance works when full api is called on same instance of ticker
99109

@@ -113,61 +123,20 @@ def test_goodTicker(self):
113123
for k in dat.fast_info:
114124
dat.fast_info[k]
115125

116-
dat.isin
117-
dat.major_holders
118-
dat.institutional_holders
119-
dat.mutualfund_holders
120-
dat.dividends
121-
dat.splits
122-
dat.actions
123-
dat.get_shares_full()
124-
dat.options
125-
dat.news
126-
dat.earnings_dates
127-
128-
dat.income_stmt
129-
dat.quarterly_income_stmt
130-
dat.balance_sheet
131-
dat.quarterly_balance_sheet
132-
dat.cashflow
133-
dat.quarterly_cashflow
134-
135-
# These require decryption which is broken:
136-
# dat.shares
137-
# dat.info
138-
# dat.calendar
139-
# dat.recommendations
140-
# dat.earnings
141-
# dat.quarterly_earnings
142-
# dat.recommendations_summary
143-
# dat.analyst_price_target
144-
# dat.revenue_forecasts
145-
# dat.sustainability
146-
# dat.earnings_trend
147-
# dat.earnings_forecasts
148-
126+
for attribute_name, attribute_type in ticker_attributes:
127+
assert_attribute_type(self, dat, attribute_name, attribute_type)
128+
129+
#TODO:: Refactor with `assert_attribute` once proxy is accepted as a parameter of `Ticker`
149130
def test_goodTicker_withProxy(self):
150131
# that yfinance works when full api is called on same instance of ticker
151132

152133
tkr = "IBM"
153134
dat = yf.Ticker(tkr, session=self.session)
154135

155-
dat._fetch_ticker_tz(proxy=self.proxy, timeout=5, debug_mode=False, raise_errors=False)
156-
dat._get_ticker_tz(proxy=self.proxy, timeout=5, debug_mode=False, raise_errors=False)
136+
dat._fetch_ticker_tz(proxy=self.proxy, timeout=5)
137+
dat._get_ticker_tz(proxy=self.proxy, timeout=5)
157138
dat.history(period="1wk", proxy=self.proxy)
158139

159-
v = dat.stats(proxy=self.proxy)
160-
self.assertIsNotNone(v)
161-
self.assertTrue(len(v) > 0)
162-
163-
v = dat.get_recommendations(proxy=self.proxy)
164-
self.assertIsNotNone(v)
165-
self.assertFalse(v.empty)
166-
167-
v = dat.get_calendar(proxy=self.proxy)
168-
self.assertIsNotNone(v)
169-
self.assertFalse(v.empty)
170-
171140
v = dat.get_major_holders(proxy=self.proxy)
172141
self.assertIsNotNone(v)
173142
self.assertFalse(v.empty)
@@ -184,38 +153,6 @@ def test_goodTicker_withProxy(self):
184153
self.assertIsNotNone(v)
185154
self.assertTrue(len(v) > 0)
186155

187-
v = dat.get_sustainability(proxy=self.proxy)
188-
self.assertIsNotNone(v)
189-
self.assertFalse(v.empty)
190-
191-
v = dat.get_recommendations_summary(proxy=self.proxy)
192-
self.assertIsNotNone(v)
193-
self.assertFalse(v.empty)
194-
195-
v = dat.get_analyst_price_target(proxy=self.proxy)
196-
self.assertIsNotNone(v)
197-
self.assertFalse(v.empty)
198-
199-
v = dat.get_rev_forecast(proxy=self.proxy)
200-
self.assertIsNotNone(v)
201-
self.assertFalse(v.empty)
202-
203-
v = dat.get_earnings_forecast(proxy=self.proxy)
204-
self.assertIsNotNone(v)
205-
self.assertFalse(v.empty)
206-
207-
v = dat.get_trend_details(proxy=self.proxy)
208-
self.assertIsNotNone(v)
209-
self.assertFalse(v.empty)
210-
211-
v = dat.get_earnings_trend(proxy=self.proxy)
212-
self.assertIsNotNone(v)
213-
self.assertFalse(v.empty)
214-
215-
v = dat.get_earnings(proxy=self.proxy)
216-
self.assertIsNotNone(v)
217-
self.assertFalse(v.empty)
218-
219156
v = dat.get_income_stmt(proxy=self.proxy)
220157
self.assertIsNotNone(v)
221158
self.assertFalse(v.empty)
@@ -244,10 +181,6 @@ def test_goodTicker_withProxy(self):
244181
self.assertIsNotNone(v)
245182
self.assertFalse(v.empty)
246183

247-
v = dat.get_shares(proxy=self.proxy)
248-
self.assertIsNotNone(v)
249-
self.assertFalse(v.empty)
250-
251184
v = dat.get_shares_full(proxy=self.proxy)
252185
self.assertIsNotNone(v)
253186
self.assertFalse(v.empty)
@@ -264,11 +197,60 @@ def test_goodTicker_withProxy(self):
264197
self.assertIsNotNone(v)
265198
self.assertFalse(v.empty)
266199

267-
# TODO: enable after merge
268-
# dat.get_history_metadata(proxy=self.proxy)
200+
dat.get_history_metadata(proxy=self.proxy)
201+
self.assertIsNotNone(v)
202+
self.assertTrue(len(v) > 0)
203+
204+
# Below will fail because not ported to Yahoo API
205+
206+
# v = dat.stats(proxy=self.proxy)
269207
# self.assertIsNotNone(v)
270208
# self.assertTrue(len(v) > 0)
271209

210+
# v = dat.get_recommendations(proxy=self.proxy)
211+
# self.assertIsNotNone(v)
212+
# self.assertFalse(v.empty)
213+
214+
# v = dat.get_calendar(proxy=self.proxy)
215+
# self.assertIsNotNone(v)
216+
# self.assertFalse(v.empty)
217+
218+
# v = dat.get_sustainability(proxy=self.proxy)
219+
# self.assertIsNotNone(v)
220+
# self.assertFalse(v.empty)
221+
222+
# v = dat.get_recommendations_summary(proxy=self.proxy)
223+
# self.assertIsNotNone(v)
224+
# self.assertFalse(v.empty)
225+
226+
# v = dat.get_analyst_price_target(proxy=self.proxy)
227+
# self.assertIsNotNone(v)
228+
# self.assertFalse(v.empty)
229+
230+
# v = dat.get_rev_forecast(proxy=self.proxy)
231+
# self.assertIsNotNone(v)
232+
# self.assertFalse(v.empty)
233+
234+
# v = dat.get_earnings_forecast(proxy=self.proxy)
235+
# self.assertIsNotNone(v)
236+
# self.assertFalse(v.empty)
237+
238+
# v = dat.get_trend_details(proxy=self.proxy)
239+
# self.assertIsNotNone(v)
240+
# self.assertFalse(v.empty)
241+
242+
# v = dat.get_earnings_trend(proxy=self.proxy)
243+
# self.assertIsNotNone(v)
244+
# self.assertFalse(v.empty)
245+
246+
# v = dat.get_earnings(proxy=self.proxy)
247+
# self.assertIsNotNone(v)
248+
# self.assertFalse(v.empty)
249+
250+
# v = dat.get_shares(proxy=self.proxy)
251+
# self.assertIsNotNone(v)
252+
# self.assertFalse(v.empty)
253+
272254

273255
class TestTickerHistory(unittest.TestCase):
274256
session = None
@@ -312,16 +294,21 @@ def test_no_expensive_calls_introduced(self):
312294
As doing other type of scraping calls than "query2.finance.yahoo.com/v8/finance/chart" to yahoo website
313295
will quickly trigger spam-block when doing bulk download of history data.
314296
"""
315-
session = requests_cache.CachedSession(backend='memory')
316-
ticker = yf.Ticker("GOOGL", session=session)
317-
ticker.history("1y")
318-
actual_urls_called = tuple([r.url for r in session.cache.filter()])
319-
session.close()
297+
symbol = "GOOGL"
298+
range = "1y"
299+
with requests_cache.CachedSession(backend="memory") as session:
300+
ticker = yf.Ticker(symbol, session=session)
301+
ticker.history(range)
302+
actual_urls_called = tuple([r.url for r in session.cache.filter()])
303+
320304
expected_urls = (
321-
'https://query2.finance.yahoo.com/v8/finance/chart/GOOGL?events=div,splits,capitalGains&includePrePost=False&interval=1d&range=1y',
305+
f"https://query2.finance.yahoo.com/v8/finance/chart/{symbol}?events=div%2Csplits%2CcapitalGains&includePrePost=False&interval=1d&range={range}",
306+
)
307+
self.assertEqual(
308+
expected_urls,
309+
actual_urls_called,
310+
"Different than expected url used to fetch history."
322311
)
323-
self.assertEqual(expected_urls, actual_urls_called, "Different than expected url used to fetch history.")
324-
325312
def test_dividends(self):
326313
data = self.ticker.dividends
327314
self.assertIsInstance(data, pd.Series, "data has wrong type")

0 commit comments

Comments
 (0)