Skip to content

Commit 1081f1e

Browse files
authored
Merge pull request #2226 from ranaroussi/dev
sync dev -> main
2 parents 5adddf3 + fed35b1 commit 1081f1e

26 files changed

+1123
-799
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Yahoo! finance API is intended for personal use only.**
3939
- `Ticker`: single ticker data
4040
- `Tickers`: multiple tickers' data
4141
- `download`: download market data for multiple tickers
42+
- `Market`: get infomation about a market
4243
- `Search`: quotes and news from search
4344
- `Sector` and `Industry`: sector and industry information
4445
- `EquityQuery` and `Screener`: build query to screen market
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import yfinance as yf
2+
3+
EUROPE = yf.Market("EUROPE")
4+
5+
status = EUROPE.status
6+
summary = EUROPE.summary

doc/source/reference/examples/search.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44
quotes = yf.Search("AAPL", max_results=10).quotes
55

66
# get list of news
7-
news = yf.Search("Google", news_count=10).news
7+
news = yf.Search("Google", news_count=10).news
8+
9+
# get list of related research
10+
research = yf.Search("apple", include_research=True).research

doc/source/reference/index.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ The following are the publicly available classes, and functions exposed by the `
1515

1616
- :attr:`Ticker <yfinance.Ticker>`: Class for accessing single ticker data.
1717
- :attr:`Tickers <yfinance.Tickers>`: Class for handling multiple tickers.
18+
- :attr:`MarketSummary <yfinance.MarketSummary>`: Class for accessing market summary.
1819
- :attr:`Search <yfinance.Search>`: Class for accessing search results.
1920
- :attr:`Sector <yfinance.Sector>`: Domain class for accessing sector information.
2021
- :attr:`Industry <yfinance.Industry>`: Domain class for accessing industry information.
2122
- :attr:`download <yfinance.download>`: Function to download market data for multiple tickers.
22-
- :attr:`EquityQuery <yfinance.EquityQuery>`: Class to build equity market query.
23+
- :attr:`EquityOperation <yfinance.EquityOperation>`: Class to build equity market operation.
24+
- :attr:`Query <yfinance.Query>`: Class to build query.
2325
- :attr:`Screener <yfinance.Screener>`: Class to screen the market using defined query.
2426
- :attr:`enable_debug_mode <yfinance.enable_debug_mode>`: Function to enable debug mode for logging.
2527
- :attr:`set_tz_cache_location <yfinance.set_tz_cache_location>`: Function to set the timezone cache location.
@@ -33,8 +35,10 @@ The following are the publicly available classes, and functions exposed by the `
3335
yfinance.stock
3436
yfinance.financials
3537
yfinance.analysis
38+
yfinance.marketsummary
3639
yfinance.search
3740
yfinance.sector_industry
41+
yfinance.screener
3842
yfinance.functions
3943

4044
yfinance.funds_data

doc/source/reference/yfinance.functions.rst

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,6 @@ The `download` function allows you to retrieve market data for multiple tickers
1313

1414
download
1515

16-
Query Market Data
17-
~~~~~~~~~~~~~~~~~~~~~
18-
The `Sector` and `Industry` modules allow you to access the sector and industry information.
19-
20-
.. autosummary::
21-
:toctree: api/
22-
23-
EquityQuery
24-
Screener
25-
26-
.. seealso::
27-
:attr:`EquityQuery.valid_operand_fields <yfinance.EquityQuery.valid_operand_fields>`
28-
supported operand values for query
29-
:attr:`EquityQuery.valid_eq_operand_map <yfinance.EquityQuery.valid_eq_operand_map>`
30-
supported `EQ query operand parameters`
31-
:attr:`Screener.predefined_bodies <yfinance.Screener.predefined_bodies>`
32-
supported predefined screens
33-
34-
3516
Enable Debug Mode
3617
~~~~~~~~~~~~~~~~~
3718
Enables logging of debug information for the `yfinance` package.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=====================
2+
Market Summary
3+
=====================
4+
.. currentmodule:: yfinance
5+
Class
6+
------------
7+
The `Market` class, allows you to access market data in a Pythonic way.
8+
.. autosummary::
9+
:toctree: api/
10+
Market
11+
12+
Market Sample Code
13+
--------------------------
14+
The `Market` class, allows you to access market summary data in a Pythonic way.
15+
.. literalinclude:: examples/market.py
16+
:language: python
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=========================
2+
Screener & Query
3+
=========================
4+
5+
.. currentmodule:: yfinance
6+
7+
Query Market Data
8+
~~~~~~~~~~~~~~~~~~~~~
9+
The `Sector` and `Industry` modules allow you to access the sector and industry information.
10+
11+
.. autosummary::
12+
:toctree: api/
13+
14+
EquityQuery
15+
FundQuery
16+
screen
17+
18+
.. seealso::
19+
:attr:`EquityQuery.valid_fields <yfinance.EquityQuery.valid_fields>`
20+
supported operand values for query
21+
:attr:`EquityQuery.valid_values <yfinance.EquityQuery.valid_values>`
22+
supported `EQ query operand parameters`
23+
:attr:`FundQuery.valid_fields <yfinance.FundQuery.valid_fields>`
24+
supported operand values for query
25+
:attr:`FundQuery.valid_values <yfinance.FundQuery.valid_values>`
26+
supported `EQ query operand parameters`
27+

requirements.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ pytz>=2022.5
88
frozendict>=2.3.4
99
beautifulsoup4>=4.11.1
1010
html5lib>=1.1
11-
peewee>=3.16.2
11+
peewee>=3.16.2
12+
requests_cache>=1.0
13+
requests_ratelimiter>=0.3.1
14+
scipy>=1.6.3

tests/test_screener.py

Lines changed: 14 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,38 @@
11
import unittest
22
from unittest.mock import patch, MagicMock
3-
from yfinance.const import PREDEFINED_SCREENER_BODY_MAP
4-
from yfinance.screener.screener import Screener
5-
from yfinance.screener.screener_query import EquityQuery
3+
from yfinance.screener.screener import screen
4+
from yfinance.screener.query import EquityQuery
65

76

87
class TestScreener(unittest.TestCase):
98

109
@classmethod
1110
def setUpClass(self):
12-
self.screener = Screener()
1311
self.query = EquityQuery('gt',['eodprice',3])
14-
15-
def test_set_default_body(self):
16-
result = self.screener.set_default_body(self.query)
17-
18-
self.assertEqual(self.screener.body['offset'], 0)
19-
self.assertEqual(self.screener.body['size'], 100)
20-
self.assertEqual(self.screener.body['sortField'], 'ticker')
21-
self.assertEqual(self.screener.body['sortType'], 'desc')
22-
self.assertEqual(self.screener.body['quoteType'], 'equity')
23-
self.assertEqual(self.screener.body['query'], self.query.to_dict())
24-
self.assertEqual(self.screener.body['userId'], '')
25-
self.assertEqual(self.screener.body['userIdType'], 'guid')
26-
self.assertEqual(self.screener, result)
27-
28-
def test_set_predefined_body(self):
29-
k = 'most_actives'
30-
result = self.screener.set_predefined_body(k)
31-
self.assertEqual(self.screener.body, PREDEFINED_SCREENER_BODY_MAP[k])
32-
self.assertEqual(self.screener, result)
33-
34-
def test_set_predefined_body_invalid_key(self):
35-
with self.assertRaises(ValueError):
36-
self.screener.set_predefined_body('invalid_key')
37-
38-
def test_set_body(self):
39-
body = {
40-
"offset": 0,
41-
"size": 100,
42-
"sortField": "ticker",
43-
"sortType": "desc",
44-
"quoteType": "equity",
45-
"query": self.query.to_dict(),
46-
"userId": "",
47-
"userIdType": "guid"
48-
}
49-
result = self.screener.set_body(body)
50-
51-
self.assertEqual(self.screener.body, body)
52-
self.assertEqual(self.screener, result)
53-
54-
def test_set_body_missing_keys(self):
55-
body = {
56-
"offset": 0,
57-
"size": 100,
58-
"sortField": "ticker",
59-
"sortType": "desc",
60-
"quoteType": "equity"
61-
}
62-
with self.assertRaises(ValueError):
63-
self.screener.set_body(body)
64-
65-
def test_set_body_extra_keys(self):
66-
body = {
67-
"offset": 0,
68-
"size": 100,
69-
"sortField": "ticker",
70-
"sortType": "desc",
71-
"quoteType": "equity",
72-
"query": self.query.to_dict(),
73-
"userId": "",
74-
"userIdType": "guid",
75-
"extraKey": "extraValue"
76-
}
77-
with self.assertRaises(ValueError):
78-
self.screener.set_body(body)
79-
80-
def test_patch_body(self):
81-
initial_body = {
82-
"offset": 0,
83-
"size": 100,
84-
"sortField": "ticker",
85-
"sortType": "desc",
86-
"quoteType": "equity",
87-
"query": self.query.to_dict(),
88-
"userId": "",
89-
"userIdType": "guid"
90-
}
91-
self.screener.set_body(initial_body)
92-
patch_values = {"size": 50}
93-
result = self.screener.patch_body(patch_values)
94-
95-
self.assertEqual(self.screener.body['size'], 50)
96-
self.assertEqual(self.screener.body['query'], self.query.to_dict())
97-
self.assertEqual(self.screener, result)
98-
99-
def test_patch_body_extra_keys(self):
100-
initial_body = {
101-
"offset": 0,
102-
"size": 100,
103-
"sortField": "ticker",
104-
"sortType": "desc",
105-
"quoteType": "equity",
106-
"query": self.query.to_dict(),
107-
"userId": "",
108-
"userIdType": "guid"
109-
}
110-
self.screener.set_body(initial_body)
111-
patch_values = {"extraKey": "extraValue"}
112-
with self.assertRaises(ValueError):
113-
self.screener.patch_body(patch_values)
12+
self.predefined = 'aggressive_small_caps'
11413

11514
@patch('yfinance.screener.screener.YfData.post')
11615
def test_set_large_size_in_body(self, mock_post):
117-
body = {
118-
"offset": 0,
119-
"size": 251, # yahoo limits at 250
120-
"sortField": "ticker",
121-
"sortType": "desc",
122-
"quoteType": "equity",
123-
"query": self.query.to_dict(),
124-
"userId": "",
125-
"userIdType": "guid"
126-
}
127-
12816
with self.assertRaises(ValueError):
129-
self.screener.set_body(body).response
17+
screen(self.query, size=251)
13018

131-
@patch('yfinance.screener.screener.YfData.post')
132-
def test_fetch(self, mock_post):
19+
@patch('yfinance.data.YfData.post')
20+
def test_fetch_query(self, mock_post):
13321
mock_response = MagicMock()
134-
mock_response.json.return_value = {'finance': {'result': [{}]}}
22+
mock_response.json.return_value = {'finance': {'result': [{'key': 'value'}]}}
13523
mock_post.return_value = mock_response
13624

137-
self.screener.set_default_body(self.query)
138-
response = self.screener._fetch()
139-
140-
self.assertEqual(response, {'finance': {'result': [{}]}})
25+
response = screen(self.query)
26+
self.assertEqual(response, {'key': 'value'})
14127

142-
@patch('yfinance.screener.screener.YfData.post')
143-
def test_fetch_and_parse(self, mock_post):
28+
@patch('yfinance.data.YfData.get')
29+
def test_fetch_predefined(self, mock_get):
14430
mock_response = MagicMock()
14531
mock_response.json.return_value = {'finance': {'result': [{'key': 'value'}]}}
146-
mock_post.return_value = mock_response
32+
mock_get.return_value = mock_response
14733

148-
self.screener.set_default_body(self.query)
149-
self.screener._fetch_and_parse()
150-
self.assertEqual(self.screener.response, {'key': 'value'})
34+
response = screen(self.predefined)
35+
self.assertEqual(response, {'key': 'value'})
15136

15237
if __name__ == '__main__':
15338
unittest.main()

tests/test_search.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,14 @@
44

55

66
class TestSearch(unittest.TestCase):
7-
def test_valid_query(self):
8-
search = yf.Search(query="AAPL", max_results=5, news_count=3)
9-
10-
self.assertEqual(len(search.quotes), 5)
11-
self.assertEqual(len(search.news), 3)
12-
self.assertIn("AAPL", search.quotes[0]['symbol'])
13-
147
def test_invalid_query(self):
158
search = yf.Search(query="XYZXYZ")
169

1710
self.assertEqual(len(search.quotes), 0)
1811
self.assertEqual(len(search.news), 0)
12+
self.assertEqual(len(search.lists), 0)
13+
self.assertEqual(len(search.nav), 0)
14+
self.assertEqual(len(search.research), 0)
1915

2016
def test_empty_query(self):
2117
search = yf.Search(query="")
@@ -29,3 +25,18 @@ def test_fuzzy_query(self):
2925
# Check if the fuzzy search retrieves relevant results despite the typo
3026
self.assertGreater(len(search.quotes), 0)
3127
self.assertIn("AAPL", search.quotes[0]['symbol'])
28+
29+
def test_quotes(self):
30+
search = yf.Search(query="AAPL", max_results=5)
31+
32+
self.assertEqual(len(search.quotes), 5)
33+
self.assertIn("AAPL", search.quotes[0]['symbol'])
34+
35+
def test_news(self):
36+
search = yf.Search(query="AAPL", news_count=3)
37+
38+
self.assertEqual(len(search.news), 3)
39+
40+
def test_research_reports(self):
41+
search = yf.Search(query="AAPL", include_research=True)
42+
self.assertEqual(len(search.research), 3)

0 commit comments

Comments
 (0)