marketgoblin
Download, store, and load financial market data — fast and without fuss.
marketgoblin is a lightweight market data platform built on Polars and yfinance. It fetches multiple datasets (OHLCV, shares-outstanding, dividends), slices them into monthly Parquet files, writes JSON sidecars with metadata, and lets you load them back with a single call.
Features
- Multi-dataset — OHLCV, shares-outstanding, and dividends selected via a
Datasetenum; per-source dispatch makes it easy to add more - Tidy stacked OHLCV — adjusted and raw prices live in one frame, distinguished by an
is_adjustedbool column; one network call per symbol covers both - Single-symbol and batch fetch —
fetch()andfetch_many()with thread-pool concurrency - Disk persistence — monthly
.pqslices with atomic writes; JSON sidecar per slice - Lazy evaluation — all data paths return
pl.LazyFrame(Polars) - Date flexibility — dates stored as
int32YYYYMMDD on disk; useparse_dates=Trueto getpl.Date - Retry logic —
YahooSourceretries transient failures with exponential backoff (3 attempts) - Rate limiting —
fetch_many()respects a configurable requests-per-second cap (default: 2 req/s) - Input validation — dates are validated before any I/O; unsupported
(provider, dataset)pairs raise at the dispatch boundary - Pluggable providers — subclass
BaseSource, implement_build_dispatch(), register in one line;YahooSourceandTiingoSourceincluded
Installation
pip install marketgoblin
Or with uv:
uv add marketgoblin
For development:
git clone https://github.com/aexsalomao/marketgoblin
cd marketgoblin
uv sync --extra dev
Quick Start
import polars as pl
from marketgoblin import Dataset, MarketGoblin
goblin = MarketGoblin(provider="yahoo", save_path="./data")
# Fetch and persist OHLCV — tidy stacked frame: each trading day appears
# twice (is_adjusted=True / False). Filter to pick a variant.
lf = goblin.fetch("AAPL", "2024-01-01", "2024-03-31", parse_dates=True)
adjusted = lf.filter(pl.col("is_adjusted")).collect()
print(adjusted)
# Load back from disk (no network call)
lf = goblin.load("AAPL", "2024-01-01", "2024-03-31", parse_dates=True)
print(lf.collect())
# Shares outstanding — sparse, corporate-action-driven series
shares = goblin.fetch("AAPL", "2024-01-01", "2024-03-31", dataset=Dataset.SHARES, parse_dates=True)
print(shares.collect())
# Dividends — event-driven (typically quarterly)
dividends = goblin.fetch("AAPL", "2024-01-01", "2024-03-31", dataset=Dataset.DIVIDENDS, parse_dates=True)
print(dividends.collect())
# Batch fetch — failed symbols are logged, never crash the batch
results = goblin.fetch_many(["AAPL", "MSFT", "GOOGL"], "2024-01-01", "2024-03-31")
for symbol, lf in results.items():
print(f"{symbol}: {lf.collect().height} rows")
Run the full walkthrough:
python example.py
API
MarketGoblin
MarketGoblin(provider: str, api_key: str | None = None, save_path: str | Path | None = None, **source_kwargs)
| Method | Description |
|---|---|
fetch(symbol, start, end, dataset=Dataset.OHLCV, parse_dates=False) |
Download, save to disk (if save_path set), return LazyFrame |
load(symbol, start, end, dataset=Dataset.OHLCV, parse_dates=False) |
Load from disk; raises RuntimeError if no save_path |
fetch_many(symbols, start, end, dataset=Dataset.OHLCV, parse_dates=False, max_workers=8, requests_per_second=2.0) |
Batch fetch via ThreadPoolExecutor, rate-limited |
supported_datasets (property) |
frozenset[Dataset] of datasets the configured provider supports |
Datasets
| Dataset | Provider support | Columns |
|---|---|---|
Dataset.OHLCV |
yahoo, tiingo |
date (int32), open / high / low / close (float32), volume (int64), is_adjusted (bool), symbol |
Dataset.SHARES |
yahoo, tiingo |
date (int32), shares (int64), symbol |
Dataset.DIVIDENDS |
yahoo, tiingo |
date (int32), dividend (float32), symbol |
Dataset.SPLITS |
tiingo |
date (int32), split_factor (float32), symbol |
Dataset.FUNDAMENTALS_DAILY |
tiingo |
date (int32), market_cap / enterprise_val (int64), pe_ratio / pb_ratio / trailing_peg_1y (float32), symbol |
Dataset.FUNDAMENTALS_STATEMENTS |
tiingo |
date (int32), fiscal_year (int16), fiscal_quarter (int8), ~76 income/balance/cash-flow/overview line items each as <field>_{as_reported,adjusted} (float64 for $/share-counts, float32 for per-share & ratios), symbol |
OHLCV is returned as a tidy stacked frame: each trading day appears twice (is_adjusted=True and is_adjusted=False). Filter downstream (.filter(pl.col("is_adjusted"))) to pick a variant. Adjusted Open/High/Low are derived locally from the Adj Close / Close ratio — verified to match yfinance's auto_adjust=True output exactly while halving network calls.
Data on disk
| Property | Detail |
|---|---|
| Date column | int32 YYYYMMDD (e.g. 20240101); parse_dates=True → pl.Date |
| OHLC columns | float32 |
| Volume column | int64 |
| Shares column | int64 |
| Dividend column | float32 |
| Parquet path | {save_path}/{provider}/{dataset}/{SYMBOL}/{SYMBOL}_{YYYY-MM}.pq |
| JSON sidecar | Same path, .json extension — row count, date range, per-dataset stats (OHLCV also records has_adjusted/has_raw and missing trading days) |
Adding a Provider
import polars as pl
from marketgoblin import Dataset
from marketgoblin.sources.base import BaseSource, Fetcher
class MySource(BaseSource):
name = "mysource"
def _build_dispatch(self) -> dict[Dataset, Fetcher]:
return {Dataset.OHLCV: self._fetch_ohlcv}
def _fetch_ohlcv(self, symbol: str, start: str, end: str) -> pl.LazyFrame:
... # return a normalized LazyFrame with an is_adjusted column
Per-dataset fetchers all share the (symbol, start, end) signature — there is no adjusted toggle, since OHLCV variants are stacked into a single frame distinguished by the is_adjusted column.
Then register it in goblin.py:
_SOURCES = {"yahoo": YahooSource, "tiingo": TiingoSource, "mysource": MySource}
Running Tests
pytest
pytest --cov=marketgoblin # with coverage
License
MIT © Antônio Salomão
Metadata
Release files for marketgoblin 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| marketgoblin-0.7.0.tar.gz | 47.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| marketgoblin-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 109.1 kB
Release files / marketgoblin-0.7.0.tar.gz
| Download URL | marketgoblin-0.7.0.tar.gz |
|---|---|
| Size | 47.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2a25503b78a240d8b18a57c036b2986a1997ff010828ad26ef5b5256e3248ab
|
|
BLAKE2b-256 checksum How to use checksums |
835c0ea3f6175a272426a68943602db3965afcc42ba5ef26ab1f73b0505dadc2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 10, 2026.
Transparency logRelease files / marketgoblin-0.7.0-py3-none-any.whl
| Download URL | marketgoblin-0.7.0-py3-none-any.whl |
|---|---|
| Size | 61.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
532d932706a424efc88e5425ffc336c7b67ad89fe721b4609eb9ca3c72361c3e
|
|
BLAKE2b-256 checksum How to use checksums |
14312afc5fb510045caa315a4ea9e9963c0798e293eafd5c55a4d8a89810cd6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 10, 2026.
Transparency log