backtest-ollama-crontab is deliberately layered so that each concern — message retrieval, signal parsing, LLM risk assessment, trade execution, and scheduling — lives in its own file and can be reasoned about independently. The two workspace packages (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-ollama-crontab/llms.txt
Use this file to discover all available pages before exploring further.
packages/core and packages/main) divide infrastructure from entrypoints; strategy files in content/ are loaded at runtime by @backtest-kit/cli and never bundled into either package. Understanding how these layers connect is the fastest path to modifying or extending the pipeline.
The Five Pipeline Layers
Each signal travels through five layers from raw Telegram message to an open (or vetoed) position. The table below maps each layer to the exact file responsible and what it does:| Layer | File | Responsibility |
|---|---|---|
| Crawl | packages/core/src/lib/services/core/CrawlerService.ts | Calls Telegram iterMessages on the configured channel and upserts raw messages into the parser-items MongoDB collection |
| Parse | packages/core/src/lib/services/screen/ChannelScreenService.ts | Regex-extracts direction, entry, targets, and stoploss from each raw message text |
| Job | packages/core/src/lib/services/job/SignalJobService.ts | Subscribes to signalJobSubject, iterates every unvisited row, and fans each one out to SignalLogicService |
| Outline | packages/core/src/logic/outline/risk.outline.ts | Builds the Ollama prompt with pre-computed metrics (avgRangePct, momentum24hPct) and 1m/15m candles; parses the Zod-validated riskAction response |
| Logic | packages/core/src/lib/services/logic/SignalLogicService.ts | Pure passthrough that writes the LLM verdict to the screen-items DTO |
| Schema | packages/core/src/schema/Screen.schema.ts | Final MongoDB document: direction, entryFrom/To, targets, stoploss, riskAction, riskSureLevel, riskConfidence, riskDescription, riskReasoning |
| Strategy | content/jan_2026.strategy/jan_2026.strategy.ts | Reads screen-items; opens a position only when riskAction === "follow" and live price is inside the entry zone |
| Cron | Same strategy file, Cron.register(...) | interval: "15m" for live polling; no interval = one-shot backtest-prepare handler |
The “Crawl” and “Parse” layers share the same 15-minute crontab trigger in live mode. During backtest the crawler pulls the entire date-range frame at startup via the one-shot
Cron.register handler (no interval set).Monorepo Layout
Dependency Injection: di-factory and globalThis.core
packages/core uses the di-factory library as its DI container. Every service is bound with a named token from TYPES and retrieved via inject<T>(). When packages/core/src/lib/index.ts runs (it is the first import of the package), all services are bound, init() is called, and the resulting service map is attached to globalThis.core.
packages/main pull services from globalThis.core without knowing how they were constructed. The root tsconfig.json points @pro/core at packages/core/types.d.ts so TypeScript understands the shape of globalThis.core at edit time with no runtime overhead.
The Two MongoDB Collections
The pipeline persists signal data in two distinct collections:parser-items
Raw parsed signals written by
CrawlerService and ChannelScreenService. Contains the channel message text, extracted direction, entry, targets, and stoploss fields. Each document is upserted by message ID so re-crawls are idempotent.screen-items
LLM-reviewed signals. Mirrors
parser-items fields and adds the Ollama verdict fields: riskAction ("follow" or "skip"), riskSureLevel, riskConfidence, riskDescription, and riskReasoning. The strategy file queries this collection and filters on riskAction === "follow".The LLM Risk Outline
The risk filter lives inpackages/core/src/logic/outline/risk.outline.ts and is the most important file in the pipeline. It uses agent-swarm-kit’s addOutline / ask / dumpOutlineResult APIs to wrap the Ollama inference call.
Two hard-coded veto rules in the system prompt
Two hard-coded veto rules in the system prompt
The prompt is built with Rule 1 — sleeping-coin SHORT veto: If
str.newline(...) from functools-kit and embeds two threshold constants:direction = SHORT and avgRangePct < 0.07% → action = "skip". A thin-liquidity asset is a stop-hunt target; one large candle sweeps short positions upward.Rule 2 — knife-catching LONG veto: If direction = LONG and momentum24hPct < −1% → action = "skip". The channel is calling a long entry into a declining market; the stop-loss almost always fires before any recovery.Rule 3 — default: If neither rule fired → action = "follow".Metrics fed into the prompt
Metrics fed into the prompt
Before the inference call, the outline computes two pre-processed metrics from the 24 hours of 1m candles prior to signal publication (up to
PRE_CANDLES_LIMIT = 1440 candles):avgRangePct— average (high − low) / low across 1m candles; measures how “active” or “sleeping” the asset is.momentum24hPct— percentage price change from 24h ago to the signal timestamp; measures trend direction.
commitMetricsHistory as a separate user message so the model reads them as ground truth rather than inferring them from raw candles.Zod-validated response schema
Zod-validated response schema
The response is validated against a Zod schema (
RiskOutlineContract) that enforces:jsonrepair is listed as a dependency to handle malformed JSON that some quantized models occasionally emit.Build Pipeline
Each package is independently buildable. The root build scripts (npm run build:x on Linux/macOS, npm run build:win on Windows) call scripts/linux/build.sh (or its Windows equivalent), which loops over every directory in packages/ and runs npm install && npm run build inside it.
| Output | Description |
|---|---|
build/index.cjs | CommonJS bundle including all local source; peer dependencies are externalised |
types.d.ts | Rolled-up type declarations via rollup-plugin-dts; this is what @pro/core / @pro/main resolve to in tsconfig.json |
Mode Detection: Backtest vs Live
backtest-kit exposes three utilities that let the same strategy code behave correctly in both contexts:
| API | What it does |
|---|---|
getMode() | Returns "backtest" or "live" depending on the CLI flag that launched the process |
getContext() | Returns the current execution context (symbol, timestamp, candle data) being processed |
ExecutionContextService | Injectable service that tracks which symbol/frame/strategy triple is currently active |
backtest.ts, waitForReady(true) signals a backtest boot; in live.ts, waitForReady(false) signals live. The boolean propagates through backtest-kit internals so getMode() returns the correct value everywhere without any manual plumbing.
The crontab registration in the strategy file exploits the same detection:
Entrypoint Flow Diagram
Risk Outline Concepts
Deep-dive into how the LLM prompt is constructed, the metrics packet, and the Zod response contract.
Quickstart
Build, authenticate, and run the backtest in four steps.
Backtest Report
Full January 2026 trade log and before/after Ollama statistics.
Introduction
Project overview, the two empirical rules, and key dependencies.