Config 1st version: just proxy

This commit is contained in:
ValueRaider
2025-03-30 17:41:13 +01:00
parent 29162bdd01
commit 89f1934bd6
22 changed files with 504 additions and 308 deletions

View File

@@ -66,7 +66,7 @@ class TestPriceHistory(unittest.TestCase):
tkrs = ["IMP.JO", "BHG.JO", "SSW.JO", "BP.L", "INTC"]
for tkr in tkrs:
dat = yf.Ticker(tkr, session=self.session)
tz = dat._get_ticker_tz(proxy=None, timeout=None)
tz = dat._get_ticker_tz(timeout=None)
dt_utc = _pd.Timestamp.utcnow()
dt = dt_utc.astimezone(_tz.timezone(tz))
@@ -86,7 +86,7 @@ class TestPriceHistory(unittest.TestCase):
test_run = False
for tkr in tkrs:
dat = yf.Ticker(tkr, session=self.session)
tz = dat._get_ticker_tz(proxy=None, timeout=None)
tz = dat._get_ticker_tz(timeout=None)
dt_utc = _pd.Timestamp.utcnow()
dt = dt_utc.astimezone(_tz.timezone(tz))
@@ -112,7 +112,7 @@ class TestPriceHistory(unittest.TestCase):
test_run = False
for tkr in tkrs:
dat = yf.Ticker(tkr, session=self.session)
tz = dat._get_ticker_tz(proxy=None, timeout=None)
tz = dat._get_ticker_tz(timeout=None)
dt = _tz.timezone(tz).localize(_dt.datetime.now())
if dt.date().weekday() not in [1, 2, 3, 4]:

View File

@@ -80,8 +80,6 @@ class TestTicker(unittest.TestCase):
def setUpClass(cls):
cls.session = session_gbl
cls.proxy = None
@classmethod
def tearDownClass(cls):
if cls.session is not None:
@@ -95,7 +93,7 @@ class TestTicker(unittest.TestCase):
# Test:
dat = yf.Ticker(tkr, session=self.session)
tz = dat._get_ticker_tz(proxy=None, timeout=5)
tz = dat._get_ticker_tz(timeout=5)
self.assertIsNotNone(tz)
@@ -227,10 +225,10 @@ class TestTicker(unittest.TestCase):
def test_goodTicker_withProxy(self):
tkr = "IBM"
dat = yf.Ticker(tkr, session=self.session, proxy=self.proxy)
dat = yf.Ticker(tkr, session=self.session)
dat._fetch_ticker_tz(proxy=None, timeout=5)
dat._get_ticker_tz(proxy=None, timeout=5)
dat._fetch_ticker_tz(timeout=5)
dat._get_ticker_tz(timeout=5)
dat.history(period="5d")
for attribute_name, attribute_type in ticker_attributes:

View File

@@ -29,6 +29,7 @@ from .cache import set_tz_cache_location
from .domain.sector import Sector
from .domain.industry import Industry
from .domain.market import Market
from .data import YfData
from .screener.query import EquityQuery, FundQuery
from .screener.screener import screen, PREDEFINED_SCREENER_QUERIES
@@ -41,4 +42,11 @@ warnings.filterwarnings('default', category=DeprecationWarning, module='^yfinanc
__all__ = ['download', 'Market', 'Search', 'Ticker', 'Tickers', 'enable_debug_mode', 'set_tz_cache_location', 'Sector', 'Industry']
# screener stuff:
__all__ += ['EquityQuery', 'FundQuery', 'screen', 'PREDEFINED_SCREENER_QUERIES']
__all__ += ['EquityQuery', 'FundQuery', 'screen', 'PREDEFINED_SCREENER_QUERIES']
# Config stuff:
_NOTSET=object()
def set_config(proxy=_NOTSET):
if proxy is not _NOTSET:
YfData(proxy=proxy)
__all__ += ["set_config"]

View File

@@ -40,15 +40,14 @@ from .scrapers.quote import Quote, FastInfo
from .scrapers.history import PriceHistory
from .scrapers.funds import FundsData
from .const import _BASE_URL_, _ROOT_URL_, _QUERY1_URL_
from .const import _BASE_URL_, _ROOT_URL_, _QUERY1_URL_, _SENTINEL_
_tz_info_fetch_ctr = 0
class TickerBase:
def __init__(self, ticker, session=None, proxy=None):
def __init__(self, ticker, session=None, proxy=_SENTINEL_):
self.ticker = ticker.upper()
self.proxy = proxy
self.session = session
self._tz = None
@@ -66,6 +65,9 @@ class TickerBase:
self.ticker = utils.get_ticker_by_isin(self.ticker, None, session)
self._data: YfData = YfData(session=session)
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
# self._price_history = PriceHistory(self._data, self.ticker)
self._price_history = None # lazy-load
@@ -85,11 +87,10 @@ class TickerBase:
def _lazy_load_price_history(self):
if self._price_history is None:
self._price_history = PriceHistory(self._data, self.ticker, self._get_ticker_tz(self.proxy, timeout=10))
self._price_history = PriceHistory(self._data, self.ticker, self._get_ticker_tz(timeout=10))
return self._price_history
def _get_ticker_tz(self, proxy, timeout):
proxy = proxy or self.proxy
def _get_ticker_tz(self, timeout):
if self._tz is not None:
return self._tz
c = cache.get_tz_cache()
@@ -101,7 +102,7 @@ class TickerBase:
tz = None
if tz is None:
tz = self._fetch_ticker_tz(proxy, timeout)
tz = self._fetch_ticker_tz(timeout)
if tz is None:
# _fetch_ticker_tz works in 99.999% of cases.
# For rare fail get from info.
@@ -123,9 +124,8 @@ class TickerBase:
return tz
@utils.log_indent_decorator
def _fetch_ticker_tz(self, proxy, timeout):
def _fetch_ticker_tz(self, timeout):
# Query Yahoo for fast price data just to get returned timezone
proxy = proxy or self.proxy
logger = utils.get_yf_logger()
params = {"range": "1d", "interval": "1d"}
@@ -134,7 +134,7 @@ class TickerBase:
url = f"{_BASE_URL_}/v8/finance/chart/{self.ticker}"
try:
data = self._data.cache_get(url=url, params=params, proxy=proxy, timeout=timeout)
data = self._data.cache_get(url=url, params=params, timeout=timeout)
data = data.json()
except YFRateLimitError:
# Must propagate this
@@ -158,95 +158,136 @@ class TickerBase:
logger.debug("-------------")
return None
def get_recommendations(self, proxy=None, as_dict=False):
def get_recommendations(self, proxy=_SENTINEL_, as_dict=False):
"""
Returns a DataFrame with the recommendations
Columns: period strongBuy buy hold sell strongSell
"""
self._quote.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._quote.recommendations
if as_dict:
return data.to_dict()
return data
def get_recommendations_summary(self, proxy=None, as_dict=False):
return self.get_recommendations(proxy=proxy, as_dict=as_dict)
def get_recommendations_summary(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
def get_upgrades_downgrades(self, proxy=None, as_dict=False):
return self.get_recommendations(as_dict=as_dict)
def get_upgrades_downgrades(self, proxy=_SENTINEL_, as_dict=False):
"""
Returns a DataFrame with the recommendations changes (upgrades/downgrades)
Index: date of grade
Columns: firm toGrade fromGrade action
"""
self._quote.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._quote.upgrades_downgrades
if as_dict:
return data.to_dict()
return data
def get_calendar(self, proxy=None) -> dict:
self._quote.proxy = proxy or self.proxy
def get_calendar(self, proxy=_SENTINEL_) -> dict:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._quote.calendar
def get_sec_filings(self, proxy=None) -> dict:
self._quote.proxy = proxy or self.proxy
def get_sec_filings(self, proxy=_SENTINEL_) -> dict:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._quote.sec_filings
def get_major_holders(self, proxy=None, as_dict=False):
self._holders.proxy = proxy or self.proxy
def get_major_holders(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._holders.major
if as_dict:
return data.to_dict()
return data
def get_institutional_holders(self, proxy=None, as_dict=False):
self._holders.proxy = proxy or self.proxy
def get_institutional_holders(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._holders.institutional
if data is not None:
if as_dict:
return data.to_dict()
return data
def get_mutualfund_holders(self, proxy=None, as_dict=False):
self._holders.proxy = proxy or self.proxy
def get_mutualfund_holders(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._holders.mutualfund
if data is not None:
if as_dict:
return data.to_dict()
return data
def get_insider_purchases(self, proxy=None, as_dict=False):
self._holders.proxy = proxy or self.proxy
def get_insider_purchases(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._holders.insider_purchases
if data is not None:
if as_dict:
return data.to_dict()
return data
def get_insider_transactions(self, proxy=None, as_dict=False):
self._holders.proxy = proxy or self.proxy
def get_insider_transactions(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._holders.insider_transactions
if data is not None:
if as_dict:
return data.to_dict()
return data
def get_insider_roster_holders(self, proxy=None, as_dict=False):
self._holders.proxy = proxy or self.proxy
def get_insider_roster_holders(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._holders.insider_roster
if data is not None:
if as_dict:
return data.to_dict()
return data
def get_info(self, proxy=None) -> dict:
self._quote.proxy = proxy or self.proxy
def get_info(self, proxy=_SENTINEL_) -> dict:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._quote.info
return data
def get_fast_info(self, proxy=None):
def get_fast_info(self, proxy=_SENTINEL_):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
if self._fast_info is None:
self._fast_info = FastInfo(self, proxy=proxy)
self._fast_info = FastInfo(self)
return self._fast_info
@property
@@ -254,76 +295,100 @@ class TickerBase:
warnings.warn("'Ticker.basic_info' is deprecated and will be removed in future, Switch to 'Ticker.fast_info'", DeprecationWarning)
return self.fast_info
def get_sustainability(self, proxy=None, as_dict=False):
self._quote.proxy = proxy or self.proxy
def get_sustainability(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._quote.sustainability
if as_dict:
return data.to_dict()
return data
def get_analyst_price_targets(self, proxy=None) -> dict:
def get_analyst_price_targets(self, proxy=_SENTINEL_) -> dict:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
"""
Keys: current low high mean median
"""
self._analysis.proxy = proxy or self.proxy
data = self._analysis.analyst_price_targets
return data
def get_earnings_estimate(self, proxy=None, as_dict=False):
def get_earnings_estimate(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
"""
Index: 0q +1q 0y +1y
Columns: numberOfAnalysts avg low high yearAgoEps growth
"""
self._analysis.proxy = proxy or self.proxy
data = self._analysis.earnings_estimate
return data.to_dict() if as_dict else data
def get_revenue_estimate(self, proxy=None, as_dict=False):
def get_revenue_estimate(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
"""
Index: 0q +1q 0y +1y
Columns: numberOfAnalysts avg low high yearAgoRevenue growth
"""
self._analysis.proxy = proxy or self.proxy
data = self._analysis.revenue_estimate
return data.to_dict() if as_dict else data
def get_earnings_history(self, proxy=None, as_dict=False):
def get_earnings_history(self, proxy=_SENTINEL_, as_dict=False):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
"""
Index: pd.DatetimeIndex
Columns: epsEstimate epsActual epsDifference surprisePercent
"""
self._analysis.proxy = proxy or self.proxy
data = self._analysis.earnings_history
return data.to_dict() if as_dict else data
def get_eps_trend(self, proxy=None, as_dict=False):
def get_eps_trend(self, proxy=_SENTINEL_, as_dict=False):
"""
Index: 0q +1q 0y +1y
Columns: current 7daysAgo 30daysAgo 60daysAgo 90daysAgo
"""
self._analysis.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._analysis.eps_trend
return data.to_dict() if as_dict else data
def get_eps_revisions(self, proxy=None, as_dict=False):
def get_eps_revisions(self, proxy=_SENTINEL_, as_dict=False):
"""
Index: 0q +1q 0y +1y
Columns: upLast7days upLast30days downLast7days downLast30days
"""
self._analysis.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._analysis.eps_revisions
return data.to_dict() if as_dict else data
def get_growth_estimates(self, proxy=None, as_dict=False):
def get_growth_estimates(self, proxy=_SENTINEL_, as_dict=False):
"""
Index: 0q +1q 0y +1y +5y -5y
Columns: stock industry sector index
"""
self._analysis.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._analysis.growth_estimates
return data.to_dict() if as_dict else data
def get_earnings(self, proxy=None, as_dict=False, freq="yearly"):
def get_earnings(self, proxy=_SENTINEL_, as_dict=False, freq="yearly"):
"""
:Parameters:
as_dict: bool
@@ -332,11 +397,11 @@ class TickerBase:
freq: str
"yearly" or "quarterly" or "trailing"
Default is "yearly"
proxy: str
Optional. Proxy server URL scheme
Default is None
"""
self._fundamentals.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
if self._fundamentals.earnings is None:
return None
data = self._fundamentals.earnings[freq]
@@ -347,7 +412,7 @@ class TickerBase:
return dict_data
return data
def get_income_stmt(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
def get_income_stmt(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly"):
"""
:Parameters:
as_dict: bool
@@ -359,13 +424,12 @@ class TickerBase:
freq: str
"yearly" or "quarterly" or "trailing"
Default is "yearly"
proxy: str
Optional. Proxy server URL scheme
Default is None
"""
self._fundamentals.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._fundamentals.financials.get_income_time_series(freq=freq, proxy=proxy)
data = self._fundamentals.financials.get_income_time_series(freq=freq)
if pretty:
data = data.copy()
@@ -374,13 +438,21 @@ class TickerBase:
return data.to_dict()
return data
def get_incomestmt(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
def get_incomestmt(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly"):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self.get_income_stmt(proxy, as_dict, pretty, freq)
def get_financials(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
def get_financials(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly"):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self.get_income_stmt(proxy, as_dict, pretty, freq)
def get_balance_sheet(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
def get_balance_sheet(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly"):
"""
:Parameters:
as_dict: bool
@@ -392,13 +464,13 @@ class TickerBase:
freq: str
"yearly" or "quarterly"
Default is "yearly"
proxy: str
Optional. Proxy server URL scheme
Default is None
"""
self._fundamentals.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._fundamentals.financials.get_balance_sheet_time_series(freq=freq, proxy=proxy)
data = self._fundamentals.financials.get_balance_sheet_time_series(freq=freq)
if pretty:
data = data.copy()
@@ -407,10 +479,14 @@ class TickerBase:
return data.to_dict()
return data
def get_balancesheet(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
def get_balancesheet(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly"):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self.get_balance_sheet(proxy, as_dict, pretty, freq)
def get_cash_flow(self, proxy=None, as_dict=False, pretty=False, freq="yearly") -> Union[pd.DataFrame, dict]:
def get_cash_flow(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly") -> Union[pd.DataFrame, dict]:
"""
:Parameters:
as_dict: bool
@@ -422,13 +498,13 @@ class TickerBase:
freq: str
"yearly" or "quarterly"
Default is "yearly"
proxy: str
Optional. Proxy server URL scheme
Default is None
"""
self._fundamentals.proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = self._fundamentals.financials.get_cash_flow_time_series(freq=freq, proxy=proxy)
data = self._fundamentals.financials.get_cash_flow_time_series(freq=freq)
if pretty:
data = data.copy()
@@ -437,34 +513,56 @@ class TickerBase:
return data.to_dict()
return data
def get_cashflow(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
def get_cashflow(self, proxy=_SENTINEL_, as_dict=False, pretty=False, freq="yearly"):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self.get_cash_flow(proxy, as_dict, pretty, freq)
def get_dividends(self, proxy=None, period="max") -> pd.Series:
return self._lazy_load_price_history().get_dividends(period=period, proxy=proxy)
def get_dividends(self, proxy=_SENTINEL_, period="max") -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._lazy_load_price_history().get_dividends(period=period)
def get_capital_gains(self, proxy=None, period="max") -> pd.Series:
return self._lazy_load_price_history().get_capital_gains(period=period, proxy=proxy)
def get_capital_gains(self, proxy=_SENTINEL_, period="max") -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._lazy_load_price_history().get_capital_gains(period=period)
def get_splits(self, proxy=None, period="max") -> pd.Series:
return self._lazy_load_price_history().get_splits(period=period, proxy=proxy)
def get_splits(self, proxy=_SENTINEL_, period="max") -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._lazy_load_price_history().get_splits(period=period)
def get_actions(self, proxy=None, period="max") -> pd.Series:
return self._lazy_load_price_history().get_actions(period=period, proxy=proxy)
def get_actions(self, proxy=_SENTINEL_, period="max") -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._lazy_load_price_history().get_actions(period=period)
def get_shares(self, proxy=_SENTINEL_, as_dict=False) -> Union[pd.DataFrame, dict]:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
def get_shares(self, proxy=None, as_dict=False) -> Union[pd.DataFrame, dict]:
self._fundamentals.proxy = proxy or self.proxy
data = self._fundamentals.shares
if as_dict:
return data.to_dict()
return data
@utils.log_indent_decorator
def get_shares_full(self, start=None, end=None, proxy=None):
def get_shares_full(self, start=None, end=None, proxy=_SENTINEL_):
logger = utils.get_yf_logger()
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
# Process dates
tz = self._get_ticker_tz(proxy=proxy, timeout=10)
tz = self._get_ticker_tz(timeout=10)
dt_now = pd.Timestamp.utcnow().tz_convert(tz)
if start is not None:
start_ts = utils._parse_user_dt(start, tz)
@@ -486,7 +584,7 @@ class TickerBase:
ts_url_base = f"https://query2.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/{self.ticker}?symbol={self.ticker}"
shares_url = f"{ts_url_base}&period1={int(start.timestamp())}&period2={int(end.timestamp())}"
try:
json_data = self._data.cache_get(url=shares_url, proxy=proxy)
json_data = self._data.cache_get(url=shares_url)
json_data = json_data.json()
except (_json.JSONDecodeError, requests.exceptions.RequestException):
logger.error(f"{self.ticker}: Yahoo web request for share count failed")
@@ -512,7 +610,11 @@ class TickerBase:
df = df.sort_index()
return df
def get_isin(self, proxy=None) -> Optional[str]:
def get_isin(self, proxy=_SENTINEL_) -> Optional[str]:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
# *** experimental ***
if self._isin is not None:
return self._isin
@@ -525,7 +627,6 @@ class TickerBase:
q = ticker
self._quote.proxy = proxy or self.proxy
if self._quote.info is None:
# Don't print error message cause self._quote.info will print one
return None
@@ -533,7 +634,7 @@ class TickerBase:
q = self._quote.info['shortName']
url = f'https://markets.businessinsider.com/ajax/SearchController_Suggest?max_results=25&query={urlencode(q)}'
data = self._data.cache_get(url=url, proxy=proxy).text
data = self._data.cache_get(url=url).text
search_str = f'"{ticker}|'
if search_str not in data:
@@ -549,13 +650,17 @@ class TickerBase:
self._isin = data.split(search_str)[1].split('"')[0].split('|')[0]
return self._isin
def get_news(self, count=10, tab="news", proxy=None) -> list:
def get_news(self, count=10, tab="news", proxy=_SENTINEL_) -> list:
"""Allowed options for tab: "news", "all", "press releases"""
if self._news:
return self._news
logger = utils.get_yf_logger()
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
tab_queryrefs = {
"all": "newsAll",
"news": "latestNews",
@@ -574,7 +679,7 @@ class TickerBase:
}
}
data = self._data.post(url, body=payload, proxy=proxy)
data = self._data.post(url, body=payload)
if data is None or "Will be right back" in data.text:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "
@@ -591,7 +696,7 @@ class TickerBase:
return self._news
@utils.log_indent_decorator
def get_earnings_dates(self, limit=12, proxy=None) -> Optional[pd.DataFrame]:
def get_earnings_dates(self, limit=12, proxy=_SENTINEL_) -> Optional[pd.DataFrame]:
"""
Get earning dates (future and historic)
@@ -599,12 +704,15 @@ class TickerBase:
limit (int): max amount of upcoming and recent earnings dates to return.
Default value 12 should return next 4 quarters and last 8 quarters.
Increase if more history is needed.
proxy: requests proxy to use.
Returns:
pd.DataFrame
"""
logger = utils.get_yf_logger()
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
clamped_limit = min(limit, 100) # YF caps at 100, don't go higher
if self._earnings_dates and clamped_limit in self._earnings_dates:
@@ -627,7 +735,7 @@ class TickerBase:
"entityIdType": "earnings",
"includeFields": ["startdatetime", "timeZoneShortName", "epsestimate", "epsactual", "epssurprisepct"]
}
response = self._data.post(url, params=params, body=body, proxy=proxy)
response = self._data.post(url, params=params, body=body)
json_data = response.json()
# Extract data
@@ -643,7 +751,7 @@ class TickerBase:
# Calculate earnings date
df['Earnings Date'] = pd.to_datetime(df['Event Start Date'])
tz = self._get_ticker_tz(proxy=proxy, timeout=30)
tz = self._get_ticker_tz(timeout=30)
if df['Earnings Date'].dt.tz is None:
df['Earnings Date'] = df['Earnings Date'].dt.tz_localize(tz)
else:
@@ -661,10 +769,18 @@ class TickerBase:
self._earnings_dates[clamped_limit] = df
return df
def get_history_metadata(self, proxy=None) -> dict:
def get_history_metadata(self, proxy=_SENTINEL_) -> dict:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self._lazy_load_price_history().get_history_metadata(proxy)
def get_funds_data(self, proxy=None) -> Optional[FundsData]:
def get_funds_data(self, proxy=_SENTINEL_) -> Optional[FundsData]:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
if not self._funds_data:
self._funds_data = FundsData(self._data, self.ticker)

View File

@@ -2,6 +2,8 @@ _QUERY1_URL_ = 'https://query1.finance.yahoo.com'
_BASE_URL_ = 'https://query2.finance.yahoo.com'
_ROOT_URL_ = 'https://finance.yahoo.com'
_SENTINEL_ = object()
fundamentals_keys = {
'financials': ["TaxEffectOfUnusualItems", "TaxRateForCalcs", "NormalizedEBITDA", "NormalizedDilutedEPS",
"NormalizedBasicEPS", "TotalUnusualItems", "TotalUnusualItemsExcludingGoodwill",

View File

@@ -51,7 +51,13 @@ class SingletonMeta(type):
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
else:
cls._instances[cls]._set_session(*args, **kwargs)
# Update the existing instance
if 'session' in kwargs or (args and len(args) > 0):
session = kwargs.get('session') if 'session' in kwargs else args[0]
cls._instances[cls]._set_session(session)
if 'proxy' in kwargs or (args and len(args) > 1):
proxy = kwargs.get('proxy') if 'proxy' in kwargs else args[1]
cls._instances[cls]._set_proxy(proxy)
return cls._instances[cls]
@@ -64,7 +70,7 @@ class YfData(metaclass=SingletonMeta):
'User-Agent': random.choice(USER_AGENTS)
}
def __init__(self, session=None):
def __init__(self, session=None, proxy=None):
self._crumb = None
self._cookie = None
@@ -75,7 +81,9 @@ class YfData(metaclass=SingletonMeta):
self._cookie_lock = threading.Lock()
self._session, self._proxy = None, None
self._set_session(session or requests.Session())
self._set_proxy(proxy)
utils.get_yf_logger().debug(f"Using User-Agent: {self.user_agent_headers['User-Agent']}")
@@ -84,6 +92,8 @@ class YfData(metaclass=SingletonMeta):
return
with self._cookie_lock:
self._session = session
if self._proxy is not None:
self._session.proxies = self._proxy
try:
self._session.cache
@@ -98,6 +108,15 @@ class YfData(metaclass=SingletonMeta):
from requests_cache import DO_NOT_CACHE
self._expire_after = DO_NOT_CACHE
def _set_proxy(self, proxy=None):
with self._cookie_lock:
if proxy is not None:
proxy = {'http': proxy, 'https': proxy} if isinstance(proxy, str) else proxy
else:
proxy = {}
self._proxy = proxy
self._session.proxies = proxy
def _set_cookie_strategy(self, strategy, have_lock=False):
if strategy == self._cookie_strategy:
return
@@ -154,7 +173,7 @@ class YfData(metaclass=SingletonMeta):
utils.get_yf_logger().debug('loaded persistent cookie')
return cookie_dict['cookie']
def _get_cookie_basic(self, proxy=None, timeout=30):
def _get_cookie_basic(self, timeout=30):
if self._cookie is not None:
utils.get_yf_logger().debug('reusing cookie')
return self._cookie
@@ -168,7 +187,6 @@ class YfData(metaclass=SingletonMeta):
response = self._session.get(
url='https://fc.yahoo.com',
headers=self.user_agent_headers,
proxies=proxy,
timeout=timeout,
allow_redirects=True)
@@ -183,7 +201,7 @@ class YfData(metaclass=SingletonMeta):
utils.get_yf_logger().debug(f"fetched basic cookie = {self._cookie}")
return self._cookie
def _get_crumb_basic(self, proxy=None, timeout=30):
def _get_crumb_basic(self, timeout=30):
if self._crumb is not None:
utils.get_yf_logger().debug('reusing crumb')
return self._crumb
@@ -197,7 +215,6 @@ class YfData(metaclass=SingletonMeta):
'url': "https://query1.finance.yahoo.com/v1/test/getcrumb",
'headers': self.user_agent_headers,
'cookies': {cookie.name: cookie.value},
'proxies': proxy,
'timeout': timeout,
'allow_redirects': True
}
@@ -215,12 +232,12 @@ class YfData(metaclass=SingletonMeta):
return self._crumb
@utils.log_indent_decorator
def _get_cookie_and_crumb_basic(self, proxy, timeout):
cookie = self._get_cookie_basic(proxy, timeout)
crumb = self._get_crumb_basic(proxy, timeout)
def _get_cookie_and_crumb_basic(self, timeout):
cookie = self._get_cookie_basic(timeout)
crumb = self._get_crumb_basic(timeout)
return cookie, crumb
def _get_cookie_csrf(self, proxy, timeout):
def _get_cookie_csrf(self, timeout):
if self._cookie is not None:
utils.get_yf_logger().debug('reusing cookie')
return True
@@ -232,7 +249,6 @@ class YfData(metaclass=SingletonMeta):
base_args = {
'headers': self.user_agent_headers,
'proxies': proxy,
'timeout': timeout}
get_args = {**base_args, 'url': 'https://guce.yahoo.com/consent'}
@@ -291,21 +307,20 @@ class YfData(metaclass=SingletonMeta):
return True
@utils.log_indent_decorator
def _get_crumb_csrf(self, proxy=None, timeout=30):
def _get_crumb_csrf(self, timeout=30):
# Credit goes to @bot-unit #1729
if self._crumb is not None:
utils.get_yf_logger().debug('reusing crumb')
return self._crumb
if not self._get_cookie_csrf(proxy, timeout):
if not self._get_cookie_csrf(timeout):
# This cookie stored in session
return None
get_args = {
'url': 'https://query2.finance.yahoo.com/v1/test/getcrumb',
'headers': self.user_agent_headers,
'proxies': proxy,
'timeout': timeout}
if self._session_is_caching:
get_args['expire_after'] = self._expire_after
@@ -322,7 +337,7 @@ class YfData(metaclass=SingletonMeta):
return self._crumb
@utils.log_indent_decorator
def _get_cookie_and_crumb(self, proxy=None, timeout=30):
def _get_cookie_and_crumb(self, timeout=30):
cookie, crumb, strategy = None, None, None
utils.get_yf_logger().debug(f"cookie_mode = '{self._cookie_strategy}'")
@@ -333,10 +348,10 @@ class YfData(metaclass=SingletonMeta):
if crumb is None:
# Fail
self._set_cookie_strategy('basic', have_lock=True)
cookie, crumb = self._get_cookie_and_crumb_basic(proxy, timeout)
cookie, crumb = self._get_cookie_and_crumb_basic(timeout)
else:
# Fallback strategy
cookie, crumb = self._get_cookie_and_crumb_basic(proxy, timeout)
cookie, crumb = self._get_cookie_and_crumb_basic(timeout)
if cookie is None or crumb is None:
# Fail
self._set_cookie_strategy('csrf', have_lock=True)
@@ -345,15 +360,15 @@ class YfData(metaclass=SingletonMeta):
return cookie, crumb, strategy
@utils.log_indent_decorator
def get(self, url, user_agent_headers=None, params=None, proxy=None, timeout=30):
return self._make_request(url, request_method = self._session.get, user_agent_headers=user_agent_headers, params=params, proxy=proxy, timeout=timeout)
def get(self, url, user_agent_headers=None, params=None, timeout=30):
return self._make_request(url, request_method = self._session.get, user_agent_headers=user_agent_headers, params=params, timeout=timeout)
@utils.log_indent_decorator
def post(self, url, body, user_agent_headers=None, params=None, proxy=None, timeout=30):
return self._make_request(url, request_method = self._session.post, user_agent_headers=user_agent_headers, body=body, params=params, proxy=proxy, timeout=timeout)
def post(self, url, body, user_agent_headers=None, params=None, timeout=30):
return self._make_request(url, request_method = self._session.post, user_agent_headers=user_agent_headers, body=body, params=params, timeout=timeout)
@utils.log_indent_decorator
def _make_request(self, url, request_method, user_agent_headers=None, body=None, params=None, proxy=None, timeout=30):
def _make_request(self, url, request_method, user_agent_headers=None, body=None, params=None, timeout=30):
# Important: treat input arguments as immutable.
if len(url) > 200:
@@ -361,7 +376,6 @@ class YfData(metaclass=SingletonMeta):
else:
utils.get_yf_logger().debug(f'url={url}')
utils.get_yf_logger().debug(f'params={params}')
proxy = self._get_proxy(proxy)
if params is None:
params = {}
@@ -383,7 +397,6 @@ class YfData(metaclass=SingletonMeta):
'url': url,
'params': {**params, **crumbs},
'cookies': cookies,
'proxies': proxy,
'timeout': timeout,
'headers': user_agent_headers or self.user_agent_headers
}
@@ -399,7 +412,7 @@ class YfData(metaclass=SingletonMeta):
self._set_cookie_strategy('csrf')
else:
self._set_cookie_strategy('basic')
cookie, crumb, strategy = self._get_cookie_and_crumb(proxy, timeout)
cookie, crumb, strategy = self._get_cookie_and_crumb(timeout)
request_args['params']['crumb'] = crumb
if strategy == 'basic':
request_args['cookies'] = {cookie.name: cookie.value}
@@ -414,19 +427,11 @@ class YfData(metaclass=SingletonMeta):
@lru_cache_freezeargs
@lru_cache(maxsize=cache_maxsize)
def cache_get(self, url, user_agent_headers=None, params=None, proxy=None, timeout=30):
return self.get(url, user_agent_headers, params, proxy, timeout)
def cache_get(self, url, user_agent_headers=None, params=None, timeout=30):
return self.get(url, user_agent_headers, params, timeout)
def _get_proxy(self, proxy):
# setup proxy in requests format
if proxy is not None:
if isinstance(proxy, (dict, frozendict)) and "https" in proxy:
proxy = proxy["https"]
proxy = {"https": proxy}
return proxy
def get_raw_json(self, url, user_agent_headers=None, params=None, proxy=None, timeout=30):
def get_raw_json(self, url, user_agent_headers=None, params=None, timeout=30):
utils.get_yf_logger().debug(f'get_raw_json(): {url}')
response = self.get(url, user_agent_headers=user_agent_headers, params=params, proxy=proxy, timeout=timeout)
response = self.get(url, user_agent_headers=user_agent_headers, params=params, timeout=timeout)
response.raise_for_status()
return response.json()

View File

@@ -1,7 +1,8 @@
from abc import ABC, abstractmethod
from ..ticker import Ticker
from ..const import _QUERY1_URL_
from ..const import _QUERY1_URL_, _SENTINEL_
from ..data import YfData
from ..utils import print_once
from typing import Dict, List, Optional
import pandas as _pd
@@ -13,20 +14,21 @@ class Domain(ABC):
and methods for fetching and parsing data. Derived classes must implement the `_fetch_and_parse()` method.
"""
def __init__(self, key: str, session=None, proxy=None):
def __init__(self, key: str, session=None, proxy=_SENTINEL_):
"""
Initializes the Domain object with a key, session, and proxy.
Args:
key (str): Unique key identifying the domain entity.
session (Optional[requests.Session]): Session object for HTTP requests. Defaults to None.
proxy (Optional[Dict]): Proxy settings. Defaults to None.
"""
self._key: str = key
self.proxy = proxy
self.session = session
self._data: YfData = YfData(session=session)
if proxy is not _SENTINEL_:
print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
self._name: Optional[str] = None
self._symbol: Optional[str] = None
self._overview: Optional[Dict] = None
@@ -109,19 +111,18 @@ class Domain(ABC):
self._ensure_fetched(self._research_reports)
return self._research_reports
def _fetch(self, query_url, proxy) -> Dict:
def _fetch(self, query_url) -> Dict:
"""
Fetches data from the given query URL.
Args:
query_url (str): The URL used for the data query.
proxy (Dict): Proxy settings for the request.
Returns:
Dict: The JSON response data from the request.
"""
params_dict = {"formatted": "true", "withReturns": "true", "lang": "en-US", "region": "US"}
result = self._data.get_raw_json(query_url, user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
result = self._data.get_raw_json(query_url, user_agent_headers=self._data.user_agent_headers, params=params_dict)
return result
def _parse_and_assign_common(self, data) -> None:
@@ -194,4 +195,4 @@ class Domain(ABC):
attribute: The attribute to check and potentially fetch.
"""
if attribute is None:
self._fetch_and_parse()
self._fetch_and_parse()

View File

@@ -5,20 +5,24 @@ import pandas as _pd
from .domain import Domain, _QUERY_URL_
from .. import utils
from ..data import YfData
from ..const import _SENTINEL_
class Industry(Domain):
"""
Represents an industry within a sector.
"""
def __init__(self, key, session=None, proxy=None):
def __init__(self, key, session=None, proxy=_SENTINEL_):
"""
Args:
key (str): The key identifier for the industry.
session (optional): The session to use for requests.
proxy (optional): The proxy to use for requests.
"""
super(Industry, self).__init__(key, session, proxy)
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
YfData(session=session, proxy=proxy)
super(Industry, self).__init__(key, session)
self._query_url = f'{_QUERY_URL_}/industries/{self._key}'
self._sector_key = None
@@ -129,7 +133,7 @@ class Industry(Domain):
result = None
try:
result = self._fetch(self._query_url, self.proxy)
result = self._fetch(self._query_url)
data = result['data']
self._parse_and_assign_common(data)
@@ -145,4 +149,4 @@ class Industry(Domain):
logger.debug("Got response: ")
logger.debug("-------------")
logger.debug(f" {result}")
logger.debug("-------------")
logger.debug("-------------")

View File

@@ -2,24 +2,27 @@ import datetime as dt
from ..data import YfData
from ..data import utils
from ..const import _QUERY1_URL_
from ..const import _QUERY1_URL_, _SENTINEL_
import json as _json
class Market:
def __init__(self, market:'str', session=None, proxy=None, timeout=30):
def __init__(self, market:'str', session=None, proxy=_SENTINEL_, timeout=30):
self.market = market
self.session = session
self.proxy = proxy
self.timeout = timeout
self._data = YfData(session=self.session)
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
self._logger = utils.get_yf_logger()
self._status = None
self._summary = None
def _fetch_json(self, url, params):
data = self._data.cache_get(url=url, params=params, proxy=self.proxy, timeout=self.timeout)
data = self._data.cache_get(url=url, params=params, timeout=self.timeout)
if data is None or "Will be right back" in data.text:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "

View File

@@ -1,12 +1,13 @@
from __future__ import print_function
from typing import Dict, Optional
from ..utils import dynamic_docstring, generate_list_table_from_dict
from ..const import SECTOR_INDUSTY_MAPPING
from ..const import SECTOR_INDUSTY_MAPPING, _SENTINEL_
import pandas as _pd
from .domain import Domain, _QUERY_URL_
from .. import utils
from ..data import YfData
class Sector(Domain):
"""
@@ -14,7 +15,7 @@ class Sector(Domain):
such as top ETFs, top mutual funds, and industry data.
"""
def __init__(self, key, session=None, proxy=None):
def __init__(self, key, session=None, proxy=_SENTINEL_):
"""
Args:
key (str): The key representing the sector.
@@ -26,7 +27,11 @@ class Sector(Domain):
:attr:`Sector.industries <yfinance.Sector.industries>`
Map of sector and industry
"""
super(Sector, self).__init__(key, session, proxy)
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
YfData(session=session, proxy=proxy)
super(Sector, self).__init__(key, session)
self._query_url: str = f'{_QUERY_URL_}/sectors/{self._key}'
self._top_etfs: Optional[Dict] = None
self._top_mutual_funds: Optional[Dict] = None
@@ -133,7 +138,7 @@ class Sector(Domain):
result = None
try:
result = self._fetch(self._query_url, self.proxy)
result = self._fetch(self._query_url)
data = result['data']
self._parse_and_assign_common(data)
@@ -147,4 +152,4 @@ class Sector(Domain):
logger.debug("Got response: ")
logger.debug("-------------")
logger.debug(f" {result}")
logger.debug("-------------")
logger.debug("-------------")

View File

@@ -32,13 +32,13 @@ import pandas as _pd
from . import Ticker, utils
from .data import YfData
from . import shared
from .const import _SENTINEL_
@utils.log_indent_decorator
def download(tickers, start=None, end=None, actions=False, threads=True,
ignore_tz=None, group_by='column', auto_adjust=None, back_adjust=False,
repair=False, keepna=False, progress=True, period="max", interval="1d",
prepost=False, proxy=None, rounding=False, timeout=10, session=None,
prepost=False, proxy=_SENTINEL_, rounding=False, timeout=10, session=None,
multi_level_index=True) -> Union[_pd.DataFrame, None]:
"""
Download yahoo tickers
@@ -79,8 +79,6 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
ignore_tz: bool
When combining from different timezones, ignore that part of datetime.
Default depends on interval. Intraday = False. Day+ = True.
proxy: str
Optional. Proxy server URL scheme. Default is None
rounding: bool
Optional. Round values to 2 decimal places?
timeout: None or float
@@ -127,7 +125,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
for ticker in tickers:
if utils.is_isin(ticker):
isin = ticker
ticker = utils.get_ticker_by_isin(ticker, proxy, session=session)
ticker = utils.get_ticker_by_isin(ticker, session=session)
shared._ISINS[ticker] = isin
_tickers_.append(ticker)
@@ -144,7 +142,11 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
shared._TRACEBACKS = {}
# Ensure data initialised with session.
YfData(session=session)
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
YfData(session=session, proxy=proxy)
else:
YfData(session=session)
# download using threads
if threads:
@@ -156,7 +158,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
start=start, end=end, prepost=prepost,
actions=actions, auto_adjust=auto_adjust,
back_adjust=back_adjust, repair=repair, keepna=keepna,
progress=(progress and i > 0), proxy=proxy,
progress=(progress and i > 0),
rounding=rounding, timeout=timeout)
while len(shared._DFS) < len(tickers):
_time.sleep(0.01)
@@ -167,7 +169,6 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
start=start, end=end, prepost=prepost,
actions=actions, auto_adjust=auto_adjust,
back_adjust=back_adjust, repair=repair, keepna=keepna,
proxy=proxy,
rounding=rounding, timeout=timeout)
if progress:
shared._PROGRESS_BAR.animate()
@@ -258,10 +259,10 @@ def _realign_dfs():
def _download_one_threaded(ticker, start=None, end=None,
auto_adjust=False, back_adjust=False, repair=False,
actions=False, progress=True, period="max",
interval="1d", prepost=False, proxy=None,
interval="1d", prepost=False,
keepna=False, rounding=False, timeout=10):
_download_one(ticker, start, end, auto_adjust, back_adjust, repair,
actions, period, interval, prepost, proxy, rounding,
actions, period, interval, prepost, rounding,
keepna, timeout)
if progress:
shared._PROGRESS_BAR.animate()
@@ -270,7 +271,7 @@ def _download_one_threaded(ticker, start=None, end=None,
def _download_one(ticker, start=None, end=None,
auto_adjust=False, back_adjust=False, repair=False,
actions=False, period="max", interval="1d",
prepost=False, proxy=None, rounding=False,
prepost=False, rounding=False,
keepna=False, timeout=10):
data = None
try:
@@ -278,7 +279,7 @@ def _download_one(ticker, start=None, end=None,
period=period, interval=interval,
start=start, end=end, prepost=prepost,
actions=actions, auto_adjust=auto_adjust,
back_adjust=back_adjust, repair=repair, proxy=proxy,
back_adjust=back_adjust, repair=repair,
rounding=rounding, keepna=keepna, timeout=timeout,
raise_errors=True
)

View File

@@ -3,17 +3,19 @@ import requests
from yfinance import utils
from yfinance.data import YfData
from yfinance.const import quote_summary_valid_modules
from yfinance.const import quote_summary_valid_modules, _SENTINEL_
from yfinance.scrapers.quote import _QUOTE_SUMMARY_URL_
from yfinance.exceptions import YFException
class Analysis:
def __init__(self, data: YfData, symbol: str, proxy=None):
def __init__(self, data: YfData, symbol: str, proxy=_SENTINEL_):
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
data._set_proxy(proxy)
self._data = data
self._symbol = symbol
self.proxy = proxy
# In quoteSummary the 'earningsTrend' module contains most of the data below.
# The format of data is not optimal so each function will process it's part of the data.
@@ -175,7 +177,7 @@ class Analysis:
raise YFException("No valid modules provided, see available modules using `valid_modules`")
params_dict = {"modules": modules, "corsDomain": "finance.yahoo.com", "formatted": "false", "symbol": self._symbol}
try:
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_ + f"/{self._symbol}", user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=self.proxy)
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_ + f"/{self._symbol}", user_agent_headers=self._data.user_agent_headers, params=params_dict)
except requests.exceptions.HTTPError as e:
utils.get_yf_logger().error(str(e))
return None

View File

@@ -10,10 +10,13 @@ from yfinance.exceptions import YFException, YFNotImplementedError
class Fundamentals:
def __init__(self, data: YfData, symbol: str, proxy=None):
def __init__(self, data: YfData, symbol: str, proxy=const._SENTINEL_):
if proxy is not const._SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
data._set_proxy(proxy)
self._data = data
self._symbol = symbol
self.proxy = proxy
self._earnings = None
self._financials = None
@@ -48,26 +51,38 @@ class Financials:
self._balance_sheet_time_series = {}
self._cash_flow_time_series = {}
def get_income_time_series(self, freq="yearly", proxy=None) -> pd.DataFrame:
def get_income_time_series(self, freq="yearly", proxy=const._SENTINEL_) -> pd.DataFrame:
if proxy is not const._SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
res = self._income_time_series
if freq not in res:
res[freq] = self._fetch_time_series("income", freq, proxy)
res[freq] = self._fetch_time_series("income", freq)
return res[freq]
def get_balance_sheet_time_series(self, freq="yearly", proxy=None) -> pd.DataFrame:
def get_balance_sheet_time_series(self, freq="yearly", proxy=const._SENTINEL_) -> pd.DataFrame:
if proxy is not const._SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
res = self._balance_sheet_time_series
if freq not in res:
res[freq] = self._fetch_time_series("balance-sheet", freq, proxy)
res[freq] = self._fetch_time_series("balance-sheet", freq)
return res[freq]
def get_cash_flow_time_series(self, freq="yearly", proxy=None) -> pd.DataFrame:
def get_cash_flow_time_series(self, freq="yearly", proxy=const._SENTINEL_) -> pd.DataFrame:
if proxy is not const._SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
res = self._cash_flow_time_series
if freq not in res:
res[freq] = self._fetch_time_series("cash-flow", freq, proxy)
res[freq] = self._fetch_time_series("cash-flow", freq)
return res[freq]
@utils.log_indent_decorator
def _fetch_time_series(self, name, timescale, proxy=None):
def _fetch_time_series(self, name, timescale):
# Fetching time series preferred over scraping 'QuoteSummaryStore',
# because it matches what Yahoo shows. But for some tickers returns nothing,
# despite 'QuoteSummaryStore' containing valid data.
@@ -84,7 +99,7 @@ class Financials:
" only available for cash-flow or income data.")
try:
statement = self._create_financials_table(name, timescale, proxy)
statement = self._create_financials_table(name, timescale)
if statement is not None:
return statement
@@ -92,7 +107,7 @@ class Financials:
utils.get_yf_logger().error(f"{self._symbol}: Failed to create {name} financials table for reason: {e}")
return pd.DataFrame()
def _create_financials_table(self, name, timescale, proxy):
def _create_financials_table(self, name, timescale):
if name == "income":
# Yahoo stores the 'income' table internally under 'financials' key
name = "financials"
@@ -100,11 +115,11 @@ class Financials:
keys = const.fundamentals_keys[name]
try:
return self.get_financials_time_series(timescale, keys, proxy)
return self._get_financials_time_series(timescale, keys)
except Exception:
pass
def get_financials_time_series(self, timescale, keys: list, proxy=None) -> pd.DataFrame:
def _get_financials_time_series(self, timescale, keys: list) -> pd.DataFrame:
timescale_translation = {"yearly": "annual", "quarterly": "quarterly", "trailing": "trailing"}
timescale = timescale_translation[timescale]
@@ -117,7 +132,7 @@ class Financials:
url += f"&period1={int(start_dt.timestamp())}&period2={int(end.timestamp())}"
# Step 3: fetch and reshape data
json_str = self._data.cache_get(url=url, proxy=proxy).text
json_str = self._data.cache_get(url=url).text
json_data = json.loads(json_str)
data_raw = json_data["timeseries"]["result"]
# data_raw = [v for v in data_raw if len(v) > 1] # Discard keys with no data

View File

@@ -1,7 +1,7 @@
import pandas as pd
from yfinance.data import YfData
from yfinance.const import _BASE_URL_
from yfinance.const import _BASE_URL_, _SENTINEL_
from yfinance.exceptions import YFDataException
from yfinance import utils
@@ -17,16 +17,17 @@ class FundsData:
Notes:
- fundPerformance module is not implemented as better data is queryable using history
"""
def __init__(self, data: YfData, symbol: str, proxy=None):
def __init__(self, data: YfData, symbol: str, proxy=_SENTINEL_):
"""
Args:
data (YfData): The YfData object for fetching data.
symbol (str): The symbol of the fund.
proxy (optional): Proxy settings for fetching data.
"""
self._data = data
self._symbol = symbol
self.proxy = proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
# quoteType
self._quote_type = None
@@ -165,26 +166,23 @@ class FundsData:
self._fetch_and_parse()
return self._sector_weightings
def _fetch(self, proxy):
def _fetch(self):
"""
Fetches the raw JSON data from the API.
Args:
proxy: Proxy settings for fetching data.
Returns:
dict: The raw JSON data.
"""
modules = ','.join(["quoteType", "summaryProfile", "topHoldings", "fundProfile"])
params_dict = {"modules": modules, "corsDomain": "finance.yahoo.com", "symbol": self._symbol, "formatted": "false"}
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_+self._symbol, user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_+self._symbol, user_agent_headers=self._data.user_agent_headers, params=params_dict)
return result
def _fetch_and_parse(self) -> None:
"""
Fetches and parses the data from the API.
"""
result = self._fetch(self.proxy)
result = self._fetch()
try:
data = result["quoteSummary"]["result"][0]
# check quote type
@@ -334,4 +332,4 @@ class FundsData:
self._parse_raw_values(_fund_operations_cat.get("annualHoldingsTurnover", pd.NA)),
self._parse_raw_values(_fund_operations_cat.get("totalNetAssets", pd.NA))
]
}).set_index("Attributes")
}).set_index("Attributes")

View File

@@ -8,15 +8,17 @@ import time as _time
import bisect
from yfinance import shared, utils
from yfinance.const import _BASE_URL_, _PRICE_COLNAMES_
from yfinance.const import _BASE_URL_, _PRICE_COLNAMES_, _SENTINEL_
from yfinance.exceptions import YFInvalidPeriodError, YFPricesMissingError, YFTzMissingError, YFRateLimitError
class PriceHistory:
def __init__(self, data, ticker, tz, session=None, proxy=None):
def __init__(self, data, ticker, tz, session=None, proxy=_SENTINEL_):
self._data = data
self.ticker = ticker.upper()
self.tz = tz
self.proxy = proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
self.session = session
self._history_cache = {}
@@ -30,7 +32,7 @@ class PriceHistory:
def history(self, period="1mo", interval="1d",
start=None, end=None, prepost=False, actions=True,
auto_adjust=True, back_adjust=False, repair=False, keepna=False,
proxy=None, rounding=False, timeout=10,
proxy=_SENTINEL_, rounding=False, timeout=10,
raise_errors=False) -> pd.DataFrame:
"""
:Parameters:
@@ -61,8 +63,6 @@ class PriceHistory:
keepna: bool
Keep NaN rows returned by Yahoo?
Default is False
proxy: str
Optional. Proxy server URL scheme. Default is None
rounding: bool
Round values to 2 decimal places?
Optional. Default is False = precision suggested by Yahoo!
@@ -74,7 +74,10 @@ class PriceHistory:
If True, then raise errors as Exceptions instead of logging.
"""
logger = utils.get_yf_logger()
proxy = proxy or self.proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
interval_user = interval
period_user = period
@@ -175,7 +178,6 @@ class PriceHistory:
data = get_fn(
url=url,
params=params,
proxy=proxy,
timeout=timeout
)
if "Will be right back" in data.text or data is None:
@@ -464,19 +466,23 @@ class PriceHistory:
self._reconstruct_start_interval = None
return df
def _get_history_cache(self, period="max", interval="1d", proxy=None) -> pd.DataFrame:
def _get_history_cache(self, period="max", interval="1d") -> pd.DataFrame:
cache_key = (interval, period)
if cache_key in self._history_cache:
return self._history_cache[cache_key]
df = self.history(period=period, interval=interval, prepost=True, proxy=proxy)
df = self.history(period=period, interval=interval, prepost=True)
self._history_cache[cache_key] = df
return df
def get_history_metadata(self, proxy=None) -> dict:
def get_history_metadata(self, proxy=_SENTINEL_) -> dict:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
if self._history_metadata is None or 'tradingPeriods' not in self._history_metadata:
# Request intraday data, because then Yahoo returns exchange schedule (tradingPeriods).
self._get_history_cache(period="5d", interval="1h", proxy=proxy)
self._get_history_cache(period="5d", interval="1h")
if self._history_metadata_formatted is False:
self._history_metadata = utils.format_history_metadata(self._history_metadata)
@@ -484,29 +490,45 @@ class PriceHistory:
return self._history_metadata
def get_dividends(self, period="max", proxy=None) -> pd.Series:
df = self._get_history_cache(period=period, proxy=proxy)
def get_dividends(self, period="max", proxy=_SENTINEL_) -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
df = self._get_history_cache(period=period)
if "Dividends" in df.columns:
dividends = df["Dividends"]
return dividends[dividends != 0]
return pd.Series()
def get_capital_gains(self, period="max", proxy=None) -> pd.Series:
df = self._get_history_cache(period=period, proxy=proxy)
def get_capital_gains(self, period="max", proxy=_SENTINEL_) -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
df = self._get_history_cache(period=period)
if "Capital Gains" in df.columns:
capital_gains = df["Capital Gains"]
return capital_gains[capital_gains != 0]
return pd.Series()
def get_splits(self, period="max", proxy=None) -> pd.Series:
df = self._get_history_cache(period=period, proxy=proxy)
def get_splits(self, period="max", proxy=_SENTINEL_) -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
df = self._get_history_cache(period=period)
if "Stock Splits" in df.columns:
splits = df["Stock Splits"]
return splits[splits != 0]
return pd.Series()
def get_actions(self, period="max", proxy=None) -> pd.Series:
df = self._get_history_cache(period=period, proxy=proxy)
def get_actions(self, period="max", proxy=_SENTINEL_) -> pd.Series:
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
df = self._get_history_cache(period=period)
action_columns = []
if "Dividends" in df.columns:

View File

@@ -3,19 +3,20 @@ import requests
from yfinance import utils
from yfinance.data import YfData
from yfinance.const import _BASE_URL_
from yfinance.const import _BASE_URL_, _SENTINEL_
from yfinance.exceptions import YFDataException
_QUOTE_SUMMARY_URL_ = f"{_BASE_URL_}/v10/finance/quoteSummary"
class Holders:
_SCRAPE_URL_ = 'https://finance.yahoo.com/quote'
def __init__(self, data: YfData, symbol: str, proxy=None):
def __init__(self, data: YfData, symbol: str, proxy=_SENTINEL_):
self._data = data
self._symbol = symbol
self.proxy = proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
data._set_proxy(proxy)
self._major = None
self._major_direct_holders = None
@@ -62,16 +63,16 @@ class Holders:
self._fetch_and_parse()
return self._insider_roster
def _fetch(self, proxy):
def _fetch(self):
modules = ','.join(
["institutionOwnership", "fundOwnership", "majorDirectHolders", "majorHoldersBreakdown", "insiderTransactions", "insiderHolders", "netSharePurchaseActivity"])
params_dict = {"modules": modules, "corsDomain": "finance.yahoo.com", "formatted": "false"}
result = self._data.get_raw_json(f"{_QUOTE_SUMMARY_URL_}/{self._symbol}", user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
result = self._data.get_raw_json(f"{_QUOTE_SUMMARY_URL_}/{self._symbol}", user_agent_headers=self._data.user_agent_headers, params=params_dict)
return result
def _fetch_and_parse(self):
try:
result = self._fetch(self.proxy)
result = self._fetch()
except requests.exceptions.HTTPError as e:
utils.get_yf_logger().error(str(e))

View File

@@ -7,7 +7,7 @@ import requests
from yfinance import utils
from yfinance.data import YfData
from yfinance.const import quote_summary_valid_modules, _BASE_URL_, _QUERY1_URL_
from yfinance.const import quote_summary_valid_modules, _BASE_URL_, _QUERY1_URL_, _SENTINEL_
from yfinance.exceptions import YFDataException, YFException
info_retired_keys_price = {"currentPrice", "dayHigh", "dayLow", "open", "previousClose", "volume", "volume24Hr"}
@@ -26,9 +26,11 @@ _QUOTE_SUMMARY_URL_ = f"{_BASE_URL_}/v10/finance/quoteSummary"
class FastInfo:
# Contain small subset of info[] items that can be fetched faster elsewhere.
# Imitates a dict.
def __init__(self, tickerBaseObject, proxy=None):
def __init__(self, tickerBaseObject, proxy=_SENTINEL_):
self._tkr = tickerBaseObject
self.proxy = proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._tkr._data._set_proxy(proxy)
self._prices_1y = None
self._prices_1wk_1h_prepost = None
@@ -128,8 +130,8 @@ class FastInfo:
def _get_1y_prices(self, fullDaysOnly=False):
if self._prices_1y is None:
self._prices_1y = self._tkr.history(period="1y", auto_adjust=False, keepna=True, proxy=self.proxy)
self._md = self._tkr.get_history_metadata(proxy=self.proxy)
self._prices_1y = self._tkr.history(period="1y", auto_adjust=False, keepna=True)
self._md = self._tkr.get_history_metadata()
try:
ctp = self._md["currentTradingPeriod"]
self._today_open = pd.to_datetime(ctp["regular"]["start"], unit='s', utc=True).tz_convert(self.timezone)
@@ -154,12 +156,12 @@ class FastInfo:
def _get_1wk_1h_prepost_prices(self):
if self._prices_1wk_1h_prepost is None:
self._prices_1wk_1h_prepost = self._tkr.history(period="5d", interval="1h", auto_adjust=False, prepost=True, proxy=self.proxy)
self._prices_1wk_1h_prepost = self._tkr.history(period="5d", interval="1h", auto_adjust=False, prepost=True)
return self._prices_1wk_1h_prepost
def _get_1wk_1h_reg_prices(self):
if self._prices_1wk_1h_reg is None:
self._prices_1wk_1h_reg = self._tkr.history(period="5d", interval="1h", auto_adjust=False, prepost=False, proxy=self.proxy)
self._prices_1wk_1h_reg = self._tkr.history(period="5d", interval="1h", auto_adjust=False, prepost=False)
return self._prices_1wk_1h_reg
def _get_exchange_metadata(self):
@@ -167,7 +169,7 @@ class FastInfo:
return self._md
self._get_1y_prices()
self._md = self._tkr.get_history_metadata(proxy=self.proxy)
self._md = self._tkr.get_history_metadata()
return self._md
def _exchange_open_now(self):
@@ -198,7 +200,7 @@ class FastInfo:
if self._currency is not None:
return self._currency
md = self._tkr.get_history_metadata(proxy=self.proxy)
md = self._tkr.get_history_metadata()
self._currency = md["currency"]
return self._currency
@@ -207,7 +209,7 @@ class FastInfo:
if self._quote_type is not None:
return self._quote_type
md = self._tkr.get_history_metadata(proxy=self.proxy)
md = self._tkr.get_history_metadata()
self._quote_type = md["instrumentType"]
return self._quote_type
@@ -232,7 +234,7 @@ class FastInfo:
if self._shares is not None:
return self._shares
shares = self._tkr.get_shares_full(start=pd.Timestamp.utcnow().date()-pd.Timedelta(days=548), proxy=self.proxy)
shares = self._tkr.get_shares_full(start=pd.Timestamp.utcnow().date()-pd.Timedelta(days=548))
# if shares is None:
# # Requesting 18 months failed, so fallback to shares which should include last year
# shares = self._tkr.get_shares()
@@ -484,11 +486,12 @@ class FastInfo:
class Quote:
def __init__(self, data: YfData, symbol: str, proxy=None):
def __init__(self, data: YfData, symbol: str, proxy=_SENTINEL_):
self._data = data
self._symbol = symbol
self.proxy = proxy
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
self._info = None
self._retired_info = None
@@ -505,15 +508,15 @@ class Quote:
@property
def info(self) -> dict:
if self._info is None:
self._fetch_info(self.proxy)
self._fetch_complementary(self.proxy)
self._fetch_info()
self._fetch_complementary()
return self._info
@property
def sustainability(self) -> pd.DataFrame:
if self._sustainability is None:
result = self._fetch(self.proxy, modules=['esgScores'])
result = self._fetch(modules=['esgScores'])
if result is None:
self._sustainability = pd.DataFrame()
else:
@@ -527,7 +530,7 @@ class Quote:
@property
def recommendations(self) -> pd.DataFrame:
if self._recommendations is None:
result = self._fetch(self.proxy, modules=['recommendationTrend'])
result = self._fetch(modules=['recommendationTrend'])
if result is None:
self._recommendations = pd.DataFrame()
else:
@@ -541,7 +544,7 @@ class Quote:
@property
def upgrades_downgrades(self) -> pd.DataFrame:
if self._upgrades_downgrades is None:
result = self._fetch(self.proxy, modules=['upgradeDowngradeHistory'])
result = self._fetch(modules=['upgradeDowngradeHistory'])
if result is None:
self._upgrades_downgrades = pd.DataFrame()
else:
@@ -575,7 +578,7 @@ class Quote:
def valid_modules():
return quote_summary_valid_modules
def _fetch(self, proxy, modules: list):
def _fetch(self, modules: list):
if not isinstance(modules, list):
raise YFException("Should provide a list of modules, see available modules using `valid_modules`")
@@ -584,30 +587,30 @@ class Quote:
raise YFException("No valid modules provided, see available modules using `valid_modules`")
params_dict = {"modules": modules, "corsDomain": "finance.yahoo.com", "formatted": "false", "symbol": self._symbol}
try:
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_ + f"/{self._symbol}", user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_ + f"/{self._symbol}", user_agent_headers=self._data.user_agent_headers, params=params_dict)
except requests.exceptions.HTTPError as e:
utils.get_yf_logger().error(str(e))
return None
return result
def _fetch_additional_info(self, proxy):
def _fetch_additional_info(self):
params_dict = {"symbols": self._symbol, "formatted": "false"}
try:
result = self._data.get_raw_json(f"{_QUERY1_URL_}/v7/finance/quote?",
user_agent_headers=self._data.user_agent_headers,
params=params_dict, proxy=proxy)
params=params_dict)
except requests.exceptions.HTTPError as e:
utils.get_yf_logger().error(str(e))
return None
return result
def _fetch_info(self, proxy):
def _fetch_info(self):
if self._already_fetched:
return
self._already_fetched = True
modules = ['financialData', 'quoteType', 'defaultKeyStatistics', 'assetProfile', 'summaryDetail']
result = self._fetch(proxy, modules=modules)
additional_info = self._fetch_additional_info(proxy)
result = self._fetch(modules=modules)
additional_info = self._fetch_additional_info()
if additional_info is not None and result is not None:
result.update(additional_info)
else:
@@ -658,13 +661,12 @@ class Quote:
self._info = {k: _format(k, v) for k, v in query1_info.items()}
def _fetch_complementary(self, proxy):
def _fetch_complementary(self):
if self._already_fetched_complementary:
return
self._already_fetched_complementary = True
# self._scrape(proxy) # decrypt broken
self._fetch_info(proxy)
self._fetch_info()
if self._info is None:
return
@@ -703,7 +705,7 @@ class Quote:
end = int(end.timestamp())
url += f"&period1={start}&period2={end}"
json_str = self._data.cache_get(url=url, proxy=proxy).text
json_str = self._data.cache_get(url=url).text
json_data = json.loads(json_str)
json_result = json_data.get("timeseries") or json_data.get("finance")
if json_result["error"] is not None:
@@ -717,7 +719,7 @@ class Quote:
def _fetch_calendar(self):
# secFilings return too old data, so not requesting it for now
result = self._fetch(self.proxy, modules=['calendarEvents'])
result = self._fetch(modules=['calendarEvents'])
if result is None:
self._calendar = {}
return
@@ -744,7 +746,7 @@ class Quote:
def _fetch_sec_filings(self):
result = self._fetch(self.proxy, modules=['secFilings'])
result = self._fetch(modules=['secFilings'])
if result is None:
return None

View File

@@ -2,10 +2,10 @@ from .query import EquityQuery as EqyQy
from .query import FundQuery as FndQy
from .query import QueryBase, EquityQuery, FundQuery
from yfinance.const import _BASE_URL_
from yfinance.const import _BASE_URL_, _SENTINEL_
from yfinance.data import YfData
from ..utils import dynamic_docstring, generate_list_table_from_dict_universal
from ..utils import dynamic_docstring, generate_list_table_from_dict_universal, print_once
from typing import Union
import requests
@@ -58,7 +58,7 @@ def screen(query: Union[str, EquityQuery, FundQuery],
sortAsc: bool = None,
userId: str = None,
userIdType: str = None,
session = None, proxy = None):
session = None, proxy = _SENTINEL_):
"""
Run a screen: predefined query, or custom query.
@@ -106,6 +106,12 @@ def screen(query: Union[str, EquityQuery, FundQuery],
{predefined_screeners}
"""
if proxy is not _SENTINEL_:
print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
_data = YfData(session=session, proxy=proxy)
else:
_data = YfData(session=session)
# Only use defaults when user NOT give a predefined, because
# Yahoo's predefined endpoint auto-applies defaults. Also,
# that endpoint might be ignoring these fields.
@@ -121,10 +127,7 @@ def screen(query: Union[str, EquityQuery, FundQuery],
if size is not None and size > 250:
raise ValueError("Yahoo limits query size to 250, reduce size.")
fields = dict(locals())
for k in ['query', 'session', 'proxy']:
if k in fields:
del fields[k]
fields = {'offset': offset, 'size': size, 'sortField': sortField, 'sortAsc': sortAsc, 'userId': userId, 'userIdType': userIdType}
params_dict = {"corsDomain": "finance.yahoo.com", "formatted": "false", "lang": "en-US", "region": "US"}
@@ -132,12 +135,11 @@ def screen(query: Union[str, EquityQuery, FundQuery],
if isinstance(query, str):
# post_query = PREDEFINED_SCREENER_QUERIES[query]
# Switch to Yahoo's predefined endpoint
_data = YfData(session=session)
params_dict['scrIds'] = query
for k,v in fields.items():
if v is not None:
params_dict[k] = v
resp = _data.get(url=_PREDEFINED_URL_, params=params_dict, proxy=proxy)
resp = _data.get(url=_PREDEFINED_URL_, params=params_dict)
try:
resp.raise_for_status()
except requests.exceptions.HTTPError:
@@ -170,11 +172,9 @@ def screen(query: Union[str, EquityQuery, FundQuery],
post_query['query'] = post_query['query'].to_dict()
# Fetch
_data = YfData(session=session)
response = _data.post(_SCREENER_URL_,
body=post_query,
user_agent_headers=_data.user_agent_headers,
params=params_dict,
proxy=proxy)
params=params_dict)
response.raise_for_status()
return response.json()['finance']['result'][0]

View File

@@ -22,14 +22,14 @@
import json as _json
from . import utils
from .const import _BASE_URL_
from .const import _BASE_URL_, _SENTINEL_
from .data import YfData
class Search:
def __init__(self, query, max_results=8, news_count=8, lists_count=8, include_cb=True, include_nav_links=False,
include_research=False, include_cultural_assets=False, enable_fuzzy_query=False, recommended=8,
session=None, proxy=None, timeout=30, raise_errors=True):
session=None, proxy=_SENTINEL_, timeout=30, raise_errors=True):
"""
Fetches and organizes search results from Yahoo Finance, including stock quotes and news articles.
@@ -45,16 +45,18 @@ class Search:
enable_fuzzy_query: Enable fuzzy search for typos (default False).
recommended: Recommended number of results to return (default 8).
session: Custom HTTP session for requests (default None).
proxy: Proxy settings for requests (default None).
timeout: Request timeout in seconds (default 30).
raise_errors: Raise exceptions on error (default True).
"""
if proxy is not _SENTINEL_:
utils.print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
self.query = query
self.max_results = max_results
self.enable_fuzzy_query = enable_fuzzy_query
self.news_count = news_count
self.session = session
self.proxy = proxy
self.timeout = timeout
self.raise_errors = raise_errors
@@ -98,7 +100,7 @@ class Search:
self._logger.debug(f'{self.query}: Yahoo GET parameters: {str(dict(params))}')
data = self._data.cache_get(url=url, params=params, proxy=self.proxy, timeout=self.timeout)
data = self._data.cache_get(url=url, params=params, timeout=self.timeout)
if data is None or "Will be right back" in data.text:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "

View File

@@ -27,11 +27,11 @@ from .scrapers.funds import FundsData
import pandas as _pd
from .base import TickerBase
from .const import _BASE_URL_
from .const import _BASE_URL_, _SENTINEL_
class Ticker(TickerBase):
def __init__(self, ticker, session=None, proxy=None):
def __init__(self, ticker, session=None, proxy=_SENTINEL_):
super(Ticker, self).__init__(ticker, session=session, proxy=proxy)
self._expirations = {}
self._underlying = {}
@@ -45,7 +45,7 @@ class Ticker(TickerBase):
else:
url = f"{_BASE_URL_}/v7/finance/options/{self.ticker}?date={date}"
r = self._data.get(url=url, proxy=self.proxy).json()
r = self._data.get(url=url).json()
if len(r.get('optionChain', {}).get('result', [])) > 0:
for exp in r['optionChain']['result'][0]['expirationDates']:
self._expirations[_pd.Timestamp(exp, unit='s').strftime('%Y-%m-%d')] = exp
@@ -321,4 +321,4 @@ class Ticker(TickerBase):
@property
def funds_data(self) -> FundsData:
return self.get_funds_data()
return self.get_funds_data()

View File

@@ -22,9 +22,9 @@
from __future__ import print_function
from . import Ticker, multi
# from collections import namedtuple as _namedtuple
from .utils import print_once
from .data import YfData
from .const import _SENTINEL_
class Tickers:
@@ -38,6 +38,8 @@ class Tickers:
self.symbols = [ticker.upper() for ticker in tickers]
self.tickers = {ticker: Ticker(ticker, session=session) for ticker in self.symbols}
self._data = YfData(session=session)
# self.tickers = _namedtuple(
# "Tickers", ticker_objects.keys(), rename=True
# )(*ticker_objects.values())
@@ -45,25 +47,32 @@ class Tickers:
def history(self, period="1mo", interval="1d",
start=None, end=None, prepost=False,
actions=True, auto_adjust=True, repair=False,
proxy=None,
proxy=_SENTINEL_,
threads=True, group_by='column', progress=True,
timeout=10, **kwargs):
if proxy is not _SENTINEL_:
print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
return self.download(
period, interval,
start, end, prepost,
actions, auto_adjust, repair,
proxy,
threads, group_by, progress,
timeout, **kwargs)
def download(self, period="1mo", interval="1d",
start=None, end=None, prepost=False,
actions=True, auto_adjust=True, repair=False,
proxy=None,
proxy=_SENTINEL_,
threads=True, group_by='column', progress=True,
timeout=10, **kwargs):
if proxy is not _SENTINEL_:
print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
self._data._set_proxy(proxy)
data = multi.download(self.symbols,
start=start, end=end,
actions=actions,
@@ -72,7 +81,6 @@ class Tickers:
period=period,
interval=interval,
prepost=prepost,
proxy=proxy,
group_by='ticker',
threads=threads,
progress=progress,

View File

@@ -44,7 +44,6 @@ from yfinance import const
user_agent_headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
# From https://stackoverflow.com/a/59128615
def attributes(obj):
disallowed_names = {
@@ -186,10 +185,14 @@ def is_isin(string):
return bool(_re.match("^([A-Z]{2})([A-Z0-9]{9})([0-9])$", string))
def get_all_by_isin(isin, proxy=None, session=None):
def get_all_by_isin(isin, proxy=const._SENTINEL_, session=None):
if not (is_isin(isin)):
raise ValueError("Invalid ISIN number")
if proxy is not const._SENTINEL_:
print_once("YF deprecation warning: set proxy via new config function: yf.set_proxy(proxy)")
proxy = None
# Deferred this to prevent circular imports
from .search import Search
@@ -212,17 +215,17 @@ def get_all_by_isin(isin, proxy=None, session=None):
}
def get_ticker_by_isin(isin, proxy=None, session=None):
def get_ticker_by_isin(isin, proxy=const._SENTINEL_, session=None):
data = get_all_by_isin(isin, proxy, session)
return data.get('ticker', {}).get('symbol', '')
def get_info_by_isin(isin, proxy=None, session=None):
def get_info_by_isin(isin, proxy=const._SENTINEL_, session=None):
data = get_all_by_isin(isin, proxy, session)
return data.get('ticker', {})
def get_news_by_isin(isin, proxy=None, session=None):
def get_news_by_isin(isin, proxy=const._SENTINEL_, session=None):
data = get_all_by_isin(isin, proxy, session)
return data.get('news', {})