mts1b-marketdata
Market-data adapters: FMP, Polygon, Finnhub, ThetaData, AlphaVantage, Tiingo, YFinance.
Repo: github.com/MTS1B/mts1b-marketdata
Layer: 2
Depends on: foundation, platform, httpx
Audience: mts1b-datalake, mts1b-research, any service needing live or historical bars/quotes/trades
What it is
Async adapters that implement mts1b_foundation.protocols.MarketDataProtocol for equities, options, fx, and futures data sources.
Supported providers
| Provider | Bars | Quotes | Trades | Options | Fx | Futures | Notes |
|---|---|---|---|---|---|---|---|
fmp | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Financial Modeling Prep — primary equities feed |
polygon | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | Polygon.io |
finnhub | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | Finnhub |
thetadata | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ThetaData (options-focused) |
alphavantage | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | Free tier; rate-limited |
tiingo | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | Tiingo |
yfinance | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | Free; rate-limited; use for prototyping |
Module layout
mts1b_marketdata/
├── __init__.py # PROVIDER_REGISTRY
├── fmp/
│ ├── client.py
│ └── normalizer.py
├── polygon/...
├── finnhub/...
├── thetadata/...
├── alphavantage/...
├── tiingo/...
├── yfinance/...
└── routing/
├── multi_source.py # MultiSourceProvider — failover + best-source-per-symbol
└── cache.py # cache layer (Redis + parquet fallback)
Top API
MarketDataProtocol
@runtime_checkable
class MarketDataProtocol(Protocol):
@property
def name(self) -> str: ...
async def get_quote(self, symbol: Symbol) -> Quote: ...
async def get_bars(
self,
symbol: Symbol,
interval: str, # "1m", "5m", "1h", "1d", ...
start: datetime,
end: datetime | None = None,
) -> list[Bar]: ...
async def get_trades(self, symbol: Symbol, asof: datetime) -> list[Trade]: ...
async def get_chain(self, root: Symbol, expiry: date | None = None) -> list[OptionContract]: ...
async def stream_quotes(self, symbols: list[Symbol]) -> AsyncIterator[Quote]: ...
Direct adapter use
from mts1b_marketdata import PROVIDER_REGISTRY
from mts1b_foundation.symbology import Symbol
async with PROVIDER_REGISTRY["fmp"](api_key="...") as md:
quote = await md.get_quote(Symbol("AAPL"))
bars = await md.get_bars(Symbol("AAPL"), interval="1d", start=datetime(2024, 1, 1))
Multi-source routing
from mts1b_marketdata.routing import MultiSourceProvider
md = MultiSourceProvider(
providers=["fmp", "polygon", "alphavantage"],
strategy="failover", # try in order; first success wins
# or "best_quote" — query all, return tightest bid-ask
# or "load_balance" — round-robin within rate-limits
)
quote = await md.get_quote(Symbol("AAPL"))
mts1b-datalake and mts1b-research use MultiSourceProvider by default — single point of failure goes away.
Symbology
All inputs are mts1b_foundation.symbology.Symbol (canonical BASE-QUOTE for crypto, ticker for equities, OCC-style for options). Each adapter renders its native format internally.
Rate-limiting
Every adapter uses mts1b_platform.ratelimit.RateLimiter configured per provider:
| Provider | Free tier | Paid |
|---|---|---|
| FMP | 250/day | 30k+/day |
| Polygon | 5/min | unlimited |
| Finnhub | 60/min | 300+/min |
| YFinance | unofficial; flaky | n/a |
Adapters share the limiter across processes via Redis (or in-memory for single-host setups).
Caching
The cache layer (routing/cache.py):
| Type | Cache | TTL |
|---|---|---|
| Quotes | Redis | 1-5s |
| Bars (intraday) | Redis | until next bar close |
| Bars (daily) | Parquet on disk | overnight refresh |
| Options chains | Redis | 30s |
Cache hits don't hit the provider's rate limit. Cache misses do.
Capabilities matrix
Each adapter has a documented capabilities matrix in docs/repos/marketdata/<provider>.md listing exactly what's supported (intraday bar intervals, historical depth, options chain detail, etc.).
Live streaming
Three adapters support WebSocket streaming:
async with PROVIDER_REGISTRY["polygon"](api_key="...") as md:
async for quote in md.stream_quotes([Symbol("AAPL"), Symbol("MSFT")]):
print(f"{quote.symbol} bid={quote.bid} ask={quote.ask}")
For providers without WS, the adapter falls back to polling (configurable interval).
Build + test
pip install -e ".[dev]"
pytest -m unit # hermetic
pytest -m live --provider=fmp # requires API key
live tests run nightly in CI against staging API keys.
Roadmap
| Version | Items |
|---|---|
| 0.1 (Wave 1) | 7 adapters + MultiSourceProvider + Redis cache |
| 0.2 (Wave 2) | Tradier, IEX Cloud, Interactive Brokers historical |
| 0.3 (Wave 2) | News feed adapters (moved from altdata if heavyweight) |
| 1.0 (LTS) | Stable interface; new providers as plugins |
See also
- Foundation MarketDataProtocol
mts1b-datalake— primary consumermts1b-altdata— for SEC, GDELT, sentiment (not price data)