Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/backtest-kit/backtest-monorepo-parallel/llms.txt

Use this file to discover all available pages before exploring further.

Mode B is the focused development path. When you run a backtest without the --entry flag, packages/main/src/main/backtest.ts detects the missing flag and early-returns immediately. Control passes entirely to @backtest-kit/cli, which loads the strategy file you provide as a path argument, calls the addStrategySchema, addExchangeSchema, and addFrameSchema functions registered inside it, and runs a single-symbol backtest based on whatever the strategy file dispatches. There is no parallel runner, no CC_SYMBOL_LIST iteration, and no cross-symbol coordination — just one strategy against one symbol at a time.

The exact command

npm run start -- --backtest --ui --noCache ./content/apr_2026.strategy/apr_2026.strategy.ts

Flag breakdown

FlagTypeEffect
--backtestbooleanActivates backtest mode globally
--uibooleanStarts the @backtest-kit/ui web dashboard on port 60050
--noCachebooleanSkips candle pre-warming; the CLI fetches candles on demand as the replay advances
(no --entry)backtest.ts early-returns; @backtest-kit/cli takes over as the sole runner

How the CLI runner works

1

Load the strategy file by path

@backtest-kit/cli receives the positional argument (./content/apr_2026.strategy/apr_2026.strategy.ts) and evaluates it at runtime. The file’s top-level code runs immediately, registering all schemas with the engine.The apr_2026.strategy.ts file registers the core strategy logic:
addStrategySchema({
  strategyName: "apr_2026_strategy",
  getSignal: async (symbol, when, currentPrice) => {
    return {
      position: "long",
      ...Position.moonbag({
        position: "long",
        currentPrice,
        percentStopLoss: HARD_STOP,
      }),
      minuteEstimatedTime: 50,
      cost: LADDER_STEP_COST,
    };
  },
});
Its companion backtest.module.ts registers the exchange and frame:
addFrameSchema({
  frameName: "apr_2026_frame",
  interval: "1m",
  startDate: new Date("2026-04-01T00:00:00Z"),
  endDate: new Date("2026-04-27T00:00:00Z"),
});
2

Resolve schemas

The CLI calls listStrategySchema(), listExchangeSchema(), and listFrameSchema() to retrieve the registered schemas. If any are missing, the run fails with a clear error message before any replay begins.
3

Run a single-symbol backtest

The CLI starts a backtest for the symbol configured inside the strategy file, replaying candles from startDate to endDate at a 1-minute interval. Only one symbol context is active at a time — there is no concurrency overhead.

When to use Mode B

Developing a new strategy

Mode B gives you a tight feedback loop. Edit the strategy file, rerun the command, and see results without waiting for 8 other symbols to finish.

Debugging a specific symbol

When a signal misfires on a single pair, Mode B lets you focus log output and UI state on exactly that symbol without noise from the rest of the list.

Low-infrastructure environments

Running 9 parallel contexts multiplies Mongo write pressure and Redis round-trips. Mode B is far lighter and works fine on machines with constrained resources.

Validating a frame change

When you update startDate or endDate in addFrameSchema, Mode B confirms the new window is sane before you commit to a full multi-symbol cache pre-warm with --cache.
Build and validate your strategy in Mode B first. Once listenActivePing produces the signal pattern you expect for a single symbol, scale up to Mode A with --entry --cache to verify the same logic holds across all 9 symbols simultaneously.

Mode A vs Mode B comparison

AspectMode A (parallel)Mode B (single)
--entry flagRequiredMust be omitted
--cache flagRecommended on first runNot needed (--noCache typical)
Runnerpackages/main/src/main/backtest.ts@backtest-kit/cli built-in runner
SymbolsAll in CC_SYMBOL_LIST (default: 9)One, as dispatched by the strategy file
Concurrency9 contexts, single Node process1 context
Mongo / Redis pressureHigh (9 concurrent writers)Low (1 writer)
Best forMeasuring aggregate performance, regression testingStrategy authoring, focused debugging, CI
Measured replay speed≈ 6 326× real-time (aggregate)Single-symbol fraction of Mode A throughput

The early-return gate

The mechanism that switches between modes is a simple guard at the top of backtest.ts. Because --entry is false by default, omitting it is all that is needed to hand control to @backtest-kit/cli:
const main = async () => {
  const { values } = getArgs();

  if (!values.entry) {
    return;          // Mode B: nothing to do here, CLI takes over
  }

  if (!values.backtest) {
    return;
  }

  // … Mode A parallel runner continues below
};
This means the two modes are mutually exclusive by design — there is no runtime toggle, no environment variable to flip mid-run, and no shared mutable state between them.

Build docs developers (and LLMs) love