Skip to main content

Documentation Index

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

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

One of Backtest Kit’s core design goals is strategy portability: the exact same addStrategySchema code you validated against historical data runs live without any modifications. Switching from a backtest to a live bot is a single function call — replace Backtest.background() with Live.background(). The engine, signal validation, risk checks, VWAP pricing, and persistence layer all stay identical.
Live mode places real orders on a connected exchange. Always confirm your exchange API keys are correctly scoped (trade permissions only, no withdrawal), and validate your strategy thoroughly in paper mode before going live.

Starting the Live Bot

import { Live, listenSignalLive } from 'backtest-kit';

Live.background('BTCUSDT', {
  strategyName: 'llm-strategy',
  exchangeName: 'binance',
});

listenSignalLive((event) => console.log(event));
Live.background() launches an infinite loop that calls your strategy’s getSignal at the configured interval, monitors open positions for TP/SL hits, and persists state to disk after every mutation. It returns a stop function you can call to trigger a graceful shutdown.
const stop = await Live.background('BTCUSDT', {
  strategyName: 'llm-strategy',
  exchangeName: 'binance',
});

// Graceful shutdown — waits for any open position to close before exiting
process.on('SIGINT', () => stop());

Crash Recovery

Live trading processes can be killed at any time — by a server restart, an OOM event, or a deployment. Backtest Kit uses a persist-and-restart pattern to make crashes transparent.
1

State is written atomically on every mutation

Every signal state change (opened → active → closed) is persisted to disk using atomic file writes via writeFileAtomic. A partial write can never corrupt the saved state.
./dump/signal/{strategyName}/{symbol}.json
2

waitForInit reloads state on the next startup

When Live.background() starts, ClientStrategy.waitForInit() reads the last persisted signal from disk before the first tick. If the process was killed mid-trade, the engine resumes monitoring that open position exactly where it left off — no duplicate entries, no missed closes.
3

Retry on the next tick

If a network error or exchange timeout prevents the engine from checking a position, it logs the error and retries on the next scheduled tick. There is no manual intervention required.
waitForInit is wrapped with singleshot from functools-kit — it runs exactly once per ClientStrategy instance regardless of how many ticks fire concurrently during startup.

Paper Trading

Paper mode runs the full live engine — same strategy, same signal validation, same state machine — but without placing real orders on the exchange. Use it to validate your strategy and broker adapter wiring before committing real capital.
# Via CLI
npx @backtest-kit/cli --paper ./content/my_strategy.ts

# Or via Docker
MODE=paper SYMBOL=BTCUSDT STRATEGY_FILE=./content/my.strategy.ts docker-compose up -d
Always run at least 30 days of paper trading before switching to live mode. Track win rate (>60%), Sharpe ratio (>1.0), and maximum drawdown (<20%) against your backtest numbers to confirm the strategy is not overfit.

Monitoring and Alerts

Event Listeners

Backtest Kit emits typed events for every lifecycle stage. Subscribe before calling Live.background().
import {
  listenSignalLive,
  listenError,
  listenRisk,
  listenPartialProfit,
  listenPartialLoss,
} from 'backtest-kit';

// All signal lifecycle events: idle | opened | active | scheduled | cancelled | closed
listenSignalLive(async (event) => {
  if (event.action === 'closed') {
    await Live.dump(event.strategyName);
    console.log(`Closed with PNL: ${event.pnl.pnlPercentage.toFixed(2)}%`);
  }
});

// Exchange API errors, validation failures, LLM timeouts
listenError((error) => {
  console.error('Bot error:', error.message);
});

// Fired when a risk validation rejects a signal
listenRisk(({ riskName, reason }) => {
  console.warn(`Risk rejected by [${riskName}]: ${reason}`);
});

// Profit milestone crossings — use to scale out
listenPartialProfit(({ symbol, price, level }) => {
  console.log(`${symbol} hit +${level}% profit at ${price}`);
});

// Loss milestone crossings — use to cut exposure
listenPartialLoss(({ symbol, price, level }) => {
  console.log(`${symbol} hit -${level}% loss at ${price}`);
});

Web Dashboard

The @backtest-kit/ui package provides a full-stack dashboard with candlestick charts, signal tracking, and real-time event feeds. Launch it with the --ui flag:
npx @backtest-kit/cli --live --ui ./content/my_strategy.ts
# Dashboard opens at http://localhost:60050

Telegram Alerts

Formatted trade notifications with price charts can be sent directly to a Telegram channel:
npx @backtest-kit/cli --live --telegram ./content/my_strategy.ts

Live Report Generation

Reports accumulate across the entire running session and can be generated or refreshed at any time.
// Save a Markdown report to ./dump/backtest/
await Live.dump('llm-strategy');

// Get the Markdown report as a string
const markdown = await Live.getReport('llm-strategy');

// Get the raw statistics object
const stats = await Live.getData('llm-strategy');

// Clear accumulated data (e.g. between sessions)
await Live.clear('llm-strategy');
The live report includes all signal states — idle, opened, active, closed — and replaces previous entries for the same signalId, so you always see one row per signal in its latest state.

Multi-Symbol Live Trading

The same Live.background() call works across multiple symbols simultaneously. Each symbol runs its own signal lifecycle and state persistence independently.
const symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT'];

for (const symbol of symbols) {
  Live.background(symbol, {
    strategyName: 'llm-strategy',
    exchangeName: 'binance',
  });
}

// Track active positions across all symbols
const positions = new Map();

listenSignalLive((event) => {
  if (event.action === 'opened')  positions.set(event.symbol, event.signal);
  if (event.action === 'closed')  positions.delete(event.symbol);
  console.log(`Active positions: ${positions.size}`);
});

Configuration Reference

VariableDescription
BINANCE_API_KEYExchange API key (trade permissions only)
BINANCE_API_SECRETExchange API secret
CC_MONGO_CONNECTION_STRINGMongoDB URI for production persistence (optional)
CC_REDIS_HOSTRedis host for O(1) state cache (optional)
OLLAMA_API_KEYLLM provider key if using @backtest-kit/ollama
TELEGRAM_BOT_TOKENTelegram bot token for --telegram alerts
Exchange API keys must be available as environment variables before calling Live.background(). Never hard-code credentials in strategy files. Scope keys to trade permissions only — never enable withdrawals on bot API keys.

Build docs developers (and LLMs) love