Documentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit-minio-s3-docker/llms.txt
Use this file to discover all available pages before exploring further.
The project supports three operating modes selected at startup. Backtest replays historical OHLCV data through the strategy logic against the MinIO candle cache, producing a full simulation of every trade. Paper mode subscribes to a live market feed and runs the strategy in real time, but executes no real orders — useful for validating a strategy before committing capital. Live mode runs the strategy on the live feed and routes order execution to the real exchange.
CLI mode
Run the compiled bundle directly with Node.js using the npm run start script. The mode is controlled by a combination of --entry (required to actually start) plus one of --backtest, --live, or --paper. The --ui ./build/index.cjs argument is consumed by the @backtest-kit/cli layer to load your compiled strategy bundle — it is not parsed by getArgs.
Replays January 2026 OHLCV data for TRXUSDT through the strategy. MinIO and Redis must be running before this command.npm run start -- --entry --backtest --ui ./build/index.cjs
Connects to Binance in real time and executes real orders. Requires valid API credentials in your environment.npm run start -- --entry --live --ui ./build/index.cjs
Connects to Binance in real time and simulates orders without real execution.npm run start -- --entry --paper --ui ./build/index.cjs
getArgs
All three entry points share the same argument parsing logic. getArgs is a singleshot-wrapped function that parses process.argv once and caches the result for the lifetime of the process:
// src/helpers/getArgs.ts
import { singleshot } from "functools-kit";
import { parseArgs } from "util";
export const getArgs = singleshot(() => {
const { values } = parseArgs({
args: process.argv,
options: {
entry: {
type: "boolean",
default: false,
},
backtest: {
type: "boolean",
default: false,
},
live: {
type: "boolean",
default: false,
},
paper: {
type: "boolean",
default: false,
},
},
strict: false,
allowPositionals: true,
});
return { values };
});
Available CLI flags parsed by getArgs:
| Flag | Type | Default | Description |
|---|
--entry | boolean | false | Required gate — without this flag, all entry points exit immediately |
--backtest | boolean | false | Activate backtest mode |
--live | boolean | false | Activate live trading mode |
--paper | boolean | false | Activate paper trading mode |
strict: false means unknown flags (such as --ui) are silently ignored by parseArgs — they are consumed by the @backtest-kit/cli layer instead.
Entry point logic
Each mode has a dedicated entry point in src/main/. All three follow the same guard pattern: check --entry, check the mode flag, wait for Redis, then hand off to the runner.
// src/main/backtest.ts
import { getArgs } from "../helpers/getArgs";
import ioc from "../lib";
import { Backtest, warmCandles, waitForReady } from "backtest-kit";
import { ExchangeName } from "../enum/ExchangeName";
import { FrameName } from "../enum/FrameName";
import { StrategyName } from "../enum/StrategyName";
const main = async () => {
const { values } = getArgs();
if (!values.entry) {
return;
}
if (!values.backtest) {
return;
}
{
await ioc.redisService.waitForInit();
}
await waitForReady(true);
await warmCandles({
exchangeName: ExchangeName.CCXT,
from: new Date("2026-01-01T00:00:00Z"),
to: new Date("2026-01-31T23:59:59Z"),
interval: "1m",
symbol: "TRXUSDT",
})
Backtest.background("TRXUSDT", {
exchangeName: ExchangeName.CCXT,
frameName: FrameName.Jan2026Frame,
strategyName: StrategyName.Jan2026Strategy,
});
};
main();
// src/main/live.ts
import { getArgs } from "../helpers/getArgs";
import ioc from "../lib";
import { Live, waitForReady } from "backtest-kit";
import { ExchangeName } from "../enum/ExchangeName";
import { StrategyName } from "../enum/StrategyName";
const main = async () => {
const { values } = getArgs();
if (!values.entry) {
return;
}
if (!values.live) {
return;
}
{
await ioc.redisService.waitForInit();
}
await waitForReady(false);
Live.background("TRXUSDT", {
exchangeName: ExchangeName.CCXT,
strategyName: StrategyName.Jan2026Strategy,
});
};
main();
// src/main/paper.ts
import { getArgs } from "../helpers/getArgs";
import ioc from "../lib";
import { Live, waitForReady } from "backtest-kit";
import { ExchangeName } from "../enum/ExchangeName";
import { StrategyName } from "../enum/StrategyName";
const main = async () => {
const { values } = getArgs();
if (!values.entry) {
return;
}
if (!values.paper) {
return;
}
{
await ioc.redisService.waitForInit();
}
await waitForReady(false);
Live.background("TRXUSDT", {
exchangeName: ExchangeName.CCXT,
strategyName: StrategyName.Jan2026Strategy,
});
};
main();
Key differences between the entry points:
| Step | Backtest | Live | Paper |
|---|
| Runner | Backtest.background() | Live.background() | Live.background() |
waitForReady argument | true (backtest mode) | false (live mode) | false (live mode) |
warmCandles | ✅ Required | ❌ Not needed | ❌ Not needed |
frameName | Required | Not used | Not used |
Paper mode uses Live.background() rather than a dedicated Paper runner — the distinction is that paper mode does not send real orders to the exchange. This is controlled at the exchange adapter level, not at the runner level.
warmCandles
Before starting a backtest replay, warmCandles pre-fetches all OHLCV data for the backtest window from Binance and stores it in MinIO. This one-time cache fill means the replay loop reads only from local MinIO storage — no live exchange calls are made during the replay itself, making backtests fast and deterministic.
await warmCandles({
exchangeName: ExchangeName.CCXT,
from: new Date("2026-01-01T00:00:00Z"),
to: new Date("2026-01-31T23:59:59Z"),
interval: "1m",
symbol: "TRXUSDT",
})
warmCandles respects the PersistCandleAdapter insert-only contract: if a candle for a given (symbol, interval, timestamp) already exists in MinIO, it is skipped without a PUT — re-running warmCandles is safe and idempotent.
Docker mode
The full Docker deploy bundles the strategy runner and backtest-kit into a single container managed by the root docker-compose.yaml. The mode is selected via the MODE environment variable instead of CLI flags.
Use the npm convenience scripts to build and start the full stack:
npm run start:docker
npm run stop:docker
The start:docker script builds the strategy bundle and starts the container stack with MODE=backtest, ENTRY=1, UI=1, and STRATEGY_FILE=./build/index.cjs set via cross-env. To run in a different mode, pass the MODE variable explicitly before the script or edit the command directly:
cross-env MODE=live ENTRY=1 UI=1 STRATEGY_FILE=./build/index.cjs docker-compose up -d
docker-compose logs -f
Available npm scripts from package.json:
| Script | Description |
|---|
npm run start | Build and run via the @backtest-kit/cli Node.js entrypoint |
npm run start:debug | Build and run with --inspect-brk for attaching a debugger |
npm run start:repl | Build and start an interactive Node.js REPL with the strategy bundle loaded |
npm run start:docker | Build and start the full Docker Compose stack in backtest mode |
npm run stop:docker | Tear down the Docker Compose stack |
Environment variable mapping
When running in Docker mode, CLI flags are replaced by environment variables that the @backtest-kit/cli container image reads:
| CLI flag | Environment variable | Description |
|---|
--entry | ENTRY=1 | Required gate — enables the entry point |
--backtest | MODE=backtest | Activate backtest mode |
--live | MODE=live | Activate live trading mode |
--paper | MODE=paper | Activate paper trading mode |
--ui <path> | UI=1 + STRATEGY_FILE=<path> | Enable web UI and set strategy bundle path |
Additional container variables:
| Variable | Description |
|---|
CC_MINIO_ENDPOINT | MinIO host (default: host.docker.internal) |
CC_MINIO_PORT | MinIO S3 port (default: 9000) |
CC_MINIO_ACCESSKEY | MinIO access key |
CC_MINIO_SECRETKEY | MinIO secret key |
CC_REDIS_HOST | Redis host (default: host.docker.internal) |
CC_REDIS_PORT | Redis port (default: 6379) |
CC_REDIS_USER | Redis username (default: default) |
CC_REDIS_PASSWORD | Redis password |
SYMBOL | Override trading symbol |
STRATEGY | Override strategy name |
EXCHANGE | Override exchange name |
FRAME | Override frame name |
TELEGRAM | Enable Telegram notifications |
VERBOSE | Enable verbose logging |
NO_CACHE | Disable candle cache reads |
NO_FLUSH | Disable state flush on startup |
The backtest-kit container exposes a healthcheck endpoint at http://localhost:60050/api/v1/health/health_check. Docker pings this endpoint every 30 seconds. If the process has not yet started the web UI (e.g., UI=1 is not set), the healthcheck will fail — set UI=1 or disable the healthcheck in docker-compose.yaml for headless runs.