Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync dev -> main #2226

Merged
merged 24 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
64b4726
Merge pull request #2184 from ranaroussi/main
ValueRaider Dec 20, 2024
af78066
Raise error if rate limited
dhruvan2006 Dec 22, 2024
ae7f371
Merge pull request #2180 from dhruvan2006/rate-limit-exception
ValueRaider Dec 22, 2024
966473f
Add Market summary & status
R5dan Dec 10, 2024
24958d5
Merge pull request #2175 from R5dan/market-summary-v2
ValueRaider Dec 29, 2024
42d946c
Fix TZ-fetch hiding 429 rate-limit error
ValueRaider Dec 29, 2024
60c7477
Custom period
R5dan Dec 24, 2024
a928eb4
Merge pull request #2192 from R5dan/custom-period
ValueRaider Dec 30, 2024
b94173b
Update Search class to allow more options
R5dan Dec 23, 2024
d0e5920
Merge pull request #2191 from R5dan/Update-Search
ValueRaider Dec 30, 2024
a9859e2
Remove hardcoded keys in Analysis
dhruvan2006 Dec 31, 2024
508afca
Analysis: refactor some fetchers, fix tests
ValueRaider Jan 1, 2025
52f6462
Merge pull request #2194 from dhruvan2006/fix/eps-revisions
ValueRaider Jan 1, 2025
5a342cf
Add missing dependencies to requirements.txt
dhruvan2006 Jan 2, 2025
506f792
Merge pull request #2199 from dhruvan2006/fix/requirements
ValueRaider Jan 2, 2025
4ded157
Fix extra row in search quotes
dhruvan2006 Jan 2, 2025
e3aae8a
Expand Search test suite
dhruvan2006 Jan 2, 2025
ffa697c
Merge pull request #2202 from dhruvan2006/fix/search-quotes
ValueRaider Jan 5, 2025
2919ccb
Add utils method for validating period
dhruvan2006 Jan 7, 2025
2d60f9a
Fix custom periods
dhruvan2006 Jan 7, 2025
20b3b43
Merge pull request #2211 from dhruvan2006/fix/custom-period
ValueRaider Jan 11, 2025
dee9a55
Simplify Screener & improve its docs
ValueRaider Jan 5, 2025
3f6a46d
Merge pull request #2207 from ranaroussi/feature/improve-screener-docs
ValueRaider Jan 18, 2025
fed35b1
Fix typo in screener RST doc file
ValueRaider Jan 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Yahoo! finance API is intended for personal use only.**
- `Ticker`: single ticker data
- `Tickers`: multiple tickers' data
- `download`: download market data for multiple tickers
- `Market`: get infomation about a market
- `Search`: quotes and news from search
- `Sector` and `Industry`: sector and industry information
- `EquityQuery` and `Screener`: build query to screen market
Expand Down
6 changes: 6 additions & 0 deletions doc/source/reference/examples/market.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import yfinance as yf

EUROPE = yf.Market("EUROPE")

status = EUROPE.status
summary = EUROPE.summary
5 changes: 4 additions & 1 deletion doc/source/reference/examples/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@
quotes = yf.Search("AAPL", max_results=10).quotes

# get list of news
news = yf.Search("Google", news_count=10).news
news = yf.Search("Google", news_count=10).news

# get list of related research
research = yf.Search("apple", include_research=True).research
6 changes: 5 additions & 1 deletion doc/source/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ The following are the publicly available classes, and functions exposed by the `

- :attr:`Ticker <yfinance.Ticker>`: Class for accessing single ticker data.
- :attr:`Tickers <yfinance.Tickers>`: Class for handling multiple tickers.
- :attr:`MarketSummary <yfinance.MarketSummary>`: Class for accessing market summary.
- :attr:`Search <yfinance.Search>`: Class for accessing search results.
- :attr:`Sector <yfinance.Sector>`: Domain class for accessing sector information.
- :attr:`Industry <yfinance.Industry>`: Domain class for accessing industry information.
- :attr:`download <yfinance.download>`: Function to download market data for multiple tickers.
- :attr:`EquityQuery <yfinance.EquityQuery>`: Class to build equity market query.
- :attr:`EquityOperation <yfinance.EquityOperation>`: Class to build equity market operation.
- :attr:`Query <yfinance.Query>`: Class to build query.
- :attr:`Screener <yfinance.Screener>`: Class to screen the market using defined query.
- :attr:`enable_debug_mode <yfinance.enable_debug_mode>`: Function to enable debug mode for logging.
- :attr:`set_tz_cache_location <yfinance.set_tz_cache_location>`: Function to set the timezone cache location.
Expand All @@ -33,8 +35,10 @@ The following are the publicly available classes, and functions exposed by the `
yfinance.stock
yfinance.financials
yfinance.analysis
yfinance.marketsummary
yfinance.search
yfinance.sector_industry
yfinance.screener
yfinance.functions

yfinance.funds_data
Expand Down
19 changes: 0 additions & 19 deletions doc/source/reference/yfinance.functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,6 @@ The `download` function allows you to retrieve market data for multiple tickers

download

Query Market Data
~~~~~~~~~~~~~~~~~~~~~
The `Sector` and `Industry` modules allow you to access the sector and industry information.

.. autosummary::
:toctree: api/

EquityQuery
Screener

.. seealso::
:attr:`EquityQuery.valid_operand_fields <yfinance.EquityQuery.valid_operand_fields>`
supported operand values for query
:attr:`EquityQuery.valid_eq_operand_map <yfinance.EquityQuery.valid_eq_operand_map>`
supported `EQ query operand parameters`
:attr:`Screener.predefined_bodies <yfinance.Screener.predefined_bodies>`
supported predefined screens


Enable Debug Mode
~~~~~~~~~~~~~~~~~
Enables logging of debug information for the `yfinance` package.
Expand Down
16 changes: 16 additions & 0 deletions doc/source/reference/yfinance.market.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=====================
Market Summary
=====================
.. currentmodule:: yfinance
Class
------------
The `Market` class, allows you to access market data in a Pythonic way.
.. autosummary::
:toctree: api/
Market

Market Sample Code
--------------------------
The `Market` class, allows you to access market summary data in a Pythonic way.
.. literalinclude:: examples/market.py
:language: python
27 changes: 27 additions & 0 deletions doc/source/reference/yfinance.screener.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=========================
Screener & Query
=========================

.. currentmodule:: yfinance

Query Market Data
~~~~~~~~~~~~~~~~~~~~~
The `Sector` and `Industry` modules allow you to access the sector and industry information.

.. autosummary::
:toctree: api/

EquityQuery
FundQuery
screen

.. seealso::
:attr:`EquityQuery.valid_fields <yfinance.EquityQuery.valid_fields>`
supported operand values for query
:attr:`EquityQuery.valid_values <yfinance.EquityQuery.valid_values>`
supported `EQ query operand parameters`
:attr:`FundQuery.valid_fields <yfinance.FundQuery.valid_fields>`
supported operand values for query
:attr:`FundQuery.valid_values <yfinance.FundQuery.valid_values>`
supported `EQ query operand parameters`

5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ pytz>=2022.5
frozendict>=2.3.4
beautifulsoup4>=4.11.1
html5lib>=1.1
peewee>=3.16.2
peewee>=3.16.2
requests_cache>=1.0
requests_ratelimiter>=0.3.1
scipy>=1.6.3
143 changes: 14 additions & 129 deletions tests/test_screener.py
Original file line number Diff line number Diff line change
@@ -1,153 +1,38 @@
import unittest
from unittest.mock import patch, MagicMock
from yfinance.const import PREDEFINED_SCREENER_BODY_MAP
from yfinance.screener.screener import Screener
from yfinance.screener.screener_query import EquityQuery
from yfinance.screener.screener import screen
from yfinance.screener.query import EquityQuery


class TestScreener(unittest.TestCase):

@classmethod
def setUpClass(self):
self.screener = Screener()
self.query = EquityQuery('gt',['eodprice',3])

def test_set_default_body(self):
result = self.screener.set_default_body(self.query)

self.assertEqual(self.screener.body['offset'], 0)
self.assertEqual(self.screener.body['size'], 100)
self.assertEqual(self.screener.body['sortField'], 'ticker')
self.assertEqual(self.screener.body['sortType'], 'desc')
self.assertEqual(self.screener.body['quoteType'], 'equity')
self.assertEqual(self.screener.body['query'], self.query.to_dict())
self.assertEqual(self.screener.body['userId'], '')
self.assertEqual(self.screener.body['userIdType'], 'guid')
self.assertEqual(self.screener, result)

def test_set_predefined_body(self):
k = 'most_actives'
result = self.screener.set_predefined_body(k)
self.assertEqual(self.screener.body, PREDEFINED_SCREENER_BODY_MAP[k])
self.assertEqual(self.screener, result)

def test_set_predefined_body_invalid_key(self):
with self.assertRaises(ValueError):
self.screener.set_predefined_body('invalid_key')

def test_set_body(self):
body = {
"offset": 0,
"size": 100,
"sortField": "ticker",
"sortType": "desc",
"quoteType": "equity",
"query": self.query.to_dict(),
"userId": "",
"userIdType": "guid"
}
result = self.screener.set_body(body)

self.assertEqual(self.screener.body, body)
self.assertEqual(self.screener, result)

def test_set_body_missing_keys(self):
body = {
"offset": 0,
"size": 100,
"sortField": "ticker",
"sortType": "desc",
"quoteType": "equity"
}
with self.assertRaises(ValueError):
self.screener.set_body(body)

def test_set_body_extra_keys(self):
body = {
"offset": 0,
"size": 100,
"sortField": "ticker",
"sortType": "desc",
"quoteType": "equity",
"query": self.query.to_dict(),
"userId": "",
"userIdType": "guid",
"extraKey": "extraValue"
}
with self.assertRaises(ValueError):
self.screener.set_body(body)

def test_patch_body(self):
initial_body = {
"offset": 0,
"size": 100,
"sortField": "ticker",
"sortType": "desc",
"quoteType": "equity",
"query": self.query.to_dict(),
"userId": "",
"userIdType": "guid"
}
self.screener.set_body(initial_body)
patch_values = {"size": 50}
result = self.screener.patch_body(patch_values)

self.assertEqual(self.screener.body['size'], 50)
self.assertEqual(self.screener.body['query'], self.query.to_dict())
self.assertEqual(self.screener, result)

def test_patch_body_extra_keys(self):
initial_body = {
"offset": 0,
"size": 100,
"sortField": "ticker",
"sortType": "desc",
"quoteType": "equity",
"query": self.query.to_dict(),
"userId": "",
"userIdType": "guid"
}
self.screener.set_body(initial_body)
patch_values = {"extraKey": "extraValue"}
with self.assertRaises(ValueError):
self.screener.patch_body(patch_values)
self.predefined = 'aggressive_small_caps'

@patch('yfinance.screener.screener.YfData.post')
def test_set_large_size_in_body(self, mock_post):
body = {
"offset": 0,
"size": 251, # yahoo limits at 250
"sortField": "ticker",
"sortType": "desc",
"quoteType": "equity",
"query": self.query.to_dict(),
"userId": "",
"userIdType": "guid"
}

with self.assertRaises(ValueError):
self.screener.set_body(body).response
screen(self.query, size=251)

@patch('yfinance.screener.screener.YfData.post')
def test_fetch(self, mock_post):
@patch('yfinance.data.YfData.post')
def test_fetch_query(self, mock_post):
mock_response = MagicMock()
mock_response.json.return_value = {'finance': {'result': [{}]}}
mock_response.json.return_value = {'finance': {'result': [{'key': 'value'}]}}
mock_post.return_value = mock_response

self.screener.set_default_body(self.query)
response = self.screener._fetch()

self.assertEqual(response, {'finance': {'result': [{}]}})
response = screen(self.query)
self.assertEqual(response, {'key': 'value'})

@patch('yfinance.screener.screener.YfData.post')
def test_fetch_and_parse(self, mock_post):
@patch('yfinance.data.YfData.get')
def test_fetch_predefined(self, mock_get):
mock_response = MagicMock()
mock_response.json.return_value = {'finance': {'result': [{'key': 'value'}]}}
mock_post.return_value = mock_response
mock_get.return_value = mock_response

self.screener.set_default_body(self.query)
self.screener._fetch_and_parse()
self.assertEqual(self.screener.response, {'key': 'value'})
response = screen(self.predefined)
self.assertEqual(response, {'key': 'value'})

if __name__ == '__main__':
unittest.main()
25 changes: 18 additions & 7 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@


class TestSearch(unittest.TestCase):
def test_valid_query(self):
search = yf.Search(query="AAPL", max_results=5, news_count=3)

self.assertEqual(len(search.quotes), 5)
self.assertEqual(len(search.news), 3)
self.assertIn("AAPL", search.quotes[0]['symbol'])

def test_invalid_query(self):
search = yf.Search(query="XYZXYZ")

self.assertEqual(len(search.quotes), 0)
self.assertEqual(len(search.news), 0)
self.assertEqual(len(search.lists), 0)
self.assertEqual(len(search.nav), 0)
self.assertEqual(len(search.research), 0)

def test_empty_query(self):
search = yf.Search(query="")
Expand All @@ -29,3 +25,18 @@ def test_fuzzy_query(self):
# Check if the fuzzy search retrieves relevant results despite the typo
self.assertGreater(len(search.quotes), 0)
self.assertIn("AAPL", search.quotes[0]['symbol'])

def test_quotes(self):
search = yf.Search(query="AAPL", max_results=5)

self.assertEqual(len(search.quotes), 5)
self.assertIn("AAPL", search.quotes[0]['symbol'])

def test_news(self):
search = yf.Search(query="AAPL", news_count=3)

self.assertEqual(len(search.news), 3)

def test_research_reports(self):
search = yf.Search(query="AAPL", include_research=True)
self.assertEqual(len(search.research), 3)
Loading
Loading