The backtest harness replays a full month of Telegram signals against Binance historical OHLCV data, applying the complete LLM pipeline — crawler, parser, RiskOutline verdict, andDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/backtest-kit/backtest-ollama-crontab/llms.txt
Use this file to discover all available pages before exploring further.
getSignal entry logic — to every candle in the frame. Under the hood, backtest-kit drives the simulation: it advances time minute by minute, calls getSignal for each tracked symbol, and simulates position open and close against real Binance price history fetched via ccxt. This means the backtest exercises the exact same code paths as live mode; only the time source and data transport differ.
Frame Configuration
The backtest frame is registered incontent/jan_2026.strategy/modules/backtest.module.ts using addFrameSchema:
| Field | Value | Description |
|---|---|---|
frameName | "jan_2026_frame" | Identifier used to reference this frame in backtest-kit |
interval | "1m" | Candle granularity — the simulation ticks forward one minute at a time |
startDate | 2026-01-01T00:00:00Z | Inclusive start of the replay window |
endDate | 2026-01-31T23:59:59Z | Inclusive end of the replay window — covers all of January 2026 |
note | "January 2026" | Human-readable label shown in the backtest-kit UI |
Exchange Schema
The exchange is registered withaddExchangeSchema using ccxt.binance configured for spot markets. The same module also sets two global backtest parameters via setConfig:
singleshot) and reused for every candle fetch. Key points:
defaultType: "spot"— all symbols are traded on the Binance spot market.enableRateLimit: true— ccxt’s built-in rate limiter is active; the backtest will pause between requests automatically to avoid hitting Binance API limits.CC_MAX_STOPLOSS_DISTANCE_PERCENT: 100— allows stop-losses up to 100% away from entry, effectively disabling the maximum stop-loss distance guard.CC_BREAKEVEN_THRESHOLD: 0— breakeven move-to-cost is disabled; the stop-loss is never automatically adjusted to entry.
Candle Fetching
getCandles calls fetchOHLCV on the Binance public API. since is passed as a Unix millisecond timestamp. The returned array is mapped from the ccxt tuple format to the named-field shape expected by backtest-kit.
Price and Quantity Formatting
formatPrice and formatQuantity read tick size and step size directly from the Binance market info (loaded by loadMarkets()), then apply roundTicks — a backtest-kit utility that rounds a number to the nearest tick — to produce exchange-compliant values. If market info is unavailable, they fall back to ccxt’s built-in priceToPrecision / amountToPrecision.
Running the Backtest
From the repository root, launch the backtest with the backtest-kit UI enabled:| Flag | Description |
|---|---|
--backtest | Runs in historical simulation mode; time advances through the configured frame instead of the system clock |
--ui | Launches the backtest-kit web interface so you can inspect trade charts, equity curves, and per-trade details in real time |
--entry | Path to the strategy file — backtest-kit loads this module at startup, executing all addStrategySchema, addExchangeSchema, addFrameSchema, and Cron.register calls |
What Happens During a Backtest
Strategy module loaded
The CLI loads
jan_2026.strategy.ts and its imported modules (backtest.module.ts). All schema registration calls — addStrategySchema, addExchangeSchema, addFrameSchema — execute synchronously, wiring up the exchange, frame window, and getSignal handler.One-shot cron fires
The
"backtest-prepare-data" cron handler fires immediately (no interval means one-shot). It calls core.crawlerMainService.crawlBacktestFrame(when), which fetches all Telegram messages from the configured channel for the January 1–31 window and upserts them into the parser-items MongoDB collection.Signal job processes parser items
signalJobSubject.next() fires, triggering SignalJobService.run(). For every unvisited parser-items row, SignalLogicService invokes the RiskOutline: the LLM receives the parsed signal fields plus 1m/15m candle metrics and returns a riskAction: "skip" | "follow" verdict. Results are written to screen-items.getSignal called per symbol per candle
backtest-kit advances the simulation clock minute by minute through the frame. At each tick,
getSignal(symbol, when, currentPrice) is called for every symbol in CC_SYMBOL_LIST. It queries screen-items for the most recent LLM-enriched signal in the last four hours; if riskAction === "follow" and price is inside the entry zone, a position is opened.Position lifecycle simulated
backtest-kit simulates execution against the Binance OHLCV data: the position opens when the candle’s price range first intersects the entry zone, and closes when price hits
targets[2] (TP3) or signal.stoploss (SL). With minuteEstimatedTime: Infinity, there is no time-based forced exit.Dump Directory
During the RiskOutline step, each LLM response is saved to disk by theonValidDocument callback. Outline results land in ./dump/outline/risk/, one JSON file per processed signal. Each file contains the full LLM response — riskAction, riskSureLevel, riskConfidence, riskDescription, and riskReasoning — alongside the input metrics packet that was sent to the model. These files are useful for auditing which signals the LLM vetoed and why, without querying the database.
January 2026 Results
Running this backtest with the Ollama LLM risk filter active produced the following outcome over 17 trades in January 2026, compared to 22 trades without the filter:| Metric | Without Ollama | With Ollama | Δ |
|---|---|---|---|
| Total trades | 22 | 17 | −5 trades skipped |
| Total PNL | +52.22% | +68.90% | +16.68 pp |
| Winrate | 68% | 82% | +14 pp |
| Wins / Losses | 15 / 7 | 14 / 3 | −4 losing trades |
| Sharpe Ratio | +0.309 | +0.512 | +0.203 |
| Profit factor | 2.73 | 6.37 | +3.64 |
| Expectancy per trade | +$2.37 | +$4.05 | +$1.68 |
TRX SHORT Jan 06 −4.24%, NEAR LONG Jan 07 −4.26%, TRX SHORT Jan 12 −4.45%, SOL LONG Jan 28 −4.34%), avoiding a combined −17.29% in losses.
The backtest requires a live internet connection. It fetches Binance historical OHLCV candles via the public ccxt API and pulls Telegram messages from the configured channel via MTProto — both happen at runtime, not from a local cache. Ensure
session.txt is in place and the CC_OLLAMA_URL endpoint is reachable before starting.