Link to the source code
LLM-driven trading strategy with multi-timeframe analysis and automated backtesting using backtest-kit framework.
Demonstrates AI-powered trading strategy capabilities for:
Multi-Timeframe Analysis: 4-level market structure analysis
LLM Integration: Ollama-powered signal generation (deepseek-v3.1:671b)
Structured Output: JSON schema validation for trading signals
Debug Logging: Automatic conversation and signal dumping via dumpSignal()
Progress Tracking: Real-time backtest progress monitoring
Performance Reports: Automated markdown report generation
demo/backtest/
├── src/
│ ├── index.mjs # Main backtest configuration
│ └── utils/
│ ├── json.mjs # LLM API client (Ollama)
│ └── messages.mjs # Multi-timeframe message builder
├── package.json # Dependencies and scripts
└── README.md # This file
# Navigate to project directory
cd demo/backtest
# Install dependencies
npm install
# Set environment variables
export OLLAMA_API_KEY=your_ollama_api_key
# Run backtest
npm start
Create a .env file or set environment variables:
OLLAMA_API_KEY=your_ollama_api_key
The demo is pre-configured for BTCUSDT with:
LLM follows strict rules for signal generation:
Position Types:
position='wait': No clear signal, wait for better conditionsposition='long': Bullish signal, price expected to riseposition='short': Bearish signal, price expected to fallEntry Price (priceOpen):
Exit Levels:
priceTakeProfit > priceOpen > priceStopLosspriceStopLoss > priceOpen > priceTakeProfitTime Estimate (minuteEstimatedTime):
Run the backtest:
npm start
Output:
Progress: 25.00%
Processed: 360 / 1440
Progress: 50.00%
Processed: 720 / 1440
Progress: 75.00%
Processed: 1080 / 1440
Progress: 100.00%
Processed: 1440 / 1440
Backtest completed: BTCUSDT
Generated files:
./dump/backtest/test_strategy.md - Performance report./dump/strategy/{uuid}/ - LLM conversation logs (one per signal)After backtest completes, check performance report:
cat ./dump/backtest/test_strategy.md
Example output:
# Backtest Report: test_strategy
| Signal ID | Symbol | Position | Open Price | Close Price | PNL (net) | Close Reason |
|-----------|----------|----------|------------|-------------|-----------|--------------|
| signal-1 | BTCUSDT | LONG | 50000 USD | 51000 USD | +2.00% | take_profit |
| signal-2 | BTCUSDT | SHORT | 51500 USD | 50500 USD | +1.94% | take_profit |
**Total signals:** 15
**Win rate:** 73.33% (11W / 4L)
**Average PNL:** +0.85%
**Sharpe Ratio:** 1.234
Each signal generates debug logs in ./dump/strategy/{uuid}/:
./dump/strategy/a1b2c3d4-e5f6-7890-abcd-ef1234567890/
├── 00_system_prompt.md # System instructions
├── 01_user_message.md # 1h candles analysis
├── 02_assistant_message.md # LLM acknowledgment
├── 03_user_message.md # 15m candles analysis
├── 04_assistant_message.md # LLM acknowledgment
├── 05_user_message.md # 5m candles analysis
├── 06_assistant_message.md # LLM acknowledgment
├── 07_user_message.md # 1m candles analysis
├── 08_assistant_message.md # LLM acknowledgment
├── 09_user_message.md # Signal generation request
└── 10_llm_output.md # Final signal with JSON
Modify src/index.mjs to analyze different cryptocurrencies:
Backtest.background("ETHUSDT", { // Change symbol
strategyName: "test_strategy",
exchangeName: "test_exchange",
frameName: "test_frame",
});
Edit backtest period in src/index.mjs:
addFrame({
frameName: "test_frame",
interval: "1m",
startDate: new Date("2025-12-15T00:00:00.000Z"),
endDate: new Date("2025-12-15T23:59:59.000Z"),
});
For each signal generation (every 5 minutes):
Creates structured conversation:
LLM analyzes all timeframes and returns:
{
"position": "long",
"note": "Strong bullish momentum on 1h, breakout on 5m...",
"priceOpen": 50000,
"priceTakeProfit": 51000,
"priceStopLoss": 49000,
"minuteEstimatedTime": 120
}
dumpSignal() saves:
./dump/strategy/{uuid}/ directoryFramework validates and executes signal:
priceOpenAfter backtest completes:
./dump/backtest/{strategyName}.md{
role: "system",
content: `
Проанализируй торговую стратегию и верни торговый сигнал.
ПРАВИЛА ОТКРЫТИЯ ПОЗИЦИЙ:
1. ТИПЫ ПОЗИЦИЙ:
- position='wait': нет четкого сигнала
- position='long': бычий сигнал
- position='short': медвежий сигнал
2. ЦЕНА ВХОДА (priceOpen):
- Текущая рыночная или отложенная цена
- Обоснование по техническому анализу
3. УРОВНИ ВЫХОДА:
- LONG: priceTakeProfit > priceOpen > priceStopLoss
- SHORT: priceStopLoss > priceOpen > priceTakeProfit
- На основе Fibonacci, S/R, Bollinger
4. ВРЕМЕННЫЕ РАМКИ:
- minuteEstimatedTime: макс 360 минут
- Расчет по ATR, ADX, MACD, Momentum
`
}
This encourages:
Final request emphasizes caution:
Проанализируй все таймфреймы и сгенерируй торговый сигнал.
Открывай позицию ТОЛЬКО при четком сигнале.
Если сигналы противоречивы или тренд слабый то position: wait
This prevents:
Backtest evaluates strategy by:
// In utils/messages.mjs
const longTermCandles = await getCandles(symbol, "4h", 24);
messages.push(
{
role: "user",
content: `Проанализируй свечи 4h:\n${formatCandles(longTermCandles, "4h")}`
},
{
role: "assistant",
content: "Долгосрочный тренд 4h проанализирован"
}
);
// In utils/json.mjs
const response = await ollama.chat({
model: "llama3.3:70b", // Use different model
messages: [...]
});
// In utils/json.mjs - modify format property
format: {
type: "object",
properties: {
position: { type: "string", enum: ["wait", "long", "short"] },
note: { type: "string" },
confidence: { type: "number", minimum: 0, maximum: 1 }, // Add confidence
// ... existing fields
}
}
// In src/index.mjs
listenSignalBacktest((event) => {
if (event.action === "opened") {
console.log(`📈 Opened ${event.signal.position} at ${event.signal.priceOpen}`);
} else if (event.action === "closed") {
console.log(`💰 Closed with PNL: ${event.pnl.pnlPercentage.toFixed(2)}%`);
}
});
// Test multiple symbols simultaneously
const symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT"];
for (const symbol of symbols) {
Backtest.background(symbol, {
strategyName: "test_strategy",
exchangeName: "test_exchange",
frameName: "test_frame",
});
}
Issue: OLLAMA_API_KEY is not defined
# Solution: Set environment variable
export OLLAMA_API_KEY=your_key
Issue: Rate limit exceeded
// Solution: Add delay between requests in utils/json.mjs
await new Promise(resolve => setTimeout(resolve, 1000));
Issue: Invalid signal structure
// Solution: Check LLM output format matches schema
// Verify all required fields are present in JSON response
Issue: No signals generated (all 'wait')
// Solution: Adjust prompt to be more aggressive
// Or check if market data has sufficient volatility
Live.background() for real-time executionMIT © tripolskypetr