yahoo
¶
Daily history and split events from Yahoo Finance, for the adjustment factor builder.
One request per ticker: Ticker.history(period="max", auto_adjust=False, actions=True). With
auto_adjust=False, Yahoo's Close is adjusted for splits, bonuses, demergers and rights issues but
not for dividends, and Stock Splits lists the split and bonus events Yahoo knows of - 2.0 for a
two-for-one split or a 1:1 bonus, 0.3 for a consolidation. Demergers are never listed; they show only
in how Close moves against the raw price, which is what factors measures.
Tickers are tried in order: NSE instruments as SYMBOL.NS; BSE instruments as SYMBOL.BO, then as the BSE scrip code, 500325.BO, then - for a company also listed on NSE - as SYMBOL.NS. Yahoo keeps many BSE tickers only as stubs: RELIANCE.BO returns two rows, and PIDILITIND.BO and 500331.BO one and none. A form that does not cover the raw history is passed over for the next.
Dates are read straight off the index, without converting time zones: .NS and .BO histories are labelled Asia/Kolkata, but scrip code tickers come back labelled America/New_York while still carrying Indian trading dates, and converting them would move every bar back a day.
Yahoo rate limits, so requests are paced at roughly one a second with jitter, and a rate limit answer backs off rather than pressing on.
Classes:
| Name | Description |
|---|---|
YahooClient |
Paced access to Yahoo Finance daily history. |
Functions:
| Name | Description |
|---|---|
ticker_forms |
The Yahoo tickers to try for an instrument, in order. |
coverage |
The share of raw trading days a Yahoo history has a close for. |
YahooClient
¶
Paced access to Yahoo Finance daily history.
Attributes:
| Name | Type | Description |
|---|---|---|
last_request |
float
|
When the last request was sent, on the monotonic clock. |
Build a client; nothing is requested until asked.
Returns:
| Type | Description |
|---|---|
None
|
This function returns nothing. |
Example programs
"""Reads RELIANCE's daily history from Yahoo Finance through `YahooClient`, and finds its 1:1 bonus among the listed events.
`YahooClient.history` asks yfinance for one ticker's whole daily history, with prices not adjusted for dividends and with corporate actions included, and returns two dictionaries: Yahoo's close by date, and the ratio of every split or bonus Yahoo lists, by its ex-date. RELIANCE went ex for a 1:1 bonus on 2024-10-28, which Yahoo lists as a ratio of 2.0. Days without an event carry 0 in the `Stock Splits` column and are left out, and a day with no close is left out of the closes.
The example runner routes all HTTP through a dead proxy, and Yahoo must never be contacted from here, so the program replaces the `yf` module inside `yahoo` with a stand-in whose `Ticker(...).history(...)` returns a small pandas frame shaped like the one yfinance returns, labelled Asia/Kolkata as Yahoo labels `.NS` tickers. The stand-in also records the arguments it was asked with. Before the request, `pace` spaces requests about a second apart; the client has sent nothing yet, so it does not wait, and the program shows only that `last_request` moved on, never a duration. It then sets `last_request` back to 0.0, which tells the client its last request was long ago, so the history call does not wait the second that `pace` would otherwise insert after the one just shown; a real caller leaves the attribute alone.
Run it from the project root:
python examples/stock_brokers/instruments/historical/utilities/unified/yahoo/YahooClient/example_1_reliance_history_and_its_bonus.py
"""
import pandas
from stock_brokers.instruments.historical.utilities.unified import yahoo
from stock_brokers.instruments.historical.utilities.unified.yahoo import (
YahooClient,
)
class StandInTicker:
"""A stand-in for `yfinance.Ticker` that returns a recorded-looking daily frame.
Attributes:
symbol (str): The ticker asked for.
requests (list): Where each history request's arguments are recorded.
"""
def __init__(self, symbol, requests):
"""Remembers the ticker and where to record requests.
Args:
symbol (str): The ticker asked for.
requests (list): Where each history request's arguments are recorded.
Returns:
None: This method returns nothing.
"""
self.symbol = symbol
self.requests = requests
def history(self, period, auto_adjust, actions):
"""Returns five days of RELIANCE around its bonus.
Args:
period (str): How much history to return.
auto_adjust (bool): Whether to adjust prices for dividends.
actions (bool): Whether to include dividends and splits.
Returns:
pandas.DataFrame: Rows indexed by date, with Close, Dividends and Stock Splits columns.
"""
self.requests.append(f'{self.symbol} period={period} auto_adjust={auto_adjust} actions={actions}')
dates = pandas.to_datetime(
[
'2024-10-23',
'2024-10-24',
'2024-10-25',
'2024-10-28',
'2024-10-29',
]
)
frame = pandas.DataFrame(
{
'Close': [
1370.35,
1360.9,
1331.4,
1333.3,
float('nan'),
],
'Dividends': [
0.0,
0.0,
0.0,
0.0,
0.0,
],
'Stock Splits': [
0.0,
0.0,
0.0,
2.0,
0.0,
],
},
index=dates.tz_localize('Asia/Kolkata'),
)
return frame
class StandInYfinance:
"""A stand-in for the `yfinance` module, offering only `Ticker`.
Attributes:
requests (list): Every history request made through it.
"""
def __init__(self):
"""Starts with no requests.
Returns:
None: This method returns nothing.
"""
self.requests = []
def Ticker(self, symbol):
"""Builds a stand-in ticker, under the name yfinance uses.
Args:
symbol (str): The Yahoo ticker.
Returns:
StandInTicker: The ticker.
"""
return StandInTicker(symbol, self.requests)
class RelianceHistoryExample:
"""Reads RELIANCE.NS through the stand-in and prints the closes and events.
Attributes:
yfinance (StandInYfinance): The stand-in yfinance module.
client (YahooClient): The client being shown.
"""
def __init__(self):
"""Installs the stand-in and builds the client.
Returns:
None: This method returns nothing.
"""
self.yfinance = StandInYfinance()
yahoo.yf = self.yfinance
self.client = YahooClient()
def run(self):
"""Paces once, reads the history and prints it.
Returns:
None: This method returns nothing.
"""
print(f'last_request before any request: {self.client.last_request}')
self.client.pace()
print(f'last_request moved on after pace: {self.client.last_request > 0}')
self.client.last_request = 0.0
closes, events = self.client.history('RELIANCE.NS')
print(f'Requests sent: {self.yfinance.requests}')
for day, close in closes.items():
print(f' {day} close {close}')
for day, ratio in events.items():
print(f'Listed event: {day} ratio {ratio}')
if __name__ == '__main__':
RelianceHistoryExample().run()
It prints:
"""Tries the Yahoo ticker forms for a BSE share in order, passing over a stub, the way the factor builder does.
Yahoo keeps many BSE tickers only as stubs: RELIANCE.BO returns two rows. So a BSE instrument is tried as SYMBOL.BO first, then as its BSE scrip code, 500325.BO, and the first form whose closes cover at least 80% of the stored raw trading days is used. `ticker_forms` lists the forms and `coverage` measures each one; `YahooClient.history` fetches them. A ticker Yahoo has nothing for comes back as an empty frame, which `history` turns into two empty dictionaries.
Scrip code tickers come back labelled America/New_York while still carrying Indian trading dates, so `history` reads the date straight off each timestamp rather than converting it; the output shows 2024-10-28 staying 2024-10-28. The example runner routes all HTTP through a dead proxy, and Yahoo must never be contacted from here, so the program replaces the `yf` module inside `yahoo` with a stand-in whose `Ticker(...).history(...)` returns small pandas frames shaped like yfinance's. Before each request it sets the client's `last_request` back to 0.0, which tells the client its last request was long ago, so `pace` does not wait the second it would normally insert between requests; a real caller leaves the attribute alone.
Run it from the project root:
python examples/stock_brokers/instruments/historical/utilities/unified/yahoo/YahooClient/example_2_trying_ticker_forms_for_a_bse_share.py
"""
import datetime
import pandas
from stock_brokers.instruments.historical.utilities.unified import yahoo
from stock_brokers.instruments.historical.utilities.unified.yahoo import (
YahooClient,
)
class StandInTicker:
"""A stand-in for `yfinance.Ticker` that returns a recorded-looking frame per ticker.
Attributes:
symbol (str): The ticker asked for.
requests (list): Where each history request's arguments are recorded.
"""
def __init__(self, symbol, requests):
"""Remembers the ticker and where to record requests.
Args:
symbol (str): The ticker asked for.
requests (list): Where each history request's arguments are recorded.
Returns:
None: This method returns nothing.
"""
self.symbol = symbol
self.requests = requests
def history(self, period, auto_adjust, actions):
"""Returns the frame recorded for this ticker.
Args:
period (str): How much history to return.
auto_adjust (bool): Whether to adjust prices for dividends.
actions (bool): Whether to include dividends and splits.
Returns:
pandas.DataFrame: Rows indexed by date, with Close and Stock Splits columns, empty when Yahoo has nothing.
"""
self.requests.append(self.symbol)
if self.symbol == 'RELIANCE.BO':
return self.frame(
[
'2024-10-28',
'2024-10-29',
],
'Asia/Kolkata',
)
if self.symbol == '500325.BO':
return self.frame(
[
'2024-10-22',
'2024-10-23',
'2024-10-24',
'2024-10-25',
'2024-10-28',
],
'America/New_York',
)
return pandas.DataFrame()
def frame(self, dates, timezone_name):
"""Builds a frame with a steady close and a 1:1 bonus on 2024-10-28.
Args:
dates (list): The trading dates, as ISO strings.
timezone_name (str): The time zone Yahoo labels the index with.
Returns:
pandas.DataFrame: The frame.
"""
closes = []
splits = []
for date_text in dates:
closes.append(1333.3)
if date_text == '2024-10-28':
splits.append(2.0)
else:
splits.append(0.0)
index = pandas.to_datetime(dates).tz_localize(timezone_name)
return pandas.DataFrame(
{
'Close': closes,
'Stock Splits': splits,
},
index=index,
)
class StandInYfinance:
"""A stand-in for the `yfinance` module, offering only `Ticker`.
Attributes:
requests (list): Every history request made through it.
"""
def __init__(self):
"""Starts with no requests.
Returns:
None: This method returns nothing.
"""
self.requests = []
def Ticker(self, symbol):
"""Builds a stand-in ticker, under the name yfinance uses.
Args:
symbol (str): The Yahoo ticker.
Returns:
StandInTicker: The ticker.
"""
return StandInTicker(symbol, self.requests)
class TryingTickerFormsExample:
"""Tries each ticker form for RELIANCE on BSE and keeps the first that covers the raw history.
Attributes:
yfinance (StandInYfinance): The stand-in yfinance module.
client (YahooClient): The client being shown.
raw_days (list): The dates the stored raw bars have.
"""
def __init__(self):
"""Installs the stand-in and builds the client.
Returns:
None: This method returns nothing.
"""
self.yfinance = StandInYfinance()
yahoo.yf = self.yfinance
self.client = YahooClient()
self.raw_days = [
datetime.date(2024, 10, 22),
datetime.date(2024, 10, 23),
datetime.date(2024, 10, 24),
datetime.date(2024, 10, 25),
datetime.date(2024, 10, 28),
]
def run(self):
"""Walks the forms and prints each one's coverage.
Returns:
None: This method returns nothing.
"""
forms = yahoo.ticker_forms('bse', 'RELIANCE', '500325', listed_on_nse=True)
print(f'Forms to try: {forms}')
for ticker, kind in forms:
self.client.last_request = 0.0
closes, events = self.client.history(ticker)
share = yahoo.coverage(closes, self.raw_days)
print(f'{ticker} ({kind}): {len(closes)} closes, coverage {share:.1f}, events {events}')
if share >= yahoo.MINIMUM_COVERAGE:
print(f'Using {ticker}')
break
self.client.last_request = 0.0
closes, events = self.client.history('NOSUCHSHARE.BO')
print(f'A ticker Yahoo has nothing for: closes {closes}, events {events}')
print(f'Requests sent: {self.yfinance.requests}')
if __name__ == '__main__':
TryingTickerFormsExample().run()
It prints:
Forms to try: [('RELIANCE.BO', 'ticker'), ('500325.BO', 'scrip_code'), ('RELIANCE.NS', 'nse_ticker')]
RELIANCE.BO (ticker): 2 closes, coverage 0.2, events {datetime.date(2024, 10, 28): 2.0}
500325.BO (scrip_code): 5 closes, coverage 1.0, events {datetime.date(2024, 10, 28): 2.0}
Using 500325.BO
A ticker Yahoo has nothing for: closes {}, events {}
Requests sent: ['RELIANCE.BO', '500325.BO', 'NOSUCHSHARE.BO']
Methods:
| Name | Description |
|---|---|
pace |
Sleep until the next request is allowed. |
history |
One ticker's full daily history. |
Source code in stock_brokers/instruments/historical/utilities/unified/yahoo.py
pace
¶
Sleep until the next request is allowed.
Returns:
| Type | Description |
|---|---|
None
|
This function returns nothing. |
Example usage
Source code in stock_brokers/instruments/historical/utilities/unified/yahoo.py
history
¶
One ticker's full daily history.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ticker
|
str
|
A Yahoo ticker, for example "RELIANCE.NS". |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
(closes, events) - closes maps datetime.date to Yahoo's Close, events maps the date of each listed split or bonus to its ratio. Both are empty when Yahoo has nothing. |
Example usage
closes, events = self.client.history('NOSUCHSHARE.BO')
Source code in stock_brokers/instruments/historical/utilities/unified/yahoo.py
ticker_forms
¶
The Yahoo tickers to try for an instrument, in order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exchange
|
str
|
"nse" or "bse". |
required |
symbol
|
str
|
The exchange trading symbol. |
required |
scrip_code
|
str | None
|
The BSE scrip code, for BSE instruments. |
required |
listed_on_nse
|
bool
|
Whether a BSE instrument's symbol is also listed on NSE. |
False
|
Returns:
| Type | Description |
|---|---|
list[tuple[str, str]]
|
(ticker, kind) pairs, kind being "ticker", "scrip_code" or "nse_ticker". |
Source code in stock_brokers/instruments/historical/utilities/unified/yahoo.py
coverage
¶
The share of raw trading days a Yahoo history has a close for.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
closes
|
dict
|
Yahoo closes by date. |
required |
raw_days
|
list[date]
|
The raw bars' dates. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Between 0 and 1. |