Spaces:
Running on Zero
Running on Zero
Download src/drift_quant/market_data.py from deveshc23/drift-quant: direct link, hf CLI and curl.
- Browser
- Download file 5.54 kB
-
https://huggingface.co/spaces/deveshc23/drift-quant/resolve/main/src/drift_quant/market_data.py
- Command line
-
hf download hf://spaces/deveshc23/drift-quant/src/drift_quant/market_data.py
-
curl -L -o market_data.py https://huggingface.co/spaces/deveshc23/drift-quant/resolve/main/src/drift_quant/market_data.py
5.54 kB
| """Provider-neutral, availability-aware market-data contracts.""" | |
| from __future__ import annotations | |
| from collections.abc import Iterable, Mapping | |
| from dataclasses import dataclass | |
| from datetime import datetime | |
| from decimal import Decimal | |
| from typing import Protocol, runtime_checkable | |
| class MarketDataError(ValueError): | |
| """Raised when normalized market data or provider registration is invalid.""" | |
| def _require_aware(timestamp: datetime, field_name: str) -> None: | |
| if timestamp.tzinfo is None or timestamp.utcoffset() is None: | |
| raise MarketDataError(f"{field_name} must be timezone-aware.") | |
| class DailyBar: | |
| """Normalized, unadjusted daily OHLCV data with explicit availability time.""" | |
| instrument_id: str | |
| event_at: datetime | |
| available_at: datetime | |
| open: Decimal | |
| high: Decimal | |
| low: Decimal | |
| close: Decimal | |
| volume: int | |
| provider_name: str | |
| dataset_id: str | |
| def __post_init__(self) -> None: | |
| if not self.instrument_id.strip(): | |
| raise MarketDataError("Instrument ID must not be blank.") | |
| if not self.provider_name.strip() or not self.dataset_id.strip(): | |
| raise MarketDataError("Provider name and dataset ID must not be blank.") | |
| _require_aware(self.event_at, "event_at") | |
| _require_aware(self.available_at, "available_at") | |
| if self.available_at < self.event_at: | |
| raise MarketDataError("available_at must not precede event_at.") | |
| if min(self.open, self.high, self.low, self.close) <= Decimal("0"): | |
| raise MarketDataError("OHLC prices must be positive.") | |
| if self.high < max(self.open, self.close) or self.low > min(self.open, self.close): | |
| raise MarketDataError("OHLC prices are internally inconsistent.") | |
| if self.volume < 0: | |
| raise MarketDataError("Volume must not be negative.") | |
| class BarRequest: | |
| """A time-bounded query whose `as_of` prevents look-ahead use.""" | |
| instrument_ids: tuple[str, ...] | |
| start_at: datetime | |
| end_at: datetime | |
| as_of: datetime | |
| def __post_init__(self) -> None: | |
| if not self.instrument_ids or any( | |
| not instrument.strip() for instrument in self.instrument_ids | |
| ): | |
| raise MarketDataError("At least one non-blank instrument ID is required.") | |
| _require_aware(self.start_at, "start_at") | |
| _require_aware(self.end_at, "end_at") | |
| _require_aware(self.as_of, "as_of") | |
| if self.start_at >= self.end_at: | |
| raise MarketDataError("start_at must be before end_at.") | |
| class MarketDataProvider(Protocol): | |
| """Port implemented by all market-data providers.""" | |
| def name(self) -> str: ... | |
| def daily_bars(self, request: BarRequest) -> tuple[DailyBar, ...]: ... | |
| class ProviderRegistry: | |
| """Explicit provider registry with no vendor dependency in consumers.""" | |
| def __init__(self) -> None: | |
| self._providers: dict[str, MarketDataProvider] = {} | |
| def register(self, provider: MarketDataProvider) -> None: | |
| name = provider.name.strip().lower() | |
| if not name: | |
| raise MarketDataError("Provider name must not be blank.") | |
| if name in self._providers: | |
| raise MarketDataError(f"Provider {name} is already registered.") | |
| self._providers[name] = provider | |
| def get(self, name: str) -> MarketDataProvider: | |
| try: | |
| return self._providers[name.strip().lower()] | |
| except KeyError as exc: | |
| raise MarketDataError(f"Provider {name!r} is not registered.") from exc | |
| def names(self) -> tuple[str, ...]: | |
| return tuple(sorted(self._providers)) | |
| class FixtureMarketDataProvider: | |
| """A deterministic local provider used by tests and reproducible examples.""" | |
| def __init__(self, name: str, bars: Iterable[DailyBar]) -> None: | |
| if not name.strip(): | |
| raise MarketDataError("Provider name must not be blank.") | |
| self._name = name | |
| grouped: dict[str, list[DailyBar]] = {} | |
| for bar in bars: | |
| if bar.provider_name != name: | |
| raise MarketDataError("Fixture bars must name the fixture provider.") | |
| grouped.setdefault(bar.instrument_id, []).append(bar) | |
| self._bars: Mapping[str, tuple[DailyBar, ...]] = { | |
| instrument_id: self._validate_and_sort(instrument_bars) | |
| for instrument_id, instrument_bars in grouped.items() | |
| } | |
| def name(self) -> str: | |
| return self._name | |
| def daily_bars(self, request: BarRequest) -> tuple[DailyBar, ...]: | |
| result: list[DailyBar] = [] | |
| for instrument_id in request.instrument_ids: | |
| for bar in self._bars.get(instrument_id, ()): | |
| if ( | |
| request.start_at <= bar.event_at < request.end_at | |
| and bar.available_at <= request.as_of | |
| ): | |
| result.append(bar) | |
| return tuple(sorted(result, key=lambda bar: (bar.event_at, bar.instrument_id))) | |
| def _validate_and_sort(bars: list[DailyBar]) -> tuple[DailyBar, ...]: | |
| ordered = sorted(bars, key=lambda bar: bar.event_at) | |
| event_times = [bar.event_at for bar in ordered] | |
| if len(event_times) != len(set(event_times)): | |
| raise MarketDataError( | |
| "Fixture bars must not duplicate an event timestamp per instrument." | |
| ) | |
| return tuple(ordered) | |