@livefolio/sdk 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/strategy/reconcile.ts","../src/portfolio/apply.ts","../src/strategy/run-backtest.ts","../src/features/series-utils.ts","../src/features/indicators/sma.ts","../src/features/indicators/ema.ts","../src/features/indicators/rsi.ts","../src/features/indicators/return.ts","../src/features/indicators/volatility.ts","../src/features/indicators/drawdown.ts","../src/features/spec.ts","../src/features/runtime.ts","../src/reference/memory-feature-cache.ts","../src/strategy/run-live.ts","../src/reference/backtest-executor.ts","../src/reference/routing-data-feed.ts","../src/reference/routing-streaming-data-feed.ts","../src/reference/routing-quote-feed.ts","../src/reference/polling-stream-from-historical.ts","../src/calendars/exchange-calendar.ts","../src/calendars/holiday-rules.ts","../src/calendars/nyse.ts","../src/calendars/lse.ts","../src/calendars/get-calendar.ts","../src/calendars/crypto-24x7.ts","../src/tactical/index.ts","../src/tactical/evaluate-rule-tree.ts","../src/tactical/asset-ref.ts","../src/tactical/evaluate-feature-specs.ts","../src/tactical/synthetics.ts","../src/tactical/from-spec.ts","../src/features/index.ts"],"sourcesContent":["import type { Portfolio } from '../portfolio/types';\nimport type { Asset, AssetId } from '../interfaces/types';\nimport type { RebalanceOrder } from '../orders/types';\n\n/**\n * Maps each asset ID to its desired portfolio weight as a fraction of total\n * portfolio value (e.g. `0.6` means 60 %). Weights need not sum to 1; any\n * residual becomes cash. Passing a weight of `0` or omitting an asset entirely\n * will generate a full exit order for any existing long position in that asset.\n */\nexport type TargetWeights = ReadonlyMap<AssetId, number>;\n\n/**\n * Maps each asset ID to its current market price. Required for every asset that\n * appears in `TargetWeights` and for every asset currently held in the portfolio.\n * `reconcile` throws if a target asset has no corresponding price entry.\n */\nexport type PriceMap = ReadonlyMap<AssetId, number>;\n\n/**\n * Converts target portfolio weights into a minimal set of `RebalanceOrder`\n * instructions that move the current portfolio toward the desired allocation.\n *\n * The algorithm:\n * 1. Compute total portfolio value as `cash + Σ(held_shares × price)`.\n * 2. For each target asset, derive `targetShares = floor(totalValue × weight / price)`.\n * 3. Emit a `RebalanceOrder` with `delta = targetShares - heldShares` for any\n * asset where the delta is non-zero (positive = buy, negative = sell).\n * 4. Emit exit orders (`delta = -heldShares`) for any long position in an asset\n * that does not appear in `targets`.\n *\n * Only long positions are considered — short positions in the portfolio are\n * ignored. Share counts are always floored to integer lots.\n *\n * @param targets - Desired weight per asset. Keys are `AssetId` strings. A weight\n * of `0` is valid and will result in a full exit for any existing position.\n * @param portfolio - Current portfolio. Cash and long positions determine total\n * value and existing share counts.\n * @param prices - Current prices for all assets that appear in `targets` or are\n * currently held. Throws `Error` if a target asset is missing from this map.\n * @param assets - Optional canonical {@link Asset} metadata keyed by id. When\n * `reconcile` needs to emit an order for an asset that is not yet held in the\n * portfolio, it consults this map for the proper `symbol`/`kind`. If the\n * asset is missing here too, the order falls back to a synthesized\n * `{ kind: 'equity', id, symbol: id }` — lossless but display-unfriendly.\n * @returns A readonly array of `RebalanceOrder` objects. The array may be empty\n * if the portfolio is already at the target allocation. Order IDs are\n * deterministic within a single call (`rebal_<assetId>_<counter>`).\n *\n * @example\n * ```ts\n * import { reconcile } from '@livefolio/sdk';\n *\n * const targets = new Map([\n * ['US:SPY', 0.6],\n * ['US:BND', 0.4],\n * ]);\n * const prices = new Map([\n * ['US:SPY', 440.0],\n * ['US:BND', 75.0],\n * ]);\n *\n * // Empty portfolio with $100 000 cash\n * const portfolio = { cash: 100_000, positions: [] };\n * const orders = reconcile(targets, portfolio, prices);\n * // orders => [\n * // { id: 'rebal_US:SPY_0', kind: 'rebalance', asset: { ... }, delta: 136 },\n * // { id: 'rebal_US:BND_1', kind: 'rebalance', asset: { ... }, delta: 533 },\n * // ]\n * ```\n */\nexport function reconcile(\n targets: TargetWeights,\n portfolio: Portfolio,\n prices: PriceMap,\n assets?: ReadonlyMap<AssetId, Asset>,\n): ReadonlyArray<RebalanceOrder> {\n const longByAsset = new Map<AssetId, { asset: Asset; quantity: number }>();\n for (const p of portfolio.positions) {\n if (p.side !== 'long') continue;\n const cur = longByAsset.get(p.asset.id);\n if (cur) cur.quantity += p.quantity;\n else longByAsset.set(p.asset.id, { asset: p.asset, quantity: p.quantity });\n }\n\n let totalValue = portfolio.cash;\n for (const { asset, quantity } of longByAsset.values()) {\n const price = prices.get(asset.id);\n if (price !== undefined) totalValue += quantity * price;\n }\n\n const orders: RebalanceOrder[] = [];\n let counter = 0;\n const nextId = (assetId: AssetId): string => `rebal_${assetId}_${counter++}`;\n\n const seen = new Set<AssetId>();\n\n for (const [assetId, weight] of targets) {\n const price = prices.get(assetId);\n if (price === undefined) {\n throw new Error(`reconcile: missing price for target asset ${assetId}`);\n }\n // Clamp at 0: long-only reconcile must never emit a negative target.\n // A negative `targetShares` can otherwise arise when `totalValue` goes\n // below zero (e.g. a held leveraged-synthetic position takes a negative\n // close price after an extreme single-bar underlying move, where\n // `1 + leverage * r < 0`). Without this clamp, the resulting negative\n // `delta` on a non-held target produces a rebalance-reduce order with\n // no position to reduce, which `applyFills` then rejects. See #37.\n const targetShares = Math.max(0, Math.floor((totalValue * weight) / price));\n const held = longByAsset.get(assetId);\n const currentShares = held?.quantity ?? 0;\n const delta = targetShares - currentShares;\n seen.add(assetId);\n if (delta !== 0) {\n const asset: Asset = held?.asset ??\n assets?.get(assetId) ?? {\n kind: 'equity',\n id: assetId,\n symbol: assetId,\n };\n orders.push({ id: nextId(assetId), kind: 'rebalance', asset, delta });\n }\n }\n\n for (const [assetId, { asset, quantity }] of longByAsset) {\n if (seen.has(assetId)) continue;\n orders.push({ id: nextId(assetId), kind: 'rebalance', asset, delta: -quantity });\n }\n\n return orders;\n}\n","import type { Portfolio, Position, PositionId } from './types';\nimport type { Order, Fill } from '../orders/types';\n\nconst newPositionId = (() => {\n let n = 0;\n return (): PositionId => `pos_${++n}`;\n})();\n\nfunction findOrder(orders: ReadonlyArray<Order>, id: string): Order | undefined {\n return orders.find((o) => o.id === id);\n}\n\n/**\n * Applies a batch of confirmed fills to a portfolio, returning a new\n * {@link Portfolio} snapshot. This is the single function that advances\n * portfolio state after order execution.\n *\n * For each fill the corresponding order is looked up in `orders` by\n * `fill.orderRef`. The order's `kind` determines the accounting treatment:\n * - `'open'` — adds a new {@link Position} and debits cash.\n * - `'close'` — removes shares from an existing position and credits cash.\n * - `'adjust'` — updates the position's `quantity`; only fees are debited.\n * - `'rebalance'` — buys or sells shares in the long position for `asset`;\n * creates the position on a positive `delta`, removes it\n * when fully reduced. A reduce against a non-existent\n * long position is silently ignored (matching the same\n * case in {@link applyOrders}).\n *\n * The returned `portfolio.t` is updated to the maximum fill timestamp.\n *\n * @param portfolio - The current portfolio state before this batch.\n * @param fills - Execution confirmations returned by {@link Executor.submit}.\n * Each fill's `orderRef` MUST match an `id` in `orders`.\n * @param orders - The full order batch that was submitted. Used to look up\n * order details for each fill.\n * @returns A new {@link Portfolio} with updated positions, cash, and timestamp.\n * The input `portfolio` is not mutated.\n *\n * @example\n * ```ts\n * import { applyFills } from '@livefolio/sdk';\n * import type { Portfolio, Order, Fill } from '@livefolio/sdk';\n *\n * const portfolio: Portfolio = { cash: 10_000, positions: [], t: new Date('2024-01-01') };\n *\n * const order: Order = {\n * id: 'ord_1', kind: 'open',\n * asset: { kind: 'equity', id: 'AAPL', symbol: 'AAPL' },\n * side: 'long', quantity: 10,\n * };\n * const fill: Fill = { orderRef: 'ord_1', t: new Date('2024-01-02'), quantity: 10, price: 185, fees: 0 };\n *\n * const next = applyFills(portfolio, [fill], [order]);\n * // next.cash === 8_250, next.positions.length === 1\n * ```\n */\nexport function applyFills(portfolio: Portfolio, fills: ReadonlyArray<Fill>, orders: ReadonlyArray<Order>): Portfolio {\n let positions: Position[] = [...portfolio.positions];\n let cash = portfolio.cash;\n let t = portfolio.t;\n\n for (const fill of fills) {\n const order = findOrder(orders, fill.orderRef);\n if (!order) {\n throw new Error(`applyFills: fill.orderRef \"${fill.orderRef}\" matches no known order`);\n }\n t = fill.t > t ? fill.t : t;\n\n switch (order.kind) {\n case 'open': {\n const pos: Position = {\n id: newPositionId(),\n asset: order.asset,\n side: order.side,\n quantity: fill.quantity,\n entry: { date: fill.t, price: fill.price },\n basis: fill.quantity * fill.price + fill.fees,\n };\n positions.push(pos);\n cash -= fill.quantity * fill.price + fill.fees;\n break;\n }\n case 'close': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) throw new Error(`applyFills: close target ${order.positionId} not found`);\n const pos = positions[idx]!;\n const sign = pos.side === 'long' ? 1 : -1;\n cash += sign * fill.quantity * fill.price - fill.fees;\n const remaining = pos.quantity - fill.quantity;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n positions[idx] = { ...pos, quantity: remaining };\n }\n break;\n }\n case 'adjust': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) throw new Error(`applyFills: adjust target ${order.positionId} not found`);\n const pos = positions[idx]!;\n const nextQty = order.changes.quantity ?? pos.quantity;\n positions[idx] = { ...pos, quantity: nextQty };\n cash -= fill.fees;\n break;\n }\n case 'rebalance': {\n const idx = positions.findIndex((p) => p.asset.id === order.asset.id && p.side === 'long');\n if (order.delta > 0) {\n const cost = fill.quantity * fill.price + fill.fees;\n cash -= cost;\n if (idx < 0) {\n positions.push({\n id: newPositionId(),\n asset: order.asset,\n side: 'long',\n quantity: fill.quantity,\n entry: { date: fill.t, price: fill.price },\n basis: cost,\n });\n } else {\n const prev = positions[idx]!;\n positions[idx] = {\n ...prev,\n quantity: prev.quantity + fill.quantity,\n basis: prev.basis + cost,\n };\n }\n } else if (idx >= 0) {\n const prev = positions[idx]!;\n cash += fill.quantity * fill.price - fill.fees;\n const remaining = prev.quantity - fill.quantity;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n const basisPerShare = prev.basis / prev.quantity;\n positions[idx] = {\n ...prev,\n quantity: remaining,\n basis: basisPerShare * remaining,\n };\n }\n }\n // No-op when reducing a non-existent long position. This matches the\n // contract of `applyOrders` (same case in the structural projection\n // also no-ops) and avoids destabilising a backtest on a stray reduce.\n // The defence in depth here pairs with the `targetShares` clamp in\n // `reconcile`, which is the primary path that previously produced\n // such reduces. See #37.\n break;\n }\n }\n }\n\n return { cash, positions, t };\n}\n\n/**\n * Projects a portfolio forward through a set of pending (unfilled) orders,\n * returning a structurally updated snapshot. Used by strategy build helpers\n * to read the expected post-step state before fills arrive.\n *\n * **v0.4 contract — structural projection only.** Quantities are updated\n * exactly as the orders specify, but:\n * - `cash` is left unchanged (no price is available at projection time).\n * - Newly opened positions have `basis: 0` and `entry.price: 0` as\n * provisional values. A price-aware projection is planned for a later phase.\n *\n * Use {@link applyFills} (not this function) to settle the portfolio after\n * confirmed execution.\n *\n * @param portfolio - The current portfolio state to project from.\n * @param orders - The pending orders to apply structurally. Order must have\n * a valid `id` and `kind`; price fields are ignored.\n * @returns A new {@link Portfolio} with positions reflecting the orders.\n * `cash` and `t` are copied unchanged from `portfolio`.\n *\n * @example\n * ```ts\n * import { applyOrders } from '@livefolio/sdk';\n * import type { Portfolio, Order } from '@livefolio/sdk';\n *\n * const portfolio: Portfolio = { cash: 10_000, positions: [], t: new Date('2024-01-01') };\n *\n * const order: Order = {\n * id: 'ord_1', kind: 'open',\n * asset: { kind: 'equity', id: 'AAPL', symbol: 'AAPL' },\n * side: 'long', quantity: 10,\n * };\n *\n * const projected = applyOrders(portfolio, [order]);\n * // projected.positions.length === 1, projected.cash === 10_000 (unchanged)\n * ```\n */\nexport function applyOrders(portfolio: Portfolio, orders: ReadonlyArray<Order>): Portfolio {\n let positions: Position[] = [...portfolio.positions];\n\n for (const order of orders) {\n switch (order.kind) {\n case 'open': {\n positions.push({\n id: newPositionId(),\n asset: order.asset,\n side: order.side,\n quantity: order.quantity,\n entry: { date: portfolio.t, price: 0 },\n basis: 0,\n });\n break;\n }\n case 'close': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) break;\n const pos = positions[idx]!;\n const remove = order.quantity ?? pos.quantity;\n const remaining = pos.quantity - remove;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n positions[idx] = { ...pos, quantity: remaining };\n }\n break;\n }\n case 'adjust': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) break;\n const pos = positions[idx]!;\n positions[idx] = { ...pos, quantity: order.changes.quantity ?? pos.quantity };\n break;\n }\n case 'rebalance': {\n const idx = positions.findIndex((p) => p.asset.id === order.asset.id && p.side === 'long');\n if (order.delta > 0) {\n if (idx < 0) {\n positions.push({\n id: newPositionId(),\n asset: order.asset,\n side: 'long',\n quantity: order.delta,\n entry: { date: portfolio.t, price: 0 },\n basis: 0,\n });\n } else {\n const prev = positions[idx]!;\n positions[idx] = { ...prev, quantity: prev.quantity + order.delta };\n }\n } else if (idx >= 0) {\n const prev = positions[idx]!;\n const remaining = prev.quantity + order.delta;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n positions[idx] = { ...prev, quantity: remaining };\n }\n }\n break;\n }\n }\n }\n\n return { ...portfolio, positions };\n}\n","import type { Strategy, Features } from './types';\nimport type { Portfolio } from '../portfolio/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { Executor } from '../interfaces/executor';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { FeatureCache } from '../interfaces/feature-cache';\nimport type { AssetId, Bar, DateRange, Frequency } from '../interfaces/types';\nimport type { Order, Fill } from '../orders/types';\nimport type { FeatureRuntime } from '../features/runtime';\nimport { applyFills } from '../portfolio/apply';\n\n/**\n * Narrows the dual return type of `Strategy.build` to the stateful object form.\n *\n * `Array.isArray` does not narrow `ReadonlyArray<T>` out of a union in TypeScript 5.x\n * when the other arm is an object type, so we use an explicit type predicate instead.\n * The helper is defined at module scope so `runLive` (Task 8) can reuse it.\n */\nexport function isStateResult<S>(\n r: ReadonlyArray<Order> | { orders: ReadonlyArray<Order>; state: S },\n): r is { orders: ReadonlyArray<Order>; state: S } {\n return !Array.isArray(r);\n}\n\n/**\n * All inputs required to run a historical backtest.\n *\n * Callers must provide a concrete `Strategy`, a `DateRange`, and the four\n * pluggable runtime layers (`dataFeed`, `executor`, `calendar`, `featureCache`).\n * The reference implementations (`MemoryFeatureCache`, `BacktestExecutor`,\n * `NYSEExchangeCalendar`) satisfy all four without network dependencies.\n */\nexport type RunBacktestOptions<F extends Features = Features, S = unknown> = {\n /** The strategy under test. Must implement `universe`, `features`, and `build`. */\n strategy: Strategy<F, S>;\n /**\n * Inclusive date range over which to iterate. The calendar resolves this\n * range into the actual sequence of trading sessions.\n */\n range: DateRange;\n /**\n * Starting portfolio state. Cash and positions are carried forward through\n * the simulation as orders are filled. This value is never mutated.\n */\n initialPortfolio: Portfolio;\n /**\n * Source of OHLCV bar data and optionally fundamentals / corporate events.\n * `FeatureRuntime` uses this to hydrate price series before computing indicators.\n */\n dataFeed: DataFeed;\n /**\n * Order router responsible for converting `Order` objects into `Fill` records.\n * Use `BacktestExecutor` for historical simulations or swap in a live\n * broker implementation for paper/live trading.\n */\n executor: Executor;\n /**\n * Trading-day calendar. Used to enumerate `sessions` within `range` and to\n * determine rebalance day boundaries via `next`.\n */\n calendar: Calendar;\n /**\n * Optional persistent indicator cache. When omitted, each `runBacktest` call\n * recomputes all indicators from scratch. Provide `MemoryFeatureCache` (or a\n * cross-process cache) to memoize results across multiple runs.\n */\n featureCache?: FeatureCache;\n /**\n * Bar frequency forwarded to `DataFeed.bars`. Defaults to `'1d'` when omitted.\n * Must match the granularity expected by the strategy's indicator specs.\n */\n freq?: Frequency;\n /**\n * Optional `FeatureRuntime` instance. When provided, its accumulated bar buffer\n * is exported on `BacktestResult.bars` for use by `runLive` (lets the streaming\n * runtime seed its buffer from the historical bars without refetching).\n */\n featureRuntime?: FeatureRuntime;\n};\n\n/**\n * A point-in-time snapshot of the simulation at the end of a single trading session.\n *\n * Each entry in `BacktestResult.snapshots` corresponds to one call of the strategy\n * loop: `universe → features → build → executor.submit → applyFills`.\n */\nexport type BacktestSnapshot = {\n /** The session date for this snapshot (midnight UTC on the trading day). */\n t: Date;\n /** Portfolio state *after* fills have been applied for this session. */\n portfolio: Portfolio;\n /** Orders emitted by `strategy.build` during this session. */\n orders: ReadonlyArray<Order>;\n /** Fills returned by the executor for the orders above. */\n fills: ReadonlyArray<Fill>;\n};\n\n/**\n * The return value of `runBacktest`, containing the full simulation history\n * and the terminal portfolio state.\n */\nexport type BacktestResult<S = unknown> = {\n /**\n * Ordered list of snapshots, one per trading session in `range`. Empty when\n * the calendar has no sessions in the requested range.\n */\n snapshots: ReadonlyArray<BacktestSnapshot>;\n /**\n * Portfolio after the last session's fills have been applied. Equivalent to\n * `snapshots[snapshots.length - 1].portfolio` when there is at least one session,\n * or `initialPortfolio` when the range is empty.\n */\n finalPortfolio: Portfolio;\n /**\n * Final value of the strategy's auxiliary state after the last `build()` call.\n * `undefined` when the strategy is state-less (no `initialState()` defined).\n * Used by `runLive` to seed the live runtime so the first live tick continues\n * from the exact state the historical run ended on.\n */\n finalState: S | undefined;\n /**\n * Per-asset bar buffer accumulated by the `FeatureRuntime` during this run.\n * Empty `Map` when no `featureRuntime` was provided in `RunBacktestOptions`.\n * Used by `runLive` to seed its streaming `FeatureRuntime` so indicators with\n * warmup periods (SMA(200), etc.) work on the first live tick.\n */\n bars: ReadonlyMap<AssetId, ReadonlyArray<Bar>>;\n};\n\n/**\n * Drives a `Strategy` over a historical date range and returns a full audit trail\n * of orders, fills, and portfolio states.\n *\n * The simulation loop:\n * 1. Enumerate trading sessions via `opts.calendar.sessions(opts.range)`.\n * 2. Call `strategy.initialState?.()` once to seed the carry-over state.\n * 3. For each session `t`, call `strategy.universe(t, portfolio)`.\n * 4. Await `strategy.features(universe, portfolio, t)`.\n * 5. Call `strategy.build(features, portfolio, state, t)` to obtain orders and\n * the next state value. Both legacy `Order[]` returns and new `{ orders, state }`\n * returns are normalised — the legacy form leaves state unchanged.\n * 6. Await `opts.executor.submit(orders, t, portfolio)` to obtain fills.\n * 7. Apply fills to the portfolio with `applyFills`.\n * 8. Append a `BacktestSnapshot` and advance to the next session.\n *\n * The portfolio is never mutated in place; each session receives the immutable\n * result of the previous session's `applyFills`.\n *\n * @param opts - Backtest configuration. See {@link RunBacktestOptions}.\n * @returns A promise that resolves to a {@link BacktestResult} containing one\n * snapshot per trading session, the final portfolio state, and the final\n * strategy state (`finalState`). Returns\n * `{ snapshots: [], finalPortfolio: opts.initialPortfolio, finalState: undefined }`\n * when the calendar has no sessions in the requested range.\n *\n * @example\n * ```ts\n * import {\n * runBacktest,\n * fromSpec,\n * MemoryFeatureCache,\n * BacktestExecutor,\n * NYSEExchangeCalendar,\n * FeatureRuntime,\n * } from '@livefolio/sdk';\n *\n * const calendar = new NYSEExchangeCalendar();\n * const range = { from: new Date('2023-01-01'), to: new Date('2023-12-31') };\n * const featureCache = new MemoryFeatureCache();\n * const runtime = new FeatureRuntime({ dataFeed, featureCache, range, freq: '1d' });\n *\n * const strategy = fromSpec(myTacticalSpec, { runtime, calendar });\n *\n * const result = await runBacktest({\n * strategy,\n * range,\n * initialPortfolio: { cash: 100_000, positions: [] },\n * dataFeed,\n * executor: new BacktestExecutor({ dataFeed }),\n * calendar,\n * featureCache,\n * freq: '1d',\n * });\n *\n * console.log(result.finalPortfolio.cash);\n * console.log(result.snapshots.length); // one entry per NYSE trading day in 2023\n * ```\n */\nexport async function runBacktest<F extends Features = Features, S = unknown>(\n opts: RunBacktestOptions<F, S>,\n): Promise<BacktestResult<S>> {\n const initialStateValue: S | undefined = opts.strategy.initialState?.();\n const sessions = opts.calendar.sessions(opts.range);\n if (sessions.length === 0) {\n return {\n snapshots: [],\n finalPortfolio: opts.initialPortfolio,\n finalState: initialStateValue,\n bars: opts.featureRuntime?.getAllBars() ?? new Map<AssetId, ReadonlyArray<Bar>>(),\n };\n }\n\n let portfolio = opts.initialPortfolio;\n let state: S | undefined = initialStateValue;\n const snapshots: BacktestSnapshot[] = [];\n\n for (const t of sessions) {\n const universe = opts.strategy.universe(t, portfolio);\n const features = await opts.strategy.features(universe, portfolio, t);\n const buildResult = opts.strategy.build(features, portfolio, state as S, t);\n\n let orders: ReadonlyArray<Order>;\n if (isStateResult(buildResult)) {\n orders = buildResult.orders;\n state = buildResult.state;\n } else {\n // Legacy state-less return shape — state unchanged.\n orders = buildResult;\n }\n\n const fills = await opts.executor.submit(orders, t, portfolio);\n portfolio = applyFills(portfolio, fills, orders);\n snapshots.push({ t, portfolio, orders, fills });\n }\n\n const bars = opts.featureRuntime?.getAllBars() ?? new Map<AssetId, ReadonlyArray<Bar>>();\n return { snapshots, finalPortfolio: portfolio, finalState: state, bars };\n}\n","import type { Bar, Series } from '../interfaces/types';\n\n/**\n * The OHLCV field of a `Bar` that should be used when converting a bar array\n * into a scalar time series. Defaults to `'close'` throughout the feature\n * pipeline unless overridden via `FeatureRuntimeOptions.field`.\n *\n * Variants: `'open'` | `'high'` | `'low'` | `'close'` | `'volume'`.\n */\nexport type BarField = 'open' | 'high' | 'low' | 'close' | 'volume';\n\n/**\n * Drains an `AsyncIterable<Bar>` into a plain `Bar[]` array.\n *\n * Used internally by `FeatureRuntime` to materialise the stream returned by\n * `DataFeed.bars` before passing it to indicator functions that expect a\n * fully-in-memory array. The resulting array is in the same order as the\n * iterable yields (typically chronological).\n *\n * @param it - Any `AsyncIterable<Bar>`, such as the return value of `DataFeed.bars`.\n * @returns A promise that resolves to a `Bar[]` containing every bar yielded\n * by the iterable, in iteration order.\n *\n * @example\n * ```ts\n * import { collectBars } from '@livefolio/sdk';\n *\n * const bars = await collectBars(dataFeed.bars(asset, range, '1d'));\n * // bars is now a Bar[] — safe to index, slice, and pass to barsToSeries\n * ```\n */\nexport async function collectBars(it: AsyncIterable<Bar>): Promise<Bar[]> {\n const out: Bar[] = [];\n for await (const b of it) out.push(b);\n return out;\n}\n\n/**\n * Converts an array of OHLCV bars into a `Series` by extracting a single\n * numeric field from each bar.\n *\n * The resulting `Series` is a readonly array of `{ t: Date; v: number }` points\n * in the same order as `bars`. The timestamp `t` is taken directly from `bar.t`,\n * and `v` is the value of `field` for that bar.\n *\n * @param bars - Source OHLCV bars in chronological order.\n * @param field - Which OHLCV field to extract. Defaults to `'close'`.\n * @returns A `Series` of the same length as `bars`. Returns an empty array when\n * `bars` is empty.\n *\n * @example\n * ```ts\n * import { collectBars, barsToSeries } from '@livefolio/sdk';\n *\n * const bars = await collectBars(dataFeed.bars(asset, range, '1d'));\n * const closeSeries = barsToSeries(bars); // default: 'close'\n * const volumeSeries = barsToSeries(bars, 'volume');\n * ```\n */\nexport function barsToSeries(bars: ReadonlyArray<Bar>, field: BarField = 'close'): Series {\n return bars.map((b) => ({ t: b.t, v: b[field] }));\n}\n\n/**\n * Looks up the value at or immediately before timestamp `t` in a sorted `Series`.\n *\n * Uses binary search to find the largest index `i` where `series[i].t <= t`.\n * This is the standard \"as-of\" lookup used throughout the strategy loop to read\n * the most recent indicator value available on a given session date without\n * peeking into the future.\n *\n * @param series - A `Series` sorted in ascending timestamp order. Behaviour is\n * undefined for unsorted input.\n * @param t - The target date. May fall between two data points or exactly on one.\n * @returns The `v` value of the last data point at or before `t`, or `undefined`\n * if the series is empty or every point comes after `t`.\n *\n * @example\n * ```ts\n * import { seriesAt } from '@livefolio/sdk';\n *\n * const series = [\n * { t: new Date('2023-01-02'), v: 380.0 },\n * { t: new Date('2023-01-03'), v: 385.0 },\n * { t: new Date('2023-01-04'), v: 390.0 },\n * ];\n *\n * seriesAt(series, new Date('2023-01-03')); // => 385.0 (exact match)\n * seriesAt(series, new Date('2023-01-03T12:00:00Z')); // => 385.0 (between points)\n * seriesAt(series, new Date('2023-01-01')); // => undefined (before all points)\n * ```\n */\nexport function seriesAt(series: Series, t: Date): number | undefined {\n if (series.length === 0) return undefined;\n const target = t.getTime();\n // binary search for largest index where series[i].t <= target\n let lo = 0;\n let hi = series.length - 1;\n let ans = -1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (series[mid]!.t.getTime() <= target) {\n ans = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return ans < 0 ? undefined : series[ans]!.v;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes a Simple Moving Average (SMA) over a price series.\n *\n * Math definition:\n * ```\n * SMA[i] = (series[i] + series[i-1] + ... + series[i-period+1]) / period\n * ```\n *\n * Warmup: the first output point corresponds to index `period - 1` in the input.\n * Inputs `series[0]` through `series[period - 2]` have no SMA value and are\n * excluded from the output entirely (no `undefined` placeholders; the output\n * array is shorter than the input).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period` — returns `[]` (not enough data for even one window).\n * - `series.length === period` — returns a single-point `Series`.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Window size in bars. Must be a positive integer.\n * @returns A `Series` of length `max(0, series.length - period + 1)`. Each point's\n * timestamp `t` is taken from the last bar in its window (`series[i + period - 1]`).\n *\n * @example\n * ```ts\n * import { sma } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 110 },\n * { t: new Date('2023-01-04'), v: 120 },\n * { t: new Date('2023-01-05'), v: 130 },\n * ];\n *\n * const result = sma(prices, 3);\n * // result.length === 2\n * // result[0] => { t: new Date('2023-01-04'), v: 110 } // (100+110+120)/3\n * // result[1] => { t: new Date('2023-01-05'), v: 120 } // (110+120+130)/3\n * ```\n */\nexport function sma(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`sma: period must be positive, got ${period}`);\n if (series.length < period) return [];\n const out: { t: Date; v: number }[] = [];\n let sum = 0;\n for (let i = 0; i < period; i++) sum += series[i]!.v;\n out.push({ t: series[period - 1]!.t, v: sum / period });\n for (let i = period; i < series.length; i++) {\n sum += series[i]!.v - series[i - period]!.v;\n out.push({ t: series[i]!.t, v: sum / period });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes an Exponential Moving Average (EMA) over a price series.\n *\n * Math definition:\n * ```\n * k = 2 / (period + 1) // smoothing factor\n * EMA[0] = SMA(series[0..period-1]) // seeded from simple average\n * EMA[i] = series[i] * k + EMA[i-1] * (1 - k)\n * ```\n *\n * Warmup: the first `period - 1` input bars are consumed to seed the SMA\n * initial value. The first EMA output point corresponds to input index\n * `period - 1`; subsequent points are computed from the recursive formula.\n * The output array is shorter than the input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period` — returns `[]` (not enough data to seed the EMA).\n * - `series.length === period` — returns a single-point `Series` equal to the\n * simple average of all input values.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Lookback window in bars. Must be a positive integer. Controls\n * the smoothing factor `k = 2 / (period + 1)`: smaller periods react faster\n * to recent prices.\n * @returns A `Series` of length `max(0, series.length - period + 1)`. Each\n * point's timestamp `t` is taken from the corresponding input bar.\n *\n * @example\n * ```ts\n * import { ema } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 110 },\n * { t: new Date('2023-01-04'), v: 120 },\n * { t: new Date('2023-01-05'), v: 130 },\n * ];\n *\n * const result = ema(prices, 3);\n * // result.length === 2\n * // result[0] => { t: new Date('2023-01-04'), v: 110 } // SMA seed: (100+110+120)/3\n * // result[1] => { t: new Date('2023-01-05'), v: ~116.7 } // EMA: 130*0.5 + 110*0.5\n * ```\n */\nexport function ema(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`ema: period must be positive, got ${period}`);\n if (series.length < period) return [];\n const k = 2 / (period + 1);\n const out: { t: Date; v: number }[] = [];\n let sum = 0;\n for (let i = 0; i < period; i++) sum += series[i]!.v;\n let prev = sum / period;\n out.push({ t: series[period - 1]!.t, v: prev });\n for (let i = period; i < series.length; i++) {\n prev = series[i]!.v * k + prev * (1 - k);\n out.push({ t: series[i]!.t, v: prev });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes the Relative Strength Index (RSI) using Wilder's smoothing method.\n *\n * Math definition:\n * ```\n * changes[i] = series[i] - series[i-1]\n *\n * // Seed from simple averages of the first `period` changes:\n * avgGain[0] = mean(max(changes[0..period-1], 0))\n * avgLoss[0] = mean(max(-changes[0..period-1], 0))\n *\n * // Wilder's smoothing for subsequent periods:\n * avgGain[i] = (avgGain[i-1] * (period-1) + gain[i]) / period\n * avgLoss[i] = (avgLoss[i-1] * (period-1) + loss[i]) / period\n *\n * RS[i] = avgGain[i] / avgLoss[i]\n * RSI[i] = 100 - 100 / (1 + RS[i])\n * ```\n *\n * Special case: when `avgLoss === 0`, RSI is clamped to 100 (infinite RS means\n * no losing periods in the window).\n *\n * Warmup: requires `period + 1` input bars to produce the first RSI value\n * (one extra bar for the initial change calculation). The first output point\n * corresponds to input index `period`. The output array is shorter than the\n * input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period + 1` — returns `[]`.\n * - Flat price series (all changes = 0) — returns RSI values of 100 because\n * `avgLoss` stays 0.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Lookback window in bars for Wilder's smoothing. Must be a\n * positive integer; 14 is the conventional default.\n * @returns A `Series` of length `max(0, series.length - period)`. Each point's\n * timestamp `t` is taken from the corresponding input bar. Values are in\n * the range `[0, 100]`.\n *\n * @example\n * ```ts\n * import { rsi } from '@livefolio/sdk';\n *\n * // Minimal example: 5 bars, period 3 → 2 RSI values\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 102 },\n * { t: new Date('2023-01-04'), v: 101 },\n * { t: new Date('2023-01-05'), v: 105 },\n * { t: new Date('2023-01-06'), v: 104 },\n * ];\n *\n * const result = rsi(prices, 3);\n * // result.length === 2\n * // result[0].t => new Date('2023-01-05')\n * // result[1].t => new Date('2023-01-06')\n * // values are in [0, 100]\n * ```\n */\nexport function rsi(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`rsi: period must be positive, got ${period}`);\n if (series.length < period + 1) return [];\n const changes: number[] = [];\n for (let i = 1; i < series.length; i++) {\n changes.push(series[i]!.v - series[i - 1]!.v);\n }\n let avgGain = 0;\n let avgLoss = 0;\n for (let i = 0; i < period; i++) {\n if (changes[i]! > 0) avgGain += changes[i]!;\n else avgLoss += Math.abs(changes[i]!);\n }\n avgGain /= period;\n avgLoss /= period;\n const out: { t: Date; v: number }[] = [];\n const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n out.push({\n t: series[period]!.t,\n v: avgLoss === 0 ? 100 : 100 - 100 / (1 + rs),\n });\n for (let i = period; i < changes.length; i++) {\n const gain = changes[i]! > 0 ? changes[i]! : 0;\n const loss = changes[i]! < 0 ? Math.abs(changes[i]!) : 0;\n avgGain = (avgGain * (period - 1) + gain) / period;\n avgLoss = (avgLoss * (period - 1) + loss) / period;\n const smoothRs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n out.push({\n t: series[i + 1]!.t,\n v: avgLoss === 0 ? 100 : 100 - 100 / (1 + smoothRs),\n });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Controls whether `returnSeries` computes percentage or absolute returns.\n *\n * - `'pct'` (default) — percentage return: `(curr - prev) / prev`. The result is\n * a dimensionless ratio (e.g. `0.05` means +5 %).\n * - `'abs'` — absolute price difference: `curr - prev`. The result is in the same\n * units as the input price series.\n */\nexport type ReturnMode = 'pct' | 'abs';\n\n/**\n * Computes a rolling period return over a price series.\n *\n * Math definition:\n * ```\n * // Percentage (default):\n * return[i] = (series[i] - series[i - period]) / series[i - period]\n *\n * // Absolute:\n * return[i] = series[i] - series[i - period]\n * ```\n *\n * Warmup: the first `period` bars are consumed as the lookback window for the\n * first output. The first output point corresponds to input index `period`.\n * The output array is shorter than the input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length <= period` — returns `[]` (needs strictly more than `period` bars).\n * - `mode === 'pct'` with `prev === 0` — produces `Infinity` or `NaN`; callers\n * should filter or guard against zero-price series.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Lookback distance in bars. A value of `1` gives a single-bar\n * (daily) return; larger values give multi-bar returns.\n * @param mode - Whether to return a percentage ratio or an absolute difference.\n * Defaults to `'pct'`.\n * @returns A `Series` of length `max(0, series.length - period)`. Each point's\n * timestamp `t` is taken from `series[i]` (the later of the two bars).\n *\n * @example\n * ```ts\n * import { returnSeries } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 105 },\n * { t: new Date('2023-01-04'), v: 110 },\n * ];\n *\n * const pct = returnSeries(prices, 1);\n * // pct[0] => { t: new Date('2023-01-03'), v: 0.05 } // (105-100)/100\n * // pct[1] => { t: new Date('2023-01-04'), v: ~0.048 } // (110-105)/105\n *\n * const abs = returnSeries(prices, 1, 'abs');\n * // abs[0] => { t: new Date('2023-01-03'), v: 5 } // 105-100\n * // abs[1] => { t: new Date('2023-01-04'), v: 5 } // 110-105\n *\n * // Two-bar return\n * const twoBar = returnSeries(prices, 2);\n * // twoBar[0] => { t: new Date('2023-01-04'), v: 0.1 } // (110-100)/100\n * ```\n */\nexport function returnSeries(series: Series, period: number, mode: ReturnMode = 'pct'): Series {\n if (period <= 0) throw new Error(`returnSeries: period must be positive, got ${period}`);\n if (series.length <= period) return [];\n const out: { t: Date; v: number }[] = [];\n for (let i = period; i < series.length; i++) {\n const curr = series[i]!.v;\n const prev = series[i - period]!.v;\n const v = mode === 'abs' ? curr - prev : (curr - prev) / prev;\n out.push({ t: series[i]!.t, v });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes a rolling historical volatility as the population standard deviation\n * of daily log-like returns over a sliding window.\n *\n * Math definition:\n * ```\n * dailyReturn[i] = series[i] / series[i-1] - 1 // simple period return\n *\n * // For each window of `period` daily returns ending at index i:\n * mean = Σ dailyReturn[i..i-period+1] / period\n * variance = Σ (dailyReturn[j] - mean)² / period // population variance\n * vol[i] = sqrt(variance)\n * ```\n *\n * The result is the per-bar standard deviation expressed as a fraction (e.g.\n * `0.012` ≈ 1.2 % daily volatility). To annualise, multiply by `sqrt(252)` for\n * daily bars.\n *\n * Warmup: requires `period + 1` price bars to produce the first volatility value:\n * one extra bar to compute the first daily return, then `period` returns for the\n * first window. The first output point corresponds to the `period`-th daily-return\n * index. The output array is shorter than the input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period + 1` — returns `[]`.\n * - Flat price series (all returns = 0) — returns volatility values of `0`.\n *\n * @param series - Input price series sorted in ascending timestamp order. Values\n * must be positive (non-zero); zero prices produce `NaN` daily returns.\n * @param period - Window size in daily-return bars. Must be a positive integer;\n * common values are 20 (≈ 1 month) or 252 (1 year) for daily data.\n * @returns A `Series` of length `max(0, series.length - period)`. Each point's\n * timestamp `t` is taken from the last daily-return bar in its window.\n *\n * @example\n * ```ts\n * import { volatility } from '@livefolio/sdk';\n *\n * // 5 price bars → 4 daily returns → 1 vol point (period=4)\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 101 },\n * { t: new Date('2023-01-04'), v: 99 },\n * { t: new Date('2023-01-05'), v: 102 },\n * { t: new Date('2023-01-06'), v: 100 },\n * ];\n *\n * const vol = volatility(prices, 4);\n * // vol.length === 1\n * // vol[0].t => new Date('2023-01-06')\n * // vol[0].v => population std-dev of the 4 daily returns (≈ 0.012)\n * ```\n */\nexport function volatility(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`volatility: period must be positive, got ${period}`);\n if (series.length < period + 1) return [];\n const dailyReturns: { t: Date; v: number }[] = [];\n for (let i = 1; i < series.length; i++) {\n dailyReturns.push({\n t: series[i]!.t,\n v: series[i]!.v / series[i - 1]!.v - 1,\n });\n }\n if (dailyReturns.length < period) return [];\n const out: { t: Date; v: number }[] = [];\n for (let i = period - 1; i < dailyReturns.length; i++) {\n const window = dailyReturns.slice(i - period + 1, i + 1);\n const mean = window.reduce((s, r) => s + r.v, 0) / period;\n const variance = window.reduce((s, r) => s + (r.v - mean) ** 2, 0) / period;\n out.push({ t: dailyReturns[i]!.t, v: Math.sqrt(variance) });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes the rolling drawdown relative to the period high for each bar.\n *\n * Math definition:\n * ```\n * rollingMax[i] = max(series[i-period+1], ..., series[i])\n * drawdown[i] = (series[i] - rollingMax[i]) / rollingMax[i]\n * ```\n *\n * The result is a non-positive fraction (e.g. `-0.15` means the current price\n * is 15 % below the period high). A value of `0` means the current price equals\n * the rolling maximum — i.e. the asset is at a new high within the window.\n *\n * Warmup: the first output point corresponds to input index `period - 1` (the\n * first complete window). The output array is shorter than the input by\n * `period - 1` points (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period` — returns `[]`.\n * - All prices in a window are equal — returns `0` (current equals max).\n * - Zero prices in the window — produces `NaN` (division by zero); callers\n * should guard against zero-price series.\n *\n * @param series - Input price series sorted in ascending timestamp order. Values\n * should be positive (non-zero) to avoid `NaN` results.\n * @param period - Rolling window size in bars. Must be a positive integer.\n * A value of `1` always returns `0` (current equals one-bar max).\n * @returns A `Series` of length `max(0, series.length - period + 1)`. Each\n * point's timestamp `t` is taken from `series[i]` (the last bar in its window).\n * Values are in the range `(-∞, 0]` but in practice within `[-1, 0]` for\n * positive price series.\n *\n * @example\n * ```ts\n * import { drawdown } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 105 },\n * { t: new Date('2023-01-04'), v: 95 },\n * { t: new Date('2023-01-05'), v: 98 },\n * ];\n *\n * const dd = drawdown(prices, 3);\n * // dd.length === 2\n * // dd[0].t => new Date('2023-01-04'), dd[0].v => (95-105)/105 ≈ -0.095\n * // dd[1].t => new Date('2023-01-05'), dd[1].v => (98-105)/105 ≈ -0.067\n * ```\n */\nexport function drawdown(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`drawdown: period must be positive, got ${period}`);\n if (series.length < period) return [];\n const out: { t: Date; v: number }[] = [];\n for (let i = period - 1; i < series.length; i++) {\n let max = -Infinity;\n for (let j = i - period + 1; j <= i; j++) {\n if (series[j]!.v > max) max = series[j]!.v;\n }\n out.push({ t: series[i]!.t, v: (series[i]!.v - max) / max });\n }\n return out;\n}\n","import type { Series } from '../interfaces/types';\nimport { sma } from './indicators/sma';\nimport { ema } from './indicators/ema';\nimport { rsi } from './indicators/rsi';\nimport { returnSeries, type ReturnMode } from './indicators/return';\nimport { volatility } from './indicators/volatility';\nimport { drawdown } from './indicators/drawdown';\n\n/**\n * A discriminated union describing every built-in feature kind and its parameters.\n *\n * Each variant has a `kind` field that identifies the indicator together with the\n * parameters that fully determine its output. `FeatureSpec` objects are used as\n * cache keys (via `paramsHash`) and as dispatch tokens (via `getFeatureCompute`).\n *\n * Variants:\n * - `{ kind: 'price' }` — raw price series; no parameters.\n * - `{ kind: 'sma'; period: number }` — simple moving average over `period` bars.\n * - `{ kind: 'ema'; period: number }` — exponential moving average seeded from an SMA.\n * - `{ kind: 'rsi'; period: number }` — Wilder's Relative Strength Index.\n * - `{ kind: 'return'; period: number; mode?: ReturnMode }` — period return; percent or absolute.\n * - `{ kind: 'volatility'; period: number }` — rolling population standard deviation of daily returns.\n * - `{ kind: 'drawdown'; period: number }` — drawdown relative to the rolling maximum.\n */\nexport type FeatureSpec =\n | { kind: 'price' }\n | { kind: 'sma'; period: number }\n | { kind: 'ema'; period: number }\n | { kind: 'rsi'; period: number }\n | { kind: 'return'; period: number; mode?: ReturnMode }\n | { kind: 'volatility'; period: number }\n | { kind: 'drawdown'; period: number };\n\n/**\n * String literal union of all valid feature `kind` values derived from `FeatureSpec`.\n *\n * Useful for typing registry keys and dispatch tables without manually listing all\n * variants: `'price' | 'sma' | 'ema' | 'rsi' | 'return' | 'volatility' | 'drawdown'`.\n */\nexport type FeatureKind = FeatureSpec['kind'];\n\n/**\n * A pure function that computes a feature `Series` from an input price `Series` and the typed\n * `FeatureSpec` describing the indicator's parameters. Implementations must be deterministic and\n * side-effect free — the SDK uses the function as a content-addressed dispatch token via\n * {@link getFeatureCompute} and as the registration target for {@link defineFeature}.\n *\n * @param series - Input price series (typically the asset's close prices).\n * @param spec - Typed indicator spec carrying the parameters relevant to `kind`.\n * @returns The computed feature series, aligned to `series` on `t`.\n */\nexport type ComputeFn = (series: Series, spec: FeatureSpec) => Series;\n\nconst registry = new Map<FeatureKind, ComputeFn>();\n\n/**\n * Registers a compute function for a new or existing feature kind.\n *\n * Throws if `kind` is already registered. Call this once at module initialisation\n * time (top-level) to extend the built-in feature registry with custom indicators.\n * The compute function receives the raw price `Series` and the full typed spec\n * object for that kind; it must return a `Series` of the same or shorter length.\n *\n * @param kind - The `FeatureKind` string that identifies this indicator.\n * @param compute - Pure function that transforms a price series according to `spec`.\n * The `spec` argument is narrowed to `Extract<FeatureSpec, { kind: K }>` so\n * TypeScript enforces that only the correct parameter shape is accessed.\n * @returns `void`. Registration is a side-effectful, one-time operation.\n *\n * @example\n * ```ts\n * import { defineFeature } from '@livefolio/sdk';\n *\n * // Register a custom 'zscore' feature kind\n * defineFeature('sma', (series, spec) => {\n * // Already built-in; this would throw due to duplicate registration.\n * // Shown here for illustration only.\n * return series;\n * });\n * ```\n */\nexport function defineFeature<K extends FeatureKind>(\n kind: K,\n compute: (series: Series, spec: Extract<FeatureSpec, { kind: K }>) => Series,\n): void {\n if (registry.has(kind)) {\n throw new Error(`defineFeature: kind \"${kind}\" is already registered`);\n }\n registry.set(kind, compute as ComputeFn);\n}\n\n/**\n * Retrieves the registered compute function for a given feature kind.\n *\n * Throws `Error` if the kind has not been registered. In normal usage the\n * built-in `defineFeature` calls at the bottom of this module pre-populate the\n * registry, so this function only throws for custom kinds that were never registered.\n *\n * @param kind - The `FeatureKind` string to look up.\n * @returns The `ComputeFn` registered for `kind`.\n *\n * @example\n * ```ts\n * import { getFeatureCompute } from '@livefolio/sdk';\n *\n * const computeSma = getFeatureCompute('sma');\n * const result = computeSma(priceSeries, { kind: 'sma', period: 20 });\n * ```\n */\nexport function getFeatureCompute(kind: FeatureKind): ComputeFn {\n const fn = registry.get(kind);\n if (!fn) throw new Error(`getFeatureCompute: unknown feature kind \"${kind}\"`);\n return fn;\n}\n\n/**\n * Recursive canonicalization: sort object keys, skip undefined values,\n * preserve null + array order, recurse into nested objects.\n */\nfunction canonicalize(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(canonicalize);\n const obj = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const k of Object.keys(obj).sort()) {\n if (obj[k] === undefined) continue;\n sorted[k] = canonicalize(obj[k]);\n }\n return sorted;\n}\n\n/**\n * Returns a deterministic string that depends only on the spec's logical\n * content — key order and undefined optional fields are normalized away.\n *\n * The same logical spec always produces the same hash regardless of how the\n * object was constructed (different key insertion order, explicit `undefined`\n * vs. omitted optional field). This string is used as the `paramsHash` field\n * in `FeatureKey` to ensure cache-hit equivalence for semantically identical\n * specs.\n *\n * Callers depend on this function's contract (same logical content → same\n * result), not on the encoding. Future replacement with SHA-256 is\n * non-breaking.\n *\n * @param spec - The `FeatureSpec` object to hash.\n * @returns A JSON string with sorted keys and no undefined values.\n *\n * @example\n * ```ts\n * import { paramsHash } from '@livefolio/sdk';\n *\n * paramsHash({ kind: 'sma', period: 20 });\n * // => '{\"kind\":\"sma\",\"period\":20}'\n *\n * // Optional field omitted vs explicitly undefined — same result:\n * paramsHash({ kind: 'return', period: 10 });\n * paramsHash({ kind: 'return', period: 10, mode: undefined });\n * // both => '{\"kind\":\"return\",\"period\":10}'\n * ```\n */\nexport function paramsHash(spec: FeatureSpec): string {\n return JSON.stringify(canonicalize(spec));\n}\n\ndefineFeature('price', (series) => series);\ndefineFeature('sma', (series, spec) => sma(series, spec.period));\ndefineFeature('ema', (series, spec) => ema(series, spec.period));\ndefineFeature('rsi', (series, spec) => rsi(series, spec.period));\ndefineFeature('return', (series, spec) => returnSeries(series, spec.period, spec.mode));\ndefineFeature('volatility', (series, spec) => volatility(series, spec.period));\ndefineFeature('drawdown', (series, spec) => drawdown(series, spec.period));\n","import type { Asset, AssetId, Bar, DateRange, Frequency, Series } from '../interfaces/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { FeatureCache, FeatureKey } from '../interfaces/feature-cache';\nimport { collectBars, barsToSeries, type BarField } from './series-utils';\nimport { getFeatureCompute, paramsHash, type FeatureSpec } from './spec';\n\n/** Sentinel range used as the `range` field in cache keys for streaming-mode\n * computations. Epoch-zero dates are never valid historical ranges, so this\n * value cannot collide with any real historical cache entry. */\nconst STREAMING_SENTINEL_RANGE: DateRange = { from: new Date(0), to: new Date(0) };\n\n/** Stub DataFeed used when no `dataFeed` is supplied in streaming mode.\n * Throws immediately if `.bars` is ever called, which surfaces accidental\n * historical-mode code paths at runtime rather than silently returning empty. */\nconst STREAMING_STUB_FEED: DataFeed = {\n bars: () => {\n throw new Error('dataFeed.bars called on streaming-mode FeatureRuntime');\n },\n};\n\n/**\n * Configuration for a `FeatureRuntime` instance.\n *\n * Accepts two shapes — select via the `mode` discriminant:\n *\n * - **`'historical'`** (default, `mode` may be omitted): range-bounded backtest\n * mode. Bars are fetched from `DataFeed` once per asset and cached in memory.\n * - **`'streaming'`**: open-ended live mode. No fixed `range`; bars are pushed\n * in via `appendBar`. Indicator computation reads from the growing in-process\n * buffer instead of calling `DataFeed.bars`.\n *\n * The historical variant is backward-compatible — existing callers that omit\n * `mode` compile and behave identically to before.\n */\nexport type FeatureRuntimeOptions =\n | {\n /** Selects historical (range-bounded) mode. May be omitted; defaults to\n * `'historical'`. */\n mode?: 'historical';\n /** The market-data source used to fetch OHLCV bars. */\n dataFeed: DataFeed;\n /**\n * Persistent indicator cache. Use `MemoryFeatureCache` for a single backtest\n * run, or supply a cross-process cache implementation to share results across\n * multiple runs or processes.\n */\n featureCache: FeatureCache;\n /**\n * The date range over which bars are fetched. This should span at least the\n * backtest range plus any indicator warmup period (e.g. `period - 1` extra\n * bars for SMA/EMA). `FeatureRuntime` does not automatically extend the range\n * for warmup — the caller is responsible for providing enough history.\n */\n range: DateRange;\n /**\n * Bar frequency forwarded to `DataFeed.bars` and embedded in cache keys.\n * Must match the granularity expected by the indicators (e.g. `'1d'` for\n * daily SMA/EMA).\n */\n freq: Frequency;\n /**\n * Which OHLCV field to use as the scalar price series. Defaults to `'close'`.\n * All indicators within a single `FeatureRuntime` instance share the same field.\n */\n field?: BarField;\n }\n | {\n /** Selects streaming (open-ended live) mode. Required. */\n mode: 'streaming';\n /** Optional — not called in streaming mode. Omit in streaming-only usages.\n * If provided it is stored but never invoked; supply only when sharing an\n * options object that also carries a `DataFeed` for other purposes. */\n dataFeed?: DataFeed;\n /**\n * Persistent indicator cache. In streaming mode the cache is bypassed\n * entirely — every `compute` call recomputes from the in-memory bar buffer.\n * The instance is still required so that a shared cache object can be\n * passed without special-casing at the call site.\n */\n featureCache: FeatureCache;\n /**\n * Bar frequency embedded in cache keys. Must match the granularity of the\n * bars pushed via `appendBar`.\n */\n freq: Frequency;\n /**\n * Which OHLCV field to use as the scalar price series. Defaults to `'close'`.\n */\n field?: BarField;\n /**\n * Optional seed bars per asset (keyed by `AssetId`), used to bootstrap the\n * streaming buffer from a prior historical run's `BacktestResult`.\n * Bars must already be in ascending `t` order per asset.\n */\n initialBars?: ReadonlyMap<AssetId, ReadonlyArray<Bar>>;\n };\n\n/**\n * Orchestrates indicator computation for a single backtest run or a live\n * streaming session.\n *\n * `FeatureRuntime` is the bridge between raw OHLCV data (via `DataFeed`) and the\n * typed indicator functions registered in the feature registry. It handles:\n *\n * - **Bar fetching** (historical mode) — Calls `DataFeed.bars` once per\n * `(asset, range, freq)` tuple and caches the resulting `Series` in memory for\n * the lifetime of the instance. Concurrent calls for the same asset share a\n * single in-flight promise; there is no redundant fetching even if `compute` is\n * called from multiple `Promise.all` branches simultaneously.\n * - **Bar buffering** (streaming mode) — Bars are pushed in via `appendBar`.\n * `compute` reads directly from the in-memory buffer; `DataFeed.bars` is never\n * called.\n * - **Indicator dispatch** — Delegates computation to the function registered via\n * `defineFeature` for the given `FeatureSpec.kind`.\n * - **Persistent caching** (historical mode only) — Checks `FeatureCache` before\n * computing. On a miss, the result is stored in the cache. Subsequent calls with\n * the same `(spec, asset)` combination return instantly from cache without\n * re-fetching bars.\n *\n * Caching semantics:\n * - Historical mode: cache keys incorporate `spec`, `asset.id`, `range`, and `freq`.\n * Results are read from and written to `featureCache`.\n * - Streaming mode: `featureCache` is bypassed entirely — every `compute` call\n * recomputes from the growing in-memory bar buffer. The `seriesCache` (per-asset\n * in-memory base-series cache) is the only cache layer; it is invalidated on each\n * `appendBar` so the next `compute` sees the updated buffer.\n * - Calling `appendBar` invalidates the in-memory series cache for that asset so\n * the next `compute` call rebuilds the series from the updated buffer.\n *\n * @example Historical mode (default)\n * ```ts\n * import {\n * FeatureRuntime,\n * MemoryFeatureCache,\n * seriesAt,\n * } from '@livefolio/sdk';\n *\n * const runtime = new FeatureRuntime({\n * dataFeed,\n * featureCache: new MemoryFeatureCache(),\n * range: { from: new Date('2022-01-01'), to: new Date('2023-12-31') },\n * freq: '1d',\n * });\n *\n * const spy = { kind: 'equity' as const, id: 'US:SPY', symbol: 'SPY' };\n * const smaSeries = await runtime.compute({ kind: 'sma', period: 20 }, spy);\n * const latestSma = seriesAt(smaSeries, new Date('2023-06-15'));\n * // => number | undefined\n * ```\n *\n * @example Streaming mode\n * ```ts\n * const runtime = new FeatureRuntime({\n * featureCache: new MemoryFeatureCache(),\n * mode: 'streaming',\n * freq: '1d',\n * initialBars, // optional seed from BacktestResult\n * });\n *\n * runtime.appendBar(spy, latestBar);\n * const smaSeries = await runtime.compute({ kind: 'sma', period: 20 }, spy);\n * ```\n */\nexport class FeatureRuntime {\n private readonly mode: 'historical' | 'streaming';\n private readonly dataFeed: DataFeed;\n private readonly featureCache: FeatureCache;\n private readonly range: DateRange | null;\n private readonly freq: Frequency;\n private readonly field: BarField;\n /** Per-asset bar buffer used in streaming mode. */\n private readonly streamingBars: Map<AssetId, Bar[]>;\n /** Per-asset bar buffer accumulated in historical mode after bars are fetched from DataFeed. */\n private readonly historicalBars: Map<AssetId, Bar[]>;\n /** Per-asset in-flight Series promise — shared across concurrent `compute` calls\n * for the same asset to avoid redundant bar fetching (historical) or rebuilding\n * (streaming). Invalidated on `appendBar`. */\n private readonly seriesCache: Map<AssetId, Promise<Series>>;\n\n constructor(opts: FeatureRuntimeOptions) {\n this.mode = opts.mode ?? 'historical';\n this.featureCache = opts.featureCache;\n this.freq = opts.freq;\n this.field = opts.field ?? 'close';\n this.seriesCache = new Map();\n this.streamingBars = new Map();\n this.historicalBars = new Map();\n\n if (opts.mode === 'streaming') {\n this.dataFeed = opts.dataFeed ?? STREAMING_STUB_FEED;\n this.range = null;\n if (opts.initialBars) {\n for (const [assetId, bars] of opts.initialBars) {\n this.streamingBars.set(assetId, [...bars]);\n }\n }\n } else {\n this.dataFeed = opts.dataFeed;\n this.range = opts.range;\n }\n }\n\n /**\n * Appends a bar to the streaming buffer for the given asset.\n *\n * Bars must be provided in non-decreasing `t` order per asset. A bar with\n * the same `t` as the most recent buffered bar replaces it in place — this\n * is the mark-to-market wiggle path used by `runLive`, where each incoming\n * tick updates the running open/high/low/close of the in-flight session bar.\n * A bar with `t` strictly greater than the buffered tail starts a fresh\n * in-flight bar (e.g. at session boundaries). A bar with `t` strictly less\n * than the buffered tail throws.\n *\n * Also invalidates the in-memory series cache for the asset so the next\n * `compute` call rebuilds the series from the updated buffer.\n *\n * @throws If called on a historical-mode runtime.\n * @throws If `bar.t` is strictly less than the last buffered bar's `t`.\n */\n appendBar(asset: Asset, bar: Bar): void {\n if (this.mode !== 'streaming') {\n throw new Error('appendBar is only valid in streaming mode');\n }\n const buf = this.streamingBars.get(asset.id) ?? [];\n const last = buf[buf.length - 1];\n if (last !== undefined && bar.t.getTime() < last.t.getTime()) {\n throw new Error(\n `appendBar: bars must be in non-decreasing t order; got ${bar.t.toISOString()} after ${last.t.toISOString()}`,\n );\n }\n if (last !== undefined && bar.t.getTime() === last.t.getTime()) {\n // Same-t mark-to-market wiggle: replace the in-flight bar in place.\n // Used by `runLive` to update the running OHLC of today's bar on each\n // tick so feature computation sees the live close.\n buf[buf.length - 1] = bar;\n } else {\n buf.push(bar);\n }\n this.streamingBars.set(asset.id, buf);\n // Invalidate cached series so next compute rebuilds from updated buffer.\n this.seriesCache.delete(asset.id);\n }\n\n private baseSeries(asset: Asset): Promise<Series> {\n const cached = this.seriesCache.get(asset.id);\n if (cached) return cached;\n\n let p: Promise<Series>;\n if (this.mode === 'streaming') {\n const buf = this.streamingBars.get(asset.id) ?? [];\n p = Promise.resolve(barsToSeries(buf, this.field));\n } else {\n // Historical mode: fetch from DataFeed and cache.\n p = (async () => {\n const bars = await collectBars(this.dataFeed.bars(asset, this.range!, this.freq));\n this.historicalBars.set(asset.id, bars);\n return barsToSeries(bars, this.field);\n })();\n }\n\n this.seriesCache.set(asset.id, p);\n return p;\n }\n\n /**\n * Returns the bar buffer for `asset`, or an empty array if none has been\n * fetched/buffered yet.\n *\n * In historical mode this is populated after the first `compute` call that\n * triggers a bar fetch for the asset. In streaming mode it reflects all bars\n * pushed via `appendBar`.\n */\n getBars(asset: Asset): ReadonlyArray<Bar> {\n return this.streamingBars.get(asset.id) ?? this.historicalBars.get(asset.id) ?? [];\n }\n\n /**\n * Returns the full per-asset bar map (keyed by `AssetId`). Merges historical\n * and streaming buffers (streaming wins on collision, but in practice an\n * instance is in one mode at a time so collisions don't occur).\n *\n * Returns a fresh `Map` — callers cannot mutate the internal state.\n */\n getAllBars(): ReadonlyMap<AssetId, ReadonlyArray<Bar>> {\n const merged = new Map<AssetId, ReadonlyArray<Bar>>();\n for (const [id, bars] of this.historicalBars) merged.set(id, bars);\n for (const [id, bars] of this.streamingBars) merged.set(id, bars);\n return merged;\n }\n\n private cacheKey(spec: FeatureSpec, asset: Asset): FeatureKey {\n return {\n feature: spec.kind,\n paramsHash: paramsHash(spec),\n scope: { kind: 'asset', asset: asset.id },\n range: this.mode === 'streaming' ? STREAMING_SENTINEL_RANGE : this.range!,\n freq: this.freq,\n };\n }\n\n /**\n * Computes (or retrieves from cache) the output `Series` for a given feature\n * spec applied to a specific asset.\n *\n * **Historical mode:** on the first call for a `(spec, asset)` pair:\n * 1. Fetches or reuses the in-memory base `Series` for the asset.\n * 2. Dispatches to the registered compute function for `spec.kind`.\n * 3. Stores the result in `featureCache`.\n * On subsequent calls, returns the cached `Series` directly from `featureCache`.\n *\n * **Streaming mode:** reads from the in-memory bar buffer populated via\n * `appendBar`. `DataFeed.bars` is never called. The persistent `featureCache`\n * is bypassed entirely — results are never read from or written to it.\n * The in-memory `seriesCache` (base series per asset) is the only cache layer\n * and is invalidated on each `appendBar`, so every `compute` after a new bar\n * reflects the updated buffer.\n *\n * @param spec - The feature specification describing which indicator to compute\n * and its parameters (e.g. `{ kind: 'sma', period: 20 }`).\n * @param asset - The asset for which to compute the feature. The asset's `id`\n * is used both for data fetching and cache key construction.\n * @returns A promise that resolves to the computed `Series`. The series length\n * is determined by the indicator's warmup: for example, SMA(20) returns\n * `series.length - 19` data points. Returns an empty array when the\n * base series is shorter than the indicator's warmup period.\n *\n * @example\n * ```ts\n * const spy = { kind: 'equity' as const, id: 'US:SPY', symbol: 'SPY' };\n *\n * const [priceSeries, smaSeries] = await Promise.all([\n * runtime.compute({ kind: 'price' }, spy),\n * runtime.compute({ kind: 'sma', period: 20 }, spy),\n * ]);\n * ```\n */\n async compute(spec: FeatureSpec, asset: Asset): Promise<Series> {\n const key = this.cacheKey(spec, asset);\n if (this.mode !== 'streaming') {\n const cached = await this.featureCache.get(key);\n if (cached) return cached;\n }\n const base = await this.baseSeries(asset);\n const compute = getFeatureCompute(spec.kind);\n const result = compute(base, spec);\n if (this.mode !== 'streaming') {\n await this.featureCache.set(key, result);\n }\n return result;\n }\n}\n","import type { FeatureCache, FeatureKey } from '../interfaces/feature-cache';\nimport type { Series } from '../interfaces/types';\n\nfunction canonicalKey(key: FeatureKey): string {\n const scopePart = key.scope.kind === 'asset' ? `asset:${key.scope.asset}` : `universe:${key.scope.universeHash}`;\n return [\n `feat=${key.feature}`,\n `params=${key.paramsHash}`,\n `scope=${scopePart}`,\n `from=${key.range.from.toISOString()}`,\n `to=${key.range.to.toISOString()}`,\n `freq=${key.freq}`,\n ].join('|');\n}\n\nfunction canonicalPrefix(prefix: Partial<FeatureKey>): string {\n const parts: string[] = [];\n if (prefix.feature !== undefined) parts.push(`feat=${prefix.feature}`);\n if (prefix.paramsHash !== undefined) parts.push(`params=${prefix.paramsHash}`);\n if (prefix.scope !== undefined) {\n const scopePart =\n prefix.scope.kind === 'asset' ? `asset:${prefix.scope.asset}` : `universe:${prefix.scope.universeHash}`;\n parts.push(`scope=${scopePart}`);\n }\n if (prefix.range !== undefined) {\n parts.push(`from=${prefix.range.from.toISOString()}`);\n parts.push(`to=${prefix.range.to.toISOString()}`);\n }\n if (prefix.freq !== undefined) parts.push(`freq=${prefix.freq}`);\n return parts.join('|');\n}\n\n/**\n * In-process, Map-backed implementation of {@link FeatureCache}. Caches\n * computed indicator series in memory for the lifetime of the instance.\n * There is no eviction policy — the cache grows until the instance is\n * garbage-collected.\n *\n * **When to use**: the right choice for single-run backtests or unit tests\n * where the full dataset fits in process memory and cross-run persistence is\n * not required. For long-running hosted services or multi-process setups,\n * substitute a persistent implementation (e.g. Redis-backed) that satisfies\n * the {@link FeatureCache} interface.\n *\n * Cache keys are content-addressed strings composed of `(feature kind, params\n * hash, asset scope, date range, frequency)` — see the internal\n * `canonicalKey` function. The `invalidate` method performs prefix-based\n * deletion using the same key segments.\n *\n * @example\n * ```ts\n * import { MemoryFeatureCache } from '@livefolio/sdk';\n * import { FeatureRuntime } from '@livefolio/sdk/features';\n *\n * const cache = new MemoryFeatureCache();\n * const runtime = new FeatureRuntime({ feed: myDataFeed, cache });\n * ```\n */\nexport class MemoryFeatureCache implements FeatureCache {\n private store = new Map<string, Series>();\n\n async get(key: FeatureKey): Promise<Series | undefined> {\n return this.store.get(canonicalKey(key));\n }\n\n async set(key: FeatureKey, series: Series): Promise<void> {\n this.store.set(canonicalKey(key), series);\n }\n\n async invalidate(prefix: Partial<FeatureKey>): Promise<void> {\n const needles = canonicalPrefix(prefix).split('|').filter(Boolean);\n if (needles.length === 0) return;\n for (const k of [...this.store.keys()]) {\n if (needles.every((n) => k.includes(n))) this.store.delete(k);\n }\n }\n}\n","import type { Asset, AssetId, Bar } from '../interfaces/types';\nimport type { StreamingDataFeed } from '../interfaces/streaming-data-feed';\nimport type { Executor } from '../interfaces/executor';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { Order } from '../orders/types';\nimport type { Portfolio } from '../portfolio/types';\nimport type { Strategy, Features } from './types';\nimport type { BacktestResult, BacktestSnapshot } from './run-backtest';\nimport { isStateResult } from './run-backtest';\nimport { FeatureRuntime } from '../features/runtime';\nimport { MemoryFeatureCache } from '../reference/memory-feature-cache';\nimport { applyFills } from '../portfolio/apply';\n\n/**\n * Unified event stream from {@link runLive}. Discriminated union of two variants:\n *\n * - **`mark`** — emitted per tick. The strategy is run in PREVIEW mode (state\n * is snapshot/restored, no executor call, no portfolio commit). Use this to\n * render the wiggling rightmost chart point and the \"if the session ended now,\n * the strategy would do X\" preview UX.\n * - **`snapshot`** — emitted when a tick crosses a session boundary. The\n * just-closed bar is finalized, `strategy.build` runs for real, orders are\n * submitted to the executor, fills are applied, and state advances. Same\n * shape as {@link BacktestSnapshot} from {@link runBacktest} (plus the\n * `type: 'snapshot'` discriminant), so consumers can append snapshot events\n * to the same chart array used by historical results.\n */\nexport type LiveEvent<F extends Features = Features, _S = unknown> =\n | {\n type: 'mark';\n /** Wall-clock arrival time of this tick. */\n t: Date;\n /** Portfolio at the start of the current session — unchanged by the preview. */\n portfolio: Portfolio;\n /**\n * Per-asset accumulating close so far in the current session. Only assets\n * that have received at least one tick this session appear in the map.\n */\n prices: ReadonlyMap<AssetId, number>;\n /** Features recomputed for the in-progress session. */\n features: F;\n /**\n * Orders the strategy would emit if the session closed at the current\n * tick price. Computed from a state SNAPSHOT — the returned `state` value\n * is discarded, so no committed state is mutated.\n */\n previewOrders: ReadonlyArray<Order>;\n /**\n * Best-effort placeholder — returns the unchanged portfolio. A future\n * `simulateFills(orders, prices)` helper will compute the hypothetical\n * post-rebalance NAV that would result from applying `previewOrders` at\n * `prices`. Until then, consumers compute NAV themselves from\n * `portfolio` + `prices`.\n */\n previewPortfolio: Portfolio;\n }\n | (BacktestSnapshot & { type: 'snapshot' });\n\n/** Required inputs to {@link runLive}. */\nexport type RunLiveOptions<F extends Features = Features, S = unknown> = {\n /** The strategy to drive. If its `features` method depends on a captured\n * `FeatureRuntime` (e.g. tactical strategies built via `fromSpec`), pass the\n * same runtime instance via {@link RunLiveOptions.streamingRuntime} so the\n * live bar buffer stays in sync with what the strategy reads. */\n strategy: Strategy<F, S>;\n /**\n * Result of a prior {@link runBacktest} call. Provides the seed `portfolio`,\n * `state`, and `bars` map for the streaming runtime.\n */\n history: BacktestResult<S>;\n /** Source of streaming ticks. */\n dataFeed: StreamingDataFeed;\n /** Order router used at session boundaries to settle the just-closed bar. */\n executor: Executor;\n /** Calendar that resolves a tick's wall-clock time into its session date. */\n calendar: Calendar;\n /**\n * Optional streaming {@link FeatureRuntime}. Provide this to share the\n * runtime with the strategy — tactical strategies built via `fromSpec`\n * capture a runtime in their `features` closure, so passing the same\n * instance here keeps the live bar buffer in sync with what the strategy\n * reads. When omitted, `runLive` constructs its own streaming runtime\n * seeded from `history.bars`.\n */\n streamingRuntime?: FeatureRuntime;\n};\n\n/**\n * Returns a structurally equivalent copy of `state` so previews cannot mutate\n * the committed state value. Uses `structuredClone` (Node ≥20). State must be\n * structured-cloneable — JSON-serializable types plus Date/Map/Set/etc.\n */\nfunction snapshotState<S>(state: S | undefined): S | undefined {\n if (state === undefined) return undefined;\n return structuredClone(state);\n}\n\n/**\n * Returns the trading-day key (midnight UTC) for the session containing\n * instant `t`, as resolved by the supplied {@link Calendar}. Two ticks belong\n * to the same session iff `findSession(t1) === findSession(t2)`.\n *\n * Uses `calendar.next(t)` to find the next trading day strictly after `t`,\n * then `calendar.previous` to back-anchor to the session that contains `t`.\n * For NYSE: a tick at Friday 17:00 ET (after-close) has `next` = Monday and\n * `previous(Monday)` = Friday — the correct session anchor.\n */\nfunction findSession(t: Date, calendar: Calendar): Date {\n const next = calendar.next(t);\n return calendar.previous(next);\n}\n\n/**\n * Drives a {@link Strategy} against a streaming market-data source and yields\n * a unified event stream that consumer charts can append to historical\n * snapshots without code branching.\n *\n * **Lifecycle on each tick:**\n * 1. Resolve the tick's session date via the supplied {@link Calendar} —\n * `calendar.previous(calendar.next(tick.t))`. This correctly handles\n * after-hours ticks (NYSE 17:00 ET stays in the same session) and DST\n * transitions.\n * 2. If the tick crosses a session boundary, finalize the just-closed bar:\n * append it to the streaming `FeatureRuntime`, run `strategy.build` for\n * REAL (committing state), submit orders to the executor, apply fills,\n * and yield a `snapshot` event identical in shape to {@link BacktestSnapshot}.\n * 3. Record the tick into the current session's accumulating bar.\n * 4. Re-run `strategy.features` and `strategy.build` in PREVIEW mode (state\n * is snapshot/restored — committed state is untouched). Yield a `mark`\n * event with the recomputed features and preview orders.\n *\n * **State semantics:** preview-build always operates on a deep clone of the\n * committed `state`. Only the boundary-crossing commit branch advances\n * committed state. This guarantees that 1000 ticks within a single session\n * produce 1000 marks but leave `state` exactly where the prior session-close\n * commit left it.\n *\n * **FeatureRuntime:** if the strategy was built via `fromSpec` it captures its\n * own runtime in the `features` closure. Pass that same instance via\n * {@link RunLiveOptions.streamingRuntime} so `appendBar` calls land on the\n * runtime the strategy actually reads. When omitted, `runLive` constructs its\n * own streaming runtime seeded from `history.bars` — this works for hand-rolled\n * strategies whose `features` method consults the runtime directly, but it\n * leaves a `fromSpec` strategy reading a stale captured runtime.\n *\n * **Bar lineage:** the streaming `FeatureRuntime` (provided or constructed) is\n * seeded from `history.bars`, so indicators with warmup periods (SMA(200),\n * etc.) work on the first live tick.\n *\n * **Universe:** captured once at startup from\n * `strategy.universe(anchorTime, portfolio)`, where `anchorTime` is the last\n * historical snapshot's timestamp (or epoch zero for empty history). Dynamic\n * universes are not yet supported in live mode.\n *\n * **Termination:** the iterable terminates when the underlying\n * `StreamingDataFeed.subscribe` iterable terminates. Real adapters yield\n * forever; tests use bounded iterables to assert specific event sequences.\n *\n * @param opts - Live-runtime configuration. See {@link RunLiveOptions}.\n * @returns An open-ended `AsyncIterable<LiveEvent>`. Consumers `for await` the\n * stream and dispatch on `ev.type`.\n *\n * @example\n * ```ts\n * for await (const ev of runLive({ strategy, history, dataFeed, executor, calendar })) {\n * if (ev.type === 'mark') {\n * chart.updateLastBar({ t: ev.t, prices: ev.prices, previewOrders: ev.previewOrders });\n * } else {\n * chart.appendBar(ev); // BacktestSnapshot-shaped\n * }\n * }\n * ```\n */\nexport async function* runLive<F extends Features = Features, S = unknown>(\n opts: RunLiveOptions<F, S>,\n): AsyncIterable<LiveEvent<F, S>> {\n const { strategy, history, dataFeed, executor, calendar } = opts;\n\n // Streaming FeatureRuntime: prefer a caller-supplied instance (so `fromSpec`\n // strategies that captured a runtime keep reading the same buffer we append\n // to). Otherwise build one seeded from `history.bars` for hand-rolled\n // strategies that consult the runtime directly.\n const runtime =\n opts.streamingRuntime ??\n new FeatureRuntime({\n mode: 'streaming',\n featureCache: new MemoryFeatureCache(),\n freq: '1d',\n initialBars: history.bars,\n });\n\n let portfolio = history.finalPortfolio;\n let state: S | undefined = history.finalState;\n // Universe is captured once at startup using the last historical snapshot's\n // timestamp as an anchor (or epoch zero for empty history). Dynamic\n // universes are not yet supported in live mode.\n const anchorTime = history.snapshots.length > 0 ? history.snapshots[history.snapshots.length - 1]!.t : new Date(0);\n const universe = strategy.universe(anchorTime, portfolio);\n\n // Track the current session being accumulated. When history is non-empty,\n // its last snapshot represents an already-committed session, so the next\n // session to accumulate is `calendar.next(lastSnapshot.t)`. Without this\n // advance, the first live tick whose session exceeds `lastSnapshot.t` would\n // re-fire the boundary and emit a duplicate snapshot for the already-closed\n // session. With empty history, we lazily adopt the first tick's session.\n let currentSession: Date | null =\n history.snapshots.length > 0 ? calendar.next(history.snapshots[history.snapshots.length - 1]!.t) : null;\n let currentBarOpen = new Map<AssetId, number>();\n let currentBarHigh = new Map<AssetId, number>();\n let currentBarLow = new Map<AssetId, number>();\n let currentBarClose = new Map<AssetId, number>();\n\n function recordTick(asset: Asset, tickBar: Bar): void {\n const id = asset.id;\n const price = tickBar.close;\n if (!currentBarOpen.has(id)) currentBarOpen.set(id, price);\n currentBarHigh.set(id, Math.max(currentBarHigh.get(id) ?? -Infinity, price));\n currentBarLow.set(id, Math.min(currentBarLow.get(id) ?? Infinity, price));\n currentBarClose.set(id, price);\n }\n\n function finalizeBars(sessionDate: Date): void {\n for (const asset of universe) {\n const close = currentBarClose.get(asset.id);\n if (close === undefined) continue;\n runtime.appendBar(asset, {\n t: sessionDate,\n open: currentBarOpen.get(asset.id)!,\n high: currentBarHigh.get(asset.id)!,\n low: currentBarLow.get(asset.id)!,\n close,\n volume: 0,\n });\n }\n currentBarOpen = new Map();\n currentBarHigh = new Map();\n currentBarLow = new Map();\n currentBarClose = new Map();\n }\n\n for await (const tick of dataFeed.subscribe(universe)) {\n const tickSession = findSession(tick.bar.t, calendar);\n\n if (currentSession === null) {\n currentSession = tickSession;\n }\n\n // Boundary crossed: finalize the previous session's bar, run REAL build,\n // submit orders, apply fills, yield snapshot, then start the new session.\n if (tickSession.getTime() > currentSession.getTime()) {\n finalizeBars(currentSession);\n const sessionFeatures = await strategy.features(universe, portfolio, currentSession);\n const buildResult = strategy.build(sessionFeatures, portfolio, state as S, currentSession);\n let orders: ReadonlyArray<Order>;\n if (isStateResult(buildResult)) {\n orders = buildResult.orders;\n state = buildResult.state;\n } else {\n orders = buildResult;\n }\n const fills = await executor.submit(orders, currentSession, portfolio);\n portfolio = applyFills(portfolio, fills, orders);\n yield {\n type: 'snapshot',\n t: currentSession,\n portfolio,\n orders,\n fills,\n };\n currentSession = tickSession;\n }\n\n // Record the tick into the current session's accumulating bar.\n recordTick(tick.asset, tick.bar);\n\n // Wiggle: push the in-flight session bar into the streaming runtime so\n // feature computations see the running close. `runtime.appendBar` allows\n // same-t replacement, so subsequent ticks within the session overwrite\n // the in-flight bar in place. Without this step, features would be\n // pinned to yesterday's close — preview decisions would be stable\n // through the session and only refresh at session-close finalization,\n // which contradicts the model that every tick is \"as if the session\n // closed at this price.\"\n for (const asset of universe) {\n const close = currentBarClose.get(asset.id);\n if (close === undefined) continue;\n runtime.appendBar(asset, {\n t: currentSession,\n open: currentBarOpen.get(asset.id)!,\n high: currentBarHigh.get(asset.id)!,\n low: currentBarLow.get(asset.id)!,\n close,\n volume: 0,\n });\n }\n\n // Preview: snapshot state, recompute features, run build with the snapshot,\n // discard the returned state. Committed `state` is never touched here.\n const prices = new Map(currentBarClose);\n const features = await strategy.features(universe, portfolio, tick.bar.t);\n const previewState = snapshotState(state);\n const previewResult = strategy.build(features, portfolio, previewState as S, tick.bar.t);\n const previewOrders: ReadonlyArray<Order> = isStateResult(previewResult) ? previewResult.orders : previewResult;\n\n yield {\n type: 'mark',\n t: tick.bar.t,\n portfolio,\n prices,\n features,\n // TODO: replace with `simulateFills(previewOrders, prices)` when the\n // helper lands so consumers see the hypothetical post-rebalance NAV.\n previewOrders,\n previewPortfolio: portfolio,\n };\n }\n}\n","import type { Executor } from '../interfaces/executor';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { Asset } from '../interfaces/types';\nimport type { Order, Fill } from '../orders/types';\nimport type { Portfolio } from '../portfolio/types';\n\n/**\n * Callback that resolves the next-open price for `asset` as seen from date `t`.\n * {@link BacktestExecutor} calls this once per order to determine the fill price.\n *\n * The function should return the opening price of the first trading session\n * strictly after `t`, along with that session's timestamp. In a typical\n * backtest setup this reads from the same data feed used to compute features.\n *\n * @param asset - The instrument being filled.\n * @param t - The date on which the rebalance order was submitted (the\n * \"signal date\"). The fill should occur on the next open after\n * this date to avoid look-ahead.\n * @returns An object with the fill timestamp `t` and the opening `price`.\n */\nexport type NextOpenFn = (asset: Asset, t: Date) => Promise<{ t: Date; price: number }>;\n\n/**\n * Constructor options for {@link BacktestExecutor}.\n */\nexport type BacktestExecutorOptions = {\n /** Exchange calendar used to route fills to the next open session. */\n calendar: Calendar;\n /**\n * Callback that resolves the next-open price for a given asset and date.\n * See {@link NextOpenFn} for the exact contract.\n */\n nextOpen: NextOpenFn;\n /**\n * One-way slippage in basis points applied to every fill. The fill price is\n * adjusted by `price × (1 + sign × slippageBps / 10 000)` where `sign` is\n * `+1` for buys and `−1` for sells. Defaults to `0`.\n */\n slippageBps?: number;\n /**\n * Flat per-share commission in the portfolio's base currency. Multiplied by\n * the fill quantity and recorded in `Fill.fees`. Defaults to `0`.\n */\n perShareFee?: number;\n};\n\nfunction resolveAsset(order: Order, portfolio: Portfolio): { asset: Asset; sign: 1 | -1; qty: number } {\n switch (order.kind) {\n case 'open':\n return { asset: order.asset, sign: order.side === 'long' ? 1 : -1, qty: order.quantity };\n case 'rebalance':\n return { asset: order.asset, sign: order.delta >= 0 ? 1 : -1, qty: Math.abs(order.delta) };\n case 'close': {\n const p = portfolio.positions.find((x) => x.id === order.positionId);\n if (!p) throw new Error(`BacktestExecutor: close target position ${order.positionId} not found`);\n return { asset: p.asset, sign: p.side === 'long' ? -1 : 1, qty: order.quantity ?? p.quantity };\n }\n case 'adjust': {\n const p = portfolio.positions.find((x) => x.id === order.positionId);\n if (!p) throw new Error(`BacktestExecutor: adjust target position ${order.positionId} not found`);\n const target = order.changes.quantity ?? p.quantity;\n const delta = target - p.quantity;\n return { asset: p.asset, sign: delta >= 0 ? 1 : -1, qty: Math.abs(delta) };\n }\n }\n}\n\n/**\n * Reference {@link Executor} implementation for backtesting. Fills each order\n * at the next-open price returned by the {@link NextOpenFn} callback, with\n * optional slippage and per-share commissions applied.\n *\n * **When to use**: suitable for historical simulations and unit tests where\n * real broker connectivity is not needed. For live or paper trading, substitute\n * a broker-backed `Executor` that satisfies the same interface.\n *\n * **Fill mechanics**: for each order in `orders`, the executor calls\n * `opts.nextOpen(asset, t)` to obtain the fill price and timestamp. The\n * raw price is then adjusted for slippage:\n * ```\n * adjustedPrice = nextOpen.price × (1 + sign × slippageBps / 10 000)\n * ```\n * where `sign` is `+1` for net-buy direction and `−1` for net-sell direction.\n * A flat per-share fee is added to `Fill.fees`. Orders with zero quantity are\n * silently skipped.\n *\n * @example\n * ```ts\n * import { BacktestExecutor } from '@livefolio/sdk';\n * import { getCalendar } from '@livefolio/sdk';\n *\n * const executor = new BacktestExecutor({\n * calendar: getCalendar('NYSE'),\n * nextOpen: async (asset, t) => {\n * // Return the first open bar strictly after t from your data feed.\n * const bar = await feed.nextBar(asset, t);\n * return { t: bar.t, price: bar.open };\n * },\n * slippageBps: 5, // 0.05% one-way\n * perShareFee: 0.005,\n * });\n * ```\n */\nexport class BacktestExecutor implements Executor {\n constructor(private readonly opts: BacktestExecutorOptions) {}\n\n async submit(orders: ReadonlyArray<Order>, t: Date, portfolio: Portfolio): Promise<ReadonlyArray<Fill>> {\n const fills: Fill[] = [];\n const slip = (this.opts.slippageBps ?? 0) / 10_000;\n const feePer = this.opts.perShareFee ?? 0;\n\n for (const order of orders) {\n const { asset, sign, qty } = resolveAsset(order, portfolio);\n if (qty === 0) continue;\n const open = await this.opts.nextOpen(asset, t);\n const adjustedPrice = open.price * (1 + sign * slip);\n fills.push({\n orderRef: order.id,\n t: open.t,\n quantity: qty,\n price: adjustedPrice,\n fees: feePer * qty,\n });\n }\n return fills;\n }\n}\n","import type { Asset, Bar, DateRange, Frequency } from '../interfaces/types';\nimport type { DataFeed, Fundamentals } from '../interfaces/data-feed';\n\n/**\n * Error thrown by {@link RoutingDataFeed} when an asset cannot be routed or\n * when the routed feed does not support the requested optional method.\n *\n * Distinguish the two cases via the message text: \"no feed registered\" vs\n * \"does not implement fundamentals\".\n */\nexport class RoutingDataFeedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoutingDataFeedError';\n }\n}\n\n/** Function form of the routing rule. Returns the feed for `asset`, or `undefined` when no feed handles it. */\nexport type RoutingDataFeedRouteFn = (asset: Asset) => DataFeed | undefined;\n\n/** Map form of the routing rule. Keys are `Asset['kind']` discriminants. */\nexport type RoutingDataFeedRouteMap = Readonly<Partial<Record<Asset['kind'], DataFeed>>>;\n\n/**\n * A {@link DataFeed} that delegates each call to one of several underlying\n * feeds based on the asset. Use this to compose vendors — e.g. Yahoo for\n * equities and FRED for macro series — behind a single `DataFeed` instance\n * accepted by `runBacktest`, `FeatureRuntime`, and `BacktestExecutor`.\n *\n * Routing rules:\n * - **Map form:** `new RoutingDataFeed({ equity: yahoo, macro: fred })`.\n * Keys are `asset.kind` discriminants. The 90% case.\n * - **Function form:** `new RoutingDataFeed((a) => a.kind === 'macro' ? fred : yahoo)`.\n * Use when routing depends on more than `kind` (e.g. allowlists).\n *\n * The router does **not** implement `events()` — the optional method is\n * genuinely absent (`'events' in router === false`). Cross-feed event\n * fan-out is deferred until a real consumer materializes.\n *\n * @example\n * ```ts\n * import { RoutingDataFeed } from '@livefolio/sdk';\n *\n * const feed = new RoutingDataFeed({ equity: yahooFeed, macro: fredFeed });\n *\n * const result = await runBacktest({\n * strategy, range, initialPortfolio,\n * dataFeed: feed,\n * executor,\n * calendar,\n * });\n * ```\n */\nexport class RoutingDataFeed implements DataFeed {\n private readonly route: RoutingDataFeedRouteFn;\n\n constructor(routes: RoutingDataFeedRouteMap | RoutingDataFeedRouteFn) {\n if (typeof routes === 'function') {\n this.route = routes;\n } else {\n this.route = (asset) => routes[asset.kind];\n }\n }\n\n // Async generator (rather than plain delegation) so resolve() runs lazily on\n // the first next() call, surfacing errors via the iterable's normal rejection\n // path instead of throwing synchronously at call time.\n async *bars(asset: Asset, range: DateRange, freq: Frequency): AsyncGenerator<Bar> {\n const feed = this.resolve(asset);\n yield* feed.bars(asset, range, freq);\n }\n\n async fundamentals(asset: Asset, t: Date): Promise<Fundamentals> {\n const feed = this.resolve(asset);\n if (typeof feed.fundamentals !== 'function') {\n throw new RoutingDataFeedError(\n `RoutingDataFeed: routed feed for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\") does not implement fundamentals()`,\n );\n }\n return feed.fundamentals(asset, t);\n }\n\n private resolve(asset: Asset): DataFeed {\n const feed = this.route(asset);\n if (feed === undefined) {\n throw new RoutingDataFeedError(\n `RoutingDataFeed: no feed registered for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\")`,\n );\n }\n return feed;\n }\n}\n","import type { Asset } from '../interfaces/types';\nimport type { StreamingDataFeed, StreamingBar } from '../interfaces/streaming-data-feed';\n\n/**\n * Error thrown by {@link RoutingStreamingDataFeed} when an asset cannot be routed.\n */\nexport class RoutingStreamingDataFeedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoutingStreamingDataFeedError';\n }\n}\n\n/** Function form of the routing rule. Returns the feed for `asset`, or `undefined` when no feed handles it. */\nexport type RoutingStreamingDataFeedRouteFn = (asset: Asset) => StreamingDataFeed | undefined;\n\n/** Map form of the routing rule. Keys are `Asset['kind']` discriminants. */\nexport type RoutingStreamingDataFeedRouteMap = Readonly<Partial<Record<Asset['kind'], StreamingDataFeed>>>;\n\n/**\n * A {@link StreamingDataFeed} that delegates `subscribe()` to one of several\n * underlying feeds based on the asset. Use this to compose vendors — e.g.\n * Polygon for equities and a polling adapter for macro series — behind a\n * single `StreamingDataFeed` instance accepted by `runLive`.\n *\n * Routing rules:\n * - **Map form:** `new RoutingStreamingDataFeed({ equity: polygon, macro: polling })`.\n * Keys are `asset.kind` discriminants. The 90% case.\n * - **Function form:** `new RoutingStreamingDataFeed((a) => a.kind === 'macro' ? polling : polygon)`.\n * Use when routing depends on more than `kind` (e.g. allowlists).\n *\n * Assets are grouped by routed feed (by reference identity) before calling\n * upstream `subscribe()` — so a vendor adapter that opens one socket for\n * `[AAPL, MSFT]` keeps doing that rather than receiving one-asset-at-a-time calls.\n *\n * @example\n * ```ts\n * import { RoutingStreamingDataFeed, pollingStreamFromHistorical } from '@livefolio/sdk';\n *\n * const feed = new RoutingStreamingDataFeed({\n * equity: polygonStreaming,\n * macro: pollingStreamFromHistorical({ feed: fredHistorical, freq: '1d', schedule: { kind: 'session-close', calendar: nyse } }),\n * });\n * ```\n */\nexport class RoutingStreamingDataFeed implements StreamingDataFeed {\n private readonly route: RoutingStreamingDataFeedRouteFn;\n\n constructor(routes: RoutingStreamingDataFeedRouteMap | RoutingStreamingDataFeedRouteFn) {\n if (typeof routes === 'function') {\n this.route = routes;\n } else {\n this.route = (asset) => routes[asset.kind];\n }\n }\n\n subscribe(assets: ReadonlyArray<Asset>): AsyncIterable<StreamingBar> {\n return this.merged(assets);\n }\n\n // Async generator so routing/grouping errors surface on first next() rather\n // than throwing synchronously at subscribe() call time — matches RoutingDataFeed.bars() shape.\n private async *merged(assets: ReadonlyArray<Asset>): AsyncGenerator<StreamingBar> {\n if (assets.length === 0) return;\n\n const groups = new Map<StreamingDataFeed, Asset[]>();\n for (const asset of assets) {\n const feed = this.route(asset);\n if (feed === undefined) {\n throw new RoutingStreamingDataFeedError(\n `RoutingStreamingDataFeed: no feed registered for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\")`,\n );\n }\n const list = groups.get(feed) ?? [];\n list.push(asset);\n groups.set(feed, list);\n }\n\n const iters = [...groups.entries()].map(([feed, group]) => feed.subscribe(group)[Symbol.asyncIterator]());\n yield* mergeIterators(iters);\n }\n}\n\nasync function* mergeIterators(iters: ReadonlyArray<AsyncIterator<StreamingBar>>): AsyncGenerator<StreamingBar> {\n type Slot = {\n iter: AsyncIterator<StreamingBar>;\n promise: Promise<{ idx: number; r: IteratorResult<StreamingBar> }>;\n };\n const live = new Map<number, Slot>();\n\n const arm = (idx: number, iter: AsyncIterator<StreamingBar>): void => {\n live.set(idx, {\n iter,\n promise: iter.next().then((r) => ({ idx, r })),\n });\n };\n\n iters.forEach((iter, idx) => arm(idx, iter));\n\n try {\n while (live.size > 0) {\n const { idx, r } = await Promise.race([...live.values()].map((s) => s.promise));\n if (r.done) {\n live.delete(idx);\n } else {\n yield r.value;\n const slot = live.get(idx);\n if (slot) arm(idx, slot.iter);\n }\n }\n } finally {\n await Promise.allSettled(\n [...live.values()].map((s) => (s.iter.return ? s.iter.return(undefined) : Promise.resolve())),\n );\n }\n}\n","import type { Asset } from '../interfaces/types';\nimport type { Quote, QuoteFeed } from '../interfaces/quote-feed';\n\n/**\n * Error thrown by {@link RoutingQuoteFeed} when an asset cannot be routed.\n */\nexport class RoutingQuoteFeedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoutingQuoteFeedError';\n }\n}\n\n/** Function form of the routing rule. Returns the feed for `asset`, or `undefined` when no feed handles it. */\nexport type RoutingQuoteFeedRouteFn = (asset: Asset) => QuoteFeed | undefined;\n\n/** Map form of the routing rule. Keys are `Asset['kind']` discriminants. */\nexport type RoutingQuoteFeedRouteMap = Readonly<Partial<Record<Asset['kind'], QuoteFeed>>>;\n\n/**\n * A {@link QuoteFeed} that delegates each call to one of several underlying\n * feeds based on the asset. Use this to compose vendors — e.g. Alpaca for\n * equity quotes and a polling adapter for macro series — behind a single\n * `QuoteFeed` instance.\n *\n * Routing rules:\n * - **Map form:** `new RoutingQuoteFeed({ equity: alpaca, macro: fredPolling })`.\n * Keys are `asset.kind` discriminants. The 90% case.\n * - **Function form:** `new RoutingQuoteFeed((a) => a.kind === 'macro' ? fred : alpaca)`.\n * Use when routing depends on more than `kind` (e.g. allowlists).\n *\n * The router always implements `quoteBatch` — even if some inner feeds lack\n * it, the router falls back to per-asset `quote()` calls within that group,\n * preserving request order across the full result.\n *\n * @example\n * ```ts\n * import { RoutingQuoteFeed } from '@livefolio/sdk';\n *\n * const feed = new RoutingQuoteFeed({ equity: alpacaQuotes, macro: fredQuotes });\n * const quotes = await feed.quoteBatch([aaplAsset, dgs10Asset, msftAsset]);\n * // quotes[0] is for AAPL, quotes[1] for DGS10, quotes[2] for MSFT — request order preserved.\n * ```\n */\nexport class RoutingQuoteFeed implements QuoteFeed {\n private readonly route: RoutingQuoteFeedRouteFn;\n\n constructor(routes: RoutingQuoteFeedRouteMap | RoutingQuoteFeedRouteFn) {\n if (typeof routes === 'function') {\n this.route = routes;\n } else {\n this.route = (asset) => routes[asset.kind];\n }\n }\n\n async quote(asset: Asset): Promise<Quote> {\n return this.resolve(asset).quote(asset);\n }\n\n async quoteBatch(assets: ReadonlyArray<Asset>): Promise<ReadonlyArray<Quote>> {\n if (assets.length === 0) return [];\n\n // Group by routed feed, tracking original index so we can re-collect in request order.\n // Resolve eagerly so unroutable assets throw before any vendor call.\n const groups = new Map<QuoteFeed, Array<{ asset: Asset; index: number }>>();\n for (let i = 0; i < assets.length; i++) {\n const asset = assets[i]!;\n const feed = this.resolve(asset);\n const bucket = groups.get(feed) ?? [];\n bucket.push({ asset, index: i });\n groups.set(feed, bucket);\n }\n\n const output = new Array<Quote>(assets.length);\n\n await Promise.all(\n [...groups.entries()].map(async ([feed, bucket]) => {\n const bucketAssets = bucket.map((b) => b.asset);\n const results =\n typeof feed.quoteBatch === 'function'\n ? await feed.quoteBatch(bucketAssets)\n : await Promise.all(bucketAssets.map((a) => feed.quote(a)));\n for (let i = 0; i < bucket.length; i++) {\n output[bucket[i]!.index] = results[i]!;\n }\n }),\n );\n\n return output;\n }\n\n private resolve(asset: Asset): QuoteFeed {\n const feed = this.route(asset);\n if (feed === undefined) {\n throw new RoutingQuoteFeedError(\n `RoutingQuoteFeed: no feed registered for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\")`,\n );\n }\n return feed;\n }\n}\n","import type { Asset, AssetId, Frequency } from '../interfaces/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { StreamingDataFeed, StreamingBar } from '../interfaces/streaming-data-feed';\n\nexport type PollingSchedule = { kind: 'interval'; intervalMs: number } | { kind: 'session-close'; calendar: Calendar };\n\nexport type PollingStreamOptions = {\n /** Historical feed to poll. Each tick of the schedule calls `feed.bars(asset, …)` for each subscribed asset. */\n feed: DataFeed;\n /** Bar frequency to request. Single value — multi-frequency requires composing two polling streams via `RoutingStreamingDataFeed`. */\n freq: Frequency;\n /** When to poll. */\n schedule: PollingSchedule;\n /**\n * Window-start for the first poll per asset. Subsequent polls fetch\n * `(lastSeenT, now]` per asset. Defaults to `new Date(0)` — every bar the\n * feed has on the first poll is yielded. For replay-then-stream, set this\n * to your backtest range's `to` so polling picks up exactly where the\n * backtest left off.\n */\n initialFrom?: Date;\n /** Inject for tests or for accelerated-time simulations. Defaults to `() => new Date()`. */\n now?: () => Date;\n /** Inject for tests or for accelerated-time simulations. Defaults to `setTimeout`-based promise. */\n sleep?: (ms: number) => Promise<void>;\n};\n\nexport function pollingStreamFromHistorical(opts: PollingStreamOptions): StreamingDataFeed {\n const now = opts.now ?? (() => new Date());\n const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((res) => setTimeout(res, ms)));\n const initialFrom = opts.initialFrom ?? new Date(0);\n\n return {\n subscribe(assets: ReadonlyArray<Asset>): AsyncIterable<StreamingBar> {\n return poll(assets);\n },\n };\n\n async function* poll(assets: ReadonlyArray<Asset>): AsyncGenerator<StreamingBar> {\n // Dedup by id, preserve input order.\n const seen = new Set<AssetId>();\n const uniq: Asset[] = [];\n for (const a of assets) {\n if (!seen.has(a.id)) {\n seen.add(a.id);\n uniq.push(a);\n }\n }\n if (uniq.length === 0) return;\n\n const lastSeenT = new Map<AssetId, Date>(uniq.map((a) => [a.id, initialFrom]));\n\n while (true) {\n await waitForNextPoll();\n for (const asset of uniq) {\n const from = lastSeenT.get(asset.id)!;\n const to = now(); // Fresh per asset by design — for cycle-stable to, pass a custom now() that caches.\n for await (const bar of opts.feed.bars(asset, { from, to }, opts.freq)) {\n const last = lastSeenT.get(asset.id)!;\n if (bar.t.getTime() > last.getTime()) {\n yield { asset, bar };\n lastSeenT.set(asset.id, bar.t);\n }\n }\n }\n }\n }\n\n async function waitForNextPoll(): Promise<void> {\n if (opts.schedule.kind === 'interval') {\n await sleep(opts.schedule.intervalMs);\n return;\n }\n // Resolve the next session close via cal.schedule() lookahead rather than\n // the cal.previous(cal.next(now)) idiom: Session exposes .close (not .end),\n // and the lookahead also covers the exotic-calendar fallback below.\n const cal = opts.schedule.calendar;\n const t = now();\n const lookaheadDays = 14;\n const range = {\n from: t,\n to: new Date(t.getTime() + lookaheadDays * 24 * 60 * 60 * 1000),\n };\n const sessions = cal.schedule(range);\n const upcoming = sessions.find((s) => s.close.getTime() > t.getTime());\n if (upcoming === undefined) {\n // No session in the next N days — sleep one day and retry.\n await sleep(24 * 60 * 60 * 1000);\n return;\n }\n const delay = Math.max(0, upcoming.close.getTime() - t.getTime());\n await sleep(delay);\n }\n}\n","import { DateTime } from 'luxon';\nimport type { Calendar, Session, TimeOfDay } from '../interfaces/calendar';\nimport type { DateRange } from '../interfaces/types';\nimport {\n resolveHolidays,\n resolveSpecialCloses,\n resolveSpecialOpens,\n type HolidayRule,\n type SpecialClose,\n type SpecialOpen,\n type AdhocTimeOverrides,\n} from './holiday-rules';\n\nconst MS_PER_DAY = 86_400_000;\n\nconst DEFAULT_WEEKMASK: ReadonlySet<number> = new Set([1, 2, 3, 4, 5]);\nconst EMPTY_ADHOC: AdhocTimeOverrides = new Map();\n\nfunction ymdKey(d: Date): string {\n return d.toISOString().slice(0, 10);\n}\n\n/**\n * Abstract base class for exchange trading calendars. Implements the full\n * {@link Calendar} interface by composing up to nine overridable hooks that\n * subclasses provide. Concrete implementations ship for {@link NYSEExchangeCalendar}\n * and {@link LSEExchangeCalendar}; additional exchanges can be added by\n * extending this class.\n *\n * **Per-year caching**: holiday sets and special-session maps are computed once\n * per calendar year and stored in private Maps, so repeated calls to `isOpen`,\n * `next`, or `sessions` within the same year are cheap.\n *\n * **Hook resolution order** (adhoc beats rule, rule beats regular):\n * 1. `adhocHolidays()` / `specialClosesAdhoc()` / `specialOpensAdhoc()` —\n * `YYYY-MM-DD` string sets/maps populated once at first access.\n * 2. `regularHolidays()` / `specialCloses()` / `specialOpens()` —\n * year-derived rule arrays applied per year via the resolver helpers.\n * 3. `regularOpen(date)` / `regularClose(date)` / `weekmask(date)` —\n * per-date fallbacks that subclasses override to encode era-varying session\n * times and trading-day sets.\n *\n * **Extending**: override only the hooks you need. All hooks have no-op / sensible\n * defaults (Mon–Fri weekmask, 09:30–16:00 session) so a minimal subclass need\n * only set `name`, `tz`, and `regularHolidays()`.\n */\nexport abstract class ExchangeCalendar implements Calendar {\n /** Short exchange name used as the registry key in {@link getCalendar}. */\n abstract readonly name: string;\n /** IANA timezone identifier, e.g. `'America/New_York'` or `'Europe/London'`. */\n abstract readonly tz: string;\n\n private readonly holidayCache = new Map<number, Set<number>>();\n private readonly specialCloseCache = new Map<number, Map<number, TimeOfDay>>();\n private readonly specialOpenCache = new Map<number, Map<number, TimeOfDay>>();\n\n private adhocHolidaysCache: ReadonlySet<string> | null = null;\n private adhocSpecialClosesCache: AdhocTimeOverrides | null = null;\n private adhocSpecialOpensCache: AdhocTimeOverrides | null = null;\n\n // --- Hooks ---\n\n /**\n * Returns the ordered list of year-derived holiday rules for this exchange.\n * The base implementation returns an empty array (no regular holidays). Override\n * to supply the full rule set; each {@link HolidayRule} in the array is applied\n * via {@link resolveHolidays} once per calendar year and cached. Rules may be\n * era-bounded via `validFrom` / `validUntil`.\n */\n protected regularHolidays(): ReadonlyArray<HolidayRule> {\n return [];\n }\n\n /**\n * Returns the set of `YYYY-MM-DD` strings for one-off full-day closures that\n * do not fit a repeating rule (e.g. presidential funerals, natural disasters).\n * The base implementation returns an empty set. Override with the complete\n * historical adhoc list for the exchange. This method is called at most once\n * per `ExchangeCalendar` instance; the result is cached.\n */\n protected adhocHolidays(): ReadonlySet<string> {\n return new Set();\n }\n\n /**\n * Returns the ordered list of year-derived early-close rules for this exchange.\n * The base implementation returns an empty array. Override to supply rules such\n * as \"day after Thanksgiving closes at 13:00\". Results are computed once per\n * year and cached; each rule is applied via {@link resolveSpecialCloses}.\n */\n protected specialCloses(): ReadonlyArray<SpecialClose> {\n return [];\n }\n\n /**\n * Returns the map of `YYYY-MM-DD` strings to override close times for\n * one-off early-close days that do not fit a repeating rule. The base\n * implementation returns an empty map. Override with the historical adhoc\n * set for the exchange. Called at most once per instance; result is cached.\n */\n protected specialClosesAdhoc(): AdhocTimeOverrides {\n return EMPTY_ADHOC;\n }\n\n /**\n * Returns the ordered list of year-derived late-open rules for this exchange.\n * The base implementation returns an empty array. Override to supply rules such\n * as \"delayed open due to a moment of silence\". Results are computed once per\n * year and cached; each rule is applied via {@link resolveSpecialOpens}.\n */\n protected specialOpens(): ReadonlyArray<SpecialOpen> {\n return [];\n }\n\n /**\n * Returns the map of `YYYY-MM-DD` strings to override open times for\n * one-off late-open days that do not fit a repeating rule. The base\n * implementation returns an empty map. Override with the historical adhoc\n * set for the exchange. Called at most once per instance; result is cached.\n */\n protected specialOpensAdhoc(): AdhocTimeOverrides {\n return EMPTY_ADHOC;\n }\n\n /**\n * Returns the default open time in local exchange time for `date` when no\n * special-open rule matches. The base implementation returns 09:30. Override\n * to encode era-varying session times (e.g. NYSE opened at 10:00 before\n * 1985-09-30).\n *\n * @param date - UTC midnight `Date` for the trading day being queried.\n */\n protected regularOpen(_date: Date): TimeOfDay {\n return { h: 9, m: 30 };\n }\n\n /**\n * Returns the default close time in local exchange time for `date` when no\n * special-close rule matches. The base implementation returns 16:00. Override\n * to encode era-varying session times (e.g. NYSE closed at 15:00 before\n * 1952-09-29, and at 15:30 until 1974-01-02).\n *\n * @param date - UTC midnight `Date` for the trading day being queried.\n */\n protected regularClose(_date: Date): TimeOfDay {\n return { h: 16, m: 0 };\n }\n\n /**\n * Returns the set of weekday indices (using `Date.getUTCDay()` convention:\n * 0 = Sunday, 1 = Monday, …, 6 = Saturday) that are regular trading days.\n * The base implementation returns `{1, 2, 3, 4, 5}` (Mon–Fri). Override to\n * encode historical six-day trading weeks (e.g. NYSE traded Mon–Sat before\n * 1952-09-29, keyed by `date` so the shift is era-aware).\n *\n * @param date - UTC midnight `Date` for the day being tested.\n */\n protected weekmask(_date: Date): ReadonlySet<number> {\n return DEFAULT_WEEKMASK;\n }\n\n // --- Adhoc caching getters ---\n private getAdhocHolidays(): ReadonlySet<string> {\n if (this.adhocHolidaysCache === null) this.adhocHolidaysCache = this.adhocHolidays();\n return this.adhocHolidaysCache;\n }\n private getAdhocSpecialCloses(): AdhocTimeOverrides {\n if (this.adhocSpecialClosesCache === null) this.adhocSpecialClosesCache = this.specialClosesAdhoc();\n return this.adhocSpecialClosesCache;\n }\n private getAdhocSpecialOpens(): AdhocTimeOverrides {\n if (this.adhocSpecialOpensCache === null) this.adhocSpecialOpensCache = this.specialOpensAdhoc();\n return this.adhocSpecialOpensCache;\n }\n\n // --- Caches ---\n /**\n * Cached lookup of regular-holiday timestamps for the given year.\n * Assumes `regularHolidays()` returns the same rule list on every call.\n */\n private holidaysForYear(year: number): Set<number> {\n let set = this.holidayCache.get(year);\n if (!set) {\n set = resolveHolidays(this.regularHolidays(), year);\n this.holidayCache.set(year, set);\n }\n return set;\n }\n\n private specialClosesForYear(year: number): Map<number, TimeOfDay> {\n let map = this.specialCloseCache.get(year);\n if (!map) {\n map = resolveSpecialCloses(this.specialCloses(), year);\n this.specialCloseCache.set(year, map);\n }\n return map;\n }\n\n private specialOpensForYear(year: number): Map<number, TimeOfDay> {\n let map = this.specialOpenCache.get(year);\n if (!map) {\n map = resolveSpecialOpens(this.specialOpens(), year);\n this.specialOpenCache.set(year, map);\n }\n return map;\n }\n\n private normalize(t: Date): Date {\n return new Date(Date.UTC(t.getUTCFullYear(), t.getUTCMonth(), t.getUTCDate()));\n }\n\n // --- Public Calendar API ---\n\n /** Returns `true` when `t` falls on a regular trading day (weekmask check, then holiday check). */\n isOpen(t: Date): boolean {\n const d = this.normalize(t);\n if (!this.weekmask(d).has(d.getUTCDay())) return false;\n if (this.getAdhocHolidays().has(ymdKey(d))) return false;\n const year = d.getUTCFullYear();\n if (this.holidaysForYear(year).has(d.getTime())) return false;\n return true;\n }\n\n /** Returns the first trading day strictly after `t`. */\n next(t: Date): Date {\n let d = new Date(this.normalize(t).getTime() + MS_PER_DAY);\n while (!this.isOpen(d)) d = new Date(d.getTime() + MS_PER_DAY);\n return d;\n }\n\n /** Returns the first trading day strictly before `t`. */\n previous(t: Date): Date {\n let d = new Date(this.normalize(t).getTime() - MS_PER_DAY);\n while (!this.isOpen(d)) d = new Date(d.getTime() - MS_PER_DAY);\n return d;\n }\n\n /**\n * Returns UTC midnight `Date` objects for every trading day in\n * `[range.from, range.to)`. The `from` bound is inclusive; `to` is exclusive.\n */\n sessions(range: DateRange): ReadonlyArray<Date> {\n const out: Date[] = [];\n let d = this.normalize(range.from);\n const end = this.normalize(range.to).getTime();\n while (d.getTime() < end) {\n if (this.isOpen(d)) out.push(d);\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n return out;\n }\n\n schedule(range: DateRange): ReadonlyArray<Session> {\n const days = this.sessions(range);\n return days.map((date) => ({\n date,\n open: this.localizedTimestamp(date, this.openTimeFor(date)),\n close: this.localizedTimestamp(date, this.closeTimeFor(date)),\n }));\n }\n\n isEarlyClose(t: Date): boolean {\n const d = this.normalize(t);\n if (!this.isOpen(d)) return false;\n if (this.getAdhocSpecialCloses().has(ymdKey(d))) return true;\n return this.specialClosesForYear(d.getUTCFullYear()).has(d.getTime());\n }\n\n // --- Resolution ---\n /** Adhoc overrides win over rule-driven; both win over `regularOpen(date)`. */\n private openTimeFor(date: Date): TimeOfDay {\n const adhoc = this.getAdhocSpecialOpens().get(ymdKey(date));\n if (adhoc) return adhoc;\n const ruled = this.specialOpensForYear(date.getUTCFullYear()).get(date.getTime());\n if (ruled) return ruled;\n return this.regularOpen(date);\n }\n\n /** Adhoc overrides win over rule-driven; both win over `regularClose(date)`. */\n private closeTimeFor(date: Date): TimeOfDay {\n const adhoc = this.getAdhocSpecialCloses().get(ymdKey(date));\n if (adhoc) return adhoc;\n const ruled = this.specialClosesForYear(date.getUTCFullYear()).get(date.getTime());\n if (ruled) return ruled;\n return this.regularClose(date);\n }\n\n private localizedTimestamp(date: Date, time: TimeOfDay): Date {\n const dt = DateTime.fromObject(\n {\n year: date.getUTCFullYear(),\n month: date.getUTCMonth() + 1,\n day: date.getUTCDate(),\n hour: time.h,\n minute: time.m,\n },\n { zone: this.tz },\n );\n return new Date(dt.toUTC().toMillis());\n }\n}\n","import type { TimeOfDay } from '../interfaces/calendar';\n\nconst MS_PER_DAY = 86_400_000;\n\nexport type { TimeOfDay };\n\n/**\n * A year-derived holiday rule consumed by {@link ExchangeCalendar}. The rule\n * is active only for years in the range `[validFrom, validUntil]` (both\n * inclusive; omit either bound to leave it open).\n *\n * `resolve(year)` returns the UTC midnight `Date` for the holiday in that year,\n * or `null` if the holiday does not occur that year (e.g. a conditional rule\n * for Good Friday in certain years). When `observe` is `true`, a Saturday\n * result is moved to Friday and a Sunday result is moved to Monday (standard\n * US-style holiday observation).\n */\nexport type HolidayRule = {\n /** Human-readable name, used for debugging and logging. */\n name: string;\n /** Returns the UTC midnight `Date` for this holiday in `year`, or `null` to skip. */\n resolve: (year: number) => Date | null;\n /** First year (inclusive) this rule applies. Defaults to −∞. */\n validFrom?: number;\n /** Last year (inclusive) this rule applies. Defaults to +∞. */\n validUntil?: number;\n /** When `true`, Saturday dates are moved to Friday, Sunday dates to Monday. */\n observe?: boolean;\n};\n\n/**\n * A year-derived early-close rule consumed by {@link ExchangeCalendar}. Follows\n * the same validity bounds and `resolve` contract as {@link HolidayRule}, but\n * instead of marking a day closed entirely it overrides the session close time\n * to `closeAt` for the matched date.\n */\nexport type SpecialClose = {\n /** Human-readable name, used for debugging and logging. */\n name: string;\n /** Returns the UTC midnight `Date` for this early-close day in `year`, or `null` to skip. */\n resolve: (year: number) => Date | null;\n /** The overridden close time in local exchange time. */\n closeAt: TimeOfDay;\n /** First year (inclusive) this rule applies. Defaults to −∞. */\n validFrom?: number;\n /** Last year (inclusive) this rule applies. Defaults to +∞. */\n validUntil?: number;\n};\n\n/**\n * A year-derived late-open rule consumed by {@link ExchangeCalendar}. Follows\n * the same validity bounds and `resolve` contract as {@link HolidayRule}, but\n * overrides the session open time to `openAt` for the matched date.\n */\nexport type SpecialOpen = {\n /** Human-readable name, used for debugging and logging. */\n name: string;\n /** Returns the UTC midnight `Date` for this late-open day in `year`, or `null` to skip. */\n resolve: (year: number) => Date | null;\n /** The overridden open time in local exchange time. */\n openAt: TimeOfDay;\n /** First year (inclusive) this rule applies. Defaults to −∞. */\n validFrom?: number;\n /** Last year (inclusive) this rule applies. Defaults to +∞. */\n validUntil?: number;\n};\n\n/**\n * Map of `YYYY-MM-DD` date strings to override times. Used for one-off\n * historical specials (e.g. a single early close due to a snowstorm) that do\n * not fit a repeating year-derived rule. Keys must be in `YYYY-MM-DD` format\n * in UTC.\n */\nexport type AdhocTimeOverrides = ReadonlyMap<string, TimeOfDay>;\n\n/**\n * Era-bounded session-time rule. Lookup picks the latest rule with `effectiveFrom ≤ date`.\n * Use `effectiveFrom: undefined` for the default (since-inception) rule.\n */\nexport type SessionTimeRule = {\n effectiveFrom?: string; // YYYY-MM-DD inclusive\n time: TimeOfDay;\n};\n\n/**\n * Returns the UTC midnight `Date` of the nth occurrence of `weekday` in the\n * given `month` and `year`. `weekday` follows `Date.getUTCDay()` convention\n * (0 = Sunday, 1 = Monday, …, 6 = Saturday). `n` is 1-based.\n *\n * Example: 3rd Monday of January 2024 → `nthWeekdayOfMonth(2024, 1, 1, 3)`.\n */\nexport function nthWeekdayOfMonth(year: number, month: number, weekday: number, n: number): Date {\n const first = new Date(Date.UTC(year, month - 1, 1));\n const offset = (weekday - first.getUTCDay() + 7) % 7;\n return new Date(Date.UTC(year, month - 1, 1 + offset + (n - 1) * 7));\n}\n\n/**\n * Returns the UTC midnight `Date` of the last occurrence of `weekday` in the\n * given `month` and `year`. `weekday` follows `Date.getUTCDay()` convention.\n *\n * Example: last Monday of May 2024 → `lastWeekdayOfMonth(2024, 5, 1)`.\n */\nexport function lastWeekdayOfMonth(year: number, month: number, weekday: number): Date {\n const last = new Date(Date.UTC(year, month, 0));\n const offset = (last.getUTCDay() - weekday + 7) % 7;\n return new Date(last.getTime() - offset * MS_PER_DAY);\n}\n\n/**\n * Computes the UTC midnight `Date` of Easter Sunday for the given Gregorian\n * `year` using the Anonymous Gregorian algorithm (also known as the \"Meeus/Jones/Butcher\"\n * algorithm). Valid for years 1583–4099.\n */\nexport function easter(year: number): Date {\n const a = year % 19;\n const b = Math.floor(year / 100);\n const c = year % 100;\n const d = Math.floor(b / 4);\n const e = b % 4;\n const f = Math.floor((b + 8) / 25);\n const g = Math.floor((b - f + 1) / 3);\n const h = (19 * a + b - d - g + 15) % 30;\n const i = Math.floor(c / 4);\n const k = c % 4;\n const L = (32 + 2 * e + 2 * i - h - k) % 7;\n const m = Math.floor((a + 11 * h + 22 * L) / 451);\n const month = Math.floor((h + L - 7 * m + 114) / 31);\n const day = ((h + L - 7 * m + 114) % 31) + 1;\n return new Date(Date.UTC(year, month - 1, day));\n}\n\n/**\n * Applies Saturday → Friday, Sunday → Monday observation to a holiday date.\n * Weekday dates are returned unchanged. Used when a {@link HolidayRule} has\n * `observe: true`.\n */\nexport function observed(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === 6) return new Date(d.getTime() - MS_PER_DAY);\n if (dow === 0) return new Date(d.getTime() + MS_PER_DAY);\n return d;\n}\n\n/**\n * Applies a list of {@link HolidayRule} definitions to a single `year` and\n * returns a `Set` of UTC millisecond timestamps for all holidays active that\n * year. Rules outside their `[validFrom, validUntil]` bounds are skipped.\n * Rules that return `null` from `resolve` are also skipped.\n */\nexport function resolveHolidays(rules: ReadonlyArray<HolidayRule>, year: number): Set<number> {\n const out = new Set<number>();\n for (const rule of rules) {\n if (rule.validFrom !== undefined && year < rule.validFrom) continue;\n if (rule.validUntil !== undefined && year > rule.validUntil) continue;\n const raw = rule.resolve(year);\n if (raw === null) continue;\n const final = rule.observe ? observed(raw) : raw;\n out.add(final.getTime());\n }\n return out;\n}\n\n/**\n * Applies a list of {@link SpecialClose} rules to a single `year` and returns\n * a map from UTC millisecond timestamp to override close time. Rules outside\n * their `[validFrom, validUntil]` bounds and rules that return `null` from\n * `resolve` are skipped.\n */\nexport function resolveSpecialCloses(rules: ReadonlyArray<SpecialClose>, year: number): Map<number, TimeOfDay> {\n const out = new Map<number, TimeOfDay>();\n for (const rule of rules) {\n if (rule.validFrom !== undefined && year < rule.validFrom) continue;\n if (rule.validUntil !== undefined && year > rule.validUntil) continue;\n const d = rule.resolve(year);\n if (d === null) continue;\n out.set(d.getTime(), rule.closeAt);\n }\n return out;\n}\n\n/**\n * Applies a list of {@link SpecialOpen} rules to a single `year` and returns\n * a map from UTC millisecond timestamp to override open time. Rules outside\n * their `[validFrom, validUntil]` bounds and rules that return `null` from\n * `resolve` are skipped.\n */\nexport function resolveSpecialOpens(rules: ReadonlyArray<SpecialOpen>, year: number): Map<number, TimeOfDay> {\n const out = new Map<number, TimeOfDay>();\n for (const rule of rules) {\n if (rule.validFrom !== undefined && year < rule.validFrom) continue;\n if (rule.validUntil !== undefined && year > rule.validUntil) continue;\n const d = rule.resolve(year);\n if (d === null) continue;\n out.set(d.getTime(), rule.openAt);\n }\n return out;\n}\n\n// ─── pandas-equivalent date helpers ─────────────────────────────────────────\n// These are general-purpose calendar utilities modelled on pandas_market_calendars\n// helpers. They are exchange-agnostic and reused across NYSE, LSE, and future ports.\n\n/** pandas `sunday_to_monday`: only Sunday observation; Saturday stays Saturday. */\nexport function sundayToMonday(d: Date): Date {\n return d.getUTCDay() === 0 ? new Date(d.getTime() + MS_PER_DAY) : d;\n}\n\n/** pandas `nearest_workday`: Sat → Friday, Sun → Monday, weekday → unchanged. */\nexport function nearestWorkday(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === 6) return new Date(d.getTime() - MS_PER_DAY);\n if (dow === 0) return new Date(d.getTime() + MS_PER_DAY);\n return d;\n}\n\n/**\n * First Monday on/after the given day of the month (mimics pandas `weekday=MO(n)` offset).\n * Pass `nth > 1` to skip forward that many additional weeks.\n */\nexport function firstMondayOnOrAfter(year: number, month: number, day: number, nth = 1): Date {\n const start = new Date(Date.UTC(year, month - 1, day));\n const offset = (1 - start.getUTCDay() + 7) % 7; // MON = 1\n return new Date(start.getTime() + (offset + 7 * (nth - 1)) * MS_PER_DAY);\n}\n\n/** Easter offset by `dayDelta` days (e.g. Good Friday = easterPlus(y, -2)). */\nexport function easterPlus(year: number, dayDelta: number): Date {\n return new Date(easter(year).getTime() + dayDelta * MS_PER_DAY);\n}\n\n/** Helper to drop a holiday if its observed date falls outside an allowed weekday set. */\nexport function dropIfNotInDays(d: Date | null, allowed: ReadonlySet<number>): Date | null {\n if (d === null) return null;\n return allowed.has(d.getUTCDay()) ? d : null;\n}\n\n/**\n * Pick the rule with the latest `effectiveFrom ≤ date.toISOString().slice(0,10)`.\n * Rules without `effectiveFrom` are treated as the default (since inception).\n * Throws if no rule matches at all (provide a default rule to guarantee a match).\n */\nexport function resolveSessionTime(rules: ReadonlyArray<SessionTimeRule>, date: Date): TimeOfDay {\n const key = date.toISOString().slice(0, 10);\n let best: SessionTimeRule | null = null;\n for (const rule of rules) {\n if (rule.effectiveFrom === undefined) {\n if (best === null) best = rule;\n continue;\n }\n if (rule.effectiveFrom <= key) {\n if (best === null || best.effectiveFrom === undefined || best.effectiveFrom < rule.effectiveFrom) {\n best = rule;\n }\n }\n }\n if (best === null) {\n throw new Error('resolveSessionTime: no matching rule (provide a default rule with no effectiveFrom)');\n }\n return best.time;\n}\n","import { ExchangeCalendar } from './exchange-calendar';\nimport {\n dropIfNotInDays,\n easterPlus,\n firstMondayOnOrAfter,\n lastWeekdayOfMonth,\n nearestWorkday,\n nthWeekdayOfMonth,\n sundayToMonday,\n type AdhocTimeOverrides,\n type HolidayRule,\n type SpecialClose,\n type SpecialOpen,\n} from './holiday-rules';\nimport type { TimeOfDay } from '../interfaces/calendar';\n\nconst MS_PER_DAY = 86_400_000;\n\n// Day-of-week constants matching JS Date.getUTCDay(): Sun=0, Mon=1, ... Sat=6.\nconst SUN = 0;\nconst MON = 1;\nconst TUE = 2;\nconst WED = 3;\nconst THU = 4;\nconst FRI = 5;\nconst SAT = 6;\n\nconst SATURDAY_END_KEY = '1952-09-29'; // Saturday trading retired starting this date.\n\nfunction utcDate(y: number, m: number, d: number): Date {\n return new Date(Date.UTC(y, m - 1, d));\n}\n\nfunction ymd(date: Date): string {\n return date.toISOString().slice(0, 10);\n}\n\nconst WEEKDAYS_MON_FRI: ReadonlySet<number> = new Set([MON, TUE, WED, THU, FRI]);\nconst WEEKDAYS_MON_SAT: ReadonlySet<number> = new Set([MON, TUE, WED, THU, FRI, SAT]);\n\nconst REGULAR_HOLIDAYS: ReadonlyArray<HolidayRule> = [\n // ── New Year's Day ─────────────────────────────────────────────────────────\n // Post-1952: Sunday → Monday observance, Saturday-NYD drops (no Friday close).\n {\n name: \"New Year's Day (post-1952)\",\n validFrom: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 1, 1)), WEEKDAYS_MON_FRI),\n },\n // Pre-1952: Saturday is a trading day; NYD on Saturday is observed on Saturday.\n {\n name: \"New Year's Day (pre-1952)\",\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 1, 1)), WEEKDAYS_MON_SAT),\n },\n\n // ── MLK Day (3rd Mon of Jan, from 1998) ────────────────────────────────────\n {\n name: 'Martin Luther King Jr. Day',\n validFrom: 1998,\n resolve: (y) => nthWeekdayOfMonth(y, 1, MON, 3),\n },\n\n // ── Presidents Day (3rd Mon of Feb, from 1971) ────────────────────────────\n {\n name: 'Presidents Day',\n validFrom: 1971,\n resolve: (y) => nthWeekdayOfMonth(y, 2, MON, 3),\n },\n\n // ── Washington's Birthday (Feb 22) ─────────────────────────────────────────\n // Pre-1952: Mon-Sat with Sunday → Monday.\n {\n name: \"Washington's Birthday (pre-1952)\",\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 2, 22)), WEEKDAYS_MON_SAT),\n },\n // 1952-09-29 → 1963: Mon-Fri with Sunday → Monday.\n {\n name: \"Washington's Birthday (1952-1963)\",\n validFrom: 1953,\n validUntil: 1963,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 2, 22)), WEEKDAYS_MON_FRI),\n },\n // 1964-1970: nearest_workday observance.\n {\n name: \"Washington's Birthday (1964-1970)\",\n validFrom: 1964,\n validUntil: 1970,\n resolve: (y) => nearestWorkday(utcDate(y, 2, 22)),\n },\n\n // ── Lincoln's Birthday (Feb 12, 1896-1953) ─────────────────────────────────\n {\n name: \"Lincoln's Birthday\",\n validFrom: 1896,\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 2, 12)),\n },\n\n // ── Good Friday ────────────────────────────────────────────────────────────\n // Closed every year EXCEPT 1898, 1906, 1907.\n {\n name: 'Good Friday (1908+)',\n validFrom: 1908,\n resolve: (y) => easterPlus(y, -2),\n },\n {\n name: 'Good Friday (pre-1898)',\n validFrom: 1885,\n validUntil: 1897,\n resolve: (y) => easterPlus(y, -2),\n },\n {\n name: 'Good Friday (1899-1905)',\n validFrom: 1899,\n validUntil: 1905,\n resolve: (y) => easterPlus(y, -2),\n },\n\n // ── Memorial Day ───────────────────────────────────────────────────────────\n // Modern: last Monday of May, from 1971.\n {\n name: 'Memorial Day (modern, 1971+)',\n validFrom: 1971,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 25),\n },\n // Pre-1952 (Mon-Sat with Sunday → Monday).\n {\n name: 'Memorial Day (pre-1952)',\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 5, 30)), WEEKDAYS_MON_SAT),\n },\n // 1952-09-29 → 1963.\n {\n name: 'Memorial Day (1952-1963)',\n validFrom: 1953,\n validUntil: 1963,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 5, 30)), WEEKDAYS_MON_FRI),\n },\n // 1964-1969 nearest_workday.\n {\n name: 'Memorial Day (1964-1969)',\n validFrom: 1964,\n validUntil: 1969,\n resolve: (y) => nearestWorkday(utcDate(y, 5, 30)),\n },\n\n // ── Juneteenth (from 2022) ─────────────────────────────────────────────────\n {\n name: 'Juneteenth',\n validFrom: 2022,\n resolve: (y) => nearestWorkday(utcDate(y, 6, 19)),\n },\n\n // ── Independence Day ───────────────────────────────────────────────────────\n // Modern: nearest_workday, from 1954.\n {\n name: 'Independence Day (modern, 1954+)',\n validFrom: 1954,\n resolve: (y) => dropIfNotInDays(nearestWorkday(utcDate(y, 7, 4)), WEEKDAYS_MON_FRI),\n },\n // Pre-1952.\n {\n name: 'Independence Day (pre-1952)',\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 7, 4)), WEEKDAYS_MON_SAT),\n },\n // 1952-09-29 → 1953 (post-Saturday-trading transition).\n {\n name: 'Independence Day (1953)',\n validFrom: 1953,\n validUntil: 1953,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 7, 4)), WEEKDAYS_MON_FRI),\n },\n\n // ── Labor Day (1st Mon of Sep, from 1887) ─────────────────────────────────\n {\n name: 'Labor Day',\n validFrom: 1887,\n resolve: (y) => nthWeekdayOfMonth(y, 9, MON, 1),\n },\n\n // ── Columbus Day (Oct 12, 1909-1953) ───────────────────────────────────────\n {\n name: 'Columbus Day',\n validFrom: 1909,\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 10, 12)),\n },\n\n // ── Election Day (1848-1967, every year; 1968-1980 adhoc; thereafter none) ─\n {\n name: 'Election Day (1848-1967)',\n validFrom: 1885,\n validUntil: 1967,\n resolve: (y) => {\n // First Tuesday on/after Nov 2 (pandas: month=11 day=2 offset=TU(1)).\n const start = utcDate(y, 11, 2);\n const offset = (TUE - start.getUTCDay() + 7) % 7;\n return new Date(start.getTime() + offset * MS_PER_DAY);\n },\n },\n\n // ── Veterans/Armistice Day (Nov 11, 1934-1953) ─────────────────────────────\n {\n name: 'Veterans Day (1934-1953)',\n validFrom: 1934,\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 11, 11)),\n },\n\n // ── Thanksgiving ───────────────────────────────────────────────────────────\n // Modern: 4th Thursday of Nov, from 1942.\n {\n name: 'Thanksgiving (modern, 1942+)',\n validFrom: 1942,\n resolve: (y) => nthWeekdayOfMonth(y, 11, THU, 4),\n },\n // Pre-1939: last Thursday of Nov.\n {\n name: 'Thanksgiving (pre-1939)',\n validFrom: 1864,\n validUntil: 1938,\n resolve: (y) => lastWeekdayOfMonth(y, 11, THU),\n },\n // 1939-1941: 2nd-to-last Thursday of Nov (Franklin Thanksgiving).\n {\n name: 'Thanksgiving (1939-1941)',\n validFrom: 1939,\n validUntil: 1941,\n resolve: (y) => {\n const last = lastWeekdayOfMonth(y, 11, THU);\n return new Date(last.getTime() - 7 * MS_PER_DAY);\n },\n },\n\n // ── Christmas ──────────────────────────────────────────────────────────────\n // Modern: nearest_workday, from 1999.\n {\n name: 'Christmas (1999+)',\n validFrom: 1999,\n resolve: (y) => nearestWorkday(utcDate(y, 12, 25)),\n },\n // 1954-1998.\n {\n name: 'Christmas (1954-1998)',\n validFrom: 1954,\n validUntil: 1998,\n resolve: (y) => nearestWorkday(utcDate(y, 12, 25)),\n },\n // Pre-1954: sunday_to_monday only.\n {\n name: 'Christmas (pre-1954)',\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 12, 25)),\n },\n];\n\n// ─── Adhoc full-day closures (literal date set) ──────────────────────────────\n// Sourced verbatim from pandas_market_calendars/holidays/nyse.py adhoc lists.\nconst ADHOC_RAW: ReadonlyArray<string> = [\n // SatAfterGoodFridayAdhoc\n '1900-04-14',\n '1901-04-06',\n '1902-03-29',\n '1903-04-11',\n '1905-04-22',\n '1907-03-30',\n '1908-04-18',\n '1909-04-10',\n '1910-03-26',\n '1911-04-15',\n '1913-03-22',\n '1920-04-03',\n '1929-03-30',\n '1930-04-19',\n // MonBeforeIndependenceDayAdhoc\n '1899-07-03',\n // SatBeforeIndependenceDayAdhoc\n '1887-07-02',\n '1892-07-02',\n '1898-07-02',\n '1904-07-02',\n '1909-07-03',\n '1910-07-02',\n '1920-07-03',\n '1921-07-02',\n '1926-07-03',\n '1932-07-02',\n '1937-07-03',\n // SatAfterIndependenceDayAdhoc\n '1890-07-05',\n '1902-07-05',\n '1913-07-05',\n '1919-07-05',\n '1930-07-05',\n // DaysAfterIndependenceDayAdhoc\n '1901-07-05',\n '1901-07-06',\n '1968-07-05',\n // SatBeforeLaborDayAdhoc\n '1888-09-01',\n '1898-09-03',\n '1900-09-01',\n '1901-08-31',\n '1902-08-30',\n '1903-09-05',\n '1904-09-03',\n '1907-08-31',\n '1908-09-05',\n '1909-09-04',\n '1910-09-03',\n '1911-09-02',\n '1912-08-31',\n '1913-08-30',\n '1917-09-01',\n '1919-08-30',\n '1920-09-04',\n '1921-09-03',\n '1926-09-04',\n '1929-08-31',\n '1930-08-30',\n '1931-09-05',\n // USElectionDay1968to1980Adhoc\n '1968-11-05',\n '1972-11-07',\n '1976-11-02',\n '1980-11-04',\n // FridayAfterThanksgivingAdHoc\n '1888-11-30',\n // SatBeforeChristmasAdhoc\n '1887-12-24',\n '1898-12-24',\n '1904-12-24',\n '1910-12-24',\n '1911-12-23',\n '1922-12-23',\n '1949-12-24',\n '1950-12-23',\n // SatAfterChristmasAdhoc\n '1891-12-26',\n '1896-12-26',\n '1903-12-26',\n '1908-12-26',\n '1925-12-26',\n '1931-12-26',\n '1936-12-26',\n // ChristmasEvesAdhoc\n '1900-12-24',\n '1945-12-24',\n '1956-12-24',\n // DayAfterChristmasAdhoc\n '1958-12-26',\n // USVetransDayAdHoc\n '1921-11-11',\n '1968-11-11',\n // SatAfterColumbusDayAdHoc\n '1917-10-13',\n '1945-10-13',\n // LincolnsBirthDayAdhoc\n '1968-02-12',\n // GrantsBirthDayAdhoc\n '1897-04-27',\n // SatBeforeNewYearsAdhoc\n '1916-12-30',\n // SatBeforeWashingtonsBirthdayAdhoc\n '1903-02-21',\n // SatAfterWashingtonsBirthdayAdhoc\n '1901-02-23',\n '1907-02-23',\n '1929-02-23',\n '1946-02-23',\n // SatBeforeAfterLincolnsBirthdayAdhoc\n '1899-02-11',\n '1909-02-13',\n // SatBeforeDecorationAdhoc\n '1904-05-28',\n '1909-05-29',\n '1910-05-28',\n '1921-05-28',\n '1926-05-29',\n '1937-05-29',\n // SatAfterDecorationAdhoc\n '1902-05-31',\n '1913-05-31',\n '1919-05-31',\n '1924-05-31',\n '1930-05-31',\n // DayBeforeDecorationAdhoc\n '1899-05-29',\n '1961-05-29',\n // ── Irregular full-day closures ──\n // UlyssesGrantFuneral1885\n '1885-08-08',\n // ColumbianCelebration1892\n '1892-10-12',\n '1892-10-21',\n '1892-10-22',\n '1893-04-27',\n // GreatBlizzardOf1888\n '1888-03-12',\n '1888-03-13',\n // WashingtonInaugurationCentennialCelebration1889\n '1889-04-29',\n '1889-04-30',\n '1889-05-01',\n // CharterDay1898\n '1898-05-04',\n // WelcomeNavalCommander1898\n '1898-08-20',\n // AdmiralDeweyCelebration1899\n '1899-09-29',\n '1899-09-30',\n // GarretHobartFuneral1899\n '1899-11-25',\n // QueenVictoriaFuneral1901\n '1901-02-02',\n // MovedToProduceExchange1901\n '1901-04-27',\n // EnlargedProduceExchange1901\n '1901-05-11',\n // McKinleyDeathAndFuneral1901\n '1901-09-14',\n '1901-09-19',\n // KingEdwardVIIcoronation1902\n '1902-08-09',\n // NYSEnewBuildingOpen1903\n '1903-04-22',\n // HudsonFultonCelebration1909\n '1909-09-25',\n // JamesShermanFuneral1912\n '1912-11-02',\n // WeatherHeatClosing1917\n '1917-08-04',\n // DraftRegistrationDay1917\n '1917-06-05',\n // WeatherNoHeatClosing1918\n '1918-01-28',\n '1918-02-04',\n '1918-02-11',\n // DraftRegistrationDay1918\n '1918-09-12',\n // ArmisticeSigned1918\n '1918-11-11',\n // Homecoming27Division1919\n '1919-03-25',\n // ParadeOf77thDivision1919\n '1919-05-06',\n // BacklogRelief1919\n '1919-07-19',\n '1919-08-02',\n '1919-08-16',\n // GeneralPershingReturn1919\n '1919-09-10',\n // OfficeLocationChange1920\n '1920-05-01',\n // HardingDeath1923, HardingFuneral1923\n '1923-08-03',\n '1923-08-10',\n // LindberghParade1927\n '1927-06-13',\n // BacklogRelief1928\n '1928-04-07',\n '1928-04-21',\n '1928-05-05',\n '1928-05-12',\n '1928-05-19',\n '1928-05-26',\n '1928-11-24',\n // BacklogRelief1929\n '1929-02-09',\n '1929-11-01',\n '1929-11-02',\n '1929-11-09',\n '1929-11-16',\n '1929-11-23',\n '1929-11-29',\n '1929-11-30',\n // CoolidgeFuneral1933\n '1933-01-07',\n // BankHolidays1933 (Mar 4, 6-14)\n '1933-03-04',\n '1933-03-06',\n '1933-03-07',\n '1933-03-08',\n '1933-03-09',\n '1933-03-10',\n '1933-03-11',\n '1933-03-13',\n '1933-03-14',\n // (Mar 12, 1933 was a Sunday — naturally non-trading; upstream set still\n // includes it but our weekmask excludes Sundays. Keep parity by listing it.)\n '1933-03-12',\n // HeavyVolume1933 (closed Saturdays)\n '1933-07-29',\n '1933-08-05',\n '1933-08-12',\n '1933-08-19',\n '1933-08-26',\n '1933-09-02',\n // SatClosings1944\n '1944-08-19',\n '1944-08-26',\n '1944-09-02',\n // RooseveltDayOfMourning1945\n '1945-04-14',\n // VJday1945\n '1945-08-15',\n '1945-08-16',\n // NavyDay1945\n '1945-10-27',\n // RailroadStrike1946\n '1946-05-25',\n // SevereWeather1948\n '1948-01-03',\n // KennedyFuneral1963\n '1963-11-25',\n // MLKdayOfMourning1968\n '1968-04-09',\n // PaperworkCrisis1968 (every Wednesday from 1968-06-12 through 1968-12-18,\n // skipping holiday weeks per upstream literal list)\n '1968-06-12',\n '1968-06-19',\n '1968-06-26',\n '1968-07-10',\n '1968-07-17',\n '1968-07-24',\n '1968-07-31',\n '1968-08-07',\n '1968-08-14',\n '1968-08-21',\n '1968-08-28',\n '1968-09-11',\n '1968-09-18',\n '1968-09-25',\n '1968-10-02',\n '1968-10-09',\n '1968-10-16',\n '1968-10-23',\n '1968-10-30',\n '1968-11-20',\n '1968-12-04',\n '1968-12-11',\n '1968-12-18',\n // SnowClosing1969\n '1969-02-10',\n // EisenhowerFuneral1969\n '1969-03-31',\n // FirstLunarLandingClosing1969\n '1969-07-21',\n // TrumanFuneral1972\n '1972-12-28',\n // JohnsonFuneral1973\n '1973-01-25',\n // NewYorkCityBlackout77\n '1977-07-14',\n // HurricaneGloriaClosings1985\n '1985-09-27',\n // NixonFuneral1994\n '1994-04-27',\n // ReaganMourning2004\n '2004-06-11',\n // FordMourning2007\n '2007-01-02',\n // September11Closings2001\n '2001-09-11',\n '2001-09-12',\n '2001-09-13',\n '2001-09-14',\n // HurricaneSandyClosings2012\n '2012-10-29',\n '2012-10-30',\n // GeorgeHWBushDeath2018\n '2018-12-05',\n // JimmyCarterDeath2025\n '2025-01-09',\n];\n\n// Saturday-summer closings 1945-1952 (every Saturday in the listed window).\nfunction* generateSummerSaturdays(): IterableIterator<string> {\n const ranges: ReadonlyArray<[string, string]> = [\n ['1945-07-07', '1945-09-01'],\n ['1946-06-01', '1946-09-28'],\n ['1947-05-31', '1947-09-27'],\n ['1948-05-29', '1948-09-25'],\n ['1949-05-28', '1949-09-24'],\n ['1950-06-03', '1950-09-30'],\n ['1951-06-02', '1951-09-29'],\n ['1952-05-31', '1952-09-27'],\n ];\n for (const [from, to] of ranges) {\n let d = new Date(`${from}T00:00:00.000Z`);\n const end = new Date(`${to}T00:00:00.000Z`);\n while (d.getTime() <= end.getTime()) {\n if (d.getUTCDay() === SAT) yield ymd(d);\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n }\n}\n\n// WWI shutdown 1914-07-31 → 1914-12-11 (every Mon-Sat).\n// Upstream: OnsetOfWWI1914 uses CustomBusinessDay(weekmask=\"Mon Tue Wed Thu Fri Sat\"),\n// confirming that Saturdays are included — consistent with the pre-1952 Mon-Sat trading week.\nfunction* generateWWIShutdown(): IterableIterator<string> {\n let d = new Date('1914-07-31T00:00:00.000Z');\n const end = new Date('1914-12-11T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow !== SUN) yield ymd(d);\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n}\n\nconst ADHOC_HOLIDAYS: ReadonlySet<string> = new Set<string>([\n ...ADHOC_RAW,\n ...generateSummerSaturdays(),\n ...generateWWIShutdown(),\n]);\n\n// ─── Rule-driven special closes (early-close rules) ─────────────────────────\nconst SPECIAL_CLOSES: ReadonlyArray<SpecialClose> = [\n // 1pm — Day-after-Thanksgiving 1993+, Christmas Eve weekday 1999+,\n // pre-Independence Day Mon/Tue/Thu (1995+), Wed before Independence Day (2013+),\n // Friday-after-Independence Day pre-2013 (1996-2012).\n {\n name: 'Day after Thanksgiving 1pm (1993+)',\n validFrom: 1993,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const t = nthWeekdayOfMonth(y, 11, THU, 4);\n return new Date(t.getTime() + MS_PER_DAY);\n },\n },\n {\n name: 'Day after Thanksgiving 2pm (1992)',\n validFrom: 1992,\n validUntil: 1992,\n closeAt: { h: 14, m: 0 },\n resolve: (y) => {\n const t = nthWeekdayOfMonth(y, 11, THU, 4);\n return new Date(t.getTime() + MS_PER_DAY);\n },\n },\n {\n name: 'Christmas Eve Mon-Thu 1pm (1999+)',\n validFrom: 1999,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 12, 24);\n const dow = d.getUTCDay();\n return dow >= MON && dow <= THU ? d : null;\n },\n },\n {\n name: 'Mon/Tue/Thu before Independence Day 1pm (1995+)',\n validFrom: 1995,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 7, 3);\n const dow = d.getUTCDay();\n return dow === MON || dow === TUE || dow === THU ? d : null;\n },\n },\n {\n name: 'Wed before Independence Day 1pm (2013+)',\n validFrom: 2013,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 7, 3);\n return d.getUTCDay() === WED ? d : null;\n },\n },\n {\n name: 'Friday after Independence Day 1pm (1996-2012)',\n validFrom: 1996,\n validUntil: 2012,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 7, 5);\n return d.getUTCDay() === FRI ? d : null;\n },\n },\n];\n\n// ─── Adhoc special closes (literal map) ─────────────────────────────────────\nconst SPECIAL_CLOSES_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>([\n // 1pm closes\n ['1908-06-26', { h: 13, m: 0 }], // Grover Cleveland funeral\n // ChristmasEve1pmEarlyCloseAdhoc\n ['1951-12-24', { h: 13, m: 0 }],\n ['1996-12-24', { h: 13, m: 0 }],\n ['1997-12-24', { h: 13, m: 0 }],\n ['1998-12-24', { h: 13, m: 0 }],\n ['1999-12-24', { h: 13, m: 0 }],\n // DayAfterChristmas1pmEarlyCloseAdhoc\n ['1997-12-26', { h: 13, m: 0 }],\n ['2003-12-26', { h: 13, m: 0 }],\n // BacklogRelief1pmEarlyClose1929\n ['1929-11-06', { h: 13, m: 0 }],\n ['1929-11-07', { h: 13, m: 0 }],\n ['1929-11-08', { h: 13, m: 0 }],\n ['1929-11-11', { h: 13, m: 0 }],\n ['1929-11-12', { h: 13, m: 0 }],\n ['1929-11-13', { h: 13, m: 0 }],\n ['1929-11-14', { h: 13, m: 0 }],\n ['1929-11-15', { h: 13, m: 0 }],\n ['1929-11-18', { h: 13, m: 0 }],\n ['1929-11-19', { h: 13, m: 0 }],\n ['1929-11-20', { h: 13, m: 0 }],\n ['1929-11-21', { h: 13, m: 0 }],\n ['1929-11-22', { h: 13, m: 0 }],\n // 12pm early close — ParadeOfNationalGuard1917\n ['1917-08-29', { h: 12, m: 0 }],\n // LibertyDay 1917\n ['1917-10-24', { h: 12, m: 0 }],\n // LibertyDay 1918\n ['1918-04-26', { h: 12, m: 0 }],\n // WallStreetExplosion 1920\n ['1920-09-16', { h: 12, m: 0 }],\n // NRAdemonstration 1933\n ['1933-09-13', { h: 12, m: 0 }],\n // 12:30pm — RooseveltFuneral 1919\n ['1919-01-07', { h: 12, m: 30 }],\n // WoodrowWilsonFuneral 1924\n ['1924-02-06', { h: 12, m: 30 }],\n // TaftFuneral 1930\n ['1930-03-11', { h: 12, m: 30 }],\n // GasFumes 1933\n ['1933-08-04', { h: 12, m: 30 }],\n // 11am close — KingEdwardDeath 1910\n ['1910-05-07', { h: 11, m: 0 }],\n // 14:00 — HooverFuneral 1964\n ['1964-10-23', { h: 14, m: 0 }],\n // Snow2pmEarlyClose1967 (Feb 7, 1967)\n ['1967-02-07', { h: 14, m: 0 }],\n // Snow2pmEarlyClose1978\n ['1978-02-06', { h: 14, m: 0 }],\n // Snow2pmEarlyClose1996\n ['1996-01-08', { h: 14, m: 0 }],\n // 14:07 — Kennedy assassination\n ['1963-11-22', { h: 14, m: 7 }],\n // 14:30 — FalseArmistice 1918\n ['1918-11-07', { h: 14, m: 30 }],\n // CromwellFuneral 1925\n ['1925-09-18', { h: 14, m: 30 }],\n // Snow230EarlyClose1975\n ['1975-02-12', { h: 14, m: 30 }],\n // Snow230pmEarlyClose1994\n ['1994-02-11', { h: 14, m: 30 }],\n // 15:00 — HurricaneWatch 1976\n ['1976-08-09', { h: 15, m: 0 }],\n // 15:17 — Reagan assassination attempt\n ['1981-03-30', { h: 15, m: 17 }],\n // 15:28 — ConEd power fail\n ['1981-09-09', { h: 15, m: 28 }],\n // 15:30 — CircuitBreakerTriggered 1997\n ['1997-10-27', { h: 15, m: 30 }],\n // 15:56 — SystemProb 2005\n ['2005-06-01', { h: 15, m: 56 }],\n // ChristmasEve2pmEarlyCloseAdhoc\n ['1974-12-24', { h: 14, m: 0 }],\n ['1975-12-24', { h: 14, m: 0 }],\n ['1990-12-24', { h: 14, m: 0 }],\n ['1991-12-24', { h: 14, m: 0 }],\n ['1992-12-24', { h: 14, m: 0 }],\n // HeavyVolume2pmEarlyClose1933\n ['1933-07-26', { h: 14, m: 0 }],\n ['1933-07-27', { h: 14, m: 0 }],\n ['1933-07-28', { h: 14, m: 0 }],\n // BacklogRelief2pmEarlyClose1928 (May 21-25, 1928 Mon-Fri+Sat)\n ['1928-05-21', { h: 14, m: 0 }],\n ['1928-05-22', { h: 14, m: 0 }],\n ['1928-05-23', { h: 14, m: 0 }],\n ['1928-05-24', { h: 14, m: 0 }],\n ['1928-05-25', { h: 14, m: 0 }],\n // 1987 backlog 2pm: Oct 23-30 (Fri-Fri); Mon-Fri set\n ['1987-10-23', { h: 14, m: 0 }],\n ['1987-10-26', { h: 14, m: 0 }],\n ['1987-10-27', { h: 14, m: 0 }],\n ['1987-10-28', { h: 14, m: 0 }],\n ['1987-10-29', { h: 14, m: 0 }],\n ['1987-10-30', { h: 14, m: 0 }],\n // 1987 backlog 2:30pm: Nov 2-4\n ['1987-11-02', { h: 14, m: 30 }],\n ['1987-11-03', { h: 14, m: 30 }],\n ['1987-11-04', { h: 14, m: 30 }],\n // 1987 backlog 3pm: Nov 5-6\n ['1987-11-05', { h: 15, m: 0 }],\n ['1987-11-06', { h: 15, m: 0 }],\n // 1987 backlog 3:30pm: Nov 9-11\n ['1987-11-09', { h: 15, m: 30 }],\n ['1987-11-10', { h: 15, m: 30 }],\n ['1987-11-11', { h: 15, m: 30 }],\n]);\n\n// 1966 transit strike 2pm closes (Jan 6-14 weekdays).\n(() => {\n let d = new Date('1966-01-06T00:00:00.000Z');\n const end = new Date('1966-01-14T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1967 backlog 2pm closes (Aug 9-18 weekdays).\n(() => {\n let d = new Date('1967-08-09T00:00:00.000Z');\n const end = new Date('1967-08-18T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1968 backlog 2pm closes (Jan 22 - Mar 1 weekdays).\n(() => {\n let d = new Date('1968-01-22T00:00:00.000Z');\n const end = new Date('1968-03-01T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1969 paperwork crisis 2pm (Jan 1 - Jul 3 weekdays).\n(() => {\n let d = new Date('1969-01-01T00:00:00.000Z');\n const end = new Date('1969-07-03T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1969 paperwork crisis 2:30pm (Jul 7 - Sep 26 weekdays).\n(() => {\n let d = new Date('1969-07-07T00:00:00.000Z');\n const end = new Date('1969-09-26T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 30 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1969-1970 paperwork crisis 3pm (Sep 29, 1969 - May 1, 1970 weekdays).\n(() => {\n let d = new Date('1969-09-29T00:00:00.000Z');\n const end = new Date('1970-05-01T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 15, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// ─── Rule-driven special opens ──────────────────────────────────────────────\nconst SPECIAL_OPENS: ReadonlyArray<SpecialOpen> = [];\n\n// ─── Adhoc special opens (literal map) ──────────────────────────────────────\nconst SPECIAL_OPENS_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>([\n // 9:31am — ConEdXformer1990\n ['1990-12-27', { h: 9, m: 31 }],\n // EnduringFreedomMomentSilence 2001\n ['2001-10-08', { h: 9, m: 31 }],\n // 9:32am — IraqiFreedom 2003\n ['2003-03-20', { h: 9, m: 32 }],\n // ReaganMomentSilence 2004\n ['2004-06-07', { h: 9, m: 32 }],\n // FordMomentSilence 2006\n ['2006-12-27', { h: 9, m: 32 }],\n // 9:33am — Sept11MomentSilence 2001\n ['2001-09-17', { h: 9, m: 33 }],\n // 10:15 — Snow1015LateOpen1967\n ['1967-02-07', { h: 10, m: 15 }],\n // MerrillLynchComputer1015LateOpen1974\n ['1974-01-16', { h: 10, m: 15 }],\n // FireDrill1015LateOpen1974\n ['1974-11-22', { h: 10, m: 15 }],\n // FireDrill1015LateOpen1976\n ['1976-06-08', { h: 10, m: 15 }],\n // 10:30 — TrafficBlockLateOpen1919\n ['1919-12-30', { h: 10, m: 30 }],\n // TrafficBlockLateOpen1920\n ['1920-02-06', { h: 10, m: 30 }],\n // Computer1030LateOpen1995\n ['1995-12-18', { h: 10, m: 30 }],\n // 10:45 — EclipseOfSunLateOpen1925\n ['1925-01-24', { h: 10, m: 45 }],\n // Storm1045LateOpen1969\n ['1969-06-02', { h: 10, m: 45 }],\n // 11:00 — Snow11amLateOpen1934\n ['1934-02-20', { h: 11, m: 0 }],\n // KingGeorgeVFuneral1936\n ['1936-01-28', { h: 11, m: 0 }],\n // Snow11amLateOpening1960\n ['1960-12-12', { h: 11, m: 0 }],\n // Snow11amLateOpen1969\n ['1969-02-11', { h: 11, m: 0 }],\n // Ice11amLateOpen1973\n ['1973-12-17', { h: 11, m: 0 }],\n // Snow11amLateOpen1978\n ['1978-02-07', { h: 11, m: 0 }],\n // Fire11amLateOpen1989\n ['1989-11-10', { h: 11, m: 0 }],\n // Snow11amLateOpen1996\n ['1996-01-08', { h: 11, m: 0 }],\n // 11:05 — PowerFail1965\n ['1965-11-10', { h: 11, m: 5 }],\n // 11:15 — Storm1115LateOpen1976\n ['1976-02-02', { h: 11, m: 15 }],\n // 12:00 — KingEdwardFuneral1910\n ['1910-05-20', { h: 12, m: 0 }],\n // JPMorganFuneral1913\n ['1913-04-14', { h: 12, m: 0 }],\n // WilliamGaynorFuneral1913\n ['1913-09-22', { h: 12, m: 0 }],\n // Snow12pmLateOpen1978\n ['1978-01-20', { h: 12, m: 0 }],\n // Sept11Anniversary 2002\n ['2002-09-11', { h: 12, m: 0 }],\n // BacklogRelief12pmLateOpen1929\n ['1929-10-31', { h: 12, m: 0 }],\n // HeavyVolume12pmLateOpen1933\n ['1933-07-24', { h: 12, m: 0 }],\n ['1933-07-25', { h: 12, m: 0 }],\n // HeavyVolume11amLateOpen1933\n ['1933-07-26', { h: 11, m: 0 }],\n ['1933-07-27', { h: 11, m: 0 }],\n ['1933-07-28', { h: 11, m: 0 }],\n // 13:00 — AnnunciatorBoardFire 1921\n ['1921-08-02', { h: 13, m: 0 }],\n // TroopsInGulf931LateOpens1991\n ['1991-01-17', { h: 9, m: 31 }],\n ['1991-02-25', { h: 9, m: 31 }],\n]);\n\n/**\n * New York Stock Exchange (NYSE) trading-day calendar covering 1885-01-01 to\n * the present. Also applicable to NYSE-equivalent venues (NASDAQ, BATS, DJIA,\n * DOW). Faithful port of `pandas_market_calendars`' `nyse.py`.\n *\n * **Era boundaries:**\n * - Weekmask: Mon–Sat through 1952-09-28; Mon–Fri from 1952-09-29 onward\n * (Saturday trading retired on that date).\n * - Regular open: 10:00 ET before 1985-09-30; 09:30 ET from 1985-09-30 onward.\n * - Regular close: 15:00 ET before 1952-09-29; 15:30 ET through 1973-12-31;\n * 16:00 ET from 1974-01-02 onward. Saturday closes (pre-1952) are\n * approximated as 12:00.\n *\n * Holiday coverage includes the full set of regular (rule-derived) and adhoc\n * (literal date set) closures sourced from `pandas_market_calendars`, spanning\n * historical events from the Ulysses Grant funeral (1885) through the Jimmy\n * Carter national day of mourning (2025).\n */\nexport class NYSEExchangeCalendar extends ExchangeCalendar {\n readonly name = 'NYSE';\n readonly tz = 'America/New_York';\n\n protected override regularHolidays(): ReadonlyArray<HolidayRule> {\n return REGULAR_HOLIDAYS;\n }\n\n protected override adhocHolidays(): ReadonlySet<string> {\n return ADHOC_HOLIDAYS;\n }\n\n protected override specialCloses(): ReadonlyArray<SpecialClose> {\n return SPECIAL_CLOSES;\n }\n\n protected override specialClosesAdhoc(): AdhocTimeOverrides {\n return SPECIAL_CLOSES_ADHOC;\n }\n\n protected override specialOpens(): ReadonlyArray<SpecialOpen> {\n return SPECIAL_OPENS;\n }\n\n protected override specialOpensAdhoc(): AdhocTimeOverrides {\n return SPECIAL_OPENS_ADHOC;\n }\n\n protected override regularOpen(date: Date): TimeOfDay {\n return ymd(date) < '1985-09-30' ? { h: 10, m: 0 } : { h: 9, m: 30 };\n }\n\n protected override regularClose(date: Date): TimeOfDay {\n const key = ymd(date);\n // Saturday close pre-1952 was 12:00 (approximation, see spec).\n if (key < SATURDAY_END_KEY && date.getUTCDay() === SAT) return { h: 12, m: 0 };\n if (key < SATURDAY_END_KEY) return { h: 15, m: 0 };\n if (key < '1974-01-02') return { h: 15, m: 30 };\n return { h: 16, m: 0 };\n }\n\n protected override weekmask(date: Date): ReadonlySet<number> {\n return ymd(date) < SATURDAY_END_KEY ? WEEKDAYS_MON_SAT : WEEKDAYS_MON_FRI;\n }\n}\n","import { ExchangeCalendar } from './exchange-calendar';\nimport {\n dropIfNotInDays,\n easterPlus,\n firstMondayOnOrAfter,\n lastWeekdayOfMonth,\n type AdhocTimeOverrides,\n type HolidayRule,\n type SpecialClose,\n type SpecialOpen,\n} from './holiday-rules';\nimport type { TimeOfDay } from '../interfaces/calendar';\n\nconst MS_PER_DAY = 86_400_000;\n\n// Day-of-week constants matching JS Date.getUTCDay(): Sun=0, Mon=1, ..., Sat=6.\nconst SUN = 0;\nconst MON = 1;\nconst TUE = 2;\nconst SAT = 6;\n\nfunction utcDate(y: number, m: number, d: number): Date {\n return new Date(Date.UTC(y, m - 1, d));\n}\n\nconst WEEKDAYS_MON_FRI: ReadonlySet<number> = new Set([1, 2, 3, 4, 5]);\nconst MON_TUE: ReadonlySet<number> = new Set([MON, TUE]);\n\n/**\n * pandas `weekend_to_monday`: Sat → Monday, Sun → Monday. Weekday → unchanged.\n * Used by LSE for New Year's Day observance.\n */\nfunction weekendToMonday(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === SAT) return new Date(d.getTime() + 2 * MS_PER_DAY);\n if (dow === SUN) return new Date(d.getTime() + MS_PER_DAY);\n return d;\n}\n\n/**\n * pandas `previous_friday`: if Sat → Friday (−1 day); if Sun → Friday (−2 days);\n * weekday → unchanged. Used by LSE for Christmas Eve and New Year's Eve early-close\n * observance — the early close moves to the prior Friday when the calendar date\n * falls on a weekend.\n */\nfunction previousFriday(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === SAT) return new Date(d.getTime() - MS_PER_DAY);\n if (dow === SUN) return new Date(d.getTime() - 2 * MS_PER_DAY);\n return d;\n}\n\n// ─── Regular holidays ───────────────────────────────────────────────────────\n// Faithful port of pandas_market_calendars/holidays/uk.py rule definitions.\nconst REGULAR_HOLIDAYS: ReadonlyArray<HolidayRule> = [\n // ── New Year's Day ─────────────────────────────────────────────────────────\n // pandas: weekend_to_monday observance (Sat → Mon, Sun → Mon).\n {\n name: \"New Year's Day\",\n resolve: (y) => weekendToMonday(utcDate(y, 1, 1)),\n },\n\n // ── Good Friday (easter − 2) ───────────────────────────────────────────────\n {\n name: 'Good Friday',\n resolve: (y) => easterPlus(y, -2),\n },\n\n // ── Easter Monday (easter + 1) ─────────────────────────────────────────────\n {\n name: 'Easter Monday',\n resolve: (y) => easterPlus(y, 1),\n },\n\n // ── Early May Bank Holiday — first Monday of May ───────────────────────────\n // Upstream splits this into three eras to remove May 4 1995 and May 4 2020,\n // which were displaced for VE-Day anniversaries.\n {\n name: 'Early May Bank Holiday (pre-1995)',\n validUntil: 1994,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 1),\n },\n {\n name: 'Early May Bank Holiday (1996-2019)',\n validFrom: 1996,\n validUntil: 2019,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 1),\n },\n {\n name: 'Early May Bank Holiday (2021+)',\n validFrom: 2021,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 1),\n },\n\n // ── Spring Bank Holiday — last Monday of May ───────────────────────────────\n // Upstream splits to skip the regular Spring Bank in 2002 (Golden Jubilee),\n // 2012 (Diamond Jubilee), and 2022 (Platinum Jubilee). Those years have\n // adhoc Jubilee closures that replace it.\n {\n name: 'Spring Bank Holiday (pre-2002)',\n validUntil: 2001,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n {\n name: 'Spring Bank Holiday (2003-2011)',\n validFrom: 2003,\n validUntil: 2011,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n {\n name: 'Spring Bank Holiday (2013-2021)',\n validFrom: 2013,\n validUntil: 2021,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n {\n name: 'Spring Bank Holiday (2023+)',\n validFrom: 2023,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n\n // ── Summer Bank Holiday — last Monday of August ────────────────────────────\n {\n name: 'Summer Bank Holiday',\n resolve: (y) => lastWeekdayOfMonth(y, 8, MON),\n },\n\n // ── Christmas Day (Dec 25) ─────────────────────────────────────────────────\n // pandas Holiday with no observance — the literal Dec 25 is added; if it\n // falls on a weekend the WeekendChristmas rule below adds a substitute Mon/Tue.\n {\n name: 'Christmas Day',\n resolve: (y) => utcDate(y, 12, 25),\n },\n\n // ── Weekend Christmas substitute (Dec 27, only Mon/Tue) ────────────────────\n // If Dec 25 is Sat → Dec 27 is Mon (substitute Christmas).\n // If Dec 25 is Sun → Dec 27 is Tue (substitute Christmas, after Boxing Day Mon Dec 26).\n {\n name: 'Weekend Christmas',\n resolve: (y) => dropIfNotInDays(utcDate(y, 12, 27), MON_TUE),\n },\n\n // ── Boxing Day (Dec 26) ────────────────────────────────────────────────────\n {\n name: 'Boxing Day',\n resolve: (y) => utcDate(y, 12, 26),\n },\n\n // ── Weekend Boxing Day substitute (Dec 28, only Mon/Tue) ───────────────────\n // If Dec 26 is Sat → Dec 28 is Mon (substitute Boxing).\n // If Dec 26 is Sun → Dec 28 is Tue (substitute Boxing, after Christmas observed Mon Dec 27).\n {\n name: 'Weekend Boxing Day',\n resolve: (y) => dropIfNotInDays(utcDate(y, 12, 28), MON_TUE),\n },\n];\n\n// ─── Adhoc full-day closures ────────────────────────────────────────────────\n// Sourced verbatim from pandas_market_calendars/holidays/uk.py UniqueCloses.\nconst ADHOC_HOLIDAYS: ReadonlySet<string> = new Set<string>([\n // VE-Day anniversaries (Early May Bank Holiday displaced)\n '1995-05-08', // 50th anniversary\n '2020-05-08', // 75th anniversary\n\n // Queen Elizabeth II Jubilees\n '1977-06-07', // Silver Jubilee\n '2002-06-03', // Golden Jubilee — Spring Bank holiday moved\n '2002-06-04', // Golden Jubilee — additional\n '2012-06-04', // Diamond Jubilee — Spring Bank holiday moved\n '2012-06-05', // Diamond Jubilee — additional\n '2022-06-02', // Platinum Jubilee — Spring Bank holiday moved\n '2022-06-03', // Platinum Jubilee — additional\n\n // State funerals\n '2022-09-19', // Queen Elizabeth II\n\n // Royal weddings\n '1973-11-14', // Princess Anne and Mark Phillips\n '1981-07-29', // Prince Charles and Diana Spencer\n '2011-04-29', // Prince William and Catherine Middleton\n\n // Coronation of King Charles III\n '2023-05-08',\n\n // Miscellaneous\n '1999-12-31', // Eve of 3rd Millennium A.D.\n]);\n\n// ─── Rule-driven special closes (12:30 early close) ─────────────────────────\n// Upstream LSE: ChristmasEve and LSENewYearsEve — both use `previous_friday`\n// observance, so when Dec 24 / Dec 31 falls on a weekend the early close moves\n// to the prior Friday (a regular trading day). Modern session close is 16:30\n// so a 12:30 close is genuinely early.\nconst SPECIAL_CLOSES: ReadonlyArray<SpecialClose> = [\n {\n name: 'Christmas Eve early close',\n closeAt: { h: 12, m: 30 },\n resolve: (y) => dropIfNotInDays(previousFriday(utcDate(y, 12, 24)), WEEKDAYS_MON_FRI),\n },\n {\n name: \"New Year's Eve early close\",\n closeAt: { h: 12, m: 30 },\n resolve: (y) => dropIfNotInDays(previousFriday(utcDate(y, 12, 31)), WEEKDAYS_MON_FRI),\n },\n];\n\n// ─── Adhoc special closes (literal) ─────────────────────────────────────────\n// Upstream LSE has no adhoc early-close map.\nconst SPECIAL_CLOSES_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>();\n\n// ─── Special opens — none in upstream LSE ───────────────────────────────────\nconst SPECIAL_OPENS: ReadonlyArray<SpecialOpen> = [];\nconst SPECIAL_OPENS_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>();\n\n/**\n * London Stock Exchange (LSE) trading-day calendar. Faithful port of\n * `pandas_market_calendars`' `lse.py` and `holidays/uk.py`. Historical\n * coverage begins 1801-01-01, aligned with the start of the modern exchange\n * after the Banking and Financial Dealings Act 1971 codified the current\n * bank-holiday framework.\n *\n * **Session**: 08:00–16:30 Europe/London. The exchange observes BST (UTC+1)\n * in summer and GMT (UTC+0) in winter — DST handling is delegated to luxon via\n * the `Europe/London` IANA timezone, so wall-clock session times are stable\n * across the DST transition while their UTC equivalents shift by one hour.\n *\n * **Early closes**: Christmas Eve (Dec 24) and New Year's Eve (Dec 31) close\n * at 12:30. Both use `previous_friday` observance — when the calendar date\n * falls on a weekend, the early close moves to the prior Friday.\n *\n * **Era boundaries**: bank-holiday exceptions for Royal Jubilees and VE-Day\n * anniversaries are implemented by splitting affected `Spring Bank Holiday`\n * and `Early May Bank Holiday` rules into era-bounded shards (matching\n * upstream `start_date` / `end_date` markers) and adding the displaced dates\n * as adhoc closures.\n */\nexport class LSEExchangeCalendar extends ExchangeCalendar {\n readonly name = 'LSE';\n readonly tz = 'Europe/London';\n\n protected override regularHolidays(): ReadonlyArray<HolidayRule> {\n return REGULAR_HOLIDAYS;\n }\n\n protected override adhocHolidays(): ReadonlySet<string> {\n return ADHOC_HOLIDAYS;\n }\n\n protected override specialCloses(): ReadonlyArray<SpecialClose> {\n return SPECIAL_CLOSES;\n }\n\n protected override specialClosesAdhoc(): AdhocTimeOverrides {\n return SPECIAL_CLOSES_ADHOC;\n }\n\n protected override specialOpens(): ReadonlyArray<SpecialOpen> {\n return SPECIAL_OPENS;\n }\n\n protected override specialOpensAdhoc(): AdhocTimeOverrides {\n return SPECIAL_OPENS_ADHOC;\n }\n\n protected override regularOpen(_date: Date): TimeOfDay {\n return { h: 8, m: 0 };\n }\n\n protected override regularClose(_date: Date): TimeOfDay {\n return { h: 16, m: 30 };\n }\n\n protected override weekmask(_date: Date): ReadonlySet<number> {\n return WEEKDAYS_MON_FRI;\n }\n}\n","import { ExchangeCalendar } from './exchange-calendar';\nimport { NYSEExchangeCalendar } from './nyse';\nimport { LSEExchangeCalendar } from './lse';\n\n/**\n * Union of supported exchange names accepted by {@link getCalendar}.\n *\n * - `'NYSE'` — New York Stock Exchange (and NYSE-equivalent venues).\n * - `'LSE'` — London Stock Exchange.\n */\nexport type ExchangeName = 'NYSE' | 'LSE';\n\n/**\n * Returns a new instance of the {@link ExchangeCalendar} registered under\n * `name`. Acts as a simple factory / registry for the two built-in calendar\n * implementations.\n *\n * Supported exchange names: `'NYSE'` ({@link NYSEExchangeCalendar}) and\n * `'LSE'` ({@link LSEExchangeCalendar}). TypeScript's exhaustive switch\n * prevents unknown names from compiling.\n *\n * @param name - One of the supported {@link ExchangeName} values.\n * @returns A fresh `ExchangeCalendar` instance for the named exchange.\n *\n * @example\n * ```ts\n * import { getCalendar } from '@livefolio/sdk';\n *\n * const nyse = getCalendar('NYSE');\n * console.log(nyse.isOpen(new Date('2024-07-04'))); // false — US Independence Day\n *\n * const lse = getCalendar('LSE');\n * console.log(lse.isOpen(new Date('2024-12-25'))); // false — Christmas Day\n * ```\n */\nexport function getCalendar(name: ExchangeName): ExchangeCalendar {\n switch (name) {\n case 'NYSE':\n return new NYSEExchangeCalendar();\n case 'LSE':\n return new LSEExchangeCalendar();\n }\n}\n","import type { Calendar, Session } from '../interfaces/calendar';\nimport type { DateRange } from '../interfaces/types';\n\nconst MS_PER_DAY = 86_400_000;\n\nfunction midnightUtc(d: Date): Date {\n return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));\n}\n\n/**\n * 24/7 calendar where every day is a single session running midnight UTC to\n * the next midnight UTC. Suitable for crypto strategies (BTC, ETH) and any\n * always-on market.\n *\n * - `isOpen(t)` always returns `true`.\n * - `next(t)` returns midnight UTC of the day after `t`.\n * - `previous(t)` returns midnight UTC of the day before `t`.\n * - `sessions(range)` returns one Date per day in `[range.from, range.to)`.\n * - `schedule(range)` returns full Sessions with `open`/`close` at midnight UTC.\n * - `isEarlyClose(t)` always returns `false`.\n */\nexport class Crypto24x7Calendar implements Calendar {\n isOpen(_t: Date): boolean {\n return true;\n }\n\n next(t: Date): Date {\n return new Date(midnightUtc(t).getTime() + MS_PER_DAY);\n }\n\n previous(t: Date): Date {\n return new Date(midnightUtc(t).getTime() - MS_PER_DAY);\n }\n\n sessions(range: DateRange): ReadonlyArray<Date> {\n const out: Date[] = [];\n let cursor = midnightUtc(range.from);\n const end = range.to.getTime();\n while (cursor.getTime() < end) {\n out.push(cursor);\n cursor = new Date(cursor.getTime() + MS_PER_DAY);\n }\n return out;\n }\n\n schedule(range: DateRange): ReadonlyArray<Session> {\n return this.sessions(range).map((date) => ({\n date,\n open: date,\n close: new Date(date.getTime() + MS_PER_DAY),\n }));\n }\n\n isEarlyClose(_t: Date): boolean {\n return false;\n }\n}\n","export type {\n AssetRef,\n SyntheticAsset,\n RebalanceFrequency,\n RebalanceConfig,\n TacticalFeatureSpec,\n TacticalFeatureKind,\n FeatureRef,\n Tolerance,\n Comparison,\n ComparisonOp,\n AllocateNode,\n IfNode,\n RuleNode,\n TacticalSpec,\n RuleTreeState,\n} from './types';\nexport { evaluateRuleTree } from './evaluate-rule-tree';\nexport { evaluateFeatureSpecs } from './evaluate-feature-specs';\nexport { withSynthetics, withStreamingSynthetics, type WithStreamingSyntheticsOptions } from './synthetics';\nexport {\n fromSpec,\n isRebalanceDay,\n periodKey,\n _resetTacticalDeprecationWarningForTesting,\n type TacticalFeatures,\n type FromSpecOptions,\n} from './from-spec';\n","import type { TargetWeights } from '../strategy/reconcile';\nimport type { AssetId } from '../interfaces/types';\nimport type { Comparison, FeatureRef, RuleNode, RuleTreeState, Tolerance } from './types';\n\nfunction isRef(operand: FeatureRef | number): operand is FeatureRef {\n return typeof operand === 'object' && operand !== null && 'ref' in operand;\n}\n\nfunction resolve(operand: FeatureRef | number, values: ReadonlyMap<string, number>): number {\n if (!isRef(operand)) return operand;\n const v = values.get(operand.ref);\n if (v === undefined) {\n throw new Error(`evaluateRuleTree: feature \"${operand.ref}\" has no value`);\n }\n return v;\n}\n\nfunction rawCompare(op: Comparison['op'], l: number, r: number): boolean {\n switch (op) {\n case 'gt':\n return l > r;\n case 'lt':\n return l < r;\n case 'gte':\n return l >= r;\n case 'lte':\n return l <= r;\n }\n}\n\nfunction band(right: number, tol: Tolerance): { lower: number; upper: number } {\n if (tol.mode === 'absolute') {\n return { lower: right - tol.value, upper: right + tol.value };\n }\n const factor = tol.value / 100;\n return { lower: right * (1 - factor), upper: right * (1 + factor) };\n}\n\nfunction evalComparison(\n cond: Comparison,\n values: ReadonlyMap<string, number>,\n state: RuleTreeState,\n outState: Map<string, 0 | 1>,\n): boolean {\n const l = resolve(cond.left, values);\n const r = resolve(cond.right, values);\n\n if (!cond.tolerance) {\n return rawCompare(cond.op, l, r);\n }\n if (cond.id === undefined) {\n throw new Error('evaluateRuleTree: comparison with tolerance requires id');\n }\n if (cond.op !== 'gt' && cond.op !== 'lt') {\n throw new Error(`evaluateRuleTree: tolerance is only supported for op gt/lt, got ${cond.op}`);\n }\n\n const prev = state.get(cond.id);\n const { lower, upper } = band(r, cond.tolerance);\n let result: 0 | 1;\n\n if (prev === undefined) {\n result = rawCompare(cond.op, l, r) ? 1 : 0;\n } else if (cond.op === 'gt') {\n if (prev === 1) result = l < lower ? 0 : 1;\n else result = l > upper ? 1 : 0;\n } else {\n if (prev === 1) result = l > upper ? 0 : 1;\n else result = l < lower ? 1 : 0;\n }\n\n outState.set(cond.id, result);\n return result === 1;\n}\n\nfunction walk(\n rules: RuleNode,\n values: ReadonlyMap<string, number>,\n state: RuleTreeState,\n outState: Map<string, 0 | 1>,\n): TargetWeights {\n if (rules.op === 'allocate') {\n const out = new Map<AssetId, number>();\n for (const [assetId, weight] of Object.entries(rules.weights)) {\n out.set(assetId, weight);\n }\n return out;\n }\n return evalComparison(rules.cond, values, state, outState)\n ? walk(rules.then, values, state, outState)\n : walk(rules.else, values, state, outState);\n}\n\n/**\n * Evaluates a {@link RuleNode} tree against a resolved set of feature values\n * and returns the target allocation weights together with the updated\n * hysteresis state.\n *\n * The tree is walked depth-first. At each {@link IfNode} the comparison is\n * evaluated (with hysteresis applied when `tolerance` and `id` are present)\n * and the walk follows either `then` or `else`. Evaluation terminates at an\n * {@link AllocateNode} whose `weights` map is returned verbatim.\n *\n * Hysteresis: when a {@link Comparison} carries a `tolerance`, the previous\n * outcome in `state` is used to decide whether to flip the result. The updated\n * outcomes for all visited comparisons are collected in the returned `state` map.\n *\n * Throws if a {@link FeatureRef} resolves to `undefined` (the caller should\n * suppress this with a guard or catch-block, as {@link fromSpec} does).\n *\n * @param rules - Root of the rule tree to evaluate.\n * @param values - Resolved feature values, keyed by feature id. Must contain\n * every `ref` string used in the tree; missing keys throw.\n * @param state - Prior hysteresis state from the previous evaluation step.\n * Pass an empty `Map` on the first call.\n * @returns An object with:\n * - `weights` — target portfolio weights as a `Map<AssetId, number>`.\n * - `state` — updated {@link RuleTreeState} to pass to the next step.\n *\n * @example\n * ```ts\n * import { evaluateRuleTree } from '@livefolio/sdk';\n * import type { RuleNode, RuleTreeState } from '@livefolio/sdk';\n *\n * const rules: RuleNode = {\n * op: 'if',\n * cond: { op: 'gt', left: { ref: 'price' }, right: { ref: 'sma200' } },\n * then: { op: 'allocate', weights: { SPY: 1 } },\n * else: { op: 'allocate', weights: { SHY: 1 } },\n * };\n *\n * let state: RuleTreeState = new Map();\n * const values = new Map([['price', 450], ['sma200', 420]]);\n * const result = evaluateRuleTree(rules, values, state);\n * // result.weights → Map { 'SPY' => 1 }\n * state = result.state;\n * ```\n */\nexport function evaluateRuleTree(\n rules: RuleNode,\n values: ReadonlyMap<string, number>,\n state: RuleTreeState = new Map(),\n): { weights: TargetWeights; state: RuleTreeState } {\n const next = new Map<string, 0 | 1>(state);\n const weights = walk(rules, values, state, next);\n return { weights, state: next };\n}\n","import type { Asset } from '../interfaces/types';\nimport type { AssetRef } from './types';\n\n/**\n * Resolves a spec-form {@link AssetRef} to a runtime {@link Asset}. The\n * `kind` field on the ref selects the variant; absent `kind` defaults to\n * `'equity'` for backward compatibility.\n *\n * Pure. No I/O. Used by `fromSpec`, `withSynthetics`, and\n * `evaluateFeatureSpecs` so the resolution rule lives in one place.\n */\nexport function resolveAssetRef(ref: AssetRef): Asset {\n if (ref.kind === 'macro') {\n return { kind: 'macro', id: ref.id, symbol: ref.symbol };\n }\n return ref.exchange !== undefined\n ? { kind: 'equity', id: ref.id, symbol: ref.symbol, exchange: ref.exchange }\n : { kind: 'equity', id: ref.id, symbol: ref.symbol };\n}\n","import type { Series } from '../interfaces/types';\nimport type { FeatureSpec } from '../features/spec';\nimport type { FeatureRuntime } from '../features/runtime';\nimport type { TacticalFeatureSpec } from './types';\nimport { resolveAssetRef } from './asset-ref';\n\nfunction toFeatureSpec(spec: TacticalFeatureSpec): FeatureSpec {\n switch (spec.kind) {\n case 'price':\n return { kind: 'price' };\n case 'sma':\n return { kind: 'sma', period: spec.period };\n case 'ema':\n return { kind: 'ema', period: spec.period };\n case 'rsi':\n return { kind: 'rsi', period: spec.period };\n case 'return':\n return spec.mode !== undefined\n ? { kind: 'return', period: spec.period, mode: spec.mode }\n : { kind: 'return', period: spec.period };\n case 'volatility':\n return { kind: 'volatility', period: spec.period };\n case 'drawdown':\n return { kind: 'drawdown', period: spec.period };\n }\n}\n\nfunction indexAtOrBefore(series: Series, t: Date): number {\n if (series.length === 0) return -1;\n const target = t.getTime();\n let lo = 0;\n let hi = series.length - 1;\n let ans = -1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (series[mid]!.t.getTime() <= target) {\n ans = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return ans;\n}\n\nfunction readDelayed(series: Series, t: Date, delay: number): number | undefined {\n const idx = indexAtOrBefore(series, t);\n if (idx < 0) return undefined;\n const target = idx - delay;\n if (target < 0) return undefined;\n return series[target]!.v;\n}\n\n/**\n * Resolves each {@link TacticalFeatureSpec} in `specs` to a scalar value as of\n * date `t` by calling into `runtime` and reading the series at the appropriate\n * bar index. All specs are computed in parallel via `Promise.all`.\n *\n * The returned map uses each spec's `id` as the key. The value is `undefined`\n * when the indicator series has no data on or before `t`, or when the `delay`\n * offset steps past the beginning of the series.\n *\n * Validation performed before dispatching to the runtime:\n * - Duplicate `id` values in `specs` throw immediately.\n * - A non-integer or negative `delay` throws immediately.\n *\n * @param specs - Ordered list of feature declarations from {@link TacticalSpec.features}.\n * @param runtime - Feature computation backend that owns the data feed and cache.\n * @param t - Evaluation date; the series is read at the latest bar on or before `t`.\n * @returns A map from feature id to resolved numeric value (`undefined` when unavailable).\n *\n * @example\n * ```ts\n * import { evaluateFeatureSpecs } from '@livefolio/sdk';\n * import type { TacticalFeatureSpec } from '@livefolio/sdk';\n *\n * const specs: TacticalFeatureSpec[] = [\n * { id: 'spy_sma200', kind: 'sma', asset: { id: 'SPY', symbol: 'SPY' }, period: 200 },\n * { id: 'spy_price', kind: 'price', asset: { id: 'SPY', symbol: 'SPY' } },\n * ];\n *\n * const values = await evaluateFeatureSpecs(specs, runtime, new Date('2024-06-01'));\n * // values.get('spy_price') → 528.3\n * ```\n */\nexport async function evaluateFeatureSpecs(\n specs: ReadonlyArray<TacticalFeatureSpec>,\n runtime: FeatureRuntime,\n t: Date,\n): Promise<Map<string, number | undefined>> {\n const seen = new Set<string>();\n for (const spec of specs) {\n if (seen.has(spec.id)) {\n throw new Error(`evaluateFeatureSpecs: duplicate feature id \"${spec.id}\"`);\n }\n seen.add(spec.id);\n const d = spec.delay;\n if (d !== undefined && (!Number.isInteger(d) || d < 0)) {\n throw new Error(`evaluateFeatureSpecs: delay must be a non-negative integer, got ${d}`);\n }\n }\n\n const entries = await Promise.all(\n specs.map(async (spec) => {\n const series = await runtime.compute(toFeatureSpec(spec), resolveAssetRef(spec.asset));\n const delay = spec.delay ?? 0;\n return [spec.id, readDelayed(series, t, delay)] as const;\n }),\n );\n\n return new Map(entries);\n}\n","import type { Asset, AssetId, Bar, DateRange, Frequency } from '../interfaces/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { StreamingDataFeed, StreamingBar } from '../interfaces/streaming-data-feed';\nimport type { SyntheticAsset } from './types';\nimport { resolveAssetRef } from './asset-ref';\n\nconst TRADING_DAYS_PER_YEAR = 252;\n\n/**\n * Per-bar core of the synthesis formula. Shared by {@link withSynthetics}\n * (historical) and {@link withStreamingSynthetics} (live) so the two code\n * paths cannot drift.\n *\n * Returns the synthesized close given the previous underlying / synthetic\n * closes and the current underlying close. Cold start (either prev undefined)\n * returns `underlyingClose` so the first emitted bar anchors to the underlying.\n */\nfunction nextSynthClose(opts: {\n prevSynthClose: number | undefined;\n prevUnderlyingClose: number | undefined;\n underlyingClose: number;\n leverage: number;\n expense: number | undefined;\n}): number {\n const { prevSynthClose, prevUnderlyingClose, underlyingClose, leverage, expense } = opts;\n if (prevSynthClose === undefined || prevUnderlyingClose === undefined) {\n return underlyingClose;\n }\n const drag = (expense ?? 0) / TRADING_DAYS_PER_YEAR;\n const safe = Number.isFinite(prevUnderlyingClose) && prevUnderlyingClose !== 0;\n const r = safe ? (underlyingClose - prevUnderlyingClose) / prevUnderlyingClose : 0;\n return prevSynthClose * (1 + leverage * r) * (1 - drag);\n}\n\nasync function* synthesize(\n underlyingBars: AsyncIterable<Bar>,\n leverage: number,\n expense: number | undefined,\n): AsyncIterable<Bar> {\n let prevUnderlyingClose: number | undefined;\n let prevSynthClose: number | undefined;\n for await (const u of underlyingBars) {\n const close = nextSynthClose({\n prevSynthClose,\n prevUnderlyingClose,\n underlyingClose: u.close,\n leverage,\n expense,\n });\n yield {\n t: u.t,\n open: close,\n high: close,\n low: close,\n close,\n volume: u.volume,\n };\n prevUnderlyingClose = u.close;\n prevSynthClose = close;\n }\n}\n\n/**\n * Wraps a {@link DataFeed} so that requests for any asset whose `id` appears in\n * `synthetics` are intercepted and their bar stream is derived on-the-fly from\n * the corresponding `underlying` asset.\n *\n * The synthesized close price on each bar is computed as:\n * ```\n * close_t = close_{t-1} × (1 + leverage × underlyingReturn_t) × (1 − expense/252)\n * ```\n * The first bar in the stream uses the underlying close directly. OHLC fields\n * other than `close` are all set to the synthesized close (they are not\n * independently scaled); `volume` is passed through from the underlying bar.\n *\n * Non-synthetic assets are proxied transparently to the original `dataFeed`.\n * `fundamentals` and `events` methods, if present, are forwarded unchanged.\n *\n * Throws at construction time if `synthetics` contains duplicate `id` values.\n *\n * @param dataFeed - The real data feed to wrap.\n * @param synthetics - Synthetic asset definitions; typically `spec.synthetics ?? []`.\n * @returns A new {@link DataFeed} that intercepts synthetic asset ids.\n *\n * @example\n * ```ts\n * import { withSynthetics } from '@livefolio/sdk';\n * import type { SyntheticAsset } from '@livefolio/sdk';\n *\n * const leveraged: SyntheticAsset = {\n * id: 'SPY_3X', symbol: 'SPY3X',\n * underlying: { id: 'SPY', symbol: 'SPY' },\n * leverage: 3,\n * expense: 0.01,\n * };\n *\n * const feed = withSynthetics(realFeed, [leveraged]);\n * // Requesting bars for asset { id: 'SPY_3X', ... } now returns 3× leveraged returns.\n * ```\n */\nexport function withSynthetics(dataFeed: DataFeed, synthetics: ReadonlyArray<SyntheticAsset>): DataFeed {\n const byId = new Map<string, SyntheticAsset>();\n for (const s of synthetics) {\n if (byId.has(s.id)) {\n throw new Error(`withSynthetics: duplicate synthetic asset id \"${s.id}\"`);\n }\n byId.set(s.id, s);\n }\n\n const wrapped: DataFeed = {\n bars(asset: Asset, range: DateRange, freq: Frequency): AsyncIterable<Bar> {\n const synth = byId.get(asset.id);\n if (!synth) return dataFeed.bars(asset, range, freq);\n const underlying = resolveAssetRef(synth.underlying);\n return synthesize(dataFeed.bars(underlying, range, freq), synth.leverage, synth.expense);\n },\n };\n\n if (dataFeed.fundamentals) {\n wrapped.fundamentals = dataFeed.fundamentals.bind(dataFeed);\n }\n if (dataFeed.events) {\n wrapped.events = dataFeed.events.bind(dataFeed);\n }\n\n return wrapped;\n}\n\n/**\n * Options for {@link withStreamingSynthetics}.\n */\nexport interface WithStreamingSyntheticsOptions {\n /**\n * Last known close per asset id, used to seed `prevUnderlyingClose` and\n * `prevSynthClose` so the first live tick of a synthetic continues smoothly\n * from the end of its historical series.\n *\n * Build it from a {@link BacktestResult}:\n * ```ts\n * const seedLastCloses = new Map<AssetId, number>();\n * for (const [id, bars] of history.bars) {\n * const last = bars.at(-1)?.close;\n * if (last !== undefined) seedLastCloses.set(id, last);\n * }\n * ```\n *\n * Without seeding, the first synthesized tick lands on the underlying's\n * price and produces a visible jump in live preview.\n */\n seedLastCloses: ReadonlyMap<AssetId, number>;\n}\n\n/**\n * Streaming-feed counterpart to {@link withSynthetics}. Wraps a\n * {@link StreamingDataFeed} so that subscriptions for synthetic asset ids\n * resolve to upstream subscriptions on the underlying, with each underlying\n * tick re-emitted as a synthesized tick on the synthetic's id using the same\n * `(1 + leverage × r) × (1 − expense/252)` formula as the historical wrapper.\n *\n * Behavior:\n * - Non-synthetic ids in the `assets` argument pass through to the inner feed\n * unchanged.\n * - Underlyings that aren't directly in `assets` but are needed by a synthetic\n * are subscribed silently — only the synthesized ticks are yielded back to\n * the caller for those.\n * - Underlyings that the caller _did_ ask for in `assets` are yielded both as\n * the raw underlying tick and as the synthesized tick(s).\n *\n * Throws at construction time if `synthetics` contains duplicate `id` values.\n *\n * @example\n * ```ts\n * import { withStreamingSynthetics, runLive } from '@livefolio/sdk';\n *\n * const seedLastCloses = new Map<AssetId, number>();\n * for (const [id, bars] of history.bars) {\n * const last = bars.at(-1)?.close;\n * if (last !== undefined) seedLastCloses.set(id, last);\n * }\n *\n * const liveFeed = withStreamingSynthetics(rawStreamingFeed, spec.synthetics ?? [], {\n * seedLastCloses,\n * });\n *\n * for await (const event of runLive({ strategy, history, dataFeed: liveFeed, executor, calendar })) {\n * // …\n * }\n * ```\n */\nexport function withStreamingSynthetics(\n inner: StreamingDataFeed,\n synthetics: ReadonlyArray<SyntheticAsset>,\n opts: WithStreamingSyntheticsOptions,\n): StreamingDataFeed {\n const synthById = new Map<AssetId, SyntheticAsset>();\n for (const s of synthetics) {\n if (synthById.has(s.id)) {\n throw new Error(`withStreamingSynthetics: duplicate synthetic asset id \"${s.id}\"`);\n }\n synthById.set(s.id, s);\n }\n\n return {\n async *subscribe(assets: ReadonlyArray<Asset>): AsyncIterable<StreamingBar> {\n const passthroughIds = new Set<AssetId>();\n const requestedSynths: SyntheticAsset[] = [];\n const upstream: Asset[] = [];\n const upstreamSeen = new Set<AssetId>();\n\n for (const a of assets) {\n const synth = synthById.get(a.id);\n if (synth) {\n requestedSynths.push(synth);\n if (!upstreamSeen.has(synth.underlying.id)) {\n upstreamSeen.add(synth.underlying.id);\n upstream.push(resolveAssetRef(synth.underlying));\n }\n } else {\n passthroughIds.add(a.id);\n if (!upstreamSeen.has(a.id)) {\n upstreamSeen.add(a.id);\n upstream.push(a);\n }\n }\n }\n\n type SynthState = {\n synth: SyntheticAsset;\n asset: Asset;\n prevUnderlyingClose: number | undefined;\n prevSynthClose: number | undefined;\n };\n const synthsByUnderlying = new Map<AssetId, SynthState[]>();\n for (const s of requestedSynths) {\n const st: SynthState = {\n synth: s,\n asset: resolveAssetRef({ id: s.id, symbol: s.symbol }),\n prevUnderlyingClose: opts.seedLastCloses.get(s.underlying.id),\n prevSynthClose: opts.seedLastCloses.get(s.id),\n };\n const list = synthsByUnderlying.get(s.underlying.id) ?? [];\n list.push(st);\n synthsByUnderlying.set(s.underlying.id, list);\n }\n\n for await (const tick of inner.subscribe(upstream)) {\n if (passthroughIds.has(tick.asset.id)) {\n yield tick;\n }\n\n const states = synthsByUnderlying.get(tick.asset.id);\n if (!states) continue;\n\n const underlyingClose = tick.bar.close;\n for (const st of states) {\n const synthClose = nextSynthClose({\n prevSynthClose: st.prevSynthClose,\n prevUnderlyingClose: st.prevUnderlyingClose,\n underlyingClose,\n leverage: st.synth.leverage,\n expense: st.synth.expense,\n });\n yield {\n asset: st.asset,\n bar: {\n t: tick.bar.t,\n open: synthClose,\n high: synthClose,\n low: synthClose,\n close: synthClose,\n volume: tick.bar.volume,\n },\n };\n st.prevSynthClose = synthClose;\n }\n for (const st of states) {\n st.prevUnderlyingClose = underlyingClose;\n }\n }\n },\n };\n}\n","import type { Asset, AssetId } from '../interfaces/types';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { Strategy } from '../strategy/types';\nimport { reconcile } from '../strategy/reconcile';\nimport type { FeatureRuntime } from '../features/runtime';\nimport { seriesAt } from '../features/series-utils';\nimport type { RebalanceFrequency, RuleTreeState, TacticalSpec } from './types';\nimport { resolveAssetRef } from './asset-ref';\nimport { evaluateRuleTree } from './evaluate-rule-tree';\nimport { evaluateFeatureSpecs } from './evaluate-feature-specs';\n\nlet _warnedV0 = false;\n\n/** Test-only: reset the once-per-process deprecation gate. */\nexport function _resetTacticalDeprecationWarningForTesting(): void {\n _warnedV0 = false;\n}\n\n/**\n * The feature bundle computed on each rebalance step and passed to the rule\n * tree. Produced by the `features` method of the {@link Strategy} returned by\n * {@link fromSpec}.\n *\n * - `values` — named indicator results keyed by the `id` field of each\n * {@link TacticalFeatureSpec}. A value is `undefined` when the indicator\n * cannot be computed for that bar (e.g. insufficient history).\n * - `prices` — most-recent closing prices for each asset in the universe,\n * keyed by asset ID.\n */\nexport type TacticalFeatures = {\n values: ReadonlyMap<string, number | undefined>;\n prices: ReadonlyMap<AssetId, number>;\n};\n\n/**\n * Runtime dependencies required by {@link fromSpec} to hydrate a\n * {@link TacticalSpec} into a runnable {@link Strategy}.\n */\nexport type FromSpecOptions = {\n /** Feature computation backend — wraps the data feed and caching layer. */\n runtime: FeatureRuntime;\n /** Exchange calendar used to gate rebalance days via {@link isRebalanceDay}. */\n calendar: Calendar;\n};\n\nfunction validateSynthetics(spec: TacticalSpec): void {\n const synths = spec.synthetics ?? [];\n if (synths.length === 0) return;\n\n const universeIds = new Set(spec.universe.map((u) => u.id));\n\n for (const s of synths) {\n if (s.underlying.id === s.id) {\n throw new Error(`fromSpec: synthetic asset \"${s.id}\" cannot reference itself as underlying`);\n }\n const u = spec.universe.find((x) => x.id === s.id);\n if (!u) continue;\n if (u.symbol !== s.symbol) {\n throw new Error(`fromSpec: synthetic asset id \"${s.id}\" collides with a universe AssetRef of a different symbol`);\n }\n if (!universeIds.has(s.underlying.id)) {\n throw new Error(\n `fromSpec: synthetic asset id \"${s.id}\" collides with a universe AssetRef whose underlying is not declared in the universe`,\n );\n }\n }\n}\n\n/**\n * Returns a stable string key that identifies the rebalance period containing\n * date `t` for the given `freq`. Two dates that map to the same key belong to\n * the same period and therefore produce the same rebalance decision. Used\n * internally by {@link isRebalanceDay} to detect period boundaries.\n *\n * @param t - The date to classify.\n * @param freq - Rebalance cadence (see {@link RebalanceFrequency}).\n * @returns A compact string such as `'2024-3'` (monthly), `'2024-W14'`\n * (weekly), or `'2024-1'` (quarterly Q2).\n */\nexport function periodKey(t: Date, freq: RebalanceFrequency): string {\n const y = t.getUTCFullYear();\n const m = t.getUTCMonth();\n switch (freq) {\n case 'Daily':\n return `${y}-${m}-${t.getUTCDate()}`;\n case 'Weekly': {\n const thu = new Date(t);\n thu.setUTCDate(thu.getUTCDate() + 3 - ((thu.getUTCDay() + 6) % 7));\n const yearStart = new Date(Date.UTC(thu.getUTCFullYear(), 0, 1));\n const weekNo = Math.ceil(((thu.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7);\n return `${thu.getUTCFullYear()}-W${weekNo}`;\n }\n case 'Monthly':\n return `${y}-${m}`;\n case 'Quarterly':\n return `${y}-Q${Math.floor(m / 3)}`;\n case 'Yearly':\n return `${y}`;\n }\n}\n\n/**\n * Returns `true` when `t` is the last trading day of its rebalance period\n * according to `freq` and `calendar`. The check is: `periodKey(t) !== periodKey(next(t))`.\n * For `'Daily'` cadence this always returns `true`.\n *\n * @param t - Current trading day (must itself be a trading day).\n * @param freq - Rebalance cadence (see {@link RebalanceFrequency}).\n * @param calendar - Exchange calendar used to find the next trading day.\n * @returns `true` if today is the last day of its period and orders should be issued.\n */\nexport function isRebalanceDay(t: Date, freq: RebalanceFrequency, calendar: Calendar): boolean {\n if (freq === 'Daily') return true;\n const next = calendar.next(t);\n return periodKey(t, freq) !== periodKey(next, freq);\n}\n\n/**\n * Hydrates a plain {@link TacticalSpec} data object into a runnable\n * {@link Strategy} that `runBacktest` can drive step-by-step.\n *\n * State is threaded explicitly through `build` via the\n * {@link Strategy | `Strategy<F, S>.build`} signature. `initialState()` returns\n * an empty {@link RuleTreeState} Map; the runtime is responsible for storing and\n * forwarding the state between calls. This design makes `build` a pure function\n * of its inputs — calling it twice with identical arguments produces identical\n * outputs, enabling snapshot/restore for preview-builds in live mode.\n *\n * Validation performed at construction time:\n * - A `'tactical/v0'` `kind` emits a one-time deprecation warning to `console.warn`.\n * - Synthetic assets are checked for self-reference, symbol collisions, and\n * missing universe entries (see internal `validateSynthetics`).\n *\n * @param spec - The declarative strategy spec.\n * @param opts - Runtime dependencies (feature backend and calendar).\n * @returns A {@link Strategy} whose `features` method fetches indicator values\n * and whose `build` method converts them to rebalance orders.\n *\n * @example\n * ```ts\n * import { fromSpec, MemoryFeatureCache, NYSEExchangeCalendar } from '@livefolio/sdk';\n * import { FeatureRuntime } from '@livefolio/sdk/features';\n *\n * const calendar = new NYSEExchangeCalendar();\n * const cache = new MemoryFeatureCache();\n * const runtime = new FeatureRuntime({ feed: myDataFeed, cache });\n *\n * const strategy = fromSpec(mySpec, { runtime, calendar });\n * ```\n */\nexport function fromSpec(spec: TacticalSpec, opts: FromSpecOptions): Strategy<TacticalFeatures, RuleTreeState> {\n if (spec.kind === 'tactical/v0' && !_warnedV0) {\n _warnedV0 = true;\n\n console.warn(\n '[@livefolio/sdk] tactical/v0 is deprecated; migrate to tactical/v1. ' +\n 'The two are byte-for-byte equivalent. This warning fires once per process.',\n );\n }\n validateSynthetics(spec);\n const universe: ReadonlyArray<Asset> = spec.universe.map(resolveAssetRef);\n const assetsById = new Map<AssetId, Asset>();\n for (const a of universe) assetsById.set(a.id, a);\n for (const s of spec.synthetics ?? []) {\n if (!assetsById.has(s.id)) {\n assetsById.set(s.id, { kind: 'equity', id: s.id, symbol: s.symbol });\n }\n }\n const { runtime, calendar } = opts;\n const cadence: RebalanceFrequency = spec.rebalance?.frequency ?? 'Daily';\n\n return {\n universe: () => universe,\n\n features: async (_u, _p, t) => {\n const [values, priceEntries] = await Promise.all([\n evaluateFeatureSpecs(spec.features, runtime, t),\n Promise.all(\n universe.map(async (asset) => {\n const s = await runtime.compute({ kind: 'price' }, asset);\n return [asset.id, seriesAt(s, t)] as const;\n }),\n ),\n ]);\n const prices = new Map<AssetId, number>();\n for (const [id, v] of priceEntries) {\n if (v !== undefined) prices.set(id, v);\n }\n return { values, prices };\n },\n\n initialState: () => new Map() as RuleTreeState,\n\n build: (features, portfolio, state, t) => {\n if (!isRebalanceDay(t, cadence, calendar)) {\n return { orders: [], state };\n }\n\n const defined = new Map<string, number>();\n for (const [id, v] of features.values) {\n if (v !== undefined) defined.set(id, v);\n }\n let evaluated;\n try {\n evaluated = evaluateRuleTree(spec.rules, defined, state);\n } catch (e) {\n if (e instanceof Error && /has no value/.test(e.message)) {\n return { orders: [], state };\n }\n throw e;\n }\n for (const assetId of evaluated.weights.keys()) {\n if (!features.prices.has(assetId)) {\n return { orders: [], state };\n }\n }\n return {\n orders: reconcile(evaluated.weights, portfolio, features.prices, assetsById),\n state: evaluated.state,\n };\n },\n };\n}\n","export * from './indicators';\nexport { collectBars, barsToSeries, seriesAt } from './series-utils';\nexport type { BarField } from './series-utils';\nexport {\n defineFeature,\n getFeatureCompute,\n paramsHash,\n type FeatureSpec,\n type FeatureKind,\n type ComputeFn,\n} from './spec';\nexport { FeatureRuntime } from './runtime';\nexport type { FeatureRuntimeOptions } from './runtime';\n"],"mappings":"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A,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAA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EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG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,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAA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","names":["MS_PER_DAY","MS_PER_DAY","MS_PER_DAY","SUN","MON","TUE","SAT","utcDate","WEEKDAYS_MON_FRI","REGULAR_HOLIDAYS","ADHOC_HOLIDAYS","SPECIAL_CLOSES","SPECIAL_CLOSES_ADHOC","SPECIAL_OPENS","SPECIAL_OPENS_ADHOC","MS_PER_DAY"]}
1
+ {"version":3,"sources":["../src/strategy/reconcile.ts","../src/portfolio/apply.ts","../src/strategy/run-backtest.ts","../src/features/series-utils.ts","../src/features/indicators/sma.ts","../src/features/indicators/ema.ts","../src/features/indicators/rsi.ts","../src/features/indicators/return.ts","../src/features/indicators/volatility.ts","../src/features/indicators/drawdown.ts","../src/features/spec.ts","../src/features/runtime.ts","../src/reference/memory-feature-cache.ts","../src/strategy/run-live.ts","../src/reference/backtest-executor.ts","../src/reference/routing-data-feed.ts","../src/reference/routing-streaming-data-feed.ts","../src/reference/routing-quote-feed.ts","../src/reference/polling-stream-from-historical.ts","../src/calendars/exchange-calendar.ts","../src/calendars/holiday-rules.ts","../src/calendars/nyse.ts","../src/calendars/lse.ts","../src/calendars/get-calendar.ts","../src/calendars/crypto-24x7.ts","../src/tactical/index.ts","../src/tactical/evaluate-rule-tree.ts","../src/tactical/asset-ref.ts","../src/tactical/evaluate-feature-specs.ts","../src/tactical/synthetics.ts","../src/tactical/from-spec.ts","../src/features/index.ts"],"sourcesContent":["import type { Portfolio } from '../portfolio/types';\nimport type { Asset, AssetId } from '../interfaces/types';\nimport type { RebalanceOrder } from '../orders/types';\n\n/**\n * Maps each asset ID to its desired portfolio weight as a fraction of total\n * portfolio value (e.g. `0.6` means 60 %). Weights need not sum to 1; any\n * residual becomes cash. Passing a weight of `0` or omitting an asset entirely\n * will generate a full exit order for any existing long position in that asset.\n */\nexport type TargetWeights = ReadonlyMap<AssetId, number>;\n\n/**\n * Maps each asset ID to its current market price. Required for every asset that\n * appears in `TargetWeights` and for every asset currently held in the portfolio.\n * `reconcile` throws if a target asset has no corresponding price entry.\n */\nexport type PriceMap = ReadonlyMap<AssetId, number>;\n\n/**\n * Converts target portfolio weights into a minimal set of `RebalanceOrder`\n * instructions that move the current portfolio toward the desired allocation.\n *\n * The algorithm:\n * 1. Compute total portfolio value as `cash + Σ(held_shares × price)`.\n * 2. For each target asset, derive `targetShares = floor(totalValue × weight / price)`.\n * 3. Emit a `RebalanceOrder` with `delta = targetShares - heldShares` for any\n * asset where the delta is non-zero (positive = buy, negative = sell).\n * 4. Emit exit orders (`delta = -heldShares`) for any long position in an asset\n * that does not appear in `targets`.\n *\n * Only long positions are considered — short positions in the portfolio are\n * ignored. Share counts are always floored to integer lots.\n *\n * @param targets - Desired weight per asset. Keys are `AssetId` strings. A weight\n * of `0` is valid and will result in a full exit for any existing position.\n * @param portfolio - Current portfolio. Cash and long positions determine total\n * value and existing share counts.\n * @param prices - Current prices for all assets that appear in `targets` or are\n * currently held. Throws `Error` if a target asset is missing from this map.\n * @param assets - Optional canonical {@link Asset} metadata keyed by id. When\n * `reconcile` needs to emit an order for an asset that is not yet held in the\n * portfolio, it consults this map for the proper `symbol`/`kind`. If the\n * asset is missing here too, the order falls back to a synthesized\n * `{ kind: 'equity', id, symbol: id }` — lossless but display-unfriendly.\n * @returns A readonly array of `RebalanceOrder` objects. The array may be empty\n * if the portfolio is already at the target allocation. Order IDs are\n * deterministic within a single call (`rebal_<assetId>_<counter>`).\n *\n * @example\n * ```ts\n * import { reconcile } from '@livefolio/sdk';\n *\n * const targets = new Map([\n * ['US:SPY', 0.6],\n * ['US:BND', 0.4],\n * ]);\n * const prices = new Map([\n * ['US:SPY', 440.0],\n * ['US:BND', 75.0],\n * ]);\n *\n * // Empty portfolio with $100 000 cash\n * const portfolio = { cash: 100_000, positions: [] };\n * const orders = reconcile(targets, portfolio, prices);\n * // orders => [\n * // { id: 'rebal_US:SPY_0', kind: 'rebalance', asset: { ... }, delta: 136 },\n * // { id: 'rebal_US:BND_1', kind: 'rebalance', asset: { ... }, delta: 533 },\n * // ]\n * ```\n */\nexport function reconcile(\n targets: TargetWeights,\n portfolio: Portfolio,\n prices: PriceMap,\n assets?: ReadonlyMap<AssetId, Asset>,\n): ReadonlyArray<RebalanceOrder> {\n const longByAsset = new Map<AssetId, { asset: Asset; quantity: number }>();\n for (const p of portfolio.positions) {\n if (p.side !== 'long') continue;\n const cur = longByAsset.get(p.asset.id);\n if (cur) cur.quantity += p.quantity;\n else longByAsset.set(p.asset.id, { asset: p.asset, quantity: p.quantity });\n }\n\n let totalValue = portfolio.cash;\n for (const { asset, quantity } of longByAsset.values()) {\n const price = prices.get(asset.id);\n if (price !== undefined) totalValue += quantity * price;\n }\n\n const orders: RebalanceOrder[] = [];\n let counter = 0;\n const nextId = (assetId: AssetId): string => `rebal_${assetId}_${counter++}`;\n\n const seen = new Set<AssetId>();\n\n for (const [assetId, weight] of targets) {\n const price = prices.get(assetId);\n if (price === undefined) {\n throw new Error(`reconcile: missing price for target asset ${assetId}`);\n }\n // Clamp at 0: long-only reconcile must never emit a negative target.\n // A negative `targetShares` can otherwise arise when `totalValue` goes\n // below zero (e.g. a held leveraged-synthetic position takes a negative\n // close price after an extreme single-bar underlying move, where\n // `1 + leverage * r < 0`). Without this clamp, the resulting negative\n // `delta` on a non-held target produces a rebalance-reduce order with\n // no position to reduce, which `applyFills` then rejects. See #37.\n const targetShares = Math.max(0, Math.floor((totalValue * weight) / price));\n const held = longByAsset.get(assetId);\n const currentShares = held?.quantity ?? 0;\n const delta = targetShares - currentShares;\n seen.add(assetId);\n if (delta !== 0) {\n const asset: Asset = held?.asset ??\n assets?.get(assetId) ?? {\n kind: 'equity',\n id: assetId,\n symbol: assetId,\n };\n orders.push({ id: nextId(assetId), kind: 'rebalance', asset, delta });\n }\n }\n\n for (const [assetId, { asset, quantity }] of longByAsset) {\n if (seen.has(assetId)) continue;\n orders.push({ id: nextId(assetId), kind: 'rebalance', asset, delta: -quantity });\n }\n\n return orders;\n}\n","import type { Portfolio, Position, PositionId } from './types';\nimport type { Order, Fill } from '../orders/types';\n\nconst newPositionId = (() => {\n let n = 0;\n return (): PositionId => `pos_${++n}`;\n})();\n\nfunction findOrder(orders: ReadonlyArray<Order>, id: string): Order | undefined {\n return orders.find((o) => o.id === id);\n}\n\n/**\n * Applies a batch of confirmed fills to a portfolio, returning a new\n * {@link Portfolio} snapshot. This is the single function that advances\n * portfolio state after order execution.\n *\n * For each fill the corresponding order is looked up in `orders` by\n * `fill.orderRef`. The order's `kind` determines the accounting treatment:\n * - `'open'` — adds a new {@link Position} and debits cash.\n * - `'close'` — removes shares from an existing position and credits cash.\n * - `'adjust'` — updates the position's `quantity`; only fees are debited.\n * - `'rebalance'` — buys or sells shares in the long position for `asset`;\n * creates the position on a positive `delta`, removes it\n * when fully reduced. A reduce against a non-existent\n * long position is silently ignored (matching the same\n * case in {@link applyOrders}).\n *\n * The returned `portfolio.t` is updated to the maximum fill timestamp.\n *\n * @param portfolio - The current portfolio state before this batch.\n * @param fills - Execution confirmations returned by {@link Executor.submit}.\n * Each fill's `orderRef` MUST match an `id` in `orders`.\n * @param orders - The full order batch that was submitted. Used to look up\n * order details for each fill.\n * @returns A new {@link Portfolio} with updated positions, cash, and timestamp.\n * The input `portfolio` is not mutated.\n *\n * @example\n * ```ts\n * import { applyFills } from '@livefolio/sdk';\n * import type { Portfolio, Order, Fill } from '@livefolio/sdk';\n *\n * const portfolio: Portfolio = { cash: 10_000, positions: [], t: new Date('2024-01-01') };\n *\n * const order: Order = {\n * id: 'ord_1', kind: 'open',\n * asset: { kind: 'equity', id: 'AAPL', symbol: 'AAPL' },\n * side: 'long', quantity: 10,\n * };\n * const fill: Fill = { orderRef: 'ord_1', t: new Date('2024-01-02'), quantity: 10, price: 185, fees: 0 };\n *\n * const next = applyFills(portfolio, [fill], [order]);\n * // next.cash === 8_250, next.positions.length === 1\n * ```\n */\nexport function applyFills(portfolio: Portfolio, fills: ReadonlyArray<Fill>, orders: ReadonlyArray<Order>): Portfolio {\n let positions: Position[] = [...portfolio.positions];\n let cash = portfolio.cash;\n let t = portfolio.t;\n\n for (const fill of fills) {\n const order = findOrder(orders, fill.orderRef);\n if (!order) {\n throw new Error(`applyFills: fill.orderRef \"${fill.orderRef}\" matches no known order`);\n }\n t = fill.t > t ? fill.t : t;\n\n switch (order.kind) {\n case 'open': {\n const pos: Position = {\n id: newPositionId(),\n asset: order.asset,\n side: order.side,\n quantity: fill.quantity,\n entry: { date: fill.t, price: fill.price },\n basis: fill.quantity * fill.price + fill.fees,\n };\n positions.push(pos);\n cash -= fill.quantity * fill.price + fill.fees;\n break;\n }\n case 'close': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) throw new Error(`applyFills: close target ${order.positionId} not found`);\n const pos = positions[idx]!;\n const sign = pos.side === 'long' ? 1 : -1;\n cash += sign * fill.quantity * fill.price - fill.fees;\n const remaining = pos.quantity - fill.quantity;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n positions[idx] = { ...pos, quantity: remaining };\n }\n break;\n }\n case 'adjust': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) throw new Error(`applyFills: adjust target ${order.positionId} not found`);\n const pos = positions[idx]!;\n const nextQty = order.changes.quantity ?? pos.quantity;\n positions[idx] = { ...pos, quantity: nextQty };\n cash -= fill.fees;\n break;\n }\n case 'rebalance': {\n const idx = positions.findIndex((p) => p.asset.id === order.asset.id && p.side === 'long');\n if (order.delta > 0) {\n const cost = fill.quantity * fill.price + fill.fees;\n cash -= cost;\n if (idx < 0) {\n positions.push({\n id: newPositionId(),\n asset: order.asset,\n side: 'long',\n quantity: fill.quantity,\n entry: { date: fill.t, price: fill.price },\n basis: cost,\n });\n } else {\n const prev = positions[idx]!;\n positions[idx] = {\n ...prev,\n quantity: prev.quantity + fill.quantity,\n basis: prev.basis + cost,\n };\n }\n } else if (idx >= 0) {\n const prev = positions[idx]!;\n cash += fill.quantity * fill.price - fill.fees;\n const remaining = prev.quantity - fill.quantity;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n const basisPerShare = prev.basis / prev.quantity;\n positions[idx] = {\n ...prev,\n quantity: remaining,\n basis: basisPerShare * remaining,\n };\n }\n }\n // No-op when reducing a non-existent long position. This matches the\n // contract of `applyOrders` (same case in the structural projection\n // also no-ops) and avoids destabilising a backtest on a stray reduce.\n // The defence in depth here pairs with the `targetShares` clamp in\n // `reconcile`, which is the primary path that previously produced\n // such reduces. See #37.\n break;\n }\n }\n }\n\n return { cash, positions, t };\n}\n\n/**\n * Projects a portfolio forward through a set of pending (unfilled) orders,\n * returning a structurally updated snapshot. Used by strategy build helpers\n * to read the expected post-step state before fills arrive.\n *\n * **v0.4 contract — structural projection only.** Quantities are updated\n * exactly as the orders specify, but:\n * - `cash` is left unchanged (no price is available at projection time).\n * - Newly opened positions have `basis: 0` and `entry.price: 0` as\n * provisional values. A price-aware projection is planned for a later phase.\n *\n * Use {@link applyFills} (not this function) to settle the portfolio after\n * confirmed execution.\n *\n * @param portfolio - The current portfolio state to project from.\n * @param orders - The pending orders to apply structurally. Order must have\n * a valid `id` and `kind`; price fields are ignored.\n * @returns A new {@link Portfolio} with positions reflecting the orders.\n * `cash` and `t` are copied unchanged from `portfolio`.\n *\n * @example\n * ```ts\n * import { applyOrders } from '@livefolio/sdk';\n * import type { Portfolio, Order } from '@livefolio/sdk';\n *\n * const portfolio: Portfolio = { cash: 10_000, positions: [], t: new Date('2024-01-01') };\n *\n * const order: Order = {\n * id: 'ord_1', kind: 'open',\n * asset: { kind: 'equity', id: 'AAPL', symbol: 'AAPL' },\n * side: 'long', quantity: 10,\n * };\n *\n * const projected = applyOrders(portfolio, [order]);\n * // projected.positions.length === 1, projected.cash === 10_000 (unchanged)\n * ```\n */\nexport function applyOrders(portfolio: Portfolio, orders: ReadonlyArray<Order>): Portfolio {\n let positions: Position[] = [...portfolio.positions];\n\n for (const order of orders) {\n switch (order.kind) {\n case 'open': {\n positions.push({\n id: newPositionId(),\n asset: order.asset,\n side: order.side,\n quantity: order.quantity,\n entry: { date: portfolio.t, price: 0 },\n basis: 0,\n });\n break;\n }\n case 'close': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) break;\n const pos = positions[idx]!;\n const remove = order.quantity ?? pos.quantity;\n const remaining = pos.quantity - remove;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n positions[idx] = { ...pos, quantity: remaining };\n }\n break;\n }\n case 'adjust': {\n const idx = positions.findIndex((p) => p.id === order.positionId);\n if (idx < 0) break;\n const pos = positions[idx]!;\n positions[idx] = { ...pos, quantity: order.changes.quantity ?? pos.quantity };\n break;\n }\n case 'rebalance': {\n const idx = positions.findIndex((p) => p.asset.id === order.asset.id && p.side === 'long');\n if (order.delta > 0) {\n if (idx < 0) {\n positions.push({\n id: newPositionId(),\n asset: order.asset,\n side: 'long',\n quantity: order.delta,\n entry: { date: portfolio.t, price: 0 },\n basis: 0,\n });\n } else {\n const prev = positions[idx]!;\n positions[idx] = { ...prev, quantity: prev.quantity + order.delta };\n }\n } else if (idx >= 0) {\n const prev = positions[idx]!;\n const remaining = prev.quantity + order.delta;\n if (remaining <= 0) {\n positions = positions.filter((_, i) => i !== idx);\n } else {\n positions[idx] = { ...prev, quantity: remaining };\n }\n }\n break;\n }\n }\n }\n\n return { ...portfolio, positions };\n}\n","import type { Strategy, Features } from './types';\nimport type { Portfolio } from '../portfolio/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { Executor } from '../interfaces/executor';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { FeatureCache } from '../interfaces/feature-cache';\nimport type { AssetId, Bar, DateRange, Frequency } from '../interfaces/types';\nimport type { Order, Fill } from '../orders/types';\nimport type { FeatureRuntime } from '../features/runtime';\nimport { applyFills } from '../portfolio/apply';\n\n/**\n * Narrows the dual return type of `Strategy.build` to the stateful object form.\n *\n * `Array.isArray` does not narrow `ReadonlyArray<T>` out of a union in TypeScript 5.x\n * when the other arm is an object type, so we use an explicit type predicate instead.\n * The helper is defined at module scope so `runLive` (Task 8) can reuse it.\n */\nexport function isStateResult<S>(\n r: ReadonlyArray<Order> | { orders: ReadonlyArray<Order>; state: S },\n): r is { orders: ReadonlyArray<Order>; state: S } {\n return !Array.isArray(r);\n}\n\n/**\n * All inputs required to run a historical backtest.\n *\n * Callers must provide a concrete `Strategy`, a `DateRange`, and the four\n * pluggable runtime layers (`dataFeed`, `executor`, `calendar`, `featureCache`).\n * The reference implementations (`MemoryFeatureCache`, `BacktestExecutor`,\n * `NYSEExchangeCalendar`) satisfy all four without network dependencies.\n */\nexport type RunBacktestOptions<F extends Features = Features, S = unknown> = {\n /** The strategy under test. Must implement `universe`, `features`, and `build`. */\n strategy: Strategy<F, S>;\n /**\n * Inclusive date range over which to iterate. The calendar resolves this\n * range into the actual sequence of trading sessions.\n */\n range: DateRange;\n /**\n * Starting portfolio state. Cash and positions are carried forward through\n * the simulation as orders are filled. This value is never mutated.\n */\n initialPortfolio: Portfolio;\n /**\n * Source of OHLCV bar data and optionally fundamentals / corporate events.\n * `FeatureRuntime` uses this to hydrate price series before computing indicators.\n */\n dataFeed: DataFeed;\n /**\n * Order router responsible for converting `Order` objects into `Fill` records.\n * Use `BacktestExecutor` for historical simulations or swap in a live\n * broker implementation for paper/live trading.\n */\n executor: Executor;\n /**\n * Trading-day calendar. Used to enumerate `sessions` within `range` and to\n * determine rebalance day boundaries via `next`.\n */\n calendar: Calendar;\n /**\n * Optional persistent indicator cache. When omitted, each `runBacktest` call\n * recomputes all indicators from scratch. Provide `MemoryFeatureCache` (or a\n * cross-process cache) to memoize results across multiple runs.\n */\n featureCache?: FeatureCache;\n /**\n * Bar frequency forwarded to `DataFeed.bars`. Defaults to `'1d'` when omitted.\n * Must match the granularity expected by the strategy's indicator specs.\n */\n freq?: Frequency;\n /**\n * Optional `FeatureRuntime` instance. When provided, its accumulated bar buffer\n * is exported on `BacktestResult.bars` for use by `runLive` (lets the streaming\n * runtime seed its buffer from the historical bars without refetching).\n */\n featureRuntime?: FeatureRuntime;\n};\n\n/**\n * A point-in-time snapshot of the simulation at the end of a single trading session.\n *\n * Each entry in `BacktestResult.snapshots` corresponds to one call of the strategy\n * loop: `universe → features → build → executor.submit → applyFills`.\n */\nexport type BacktestSnapshot = {\n /** The session date for this snapshot (midnight UTC on the trading day). */\n t: Date;\n /** Portfolio state *after* fills have been applied for this session. */\n portfolio: Portfolio;\n /** Orders emitted by `strategy.build` during this session. */\n orders: ReadonlyArray<Order>;\n /** Fills returned by the executor for the orders above. */\n fills: ReadonlyArray<Fill>;\n};\n\n/**\n * The return value of `runBacktest`, containing the full simulation history\n * and the terminal portfolio state.\n */\nexport type BacktestResult<S = unknown> = {\n /**\n * Ordered list of snapshots, one per trading session in `range`. Empty when\n * the calendar has no sessions in the requested range.\n */\n snapshots: ReadonlyArray<BacktestSnapshot>;\n /**\n * Portfolio after the last session's fills have been applied. Equivalent to\n * `snapshots[snapshots.length - 1].portfolio` when there is at least one session,\n * or `initialPortfolio` when the range is empty.\n */\n finalPortfolio: Portfolio;\n /**\n * Final value of the strategy's auxiliary state after the last `build()` call.\n * `undefined` when the strategy is state-less (no `initialState()` defined).\n * Used by `runLive` to seed the live runtime so the first live tick continues\n * from the exact state the historical run ended on.\n */\n finalState: S | undefined;\n /**\n * Per-asset bar buffer accumulated by the `FeatureRuntime` during this run.\n * Empty `Map` when no `featureRuntime` was provided in `RunBacktestOptions`.\n * Used by `runLive` to seed its streaming `FeatureRuntime` so indicators with\n * warmup periods (SMA(200), etc.) work on the first live tick.\n */\n bars: ReadonlyMap<AssetId, ReadonlyArray<Bar>>;\n};\n\n/**\n * Drives a `Strategy` over a historical date range and returns a full audit trail\n * of orders, fills, and portfolio states.\n *\n * The simulation loop:\n * 1. Enumerate trading sessions via `opts.calendar.sessions(opts.range)`.\n * 2. Call `strategy.initialState?.()` once to seed the carry-over state.\n * 3. For each session `t`, call `strategy.universe(t, portfolio)`.\n * 4. Await `strategy.features(universe, portfolio, t)`.\n * 5. Call `strategy.build(features, portfolio, state, t)` to obtain orders and\n * the next state value. Both legacy `Order[]` returns and new `{ orders, state }`\n * returns are normalised — the legacy form leaves state unchanged.\n * 6. Await `opts.executor.submit(orders, t, portfolio)` to obtain fills.\n * 7. Apply fills to the portfolio with `applyFills`.\n * 8. Append a `BacktestSnapshot` and advance to the next session.\n *\n * The portfolio is never mutated in place; each session receives the immutable\n * result of the previous session's `applyFills`.\n *\n * @param opts - Backtest configuration. See {@link RunBacktestOptions}.\n * @returns A promise that resolves to a {@link BacktestResult} containing one\n * snapshot per trading session, the final portfolio state, and the final\n * strategy state (`finalState`). Returns\n * `{ snapshots: [], finalPortfolio: opts.initialPortfolio, finalState: undefined }`\n * when the calendar has no sessions in the requested range.\n *\n * @example\n * ```ts\n * import {\n * runBacktest,\n * fromSpec,\n * MemoryFeatureCache,\n * BacktestExecutor,\n * NYSEExchangeCalendar,\n * FeatureRuntime,\n * } from '@livefolio/sdk';\n *\n * const calendar = new NYSEExchangeCalendar();\n * const range = { from: new Date('2023-01-01'), to: new Date('2023-12-31') };\n * const featureCache = new MemoryFeatureCache();\n * const runtime = new FeatureRuntime({ dataFeed, featureCache, range, freq: '1d' });\n *\n * const strategy = fromSpec(myTacticalSpec, { runtime, calendar });\n *\n * const result = await runBacktest({\n * strategy,\n * range,\n * initialPortfolio: { cash: 100_000, positions: [] },\n * dataFeed,\n * executor: new BacktestExecutor({ dataFeed }),\n * calendar,\n * featureCache,\n * freq: '1d',\n * });\n *\n * console.log(result.finalPortfolio.cash);\n * console.log(result.snapshots.length); // one entry per NYSE trading day in 2023\n * ```\n */\nexport async function runBacktest<F extends Features = Features, S = unknown>(\n opts: RunBacktestOptions<F, S>,\n): Promise<BacktestResult<S>> {\n const initialStateValue: S | undefined = opts.strategy.initialState?.();\n const sessions = opts.calendar.sessions(opts.range);\n if (sessions.length === 0) {\n return {\n snapshots: [],\n finalPortfolio: opts.initialPortfolio,\n finalState: initialStateValue,\n bars: opts.featureRuntime?.getAllBars() ?? new Map<AssetId, ReadonlyArray<Bar>>(),\n };\n }\n\n let portfolio = opts.initialPortfolio;\n let state: S | undefined = initialStateValue;\n const snapshots: BacktestSnapshot[] = [];\n\n for (const t of sessions) {\n const universe = opts.strategy.universe(t, portfolio);\n const features = await opts.strategy.features(universe, portfolio, t);\n const buildResult = opts.strategy.build(features, portfolio, state as S, t);\n\n let orders: ReadonlyArray<Order>;\n if (isStateResult(buildResult)) {\n orders = buildResult.orders;\n state = buildResult.state;\n } else {\n // Legacy state-less return shape — state unchanged.\n orders = buildResult;\n }\n\n const fills = await opts.executor.submit(orders, t, portfolio);\n portfolio = applyFills(portfolio, fills, orders);\n snapshots.push({ t, portfolio, orders, fills });\n }\n\n const bars = opts.featureRuntime?.getAllBars() ?? new Map<AssetId, ReadonlyArray<Bar>>();\n return { snapshots, finalPortfolio: portfolio, finalState: state, bars };\n}\n","import type { Bar, Series } from '../interfaces/types';\n\n/**\n * The OHLCV field of a `Bar` that should be used when converting a bar array\n * into a scalar time series. Defaults to `'close'` throughout the feature\n * pipeline unless overridden via `FeatureRuntimeOptions.field`.\n *\n * Variants: `'open'` | `'high'` | `'low'` | `'close'` | `'volume'`.\n */\nexport type BarField = 'open' | 'high' | 'low' | 'close' | 'volume';\n\n/**\n * Drains an `AsyncIterable<Bar>` into a plain `Bar[]` array.\n *\n * Used internally by `FeatureRuntime` to materialise the stream returned by\n * `DataFeed.bars` before passing it to indicator functions that expect a\n * fully-in-memory array. The resulting array is in the same order as the\n * iterable yields (typically chronological).\n *\n * @param it - Any `AsyncIterable<Bar>`, such as the return value of `DataFeed.bars`.\n * @returns A promise that resolves to a `Bar[]` containing every bar yielded\n * by the iterable, in iteration order.\n *\n * @example\n * ```ts\n * import { collectBars } from '@livefolio/sdk';\n *\n * const bars = await collectBars(dataFeed.bars(asset, range, '1d'));\n * // bars is now a Bar[] — safe to index, slice, and pass to barsToSeries\n * ```\n */\nexport async function collectBars(it: AsyncIterable<Bar>): Promise<Bar[]> {\n const out: Bar[] = [];\n for await (const b of it) out.push(b);\n return out;\n}\n\n/**\n * Converts an array of OHLCV bars into a `Series` by extracting a single\n * numeric field from each bar.\n *\n * The resulting `Series` is a readonly array of `{ t: Date; v: number }` points\n * in the same order as `bars`. The timestamp `t` is taken directly from `bar.t`,\n * and `v` is the value of `field` for that bar.\n *\n * @param bars - Source OHLCV bars in chronological order.\n * @param field - Which OHLCV field to extract. Defaults to `'close'`.\n * @returns A `Series` of the same length as `bars`. Returns an empty array when\n * `bars` is empty.\n *\n * @example\n * ```ts\n * import { collectBars, barsToSeries } from '@livefolio/sdk';\n *\n * const bars = await collectBars(dataFeed.bars(asset, range, '1d'));\n * const closeSeries = barsToSeries(bars); // default: 'close'\n * const volumeSeries = barsToSeries(bars, 'volume');\n * ```\n */\nexport function barsToSeries(bars: ReadonlyArray<Bar>, field: BarField = 'close'): Series {\n return bars.map((b) => ({ t: b.t, v: b[field] }));\n}\n\n/**\n * Looks up the value at or immediately before timestamp `t` in a sorted `Series`.\n *\n * Uses binary search to find the largest index `i` where `series[i].t <= t`.\n * This is the standard \"as-of\" lookup used throughout the strategy loop to read\n * the most recent indicator value available on a given session date without\n * peeking into the future.\n *\n * @param series - A `Series` sorted in ascending timestamp order. Behaviour is\n * undefined for unsorted input.\n * @param t - The target date. May fall between two data points or exactly on one.\n * @returns The `v` value of the last data point at or before `t`, or `undefined`\n * if the series is empty or every point comes after `t`.\n *\n * @example\n * ```ts\n * import { seriesAt } from '@livefolio/sdk';\n *\n * const series = [\n * { t: new Date('2023-01-02'), v: 380.0 },\n * { t: new Date('2023-01-03'), v: 385.0 },\n * { t: new Date('2023-01-04'), v: 390.0 },\n * ];\n *\n * seriesAt(series, new Date('2023-01-03')); // => 385.0 (exact match)\n * seriesAt(series, new Date('2023-01-03T12:00:00Z')); // => 385.0 (between points)\n * seriesAt(series, new Date('2023-01-01')); // => undefined (before all points)\n * ```\n */\nexport function seriesAt(series: Series, t: Date): number | undefined {\n if (series.length === 0) return undefined;\n const target = t.getTime();\n // binary search for largest index where series[i].t <= target\n let lo = 0;\n let hi = series.length - 1;\n let ans = -1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (series[mid]!.t.getTime() <= target) {\n ans = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return ans < 0 ? undefined : series[ans]!.v;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes a Simple Moving Average (SMA) over a price series.\n *\n * Math definition:\n * ```\n * SMA[i] = (series[i] + series[i-1] + ... + series[i-period+1]) / period\n * ```\n *\n * Warmup: the first output point corresponds to index `period - 1` in the input.\n * Inputs `series[0]` through `series[period - 2]` have no SMA value and are\n * excluded from the output entirely (no `undefined` placeholders; the output\n * array is shorter than the input).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period` — returns `[]` (not enough data for even one window).\n * - `series.length === period` — returns a single-point `Series`.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Window size in bars. Must be a positive integer.\n * @returns A `Series` of length `max(0, series.length - period + 1)`. Each point's\n * timestamp `t` is taken from the last bar in its window (`series[i + period - 1]`).\n *\n * @example\n * ```ts\n * import { sma } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 110 },\n * { t: new Date('2023-01-04'), v: 120 },\n * { t: new Date('2023-01-05'), v: 130 },\n * ];\n *\n * const result = sma(prices, 3);\n * // result.length === 2\n * // result[0] => { t: new Date('2023-01-04'), v: 110 } // (100+110+120)/3\n * // result[1] => { t: new Date('2023-01-05'), v: 120 } // (110+120+130)/3\n * ```\n */\nexport function sma(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`sma: period must be positive, got ${period}`);\n if (series.length < period) return [];\n const out: { t: Date; v: number }[] = [];\n let sum = 0;\n for (let i = 0; i < period; i++) sum += series[i]!.v;\n out.push({ t: series[period - 1]!.t, v: sum / period });\n for (let i = period; i < series.length; i++) {\n sum += series[i]!.v - series[i - period]!.v;\n out.push({ t: series[i]!.t, v: sum / period });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes an Exponential Moving Average (EMA) over a price series.\n *\n * Math definition:\n * ```\n * k = 2 / (period + 1) // smoothing factor\n * EMA[0] = SMA(series[0..period-1]) // seeded from simple average\n * EMA[i] = series[i] * k + EMA[i-1] * (1 - k)\n * ```\n *\n * Warmup: the first `period - 1` input bars are consumed to seed the SMA\n * initial value. The first EMA output point corresponds to input index\n * `period - 1`; subsequent points are computed from the recursive formula.\n * The output array is shorter than the input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period` — returns `[]` (not enough data to seed the EMA).\n * - `series.length === period` — returns a single-point `Series` equal to the\n * simple average of all input values.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Lookback window in bars. Must be a positive integer. Controls\n * the smoothing factor `k = 2 / (period + 1)`: smaller periods react faster\n * to recent prices.\n * @returns A `Series` of length `max(0, series.length - period + 1)`. Each\n * point's timestamp `t` is taken from the corresponding input bar.\n *\n * @example\n * ```ts\n * import { ema } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 110 },\n * { t: new Date('2023-01-04'), v: 120 },\n * { t: new Date('2023-01-05'), v: 130 },\n * ];\n *\n * const result = ema(prices, 3);\n * // result.length === 2\n * // result[0] => { t: new Date('2023-01-04'), v: 110 } // SMA seed: (100+110+120)/3\n * // result[1] => { t: new Date('2023-01-05'), v: ~116.7 } // EMA: 130*0.5 + 110*0.5\n * ```\n */\nexport function ema(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`ema: period must be positive, got ${period}`);\n if (series.length < period) return [];\n const k = 2 / (period + 1);\n const out: { t: Date; v: number }[] = [];\n let sum = 0;\n for (let i = 0; i < period; i++) sum += series[i]!.v;\n let prev = sum / period;\n out.push({ t: series[period - 1]!.t, v: prev });\n for (let i = period; i < series.length; i++) {\n prev = series[i]!.v * k + prev * (1 - k);\n out.push({ t: series[i]!.t, v: prev });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes the Relative Strength Index (RSI) using Wilder's smoothing method.\n *\n * Math definition:\n * ```\n * changes[i] = series[i] - series[i-1]\n *\n * // Seed from simple averages of the first `period` changes:\n * avgGain[0] = mean(max(changes[0..period-1], 0))\n * avgLoss[0] = mean(max(-changes[0..period-1], 0))\n *\n * // Wilder's smoothing for subsequent periods:\n * avgGain[i] = (avgGain[i-1] * (period-1) + gain[i]) / period\n * avgLoss[i] = (avgLoss[i-1] * (period-1) + loss[i]) / period\n *\n * RS[i] = avgGain[i] / avgLoss[i]\n * RSI[i] = 100 - 100 / (1 + RS[i])\n * ```\n *\n * Special case: when `avgLoss === 0`, RSI is clamped to 100 (infinite RS means\n * no losing periods in the window).\n *\n * Warmup: requires `period + 1` input bars to produce the first RSI value\n * (one extra bar for the initial change calculation). The first output point\n * corresponds to input index `period`. The output array is shorter than the\n * input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period + 1` — returns `[]`.\n * - Flat price series (all changes = 0) — returns RSI values of 100 because\n * `avgLoss` stays 0.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Lookback window in bars for Wilder's smoothing. Must be a\n * positive integer; 14 is the conventional default.\n * @returns A `Series` of length `max(0, series.length - period)`. Each point's\n * timestamp `t` is taken from the corresponding input bar. Values are in\n * the range `[0, 100]`.\n *\n * @example\n * ```ts\n * import { rsi } from '@livefolio/sdk';\n *\n * // Minimal example: 5 bars, period 3 → 2 RSI values\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 102 },\n * { t: new Date('2023-01-04'), v: 101 },\n * { t: new Date('2023-01-05'), v: 105 },\n * { t: new Date('2023-01-06'), v: 104 },\n * ];\n *\n * const result = rsi(prices, 3);\n * // result.length === 2\n * // result[0].t => new Date('2023-01-05')\n * // result[1].t => new Date('2023-01-06')\n * // values are in [0, 100]\n * ```\n */\nexport function rsi(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`rsi: period must be positive, got ${period}`);\n if (series.length < period + 1) return [];\n const changes: number[] = [];\n for (let i = 1; i < series.length; i++) {\n changes.push(series[i]!.v - series[i - 1]!.v);\n }\n let avgGain = 0;\n let avgLoss = 0;\n for (let i = 0; i < period; i++) {\n if (changes[i]! > 0) avgGain += changes[i]!;\n else avgLoss += Math.abs(changes[i]!);\n }\n avgGain /= period;\n avgLoss /= period;\n const out: { t: Date; v: number }[] = [];\n const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n out.push({\n t: series[period]!.t,\n v: avgLoss === 0 ? 100 : 100 - 100 / (1 + rs),\n });\n for (let i = period; i < changes.length; i++) {\n const gain = changes[i]! > 0 ? changes[i]! : 0;\n const loss = changes[i]! < 0 ? Math.abs(changes[i]!) : 0;\n avgGain = (avgGain * (period - 1) + gain) / period;\n avgLoss = (avgLoss * (period - 1) + loss) / period;\n const smoothRs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n out.push({\n t: series[i + 1]!.t,\n v: avgLoss === 0 ? 100 : 100 - 100 / (1 + smoothRs),\n });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Controls whether `returnSeries` computes percentage or absolute returns.\n *\n * - `'pct'` (default) — percentage return: `(curr - prev) / prev`. The result is\n * a dimensionless ratio (e.g. `0.05` means +5 %).\n * - `'abs'` — absolute price difference: `curr - prev`. The result is in the same\n * units as the input price series.\n */\nexport type ReturnMode = 'pct' | 'abs';\n\n/**\n * Computes a rolling period return over a price series.\n *\n * Math definition:\n * ```\n * // Percentage (default):\n * return[i] = (series[i] - series[i - period]) / series[i - period]\n *\n * // Absolute:\n * return[i] = series[i] - series[i - period]\n * ```\n *\n * Warmup: the first `period` bars are consumed as the lookback window for the\n * first output. The first output point corresponds to input index `period`.\n * The output array is shorter than the input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length <= period` — returns `[]` (needs strictly more than `period` bars).\n * - `mode === 'pct'` with `prev === 0` — produces `Infinity` or `NaN`; callers\n * should filter or guard against zero-price series.\n *\n * @param series - Input price series sorted in ascending timestamp order.\n * @param period - Lookback distance in bars. A value of `1` gives a single-bar\n * (daily) return; larger values give multi-bar returns.\n * @param mode - Whether to return a percentage ratio or an absolute difference.\n * Defaults to `'pct'`.\n * @returns A `Series` of length `max(0, series.length - period)`. Each point's\n * timestamp `t` is taken from `series[i]` (the later of the two bars).\n *\n * @example\n * ```ts\n * import { returnSeries } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 105 },\n * { t: new Date('2023-01-04'), v: 110 },\n * ];\n *\n * const pct = returnSeries(prices, 1);\n * // pct[0] => { t: new Date('2023-01-03'), v: 0.05 } // (105-100)/100\n * // pct[1] => { t: new Date('2023-01-04'), v: ~0.048 } // (110-105)/105\n *\n * const abs = returnSeries(prices, 1, 'abs');\n * // abs[0] => { t: new Date('2023-01-03'), v: 5 } // 105-100\n * // abs[1] => { t: new Date('2023-01-04'), v: 5 } // 110-105\n *\n * // Two-bar return\n * const twoBar = returnSeries(prices, 2);\n * // twoBar[0] => { t: new Date('2023-01-04'), v: 0.1 } // (110-100)/100\n * ```\n */\nexport function returnSeries(series: Series, period: number, mode: ReturnMode = 'pct'): Series {\n if (period <= 0) throw new Error(`returnSeries: period must be positive, got ${period}`);\n if (series.length <= period) return [];\n const out: { t: Date; v: number }[] = [];\n for (let i = period; i < series.length; i++) {\n const curr = series[i]!.v;\n const prev = series[i - period]!.v;\n const v = mode === 'abs' ? curr - prev : (curr - prev) / prev;\n out.push({ t: series[i]!.t, v });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes a rolling historical volatility as the population standard deviation\n * of daily log-like returns over a sliding window.\n *\n * Math definition:\n * ```\n * dailyReturn[i] = series[i] / series[i-1] - 1 // simple period return\n *\n * // For each window of `period` daily returns ending at index i:\n * mean = Σ dailyReturn[i..i-period+1] / period\n * variance = Σ (dailyReturn[j] - mean)² / period // population variance\n * vol[i] = sqrt(variance)\n * ```\n *\n * The result is the per-bar standard deviation expressed as a fraction (e.g.\n * `0.012` ≈ 1.2 % daily volatility). To annualise, multiply by `sqrt(252)` for\n * daily bars.\n *\n * Warmup: requires `period + 1` price bars to produce the first volatility value:\n * one extra bar to compute the first daily return, then `period` returns for the\n * first window. The first output point corresponds to the `period`-th daily-return\n * index. The output array is shorter than the input (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period + 1` — returns `[]`.\n * - Flat price series (all returns = 0) — returns volatility values of `0`.\n *\n * @param series - Input price series sorted in ascending timestamp order. Values\n * must be positive (non-zero); zero prices produce `NaN` daily returns.\n * @param period - Window size in daily-return bars. Must be a positive integer;\n * common values are 20 (≈ 1 month) or 252 (1 year) for daily data.\n * @returns A `Series` of length `max(0, series.length - period)`. Each point's\n * timestamp `t` is taken from the last daily-return bar in its window.\n *\n * @example\n * ```ts\n * import { volatility } from '@livefolio/sdk';\n *\n * // 5 price bars → 4 daily returns → 1 vol point (period=4)\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 101 },\n * { t: new Date('2023-01-04'), v: 99 },\n * { t: new Date('2023-01-05'), v: 102 },\n * { t: new Date('2023-01-06'), v: 100 },\n * ];\n *\n * const vol = volatility(prices, 4);\n * // vol.length === 1\n * // vol[0].t => new Date('2023-01-06')\n * // vol[0].v => population std-dev of the 4 daily returns (≈ 0.012)\n * ```\n */\nexport function volatility(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`volatility: period must be positive, got ${period}`);\n if (series.length < period + 1) return [];\n const dailyReturns: { t: Date; v: number }[] = [];\n for (let i = 1; i < series.length; i++) {\n dailyReturns.push({\n t: series[i]!.t,\n v: series[i]!.v / series[i - 1]!.v - 1,\n });\n }\n if (dailyReturns.length < period) return [];\n const out: { t: Date; v: number }[] = [];\n for (let i = period - 1; i < dailyReturns.length; i++) {\n const window = dailyReturns.slice(i - period + 1, i + 1);\n const mean = window.reduce((s, r) => s + r.v, 0) / period;\n const variance = window.reduce((s, r) => s + (r.v - mean) ** 2, 0) / period;\n out.push({ t: dailyReturns[i]!.t, v: Math.sqrt(variance) });\n }\n return out;\n}\n","import type { Series } from '../../interfaces/types';\n\n/**\n * Computes the rolling drawdown relative to the period high for each bar.\n *\n * Math definition:\n * ```\n * rollingMax[i] = max(series[i-period+1], ..., series[i])\n * drawdown[i] = (series[i] - rollingMax[i]) / rollingMax[i]\n * ```\n *\n * The result is a non-positive fraction (e.g. `-0.15` means the current price\n * is 15 % below the period high). A value of `0` means the current price equals\n * the rolling maximum — i.e. the asset is at a new high within the window.\n *\n * Warmup: the first output point corresponds to input index `period - 1` (the\n * first complete window). The output array is shorter than the input by\n * `period - 1` points (no `undefined` placeholders).\n *\n * Edge cases:\n * - `period <= 0` — throws `Error`.\n * - `series.length < period` — returns `[]`.\n * - All prices in a window are equal — returns `0` (current equals max).\n * - Zero prices in the window — produces `NaN` (division by zero); callers\n * should guard against zero-price series.\n *\n * @param series - Input price series sorted in ascending timestamp order. Values\n * should be positive (non-zero) to avoid `NaN` results.\n * @param period - Rolling window size in bars. Must be a positive integer.\n * A value of `1` always returns `0` (current equals one-bar max).\n * @returns A `Series` of length `max(0, series.length - period + 1)`. Each\n * point's timestamp `t` is taken from `series[i]` (the last bar in its window).\n * Values are in the range `(-∞, 0]` but in practice within `[-1, 0]` for\n * positive price series.\n *\n * @example\n * ```ts\n * import { drawdown } from '@livefolio/sdk';\n *\n * const prices = [\n * { t: new Date('2023-01-02'), v: 100 },\n * { t: new Date('2023-01-03'), v: 105 },\n * { t: new Date('2023-01-04'), v: 95 },\n * { t: new Date('2023-01-05'), v: 98 },\n * ];\n *\n * const dd = drawdown(prices, 3);\n * // dd.length === 2\n * // dd[0].t => new Date('2023-01-04'), dd[0].v => (95-105)/105 ≈ -0.095\n * // dd[1].t => new Date('2023-01-05'), dd[1].v => (98-105)/105 ≈ -0.067\n * ```\n */\nexport function drawdown(series: Series, period: number): Series {\n if (period <= 0) throw new Error(`drawdown: period must be positive, got ${period}`);\n if (series.length < period) return [];\n const out: { t: Date; v: number }[] = [];\n for (let i = period - 1; i < series.length; i++) {\n let max = -Infinity;\n for (let j = i - period + 1; j <= i; j++) {\n if (series[j]!.v > max) max = series[j]!.v;\n }\n out.push({ t: series[i]!.t, v: (series[i]!.v - max) / max });\n }\n return out;\n}\n","import type { Series } from '../interfaces/types';\nimport { sma } from './indicators/sma';\nimport { ema } from './indicators/ema';\nimport { rsi } from './indicators/rsi';\nimport { returnSeries, type ReturnMode } from './indicators/return';\nimport { volatility } from './indicators/volatility';\nimport { drawdown } from './indicators/drawdown';\n\n/**\n * A discriminated union describing every built-in feature kind and its parameters.\n *\n * Each variant has a `kind` field that identifies the indicator together with the\n * parameters that fully determine its output. `FeatureSpec` objects are used as\n * cache keys (via `paramsHash`) and as dispatch tokens (via `getFeatureCompute`).\n *\n * Variants:\n * - `{ kind: 'price' }` — raw price series; no parameters.\n * - `{ kind: 'sma'; period: number }` — simple moving average over `period` bars.\n * - `{ kind: 'ema'; period: number }` — exponential moving average seeded from an SMA.\n * - `{ kind: 'rsi'; period: number }` — Wilder's Relative Strength Index.\n * - `{ kind: 'return'; period: number; mode?: ReturnMode }` — period return; percent or absolute.\n * - `{ kind: 'volatility'; period: number }` — rolling population standard deviation of daily returns.\n * - `{ kind: 'drawdown'; period: number }` — drawdown relative to the rolling maximum.\n */\nexport type FeatureSpec =\n | { kind: 'price' }\n | { kind: 'sma'; period: number }\n | { kind: 'ema'; period: number }\n | { kind: 'rsi'; period: number }\n | { kind: 'return'; period: number; mode?: ReturnMode }\n | { kind: 'volatility'; period: number }\n | { kind: 'drawdown'; period: number };\n\n/**\n * String literal union of all valid feature `kind` values derived from `FeatureSpec`.\n *\n * Useful for typing registry keys and dispatch tables without manually listing all\n * variants: `'price' | 'sma' | 'ema' | 'rsi' | 'return' | 'volatility' | 'drawdown'`.\n */\nexport type FeatureKind = FeatureSpec['kind'];\n\n/**\n * A pure function that computes a feature `Series` from an input price `Series` and the typed\n * `FeatureSpec` describing the indicator's parameters. Implementations must be deterministic and\n * side-effect free — the SDK uses the function as a content-addressed dispatch token via\n * {@link getFeatureCompute} and as the registration target for {@link defineFeature}.\n *\n * @param series - Input price series (typically the asset's close prices).\n * @param spec - Typed indicator spec carrying the parameters relevant to `kind`.\n * @returns The computed feature series, aligned to `series` on `t`.\n */\nexport type ComputeFn = (series: Series, spec: FeatureSpec) => Series;\n\nconst registry = new Map<FeatureKind, ComputeFn>();\n\n/**\n * Registers a compute function for a new or existing feature kind.\n *\n * Throws if `kind` is already registered. Call this once at module initialisation\n * time (top-level) to extend the built-in feature registry with custom indicators.\n * The compute function receives the raw price `Series` and the full typed spec\n * object for that kind; it must return a `Series` of the same or shorter length.\n *\n * @param kind - The `FeatureKind` string that identifies this indicator.\n * @param compute - Pure function that transforms a price series according to `spec`.\n * The `spec` argument is narrowed to `Extract<FeatureSpec, { kind: K }>` so\n * TypeScript enforces that only the correct parameter shape is accessed.\n * @returns `void`. Registration is a side-effectful, one-time operation.\n *\n * @example\n * ```ts\n * import { defineFeature } from '@livefolio/sdk';\n *\n * // Register a custom 'zscore' feature kind\n * defineFeature('sma', (series, spec) => {\n * // Already built-in; this would throw due to duplicate registration.\n * // Shown here for illustration only.\n * return series;\n * });\n * ```\n */\nexport function defineFeature<K extends FeatureKind>(\n kind: K,\n compute: (series: Series, spec: Extract<FeatureSpec, { kind: K }>) => Series,\n): void {\n if (registry.has(kind)) {\n throw new Error(`defineFeature: kind \"${kind}\" is already registered`);\n }\n registry.set(kind, compute as ComputeFn);\n}\n\n/**\n * Retrieves the registered compute function for a given feature kind.\n *\n * Throws `Error` if the kind has not been registered. In normal usage the\n * built-in `defineFeature` calls at the bottom of this module pre-populate the\n * registry, so this function only throws for custom kinds that were never registered.\n *\n * @param kind - The `FeatureKind` string to look up.\n * @returns The `ComputeFn` registered for `kind`.\n *\n * @example\n * ```ts\n * import { getFeatureCompute } from '@livefolio/sdk';\n *\n * const computeSma = getFeatureCompute('sma');\n * const result = computeSma(priceSeries, { kind: 'sma', period: 20 });\n * ```\n */\nexport function getFeatureCompute(kind: FeatureKind): ComputeFn {\n const fn = registry.get(kind);\n if (!fn) throw new Error(`getFeatureCompute: unknown feature kind \"${kind}\"`);\n return fn;\n}\n\n/**\n * Recursive canonicalization: sort object keys, skip undefined values,\n * preserve null + array order, recurse into nested objects.\n */\nfunction canonicalize(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(canonicalize);\n const obj = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const k of Object.keys(obj).sort()) {\n if (obj[k] === undefined) continue;\n sorted[k] = canonicalize(obj[k]);\n }\n return sorted;\n}\n\n/**\n * Returns a deterministic string that depends only on the spec's logical\n * content — key order and undefined optional fields are normalized away.\n *\n * The same logical spec always produces the same hash regardless of how the\n * object was constructed (different key insertion order, explicit `undefined`\n * vs. omitted optional field). This string is used as the `paramsHash` field\n * in `FeatureKey` to ensure cache-hit equivalence for semantically identical\n * specs.\n *\n * Callers depend on this function's contract (same logical content → same\n * result), not on the encoding. Future replacement with SHA-256 is\n * non-breaking.\n *\n * @param spec - The `FeatureSpec` object to hash.\n * @returns A JSON string with sorted keys and no undefined values.\n *\n * @example\n * ```ts\n * import { paramsHash } from '@livefolio/sdk';\n *\n * paramsHash({ kind: 'sma', period: 20 });\n * // => '{\"kind\":\"sma\",\"period\":20}'\n *\n * // Optional field omitted vs explicitly undefined — same result:\n * paramsHash({ kind: 'return', period: 10 });\n * paramsHash({ kind: 'return', period: 10, mode: undefined });\n * // both => '{\"kind\":\"return\",\"period\":10}'\n * ```\n */\nexport function paramsHash(spec: FeatureSpec): string {\n return JSON.stringify(canonicalize(spec));\n}\n\ndefineFeature('price', (series) => series);\ndefineFeature('sma', (series, spec) => sma(series, spec.period));\ndefineFeature('ema', (series, spec) => ema(series, spec.period));\ndefineFeature('rsi', (series, spec) => rsi(series, spec.period));\ndefineFeature('return', (series, spec) => returnSeries(series, spec.period, spec.mode));\ndefineFeature('volatility', (series, spec) => volatility(series, spec.period));\ndefineFeature('drawdown', (series, spec) => drawdown(series, spec.period));\n","import type { Asset, AssetId, Bar, DateRange, Frequency, Series } from '../interfaces/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { FeatureCache, FeatureKey } from '../interfaces/feature-cache';\nimport { collectBars, barsToSeries, type BarField } from './series-utils';\nimport { getFeatureCompute, paramsHash, type FeatureSpec } from './spec';\n\n/** Sentinel range used as the `range` field in cache keys for streaming-mode\n * computations. Epoch-zero dates are never valid historical ranges, so this\n * value cannot collide with any real historical cache entry. */\nconst STREAMING_SENTINEL_RANGE: DateRange = { from: new Date(0), to: new Date(0) };\n\n/** Stub DataFeed used when no `dataFeed` is supplied in streaming mode.\n * Throws immediately if `.bars` is ever called, which surfaces accidental\n * historical-mode code paths at runtime rather than silently returning empty. */\nconst STREAMING_STUB_FEED: DataFeed = {\n bars: () => {\n throw new Error('dataFeed.bars called on streaming-mode FeatureRuntime');\n },\n};\n\n/**\n * Configuration for a `FeatureRuntime` instance.\n *\n * Accepts two shapes — select via the `mode` discriminant:\n *\n * - **`'historical'`** (default, `mode` may be omitted): range-bounded backtest\n * mode. Bars are fetched from `DataFeed` once per asset and cached in memory.\n * - **`'streaming'`**: open-ended live mode. No fixed `range`; bars are pushed\n * in via `appendBar`. Indicator computation reads from the growing in-process\n * buffer instead of calling `DataFeed.bars`.\n *\n * The historical variant is backward-compatible — existing callers that omit\n * `mode` compile and behave identically to before.\n */\nexport type FeatureRuntimeOptions =\n | {\n /** Selects historical (range-bounded) mode. May be omitted; defaults to\n * `'historical'`. */\n mode?: 'historical';\n /** The market-data source used to fetch OHLCV bars. */\n dataFeed: DataFeed;\n /**\n * Persistent indicator cache. Use `MemoryFeatureCache` for a single backtest\n * run, or supply a cross-process cache implementation to share results across\n * multiple runs or processes.\n */\n featureCache: FeatureCache;\n /**\n * The date range over which bars are fetched. This should span at least the\n * backtest range plus any indicator warmup period (e.g. `period - 1` extra\n * bars for SMA/EMA). `FeatureRuntime` does not automatically extend the range\n * for warmup — the caller is responsible for providing enough history.\n */\n range: DateRange;\n /**\n * Bar frequency forwarded to `DataFeed.bars` and embedded in cache keys.\n * Must match the granularity expected by the indicators (e.g. `'1d'` for\n * daily SMA/EMA).\n */\n freq: Frequency;\n /**\n * Which OHLCV field to use as the scalar price series. Defaults to `'close'`.\n * All indicators within a single `FeatureRuntime` instance share the same field.\n */\n field?: BarField;\n }\n | {\n /** Selects streaming (open-ended live) mode. Required. */\n mode: 'streaming';\n /** Optional — not called in streaming mode. Omit in streaming-only usages.\n * If provided it is stored but never invoked; supply only when sharing an\n * options object that also carries a `DataFeed` for other purposes. */\n dataFeed?: DataFeed;\n /**\n * Persistent indicator cache. In streaming mode the cache is bypassed\n * entirely — every `compute` call recomputes from the in-memory bar buffer.\n * The instance is still required so that a shared cache object can be\n * passed without special-casing at the call site.\n */\n featureCache: FeatureCache;\n /**\n * Bar frequency embedded in cache keys. Must match the granularity of the\n * bars pushed via `appendBar`.\n */\n freq: Frequency;\n /**\n * Which OHLCV field to use as the scalar price series. Defaults to `'close'`.\n */\n field?: BarField;\n /**\n * Optional seed bars per asset (keyed by `AssetId`), used to bootstrap the\n * streaming buffer from a prior historical run's `BacktestResult`.\n * Bars must already be in ascending `t` order per asset.\n */\n initialBars?: ReadonlyMap<AssetId, ReadonlyArray<Bar>>;\n };\n\n/**\n * Orchestrates indicator computation for a single backtest run or a live\n * streaming session.\n *\n * `FeatureRuntime` is the bridge between raw OHLCV data (via `DataFeed`) and the\n * typed indicator functions registered in the feature registry. It handles:\n *\n * - **Bar fetching** (historical mode) — Calls `DataFeed.bars` once per\n * `(asset, range, freq)` tuple and caches the resulting `Series` in memory for\n * the lifetime of the instance. Concurrent calls for the same asset share a\n * single in-flight promise; there is no redundant fetching even if `compute` is\n * called from multiple `Promise.all` branches simultaneously.\n * - **Bar buffering** (streaming mode) — Bars are pushed in via `appendBar`.\n * `compute` reads directly from the in-memory buffer; `DataFeed.bars` is never\n * called.\n * - **Indicator dispatch** — Delegates computation to the function registered via\n * `defineFeature` for the given `FeatureSpec.kind`.\n * - **Persistent caching** (historical mode only) — Checks `FeatureCache` before\n * computing. On a miss, the result is stored in the cache. Subsequent calls with\n * the same `(spec, asset)` combination return instantly from cache without\n * re-fetching bars.\n *\n * Caching semantics:\n * - Historical mode: cache keys incorporate `spec`, `asset.id`, `range`, and `freq`.\n * Results are read from and written to `featureCache`.\n * - Streaming mode: `featureCache` is bypassed entirely — every `compute` call\n * recomputes from the growing in-memory bar buffer. The `seriesCache` (per-asset\n * in-memory base-series cache) is the only cache layer; it is invalidated on each\n * `appendBar` so the next `compute` sees the updated buffer.\n * - Calling `appendBar` invalidates the in-memory series cache for that asset so\n * the next `compute` call rebuilds the series from the updated buffer.\n *\n * @example Historical mode (default)\n * ```ts\n * import {\n * FeatureRuntime,\n * MemoryFeatureCache,\n * seriesAt,\n * } from '@livefolio/sdk';\n *\n * const runtime = new FeatureRuntime({\n * dataFeed,\n * featureCache: new MemoryFeatureCache(),\n * range: { from: new Date('2022-01-01'), to: new Date('2023-12-31') },\n * freq: '1d',\n * });\n *\n * const spy = { kind: 'equity' as const, id: 'US:SPY', symbol: 'SPY' };\n * const smaSeries = await runtime.compute({ kind: 'sma', period: 20 }, spy);\n * const latestSma = seriesAt(smaSeries, new Date('2023-06-15'));\n * // => number | undefined\n * ```\n *\n * @example Streaming mode\n * ```ts\n * const runtime = new FeatureRuntime({\n * featureCache: new MemoryFeatureCache(),\n * mode: 'streaming',\n * freq: '1d',\n * initialBars, // optional seed from BacktestResult\n * });\n *\n * runtime.appendBar(spy, latestBar);\n * const smaSeries = await runtime.compute({ kind: 'sma', period: 20 }, spy);\n * ```\n */\nexport class FeatureRuntime {\n private readonly mode: 'historical' | 'streaming';\n private readonly dataFeed: DataFeed;\n private readonly featureCache: FeatureCache;\n private readonly range: DateRange | null;\n private readonly freq: Frequency;\n private readonly field: BarField;\n /** Per-asset bar buffer used in streaming mode. */\n private readonly streamingBars: Map<AssetId, Bar[]>;\n /** Per-asset bar buffer accumulated in historical mode after bars are fetched from DataFeed. */\n private readonly historicalBars: Map<AssetId, Bar[]>;\n /** Per-asset in-flight Series promise — shared across concurrent `compute` calls\n * for the same asset to avoid redundant bar fetching (historical) or rebuilding\n * (streaming). Invalidated on `appendBar`. */\n private readonly seriesCache: Map<AssetId, Promise<Series>>;\n\n constructor(opts: FeatureRuntimeOptions) {\n this.mode = opts.mode ?? 'historical';\n this.featureCache = opts.featureCache;\n this.freq = opts.freq;\n this.field = opts.field ?? 'close';\n this.seriesCache = new Map();\n this.streamingBars = new Map();\n this.historicalBars = new Map();\n\n if (opts.mode === 'streaming') {\n this.dataFeed = opts.dataFeed ?? STREAMING_STUB_FEED;\n this.range = null;\n if (opts.initialBars) {\n for (const [assetId, bars] of opts.initialBars) {\n this.streamingBars.set(assetId, [...bars]);\n }\n }\n } else {\n this.dataFeed = opts.dataFeed;\n this.range = opts.range;\n }\n }\n\n /**\n * Appends a bar to the streaming buffer for the given asset.\n *\n * Bars must be provided in non-decreasing `t` order per asset. A bar with\n * the same `t` as the most recent buffered bar replaces it in place — this\n * is the mark-to-market wiggle path used by `runLive`, where each incoming\n * tick updates the running open/high/low/close of the in-flight session bar.\n * A bar with `t` strictly greater than the buffered tail starts a fresh\n * in-flight bar (e.g. at session boundaries). A bar with `t` strictly less\n * than the buffered tail throws.\n *\n * Also invalidates the in-memory series cache for the asset so the next\n * `compute` call rebuilds the series from the updated buffer.\n *\n * @throws If called on a historical-mode runtime.\n * @throws If `bar.t` is strictly less than the last buffered bar's `t`.\n */\n appendBar(asset: Asset, bar: Bar): void {\n if (this.mode !== 'streaming') {\n throw new Error('appendBar is only valid in streaming mode');\n }\n const buf = this.streamingBars.get(asset.id) ?? [];\n const last = buf[buf.length - 1];\n if (last !== undefined && bar.t.getTime() < last.t.getTime()) {\n throw new Error(\n `appendBar: bars must be in non-decreasing t order; got ${bar.t.toISOString()} after ${last.t.toISOString()}`,\n );\n }\n if (last !== undefined && bar.t.getTime() === last.t.getTime()) {\n // Same-t mark-to-market wiggle: replace the in-flight bar in place.\n // Used by `runLive` to update the running OHLC of today's bar on each\n // tick so feature computation sees the live close.\n buf[buf.length - 1] = bar;\n } else {\n buf.push(bar);\n }\n this.streamingBars.set(asset.id, buf);\n // Invalidate cached series so next compute rebuilds from updated buffer.\n this.seriesCache.delete(asset.id);\n }\n\n private baseSeries(asset: Asset): Promise<Series> {\n const cached = this.seriesCache.get(asset.id);\n if (cached) return cached;\n\n let p: Promise<Series>;\n if (this.mode === 'streaming') {\n const buf = this.streamingBars.get(asset.id) ?? [];\n p = Promise.resolve(barsToSeries(buf, this.field));\n } else {\n // Historical mode: fetch from DataFeed and cache.\n p = (async () => {\n const bars = await collectBars(this.dataFeed.bars(asset, this.range!, this.freq));\n this.historicalBars.set(asset.id, bars);\n return barsToSeries(bars, this.field);\n })();\n }\n\n this.seriesCache.set(asset.id, p);\n return p;\n }\n\n /**\n * Returns the bar buffer for `asset`, or an empty array if none has been\n * fetched/buffered yet.\n *\n * In historical mode this is populated after the first `compute` call that\n * triggers a bar fetch for the asset. In streaming mode it reflects all bars\n * pushed via `appendBar`.\n */\n getBars(asset: Asset): ReadonlyArray<Bar> {\n return this.streamingBars.get(asset.id) ?? this.historicalBars.get(asset.id) ?? [];\n }\n\n /**\n * Returns the full per-asset bar map (keyed by `AssetId`). Merges historical\n * and streaming buffers (streaming wins on collision, but in practice an\n * instance is in one mode at a time so collisions don't occur).\n *\n * Returns a fresh `Map` — callers cannot mutate the internal state.\n */\n getAllBars(): ReadonlyMap<AssetId, ReadonlyArray<Bar>> {\n const merged = new Map<AssetId, ReadonlyArray<Bar>>();\n for (const [id, bars] of this.historicalBars) merged.set(id, bars);\n for (const [id, bars] of this.streamingBars) merged.set(id, bars);\n return merged;\n }\n\n private cacheKey(spec: FeatureSpec, asset: Asset): FeatureKey {\n return {\n feature: spec.kind,\n paramsHash: paramsHash(spec),\n scope: { kind: 'asset', asset: asset.id },\n range: this.mode === 'streaming' ? STREAMING_SENTINEL_RANGE : this.range!,\n freq: this.freq,\n };\n }\n\n /**\n * Computes (or retrieves from cache) the output `Series` for a given feature\n * spec applied to a specific asset.\n *\n * **Historical mode:** on the first call for a `(spec, asset)` pair:\n * 1. Fetches or reuses the in-memory base `Series` for the asset.\n * 2. Dispatches to the registered compute function for `spec.kind`.\n * 3. Stores the result in `featureCache`.\n * On subsequent calls, returns the cached `Series` directly from `featureCache`.\n *\n * **Streaming mode:** reads from the in-memory bar buffer populated via\n * `appendBar`. `DataFeed.bars` is never called. The persistent `featureCache`\n * is bypassed entirely — results are never read from or written to it.\n * The in-memory `seriesCache` (base series per asset) is the only cache layer\n * and is invalidated on each `appendBar`, so every `compute` after a new bar\n * reflects the updated buffer.\n *\n * @param spec - The feature specification describing which indicator to compute\n * and its parameters (e.g. `{ kind: 'sma', period: 20 }`).\n * @param asset - The asset for which to compute the feature. The asset's `id`\n * is used both for data fetching and cache key construction.\n * @returns A promise that resolves to the computed `Series`. The series length\n * is determined by the indicator's warmup: for example, SMA(20) returns\n * `series.length - 19` data points. Returns an empty array when the\n * base series is shorter than the indicator's warmup period.\n *\n * @example\n * ```ts\n * const spy = { kind: 'equity' as const, id: 'US:SPY', symbol: 'SPY' };\n *\n * const [priceSeries, smaSeries] = await Promise.all([\n * runtime.compute({ kind: 'price' }, spy),\n * runtime.compute({ kind: 'sma', period: 20 }, spy),\n * ]);\n * ```\n */\n async compute(spec: FeatureSpec, asset: Asset): Promise<Series> {\n const key = this.cacheKey(spec, asset);\n if (this.mode !== 'streaming') {\n const cached = await this.featureCache.get(key);\n if (cached) return cached;\n }\n const base = await this.baseSeries(asset);\n const compute = getFeatureCompute(spec.kind);\n const result = compute(base, spec);\n if (this.mode !== 'streaming') {\n await this.featureCache.set(key, result);\n }\n return result;\n }\n}\n","import type { FeatureCache, FeatureKey } from '../interfaces/feature-cache';\nimport type { Series } from '../interfaces/types';\n\nfunction canonicalKey(key: FeatureKey): string {\n const scopePart = key.scope.kind === 'asset' ? `asset:${key.scope.asset}` : `universe:${key.scope.universeHash}`;\n return [\n `feat=${key.feature}`,\n `params=${key.paramsHash}`,\n `scope=${scopePart}`,\n `from=${key.range.from.toISOString()}`,\n `to=${key.range.to.toISOString()}`,\n `freq=${key.freq}`,\n ].join('|');\n}\n\nfunction canonicalPrefix(prefix: Partial<FeatureKey>): string {\n const parts: string[] = [];\n if (prefix.feature !== undefined) parts.push(`feat=${prefix.feature}`);\n if (prefix.paramsHash !== undefined) parts.push(`params=${prefix.paramsHash}`);\n if (prefix.scope !== undefined) {\n const scopePart =\n prefix.scope.kind === 'asset' ? `asset:${prefix.scope.asset}` : `universe:${prefix.scope.universeHash}`;\n parts.push(`scope=${scopePart}`);\n }\n if (prefix.range !== undefined) {\n parts.push(`from=${prefix.range.from.toISOString()}`);\n parts.push(`to=${prefix.range.to.toISOString()}`);\n }\n if (prefix.freq !== undefined) parts.push(`freq=${prefix.freq}`);\n return parts.join('|');\n}\n\n/**\n * In-process, Map-backed implementation of {@link FeatureCache}. Caches\n * computed indicator series in memory for the lifetime of the instance.\n * There is no eviction policy — the cache grows until the instance is\n * garbage-collected.\n *\n * **When to use**: the right choice for single-run backtests or unit tests\n * where the full dataset fits in process memory and cross-run persistence is\n * not required. For long-running hosted services or multi-process setups,\n * substitute a persistent implementation (e.g. Redis-backed) that satisfies\n * the {@link FeatureCache} interface.\n *\n * Cache keys are content-addressed strings composed of `(feature kind, params\n * hash, asset scope, date range, frequency)` — see the internal\n * `canonicalKey` function. The `invalidate` method performs prefix-based\n * deletion using the same key segments.\n *\n * @example\n * ```ts\n * import { MemoryFeatureCache } from '@livefolio/sdk';\n * import { FeatureRuntime } from '@livefolio/sdk/features';\n *\n * const cache = new MemoryFeatureCache();\n * const runtime = new FeatureRuntime({ feed: myDataFeed, cache });\n * ```\n */\nexport class MemoryFeatureCache implements FeatureCache {\n private store = new Map<string, Series>();\n\n async get(key: FeatureKey): Promise<Series | undefined> {\n return this.store.get(canonicalKey(key));\n }\n\n async set(key: FeatureKey, series: Series): Promise<void> {\n this.store.set(canonicalKey(key), series);\n }\n\n async invalidate(prefix: Partial<FeatureKey>): Promise<void> {\n const needles = canonicalPrefix(prefix).split('|').filter(Boolean);\n if (needles.length === 0) return;\n for (const k of [...this.store.keys()]) {\n if (needles.every((n) => k.includes(n))) this.store.delete(k);\n }\n }\n}\n","import type { Asset, AssetId, Bar } from '../interfaces/types';\nimport type { StreamingDataFeed } from '../interfaces/streaming-data-feed';\nimport type { Executor } from '../interfaces/executor';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { Order } from '../orders/types';\nimport type { Portfolio } from '../portfolio/types';\nimport type { Strategy, Features } from './types';\nimport type { BacktestResult, BacktestSnapshot } from './run-backtest';\nimport { isStateResult } from './run-backtest';\nimport { FeatureRuntime } from '../features/runtime';\nimport { MemoryFeatureCache } from '../reference/memory-feature-cache';\nimport { applyFills } from '../portfolio/apply';\n\n/**\n * Unified event stream from {@link runLive}. Discriminated union of two variants:\n *\n * - **`mark`** — emitted per tick. The strategy is run in PREVIEW mode (state\n * is snapshot/restored, no executor call, no portfolio commit). Use this to\n * render the wiggling rightmost chart point and the \"if the session ended now,\n * the strategy would do X\" preview UX.\n * - **`snapshot`** — emitted when a tick crosses a session boundary. The\n * just-closed bar is finalized, `strategy.build` runs for real, orders are\n * submitted to the executor, fills are applied, and state advances. Same\n * shape as {@link BacktestSnapshot} from {@link runBacktest} (plus the\n * `type: 'snapshot'` discriminant), so consumers can append snapshot events\n * to the same chart array used by historical results.\n */\nexport type LiveEvent<F extends Features = Features, _S = unknown> =\n | {\n type: 'mark';\n /** Wall-clock arrival time of this tick. */\n t: Date;\n /** Portfolio at the start of the current session — unchanged by the preview. */\n portfolio: Portfolio;\n /**\n * Per-asset accumulating close so far in the current session. Only assets\n * that have received at least one tick this session appear in the map.\n */\n prices: ReadonlyMap<AssetId, number>;\n /** Features recomputed for the in-progress session. */\n features: F;\n /**\n * Orders the strategy would emit if the session closed at the current\n * tick price. Computed from a state SNAPSHOT — the returned `state` value\n * is discarded, so no committed state is mutated.\n */\n previewOrders: ReadonlyArray<Order>;\n /**\n * Best-effort placeholder — returns the unchanged portfolio. A future\n * `simulateFills(orders, prices)` helper will compute the hypothetical\n * post-rebalance NAV that would result from applying `previewOrders` at\n * `prices`. Until then, consumers compute NAV themselves from\n * `portfolio` + `prices`.\n */\n previewPortfolio: Portfolio;\n }\n | (BacktestSnapshot & { type: 'snapshot' });\n\n/** Required inputs to {@link runLive}. */\nexport type RunLiveOptions<F extends Features = Features, S = unknown> = {\n /** The strategy to drive. If its `features` method depends on a captured\n * `FeatureRuntime` (e.g. tactical strategies built via `fromSpec`), pass the\n * same runtime instance via {@link RunLiveOptions.streamingRuntime} so the\n * live bar buffer stays in sync with what the strategy reads. */\n strategy: Strategy<F, S>;\n /**\n * Result of a prior {@link runBacktest} call. Provides the seed `portfolio`,\n * `state`, and `bars` map for the streaming runtime.\n */\n history: BacktestResult<S>;\n /** Source of streaming ticks. */\n dataFeed: StreamingDataFeed;\n /** Order router used at session boundaries to settle the just-closed bar. */\n executor: Executor;\n /** Calendar that resolves a tick's wall-clock time into its session date. */\n calendar: Calendar;\n /**\n * Optional streaming {@link FeatureRuntime}. Provide this to share the\n * runtime with the strategy — tactical strategies built via `fromSpec`\n * capture a runtime in their `features` closure, so passing the same\n * instance here keeps the live bar buffer in sync with what the strategy\n * reads. When omitted, `runLive` constructs its own streaming runtime\n * seeded from `history.bars`.\n */\n streamingRuntime?: FeatureRuntime;\n};\n\n/**\n * Returns a structurally equivalent copy of `state` so previews cannot mutate\n * the committed state value. Uses `structuredClone` (Node ≥20). State must be\n * structured-cloneable — JSON-serializable types plus Date/Map/Set/etc.\n */\nfunction snapshotState<S>(state: S | undefined): S | undefined {\n if (state === undefined) return undefined;\n return structuredClone(state);\n}\n\n/**\n * Returns the trading-day key (midnight UTC) for the session containing\n * instant `t`, as resolved by the supplied {@link Calendar}. Two ticks belong\n * to the same session iff `findSession(t1) === findSession(t2)`.\n *\n * Uses `calendar.next(t)` to find the next trading day strictly after `t`,\n * then `calendar.previous` to back-anchor to the session that contains `t`.\n * For NYSE: a tick at Friday 17:00 ET (after-close) has `next` = Monday and\n * `previous(Monday)` = Friday — the correct session anchor.\n */\nfunction findSession(t: Date, calendar: Calendar): Date {\n const next = calendar.next(t);\n return calendar.previous(next);\n}\n\n/**\n * Drives a {@link Strategy} against a streaming market-data source and yields\n * a unified event stream that consumer charts can append to historical\n * snapshots without code branching.\n *\n * **Lifecycle on each tick:**\n * 1. Resolve the tick's session date via the supplied {@link Calendar} —\n * `calendar.previous(calendar.next(tick.t))`. This correctly handles\n * after-hours ticks (NYSE 17:00 ET stays in the same session) and DST\n * transitions.\n * 2. If the tick crosses a session boundary, finalize the just-closed bar:\n * append it to the streaming `FeatureRuntime`, run `strategy.build` for\n * REAL (committing state), submit orders to the executor, apply fills,\n * and yield a `snapshot` event identical in shape to {@link BacktestSnapshot}.\n * 3. Record the tick into the current session's accumulating bar.\n * 4. Re-run `strategy.features` and `strategy.build` in PREVIEW mode (state\n * is snapshot/restored — committed state is untouched). Yield a `mark`\n * event with the recomputed features and preview orders.\n *\n * **State semantics:** preview-build always operates on a deep clone of the\n * committed `state`. Only the boundary-crossing commit branch advances\n * committed state. This guarantees that 1000 ticks within a single session\n * produce 1000 marks but leave `state` exactly where the prior session-close\n * commit left it.\n *\n * **FeatureRuntime:** if the strategy was built via `fromSpec` it captures its\n * own runtime in the `features` closure. Pass that same instance via\n * {@link RunLiveOptions.streamingRuntime} so `appendBar` calls land on the\n * runtime the strategy actually reads. When omitted, `runLive` constructs its\n * own streaming runtime seeded from `history.bars` — this works for hand-rolled\n * strategies whose `features` method consults the runtime directly, but it\n * leaves a `fromSpec` strategy reading a stale captured runtime.\n *\n * **Bar lineage:** the streaming `FeatureRuntime` (provided or constructed) is\n * seeded from `history.bars`, so indicators with warmup periods (SMA(200),\n * etc.) work on the first live tick.\n *\n * **Universe:** captured once at startup from\n * `strategy.universe(anchorTime, portfolio)`, where `anchorTime` is the last\n * historical snapshot's timestamp (or epoch zero for empty history). Dynamic\n * universes are not yet supported in live mode.\n *\n * **Termination:** the iterable terminates when the underlying\n * `StreamingDataFeed.subscribe` iterable terminates. Real adapters yield\n * forever; tests use bounded iterables to assert specific event sequences.\n *\n * @param opts - Live-runtime configuration. See {@link RunLiveOptions}.\n * @returns An open-ended `AsyncIterable<LiveEvent>`. Consumers `for await` the\n * stream and dispatch on `ev.type`.\n *\n * @example\n * ```ts\n * for await (const ev of runLive({ strategy, history, dataFeed, executor, calendar })) {\n * if (ev.type === 'mark') {\n * chart.updateLastBar({ t: ev.t, prices: ev.prices, previewOrders: ev.previewOrders });\n * } else {\n * chart.appendBar(ev); // BacktestSnapshot-shaped\n * }\n * }\n * ```\n */\nexport async function* runLive<F extends Features = Features, S = unknown>(\n opts: RunLiveOptions<F, S>,\n): AsyncIterable<LiveEvent<F, S>> {\n const { strategy, history, dataFeed, executor, calendar } = opts;\n\n // Streaming FeatureRuntime: prefer a caller-supplied instance (so `fromSpec`\n // strategies that captured a runtime keep reading the same buffer we append\n // to). Otherwise build one seeded from `history.bars` for hand-rolled\n // strategies that consult the runtime directly.\n const runtime =\n opts.streamingRuntime ??\n new FeatureRuntime({\n mode: 'streaming',\n featureCache: new MemoryFeatureCache(),\n freq: '1d',\n initialBars: history.bars,\n });\n\n let portfolio = history.finalPortfolio;\n let state: S | undefined = history.finalState;\n // Universe is captured once at startup using the last historical snapshot's\n // timestamp as an anchor (or epoch zero for empty history). Dynamic\n // universes are not yet supported in live mode.\n const anchorTime = history.snapshots.length > 0 ? history.snapshots[history.snapshots.length - 1]!.t : new Date(0);\n const universe = strategy.universe(anchorTime, portfolio);\n\n // Track the current session being accumulated. When history is non-empty,\n // its last snapshot represents an already-committed session, so the next\n // session to accumulate is `calendar.next(lastSnapshot.t)`. Without this\n // advance, the first live tick whose session exceeds `lastSnapshot.t` would\n // re-fire the boundary and emit a duplicate snapshot for the already-closed\n // session. With empty history, we lazily adopt the first tick's session.\n let currentSession: Date | null =\n history.snapshots.length > 0 ? calendar.next(history.snapshots[history.snapshots.length - 1]!.t) : null;\n let currentBarOpen = new Map<AssetId, number>();\n let currentBarHigh = new Map<AssetId, number>();\n let currentBarLow = new Map<AssetId, number>();\n let currentBarClose = new Map<AssetId, number>();\n\n function recordTick(asset: Asset, tickBar: Bar): void {\n const id = asset.id;\n const price = tickBar.close;\n if (!currentBarOpen.has(id)) currentBarOpen.set(id, price);\n currentBarHigh.set(id, Math.max(currentBarHigh.get(id) ?? -Infinity, price));\n currentBarLow.set(id, Math.min(currentBarLow.get(id) ?? Infinity, price));\n currentBarClose.set(id, price);\n }\n\n function finalizeBars(sessionDate: Date): void {\n for (const asset of universe) {\n const close = currentBarClose.get(asset.id);\n if (close === undefined) continue;\n runtime.appendBar(asset, {\n t: sessionDate,\n open: currentBarOpen.get(asset.id)!,\n high: currentBarHigh.get(asset.id)!,\n low: currentBarLow.get(asset.id)!,\n close,\n volume: 0,\n });\n }\n currentBarOpen = new Map();\n currentBarHigh = new Map();\n currentBarLow = new Map();\n currentBarClose = new Map();\n }\n\n for await (const tick of dataFeed.subscribe(universe)) {\n const tickSession = findSession(tick.bar.t, calendar);\n\n if (currentSession === null) {\n currentSession = tickSession;\n }\n\n // Boundary crossed: finalize the previous session's bar, run REAL build,\n // submit orders, apply fills, yield snapshot, then start the new session.\n if (tickSession.getTime() > currentSession.getTime()) {\n finalizeBars(currentSession);\n const sessionFeatures = await strategy.features(universe, portfolio, currentSession);\n const buildResult = strategy.build(sessionFeatures, portfolio, state as S, currentSession);\n let orders: ReadonlyArray<Order>;\n if (isStateResult(buildResult)) {\n orders = buildResult.orders;\n state = buildResult.state;\n } else {\n orders = buildResult;\n }\n const fills = await executor.submit(orders, currentSession, portfolio);\n portfolio = applyFills(portfolio, fills, orders);\n yield {\n type: 'snapshot',\n t: currentSession,\n portfolio,\n orders,\n fills,\n };\n currentSession = tickSession;\n }\n\n // Record the tick into the current session's accumulating bar.\n recordTick(tick.asset, tick.bar);\n\n // Wiggle: push the in-flight session bar into the streaming runtime so\n // feature computations see the running close. `runtime.appendBar` allows\n // same-t replacement, so subsequent ticks within the session overwrite\n // the in-flight bar in place. Without this step, features would be\n // pinned to yesterday's close — preview decisions would be stable\n // through the session and only refresh at session-close finalization,\n // which contradicts the model that every tick is \"as if the session\n // closed at this price.\"\n for (const asset of universe) {\n const close = currentBarClose.get(asset.id);\n if (close === undefined) continue;\n runtime.appendBar(asset, {\n t: currentSession,\n open: currentBarOpen.get(asset.id)!,\n high: currentBarHigh.get(asset.id)!,\n low: currentBarLow.get(asset.id)!,\n close,\n volume: 0,\n });\n }\n\n // Preview: snapshot state, recompute features, run build with the snapshot,\n // discard the returned state. Committed `state` is never touched here.\n const prices = new Map(currentBarClose);\n const features = await strategy.features(universe, portfolio, tick.bar.t);\n const previewState = snapshotState(state);\n const previewResult = strategy.build(features, portfolio, previewState as S, tick.bar.t);\n const previewOrders: ReadonlyArray<Order> = isStateResult(previewResult) ? previewResult.orders : previewResult;\n\n yield {\n type: 'mark',\n t: tick.bar.t,\n portfolio,\n prices,\n features,\n // TODO: replace with `simulateFills(previewOrders, prices)` when the\n // helper lands so consumers see the hypothetical post-rebalance NAV.\n previewOrders,\n previewPortfolio: portfolio,\n };\n }\n}\n","import type { Executor } from '../interfaces/executor';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { Asset } from '../interfaces/types';\nimport type { Order, Fill } from '../orders/types';\nimport type { Portfolio } from '../portfolio/types';\n\n/**\n * Callback that resolves the next-open price for `asset` as seen from date `t`.\n * {@link BacktestExecutor} calls this once per order to determine the fill price.\n *\n * The function should return the opening price of the first trading session\n * strictly after `t`, along with that session's timestamp. In a typical\n * backtest setup this reads from the same data feed used to compute features.\n *\n * @param asset - The instrument being filled.\n * @param t - The date on which the rebalance order was submitted (the\n * \"signal date\"). The fill should occur on the next open after\n * this date to avoid look-ahead.\n * @returns An object with the fill timestamp `t` and the opening `price`.\n */\nexport type NextOpenFn = (asset: Asset, t: Date) => Promise<{ t: Date; price: number }>;\n\n/**\n * Constructor options for {@link BacktestExecutor}.\n */\nexport type BacktestExecutorOptions = {\n /** Exchange calendar used to route fills to the next open session. */\n calendar: Calendar;\n /**\n * Callback that resolves the next-open price for a given asset and date.\n * See {@link NextOpenFn} for the exact contract.\n */\n nextOpen: NextOpenFn;\n /**\n * One-way slippage in basis points applied to every fill. The fill price is\n * adjusted by `price × (1 + sign × slippageBps / 10 000)` where `sign` is\n * `+1` for buys and `−1` for sells. Defaults to `0`.\n */\n slippageBps?: number;\n /**\n * Flat per-share commission in the portfolio's base currency. Multiplied by\n * the fill quantity and recorded in `Fill.fees`. Defaults to `0`.\n */\n perShareFee?: number;\n};\n\nfunction resolveAsset(order: Order, portfolio: Portfolio): { asset: Asset; sign: 1 | -1; qty: number } {\n switch (order.kind) {\n case 'open':\n return { asset: order.asset, sign: order.side === 'long' ? 1 : -1, qty: order.quantity };\n case 'rebalance':\n return { asset: order.asset, sign: order.delta >= 0 ? 1 : -1, qty: Math.abs(order.delta) };\n case 'close': {\n const p = portfolio.positions.find((x) => x.id === order.positionId);\n if (!p) throw new Error(`BacktestExecutor: close target position ${order.positionId} not found`);\n return { asset: p.asset, sign: p.side === 'long' ? -1 : 1, qty: order.quantity ?? p.quantity };\n }\n case 'adjust': {\n const p = portfolio.positions.find((x) => x.id === order.positionId);\n if (!p) throw new Error(`BacktestExecutor: adjust target position ${order.positionId} not found`);\n const target = order.changes.quantity ?? p.quantity;\n const delta = target - p.quantity;\n return { asset: p.asset, sign: delta >= 0 ? 1 : -1, qty: Math.abs(delta) };\n }\n }\n}\n\n/**\n * Reference {@link Executor} implementation for backtesting. Fills each order\n * at the next-open price returned by the {@link NextOpenFn} callback, with\n * optional slippage and per-share commissions applied.\n *\n * **When to use**: suitable for historical simulations and unit tests where\n * real broker connectivity is not needed. For live or paper trading, substitute\n * a broker-backed `Executor` that satisfies the same interface.\n *\n * **Fill mechanics**: for each order in `orders`, the executor calls\n * `opts.nextOpen(asset, t)` to obtain the fill price and timestamp. The\n * raw price is then adjusted for slippage:\n * ```\n * adjustedPrice = nextOpen.price × (1 + sign × slippageBps / 10 000)\n * ```\n * where `sign` is `+1` for net-buy direction and `−1` for net-sell direction.\n * A flat per-share fee is added to `Fill.fees`. Orders with zero quantity are\n * silently skipped.\n *\n * @example\n * ```ts\n * import { BacktestExecutor } from '@livefolio/sdk';\n * import { getCalendar } from '@livefolio/sdk';\n *\n * const executor = new BacktestExecutor({\n * calendar: getCalendar('NYSE'),\n * nextOpen: async (asset, t) => {\n * // Return the first open bar strictly after t from your data feed.\n * const bar = await feed.nextBar(asset, t);\n * return { t: bar.t, price: bar.open };\n * },\n * slippageBps: 5, // 0.05% one-way\n * perShareFee: 0.005,\n * });\n * ```\n */\nexport class BacktestExecutor implements Executor {\n constructor(private readonly opts: BacktestExecutorOptions) {}\n\n async submit(orders: ReadonlyArray<Order>, t: Date, portfolio: Portfolio): Promise<ReadonlyArray<Fill>> {\n const fills: Fill[] = [];\n const slip = (this.opts.slippageBps ?? 0) / 10_000;\n const feePer = this.opts.perShareFee ?? 0;\n\n for (const order of orders) {\n const { asset, sign, qty } = resolveAsset(order, portfolio);\n if (qty === 0) continue;\n const open = await this.opts.nextOpen(asset, t);\n const adjustedPrice = open.price * (1 + sign * slip);\n fills.push({\n orderRef: order.id,\n t: open.t,\n quantity: qty,\n price: adjustedPrice,\n fees: feePer * qty,\n });\n }\n return fills;\n }\n}\n","import type { Asset, Bar, DateRange, Frequency } from '../interfaces/types';\nimport type { DataFeed, Fundamentals } from '../interfaces/data-feed';\n\n/**\n * Error thrown by {@link RoutingDataFeed} when an asset cannot be routed or\n * when the routed feed does not support the requested optional method.\n *\n * Distinguish the two cases via the message text: \"no feed registered\" vs\n * \"does not implement fundamentals\".\n */\nexport class RoutingDataFeedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoutingDataFeedError';\n }\n}\n\n/** Function form of the routing rule. Returns the feed for `asset`, or `undefined` when no feed handles it. */\nexport type RoutingDataFeedRouteFn = (asset: Asset) => DataFeed | undefined;\n\n/** Map form of the routing rule. Keys are `Asset['kind']` discriminants. */\nexport type RoutingDataFeedRouteMap = Readonly<Partial<Record<Asset['kind'], DataFeed>>>;\n\n/**\n * A {@link DataFeed} that delegates each call to one of several underlying\n * feeds based on the asset. Use this to compose vendors — e.g. Yahoo for\n * equities and FRED for macro series — behind a single `DataFeed` instance\n * accepted by `runBacktest`, `FeatureRuntime`, and `BacktestExecutor`.\n *\n * Routing rules:\n * - **Map form:** `new RoutingDataFeed({ equity: yahoo, macro: fred })`.\n * Keys are `asset.kind` discriminants. The 90% case.\n * - **Function form:** `new RoutingDataFeed((a) => a.kind === 'macro' ? fred : yahoo)`.\n * Use when routing depends on more than `kind` (e.g. allowlists).\n *\n * The router does **not** implement `events()` — the optional method is\n * genuinely absent (`'events' in router === false`). Cross-feed event\n * fan-out is deferred until a real consumer materializes.\n *\n * @example\n * ```ts\n * import { RoutingDataFeed } from '@livefolio/sdk';\n *\n * const feed = new RoutingDataFeed({ equity: yahooFeed, macro: fredFeed });\n *\n * const result = await runBacktest({\n * strategy, range, initialPortfolio,\n * dataFeed: feed,\n * executor,\n * calendar,\n * });\n * ```\n */\nexport class RoutingDataFeed implements DataFeed {\n private readonly route: RoutingDataFeedRouteFn;\n\n constructor(routes: RoutingDataFeedRouteMap | RoutingDataFeedRouteFn) {\n if (typeof routes === 'function') {\n this.route = routes;\n } else {\n this.route = (asset) => routes[asset.kind];\n }\n }\n\n // Async generator (rather than plain delegation) so resolve() runs lazily on\n // the first next() call, surfacing errors via the iterable's normal rejection\n // path instead of throwing synchronously at call time.\n async *bars(asset: Asset, range: DateRange, freq: Frequency): AsyncGenerator<Bar> {\n const feed = this.resolve(asset);\n yield* feed.bars(asset, range, freq);\n }\n\n async fundamentals(asset: Asset, t: Date): Promise<Fundamentals> {\n const feed = this.resolve(asset);\n if (typeof feed.fundamentals !== 'function') {\n throw new RoutingDataFeedError(\n `RoutingDataFeed: routed feed for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\") does not implement fundamentals()`,\n );\n }\n return feed.fundamentals(asset, t);\n }\n\n private resolve(asset: Asset): DataFeed {\n const feed = this.route(asset);\n if (feed === undefined) {\n throw new RoutingDataFeedError(\n `RoutingDataFeed: no feed registered for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\")`,\n );\n }\n return feed;\n }\n}\n","import type { Asset } from '../interfaces/types';\nimport type { StreamingDataFeed, StreamingBar } from '../interfaces/streaming-data-feed';\n\n/**\n * Error thrown by {@link RoutingStreamingDataFeed} when an asset cannot be routed.\n */\nexport class RoutingStreamingDataFeedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoutingStreamingDataFeedError';\n }\n}\n\n/** Function form of the routing rule. Returns the feed for `asset`, or `undefined` when no feed handles it. */\nexport type RoutingStreamingDataFeedRouteFn = (asset: Asset) => StreamingDataFeed | undefined;\n\n/** Map form of the routing rule. Keys are `Asset['kind']` discriminants. */\nexport type RoutingStreamingDataFeedRouteMap = Readonly<Partial<Record<Asset['kind'], StreamingDataFeed>>>;\n\n/**\n * A {@link StreamingDataFeed} that delegates `subscribe()` to one of several\n * underlying feeds based on the asset. Use this to compose vendors — e.g.\n * Polygon for equities and a polling adapter for macro series — behind a\n * single `StreamingDataFeed` instance accepted by `runLive`.\n *\n * Routing rules:\n * - **Map form:** `new RoutingStreamingDataFeed({ equity: polygon, macro: polling })`.\n * Keys are `asset.kind` discriminants. The 90% case.\n * - **Function form:** `new RoutingStreamingDataFeed((a) => a.kind === 'macro' ? polling : polygon)`.\n * Use when routing depends on more than `kind` (e.g. allowlists).\n *\n * Assets are grouped by routed feed (by reference identity) before calling\n * upstream `subscribe()` — so a vendor adapter that opens one socket for\n * `[AAPL, MSFT]` keeps doing that rather than receiving one-asset-at-a-time calls.\n *\n * @example\n * ```ts\n * import { RoutingStreamingDataFeed, pollingStreamFromHistorical } from '@livefolio/sdk';\n *\n * const feed = new RoutingStreamingDataFeed({\n * equity: polygonStreaming,\n * macro: pollingStreamFromHistorical({ feed: fredHistorical, freq: '1d', schedule: { kind: 'session-close', calendar: nyse } }),\n * });\n * ```\n */\nexport class RoutingStreamingDataFeed implements StreamingDataFeed {\n private readonly route: RoutingStreamingDataFeedRouteFn;\n\n constructor(routes: RoutingStreamingDataFeedRouteMap | RoutingStreamingDataFeedRouteFn) {\n if (typeof routes === 'function') {\n this.route = routes;\n } else {\n this.route = (asset) => routes[asset.kind];\n }\n }\n\n subscribe(assets: ReadonlyArray<Asset>): AsyncIterable<StreamingBar> {\n return this.merged(assets);\n }\n\n // Async generator so routing/grouping errors surface on first next() rather\n // than throwing synchronously at subscribe() call time — matches RoutingDataFeed.bars() shape.\n private async *merged(assets: ReadonlyArray<Asset>): AsyncGenerator<StreamingBar> {\n if (assets.length === 0) return;\n\n const groups = new Map<StreamingDataFeed, Asset[]>();\n for (const asset of assets) {\n const feed = this.route(asset);\n if (feed === undefined) {\n throw new RoutingStreamingDataFeedError(\n `RoutingStreamingDataFeed: no feed registered for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\")`,\n );\n }\n const list = groups.get(feed) ?? [];\n list.push(asset);\n groups.set(feed, list);\n }\n\n const iters = [...groups.entries()].map(([feed, group]) => feed.subscribe(group)[Symbol.asyncIterator]());\n yield* mergeIterators(iters);\n }\n}\n\nasync function* mergeIterators(iters: ReadonlyArray<AsyncIterator<StreamingBar>>): AsyncGenerator<StreamingBar> {\n type Slot = {\n iter: AsyncIterator<StreamingBar>;\n promise: Promise<{ idx: number; r: IteratorResult<StreamingBar> }>;\n };\n const live = new Map<number, Slot>();\n\n const arm = (idx: number, iter: AsyncIterator<StreamingBar>): void => {\n live.set(idx, {\n iter,\n promise: iter.next().then((r) => ({ idx, r })),\n });\n };\n\n iters.forEach((iter, idx) => arm(idx, iter));\n\n try {\n while (live.size > 0) {\n const { idx, r } = await Promise.race([...live.values()].map((s) => s.promise));\n if (r.done) {\n live.delete(idx);\n } else {\n yield r.value;\n const slot = live.get(idx);\n if (slot) arm(idx, slot.iter);\n }\n }\n } finally {\n await Promise.allSettled(\n [...live.values()].map((s) => (s.iter.return ? s.iter.return(undefined) : Promise.resolve())),\n );\n }\n}\n","import type { Asset } from '../interfaces/types';\nimport type { Quote, QuoteFeed } from '../interfaces/quote-feed';\n\n/**\n * Error thrown by {@link RoutingQuoteFeed} when an asset cannot be routed.\n */\nexport class RoutingQuoteFeedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoutingQuoteFeedError';\n }\n}\n\n/** Function form of the routing rule. Returns the feed for `asset`, or `undefined` when no feed handles it. */\nexport type RoutingQuoteFeedRouteFn = (asset: Asset) => QuoteFeed | undefined;\n\n/** Map form of the routing rule. Keys are `Asset['kind']` discriminants. */\nexport type RoutingQuoteFeedRouteMap = Readonly<Partial<Record<Asset['kind'], QuoteFeed>>>;\n\n/**\n * A {@link QuoteFeed} that delegates each call to one of several underlying\n * feeds based on the asset. Use this to compose vendors — e.g. Alpaca for\n * equity quotes and a polling adapter for macro series — behind a single\n * `QuoteFeed` instance.\n *\n * Routing rules:\n * - **Map form:** `new RoutingQuoteFeed({ equity: alpaca, macro: fredPolling })`.\n * Keys are `asset.kind` discriminants. The 90% case.\n * - **Function form:** `new RoutingQuoteFeed((a) => a.kind === 'macro' ? fred : alpaca)`.\n * Use when routing depends on more than `kind` (e.g. allowlists).\n *\n * The router always implements `quoteBatch` — even if some inner feeds lack\n * it, the router falls back to per-asset `quote()` calls within that group,\n * preserving request order across the full result.\n *\n * @example\n * ```ts\n * import { RoutingQuoteFeed } from '@livefolio/sdk';\n *\n * const feed = new RoutingQuoteFeed({ equity: alpacaQuotes, macro: fredQuotes });\n * const quotes = await feed.quoteBatch([aaplAsset, dgs10Asset, msftAsset]);\n * // quotes[0] is for AAPL, quotes[1] for DGS10, quotes[2] for MSFT — request order preserved.\n * ```\n */\nexport class RoutingQuoteFeed implements QuoteFeed {\n private readonly route: RoutingQuoteFeedRouteFn;\n\n constructor(routes: RoutingQuoteFeedRouteMap | RoutingQuoteFeedRouteFn) {\n if (typeof routes === 'function') {\n this.route = routes;\n } else {\n this.route = (asset) => routes[asset.kind];\n }\n }\n\n async quote(asset: Asset): Promise<Quote> {\n return this.resolve(asset).quote(asset);\n }\n\n async quoteBatch(assets: ReadonlyArray<Asset>): Promise<ReadonlyArray<Quote>> {\n if (assets.length === 0) return [];\n\n // Group by routed feed, tracking original index so we can re-collect in request order.\n // Resolve eagerly so unroutable assets throw before any vendor call.\n const groups = new Map<QuoteFeed, Array<{ asset: Asset; index: number }>>();\n for (let i = 0; i < assets.length; i++) {\n const asset = assets[i]!;\n const feed = this.resolve(asset);\n const bucket = groups.get(feed) ?? [];\n bucket.push({ asset, index: i });\n groups.set(feed, bucket);\n }\n\n const output = new Array<Quote>(assets.length);\n\n await Promise.all(\n [...groups.entries()].map(async ([feed, bucket]) => {\n const bucketAssets = bucket.map((b) => b.asset);\n const results =\n typeof feed.quoteBatch === 'function'\n ? await feed.quoteBatch(bucketAssets)\n : await Promise.all(bucketAssets.map((a) => feed.quote(a)));\n for (let i = 0; i < bucket.length; i++) {\n output[bucket[i]!.index] = results[i]!;\n }\n }),\n );\n\n return output;\n }\n\n private resolve(asset: Asset): QuoteFeed {\n const feed = this.route(asset);\n if (feed === undefined) {\n throw new RoutingQuoteFeedError(\n `RoutingQuoteFeed: no feed registered for asset.kind=\"${asset.kind}\" (id=\"${asset.id}\")`,\n );\n }\n return feed;\n }\n}\n","import type { Asset, AssetId, Frequency } from '../interfaces/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { StreamingDataFeed, StreamingBar } from '../interfaces/streaming-data-feed';\n\nexport type PollingSchedule = { kind: 'interval'; intervalMs: number } | { kind: 'session-close'; calendar: Calendar };\n\nexport type PollingStreamOptions = {\n /** Historical feed to poll. Each tick of the schedule calls `feed.bars(asset, …)` for each subscribed asset. */\n feed: DataFeed;\n /** Bar frequency to request. Single value — multi-frequency requires composing two polling streams via `RoutingStreamingDataFeed`. */\n freq: Frequency;\n /** When to poll. */\n schedule: PollingSchedule;\n /**\n * Window-start for the first poll per asset. Subsequent polls fetch\n * `(lastSeenT, now]` per asset. Defaults to `new Date(0)` — every bar the\n * feed has on the first poll is yielded. For replay-then-stream, set this\n * to your backtest range's `to` so polling picks up exactly where the\n * backtest left off.\n */\n initialFrom?: Date;\n /** Inject for tests or for accelerated-time simulations. Defaults to `() => new Date()`. */\n now?: () => Date;\n /** Inject for tests or for accelerated-time simulations. Defaults to `setTimeout`-based promise. */\n sleep?: (ms: number) => Promise<void>;\n};\n\nexport function pollingStreamFromHistorical(opts: PollingStreamOptions): StreamingDataFeed {\n const now = opts.now ?? (() => new Date());\n const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((res) => setTimeout(res, ms)));\n const initialFrom = opts.initialFrom ?? new Date(0);\n\n return {\n subscribe(assets: ReadonlyArray<Asset>): AsyncIterable<StreamingBar> {\n return poll(assets);\n },\n };\n\n async function* poll(assets: ReadonlyArray<Asset>): AsyncGenerator<StreamingBar> {\n // Dedup by id, preserve input order.\n const seen = new Set<AssetId>();\n const uniq: Asset[] = [];\n for (const a of assets) {\n if (!seen.has(a.id)) {\n seen.add(a.id);\n uniq.push(a);\n }\n }\n if (uniq.length === 0) return;\n\n const lastSeenT = new Map<AssetId, Date>(uniq.map((a) => [a.id, initialFrom]));\n\n while (true) {\n await waitForNextPoll();\n for (const asset of uniq) {\n const from = lastSeenT.get(asset.id)!;\n const to = now(); // Fresh per asset by design — for cycle-stable to, pass a custom now() that caches.\n for await (const bar of opts.feed.bars(asset, { from, to }, opts.freq)) {\n const last = lastSeenT.get(asset.id)!;\n if (bar.t.getTime() > last.getTime()) {\n yield { asset, bar };\n lastSeenT.set(asset.id, bar.t);\n }\n }\n }\n }\n }\n\n async function waitForNextPoll(): Promise<void> {\n if (opts.schedule.kind === 'interval') {\n await sleep(opts.schedule.intervalMs);\n return;\n }\n // Resolve the next session close via cal.schedule() lookahead rather than\n // the cal.previous(cal.next(now)) idiom: Session exposes .close (not .end),\n // and the lookahead also covers the exotic-calendar fallback below.\n const cal = opts.schedule.calendar;\n const t = now();\n const lookaheadDays = 14;\n const range = {\n from: t,\n to: new Date(t.getTime() + lookaheadDays * 24 * 60 * 60 * 1000),\n };\n const sessions = cal.schedule(range);\n const upcoming = sessions.find((s) => s.close.getTime() > t.getTime());\n if (upcoming === undefined) {\n // No session in the next N days — sleep one day and retry.\n await sleep(24 * 60 * 60 * 1000);\n return;\n }\n const delay = Math.max(0, upcoming.close.getTime() - t.getTime());\n await sleep(delay);\n }\n}\n","import { DateTime } from 'luxon';\nimport type { Calendar, Session, TimeOfDay } from '../interfaces/calendar';\nimport type { DateRange } from '../interfaces/types';\nimport {\n resolveHolidays,\n resolveSpecialCloses,\n resolveSpecialOpens,\n type HolidayRule,\n type SpecialClose,\n type SpecialOpen,\n type AdhocTimeOverrides,\n} from './holiday-rules';\n\nconst MS_PER_DAY = 86_400_000;\n\nconst DEFAULT_WEEKMASK: ReadonlySet<number> = new Set([1, 2, 3, 4, 5]);\nconst EMPTY_ADHOC: AdhocTimeOverrides = new Map();\n\nfunction ymdKey(d: Date): string {\n return d.toISOString().slice(0, 10);\n}\n\n/**\n * Abstract base class for exchange trading calendars. Implements the full\n * {@link Calendar} interface by composing up to nine overridable hooks that\n * subclasses provide. Concrete implementations ship for {@link NYSEExchangeCalendar}\n * and {@link LSEExchangeCalendar}; additional exchanges can be added by\n * extending this class.\n *\n * **Per-year caching**: holiday sets and special-session maps are computed once\n * per calendar year and stored in private Maps, so repeated calls to `isOpen`,\n * `next`, or `sessions` within the same year are cheap.\n *\n * **Hook resolution order** (adhoc beats rule, rule beats regular):\n * 1. `adhocHolidays()` / `specialClosesAdhoc()` / `specialOpensAdhoc()` —\n * `YYYY-MM-DD` string sets/maps populated once at first access.\n * 2. `regularHolidays()` / `specialCloses()` / `specialOpens()` —\n * year-derived rule arrays applied per year via the resolver helpers.\n * 3. `regularOpen(date)` / `regularClose(date)` / `weekmask(date)` —\n * per-date fallbacks that subclasses override to encode era-varying session\n * times and trading-day sets.\n *\n * **Extending**: override only the hooks you need. All hooks have no-op / sensible\n * defaults (Mon–Fri weekmask, 09:30–16:00 session) so a minimal subclass need\n * only set `name`, `tz`, and `regularHolidays()`.\n */\nexport abstract class ExchangeCalendar implements Calendar {\n /** Short exchange name used as the registry key in {@link getCalendar}. */\n abstract readonly name: string;\n /** IANA timezone identifier, e.g. `'America/New_York'` or `'Europe/London'`. */\n abstract readonly tz: string;\n\n private readonly holidayCache = new Map<number, Set<number>>();\n private readonly specialCloseCache = new Map<number, Map<number, TimeOfDay>>();\n private readonly specialOpenCache = new Map<number, Map<number, TimeOfDay>>();\n\n private adhocHolidaysCache: ReadonlySet<string> | null = null;\n private adhocSpecialClosesCache: AdhocTimeOverrides | null = null;\n private adhocSpecialOpensCache: AdhocTimeOverrides | null = null;\n\n // --- Hooks ---\n\n /**\n * Returns the ordered list of year-derived holiday rules for this exchange.\n * The base implementation returns an empty array (no regular holidays). Override\n * to supply the full rule set; each {@link HolidayRule} in the array is applied\n * via {@link resolveHolidays} once per calendar year and cached. Rules may be\n * era-bounded via `validFrom` / `validUntil`.\n */\n protected regularHolidays(): ReadonlyArray<HolidayRule> {\n return [];\n }\n\n /**\n * Returns the set of `YYYY-MM-DD` strings for one-off full-day closures that\n * do not fit a repeating rule (e.g. presidential funerals, natural disasters).\n * The base implementation returns an empty set. Override with the complete\n * historical adhoc list for the exchange. This method is called at most once\n * per `ExchangeCalendar` instance; the result is cached.\n */\n protected adhocHolidays(): ReadonlySet<string> {\n return new Set();\n }\n\n /**\n * Returns the ordered list of year-derived early-close rules for this exchange.\n * The base implementation returns an empty array. Override to supply rules such\n * as \"day after Thanksgiving closes at 13:00\". Results are computed once per\n * year and cached; each rule is applied via {@link resolveSpecialCloses}.\n */\n protected specialCloses(): ReadonlyArray<SpecialClose> {\n return [];\n }\n\n /**\n * Returns the map of `YYYY-MM-DD` strings to override close times for\n * one-off early-close days that do not fit a repeating rule. The base\n * implementation returns an empty map. Override with the historical adhoc\n * set for the exchange. Called at most once per instance; result is cached.\n */\n protected specialClosesAdhoc(): AdhocTimeOverrides {\n return EMPTY_ADHOC;\n }\n\n /**\n * Returns the ordered list of year-derived late-open rules for this exchange.\n * The base implementation returns an empty array. Override to supply rules such\n * as \"delayed open due to a moment of silence\". Results are computed once per\n * year and cached; each rule is applied via {@link resolveSpecialOpens}.\n */\n protected specialOpens(): ReadonlyArray<SpecialOpen> {\n return [];\n }\n\n /**\n * Returns the map of `YYYY-MM-DD` strings to override open times for\n * one-off late-open days that do not fit a repeating rule. The base\n * implementation returns an empty map. Override with the historical adhoc\n * set for the exchange. Called at most once per instance; result is cached.\n */\n protected specialOpensAdhoc(): AdhocTimeOverrides {\n return EMPTY_ADHOC;\n }\n\n /**\n * Returns the default open time in local exchange time for `date` when no\n * special-open rule matches. The base implementation returns 09:30. Override\n * to encode era-varying session times (e.g. NYSE opened at 10:00 before\n * 1985-09-30).\n *\n * @param date - UTC midnight `Date` for the trading day being queried.\n */\n protected regularOpen(_date: Date): TimeOfDay {\n return { h: 9, m: 30 };\n }\n\n /**\n * Returns the default close time in local exchange time for `date` when no\n * special-close rule matches. The base implementation returns 16:00. Override\n * to encode era-varying session times (e.g. NYSE closed at 15:00 before\n * 1952-09-29, and at 15:30 until 1974-01-02).\n *\n * @param date - UTC midnight `Date` for the trading day being queried.\n */\n protected regularClose(_date: Date): TimeOfDay {\n return { h: 16, m: 0 };\n }\n\n /**\n * Returns the set of weekday indices (using `Date.getUTCDay()` convention:\n * 0 = Sunday, 1 = Monday, …, 6 = Saturday) that are regular trading days.\n * The base implementation returns `{1, 2, 3, 4, 5}` (Mon–Fri). Override to\n * encode historical six-day trading weeks (e.g. NYSE traded Mon–Sat before\n * 1952-09-29, keyed by `date` so the shift is era-aware).\n *\n * @param date - UTC midnight `Date` for the day being tested.\n */\n protected weekmask(_date: Date): ReadonlySet<number> {\n return DEFAULT_WEEKMASK;\n }\n\n // --- Adhoc caching getters ---\n private getAdhocHolidays(): ReadonlySet<string> {\n if (this.adhocHolidaysCache === null) this.adhocHolidaysCache = this.adhocHolidays();\n return this.adhocHolidaysCache;\n }\n private getAdhocSpecialCloses(): AdhocTimeOverrides {\n if (this.adhocSpecialClosesCache === null) this.adhocSpecialClosesCache = this.specialClosesAdhoc();\n return this.adhocSpecialClosesCache;\n }\n private getAdhocSpecialOpens(): AdhocTimeOverrides {\n if (this.adhocSpecialOpensCache === null) this.adhocSpecialOpensCache = this.specialOpensAdhoc();\n return this.adhocSpecialOpensCache;\n }\n\n // --- Caches ---\n /**\n * Cached lookup of regular-holiday timestamps for the given year.\n * Assumes `regularHolidays()` returns the same rule list on every call.\n */\n private holidaysForYear(year: number): Set<number> {\n let set = this.holidayCache.get(year);\n if (!set) {\n set = resolveHolidays(this.regularHolidays(), year);\n this.holidayCache.set(year, set);\n }\n return set;\n }\n\n private specialClosesForYear(year: number): Map<number, TimeOfDay> {\n let map = this.specialCloseCache.get(year);\n if (!map) {\n map = resolveSpecialCloses(this.specialCloses(), year);\n this.specialCloseCache.set(year, map);\n }\n return map;\n }\n\n private specialOpensForYear(year: number): Map<number, TimeOfDay> {\n let map = this.specialOpenCache.get(year);\n if (!map) {\n map = resolveSpecialOpens(this.specialOpens(), year);\n this.specialOpenCache.set(year, map);\n }\n return map;\n }\n\n private normalize(t: Date): Date {\n return new Date(Date.UTC(t.getUTCFullYear(), t.getUTCMonth(), t.getUTCDate()));\n }\n\n // --- Public Calendar API ---\n\n /** Returns `true` when `t` falls on a regular trading day (weekmask check, then holiday check). */\n isOpen(t: Date): boolean {\n const d = this.normalize(t);\n if (!this.weekmask(d).has(d.getUTCDay())) return false;\n if (this.getAdhocHolidays().has(ymdKey(d))) return false;\n const year = d.getUTCFullYear();\n if (this.holidaysForYear(year).has(d.getTime())) return false;\n return true;\n }\n\n /** Returns the first trading day strictly after `t`. */\n next(t: Date): Date {\n let d = new Date(this.normalize(t).getTime() + MS_PER_DAY);\n while (!this.isOpen(d)) d = new Date(d.getTime() + MS_PER_DAY);\n return d;\n }\n\n /** Returns the first trading day strictly before `t`. */\n previous(t: Date): Date {\n let d = new Date(this.normalize(t).getTime() - MS_PER_DAY);\n while (!this.isOpen(d)) d = new Date(d.getTime() - MS_PER_DAY);\n return d;\n }\n\n /**\n * Returns UTC midnight `Date` objects for every trading day in\n * `[range.from, range.to)`. The `from` bound is inclusive; `to` is exclusive.\n */\n sessions(range: DateRange): ReadonlyArray<Date> {\n const out: Date[] = [];\n let d = this.normalize(range.from);\n const end = this.normalize(range.to).getTime();\n while (d.getTime() < end) {\n if (this.isOpen(d)) out.push(d);\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n return out;\n }\n\n schedule(range: DateRange): ReadonlyArray<Session> {\n const days = this.sessions(range);\n return days.map((date) => ({\n date,\n open: this.localizedTimestamp(date, this.openTimeFor(date)),\n close: this.localizedTimestamp(date, this.closeTimeFor(date)),\n }));\n }\n\n isEarlyClose(t: Date): boolean {\n const d = this.normalize(t);\n if (!this.isOpen(d)) return false;\n if (this.getAdhocSpecialCloses().has(ymdKey(d))) return true;\n return this.specialClosesForYear(d.getUTCFullYear()).has(d.getTime());\n }\n\n // --- Resolution ---\n /** Adhoc overrides win over rule-driven; both win over `regularOpen(date)`. */\n private openTimeFor(date: Date): TimeOfDay {\n const adhoc = this.getAdhocSpecialOpens().get(ymdKey(date));\n if (adhoc) return adhoc;\n const ruled = this.specialOpensForYear(date.getUTCFullYear()).get(date.getTime());\n if (ruled) return ruled;\n return this.regularOpen(date);\n }\n\n /** Adhoc overrides win over rule-driven; both win over `regularClose(date)`. */\n private closeTimeFor(date: Date): TimeOfDay {\n const adhoc = this.getAdhocSpecialCloses().get(ymdKey(date));\n if (adhoc) return adhoc;\n const ruled = this.specialClosesForYear(date.getUTCFullYear()).get(date.getTime());\n if (ruled) return ruled;\n return this.regularClose(date);\n }\n\n private localizedTimestamp(date: Date, time: TimeOfDay): Date {\n const dt = DateTime.fromObject(\n {\n year: date.getUTCFullYear(),\n month: date.getUTCMonth() + 1,\n day: date.getUTCDate(),\n hour: time.h,\n minute: time.m,\n },\n { zone: this.tz },\n );\n return new Date(dt.toUTC().toMillis());\n }\n}\n","import type { TimeOfDay } from '../interfaces/calendar';\n\nconst MS_PER_DAY = 86_400_000;\n\nexport type { TimeOfDay };\n\n/**\n * A year-derived holiday rule consumed by {@link ExchangeCalendar}. The rule\n * is active only for years in the range `[validFrom, validUntil]` (both\n * inclusive; omit either bound to leave it open).\n *\n * `resolve(year)` returns the UTC midnight `Date` for the holiday in that year,\n * or `null` if the holiday does not occur that year (e.g. a conditional rule\n * for Good Friday in certain years). When `observe` is `true`, a Saturday\n * result is moved to Friday and a Sunday result is moved to Monday (standard\n * US-style holiday observation).\n */\nexport type HolidayRule = {\n /** Human-readable name, used for debugging and logging. */\n name: string;\n /** Returns the UTC midnight `Date` for this holiday in `year`, or `null` to skip. */\n resolve: (year: number) => Date | null;\n /** First year (inclusive) this rule applies. Defaults to −∞. */\n validFrom?: number;\n /** Last year (inclusive) this rule applies. Defaults to +∞. */\n validUntil?: number;\n /** When `true`, Saturday dates are moved to Friday, Sunday dates to Monday. */\n observe?: boolean;\n};\n\n/**\n * A year-derived early-close rule consumed by {@link ExchangeCalendar}. Follows\n * the same validity bounds and `resolve` contract as {@link HolidayRule}, but\n * instead of marking a day closed entirely it overrides the session close time\n * to `closeAt` for the matched date.\n */\nexport type SpecialClose = {\n /** Human-readable name, used for debugging and logging. */\n name: string;\n /** Returns the UTC midnight `Date` for this early-close day in `year`, or `null` to skip. */\n resolve: (year: number) => Date | null;\n /** The overridden close time in local exchange time. */\n closeAt: TimeOfDay;\n /** First year (inclusive) this rule applies. Defaults to −∞. */\n validFrom?: number;\n /** Last year (inclusive) this rule applies. Defaults to +∞. */\n validUntil?: number;\n};\n\n/**\n * A year-derived late-open rule consumed by {@link ExchangeCalendar}. Follows\n * the same validity bounds and `resolve` contract as {@link HolidayRule}, but\n * overrides the session open time to `openAt` for the matched date.\n */\nexport type SpecialOpen = {\n /** Human-readable name, used for debugging and logging. */\n name: string;\n /** Returns the UTC midnight `Date` for this late-open day in `year`, or `null` to skip. */\n resolve: (year: number) => Date | null;\n /** The overridden open time in local exchange time. */\n openAt: TimeOfDay;\n /** First year (inclusive) this rule applies. Defaults to −∞. */\n validFrom?: number;\n /** Last year (inclusive) this rule applies. Defaults to +∞. */\n validUntil?: number;\n};\n\n/**\n * Map of `YYYY-MM-DD` date strings to override times. Used for one-off\n * historical specials (e.g. a single early close due to a snowstorm) that do\n * not fit a repeating year-derived rule. Keys must be in `YYYY-MM-DD` format\n * in UTC.\n */\nexport type AdhocTimeOverrides = ReadonlyMap<string, TimeOfDay>;\n\n/**\n * Era-bounded session-time rule. Lookup picks the latest rule with `effectiveFrom ≤ date`.\n * Use `effectiveFrom: undefined` for the default (since-inception) rule.\n */\nexport type SessionTimeRule = {\n effectiveFrom?: string; // YYYY-MM-DD inclusive\n time: TimeOfDay;\n};\n\n/**\n * Returns the UTC midnight `Date` of the nth occurrence of `weekday` in the\n * given `month` and `year`. `weekday` follows `Date.getUTCDay()` convention\n * (0 = Sunday, 1 = Monday, …, 6 = Saturday). `n` is 1-based.\n *\n * Example: 3rd Monday of January 2024 → `nthWeekdayOfMonth(2024, 1, 1, 3)`.\n */\nexport function nthWeekdayOfMonth(year: number, month: number, weekday: number, n: number): Date {\n const first = new Date(Date.UTC(year, month - 1, 1));\n const offset = (weekday - first.getUTCDay() + 7) % 7;\n return new Date(Date.UTC(year, month - 1, 1 + offset + (n - 1) * 7));\n}\n\n/**\n * Returns the UTC midnight `Date` of the last occurrence of `weekday` in the\n * given `month` and `year`. `weekday` follows `Date.getUTCDay()` convention.\n *\n * Example: last Monday of May 2024 → `lastWeekdayOfMonth(2024, 5, 1)`.\n */\nexport function lastWeekdayOfMonth(year: number, month: number, weekday: number): Date {\n const last = new Date(Date.UTC(year, month, 0));\n const offset = (last.getUTCDay() - weekday + 7) % 7;\n return new Date(last.getTime() - offset * MS_PER_DAY);\n}\n\n/**\n * Computes the UTC midnight `Date` of Easter Sunday for the given Gregorian\n * `year` using the Anonymous Gregorian algorithm (also known as the \"Meeus/Jones/Butcher\"\n * algorithm). Valid for years 1583–4099.\n */\nexport function easter(year: number): Date {\n const a = year % 19;\n const b = Math.floor(year / 100);\n const c = year % 100;\n const d = Math.floor(b / 4);\n const e = b % 4;\n const f = Math.floor((b + 8) / 25);\n const g = Math.floor((b - f + 1) / 3);\n const h = (19 * a + b - d - g + 15) % 30;\n const i = Math.floor(c / 4);\n const k = c % 4;\n const L = (32 + 2 * e + 2 * i - h - k) % 7;\n const m = Math.floor((a + 11 * h + 22 * L) / 451);\n const month = Math.floor((h + L - 7 * m + 114) / 31);\n const day = ((h + L - 7 * m + 114) % 31) + 1;\n return new Date(Date.UTC(year, month - 1, day));\n}\n\n/**\n * Applies Saturday → Friday, Sunday → Monday observation to a holiday date.\n * Weekday dates are returned unchanged. Used when a {@link HolidayRule} has\n * `observe: true`.\n */\nexport function observed(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === 6) return new Date(d.getTime() - MS_PER_DAY);\n if (dow === 0) return new Date(d.getTime() + MS_PER_DAY);\n return d;\n}\n\n/**\n * Applies a list of {@link HolidayRule} definitions to a single `year` and\n * returns a `Set` of UTC millisecond timestamps for all holidays active that\n * year. Rules outside their `[validFrom, validUntil]` bounds are skipped.\n * Rules that return `null` from `resolve` are also skipped.\n */\nexport function resolveHolidays(rules: ReadonlyArray<HolidayRule>, year: number): Set<number> {\n const out = new Set<number>();\n for (const rule of rules) {\n if (rule.validFrom !== undefined && year < rule.validFrom) continue;\n if (rule.validUntil !== undefined && year > rule.validUntil) continue;\n const raw = rule.resolve(year);\n if (raw === null) continue;\n const final = rule.observe ? observed(raw) : raw;\n out.add(final.getTime());\n }\n return out;\n}\n\n/**\n * Applies a list of {@link SpecialClose} rules to a single `year` and returns\n * a map from UTC millisecond timestamp to override close time. Rules outside\n * their `[validFrom, validUntil]` bounds and rules that return `null` from\n * `resolve` are skipped.\n */\nexport function resolveSpecialCloses(rules: ReadonlyArray<SpecialClose>, year: number): Map<number, TimeOfDay> {\n const out = new Map<number, TimeOfDay>();\n for (const rule of rules) {\n if (rule.validFrom !== undefined && year < rule.validFrom) continue;\n if (rule.validUntil !== undefined && year > rule.validUntil) continue;\n const d = rule.resolve(year);\n if (d === null) continue;\n out.set(d.getTime(), rule.closeAt);\n }\n return out;\n}\n\n/**\n * Applies a list of {@link SpecialOpen} rules to a single `year` and returns\n * a map from UTC millisecond timestamp to override open time. Rules outside\n * their `[validFrom, validUntil]` bounds and rules that return `null` from\n * `resolve` are skipped.\n */\nexport function resolveSpecialOpens(rules: ReadonlyArray<SpecialOpen>, year: number): Map<number, TimeOfDay> {\n const out = new Map<number, TimeOfDay>();\n for (const rule of rules) {\n if (rule.validFrom !== undefined && year < rule.validFrom) continue;\n if (rule.validUntil !== undefined && year > rule.validUntil) continue;\n const d = rule.resolve(year);\n if (d === null) continue;\n out.set(d.getTime(), rule.openAt);\n }\n return out;\n}\n\n// ─── pandas-equivalent date helpers ─────────────────────────────────────────\n// These are general-purpose calendar utilities modelled on pandas_market_calendars\n// helpers. They are exchange-agnostic and reused across NYSE, LSE, and future ports.\n\n/** pandas `sunday_to_monday`: only Sunday observation; Saturday stays Saturday. */\nexport function sundayToMonday(d: Date): Date {\n return d.getUTCDay() === 0 ? new Date(d.getTime() + MS_PER_DAY) : d;\n}\n\n/** pandas `nearest_workday`: Sat → Friday, Sun → Monday, weekday → unchanged. */\nexport function nearestWorkday(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === 6) return new Date(d.getTime() - MS_PER_DAY);\n if (dow === 0) return new Date(d.getTime() + MS_PER_DAY);\n return d;\n}\n\n/**\n * First Monday on/after the given day of the month (mimics pandas `weekday=MO(n)` offset).\n * Pass `nth > 1` to skip forward that many additional weeks.\n */\nexport function firstMondayOnOrAfter(year: number, month: number, day: number, nth = 1): Date {\n const start = new Date(Date.UTC(year, month - 1, day));\n const offset = (1 - start.getUTCDay() + 7) % 7; // MON = 1\n return new Date(start.getTime() + (offset + 7 * (nth - 1)) * MS_PER_DAY);\n}\n\n/** Easter offset by `dayDelta` days (e.g. Good Friday = easterPlus(y, -2)). */\nexport function easterPlus(year: number, dayDelta: number): Date {\n return new Date(easter(year).getTime() + dayDelta * MS_PER_DAY);\n}\n\n/** Helper to drop a holiday if its observed date falls outside an allowed weekday set. */\nexport function dropIfNotInDays(d: Date | null, allowed: ReadonlySet<number>): Date | null {\n if (d === null) return null;\n return allowed.has(d.getUTCDay()) ? d : null;\n}\n\n/**\n * Pick the rule with the latest `effectiveFrom ≤ date.toISOString().slice(0,10)`.\n * Rules without `effectiveFrom` are treated as the default (since inception).\n * Throws if no rule matches at all (provide a default rule to guarantee a match).\n */\nexport function resolveSessionTime(rules: ReadonlyArray<SessionTimeRule>, date: Date): TimeOfDay {\n const key = date.toISOString().slice(0, 10);\n let best: SessionTimeRule | null = null;\n for (const rule of rules) {\n if (rule.effectiveFrom === undefined) {\n if (best === null) best = rule;\n continue;\n }\n if (rule.effectiveFrom <= key) {\n if (best === null || best.effectiveFrom === undefined || best.effectiveFrom < rule.effectiveFrom) {\n best = rule;\n }\n }\n }\n if (best === null) {\n throw new Error('resolveSessionTime: no matching rule (provide a default rule with no effectiveFrom)');\n }\n return best.time;\n}\n","import { ExchangeCalendar } from './exchange-calendar';\nimport {\n dropIfNotInDays,\n easterPlus,\n firstMondayOnOrAfter,\n lastWeekdayOfMonth,\n nearestWorkday,\n nthWeekdayOfMonth,\n sundayToMonday,\n type AdhocTimeOverrides,\n type HolidayRule,\n type SpecialClose,\n type SpecialOpen,\n} from './holiday-rules';\nimport type { TimeOfDay } from '../interfaces/calendar';\n\nconst MS_PER_DAY = 86_400_000;\n\n// Day-of-week constants matching JS Date.getUTCDay(): Sun=0, Mon=1, ... Sat=6.\nconst SUN = 0;\nconst MON = 1;\nconst TUE = 2;\nconst WED = 3;\nconst THU = 4;\nconst FRI = 5;\nconst SAT = 6;\n\nconst SATURDAY_END_KEY = '1952-09-29'; // Saturday trading retired starting this date.\n\nfunction utcDate(y: number, m: number, d: number): Date {\n return new Date(Date.UTC(y, m - 1, d));\n}\n\nfunction ymd(date: Date): string {\n return date.toISOString().slice(0, 10);\n}\n\nconst WEEKDAYS_MON_FRI: ReadonlySet<number> = new Set([MON, TUE, WED, THU, FRI]);\nconst WEEKDAYS_MON_SAT: ReadonlySet<number> = new Set([MON, TUE, WED, THU, FRI, SAT]);\n\nconst REGULAR_HOLIDAYS: ReadonlyArray<HolidayRule> = [\n // ── New Year's Day ─────────────────────────────────────────────────────────\n // Post-1952: Sunday → Monday observance, Saturday-NYD drops (no Friday close).\n {\n name: \"New Year's Day (post-1952)\",\n validFrom: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 1, 1)), WEEKDAYS_MON_FRI),\n },\n // Pre-1952: Saturday is a trading day; NYD on Saturday is observed on Saturday.\n {\n name: \"New Year's Day (pre-1952)\",\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 1, 1)), WEEKDAYS_MON_SAT),\n },\n\n // ── MLK Day (3rd Mon of Jan, from 1998) ────────────────────────────────────\n {\n name: 'Martin Luther King Jr. Day',\n validFrom: 1998,\n resolve: (y) => nthWeekdayOfMonth(y, 1, MON, 3),\n },\n\n // ── Presidents Day (3rd Mon of Feb, from 1971) ────────────────────────────\n {\n name: 'Presidents Day',\n validFrom: 1971,\n resolve: (y) => nthWeekdayOfMonth(y, 2, MON, 3),\n },\n\n // ── Washington's Birthday (Feb 22) ─────────────────────────────────────────\n // Pre-1952: Mon-Sat with Sunday → Monday.\n {\n name: \"Washington's Birthday (pre-1952)\",\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 2, 22)), WEEKDAYS_MON_SAT),\n },\n // 1952-09-29 → 1963: Mon-Fri with Sunday → Monday.\n {\n name: \"Washington's Birthday (1952-1963)\",\n validFrom: 1953,\n validUntil: 1963,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 2, 22)), WEEKDAYS_MON_FRI),\n },\n // 1964-1970: nearest_workday observance.\n {\n name: \"Washington's Birthday (1964-1970)\",\n validFrom: 1964,\n validUntil: 1970,\n resolve: (y) => nearestWorkday(utcDate(y, 2, 22)),\n },\n\n // ── Lincoln's Birthday (Feb 12, 1896-1953) ─────────────────────────────────\n {\n name: \"Lincoln's Birthday\",\n validFrom: 1896,\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 2, 12)),\n },\n\n // ── Good Friday ────────────────────────────────────────────────────────────\n // Closed every year EXCEPT 1898, 1906, 1907.\n {\n name: 'Good Friday (1908+)',\n validFrom: 1908,\n resolve: (y) => easterPlus(y, -2),\n },\n {\n name: 'Good Friday (pre-1898)',\n validFrom: 1885,\n validUntil: 1897,\n resolve: (y) => easterPlus(y, -2),\n },\n {\n name: 'Good Friday (1899-1905)',\n validFrom: 1899,\n validUntil: 1905,\n resolve: (y) => easterPlus(y, -2),\n },\n\n // ── Memorial Day ───────────────────────────────────────────────────────────\n // Modern: last Monday of May, from 1971.\n {\n name: 'Memorial Day (modern, 1971+)',\n validFrom: 1971,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 25),\n },\n // Pre-1952 (Mon-Sat with Sunday → Monday).\n {\n name: 'Memorial Day (pre-1952)',\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 5, 30)), WEEKDAYS_MON_SAT),\n },\n // 1952-09-29 → 1963.\n {\n name: 'Memorial Day (1952-1963)',\n validFrom: 1953,\n validUntil: 1963,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 5, 30)), WEEKDAYS_MON_FRI),\n },\n // 1964-1969 nearest_workday.\n {\n name: 'Memorial Day (1964-1969)',\n validFrom: 1964,\n validUntil: 1969,\n resolve: (y) => nearestWorkday(utcDate(y, 5, 30)),\n },\n\n // ── Juneteenth (from 2022) ─────────────────────────────────────────────────\n {\n name: 'Juneteenth',\n validFrom: 2022,\n resolve: (y) => nearestWorkday(utcDate(y, 6, 19)),\n },\n\n // ── Independence Day ───────────────────────────────────────────────────────\n // Modern: nearest_workday, from 1954.\n {\n name: 'Independence Day (modern, 1954+)',\n validFrom: 1954,\n resolve: (y) => dropIfNotInDays(nearestWorkday(utcDate(y, 7, 4)), WEEKDAYS_MON_FRI),\n },\n // Pre-1952.\n {\n name: 'Independence Day (pre-1952)',\n validUntil: 1952,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 7, 4)), WEEKDAYS_MON_SAT),\n },\n // 1952-09-29 → 1953 (post-Saturday-trading transition).\n {\n name: 'Independence Day (1953)',\n validFrom: 1953,\n validUntil: 1953,\n resolve: (y) => dropIfNotInDays(sundayToMonday(utcDate(y, 7, 4)), WEEKDAYS_MON_FRI),\n },\n\n // ── Labor Day (1st Mon of Sep, from 1887) ─────────────────────────────────\n {\n name: 'Labor Day',\n validFrom: 1887,\n resolve: (y) => nthWeekdayOfMonth(y, 9, MON, 1),\n },\n\n // ── Columbus Day (Oct 12, 1909-1953) ───────────────────────────────────────\n {\n name: 'Columbus Day',\n validFrom: 1909,\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 10, 12)),\n },\n\n // ── Election Day (1848-1967, every year; 1968-1980 adhoc; thereafter none) ─\n {\n name: 'Election Day (1848-1967)',\n validFrom: 1885,\n validUntil: 1967,\n resolve: (y) => {\n // First Tuesday on/after Nov 2 (pandas: month=11 day=2 offset=TU(1)).\n const start = utcDate(y, 11, 2);\n const offset = (TUE - start.getUTCDay() + 7) % 7;\n return new Date(start.getTime() + offset * MS_PER_DAY);\n },\n },\n\n // ── Veterans/Armistice Day (Nov 11, 1934-1953) ─────────────────────────────\n {\n name: 'Veterans Day (1934-1953)',\n validFrom: 1934,\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 11, 11)),\n },\n\n // ── Thanksgiving ───────────────────────────────────────────────────────────\n // Modern: 4th Thursday of Nov, from 1942.\n {\n name: 'Thanksgiving (modern, 1942+)',\n validFrom: 1942,\n resolve: (y) => nthWeekdayOfMonth(y, 11, THU, 4),\n },\n // Pre-1939: last Thursday of Nov.\n {\n name: 'Thanksgiving (pre-1939)',\n validFrom: 1864,\n validUntil: 1938,\n resolve: (y) => lastWeekdayOfMonth(y, 11, THU),\n },\n // 1939-1941: 2nd-to-last Thursday of Nov (Franklin Thanksgiving).\n {\n name: 'Thanksgiving (1939-1941)',\n validFrom: 1939,\n validUntil: 1941,\n resolve: (y) => {\n const last = lastWeekdayOfMonth(y, 11, THU);\n return new Date(last.getTime() - 7 * MS_PER_DAY);\n },\n },\n\n // ── Christmas ──────────────────────────────────────────────────────────────\n // Modern: nearest_workday, from 1999.\n {\n name: 'Christmas (1999+)',\n validFrom: 1999,\n resolve: (y) => nearestWorkday(utcDate(y, 12, 25)),\n },\n // 1954-1998.\n {\n name: 'Christmas (1954-1998)',\n validFrom: 1954,\n validUntil: 1998,\n resolve: (y) => nearestWorkday(utcDate(y, 12, 25)),\n },\n // Pre-1954: sunday_to_monday only.\n {\n name: 'Christmas (pre-1954)',\n validUntil: 1953,\n resolve: (y) => sundayToMonday(utcDate(y, 12, 25)),\n },\n];\n\n// ─── Adhoc full-day closures (literal date set) ──────────────────────────────\n// Sourced verbatim from pandas_market_calendars/holidays/nyse.py adhoc lists.\nconst ADHOC_RAW: ReadonlyArray<string> = [\n // SatAfterGoodFridayAdhoc\n '1900-04-14',\n '1901-04-06',\n '1902-03-29',\n '1903-04-11',\n '1905-04-22',\n '1907-03-30',\n '1908-04-18',\n '1909-04-10',\n '1910-03-26',\n '1911-04-15',\n '1913-03-22',\n '1920-04-03',\n '1929-03-30',\n '1930-04-19',\n // MonBeforeIndependenceDayAdhoc\n '1899-07-03',\n // SatBeforeIndependenceDayAdhoc\n '1887-07-02',\n '1892-07-02',\n '1898-07-02',\n '1904-07-02',\n '1909-07-03',\n '1910-07-02',\n '1920-07-03',\n '1921-07-02',\n '1926-07-03',\n '1932-07-02',\n '1937-07-03',\n // SatAfterIndependenceDayAdhoc\n '1890-07-05',\n '1902-07-05',\n '1913-07-05',\n '1919-07-05',\n '1930-07-05',\n // DaysAfterIndependenceDayAdhoc\n '1901-07-05',\n '1901-07-06',\n '1968-07-05',\n // SatBeforeLaborDayAdhoc\n '1888-09-01',\n '1898-09-03',\n '1900-09-01',\n '1901-08-31',\n '1902-08-30',\n '1903-09-05',\n '1904-09-03',\n '1907-08-31',\n '1908-09-05',\n '1909-09-04',\n '1910-09-03',\n '1911-09-02',\n '1912-08-31',\n '1913-08-30',\n '1917-09-01',\n '1919-08-30',\n '1920-09-04',\n '1921-09-03',\n '1926-09-04',\n '1929-08-31',\n '1930-08-30',\n '1931-09-05',\n // USElectionDay1968to1980Adhoc\n '1968-11-05',\n '1972-11-07',\n '1976-11-02',\n '1980-11-04',\n // FridayAfterThanksgivingAdHoc\n '1888-11-30',\n // SatBeforeChristmasAdhoc\n '1887-12-24',\n '1898-12-24',\n '1904-12-24',\n '1910-12-24',\n '1911-12-23',\n '1922-12-23',\n '1949-12-24',\n '1950-12-23',\n // SatAfterChristmasAdhoc\n '1891-12-26',\n '1896-12-26',\n '1903-12-26',\n '1908-12-26',\n '1925-12-26',\n '1931-12-26',\n '1936-12-26',\n // ChristmasEvesAdhoc\n '1900-12-24',\n '1945-12-24',\n '1956-12-24',\n // DayAfterChristmasAdhoc\n '1958-12-26',\n // USVetransDayAdHoc\n '1921-11-11',\n '1968-11-11',\n // SatAfterColumbusDayAdHoc\n '1917-10-13',\n '1945-10-13',\n // LincolnsBirthDayAdhoc\n '1968-02-12',\n // GrantsBirthDayAdhoc\n '1897-04-27',\n // SatBeforeNewYearsAdhoc\n '1916-12-30',\n // SatBeforeWashingtonsBirthdayAdhoc\n '1903-02-21',\n // SatAfterWashingtonsBirthdayAdhoc\n '1901-02-23',\n '1907-02-23',\n '1929-02-23',\n '1946-02-23',\n // SatBeforeAfterLincolnsBirthdayAdhoc\n '1899-02-11',\n '1909-02-13',\n // SatBeforeDecorationAdhoc\n '1904-05-28',\n '1909-05-29',\n '1910-05-28',\n '1921-05-28',\n '1926-05-29',\n '1937-05-29',\n // SatAfterDecorationAdhoc\n '1902-05-31',\n '1913-05-31',\n '1919-05-31',\n '1924-05-31',\n '1930-05-31',\n // DayBeforeDecorationAdhoc\n '1899-05-29',\n '1961-05-29',\n // ── Irregular full-day closures ──\n // UlyssesGrantFuneral1885\n '1885-08-08',\n // ColumbianCelebration1892\n '1892-10-12',\n '1892-10-21',\n '1892-10-22',\n '1893-04-27',\n // GreatBlizzardOf1888\n '1888-03-12',\n '1888-03-13',\n // WashingtonInaugurationCentennialCelebration1889\n '1889-04-29',\n '1889-04-30',\n '1889-05-01',\n // CharterDay1898\n '1898-05-04',\n // WelcomeNavalCommander1898\n '1898-08-20',\n // AdmiralDeweyCelebration1899\n '1899-09-29',\n '1899-09-30',\n // GarretHobartFuneral1899\n '1899-11-25',\n // QueenVictoriaFuneral1901\n '1901-02-02',\n // MovedToProduceExchange1901\n '1901-04-27',\n // EnlargedProduceExchange1901\n '1901-05-11',\n // McKinleyDeathAndFuneral1901\n '1901-09-14',\n '1901-09-19',\n // KingEdwardVIIcoronation1902\n '1902-08-09',\n // NYSEnewBuildingOpen1903\n '1903-04-22',\n // HudsonFultonCelebration1909\n '1909-09-25',\n // JamesShermanFuneral1912\n '1912-11-02',\n // WeatherHeatClosing1917\n '1917-08-04',\n // DraftRegistrationDay1917\n '1917-06-05',\n // WeatherNoHeatClosing1918\n '1918-01-28',\n '1918-02-04',\n '1918-02-11',\n // DraftRegistrationDay1918\n '1918-09-12',\n // ArmisticeSigned1918\n '1918-11-11',\n // Homecoming27Division1919\n '1919-03-25',\n // ParadeOf77thDivision1919\n '1919-05-06',\n // BacklogRelief1919\n '1919-07-19',\n '1919-08-02',\n '1919-08-16',\n // GeneralPershingReturn1919\n '1919-09-10',\n // OfficeLocationChange1920\n '1920-05-01',\n // HardingDeath1923, HardingFuneral1923\n '1923-08-03',\n '1923-08-10',\n // LindberghParade1927\n '1927-06-13',\n // BacklogRelief1928\n '1928-04-07',\n '1928-04-21',\n '1928-05-05',\n '1928-05-12',\n '1928-05-19',\n '1928-05-26',\n '1928-11-24',\n // BacklogRelief1929\n '1929-02-09',\n '1929-11-01',\n '1929-11-02',\n '1929-11-09',\n '1929-11-16',\n '1929-11-23',\n '1929-11-29',\n '1929-11-30',\n // CoolidgeFuneral1933\n '1933-01-07',\n // BankHolidays1933 (Mar 4, 6-14)\n '1933-03-04',\n '1933-03-06',\n '1933-03-07',\n '1933-03-08',\n '1933-03-09',\n '1933-03-10',\n '1933-03-11',\n '1933-03-13',\n '1933-03-14',\n // (Mar 12, 1933 was a Sunday — naturally non-trading; upstream set still\n // includes it but our weekmask excludes Sundays. Keep parity by listing it.)\n '1933-03-12',\n // HeavyVolume1933 (closed Saturdays)\n '1933-07-29',\n '1933-08-05',\n '1933-08-12',\n '1933-08-19',\n '1933-08-26',\n '1933-09-02',\n // SatClosings1944\n '1944-08-19',\n '1944-08-26',\n '1944-09-02',\n // RooseveltDayOfMourning1945\n '1945-04-14',\n // VJday1945\n '1945-08-15',\n '1945-08-16',\n // NavyDay1945\n '1945-10-27',\n // RailroadStrike1946\n '1946-05-25',\n // SevereWeather1948\n '1948-01-03',\n // KennedyFuneral1963\n '1963-11-25',\n // MLKdayOfMourning1968\n '1968-04-09',\n // PaperworkCrisis1968 (every Wednesday from 1968-06-12 through 1968-12-18,\n // skipping holiday weeks per upstream literal list)\n '1968-06-12',\n '1968-06-19',\n '1968-06-26',\n '1968-07-10',\n '1968-07-17',\n '1968-07-24',\n '1968-07-31',\n '1968-08-07',\n '1968-08-14',\n '1968-08-21',\n '1968-08-28',\n '1968-09-11',\n '1968-09-18',\n '1968-09-25',\n '1968-10-02',\n '1968-10-09',\n '1968-10-16',\n '1968-10-23',\n '1968-10-30',\n '1968-11-20',\n '1968-12-04',\n '1968-12-11',\n '1968-12-18',\n // SnowClosing1969\n '1969-02-10',\n // EisenhowerFuneral1969\n '1969-03-31',\n // FirstLunarLandingClosing1969\n '1969-07-21',\n // TrumanFuneral1972\n '1972-12-28',\n // JohnsonFuneral1973\n '1973-01-25',\n // NewYorkCityBlackout77\n '1977-07-14',\n // HurricaneGloriaClosings1985\n '1985-09-27',\n // NixonFuneral1994\n '1994-04-27',\n // ReaganMourning2004\n '2004-06-11',\n // FordMourning2007\n '2007-01-02',\n // September11Closings2001\n '2001-09-11',\n '2001-09-12',\n '2001-09-13',\n '2001-09-14',\n // HurricaneSandyClosings2012\n '2012-10-29',\n '2012-10-30',\n // GeorgeHWBushDeath2018\n '2018-12-05',\n // JimmyCarterDeath2025\n '2025-01-09',\n];\n\n// Saturday-summer closings 1945-1952 (every Saturday in the listed window).\nfunction* generateSummerSaturdays(): IterableIterator<string> {\n const ranges: ReadonlyArray<[string, string]> = [\n ['1945-07-07', '1945-09-01'],\n ['1946-06-01', '1946-09-28'],\n ['1947-05-31', '1947-09-27'],\n ['1948-05-29', '1948-09-25'],\n ['1949-05-28', '1949-09-24'],\n ['1950-06-03', '1950-09-30'],\n ['1951-06-02', '1951-09-29'],\n ['1952-05-31', '1952-09-27'],\n ];\n for (const [from, to] of ranges) {\n let d = new Date(`${from}T00:00:00.000Z`);\n const end = new Date(`${to}T00:00:00.000Z`);\n while (d.getTime() <= end.getTime()) {\n if (d.getUTCDay() === SAT) yield ymd(d);\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n }\n}\n\n// WWI shutdown 1914-07-31 → 1914-12-11 (every Mon-Sat).\n// Upstream: OnsetOfWWI1914 uses CustomBusinessDay(weekmask=\"Mon Tue Wed Thu Fri Sat\"),\n// confirming that Saturdays are included — consistent with the pre-1952 Mon-Sat trading week.\nfunction* generateWWIShutdown(): IterableIterator<string> {\n let d = new Date('1914-07-31T00:00:00.000Z');\n const end = new Date('1914-12-11T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow !== SUN) yield ymd(d);\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n}\n\nconst ADHOC_HOLIDAYS: ReadonlySet<string> = new Set<string>([\n ...ADHOC_RAW,\n ...generateSummerSaturdays(),\n ...generateWWIShutdown(),\n]);\n\n// ─── Rule-driven special closes (early-close rules) ─────────────────────────\nconst SPECIAL_CLOSES: ReadonlyArray<SpecialClose> = [\n // 1pm — Day-after-Thanksgiving 1993+, Christmas Eve weekday 1999+,\n // pre-Independence Day Mon/Tue/Thu (1995+), Wed before Independence Day (2013+),\n // Friday-after-Independence Day pre-2013 (1996-2012).\n {\n name: 'Day after Thanksgiving 1pm (1993+)',\n validFrom: 1993,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const t = nthWeekdayOfMonth(y, 11, THU, 4);\n return new Date(t.getTime() + MS_PER_DAY);\n },\n },\n {\n name: 'Day after Thanksgiving 2pm (1992)',\n validFrom: 1992,\n validUntil: 1992,\n closeAt: { h: 14, m: 0 },\n resolve: (y) => {\n const t = nthWeekdayOfMonth(y, 11, THU, 4);\n return new Date(t.getTime() + MS_PER_DAY);\n },\n },\n {\n name: 'Christmas Eve Mon-Thu 1pm (1999+)',\n validFrom: 1999,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 12, 24);\n const dow = d.getUTCDay();\n return dow >= MON && dow <= THU ? d : null;\n },\n },\n {\n name: 'Mon/Tue/Thu before Independence Day 1pm (1995+)',\n validFrom: 1995,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 7, 3);\n const dow = d.getUTCDay();\n return dow === MON || dow === TUE || dow === THU ? d : null;\n },\n },\n {\n name: 'Wed before Independence Day 1pm (2013+)',\n validFrom: 2013,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 7, 3);\n return d.getUTCDay() === WED ? d : null;\n },\n },\n {\n name: 'Friday after Independence Day 1pm (1996-2012)',\n validFrom: 1996,\n validUntil: 2012,\n closeAt: { h: 13, m: 0 },\n resolve: (y) => {\n const d = utcDate(y, 7, 5);\n return d.getUTCDay() === FRI ? d : null;\n },\n },\n];\n\n// ─── Adhoc special closes (literal map) ─────────────────────────────────────\nconst SPECIAL_CLOSES_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>([\n // 1pm closes\n ['1908-06-26', { h: 13, m: 0 }], // Grover Cleveland funeral\n // ChristmasEve1pmEarlyCloseAdhoc\n ['1951-12-24', { h: 13, m: 0 }],\n ['1996-12-24', { h: 13, m: 0 }],\n ['1997-12-24', { h: 13, m: 0 }],\n ['1998-12-24', { h: 13, m: 0 }],\n ['1999-12-24', { h: 13, m: 0 }],\n // DayAfterChristmas1pmEarlyCloseAdhoc\n ['1997-12-26', { h: 13, m: 0 }],\n ['2003-12-26', { h: 13, m: 0 }],\n // BacklogRelief1pmEarlyClose1929\n ['1929-11-06', { h: 13, m: 0 }],\n ['1929-11-07', { h: 13, m: 0 }],\n ['1929-11-08', { h: 13, m: 0 }],\n ['1929-11-11', { h: 13, m: 0 }],\n ['1929-11-12', { h: 13, m: 0 }],\n ['1929-11-13', { h: 13, m: 0 }],\n ['1929-11-14', { h: 13, m: 0 }],\n ['1929-11-15', { h: 13, m: 0 }],\n ['1929-11-18', { h: 13, m: 0 }],\n ['1929-11-19', { h: 13, m: 0 }],\n ['1929-11-20', { h: 13, m: 0 }],\n ['1929-11-21', { h: 13, m: 0 }],\n ['1929-11-22', { h: 13, m: 0 }],\n // 12pm early close — ParadeOfNationalGuard1917\n ['1917-08-29', { h: 12, m: 0 }],\n // LibertyDay 1917\n ['1917-10-24', { h: 12, m: 0 }],\n // LibertyDay 1918\n ['1918-04-26', { h: 12, m: 0 }],\n // WallStreetExplosion 1920\n ['1920-09-16', { h: 12, m: 0 }],\n // NRAdemonstration 1933\n ['1933-09-13', { h: 12, m: 0 }],\n // 12:30pm — RooseveltFuneral 1919\n ['1919-01-07', { h: 12, m: 30 }],\n // WoodrowWilsonFuneral 1924\n ['1924-02-06', { h: 12, m: 30 }],\n // TaftFuneral 1930\n ['1930-03-11', { h: 12, m: 30 }],\n // GasFumes 1933\n ['1933-08-04', { h: 12, m: 30 }],\n // 11am close — KingEdwardDeath 1910\n ['1910-05-07', { h: 11, m: 0 }],\n // 14:00 — HooverFuneral 1964\n ['1964-10-23', { h: 14, m: 0 }],\n // Snow2pmEarlyClose1967 (Feb 7, 1967)\n ['1967-02-07', { h: 14, m: 0 }],\n // Snow2pmEarlyClose1978\n ['1978-02-06', { h: 14, m: 0 }],\n // Snow2pmEarlyClose1996\n ['1996-01-08', { h: 14, m: 0 }],\n // 14:07 — Kennedy assassination\n ['1963-11-22', { h: 14, m: 7 }],\n // 14:30 — FalseArmistice 1918\n ['1918-11-07', { h: 14, m: 30 }],\n // CromwellFuneral 1925\n ['1925-09-18', { h: 14, m: 30 }],\n // Snow230EarlyClose1975\n ['1975-02-12', { h: 14, m: 30 }],\n // Snow230pmEarlyClose1994\n ['1994-02-11', { h: 14, m: 30 }],\n // 15:00 — HurricaneWatch 1976\n ['1976-08-09', { h: 15, m: 0 }],\n // 15:17 — Reagan assassination attempt\n ['1981-03-30', { h: 15, m: 17 }],\n // 15:28 — ConEd power fail\n ['1981-09-09', { h: 15, m: 28 }],\n // 15:30 — CircuitBreakerTriggered 1997\n ['1997-10-27', { h: 15, m: 30 }],\n // 15:56 — SystemProb 2005\n ['2005-06-01', { h: 15, m: 56 }],\n // ChristmasEve2pmEarlyCloseAdhoc\n ['1974-12-24', { h: 14, m: 0 }],\n ['1975-12-24', { h: 14, m: 0 }],\n ['1990-12-24', { h: 14, m: 0 }],\n ['1991-12-24', { h: 14, m: 0 }],\n ['1992-12-24', { h: 14, m: 0 }],\n // HeavyVolume2pmEarlyClose1933\n ['1933-07-26', { h: 14, m: 0 }],\n ['1933-07-27', { h: 14, m: 0 }],\n ['1933-07-28', { h: 14, m: 0 }],\n // BacklogRelief2pmEarlyClose1928 (May 21-25, 1928 Mon-Fri+Sat)\n ['1928-05-21', { h: 14, m: 0 }],\n ['1928-05-22', { h: 14, m: 0 }],\n ['1928-05-23', { h: 14, m: 0 }],\n ['1928-05-24', { h: 14, m: 0 }],\n ['1928-05-25', { h: 14, m: 0 }],\n // 1987 backlog 2pm: Oct 23-30 (Fri-Fri); Mon-Fri set\n ['1987-10-23', { h: 14, m: 0 }],\n ['1987-10-26', { h: 14, m: 0 }],\n ['1987-10-27', { h: 14, m: 0 }],\n ['1987-10-28', { h: 14, m: 0 }],\n ['1987-10-29', { h: 14, m: 0 }],\n ['1987-10-30', { h: 14, m: 0 }],\n // 1987 backlog 2:30pm: Nov 2-4\n ['1987-11-02', { h: 14, m: 30 }],\n ['1987-11-03', { h: 14, m: 30 }],\n ['1987-11-04', { h: 14, m: 30 }],\n // 1987 backlog 3pm: Nov 5-6\n ['1987-11-05', { h: 15, m: 0 }],\n ['1987-11-06', { h: 15, m: 0 }],\n // 1987 backlog 3:30pm: Nov 9-11\n ['1987-11-09', { h: 15, m: 30 }],\n ['1987-11-10', { h: 15, m: 30 }],\n ['1987-11-11', { h: 15, m: 30 }],\n]);\n\n// 1966 transit strike 2pm closes (Jan 6-14 weekdays).\n(() => {\n let d = new Date('1966-01-06T00:00:00.000Z');\n const end = new Date('1966-01-14T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1967 backlog 2pm closes (Aug 9-18 weekdays).\n(() => {\n let d = new Date('1967-08-09T00:00:00.000Z');\n const end = new Date('1967-08-18T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1968 backlog 2pm closes (Jan 22 - Mar 1 weekdays).\n(() => {\n let d = new Date('1968-01-22T00:00:00.000Z');\n const end = new Date('1968-03-01T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1969 paperwork crisis 2pm (Jan 1 - Jul 3 weekdays).\n(() => {\n let d = new Date('1969-01-01T00:00:00.000Z');\n const end = new Date('1969-07-03T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1969 paperwork crisis 2:30pm (Jul 7 - Sep 26 weekdays).\n(() => {\n let d = new Date('1969-07-07T00:00:00.000Z');\n const end = new Date('1969-09-26T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 14, m: 30 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// 1969-1970 paperwork crisis 3pm (Sep 29, 1969 - May 1, 1970 weekdays).\n(() => {\n let d = new Date('1969-09-29T00:00:00.000Z');\n const end = new Date('1970-05-01T00:00:00.000Z');\n while (d.getTime() <= end.getTime()) {\n const dow = d.getUTCDay();\n if (dow >= MON && dow <= FRI) {\n (SPECIAL_CLOSES_ADHOC as Map<string, TimeOfDay>).set(ymd(d), { h: 15, m: 0 });\n }\n d = new Date(d.getTime() + MS_PER_DAY);\n }\n})();\n\n// ─── Rule-driven special opens ──────────────────────────────────────────────\nconst SPECIAL_OPENS: ReadonlyArray<SpecialOpen> = [];\n\n// ─── Adhoc special opens (literal map) ──────────────────────────────────────\nconst SPECIAL_OPENS_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>([\n // 9:31am — ConEdXformer1990\n ['1990-12-27', { h: 9, m: 31 }],\n // EnduringFreedomMomentSilence 2001\n ['2001-10-08', { h: 9, m: 31 }],\n // 9:32am — IraqiFreedom 2003\n ['2003-03-20', { h: 9, m: 32 }],\n // ReaganMomentSilence 2004\n ['2004-06-07', { h: 9, m: 32 }],\n // FordMomentSilence 2006\n ['2006-12-27', { h: 9, m: 32 }],\n // 9:33am — Sept11MomentSilence 2001\n ['2001-09-17', { h: 9, m: 33 }],\n // 10:15 — Snow1015LateOpen1967\n ['1967-02-07', { h: 10, m: 15 }],\n // MerrillLynchComputer1015LateOpen1974\n ['1974-01-16', { h: 10, m: 15 }],\n // FireDrill1015LateOpen1974\n ['1974-11-22', { h: 10, m: 15 }],\n // FireDrill1015LateOpen1976\n ['1976-06-08', { h: 10, m: 15 }],\n // 10:30 — TrafficBlockLateOpen1919\n ['1919-12-30', { h: 10, m: 30 }],\n // TrafficBlockLateOpen1920\n ['1920-02-06', { h: 10, m: 30 }],\n // Computer1030LateOpen1995\n ['1995-12-18', { h: 10, m: 30 }],\n // 10:45 — EclipseOfSunLateOpen1925\n ['1925-01-24', { h: 10, m: 45 }],\n // Storm1045LateOpen1969\n ['1969-06-02', { h: 10, m: 45 }],\n // 11:00 — Snow11amLateOpen1934\n ['1934-02-20', { h: 11, m: 0 }],\n // KingGeorgeVFuneral1936\n ['1936-01-28', { h: 11, m: 0 }],\n // Snow11amLateOpening1960\n ['1960-12-12', { h: 11, m: 0 }],\n // Snow11amLateOpen1969\n ['1969-02-11', { h: 11, m: 0 }],\n // Ice11amLateOpen1973\n ['1973-12-17', { h: 11, m: 0 }],\n // Snow11amLateOpen1978\n ['1978-02-07', { h: 11, m: 0 }],\n // Fire11amLateOpen1989\n ['1989-11-10', { h: 11, m: 0 }],\n // Snow11amLateOpen1996\n ['1996-01-08', { h: 11, m: 0 }],\n // 11:05 — PowerFail1965\n ['1965-11-10', { h: 11, m: 5 }],\n // 11:15 — Storm1115LateOpen1976\n ['1976-02-02', { h: 11, m: 15 }],\n // 12:00 — KingEdwardFuneral1910\n ['1910-05-20', { h: 12, m: 0 }],\n // JPMorganFuneral1913\n ['1913-04-14', { h: 12, m: 0 }],\n // WilliamGaynorFuneral1913\n ['1913-09-22', { h: 12, m: 0 }],\n // Snow12pmLateOpen1978\n ['1978-01-20', { h: 12, m: 0 }],\n // Sept11Anniversary 2002\n ['2002-09-11', { h: 12, m: 0 }],\n // BacklogRelief12pmLateOpen1929\n ['1929-10-31', { h: 12, m: 0 }],\n // HeavyVolume12pmLateOpen1933\n ['1933-07-24', { h: 12, m: 0 }],\n ['1933-07-25', { h: 12, m: 0 }],\n // HeavyVolume11amLateOpen1933\n ['1933-07-26', { h: 11, m: 0 }],\n ['1933-07-27', { h: 11, m: 0 }],\n ['1933-07-28', { h: 11, m: 0 }],\n // 13:00 — AnnunciatorBoardFire 1921\n ['1921-08-02', { h: 13, m: 0 }],\n // TroopsInGulf931LateOpens1991\n ['1991-01-17', { h: 9, m: 31 }],\n ['1991-02-25', { h: 9, m: 31 }],\n]);\n\n/**\n * New York Stock Exchange (NYSE) trading-day calendar covering 1885-01-01 to\n * the present. Also applicable to NYSE-equivalent venues (NASDAQ, BATS, DJIA,\n * DOW). Faithful port of `pandas_market_calendars`' `nyse.py`.\n *\n * **Era boundaries:**\n * - Weekmask: Mon–Sat through 1952-09-28; Mon–Fri from 1952-09-29 onward\n * (Saturday trading retired on that date).\n * - Regular open: 10:00 ET before 1985-09-30; 09:30 ET from 1985-09-30 onward.\n * - Regular close: 15:00 ET before 1952-09-29; 15:30 ET through 1973-12-31;\n * 16:00 ET from 1974-01-02 onward. Saturday closes (pre-1952) are\n * approximated as 12:00.\n *\n * Holiday coverage includes the full set of regular (rule-derived) and adhoc\n * (literal date set) closures sourced from `pandas_market_calendars`, spanning\n * historical events from the Ulysses Grant funeral (1885) through the Jimmy\n * Carter national day of mourning (2025).\n */\nexport class NYSEExchangeCalendar extends ExchangeCalendar {\n readonly name = 'NYSE';\n readonly tz = 'America/New_York';\n\n protected override regularHolidays(): ReadonlyArray<HolidayRule> {\n return REGULAR_HOLIDAYS;\n }\n\n protected override adhocHolidays(): ReadonlySet<string> {\n return ADHOC_HOLIDAYS;\n }\n\n protected override specialCloses(): ReadonlyArray<SpecialClose> {\n return SPECIAL_CLOSES;\n }\n\n protected override specialClosesAdhoc(): AdhocTimeOverrides {\n return SPECIAL_CLOSES_ADHOC;\n }\n\n protected override specialOpens(): ReadonlyArray<SpecialOpen> {\n return SPECIAL_OPENS;\n }\n\n protected override specialOpensAdhoc(): AdhocTimeOverrides {\n return SPECIAL_OPENS_ADHOC;\n }\n\n protected override regularOpen(date: Date): TimeOfDay {\n return ymd(date) < '1985-09-30' ? { h: 10, m: 0 } : { h: 9, m: 30 };\n }\n\n protected override regularClose(date: Date): TimeOfDay {\n const key = ymd(date);\n // Saturday close pre-1952 was 12:00 (approximation, see spec).\n if (key < SATURDAY_END_KEY && date.getUTCDay() === SAT) return { h: 12, m: 0 };\n if (key < SATURDAY_END_KEY) return { h: 15, m: 0 };\n if (key < '1974-01-02') return { h: 15, m: 30 };\n return { h: 16, m: 0 };\n }\n\n protected override weekmask(date: Date): ReadonlySet<number> {\n return ymd(date) < SATURDAY_END_KEY ? WEEKDAYS_MON_SAT : WEEKDAYS_MON_FRI;\n }\n}\n","import { ExchangeCalendar } from './exchange-calendar';\nimport {\n dropIfNotInDays,\n easterPlus,\n firstMondayOnOrAfter,\n lastWeekdayOfMonth,\n type AdhocTimeOverrides,\n type HolidayRule,\n type SpecialClose,\n type SpecialOpen,\n} from './holiday-rules';\nimport type { TimeOfDay } from '../interfaces/calendar';\n\nconst MS_PER_DAY = 86_400_000;\n\n// Day-of-week constants matching JS Date.getUTCDay(): Sun=0, Mon=1, ..., Sat=6.\nconst SUN = 0;\nconst MON = 1;\nconst TUE = 2;\nconst SAT = 6;\n\nfunction utcDate(y: number, m: number, d: number): Date {\n return new Date(Date.UTC(y, m - 1, d));\n}\n\nconst WEEKDAYS_MON_FRI: ReadonlySet<number> = new Set([1, 2, 3, 4, 5]);\nconst MON_TUE: ReadonlySet<number> = new Set([MON, TUE]);\n\n/**\n * pandas `weekend_to_monday`: Sat → Monday, Sun → Monday. Weekday → unchanged.\n * Used by LSE for New Year's Day observance.\n */\nfunction weekendToMonday(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === SAT) return new Date(d.getTime() + 2 * MS_PER_DAY);\n if (dow === SUN) return new Date(d.getTime() + MS_PER_DAY);\n return d;\n}\n\n/**\n * pandas `previous_friday`: if Sat → Friday (−1 day); if Sun → Friday (−2 days);\n * weekday → unchanged. Used by LSE for Christmas Eve and New Year's Eve early-close\n * observance — the early close moves to the prior Friday when the calendar date\n * falls on a weekend.\n */\nfunction previousFriday(d: Date): Date {\n const dow = d.getUTCDay();\n if (dow === SAT) return new Date(d.getTime() - MS_PER_DAY);\n if (dow === SUN) return new Date(d.getTime() - 2 * MS_PER_DAY);\n return d;\n}\n\n// ─── Regular holidays ───────────────────────────────────────────────────────\n// Faithful port of pandas_market_calendars/holidays/uk.py rule definitions.\nconst REGULAR_HOLIDAYS: ReadonlyArray<HolidayRule> = [\n // ── New Year's Day ─────────────────────────────────────────────────────────\n // pandas: weekend_to_monday observance (Sat → Mon, Sun → Mon).\n {\n name: \"New Year's Day\",\n resolve: (y) => weekendToMonday(utcDate(y, 1, 1)),\n },\n\n // ── Good Friday (easter − 2) ───────────────────────────────────────────────\n {\n name: 'Good Friday',\n resolve: (y) => easterPlus(y, -2),\n },\n\n // ── Easter Monday (easter + 1) ─────────────────────────────────────────────\n {\n name: 'Easter Monday',\n resolve: (y) => easterPlus(y, 1),\n },\n\n // ── Early May Bank Holiday — first Monday of May ───────────────────────────\n // Upstream splits this into three eras to remove May 4 1995 and May 4 2020,\n // which were displaced for VE-Day anniversaries.\n {\n name: 'Early May Bank Holiday (pre-1995)',\n validUntil: 1994,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 1),\n },\n {\n name: 'Early May Bank Holiday (1996-2019)',\n validFrom: 1996,\n validUntil: 2019,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 1),\n },\n {\n name: 'Early May Bank Holiday (2021+)',\n validFrom: 2021,\n resolve: (y) => firstMondayOnOrAfter(y, 5, 1),\n },\n\n // ── Spring Bank Holiday — last Monday of May ───────────────────────────────\n // Upstream splits to skip the regular Spring Bank in 2002 (Golden Jubilee),\n // 2012 (Diamond Jubilee), and 2022 (Platinum Jubilee). Those years have\n // adhoc Jubilee closures that replace it.\n {\n name: 'Spring Bank Holiday (pre-2002)',\n validUntil: 2001,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n {\n name: 'Spring Bank Holiday (2003-2011)',\n validFrom: 2003,\n validUntil: 2011,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n {\n name: 'Spring Bank Holiday (2013-2021)',\n validFrom: 2013,\n validUntil: 2021,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n {\n name: 'Spring Bank Holiday (2023+)',\n validFrom: 2023,\n resolve: (y) => lastWeekdayOfMonth(y, 5, MON),\n },\n\n // ── Summer Bank Holiday — last Monday of August ────────────────────────────\n {\n name: 'Summer Bank Holiday',\n resolve: (y) => lastWeekdayOfMonth(y, 8, MON),\n },\n\n // ── Christmas Day (Dec 25) ─────────────────────────────────────────────────\n // pandas Holiday with no observance — the literal Dec 25 is added; if it\n // falls on a weekend the WeekendChristmas rule below adds a substitute Mon/Tue.\n {\n name: 'Christmas Day',\n resolve: (y) => utcDate(y, 12, 25),\n },\n\n // ── Weekend Christmas substitute (Dec 27, only Mon/Tue) ────────────────────\n // If Dec 25 is Sat → Dec 27 is Mon (substitute Christmas).\n // If Dec 25 is Sun → Dec 27 is Tue (substitute Christmas, after Boxing Day Mon Dec 26).\n {\n name: 'Weekend Christmas',\n resolve: (y) => dropIfNotInDays(utcDate(y, 12, 27), MON_TUE),\n },\n\n // ── Boxing Day (Dec 26) ────────────────────────────────────────────────────\n {\n name: 'Boxing Day',\n resolve: (y) => utcDate(y, 12, 26),\n },\n\n // ── Weekend Boxing Day substitute (Dec 28, only Mon/Tue) ───────────────────\n // If Dec 26 is Sat → Dec 28 is Mon (substitute Boxing).\n // If Dec 26 is Sun → Dec 28 is Tue (substitute Boxing, after Christmas observed Mon Dec 27).\n {\n name: 'Weekend Boxing Day',\n resolve: (y) => dropIfNotInDays(utcDate(y, 12, 28), MON_TUE),\n },\n];\n\n// ─── Adhoc full-day closures ────────────────────────────────────────────────\n// Sourced verbatim from pandas_market_calendars/holidays/uk.py UniqueCloses.\nconst ADHOC_HOLIDAYS: ReadonlySet<string> = new Set<string>([\n // VE-Day anniversaries (Early May Bank Holiday displaced)\n '1995-05-08', // 50th anniversary\n '2020-05-08', // 75th anniversary\n\n // Queen Elizabeth II Jubilees\n '1977-06-07', // Silver Jubilee\n '2002-06-03', // Golden Jubilee — Spring Bank holiday moved\n '2002-06-04', // Golden Jubilee — additional\n '2012-06-04', // Diamond Jubilee — Spring Bank holiday moved\n '2012-06-05', // Diamond Jubilee — additional\n '2022-06-02', // Platinum Jubilee — Spring Bank holiday moved\n '2022-06-03', // Platinum Jubilee — additional\n\n // State funerals\n '2022-09-19', // Queen Elizabeth II\n\n // Royal weddings\n '1973-11-14', // Princess Anne and Mark Phillips\n '1981-07-29', // Prince Charles and Diana Spencer\n '2011-04-29', // Prince William and Catherine Middleton\n\n // Coronation of King Charles III\n '2023-05-08',\n\n // Miscellaneous\n '1999-12-31', // Eve of 3rd Millennium A.D.\n]);\n\n// ─── Rule-driven special closes (12:30 early close) ─────────────────────────\n// Upstream LSE: ChristmasEve and LSENewYearsEve — both use `previous_friday`\n// observance, so when Dec 24 / Dec 31 falls on a weekend the early close moves\n// to the prior Friday (a regular trading day). Modern session close is 16:30\n// so a 12:30 close is genuinely early.\nconst SPECIAL_CLOSES: ReadonlyArray<SpecialClose> = [\n {\n name: 'Christmas Eve early close',\n closeAt: { h: 12, m: 30 },\n resolve: (y) => dropIfNotInDays(previousFriday(utcDate(y, 12, 24)), WEEKDAYS_MON_FRI),\n },\n {\n name: \"New Year's Eve early close\",\n closeAt: { h: 12, m: 30 },\n resolve: (y) => dropIfNotInDays(previousFriday(utcDate(y, 12, 31)), WEEKDAYS_MON_FRI),\n },\n];\n\n// ─── Adhoc special closes (literal) ─────────────────────────────────────────\n// Upstream LSE has no adhoc early-close map.\nconst SPECIAL_CLOSES_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>();\n\n// ─── Special opens — none in upstream LSE ───────────────────────────────────\nconst SPECIAL_OPENS: ReadonlyArray<SpecialOpen> = [];\nconst SPECIAL_OPENS_ADHOC: AdhocTimeOverrides = new Map<string, TimeOfDay>();\n\n/**\n * London Stock Exchange (LSE) trading-day calendar. Faithful port of\n * `pandas_market_calendars`' `lse.py` and `holidays/uk.py`. Historical\n * coverage begins 1801-01-01, aligned with the start of the modern exchange\n * after the Banking and Financial Dealings Act 1971 codified the current\n * bank-holiday framework.\n *\n * **Session**: 08:00–16:30 Europe/London. The exchange observes BST (UTC+1)\n * in summer and GMT (UTC+0) in winter — DST handling is delegated to luxon via\n * the `Europe/London` IANA timezone, so wall-clock session times are stable\n * across the DST transition while their UTC equivalents shift by one hour.\n *\n * **Early closes**: Christmas Eve (Dec 24) and New Year's Eve (Dec 31) close\n * at 12:30. Both use `previous_friday` observance — when the calendar date\n * falls on a weekend, the early close moves to the prior Friday.\n *\n * **Era boundaries**: bank-holiday exceptions for Royal Jubilees and VE-Day\n * anniversaries are implemented by splitting affected `Spring Bank Holiday`\n * and `Early May Bank Holiday` rules into era-bounded shards (matching\n * upstream `start_date` / `end_date` markers) and adding the displaced dates\n * as adhoc closures.\n */\nexport class LSEExchangeCalendar extends ExchangeCalendar {\n readonly name = 'LSE';\n readonly tz = 'Europe/London';\n\n protected override regularHolidays(): ReadonlyArray<HolidayRule> {\n return REGULAR_HOLIDAYS;\n }\n\n protected override adhocHolidays(): ReadonlySet<string> {\n return ADHOC_HOLIDAYS;\n }\n\n protected override specialCloses(): ReadonlyArray<SpecialClose> {\n return SPECIAL_CLOSES;\n }\n\n protected override specialClosesAdhoc(): AdhocTimeOverrides {\n return SPECIAL_CLOSES_ADHOC;\n }\n\n protected override specialOpens(): ReadonlyArray<SpecialOpen> {\n return SPECIAL_OPENS;\n }\n\n protected override specialOpensAdhoc(): AdhocTimeOverrides {\n return SPECIAL_OPENS_ADHOC;\n }\n\n protected override regularOpen(_date: Date): TimeOfDay {\n return { h: 8, m: 0 };\n }\n\n protected override regularClose(_date: Date): TimeOfDay {\n return { h: 16, m: 30 };\n }\n\n protected override weekmask(_date: Date): ReadonlySet<number> {\n return WEEKDAYS_MON_FRI;\n }\n}\n","import { ExchangeCalendar } from './exchange-calendar';\nimport { NYSEExchangeCalendar } from './nyse';\nimport { LSEExchangeCalendar } from './lse';\n\n/**\n * Union of supported exchange names accepted by {@link getCalendar}.\n *\n * - `'NYSE'` — New York Stock Exchange (and NYSE-equivalent venues).\n * - `'LSE'` — London Stock Exchange.\n */\nexport type ExchangeName = 'NYSE' | 'LSE';\n\n/**\n * Returns a new instance of the {@link ExchangeCalendar} registered under\n * `name`. Acts as a simple factory / registry for the two built-in calendar\n * implementations.\n *\n * Supported exchange names: `'NYSE'` ({@link NYSEExchangeCalendar}) and\n * `'LSE'` ({@link LSEExchangeCalendar}). TypeScript's exhaustive switch\n * prevents unknown names from compiling.\n *\n * @param name - One of the supported {@link ExchangeName} values.\n * @returns A fresh `ExchangeCalendar` instance for the named exchange.\n *\n * @example\n * ```ts\n * import { getCalendar } from '@livefolio/sdk';\n *\n * const nyse = getCalendar('NYSE');\n * console.log(nyse.isOpen(new Date('2024-07-04'))); // false — US Independence Day\n *\n * const lse = getCalendar('LSE');\n * console.log(lse.isOpen(new Date('2024-12-25'))); // false — Christmas Day\n * ```\n */\nexport function getCalendar(name: ExchangeName): ExchangeCalendar {\n switch (name) {\n case 'NYSE':\n return new NYSEExchangeCalendar();\n case 'LSE':\n return new LSEExchangeCalendar();\n }\n}\n","import type { Calendar, Session } from '../interfaces/calendar';\nimport type { DateRange } from '../interfaces/types';\n\nconst MS_PER_DAY = 86_400_000;\n\nfunction midnightUtc(d: Date): Date {\n return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));\n}\n\n/**\n * 24/7 calendar where every day is a single session running midnight UTC to\n * the next midnight UTC. Suitable for crypto strategies (BTC, ETH) and any\n * always-on market.\n *\n * - `isOpen(t)` always returns `true`.\n * - `next(t)` returns midnight UTC of the day after `t`.\n * - `previous(t)` returns midnight UTC of the day before `t`.\n * - `sessions(range)` returns one Date per day in `[range.from, range.to)`.\n * - `schedule(range)` returns full Sessions with `open`/`close` at midnight UTC.\n * - `isEarlyClose(t)` always returns `false`.\n */\nexport class Crypto24x7Calendar implements Calendar {\n isOpen(_t: Date): boolean {\n return true;\n }\n\n next(t: Date): Date {\n return new Date(midnightUtc(t).getTime() + MS_PER_DAY);\n }\n\n previous(t: Date): Date {\n return new Date(midnightUtc(t).getTime() - MS_PER_DAY);\n }\n\n sessions(range: DateRange): ReadonlyArray<Date> {\n const out: Date[] = [];\n let cursor = midnightUtc(range.from);\n const end = range.to.getTime();\n while (cursor.getTime() < end) {\n out.push(cursor);\n cursor = new Date(cursor.getTime() + MS_PER_DAY);\n }\n return out;\n }\n\n schedule(range: DateRange): ReadonlyArray<Session> {\n return this.sessions(range).map((date) => ({\n date,\n open: date,\n close: new Date(date.getTime() + MS_PER_DAY),\n }));\n }\n\n isEarlyClose(_t: Date): boolean {\n return false;\n }\n}\n","export type {\n AssetRef,\n SyntheticAsset,\n RebalanceFrequency,\n RebalanceConfig,\n TacticalFeatureSpec,\n TacticalFeatureKind,\n FeatureRef,\n Tolerance,\n Comparison,\n ComparisonOp,\n AllocateNode,\n IfNode,\n RuleNode,\n TacticalSpec,\n RuleTreeState,\n} from './types';\nexport { evaluateRuleTree } from './evaluate-rule-tree';\nexport { evaluateFeatureSpecs } from './evaluate-feature-specs';\nexport { withSynthetics, withStreamingSynthetics, type WithStreamingSyntheticsOptions } from './synthetics';\nexport {\n fromSpec,\n isRebalanceDay,\n periodKey,\n _resetTacticalDeprecationWarningForTesting,\n type TacticalFeatures,\n type FromSpecOptions,\n} from './from-spec';\n","import type { TargetWeights } from '../strategy/reconcile';\nimport type { AssetId } from '../interfaces/types';\nimport type { Comparison, FeatureRef, RuleNode, RuleTreeState, Tolerance } from './types';\n\nfunction isRef(operand: FeatureRef | number): operand is FeatureRef {\n return typeof operand === 'object' && operand !== null && 'ref' in operand;\n}\n\nfunction resolve(operand: FeatureRef | number, values: ReadonlyMap<string, number>): number {\n if (!isRef(operand)) return operand;\n const v = values.get(operand.ref);\n if (v === undefined) {\n throw new Error(`evaluateRuleTree: feature \"${operand.ref}\" has no value`);\n }\n return v;\n}\n\nfunction rawCompare(op: Comparison['op'], l: number, r: number): boolean {\n switch (op) {\n case 'gt':\n return l > r;\n case 'lt':\n return l < r;\n case 'gte':\n return l >= r;\n case 'lte':\n return l <= r;\n case 'eq':\n return l === r;\n }\n}\n\nfunction band(right: number, tol: Tolerance): { lower: number; upper: number } {\n if (tol.mode === 'absolute') {\n return { lower: right - tol.value, upper: right + tol.value };\n }\n const factor = tol.value / 100;\n return { lower: right * (1 - factor), upper: right * (1 + factor) };\n}\n\nfunction evalComparison(\n cond: Comparison,\n values: ReadonlyMap<string, number>,\n state: RuleTreeState,\n outState: Map<string, 0 | 1>,\n): boolean {\n const l = resolve(cond.left, values);\n const r = resolve(cond.right, values);\n\n if (!cond.tolerance) {\n return rawCompare(cond.op, l, r);\n }\n if (cond.id === undefined) {\n throw new Error('evaluateRuleTree: comparison with tolerance requires id');\n }\n if (cond.op !== 'gt' && cond.op !== 'lt' && cond.op !== 'eq') {\n throw new Error(`evaluateRuleTree: tolerance is only supported for op gt/lt/eq, got ${cond.op}`);\n }\n\n const prev = state.get(cond.id);\n const { lower, upper } = band(r, cond.tolerance);\n let result: 0 | 1;\n\n if (cond.op === 'eq') {\n result = l >= lower && l <= upper ? 1 : 0;\n } else if (prev === undefined) {\n result = rawCompare(cond.op, l, r) ? 1 : 0;\n } else if (cond.op === 'gt') {\n if (prev === 1) result = l < lower ? 0 : 1;\n else result = l > upper ? 1 : 0;\n } else {\n if (prev === 1) result = l > upper ? 0 : 1;\n else result = l < lower ? 1 : 0;\n }\n\n outState.set(cond.id, result);\n return result === 1;\n}\n\nfunction walk(\n rules: RuleNode,\n values: ReadonlyMap<string, number>,\n state: RuleTreeState,\n outState: Map<string, 0 | 1>,\n): TargetWeights {\n if (rules.op === 'allocate') {\n const out = new Map<AssetId, number>();\n for (const [assetId, weight] of Object.entries(rules.weights)) {\n out.set(assetId, weight);\n }\n return out;\n }\n return evalComparison(rules.cond, values, state, outState)\n ? walk(rules.then, values, state, outState)\n : walk(rules.else, values, state, outState);\n}\n\n/**\n * Evaluates a {@link RuleNode} tree against a resolved set of feature values\n * and returns the target allocation weights together with the updated\n * hysteresis state.\n *\n * The tree is walked depth-first. At each {@link IfNode} the comparison is\n * evaluated (with hysteresis applied when `tolerance` and `id` are present)\n * and the walk follows either `then` or `else`. Evaluation terminates at an\n * {@link AllocateNode} whose `weights` map is returned verbatim.\n *\n * Hysteresis: when a {@link Comparison} carries a `tolerance`, the previous\n * outcome in `state` is used to decide whether to flip the result. The updated\n * outcomes for all visited comparisons are collected in the returned `state` map.\n *\n * Throws if a {@link FeatureRef} resolves to `undefined` (the caller should\n * suppress this with a guard or catch-block, as {@link fromSpec} does).\n *\n * @param rules - Root of the rule tree to evaluate.\n * @param values - Resolved feature values, keyed by feature id. Must contain\n * every `ref` string used in the tree; missing keys throw.\n * @param state - Prior hysteresis state from the previous evaluation step.\n * Pass an empty `Map` on the first call.\n * @returns An object with:\n * - `weights` — target portfolio weights as a `Map<AssetId, number>`.\n * - `state` — updated {@link RuleTreeState} to pass to the next step.\n *\n * @example\n * ```ts\n * import { evaluateRuleTree } from '@livefolio/sdk';\n * import type { RuleNode, RuleTreeState } from '@livefolio/sdk';\n *\n * const rules: RuleNode = {\n * op: 'if',\n * cond: { op: 'gt', left: { ref: 'price' }, right: { ref: 'sma200' } },\n * then: { op: 'allocate', weights: { SPY: 1 } },\n * else: { op: 'allocate', weights: { SHY: 1 } },\n * };\n *\n * let state: RuleTreeState = new Map();\n * const values = new Map([['price', 450], ['sma200', 420]]);\n * const result = evaluateRuleTree(rules, values, state);\n * // result.weights → Map { 'SPY' => 1 }\n * state = result.state;\n * ```\n */\nexport function evaluateRuleTree(\n rules: RuleNode,\n values: ReadonlyMap<string, number>,\n state: RuleTreeState = new Map(),\n): { weights: TargetWeights; state: RuleTreeState } {\n const next = new Map<string, 0 | 1>(state);\n const weights = walk(rules, values, state, next);\n return { weights, state: next };\n}\n","import type { Asset } from '../interfaces/types';\nimport type { AssetRef } from './types';\n\n/**\n * Resolves a spec-form {@link AssetRef} to a runtime {@link Asset}. The\n * `kind` field on the ref selects the variant; absent `kind` defaults to\n * `'equity'` for backward compatibility.\n *\n * Pure. No I/O. Used by `fromSpec`, `withSynthetics`, and\n * `evaluateFeatureSpecs` so the resolution rule lives in one place.\n */\nexport function resolveAssetRef(ref: AssetRef): Asset {\n if (ref.kind === 'macro') {\n return { kind: 'macro', id: ref.id, symbol: ref.symbol };\n }\n return ref.exchange !== undefined\n ? { kind: 'equity', id: ref.id, symbol: ref.symbol, exchange: ref.exchange }\n : { kind: 'equity', id: ref.id, symbol: ref.symbol };\n}\n","import type { Series } from '../interfaces/types';\nimport type { FeatureSpec } from '../features/spec';\nimport type { FeatureRuntime } from '../features/runtime';\nimport type { TacticalFeatureSpec } from './types';\nimport { resolveAssetRef } from './asset-ref';\n\nfunction toFeatureSpec(spec: TacticalFeatureSpec): FeatureSpec {\n switch (spec.kind) {\n case 'price':\n return { kind: 'price' };\n case 'sma':\n return { kind: 'sma', period: spec.period };\n case 'ema':\n return { kind: 'ema', period: spec.period };\n case 'rsi':\n return { kind: 'rsi', period: spec.period };\n case 'return':\n return spec.mode !== undefined\n ? { kind: 'return', period: spec.period, mode: spec.mode }\n : { kind: 'return', period: spec.period };\n case 'volatility':\n return { kind: 'volatility', period: spec.period };\n case 'drawdown':\n return { kind: 'drawdown', period: spec.period };\n }\n}\n\nfunction indexAtOrBefore(series: Series, t: Date): number {\n if (series.length === 0) return -1;\n const target = t.getTime();\n let lo = 0;\n let hi = series.length - 1;\n let ans = -1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (series[mid]!.t.getTime() <= target) {\n ans = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return ans;\n}\n\nfunction readDelayed(series: Series, t: Date, delay: number): number | undefined {\n const idx = indexAtOrBefore(series, t);\n if (idx < 0) return undefined;\n const target = idx - delay;\n if (target < 0) return undefined;\n return series[target]!.v;\n}\n\n/**\n * Resolves each {@link TacticalFeatureSpec} in `specs` to a scalar value as of\n * date `t` by calling into `runtime` and reading the series at the appropriate\n * bar index. All specs are computed in parallel via `Promise.all`.\n *\n * The returned map uses each spec's `id` as the key. The value is `undefined`\n * when the indicator series has no data on or before `t`, or when the `delay`\n * offset steps past the beginning of the series.\n *\n * Validation performed before dispatching to the runtime:\n * - Duplicate `id` values in `specs` throw immediately.\n * - A non-integer or negative `delay` throws immediately.\n *\n * @param specs - Ordered list of feature declarations from {@link TacticalSpec.features}.\n * @param runtime - Feature computation backend that owns the data feed and cache.\n * @param t - Evaluation date; the series is read at the latest bar on or before `t`.\n * @returns A map from feature id to resolved numeric value (`undefined` when unavailable).\n *\n * @example\n * ```ts\n * import { evaluateFeatureSpecs } from '@livefolio/sdk';\n * import type { TacticalFeatureSpec } from '@livefolio/sdk';\n *\n * const specs: TacticalFeatureSpec[] = [\n * { id: 'spy_sma200', kind: 'sma', asset: { id: 'SPY', symbol: 'SPY' }, period: 200 },\n * { id: 'spy_price', kind: 'price', asset: { id: 'SPY', symbol: 'SPY' } },\n * ];\n *\n * const values = await evaluateFeatureSpecs(specs, runtime, new Date('2024-06-01'));\n * // values.get('spy_price') → 528.3\n * ```\n */\nexport async function evaluateFeatureSpecs(\n specs: ReadonlyArray<TacticalFeatureSpec>,\n runtime: FeatureRuntime,\n t: Date,\n): Promise<Map<string, number | undefined>> {\n const seen = new Set<string>();\n for (const spec of specs) {\n if (seen.has(spec.id)) {\n throw new Error(`evaluateFeatureSpecs: duplicate feature id \"${spec.id}\"`);\n }\n seen.add(spec.id);\n const d = spec.delay;\n if (d !== undefined && (!Number.isInteger(d) || d < 0)) {\n throw new Error(`evaluateFeatureSpecs: delay must be a non-negative integer, got ${d}`);\n }\n }\n\n const entries = await Promise.all(\n specs.map(async (spec) => {\n const series = await runtime.compute(toFeatureSpec(spec), resolveAssetRef(spec.asset));\n const delay = spec.delay ?? 0;\n return [spec.id, readDelayed(series, t, delay)] as const;\n }),\n );\n\n return new Map(entries);\n}\n","import type { Asset, AssetId, Bar, DateRange, Frequency } from '../interfaces/types';\nimport type { DataFeed } from '../interfaces/data-feed';\nimport type { StreamingDataFeed, StreamingBar } from '../interfaces/streaming-data-feed';\nimport type { SyntheticAsset } from './types';\nimport { resolveAssetRef } from './asset-ref';\n\nconst TRADING_DAYS_PER_YEAR = 252;\n\n/**\n * Per-bar core of the synthesis formula. Shared by {@link withSynthetics}\n * (historical) and {@link withStreamingSynthetics} (live) so the two code\n * paths cannot drift.\n *\n * Returns the synthesized close given the previous underlying / synthetic\n * closes and the current underlying close. Cold start (either prev undefined)\n * returns `underlyingClose` so the first emitted bar anchors to the underlying.\n */\nfunction nextSynthClose(opts: {\n prevSynthClose: number | undefined;\n prevUnderlyingClose: number | undefined;\n underlyingClose: number;\n leverage: number;\n expense: number | undefined;\n}): number {\n const { prevSynthClose, prevUnderlyingClose, underlyingClose, leverage, expense } = opts;\n if (prevSynthClose === undefined || prevUnderlyingClose === undefined) {\n return underlyingClose;\n }\n const drag = (expense ?? 0) / TRADING_DAYS_PER_YEAR;\n const safe = Number.isFinite(prevUnderlyingClose) && prevUnderlyingClose !== 0;\n const r = safe ? (underlyingClose - prevUnderlyingClose) / prevUnderlyingClose : 0;\n return prevSynthClose * (1 + leverage * r) * (1 - drag);\n}\n\nasync function* synthesize(\n underlyingBars: AsyncIterable<Bar>,\n leverage: number,\n expense: number | undefined,\n): AsyncIterable<Bar> {\n let prevUnderlyingClose: number | undefined;\n let prevSynthClose: number | undefined;\n for await (const u of underlyingBars) {\n const close = nextSynthClose({\n prevSynthClose,\n prevUnderlyingClose,\n underlyingClose: u.close,\n leverage,\n expense,\n });\n yield {\n t: u.t,\n open: close,\n high: close,\n low: close,\n close,\n volume: u.volume,\n };\n prevUnderlyingClose = u.close;\n prevSynthClose = close;\n }\n}\n\n/**\n * Wraps a {@link DataFeed} so that requests for any asset whose `id` appears in\n * `synthetics` are intercepted and their bar stream is derived on-the-fly from\n * the corresponding `underlying` asset.\n *\n * The synthesized close price on each bar is computed as:\n * ```\n * close_t = close_{t-1} × (1 + leverage × underlyingReturn_t) × (1 − expense/252)\n * ```\n * The first bar in the stream uses the underlying close directly. OHLC fields\n * other than `close` are all set to the synthesized close (they are not\n * independently scaled); `volume` is passed through from the underlying bar.\n *\n * Non-synthetic assets are proxied transparently to the original `dataFeed`.\n * `fundamentals` and `events` methods, if present, are forwarded unchanged.\n *\n * Throws at construction time if `synthetics` contains duplicate `id` values.\n *\n * @param dataFeed - The real data feed to wrap.\n * @param synthetics - Synthetic asset definitions; typically `spec.synthetics ?? []`.\n * @returns A new {@link DataFeed} that intercepts synthetic asset ids.\n *\n * @example\n * ```ts\n * import { withSynthetics } from '@livefolio/sdk';\n * import type { SyntheticAsset } from '@livefolio/sdk';\n *\n * const leveraged: SyntheticAsset = {\n * id: 'SPY_3X', symbol: 'SPY3X',\n * underlying: { id: 'SPY', symbol: 'SPY' },\n * leverage: 3,\n * expense: 0.01,\n * };\n *\n * const feed = withSynthetics(realFeed, [leveraged]);\n * // Requesting bars for asset { id: 'SPY_3X', ... } now returns 3× leveraged returns.\n * ```\n */\nexport function withSynthetics(dataFeed: DataFeed, synthetics: ReadonlyArray<SyntheticAsset>): DataFeed {\n const byId = new Map<string, SyntheticAsset>();\n for (const s of synthetics) {\n if (byId.has(s.id)) {\n throw new Error(`withSynthetics: duplicate synthetic asset id \"${s.id}\"`);\n }\n byId.set(s.id, s);\n }\n\n const wrapped: DataFeed = {\n bars(asset: Asset, range: DateRange, freq: Frequency): AsyncIterable<Bar> {\n const synth = byId.get(asset.id);\n if (!synth) return dataFeed.bars(asset, range, freq);\n const underlying = resolveAssetRef(synth.underlying);\n return synthesize(dataFeed.bars(underlying, range, freq), synth.leverage, synth.expense);\n },\n };\n\n if (dataFeed.fundamentals) {\n wrapped.fundamentals = dataFeed.fundamentals.bind(dataFeed);\n }\n if (dataFeed.events) {\n wrapped.events = dataFeed.events.bind(dataFeed);\n }\n\n return wrapped;\n}\n\n/**\n * Options for {@link withStreamingSynthetics}.\n */\nexport interface WithStreamingSyntheticsOptions {\n /**\n * Last known close per asset id, used to seed `prevUnderlyingClose` and\n * `prevSynthClose` so the first live tick of a synthetic continues smoothly\n * from the end of its historical series.\n *\n * Build it from a {@link BacktestResult}:\n * ```ts\n * const seedLastCloses = new Map<AssetId, number>();\n * for (const [id, bars] of history.bars) {\n * const last = bars.at(-1)?.close;\n * if (last !== undefined) seedLastCloses.set(id, last);\n * }\n * ```\n *\n * Without seeding, the first synthesized tick lands on the underlying's\n * price and produces a visible jump in live preview.\n */\n seedLastCloses: ReadonlyMap<AssetId, number>;\n}\n\n/**\n * Streaming-feed counterpart to {@link withSynthetics}. Wraps a\n * {@link StreamingDataFeed} so that subscriptions for synthetic asset ids\n * resolve to upstream subscriptions on the underlying, with each underlying\n * tick re-emitted as a synthesized tick on the synthetic's id using the same\n * `(1 + leverage × r) × (1 − expense/252)` formula as the historical wrapper.\n *\n * Behavior:\n * - Non-synthetic ids in the `assets` argument pass through to the inner feed\n * unchanged.\n * - Underlyings that aren't directly in `assets` but are needed by a synthetic\n * are subscribed silently — only the synthesized ticks are yielded back to\n * the caller for those.\n * - Underlyings that the caller _did_ ask for in `assets` are yielded both as\n * the raw underlying tick and as the synthesized tick(s).\n *\n * Throws at construction time if `synthetics` contains duplicate `id` values.\n *\n * @example\n * ```ts\n * import { withStreamingSynthetics, runLive } from '@livefolio/sdk';\n *\n * const seedLastCloses = new Map<AssetId, number>();\n * for (const [id, bars] of history.bars) {\n * const last = bars.at(-1)?.close;\n * if (last !== undefined) seedLastCloses.set(id, last);\n * }\n *\n * const liveFeed = withStreamingSynthetics(rawStreamingFeed, spec.synthetics ?? [], {\n * seedLastCloses,\n * });\n *\n * for await (const event of runLive({ strategy, history, dataFeed: liveFeed, executor, calendar })) {\n * // …\n * }\n * ```\n */\nexport function withStreamingSynthetics(\n inner: StreamingDataFeed,\n synthetics: ReadonlyArray<SyntheticAsset>,\n opts: WithStreamingSyntheticsOptions,\n): StreamingDataFeed {\n const synthById = new Map<AssetId, SyntheticAsset>();\n for (const s of synthetics) {\n if (synthById.has(s.id)) {\n throw new Error(`withStreamingSynthetics: duplicate synthetic asset id \"${s.id}\"`);\n }\n synthById.set(s.id, s);\n }\n\n return {\n async *subscribe(assets: ReadonlyArray<Asset>): AsyncIterable<StreamingBar> {\n const passthroughIds = new Set<AssetId>();\n const requestedSynths: SyntheticAsset[] = [];\n const upstream: Asset[] = [];\n const upstreamSeen = new Set<AssetId>();\n\n for (const a of assets) {\n const synth = synthById.get(a.id);\n if (synth) {\n requestedSynths.push(synth);\n if (!upstreamSeen.has(synth.underlying.id)) {\n upstreamSeen.add(synth.underlying.id);\n upstream.push(resolveAssetRef(synth.underlying));\n }\n } else {\n passthroughIds.add(a.id);\n if (!upstreamSeen.has(a.id)) {\n upstreamSeen.add(a.id);\n upstream.push(a);\n }\n }\n }\n\n type SynthState = {\n synth: SyntheticAsset;\n asset: Asset;\n prevUnderlyingClose: number | undefined;\n prevSynthClose: number | undefined;\n };\n const synthsByUnderlying = new Map<AssetId, SynthState[]>();\n for (const s of requestedSynths) {\n const st: SynthState = {\n synth: s,\n asset: resolveAssetRef({ id: s.id, symbol: s.symbol }),\n prevUnderlyingClose: opts.seedLastCloses.get(s.underlying.id),\n prevSynthClose: opts.seedLastCloses.get(s.id),\n };\n const list = synthsByUnderlying.get(s.underlying.id) ?? [];\n list.push(st);\n synthsByUnderlying.set(s.underlying.id, list);\n }\n\n for await (const tick of inner.subscribe(upstream)) {\n if (passthroughIds.has(tick.asset.id)) {\n yield tick;\n }\n\n const states = synthsByUnderlying.get(tick.asset.id);\n if (!states) continue;\n\n const underlyingClose = tick.bar.close;\n for (const st of states) {\n const synthClose = nextSynthClose({\n prevSynthClose: st.prevSynthClose,\n prevUnderlyingClose: st.prevUnderlyingClose,\n underlyingClose,\n leverage: st.synth.leverage,\n expense: st.synth.expense,\n });\n yield {\n asset: st.asset,\n bar: {\n t: tick.bar.t,\n open: synthClose,\n high: synthClose,\n low: synthClose,\n close: synthClose,\n volume: tick.bar.volume,\n },\n };\n st.prevSynthClose = synthClose;\n }\n for (const st of states) {\n st.prevUnderlyingClose = underlyingClose;\n }\n }\n },\n };\n}\n","import type { Asset, AssetId } from '../interfaces/types';\nimport type { Calendar } from '../interfaces/calendar';\nimport type { Strategy } from '../strategy/types';\nimport { reconcile } from '../strategy/reconcile';\nimport type { FeatureRuntime } from '../features/runtime';\nimport { seriesAt } from '../features/series-utils';\nimport type { RebalanceFrequency, RuleTreeState, TacticalSpec } from './types';\nimport { resolveAssetRef } from './asset-ref';\nimport { evaluateRuleTree } from './evaluate-rule-tree';\nimport { evaluateFeatureSpecs } from './evaluate-feature-specs';\n\nlet _warnedV0 = false;\n\n/** Test-only: reset the once-per-process deprecation gate. */\nexport function _resetTacticalDeprecationWarningForTesting(): void {\n _warnedV0 = false;\n}\n\n/**\n * The feature bundle computed on each rebalance step and passed to the rule\n * tree. Produced by the `features` method of the {@link Strategy} returned by\n * {@link fromSpec}.\n *\n * - `values` — named indicator results keyed by the `id` field of each\n * {@link TacticalFeatureSpec}. A value is `undefined` when the indicator\n * cannot be computed for that bar (e.g. insufficient history).\n * - `prices` — most-recent closing prices for each asset in the universe,\n * keyed by asset ID.\n */\nexport type TacticalFeatures = {\n values: ReadonlyMap<string, number | undefined>;\n prices: ReadonlyMap<AssetId, number>;\n};\n\n/**\n * Runtime dependencies required by {@link fromSpec} to hydrate a\n * {@link TacticalSpec} into a runnable {@link Strategy}.\n */\nexport type FromSpecOptions = {\n /** Feature computation backend — wraps the data feed and caching layer. */\n runtime: FeatureRuntime;\n /** Exchange calendar used to gate rebalance days via {@link isRebalanceDay}. */\n calendar: Calendar;\n};\n\nfunction validateSynthetics(spec: TacticalSpec): void {\n const synths = spec.synthetics ?? [];\n if (synths.length === 0) return;\n\n const universeIds = new Set(spec.universe.map((u) => u.id));\n\n for (const s of synths) {\n if (s.underlying.id === s.id) {\n throw new Error(`fromSpec: synthetic asset \"${s.id}\" cannot reference itself as underlying`);\n }\n const u = spec.universe.find((x) => x.id === s.id);\n if (!u) continue;\n if (u.symbol !== s.symbol) {\n throw new Error(`fromSpec: synthetic asset id \"${s.id}\" collides with a universe AssetRef of a different symbol`);\n }\n if (!universeIds.has(s.underlying.id)) {\n throw new Error(\n `fromSpec: synthetic asset id \"${s.id}\" collides with a universe AssetRef whose underlying is not declared in the universe`,\n );\n }\n }\n}\n\n/**\n * Returns a stable string key that identifies the rebalance period containing\n * date `t` for the given `freq`. Two dates that map to the same key belong to\n * the same period and therefore produce the same rebalance decision. Used\n * internally by {@link isRebalanceDay} to detect period boundaries.\n *\n * @param t - The date to classify.\n * @param freq - Rebalance cadence (see {@link RebalanceFrequency}).\n * @returns A compact string such as `'2024-3'` (monthly), `'2024-W14'`\n * (weekly), or `'2024-1'` (quarterly Q2).\n */\nexport function periodKey(t: Date, freq: RebalanceFrequency): string {\n const y = t.getUTCFullYear();\n const m = t.getUTCMonth();\n switch (freq) {\n case 'Daily':\n return `${y}-${m}-${t.getUTCDate()}`;\n case 'Weekly': {\n const thu = new Date(t);\n thu.setUTCDate(thu.getUTCDate() + 3 - ((thu.getUTCDay() + 6) % 7));\n const yearStart = new Date(Date.UTC(thu.getUTCFullYear(), 0, 1));\n const weekNo = Math.ceil(((thu.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7);\n return `${thu.getUTCFullYear()}-W${weekNo}`;\n }\n case 'Monthly':\n return `${y}-${m}`;\n case 'Quarterly':\n return `${y}-Q${Math.floor(m / 3)}`;\n case 'Yearly':\n return `${y}`;\n }\n}\n\n/**\n * Returns `true` when `t` is the last trading day of its rebalance period\n * according to `freq` and `calendar`. The check is: `periodKey(t) !== periodKey(next(t))`.\n * For `'Daily'` cadence this always returns `true`.\n *\n * @param t - Current trading day (must itself be a trading day).\n * @param freq - Rebalance cadence (see {@link RebalanceFrequency}).\n * @param calendar - Exchange calendar used to find the next trading day.\n * @returns `true` if today is the last day of its period and orders should be issued.\n */\nexport function isRebalanceDay(t: Date, freq: RebalanceFrequency, calendar: Calendar): boolean {\n if (freq === 'Daily') return true;\n const next = calendar.next(t);\n return periodKey(t, freq) !== periodKey(next, freq);\n}\n\n/**\n * Hydrates a plain {@link TacticalSpec} data object into a runnable\n * {@link Strategy} that `runBacktest` can drive step-by-step.\n *\n * State is threaded explicitly through `build` via the\n * {@link Strategy | `Strategy<F, S>.build`} signature. `initialState()` returns\n * an empty {@link RuleTreeState} Map; the runtime is responsible for storing and\n * forwarding the state between calls. This design makes `build` a pure function\n * of its inputs — calling it twice with identical arguments produces identical\n * outputs, enabling snapshot/restore for preview-builds in live mode.\n *\n * Validation performed at construction time:\n * - A `'tactical/v0'` `kind` emits a one-time deprecation warning to `console.warn`.\n * - Synthetic assets are checked for self-reference, symbol collisions, and\n * missing universe entries (see internal `validateSynthetics`).\n *\n * @param spec - The declarative strategy spec.\n * @param opts - Runtime dependencies (feature backend and calendar).\n * @returns A {@link Strategy} whose `features` method fetches indicator values\n * and whose `build` method converts them to rebalance orders.\n *\n * @example\n * ```ts\n * import { fromSpec, MemoryFeatureCache, NYSEExchangeCalendar } from '@livefolio/sdk';\n * import { FeatureRuntime } from '@livefolio/sdk/features';\n *\n * const calendar = new NYSEExchangeCalendar();\n * const cache = new MemoryFeatureCache();\n * const runtime = new FeatureRuntime({ feed: myDataFeed, cache });\n *\n * const strategy = fromSpec(mySpec, { runtime, calendar });\n * ```\n */\nexport function fromSpec(spec: TacticalSpec, opts: FromSpecOptions): Strategy<TacticalFeatures, RuleTreeState> {\n if (spec.kind === 'tactical/v0' && !_warnedV0) {\n _warnedV0 = true;\n\n console.warn(\n '[@livefolio/sdk] tactical/v0 is deprecated; migrate to tactical/v1. ' +\n 'The two are byte-for-byte equivalent. This warning fires once per process.',\n );\n }\n validateSynthetics(spec);\n const universe: ReadonlyArray<Asset> = spec.universe.map(resolveAssetRef);\n const assetsById = new Map<AssetId, Asset>();\n for (const a of universe) assetsById.set(a.id, a);\n for (const s of spec.synthetics ?? []) {\n if (!assetsById.has(s.id)) {\n assetsById.set(s.id, { kind: 'equity', id: s.id, symbol: s.symbol });\n }\n }\n const { runtime, calendar } = opts;\n const cadence: RebalanceFrequency = spec.rebalance?.frequency ?? 'Daily';\n\n return {\n universe: () => universe,\n\n features: async (_u, _p, t) => {\n const [values, priceEntries] = await Promise.all([\n evaluateFeatureSpecs(spec.features, runtime, t),\n Promise.all(\n universe.map(async (asset) => {\n const s = await runtime.compute({ kind: 'price' }, asset);\n return [asset.id, seriesAt(s, t)] as const;\n }),\n ),\n ]);\n const prices = new Map<AssetId, number>();\n for (const [id, v] of priceEntries) {\n if (v !== undefined) prices.set(id, v);\n }\n return { values, prices };\n },\n\n initialState: () => new Map() as RuleTreeState,\n\n build: (features, portfolio, state, t) => {\n if (!isRebalanceDay(t, cadence, calendar)) {\n return { orders: [], state };\n }\n\n const defined = new Map<string, number>();\n for (const [id, v] of features.values) {\n if (v !== undefined) defined.set(id, v);\n }\n let evaluated;\n try {\n evaluated = evaluateRuleTree(spec.rules, defined, state);\n } catch (e) {\n if (e instanceof Error && /has no value/.test(e.message)) {\n return { orders: [], state };\n }\n throw e;\n }\n for (const assetId of evaluated.weights.keys()) {\n if (!features.prices.has(assetId)) {\n return { orders: [], state };\n }\n }\n return {\n orders: reconcile(evaluated.weights, portfolio, features.prices, assetsById),\n state: evaluated.state,\n };\n },\n };\n}\n","export * from './indicators';\nexport { collectBars, barsToSeries, seriesAt } from './series-utils';\nexport type { BarField } from './series-utils';\nexport {\n defineFeature,\n getFeatureCompute,\n paramsHash,\n type FeatureSpec,\n type FeatureKind,\n type ComputeFn,\n} from './spec';\nexport { FeatureRuntime } from './runtime';\nexport type { FeatureRuntimeOptions } from './runtime';\n"],"mappings":"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A,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAA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EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG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,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC;AAAA;AAAA,EAE/B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;AAAA;AAAA,EAE9B,CAAC,cAAc,EAAE,GAAG,IAAI,GAAG,EAA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","names":["MS_PER_DAY","MS_PER_DAY","MS_PER_DAY","SUN","MON","TUE","SAT","utcDate","WEEKDAYS_MON_FRI","REGULAR_HOLIDAYS","ADHOC_HOLIDAYS","SPECIAL_CLOSES","SPECIAL_CLOSES_ADHOC","SPECIAL_OPENS","SPECIAL_OPENS_ADHOC","MS_PER_DAY"]}