Reading the Market with yfinance¶
Pull real NSE, BSE and US data with no broker account — OHLCV, adjusted close, dividends and splits explained.
Part 24 of 35 in the ServLoci algo/options trading notebook series — full index in notebooks/README.md.
Setup — no broker account needed¶
!pip install -q yfinance
import yfinance as yf
import pandas as pd
import numpy as np
# No broker account, no API key, no ServLoci setup needed for this chapter —
# yfinance reads public end-of-day data from Yahoo Finance. Indian tickers take
# an ".NS" suffix (NSE) or ".BO" (BSE); index tickers are prefixed with "^"
# (^NSEI = Nifty 50, ^BSESN = Sensex, ^GSPC = S&P 500).
NSE_TICKER = "RELIANCE.NS"
US_TICKER = "AAPL"
INDEX_TICKER = "^NSEI"
print("yfinance", yf.__version__)
Start here — no account, no API key, no waiting¶
Every other notebook in this series eventually asks you to whitelist an IP or authenticate against a broker. This one doesn't, on purpose. yfinance reads Yahoo Finance's public end-of-day (and near-real-time delayed) data over plain HTTPS — no credentials, no approval process, no rate-limited sandbox. That makes it the right place to actually learn what market data is before you ever touch an order API.
Ticker conventions. Yahoo's symbol format is not the same everywhere:
- NSE-listed stocks take an
.NSsuffix —RELIANCE.NS,TCS.NS,INFY.NS. - BSE-listed stocks take
.BOinstead. - Indices are prefixed with
^—^NSEIis Nifty 50,^BSESNis Sensex,^GSPCis the S&P 500. - US stocks have no suffix at all —
AAPL,MSFT.
Get this wrong (e.g. request RELIANCE instead of RELIANCE.NS) and yfinance either returns an empty frame or silently resolves to an unrelated ticker on another exchange — always check the row count and the first few rows before trusting a pull.
nse = yf.download(NSE_TICKER, period="1y", interval="1d", progress=False, multi_level_index=False)
us = yf.download(US_TICKER, period="1y", interval="1d", progress=False, multi_level_index=False)
index = yf.download(INDEX_TICKER, period="1y", interval="1d", progress=False, multi_level_index=False)
print(NSE_TICKER, "rows:", len(nse))
print(US_TICKER, "rows:", len(us))
print(INDEX_TICKER, "rows:", len(index))
nse.tail()
What's actually in a row of OHLCV¶
Every row is one trading session: Open, High, Low, Close, and Volume. Open and Close are auction prices (in India, set by the pre-open and closing call auctions); High and Low are the extremes touched intraday; Volume is shares traded, not rupee/dollar turnover.
A candlestick is just that same row drawn as a shape: a "body" spanning Open→Close (colored by whether the session closed up or down) and "wicks" reaching to the High and Low. Reading one candle tells you very little — reading a sequence of them is what technical analysis (notebook 27) is actually about.
Close vs Adj Close — the bug that quietly wrecks backtests. Close is the literal traded price that day. Adj Close retroactively adjusts every historical price for dividends and stock splits, so that a simple percentage-return calculation across the whole series stays correct. If a stock does a 1:2 split, its raw Close halves overnight — a backtest using raw Close would show a fake 50% crash on the split date. Always compute returns from Adj Close, never from Close, unless you have a specific reason not to (yfinance's newer default already auto-adjusts Close in some call modes — check the columns you actually got back rather than assuming).
ticker = yf.Ticker(NSE_TICKER)
divs = ticker.dividends
splits = ticker.splits
print("Dividend events in history:", len(divs))
print(divs.tail())
print("\nSplit events in history:", len(splits))
print(splits)
# Ticker().history() is the equivalent of yf.download() for a single symbol,
# and exposes the same adjustment behavior:
hist = ticker.history(period="6mo", auto_adjust=True)
hist[["Open", "High", "Low", "Close", "Volume"]].head()
Settlement: why "the trade happened" isn't "the money moved"¶
A filled order is not the end of the transaction. India moved to T+1 settlement in 2023 — shares and funds change hands one business day after the trade date. The US settles most equities on T+1 as well since mid-2024. This matters directly for algo trading: your buying power and holdings are not the same thing as "what I clicked buy on this morning," and any position-tracking code (see notebook 16's OMS) has to account for the settlement lag, not just the fill.
Next: notebook 25 turns these raw prices into the return and risk numbers every strategy claim should be judged by.
« Previous: Alerts and ServLoci Dispatch
Next: Returns, Volatility & the Numbers Courses Skip »
Try the concepts above interactively: Options Strategy Builder · Docs · Get your static IP