1"""Canonical data contracts.23Bars: `timestamp_utc` marks the **bar open** (vendor convention, enforced). `session_date` is the trading4session date in the exchange timezone (America/New_York for US listings). Volumes are float64 because vendor5adjusted volumes may be fractional; raw volumes are integral and validated as such.6"""7from __future__ import annotations89import json10from enum import StrEnum1112import pyarrow as pa1314BARS_SCHEMA_VERSION = 1151617class AssetClass(StrEnum):18 stock = "stock"19 etf = "etf"20 futures = "futures"21 index = "index"22 fx = "fx"23 crypto = "crypto"24 options = "options"252627class Interval(StrEnum):28 m1 = "1m"29 m5 = "5m"30 m30 = "30m"31 h1 = "1h"32 d1 = "1d"333435INTERVAL_SECONDS = {Interval.m1: 60, Interval.m5: 300, Interval.m30: 1800, Interval.h1: 3600, Interval.d1: 86400}363738class Adjustment(StrEnum):39 raw = "raw" # vendor UNADJUSTED40 split = "split" # vendor adj_split41 split_div = "split_div" # vendor adj_splitdiv42 continuous = "continuous"434445class QualityFlag(StrEnum):46 ohlc_inconsistent = "ohlc_inconsistent" # high < max(open, close) or low > min(open, close)47 nonpositive_price = "nonpositive_price"48 negative_volume = "negative_volume"49 zero_volume = "zero_volume"50 fractional_volume = "fractional_volume"51 off_interval = "off_interval" # timestamp not aligned to the interval grid52 duplicate = "duplicate"53 outside_session = "outside_session" # outside 04:00–20:00 ET extended hours54 non_trading_day = "non_trading_day"55 price_jump = "price_jump" # |close/prev_close - 1| > threshold within a session56 gap = "gap" # missing regular-hours minute(s) before this bar57 out_of_order = "out_of_order"585960# The vendor CSV layout for intraday/daily bars (no header).61RAW_BAR_COLUMNS = ["datetime", "open", "high", "low", "close", "volume"]6263BARS_SCHEMA = pa.schema(64 [65 pa.field("instrument_id", pa.string(), nullable=False),66 pa.field("symbol_as_reported", pa.string(), nullable=False),67 pa.field("venue", pa.string(), nullable=True),68 pa.field("asset_class", pa.string(), nullable=False),69 pa.field("interval", pa.string(), nullable=False),70 pa.field("timestamp_utc", pa.timestamp("us", tz="UTC"), nullable=False),71 pa.field("session_date", pa.date32(), nullable=False),72 pa.field("open", pa.float64(), nullable=False),73 pa.field("high", pa.float64(), nullable=False),74 pa.field("low", pa.float64(), nullable=False),75 pa.field("close", pa.float64(), nullable=False),76 pa.field("volume", pa.float64(), nullable=False),77 pa.field("currency", pa.string(), nullable=False),78 pa.field("trade_count", pa.int32(), nullable=True),79 pa.field("vwap", pa.float64(), nullable=True),80 pa.field("adjustment_type", pa.string(), nullable=False),81 pa.field("source_id", pa.string(), nullable=False),82 pa.field("ingested_at", pa.timestamp("us", tz="UTC"), nullable=False),83 pa.field("quality_flags", pa.list_(pa.string()), nullable=False),84 pa.field("revision_id", pa.int32(), nullable=False),85 ],86 metadata={b"findata.schema": b"bars", b"findata.schema_version": str(BARS_SCHEMA_VERSION).encode(),87 b"findata.timestamp_convention": b"bar_open"},88)8990FUNDAMENTALS_SCHEMA = pa.schema(91 [92 pa.field("entity_id", pa.string(), nullable=False),93 pa.field("security_id", pa.string(), nullable=True),94 pa.field("metric", pa.string(), nullable=False),95 pa.field("value", pa.float64(), nullable=True),96 pa.field("unit", pa.string(), nullable=False),97 pa.field("currency", pa.string(), nullable=True),98 pa.field("fiscal_period_start", pa.date32(), nullable=True),99 pa.field("fiscal_period_end", pa.date32(), nullable=False),100 pa.field("fiscal_period_type", pa.string(), nullable=False),101 pa.field("fiscal_year", pa.int32(), nullable=True),102 pa.field("fiscal_period", pa.string(), nullable=True),103 pa.field("filing_date", pa.date32(), nullable=True),104 pa.field("published_at", pa.timestamp("us", tz="UTC"), nullable=True),105 pa.field("available_at", pa.timestamp("us", tz="UTC"), nullable=False),106 pa.field("observed_at", pa.timestamp("us", tz="UTC"), nullable=False),107 pa.field("source_document", pa.string(), nullable=True),108 pa.field("source_id", pa.string(), nullable=False),109 pa.field("revision_id", pa.int32(), nullable=False),110 pa.field("restatement_flag", pa.bool_(), nullable=False),111 ]112)113114115def bars_schema_json() -> str:116 return json.dumps([{"name": f.name, "type": str(f.type), "nullable": f.nullable} for f in BARS_SCHEMA], indent=2)117118119# End-of-day option chains (vendor CBOE feed). trade_date is the session; not split-adjusted at source.120OPTIONS_SCHEMA = pa.schema(121 [122 pa.field("underlying", pa.string(), nullable=False),123 pa.field("trade_date", pa.date32(), nullable=False),124 pa.field("strike", pa.float64(), nullable=False),125 pa.field("expiry", pa.date32(), nullable=False),126 pa.field("call_put", pa.string(), nullable=False),127 pa.field("last_price", pa.float64(), nullable=True),128 pa.field("bid", pa.float64(), nullable=True),129 pa.field("ask", pa.float64(), nullable=True),130 pa.field("bid_iv", pa.float64(), nullable=True),131 pa.field("ask_iv", pa.float64(), nullable=True),132 pa.field("open_interest", pa.float64(), nullable=True),133 pa.field("volume", pa.float64(), nullable=True),134 pa.field("delta", pa.float64(), nullable=True),135 pa.field("gamma", pa.float64(), nullable=True),136 pa.field("vega", pa.float64(), nullable=True),137 pa.field("theta", pa.float64(), nullable=True),138 pa.field("rho", pa.float64(), nullable=True),139 pa.field("source_id", pa.string(), nullable=False),140 pa.field("ingested_at", pa.timestamp("us", tz="UTC"), nullable=False),141 ],142 metadata={b"findata.schema": b"options_eod", b"findata.schema_version": b"1"},143)144