@livefolio/sdk 0.3.2 → 0.3.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.d.ts CHANGED
@@ -112,9 +112,9 @@ declare class IndicatorHandle {
112
112
  private _getLatestSeriesDate;
113
113
  private _ensureFresh;
114
114
  private _sync;
115
+ private _fetchRawBarsForIncremental;
115
116
  private _upsertSeries;
116
117
  private _querySeriesFromDb;
117
- withMarket(market: MarketProvider): IndicatorHandle;
118
118
  /**
119
119
  * Apply leverage compounding to a raw bar series, anchored to a stored
120
120
  * leveraged value. Used by both `_sync` and `computeAt` so they stay
@@ -130,37 +130,72 @@ declare class IndicatorHandle {
130
130
  */
131
131
  private _applyLeverage;
132
132
  /**
133
- * Compute the indicator's value at `date` using the given market (typically
134
- * an overlay market for pre-close preview). Pure no writes to storage.
133
+ * Compute the indicator's value at `date` without persisting anything, with
134
+ * optional live-quote `overrides` keyed by raw market symbol (the same symbol
135
+ * space `MarketProvider.fetchBars` uses — ticker symbols for Price/SMA/etc.,
136
+ * `^VIX` / `^VIX3M` for macro, FRED series IDs like `DGS3MO` for Treasury).
135
137
  *
136
- * For fetched types (yahoo/fred): fetches a small window of bars from
137
- * `market`, applies leverage compounding anchored to the stored leveraged
138
- * value at the bar before `date`.
139
- * For computed types (SMA, RSI, etc.): fetches enough raw price bars to
140
- * cover the indicator's lookback from `market`, applies leverage anchored
141
- * to the stored value just before the fetch window, runs the computation,
142
- * and returns the value at `date`.
143
- * For Threshold: returns the threshold constant.
144
- * For calendar: computes calendar value from the trading days list.
145
- * Returns null if the value cannot be computed.
138
+ * Bars for the underlying symbol are resolved storage-first when the market
139
+ * hasn't yet produced bars for `date` (trading day still open), and storage
140
+ * is the fallback whenever the remote fetch fails — see `_resolveRawBars`.
141
+ *
142
+ * For Threshold: returns the threshold constant. For calendar types: computed
143
+ * from `tradingDays.getRange()`. For all others: `_resolveRawBars` leverage
144
+ * compounding (if any) lookback-specific computation. Returns null if the
145
+ * value cannot be computed.
146
+ */
147
+ computeAt(date: string, overrides?: Record<string, number>): Promise<number | null>;
148
+ /**
149
+ * Raw (unleveraged) bars for `symbol` up through `date`, with the live quote
150
+ * from `overrides[symbol]` (if any) spliced in at `date`.
151
+ *
152
+ * Decision policy:
153
+ * - `date` > `tradingDays.getLatestClosed()`: market has nothing for that
154
+ * day yet — skip the remote fetch entirely and read from storage.
155
+ * - otherwise: try `this._market.fetchBars(symbol, from)`. On failure, fall
156
+ * back to storage — upstream HTTP providers (Yahoo / FRED) are flaky.
157
+ *
158
+ * After the base is resolved, `overrides[symbol]` is spliced at `date`
159
+ * (replaces the existing bar, or is appended in-order). When no override is
160
+ * present but `date` isn't in the base bars, the last known value is carried
161
+ * forward to `date` — this preserves the fallbackMissingQuotes behaviour the
162
+ * old overlay exposed so leverage compounding / computations always have a
163
+ * point at `date` to land on.
164
+ */
165
+ private _resolveRawBars;
166
+ /**
167
+ * Resolve the single raw (unleveraged) value for `symbol` at `date`.
168
+ * Returns the override directly when present; otherwise delegates to
169
+ * `_resolveRawBars` with a one-day window and picks the matching bar.
146
170
  */
147
- computeAt(market: MarketProvider, date: string): Promise<number | null>;
171
+ private _resolveRawBarAt;
172
+ /**
173
+ * Resolve raw (unleveraged) bars for a market symbol from storage. Maps:
174
+ * - `^VIX` → the VIX indicator's stored series
175
+ * - `^VIX3M` → the VIX3M indicator's stored series
176
+ * - `DGS*` → the matching Treasury-tenor indicator's stored series
177
+ * - anything else → the `Price` indicator for that ticker symbol with
178
+ * `leverage = 1` (the raw contract that `MarketProvider.fetchBars` has).
179
+ *
180
+ * Returns `[]` when the resolved indicator has no stored bars yet.
181
+ */
182
+ private _readStoredBars;
148
183
  series(range?: DateRange): Promise<DailyBar[]>;
149
184
  private _syntheticThresholdSeries;
150
185
  value(date?: string): Promise<number | null>;
151
186
  /**
152
- * Read-only preview of the indicator series that includes an in-memory bar
153
- * at `date` computed via `computeAt` against a quote-overlay market. Does
187
+ * Read-only preview of the indicator series with an in-memory bar at `date`
188
+ * computed via `computeAt` with the supplied live-quote `overrides`. Does
154
189
  * NOT write to `indicators_series`. Safe to call before market close.
155
190
  *
156
- * @param date - Target trading day whose value is computed in-memory from
157
- * the overridden quotes. Must be in `tradingDays.getRange()`.
158
- * @param quoteOverrides - Raw (unleveraged) quotes keyed by ticker symbol.
159
- * Symbols omitted here fall back to yesterday's close via the overlay.
191
+ * @param date - Target trading day whose value is computed in-memory.
192
+ * Must be in `tradingDays.getRange()`.
193
+ * @param overrides - Raw (unleveraged) quotes keyed by market symbol.
194
+ * Symbols omitted fall back to the last known value (see `_resolveRawBars`).
160
195
  * @param range - Optional filter applied to the returned bars.
161
196
  * @returns Stored historical bars plus (or with) today's in-memory value.
162
197
  */
163
- previewSeries(date: string, quoteOverrides: Record<string, number>, range?: DateRange): Promise<DailyBar[]>;
198
+ previewSeries(date: string, overrides: Record<string, number>, range?: DateRange): Promise<DailyBar[]>;
164
199
  }
165
200
 
166
201
  interface StorageProvider {
@@ -194,9 +229,16 @@ interface StorageProvider {
194
229
  id: number;
195
230
  }>;
196
231
  getSeries(indicatorId: number, range?: DateRange): Promise<DailyBar[]>;
197
- writeSeries(indicatorId: number, bars: DailyBar[]): Promise<void>;
232
+ writeSeries(indicatorId: number, bars: DailyBar[], opts?: {
233
+ metadata?: unknown;
234
+ }): Promise<void>;
198
235
  getLatestSeriesDate(indicatorId: number): Promise<string | null>;
199
236
  getValue(indicatorId: number, date?: string): Promise<number | null>;
237
+ getLatestBar(indicatorId: number): Promise<{
238
+ date: string;
239
+ value: number;
240
+ metadata: unknown;
241
+ } | null>;
200
242
  };
201
243
  signals: {
202
244
  upsert(identity: {
@@ -253,13 +295,12 @@ declare class SignalHandle {
253
295
  readonly comparison: Comparison;
254
296
  readonly tolerance: number;
255
297
  private _storage;
256
- private _market;
257
298
  private _resolvedId;
258
299
  private _resolving;
259
300
  private _cachedSeries;
260
301
  private _cachedAsOf;
261
302
  private _syncing;
262
- constructor(storage: StorageProvider, market: MarketProvider, identity: SignalIdentity);
303
+ constructor(storage: StorageProvider, _market: MarketProvider, identity: SignalIdentity);
263
304
  get id(): number;
264
305
  resolve(): Promise<{
265
306
  id: number;
@@ -270,14 +311,16 @@ declare class SignalHandle {
270
311
  private _getLatestSignalSeriesDate;
271
312
  private _getLastSignalValue;
272
313
  private _ensureFresh;
314
+ private _isSingleBarFastPath;
273
315
  private _sync;
316
+ private _evaluateOneBar;
274
317
  private _upsertSeries;
275
318
  private _querySeriesFromDb;
276
- withMarket(market: MarketProvider): SignalHandle;
277
319
  /**
278
- * Compute the signal's boolean value at `date` using the given market
279
- * (typically an overlay market for pre-close preview). Pure — no writes.
280
- * Returns null if either indicator cannot produce a value at `date`.
320
+ * Compute the signal's boolean value at `date` without persisting anything,
321
+ * with optional live-quote `overrides` that are routed through each
322
+ * indicator's `computeAt`. Returns null if either indicator cannot produce
323
+ * a value at `date`.
281
324
  *
282
325
  * @param prevBool - The signal's boolean value at the bar immediately
283
326
  * preceding `date`, used for hysteresis when `tolerance > 0`. If not
@@ -285,19 +328,19 @@ declare class SignalHandle {
285
328
  * standalone callers). On the preview path `_evaluate` passes this from
286
329
  * the in-memory `dateMap` so we never read stale storage.
287
330
  */
288
- computeAt(market: MarketProvider, date: string, prevBool?: boolean | null): Promise<boolean | null>;
331
+ computeAt(date: string, overrides?: Record<string, number>, prevBool?: boolean | null): Promise<boolean | null>;
289
332
  series(range?: DateRange): Promise<DailyBar[]>;
290
333
  value(date?: string): Promise<number | null>;
291
334
  /**
292
335
  * Read-only preview of the signal series with an in-memory bar at `date`
293
- * computed via `computeAt` against a quote-overlay market. Does NOT write
294
- * to `signals_series`.
336
+ * computed via `computeAt` with the supplied live-quote `overrides`. Does
337
+ * NOT write to `signals_series`.
295
338
  *
296
339
  * @param date - Target trading day whose boolean is computed in-memory.
297
- * @param quoteOverrides - Raw (unleveraged) quotes keyed by ticker symbol.
340
+ * @param overrides - Raw (unleveraged) quotes keyed by market symbol.
298
341
  * @param range - Optional filter applied to the returned bars.
299
342
  */
300
- previewSeries(date: string, quoteOverrides: Record<string, number>, range?: DateRange): Promise<DailyBar[]>;
343
+ previewSeries(date: string, overrides: Record<string, number>, range?: DateRange): Promise<DailyBar[]>;
301
344
  }
302
345
 
303
346
  declare class AllocationHandle {
@@ -311,6 +354,11 @@ declare class AllocationHandle {
311
354
  id: number;
312
355
  }>;
313
356
  static fromResolved(storage: StorageProvider, id: number, holdings: [TickerHandle, number][]): AllocationHandle;
357
+ toJSON(): Array<{
358
+ symbol: string;
359
+ leverage: number;
360
+ weight: number;
361
+ }>;
314
362
  private _doResolve;
315
363
  }
316
364
 
@@ -348,6 +396,45 @@ interface FinalState {
348
396
  closePrices: Record<string, number>;
349
397
  leveragedPrices: Record<string, number>;
350
398
  }
399
+ /** Per-signal slice of a live strategy snapshot. */
400
+ interface LiveSignalState {
401
+ indicator1: {
402
+ value: number | null;
403
+ date: string | null;
404
+ };
405
+ indicator2: {
406
+ value: number | null;
407
+ date: string | null;
408
+ };
409
+ isTrue: boolean;
410
+ }
411
+ /** Per-rule collection of live signal states, in the same order as the rule's `when` list. */
412
+ interface LiveRuleState {
413
+ signals: LiveSignalState[];
414
+ }
415
+ /** Full live strategy view for a single evaluation date — no portfolio info. */
416
+ interface StrategyLiveState {
417
+ allocation: AllocationHandle | null;
418
+ activeRuleIndex: number;
419
+ rules: LiveRuleState[];
420
+ }
421
+ /**
422
+ * Combined live state returned by `SimulationHandle.pushAndPreview`: both the
423
+ * portfolio snapshot from a `push` and the strategy evaluation at the target
424
+ * date under the accumulated live-quote overrides.
425
+ */
426
+ interface LivePreviewState extends StrategyLiveState {
427
+ snapshot: PortfolioSnapshot;
428
+ }
429
+ /**
430
+ * Callback shape that `SimulationHandle.pushAndPreview` delegates to. Exists
431
+ * purely to break the circular import between `SimulationHandle` (in this
432
+ * file) and `StrategyHandle` (which creates simulations) — a strategy passes
433
+ * a bound `(date, overrides) => previewLiveState(...)` into the handle.
434
+ */
435
+ interface LiveEvaluator {
436
+ previewLiveState(date: string, overrides: Record<string, number>): Promise<StrategyLiveState>;
437
+ }
351
438
  declare class SimulationHandle {
352
439
  readonly series: DailyBar[];
353
440
  readonly trades: Trade[];
@@ -358,8 +445,30 @@ declare class SimulationHandle {
358
445
  private _lastLeveragedPrices;
359
446
  private _currentLeveragedPrices;
360
447
  private _lastDate;
361
- constructor(series: DailyBar[], trades: Trade[], startingPortfolio: PortfolioHandle, finalState?: FinalState);
448
+ private _pushedQuotes;
449
+ private _liveEvaluator;
450
+ constructor(series: DailyBar[], trades: Trade[], startingPortfolio: PortfolioHandle, finalState?: FinalState, liveEvaluator?: LiveEvaluator);
362
451
  push(...prices: [TickerHandle, number][]): PortfolioSnapshot;
452
+ /**
453
+ * One-call live update. Feeds portfolio-relevant ticker prices into `push`
454
+ * (derived from `quotes` via the running portfolio's holdings), accumulates
455
+ * every symbol in `quotes` into an internal override map so macro symbols
456
+ * (e.g. `^VIX`) persist across ticks, then delegates to the simulation's
457
+ * strategy for rule / signal / indicator evaluation at `date`.
458
+ *
459
+ * Without a live evaluator attached, returns just the portfolio snapshot
460
+ * with allocation/rules/signals empty.
461
+ *
462
+ * @param quotes Symbol → raw live price. Portfolio tickers flow through
463
+ * `push` for leveraged-equity math; non-portfolio symbols are still
464
+ * layered into the overlay so indicators can see them.
465
+ * @param options.date Target trading day to evaluate against. Defaults to
466
+ * the current UTC ISO date; callers with non-UTC semantics or after-hours
467
+ * rollover should supply their own.
468
+ */
469
+ pushAndPreview(quotes: Record<string, number>, options?: {
470
+ date?: string;
471
+ }): Promise<LivePreviewState>;
363
472
  }
364
473
 
365
474
  interface StrategyRule {
@@ -398,6 +507,7 @@ declare class StrategyHandle {
398
507
  get freq(): TradingFreq;
399
508
  get offset(): number;
400
509
  get rules(): StrategyRule[];
510
+ marketSymbols(): string[];
401
511
  resolve(): Promise<{
402
512
  id: number;
403
513
  }>;
@@ -410,16 +520,22 @@ declare class StrategyHandle {
410
520
  /**
411
521
  * Pure evaluate — runs the same pipeline as _sync but returns the computed
412
522
  * evaluation instead of persisting. Used by both _sync (post-close write
413
- * path) and previewAllocation (pre-close read-only path).
523
+ * path) and the public preview methods (pre-close read-only path).
524
+ *
525
+ * When `overrides` is `undefined` we take the write path — syncing signals
526
+ * through storage as normal. When `overrides` is provided (even an empty
527
+ * map) we take the read-only preview path: historical signal bars come
528
+ * straight from storage, today's bar is computed in-memory via
529
+ * `signal.computeAt(date, overrides, prevBool)`, and nothing is written.
414
530
  *
415
- * The `market === this._market` identity check is what distinguishes the two
416
- * paths: when they are the same object the method takes the write path (syncing
417
- * signals through storage as normal); when they differ it takes the read-only
418
- * preview path (historical bars from storage, today's value computed in-memory
419
- * via `computeAt`). Callers that want the preview path must therefore supply a
420
- * *different* market object — for example one produced by `createQuoteOverlay`.
531
+ * Incremental path: when a strategy checkpoint exists (`getLatestSeriesDate`
532
+ * returns non-null), only the window (lastDate, limitDate] is processed.
533
+ * The current allocation is carried forward from `getLatestAllocationId`.
534
+ * Bootstrap: when no checkpoint exists, falls back to `_evaluateCold` which
535
+ * runs the full-history evaluation.
421
536
  */
422
537
  private _evaluate;
538
+ private _evaluateCold;
423
539
  private _querySeriesFromDb;
424
540
  series(range?: DateRange): Promise<StrategyBar[]>;
425
541
  value(date?: string): Promise<AllocationHandle | null>;
@@ -430,23 +546,34 @@ declare class StrategyHandle {
430
546
  * signals_series, or indicators_series. Safe to call before market close.
431
547
  *
432
548
  * @param date - The trading day to preview (must be in tradingDays.getRange()).
433
- * @param quoteOverrides - Raw (unleveraged) live prices keyed by ticker symbol.
434
- * Symbols absent from this map will fall back to yesterday's close via the
435
- * quote overlay.
549
+ * @param overrides - Raw (unleveraged) live prices keyed by market symbol.
550
+ * Symbols absent from this map fall back to the last stored value
551
+ * (see `IndicatorHandle._resolveRawBars`).
436
552
  * @returns The AllocationHandle for `date`, or null if the strategy has no
437
553
  * evaluable entry for that date.
438
554
  */
439
- previewAllocation(date: string, quoteOverrides: Record<string, number>): Promise<AllocationHandle | null>;
555
+ previewAllocation(date: string, overrides: Record<string, number>): Promise<AllocationHandle | null>;
440
556
  /**
441
557
  * Read-only preview of the strategy's allocation series including `date`.
442
558
  * Returns stored historical allocations plus an in-memory bar at `date`
443
- * computed via the same overlay path as `previewAllocation`.
559
+ * computed via the same overrides-based preview path as `previewAllocation`.
444
560
  *
445
- * @param date - Target trading day to splice in-memory via overlay market.
446
- * @param quoteOverrides - Raw (unleveraged) quotes keyed by ticker symbol.
561
+ * @param date - Target trading day to splice in-memory.
562
+ * @param overrides - Raw (unleveraged) quotes keyed by market symbol.
447
563
  * @param range - Optional filter applied to the returned bars.
448
564
  */
449
- previewSeries(date: string, quoteOverrides: Record<string, number>, range?: DateRange): Promise<StrategyBar[]>;
565
+ previewSeries(date: string, overrides: Record<string, number>, range?: DateRange): Promise<StrategyBar[]>;
566
+ /**
567
+ * Full live strategy view at `date` under live-quote `overrides`: the active
568
+ * allocation, the index of the rule that fired (or fallback), and per-rule
569
+ * per-signal indicator values + truth. Computed entirely through the
570
+ * overrides preview path — no writes to any `*_series` tables.
571
+ *
572
+ * Threshold indicators have their date suppressed (`null`) since their
573
+ * synthetic series runs over every trading day in storage including future
574
+ * dates and would report a far-future date for the last bar.
575
+ */
576
+ previewLiveState(date: string, overrides: Record<string, number>): Promise<StrategyLiveState>;
450
577
  private _fetchPricesForTickers;
451
578
  private _fetchRawClosePrices;
452
579
  }
@@ -498,4 +625,8 @@ interface PriceStream {
498
625
  close(): void;
499
626
  }
500
627
 
501
- export { AllocationHandle, type Comparison, type DailyBar, type DateRange, IndicatorHandle, type IndicatorIdentity, type IndicatorType, type LivefolioClient, type LivefolioClientOptions, type MarketProvider, PortfolioHandle, type PortfolioSnapshot, type PriceStream, SignalHandle, type SignalIdentity, type SimulateOptions, SimulationHandle, type StorageProvider, type StrategyBar, type StrategyDefinition, StrategyHandle, type StrategyOptions, type StrategyReferenceData, type StrategyRule, type StrategyRuleDefinition, type StrategySeriesEntry, type StreamStatus, TickerHandle, type Trade, type TradingFreq, type Unit, createClient };
628
+ declare function allocationsEqual(a: AllocationHandle | null, b: AllocationHandle | null): boolean;
629
+
630
+ declare function computeRebalanceDates(tradingDays: string[], freq: TradingFreq, offset: number): Set<string>;
631
+
632
+ export { AllocationHandle, type Comparison, type DailyBar, type DateRange, IndicatorHandle, type IndicatorIdentity, type IndicatorType, type LiveEvaluator, type LivePreviewState, type LiveRuleState, type LiveSignalState, type LivefolioClient, type LivefolioClientOptions, type MarketProvider, PortfolioHandle, type PortfolioSnapshot, type PriceStream, SignalHandle, type SignalIdentity, type SimulateOptions, SimulationHandle, type StorageProvider, type StrategyBar, type StrategyDefinition, StrategyHandle, type StrategyLiveState, type StrategyOptions, type StrategyReferenceData, type StrategyRule, type StrategyRuleDefinition, type StrategySeriesEntry, type StreamStatus, TickerHandle, type Trade, type TradingFreq, type Unit, allocationsEqual, computeRebalanceDates, createClient };