Skip to content

@livefolio/sdk / runBacktest

Function: runBacktest()

runBacktest<F, S>(opts): Promise<BacktestResult<S>>

Defined in: strategy/run-backtest.ts:189

Drives a Strategy over a historical date range and returns a full audit trail of orders, fills, and portfolio states.

The simulation loop:

  1. Enumerate trading sessions via opts.calendar.sessions(opts.range).
  2. Call strategy.initialState?.() once to seed the carry-over state.
  3. For each session t, call strategy.universe(t, portfolio).
  4. Await strategy.features(universe, portfolio, t).
  5. Call strategy.build(features, portfolio, state, t) to obtain orders and the next state value. Both legacy Order[] returns and new { orders, state } returns are normalised — the legacy form leaves state unchanged.
  6. Await opts.executor.submit(orders, t, portfolio) to obtain fills.
  7. Apply fills to the portfolio with applyFills.
  8. Append a BacktestSnapshot and advance to the next session.

The portfolio is never mutated in place; each session receives the immutable result of the previous session's applyFills.

Type Parameters

Type ParameterDefault type
F extends Readonly<Record<string, unknown>>Readonly<Record<string, unknown>>
Sunknown

Parameters

ParameterTypeDescription
optsRunBacktestOptions<F, S>Backtest configuration. See RunBacktestOptions.

Returns

Promise<BacktestResult<S>>

A promise that resolves to a BacktestResult containing one snapshot per trading session, the final portfolio state, and the final strategy state (finalState). Returns { snapshots: [], finalPortfolio: opts.initialPortfolio, finalState: undefined } when the calendar has no sessions in the requested range.

Example

ts
import {
  runBacktest,
  fromSpec,
  MemoryFeatureCache,
  BacktestExecutor,
  NYSEExchangeCalendar,
  FeatureRuntime,
} from '@livefolio/sdk';

const calendar = new NYSEExchangeCalendar();
const range = { from: new Date('2023-01-01'), to: new Date('2023-12-31') };
const featureCache = new MemoryFeatureCache();
const runtime = new FeatureRuntime({ dataFeed, featureCache, range, freq: '1d' });

const strategy = fromSpec(myTacticalSpec, { runtime, calendar });

const result = await runBacktest({
  strategy,
  range,
  initialPortfolio: { cash: 100_000, positions: [] },
  dataFeed,
  executor: new BacktestExecutor({ dataFeed }),
  calendar,
  featureCache,
  freq: '1d',
});

console.log(result.finalPortfolio.cash);
console.log(result.snapshots.length); // one entry per NYSE trading day in 2023

Released under the MIT License.