Skip to content

Data Fetching

fetch

Data fetching utilities using yfinance.

Function

fetch_ohlc( ticker, interval="1d", lookback: str | None = None, start: str | None = None, end: str | None = None, auto_adjust: bool = False, )

Parameter semantics
  • interval: Aggregation interval for candles ('1d', '1wk', '1mo').
  • lookback: Relative period for history breadth ('5d','1mo','3mo','6mo','1y','2y','5y','10y','ytd','max').
  • start/end: Explicit date range (YYYY-MM-DD). If both provided they override lookback. Passing values like '3mo' to start/end is invalid and will be ignored.

We guard against accidentally sending a lookback string where a date is required by validating format.

Returns a pandas DataFrame with columns: Open, High, Low, Close, Volume

fetch_ohlc(ticker, interval='1d', lookback=None, start=None, end=None, auto_adjust=False)

Fetch OHLC data for a single ticker.

Parameters:

Name Type Description Default
ticker str

Stock symbol to download.

required
interval str

Aggregation interval ('1d', '1wk', '1mo').

'1d'
lookback str | None

Relative period for history ('1y', '5y', 'max', etc.).

None
start str | None

Start date YYYY-MM-DD (overrides lookback if end also provided).

None
end str | None

End date YYYY-MM-DD.

None
auto_adjust bool

Whether to adjust OHLC for splits/dividends.

False

Returns:

Type Description
DataFrame

DataFrame with columns [Open, High, Low, Close, Volume].

Raises:

Type Description
ValueError

If data is empty or missing required columns.

Note
  • If both start and end are valid dates they override lookback.
  • If either start/end is invalid (e.g. '3mo'), it is ignored.
  • If nothing specified, default lookback = '1y'.
Source code in src/stockcharts/data/fetch.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def fetch_ohlc(
    ticker: str,
    interval: str = "1d",
    lookback: str | None = None,
    start: str | None = None,
    end: str | None = None,
    auto_adjust: bool = False,
) -> pd.DataFrame:
    """Fetch OHLC data for a single ticker.

    Args:
        ticker: Stock symbol to download.
        interval: Aggregation interval ('1d', '1wk', '1mo').
        lookback: Relative period for history ('1y', '5y', 'max', etc.).
        start: Start date YYYY-MM-DD (overrides lookback if end also provided).
        end: End date YYYY-MM-DD.
        auto_adjust: Whether to adjust OHLC for splits/dividends.

    Returns:
        DataFrame with columns [Open, High, Low, Close, Volume].

    Raises:
        ValueError: If data is empty or missing required columns.

    Note:
        - If both start and end are valid dates they override lookback.
        - If either start/end is invalid (e.g. '3mo'), it is ignored.
        - If nothing specified, default lookback = '1y'.
    """
    download_kwargs = _validate_and_build_download_kwargs(
        interval, lookback, start, end, auto_adjust
    )

    df = yf.download(ticker, **download_kwargs)
    return _normalize_single_ticker_df(df, ticker)

fetch_ohlc_batch(tickers, interval='1d', lookback=None, start=None, end=None, auto_adjust=False, threads=True, progress=False)

Fetch OHLC data for multiple tickers in a single batch request.

Uses yfinance's built-in threading for parallel downloads, which is significantly faster than sequential single-ticker downloads.

Parameters:

Name Type Description Default
tickers list[str]

List of stock symbols to download.

required
interval str

Aggregation interval ('1d', '1wk', '1mo').

'1d'
lookback str | None

Relative period for history ('1y', '5y', 'max', etc.).

None
start str | None

Start date YYYY-MM-DD (overrides lookback if end also provided).

None
end str | None

End date YYYY-MM-DD.

None
auto_adjust bool

Whether to adjust OHLC for splits/dividends.

False
threads bool

Use multi-threading for faster downloads (default: True).

True
progress bool

Show download progress bar (default: False).

False

Returns:

Type Description
dict[str, DataFrame]

Dictionary mapping ticker symbols to their OHLC DataFrames.

dict[str, DataFrame]

Failed downloads are silently omitted from results.

Source code in src/stockcharts/data/fetch.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def fetch_ohlc_batch(
    tickers: list[str],
    interval: str = "1d",
    lookback: str | None = None,
    start: str | None = None,
    end: str | None = None,
    auto_adjust: bool = False,
    threads: bool = True,
    progress: bool = False,
) -> dict[str, pd.DataFrame]:
    """Fetch OHLC data for multiple tickers in a single batch request.

    Uses yfinance's built-in threading for parallel downloads, which is
    significantly faster than sequential single-ticker downloads.

    Args:
        tickers: List of stock symbols to download.
        interval: Aggregation interval ('1d', '1wk', '1mo').
        lookback: Relative period for history ('1y', '5y', 'max', etc.).
        start: Start date YYYY-MM-DD (overrides lookback if end also provided).
        end: End date YYYY-MM-DD.
        auto_adjust: Whether to adjust OHLC for splits/dividends.
        threads: Use multi-threading for faster downloads (default: True).
        progress: Show download progress bar (default: False).

    Returns:
        Dictionary mapping ticker symbols to their OHLC DataFrames.
        Failed downloads are silently omitted from results.
    """
    if not tickers:
        return {}

    download_kwargs = _validate_and_build_download_kwargs(
        interval, lookback, start, end, auto_adjust
    )
    download_kwargs["progress"] = progress
    download_kwargs["threads"] = threads
    download_kwargs["group_by"] = "ticker"

    # yfinance accepts list or space-separated string
    batch_df = yf.download(tickers, **download_kwargs)

    results: dict[str, pd.DataFrame] = {}

    if batch_df.empty:
        return results

    # Handle single vs multiple ticker return format
    if len(tickers) == 1:
        # Single ticker: columns are just OHLCV
        ticker = tickers[0]
        try:
            df = _normalize_single_ticker_df(batch_df.copy(), ticker)
            results[ticker] = df
        except ValueError:
            pass  # Skip failed ticker
    else:
        # Multiple tickers: MultiIndex columns (ticker, OHLCV)
        for ticker in tickers:
            try:
                if ticker not in batch_df.columns.get_level_values(0):
                    continue

                ticker_df = batch_df[ticker].copy()

                # Drop rows where all values are NaN (no data for this date)
                ticker_df = ticker_df.dropna(how="all")

                if ticker_df.empty:
                    continue

                # Standardize columns
                ticker_df = ticker_df.reset_index().set_index(ticker_df.index.names[0])

                needed = ["Open", "High", "Low", "Close", "Volume"]
                missing = [c for c in needed if c not in ticker_df.columns]
                if missing:
                    continue

                results[ticker] = ticker_df[needed].copy()
            except Exception:
                # Skip any ticker that fails processing
                continue

    return results