"""Canonical data contracts. Bars: `timestamp_utc` marks the **bar open** (vendor convention, enforced). `session_date` is the trading session date in the exchange timezone (America/New_York for US listings). Volumes are float64 because vendor adjusted volumes may be fractional; raw volumes are integral and validated as such. """ from __future__ import annotations import json from enum import StrEnum import pyarrow as pa BARS_SCHEMA_VERSION = 1 class AssetClass(StrEnum): stock = "stock" etf = "etf" futures = "futures" index = "index" fx = "fx" crypto = "crypto" options = "options" class Interval(StrEnum): m1 = "1m" m5 = "5m" m30 = "30m" h1 = "1h" d1 = "1d" INTERVAL_SECONDS = {Interval.m1: 60, Interval.m5: 300, Interval.m30: 1800, Interval.h1: 3600, Interval.d1: 86400} class Adjustment(StrEnum): raw = "raw" # vendor UNADJUSTED split = "split" # vendor adj_split split_div = "split_div" # vendor adj_splitdiv continuous = "continuous" class QualityFlag(StrEnum): ohlc_inconsistent = "ohlc_inconsistent" # high < max(open, close) or low > min(open, close) nonpositive_price = "nonpositive_price" negative_volume = "negative_volume" zero_volume = "zero_volume" fractional_volume = "fractional_volume" off_interval = "off_interval" # timestamp not aligned to the interval grid duplicate = "duplicate" outside_session = "outside_session" # outside 04:00–20:00 ET extended hours non_trading_day = "non_trading_day" price_jump = "price_jump" # |close/prev_close - 1| > threshold within a session gap = "gap" # missing regular-hours minute(s) before this bar out_of_order = "out_of_order" # The vendor CSV layout for intraday/daily bars (no header). RAW_BAR_COLUMNS = ["datetime", "open", "high", "low", "close", "volume"] BARS_SCHEMA = pa.schema( [ pa.field("instrument_id", pa.string(), nullable=False), pa.field("symbol_as_reported", pa.string(), nullable=False), pa.field("venue", pa.string(), nullable=True), pa.field("asset_class", pa.string(), nullable=False), pa.field("interval", pa.string(), nullable=False), pa.field("timestamp_utc", pa.timestamp("us", tz="UTC"), nullable=False), pa.field("session_date", pa.date32(), nullable=False), pa.field("open", pa.float64(), nullable=False), pa.field("high", pa.float64(), nullable=False), pa.field("low", pa.float64(), nullable=False), pa.field("close", pa.float64(), nullable=False), pa.field("volume", pa.float64(), nullable=False), pa.field("currency", pa.string(), nullable=False), pa.field("trade_count", pa.int32(), nullable=True), pa.field("vwap", pa.float64(), nullable=True), pa.field("adjustment_type", pa.string(), nullable=False), pa.field("source_id", pa.string(), nullable=False), pa.field("ingested_at", pa.timestamp("us", tz="UTC"), nullable=False), pa.field("quality_flags", pa.list_(pa.string()), nullable=False), pa.field("revision_id", pa.int32(), nullable=False), ], metadata={b"findata.schema": b"bars", b"findata.schema_version": str(BARS_SCHEMA_VERSION).encode(), b"findata.timestamp_convention": b"bar_open"}, ) FUNDAMENTALS_SCHEMA = pa.schema( [ pa.field("entity_id", pa.string(), nullable=False), pa.field("security_id", pa.string(), nullable=True), pa.field("metric", pa.string(), nullable=False), pa.field("value", pa.float64(), nullable=True), pa.field("unit", pa.string(), nullable=False), pa.field("currency", pa.string(), nullable=True), pa.field("fiscal_period_start", pa.date32(), nullable=True), pa.field("fiscal_period_end", pa.date32(), nullable=False), pa.field("fiscal_period_type", pa.string(), nullable=False), pa.field("fiscal_year", pa.int32(), nullable=True), pa.field("fiscal_period", pa.string(), nullable=True), pa.field("filing_date", pa.date32(), nullable=True), pa.field("published_at", pa.timestamp("us", tz="UTC"), nullable=True), pa.field("available_at", pa.timestamp("us", tz="UTC"), nullable=False), pa.field("observed_at", pa.timestamp("us", tz="UTC"), nullable=False), pa.field("source_document", pa.string(), nullable=True), pa.field("source_id", pa.string(), nullable=False), pa.field("revision_id", pa.int32(), nullable=False), pa.field("restatement_flag", pa.bool_(), nullable=False), ] ) def bars_schema_json() -> str: return json.dumps([{"name": f.name, "type": str(f.type), "nullable": f.nullable} for f in BARS_SCHEMA], indent=2) # End-of-day option chains (vendor CBOE feed). trade_date is the session; not split-adjusted at source. OPTIONS_SCHEMA = pa.schema( [ pa.field("underlying", pa.string(), nullable=False), pa.field("trade_date", pa.date32(), nullable=False), pa.field("strike", pa.float64(), nullable=False), pa.field("expiry", pa.date32(), nullable=False), pa.field("call_put", pa.string(), nullable=False), pa.field("last_price", pa.float64(), nullable=True), pa.field("bid", pa.float64(), nullable=True), pa.field("ask", pa.float64(), nullable=True), pa.field("bid_iv", pa.float64(), nullable=True), pa.field("ask_iv", pa.float64(), nullable=True), pa.field("open_interest", pa.float64(), nullable=True), pa.field("volume", pa.float64(), nullable=True), pa.field("delta", pa.float64(), nullable=True), pa.field("gamma", pa.float64(), nullable=True), pa.field("vega", pa.float64(), nullable=True), pa.field("theta", pa.float64(), nullable=True), pa.field("rho", pa.float64(), nullable=True), pa.field("source_id", pa.string(), nullable=False), pa.field("ingested_at", pa.timestamp("us", tz="UTC"), nullable=False), ], metadata={b"findata.schema": b"options_eod", b"findata.schema_version": b"1"}, )