backtest-ollama-crontab is a TypeScript monorepo that wires a free, public Telegram trading-signals channel through a locally-hosted Ollama LLM and into theDocumentation 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.
backtest-kit strategy execution engine. Instead of blindly following every signal the channel publishes, the pipeline asks a quantized gpt-oss model whether each signal is safe to trade before a position is ever opened. The result is a measurable improvement in January 2026 simulated P&L — from +52.22% to +68.90% — achieved by dropping only five trades, four of which were losers.
What the Project Does
At its core, the repo answers one question: can a local LLM acting as a rule-based risk filter genuinely improve P&L on historical trading signals? The experiment takes raw text messages from a Telegram channel, parses entry/target/stop-loss fields out of them, runs each parsed signal through an Ollama inference call, and only opens a position when the model returnsriskAction: "follow". Every other piece of infrastructure — candle caching, backtesting replay, live 15-minute polling — exists to support that single gate.
Two Operating Modes
The same codebase supports two distinct modes controlled by CLI flags:- Backtest Mode
- Live Mode
Triggered with
--backtest. The backtest-kit engine replays historical candle data over the full signal set for a given date range. Candles can be pre-fetched with --cache so the replay runs offline. The January 2026 strategy file in content/jan_2026.strategy/ is the reference experiment.The Core Insight: Two Empirical Rules
The LLM is not doing open-ended reasoning. Its system prompt encodes exactly two empirically-derived veto rules:Rule 1 — Sleeping-Coin SHORT Veto
If
direction = SHORT and avgRangePct < 0.07%, the signal is skipped. A low-volatility asset with thin liquidity is a classic stop-hunt target: one large candle sweeps shorts upward before the move reverses.Rule 2 — Knife-Catching LONG Veto
If
direction = LONG and momentum24hPct < −1%, the signal is skipped. Buying into a declining market on a channel signal is catching a falling knife; the stop-loss is almost always triggered before any recovery.packages/core/src/logic/outline/risk.outline.ts as plain string constants (SHORT_MIN_AVG_RANGE_PCT = 0.07, LONG_MIN_MOMENTUM_24H_PCT = -1). They are tunable in one place without recompiling any package.
January 2026 Results at a Glance
The headline numbers from running the same parsed-signal set with and without the Ollama gate:| 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 LONG Jan 15 +1.86%) captured in the filtered run.
Monorepo Structure
The workspace contains two packages underpackages/ plus strategy files under content/:
| Package / Path | Role |
|---|---|
packages/core | LLM outline logic, Telegram crawler, signal parser, MongoDB services, DI container — exposes everything on globalThis.core |
packages/main | CLI entrypoints: backtest, live, paper, session — reads globalThis.core via inject<T>() |
content/jan_2026.strategy/ | Reference strategy file loaded at runtime by @backtest-kit/cli; never bundled into packages |
packages/core internals
packages/core internals
| Sub-path | Responsibility |
|---|---|
src/lib/services/core/CrawlerService.ts | Telegram iterMessages → parser-items upsert |
src/lib/services/screen/ChannelScreenService.ts | Regex extraction of direction / entry / targets / stoploss |
src/lib/services/job/SignalJobService.ts | Subscribes to signalJobSubject, fans out each unvisited row to SignalLogicService |
src/logic/outline/risk.outline.ts | Ollama prompt construction, commitMetricsHistory, Zod-validated riskAction response |
src/lib/services/logic/SignalLogicService.ts | Pure passthrough of LLM verdict to screen-items DTO |
src/schema/Screen.schema.ts | Final stored fields including riskAction, riskSureLevel, riskConfidence, riskDescription, riskReasoning |
packages/main entrypoints
packages/main entrypoints
| File | Activated by flag |
|---|---|
src/main/backtest.ts | --backtest |
src/main/live.ts | --live |
src/main/paper.ts | --paper |
src/main/session.ts | --session |
Key External Dependencies
| Package | Purpose |
|---|---|
backtest-kit | Strategy execution engine, Cron.register, Backtest.background, Live.background, candle APIs |
agent-swarm-kit | addOutline, ask, dumpOutlineResult — wraps the Ollama inference call |
ollama | HTTP client used to call the Ollama API at https://ollama.com; bearer token passed via CC_OLLAMA_TOKEN |
telegram (GramJS) | MTProto client for reading Telegram channel messages |
mongoose | MongoDB ODM for parser-items and screen-items collections |
ioredis | Redis client used by backtest-kit internals |
di-factory | Dependency injection container; inject<T>() pulls services; all bound on globalThis.core |
functools-kit | Utility helpers including singleshot memoization |
Quickstart
Build the workspace, authenticate Telegram, and run the January 2026 backtest in under 10 minutes.
Architecture
Detailed walkthrough of the five-layer crawl → parse → LLM → strategy → cron pipeline.
Risk Outline
How the LLM prompt is constructed, what metrics are fed in, and how the Zod schema validates the response.
Backtest Report
Full January 2026 trade-by-trade log comparing the baseline run against the Ollama-gated run.