Skip to main content

Documentation 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.

The strategy file (content/jan_2026.strategy/jan_2026.strategy.ts) registers exactly two Cron.register calls to handle the fundamental difference between live and backtest execution. In live mode a recurring 15-minute timer polls the Telegram channel for new signals and immediately triggers the risk-filtering job. In backtest mode a one-shot initialiser pre-loads the full frame’s historical messages before backtest-kit begins replaying candle data. Both handlers share the same core service instances and simply skip themselves when the mode does not match.

Live Mode Crontab

Cron.register({
  name: "live-fetch-data",
  handler: async (symbol, when, backtest) => {
    if (backtest) {
      return;
    }
    console.log(`Fetching live data symbol=${symbol} when=${when}`);
    await core.crawlerMainService.crawlLiveFrame(when);
  },
  interval: "15m",
});
The interval: "15m" field instructs backtest-kit to invoke this handler once every 15 minutes during live execution. When backtest is true (i.e. history replay is active) the handler returns immediately, so the cron entry is harmless during backtests. When the handler fires in live mode it calls crawlLiveFrame(when), which executes the following steps:
1

Mode guard

CrawlerMainService.crawlLiveFrame calls getMode() internally. If the runtime unexpectedly reports "backtest", the method returns without doing anything.
2

Compute today's moment-stamp

getMomentStamp(when) converts the current Date to an integer like 20260115, identifying the calendar day to crawl.
3

Crawl today's messages

crawlerService.crawlDay(stamp) fetches all Telegram messages published on that day and upserts them into the parser-items collection.
4

Trigger the signal job

signalJobSubject.next() fires, which causes SignalJobService to process every unvisited parser-item through the RiskOutline and write results to screen-items.

Backtest Prepare Crontab

Cron.register({
  name: "backtest-prepare-data",
  handler: async (symbol, when, backtest) => {
    if (!backtest) {
      return;
    }
    console.log(`Fetching backtest data symbol=${symbol} when=${when}`);
    await core.crawlerMainService.crawlBacktestFrame(when);
  },
});
The absence of an interval field means backtest-kit calls this handler once, at strategy startup, before the candle replay begins. In live mode (backtest is false) the handler returns immediately. When executed in backtest mode, crawlBacktestFrame(when) performs the following:
1

Resolve the active frame

Reads frameName from getContext() and looks up the matching entry in listFrameSchema() to obtain the frame’s startDate and endDate.
2

Crawl the full date range

Converts both boundary dates to moment-stamps and calls crawlerService.crawlRange(fromStamp, toStamp), which fetches every calendar day in the frame in parallel, upserting all parsed messages into parser-items.
3

Trigger the signal job

signalJobSubject.next() fires, which causes SignalJobService to iterate over all parser-items in the frame range and run each through the RiskOutline, populating screen-items before the strategy’s getSignal callback starts being called.

The Signal Job

signalJobSubject (from packages/core/src/config/emitters.ts) is a Subject<void> from functools-kit:
import { Subject } from "functools-kit";

export const signalJobSubject = new Subject<void>();
It acts as the connector between the crawler and the signal processing pipeline. SignalJobService subscribes to it via enable() and invokes run() on each emission. The run() method is wrapped with queued() so that concurrent emissions do not cause parallel executions — if a crawl fires the subject while a previous job is still processing, the second run is queued and executes after the first completes.

Backtest mode

const RUN_BACKTEST_FN = async (self: SignalJobService, frameName: string) => {
  const frameList = await listFrameSchema();
  const { startDate, endDate } = frameList.find(
    (frame) => frame.frameName === frameName,
  );
  const rowList = await self.parserDbService.findAllByPublishedAt(startDate, endDate);
  for (const row of rowList) {
    if (await self.screenDbService.findByParserItem(row.id)) {
      continue; // already processed, skip
    }
    const dto = await RUN_IN_CONTEXT_FN(self, row, true);
    await self.screenDbService.create(dto);
    await self.parserDbService.markVisited(row.id);
  }
};
  • Loads all parser-items whose publishedAt falls within the frame’s date range.
  • Skips any row that already has a corresponding screen-items entry.
  • Runs each remaining row through SignalLogicService.execute(), which calls the RiskOutline, and writes the result to screen-items.
  • Marks each parser-item as visited: true.

Live mode

const RUN_LIVE_FN = async (self: SignalJobService) => {
  const rowList = await self.parserDbService.findAllByVisited(false);
  for (const row of rowList) {
    if (await self.screenDbService.findByParserItem(row.id)) {
      continue;
    }
    const dto = await RUN_IN_CONTEXT_FN(self, row, false);
    await self.screenDbService.create(dto);
    await self.parserDbService.markVisited(row.id);
  }
};
  • Loads all parser-items that have not yet been visited (visited: false).
  • Applies the same deduplication check against screen-items.
  • Runs the RiskOutline and writes the result, then marks the row visited.
Each row is processed inside ExecutionContextService.runInContext() with symbol, when (aligned to 1-minute intervals via alignToInterval), and backtest set appropriately, so that backtest-kit’s candle APIs return historically accurate data for the exact moment the signal was published.

Cron Handler Parameters

Every Cron.register handler receives three arguments:
ParameterTypeDescription
symbolstringThe current symbol being processed by the strategy runner. Identifies which asset’s candle stream is active.
whenDateThe current timestamp. In live mode this is the real wall-clock time. In backtest mode this is the simulated time at the current replay position.
backtestbooleantrue when backtest-kit is replaying historical data, false in live execution. Use this flag to guard mode-specific logic at the handler level.

The interval field in Cron.register accepts the same interval strings used throughout backtest-kit for candle resolution: "1m", "5m", "15m", "1h", "4h", and "1d". Omitting interval produces a one-shot cron that fires once at strategy startup — useful for data-preparation tasks that must complete before the candle replay begins.

Build docs developers (and LLMs) love