// How Alpaca's fields become flat rows: numbers sent as strings, OCC option symbols, orders. /** A number from Alpaca's string or number, or null when missing. */ export function num(value: unknown): number | null { if (value === null || value === undefined || value === '') return null const n = Number(value) return Number.isFinite(n) ? n : null } /** A string or null. */ export function str(value: unknown): string | null { return value === null || value === undefined || value === '' ? null : String(value) } /** Underlying, expiration (YYYY-MM-DD), type, and strike from an OCC symbol like AAPL250117C00190000. */ export function parseOcc(symbol: string): { underlying: string | null expiration: string | null type: 'call' | 'put' | null strike: number | null } { const m = /^([A-Z0-9.]+?)(\d{6})([CP])(\d{8})$/.exec(symbol.replace(/\s+/g, '').toUpperCase()) if (!m) return { underlying: null, expiration: null, type: null, strike: null } const [, root, date, cp, strike] = m as unknown as [string, string, string, string, string] return { underlying: root, expiration: `20${date.slice(0, 2)}-${date.slice(2, 4)}-${date.slice(4, 6)}`, type: cp === 'C' ? 'call' : 'put', strike: Number(strike) / 1000, } } /** The midpoint of a bid and ask, or null when either is missing or zero. */ export function mid(bid: number | null, ask: number | null): number | null { return bid && ask ? Math.round(((bid + ask) / 2) * 10000) / 10000 : null } /** One order as a flat row. */ export function orderRow(order: Record): Record { return { id: str(order.id), client_order_id: str(order.client_order_id), symbol: str(order.symbol), asset_class: str(order.asset_class), side: str(order.side), type: str(order.type ?? order.order_type), time_in_force: str(order.time_in_force), order_class: str(order.order_class), qty: num(order.qty), notional: num(order.notional), filled_qty: num(order.filled_qty), limit_price: num(order.limit_price), stop_price: num(order.stop_price), trail_price: num(order.trail_price), trail_percent: num(order.trail_percent), filled_avg_price: num(order.filled_avg_price), status: str(order.status), extended_hours: Boolean(order.extended_hours), created_at: str(order.created_at), submitted_at: str(order.submitted_at), filled_at: str(order.filled_at), canceled_at: str(order.canceled_at), expired_at: str(order.expired_at), } }