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.

This strategy achieved +16.99% PnL during February 2026 — a month when BTC fell −16.4% peak-to-trough. Rather than following technical indicators that lagged the macro-driven selloff, the strategy read live news and macro headlines every few hours, asked a local LLM to classify the environment as bullish, bearish, or wait, and held the position until either a trailing take-profit fired or the sentiment flipped to an opposing forecast. The LLM correctly held SHORT bias for nearly the entire month while BTC fell from ~79kto 79k to ~60k, switched briefly to LONG on a recovery bounce, then reversed back to SHORT on geopolitical escalation news.

Backtest results (February 2026)

MetricValue
BTC price move (month)−16.4% (78,73478,734 → 65,879)
Strategy net PNL+16.99%
Sharpe ratio0.25
Win rate68.8% (11 / 16 trades)
Profit factor2.25
Avg win+2.78%
Avg loss−2.72%
Best trade+14.28% (SHORT Feb 4: 75,74075,740 → 64,657)
Worst trade−3.41% (stop-loss)
Direction split14 × SHORT / 2 × LONG
Closed by trailing take-profit9
Closed by stop-loss4
Closed by sentiment flip3
The strategy’s peak equity reached +23.67% after trade 5 (Feb 6). Late-month stop-losses on two trades pulled the final result back to +16.99%.
The full reference implementation is available at github.com/tripolskypetr/backtest-kit/tree/master/example/content/feb_2026.strategy. It includes the Tavily fetch logic, the Ollama prompt format, and the trailing exit implementation.

How it works

The strategy operates on two independent clocks: a periodic news refresh job and a per-candle signal check.
A Cron job registered with interval: '4h' fires once per virtual 4-hour boundary. It calls the Tavily search API with queries covering Bitcoin, macro economics, and geopolitical risk, collects the returned headlines and summaries, and passes the text to a local Ollama LLM instance.The LLM prompt asks for a single-word forecast: bullish, bearish, or wait. The response is written to a module-level variable currentForecast. In backtest mode, the cron job fires at the correct virtual timestamps — the same code runs live by simply calling Cron.enable() before starting the engine.
import { Cron } from 'backtest-kit';

let currentForecast: 'bullish' | 'bearish' | 'wait' = 'wait';

Cron.register({
  name: 'news-refresh',
  interval: '4h',
  handler: async ({ symbol, when }) => {
    // 1. Fetch headlines from Tavily
    // 2. Pass to Ollama: "Based on the following news, is the BTC
    //    outlook bullish, bearish, or wait?"
    // 3. currentForecast = parsed LLM response
  },
});

Cron.enable();
addStrategySchema registers the strategy on a 15-minute interval. On every candle, getSignal reads currentForecast:
  • 'wait' → return null (no new position opened)
  • 'bullish' → open LONG with trailing take-profit (1% drawdown from peak) and stop-loss (1% from entry)
  • 'bearish' → open SHORT with the same trailing/stop parameters
If a position is already open and getSignal returns the same direction, the signal is silently rejected (no duplicate). If there is no open position and the forecast is wait, nothing happens until the next news refresh changes the forecast.
While a position is open, each listenActivePing tick checks whether the latest currentForecast conflicts with the open position direction. A bullish forecast while SHORT, or a bearish forecast while LONG, triggers commitClosePending to close the current position at market. The next call to getSignal will then open a fresh position in the new direction.Three of the 16 February trades were closed this way — including the Feb 9 SHORT that was exited for −0.01% when signals briefly became mixed before the LLM returned to a SHORT bias.
Positions that are not flipped by a sentiment change close via two exit conditions registered on listenActivePing:
  • Trailing take-profit: tracks the peak profit seen since entry. If the current profit falls more than 1% below the peak, the position closes. This lets winning trades run during fast trending moves (the +14.28% Feb 4 SHORT rode most of a $11k BTC decline) while cutting off retracements.
  • Stop-loss: a fixed 1% from the entry price. Four trades in February hit this level.

Install dependencies

npm install @backtest-kit/ollama agent-swarm-kit backtest-kit
@backtest-kit/ollama provides a unified multi-provider LLM interface supporting Ollama, OpenAI, Claude, DeepSeek, Grok, Mistral, and others. agent-swarm-kit is required as a peer dependency for the agent coordination layer.

Strategy code pattern

import { addStrategySchema, getCandles, commitClosePending, Cron } from 'backtest-kit';

let currentForecast: 'bullish' | 'bearish' | 'wait' = 'wait';

// Cron job: refresh news sentiment every 4 hours
Cron.register({
  name: 'news-refresh',
  interval: '4h',
  handler: async ({ symbol, when }) => {
    // fetch headlines and analyze with LLM
    // currentForecast = await analyzeSentiment(headlines);
  },
});
Cron.enable();

addStrategySchema({
  strategyName: 'feb_2026_strategy',
  interval: '15m',
  getSignal: async (symbol, when, currentPrice) => {
    if (currentForecast === 'wait') return null;
    return {
      position: currentForecast === 'bullish' ? 'long' : 'short',
      // ... entry/TP/SL parameters
    };
  },
});

Environment variables

# Tavily news search API key
TAVILY_API_KEY=your_key_here

# Ollama base URL (default: http://localhost:11434)
OLLAMA_BASE_URL=http://localhost:11434

# Telegram notifications (optional)
CC_TELEGRAM_TOKEN=your_bot_token_here
CC_TELEGRAM_CHANNEL=-100123456789

Key techniques

Cron virtual-time scheduler: Cron fires handlers on virtual time boundaries, not wall-clock time. In backtest mode, a 4h interval fires once every four simulated hours as the engine replays historical candles. In live mode, it fires every four real hours. The same strategy code runs in both modes with no branching. Position flip on sentiment change: Rather than letting the strategy accumulate both LONG and SHORT positions, the active-ping handler closes any position that conflicts with the current forecast. This keeps the strategy always aligned with the most recent LLM signal while avoiding the complexity of managing opposing positions. Trailing exit for trending moves: The trailing take-profit (1% drawdown from peak) captures the bulk of sharp directional moves, like the Feb 4 SHORT that rode $11k of BTC decline before the trailing stop fired at +14.28%. A fixed take-profit would have closed the same trade much earlier.

Running the backtest

npm start -- --backtest --symbol BTCUSDT \
  --strategy feb_2026_strategy \
  --exchange ccxt-exchange \
  --frame feb_2026_frame \
  ./content/feb_2026.strategy/feb_2026.strategy.ts

# With visual dashboard
npm start -- --backtest --symbol BTCUSDT --ui \
  ./content/feb_2026.strategy/feb_2026.strategy.ts
For better LLM forecast accuracy, inject technical indicator context alongside the news headlines. @backtest-kit/signals computes 50+ indicators across four timeframes (1m, 15m, 30m, 1h) and returns a ready-to-use markdown report. Adding this to the LLM prompt gives the model both the macro news narrative and the current technical picture — particularly useful for filtering out news that is already priced in. Install with npm install @backtest-kit/signals backtest-kit.

Build docs developers (and LLMs) love