/** * @fileoverview Market Data Domain - OHLCV + Technical Indicators * Clean separation: Raw market data + TA persistence for ML processing * Purpose: OHLCV bars + pandas-ta indicator catalog with deduplication */ import { z } from 'zod'; // Core timeframes for market data export const Timeframe = z.enum(["1m","5m","15m","1h","4h","1d"]); export type TimeframeType = z.infer; // OHLCV Bar with stable bar_id for joins export const OHLCVBar = z.object({ bar_id: z.string().uuid(), // DB PK, server-side generated symbol: z.string(), timeframe: Timeframe, bar_ts: z.string(), // ISO8601 datetime open: z.number(), high: z.number(), low: z.number(), close: z.number(), volume: z.number().optional() // Some symbols don't have volume }).strict(); export type OHLCVBarType = z.infer; // Market Data Request export const GetOHLCVRequest = z.object({ symbol: z.string(), timeframe: Timeframe, from: z.string().datetime().optional(), to: z.string().datetime().optional(), limit: z.number().int().positive().max(1000).default(500) }).strict(); // Market Data Response export const OHLCVResponse = z.object({ success: z.literal(true), data: z.object({ symbol: z.string(), timeframe: Timeframe, bars: z.array(OHLCVBar), count: z.number().int().nonnegative() }).strict(), requestId: z.string(), timestamp: z.string().datetime() }).strict(); // Market Universe Control (Infrastructure Migration: 20250916000005) export const AssetClass = z.enum(['fx', 'equity', 'index', 'crypto', 'etf']); export const MarketProvider = z.enum(['yfinance', 'polygon', 'ig', 'manual']); export const MarketUniverse = z.object({ symbol: z.string(), enabled: z.boolean(), class: AssetClass, // Asset classification provider: MarketProvider, // Data provider yfinance_symbol: z.string().optional(), // Provider-specific symbol description: z.string().optional(), // Human-readable name created_at: z.string().datetime() }).strict(); export const MarketTimeframe = z.object({ timeframe: Timeframe, enabled: z.boolean(), retention_days: z.number().int().positive().optional(), // Data retention policy created_at: z.string().datetime() }).strict(); export const IngestionStatus = z.object({ symbol: z.string(), timeframe: Timeframe, last_bar_ts: z.string().datetime().optional(), // Latest bar timestamp last_ingestion_at: z.string().datetime(), // Last ingestion run bars_count: z.number().int().nonnegative(), // Total bars count status: z.enum(['active', 'error', 'disabled']), // Health status error_message: z.string().optional() // Error details }).strict(); // Alternative Market Bars (with provider in PK) export const MarketBar = z.object({ symbol: z.string(), timeframe: Timeframe, bar_ts: z.string().datetime(), open: z.number(), high: z.number(), low: z.number(), close: z.number(), volume: z.number().optional(), provider: MarketProvider, // In primary key ingested_at: z.string().datetime() // Ingestion timestamp }).strict(); // Pre-computed Indicators (alternative to ta_indicator_values) export const PrecomputedIndicators = z.object({ symbol: z.string(), timeframe: Timeframe, bar_ts: z.string().datetime(), rsi_14: z.number().optional(), // Pre-computed RSI ema_20: z.number().optional(), // Pre-computed EMA macd_12_26_9: z.array(z.number()).length(3).optional(), // [macd, signal, histogram] bb_upper: z.number().optional(), // Bollinger upper bb_lower: z.number().optional(), // Bollinger lower bb_middle: z.number().optional(), // Bollinger middle atr_14: z.number().optional(), // Average True Range computed_at: z.string().datetime() }).strict(); // Supported instruments for spread betting (Legacy compatibility) export const SPREAD_BETTING_SYMBOLS = [ // Major FX pairs 'EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD', 'NZDUSD', // Commodities 'XAUUSD', 'XAGUSD', 'USOIL', 'BRENT', // Indices 'US500', 'NAS100', 'GER40', 'UK100', 'FRA40', 'JPN225', // Individual stocks (major) 'AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'NVDA' ] as const; // Technical Indicators (pandas-ta catalog support) export const TAIndicatorValue = z.object({ // Dedup key = (bar_id, name, params_hash) bar_id: z.string().uuid(), symbol: z.string(), timeframe: Timeframe, bar_ts: z.string(), // ISO8601, convenience echo name: z.string(), // "rsi","macd","bbands",... provider: z.literal("pandas_ta"), params: z.record(z.any()), // Raw params params_hash: z.string(), // Hex, stable (see helper) values: z.record(z.number()), // 1..N outputs (e.g. macd/macds/machd) computed_at: z.string() // ISO8601 }).strict(); export const TAIndicatorBatch = z.object({ items: z.array(TAIndicatorValue).min(1) }).strict(); // Technical Indicators Request/Response export const GetIndicatorsRequest = z.object({ symbol: z.string(), timeframe: Timeframe, name: z.string().optional(), // Filter by indicator name limit: z.number().int().positive().max(1000).default(500) }).strict(); export const IndicatorsResponse = z.object({ success: z.literal(true), data: z.object({ symbol: z.string(), timeframe: Timeframe, indicators: z.array(TAIndicatorValue), count: z.number().int().nonnegative() }).strict(), requestId: z.string(), timestamp: z.string().datetime() }).strict(); // New envelope type for missing route export const SymbolsResponse = z.object({ success: z.literal(true), data: z.object({ symbols: z.array(z.object({ symbol: z.string(), description: z.string().optional(), class: AssetClass, provider: MarketProvider, enabled: z.boolean(), yfinance_symbol: z.string().optional() }).strict()), count: z.number().int().nonnegative(), asset_classes: z.array(AssetClass).optional(), providers: z.array(MarketProvider).optional() }).strict(), requestId: z.string(), timestamp: z.string().datetime() }).strict(); // Type exports export type AssetClassType = z.infer; export type MarketProviderType = z.infer; export type MarketUniverseType = z.infer; export type MarketTimeframeType = z.infer; export type IngestionStatusType = z.infer; export type MarketBarType = z.infer; export type PrecomputedIndicatorsType = z.infer; export type GetOHLCVRequestType = z.infer; export type OHLCVResponseType = z.infer; export type SpreadBettingSymbol = typeof SPREAD_BETTING_SYMBOLS[number]; export type TAIndicatorValueType = z.infer; export type TAIndicatorBatchType = z.infer; export type GetIndicatorsRequestType = z.infer; export type IndicatorsResponseType = z.infer; export type SymbolsResponseType = z.infer;