The AI-Powered Strategy Optimization system enables automated generation of executable trading strategies using Large Language Models (LLMs). This document provides an overview of the optimizer's architecture, configuration, and execution flow. For detailed information about specific subsystems, see:
The Optimizer generates complete, executable trading strategy code by analyzing historical market data through LLMs. The system collects data from configurable sources across multiple time ranges, builds multi-timeframe conversation context, and leverages Ollama's deepseek-v3.1:671b model to synthesize trading strategies. The output is a ready-to-execute .mjs file containing strategies, exchange configurations, frames, and a Walker for performance comparison.
Key Capabilities:
| Feature | Description |
|---|---|
| Data Collection | Paginated fetching with deduplication from multiple sources |
| Multi-Timeframe Analysis | Progressive context building (1h → 15m → 5m → 1m) |
| LLM Code Generation | JSON-structured signal output with validation |
| Template System | Modular code generation with customizable templates |
| Automated Testing | Generated Walker for immediate strategy comparison |
The optimizer bridges raw market data to production-ready strategies through a four-stage pipeline: data collection, prompt construction, LLM inference, and code assembly.
Component Responsibilities:
| Component | File | Responsibility |
|---|---|---|
| OptimizerUtils | src/classes/Optimizer.ts:32-121 | Public API facade for optimizer operations |
| OptimizerGlobalService | src/lib/services/global/OptimizerGlobalService.ts:21-102 | Validation entry point, delegates to ConnectionService |
| OptimizerConnectionService | src/lib/services/connection/OptimizerConnectionService.ts:41-172 | Memoized instance creation with template merging |
| OptimizerSchemaService | src/lib/services/schema/OptimizerSchemaService.ts:13-95 | Immutable schema storage via ToolRegistry |
| OptimizerTemplateService | src/lib/services/template/OptimizerTemplateService.ts:27-713 | Default code generation templates |
| ClientOptimizer | src/client/ClientOptimizer.ts:397-445 | Core business logic for data and code generation |
The IOptimizerSchema defines the complete optimizer configuration. Registration is performed via the addOptimizer function (see Component Registration).
Schema Fields:
| Field | Type | Required | Description |
|---|---|---|---|
optimizerName |
string | Yes | Unique identifier for registry lookup |
rangeTrain |
IOptimizerRange[] | Yes | Training time ranges for strategy generation |
rangeTest |
IOptimizerRange | Yes | Testing time range for Walker validation |
source |
Source[] | Yes | Data sources for LLM context building |
getPrompt |
function | Yes | Generates strategy logic from conversation history |
template |
Partial<IOptimizerTemplate> | No | Custom code generation overrides |
callbacks |
Partial<IOptimizerCallbacks> | No | Lifecycle hooks for monitoring |
Data Source Configuration:
// Simple source - just a fetch function
const simpleFetch: IOptimizerSourceFn = async ({ symbol, startDate, endDate, limit, offset }) => {
return await fetchDataFromDatabase(symbol, startDate, endDate, limit, offset);
};
// Complex source - with custom message formatters
const complexSource: IOptimizerSource = {
name: "backtest-results",
fetch: async ({ symbol, startDate, endDate, limit, offset }) => {
return await fetchBacktestResults(symbol, startDate, endDate, limit, offset);
},
user: async (symbol, data, name) => {
return `Analyze these ${data.length} backtest results for ${symbol}:\n${JSON.stringify(data)}`;
},
assistant: async (symbol, data, name) => {
return `Analyzed ${data.length} results. Key patterns identified.`;
}
};
The optimizer executes through three primary methods: getData(), getCode(), and dump(). Each builds upon the previous.
Step-by-Step Process:
addOptimizer() validates schema and stores in registryOptimizerValidationService confirms optimizer existsOptimizerConnectionService creates memoized ClientOptimizerrangeTrain, fetches from each source with paginationgetPrompt() with complete conversation history{optimizerName}_{symbol}.mjs to filesystemThe Optimizer singleton provides three methods for strategy generation:
Example Usage:
import { addOptimizer, Optimizer } from "backtest-kit";
// 1. Register optimizer configuration
addOptimizer({
optimizerName: "btc-momentum-v1",
rangeTrain: [
{ startDate: new Date("2024-01-01"), endDate: new Date("2024-01-31") },
{ startDate: new Date("2024-02-01"), endDate: new Date("2024-02-28") }
],
rangeTest: {
startDate: new Date("2024-03-01"),
endDate: new Date("2024-03-31")
},
source: [
async ({ symbol, startDate, endDate, limit, offset }) => {
// Fetch historical candles or indicators
return await fetchIndicators(symbol, startDate, endDate, limit, offset);
}
],
getPrompt: async (symbol, messages) => {
// Generate strategy logic prompt
return "Купить на пробое RSI 30, продать на RSI 70. Стоп-лосс 2%, тейк-профит 4%.";
}
});
// 2. Get strategy data (intermediate result)
const strategies = await Optimizer.getData("BTCUSDT", {
optimizerName: "btc-momentum-v1"
});
console.log(strategies); // Array of strategy metadata with conversation history
// 3. Generate executable code
const code = await Optimizer.getCode("BTCUSDT", {
optimizerName: "btc-momentum-v1"
});
console.log(code); // Complete .mjs file content
// 4. Save to file
await Optimizer.dump("BTCUSDT", {
optimizerName: "btc-momentum-v1"
}, "./strategies");
// Creates: ./strategies/btc-momentum-v1_BTCUSDT.mjs
Method Comparison:
| Method | Purpose | Output | Use Case |
|---|---|---|---|
getData() |
Fetch data and build conversation history | IOptimizerStrategy[] |
Inspect intermediate data, debug prompts |
getCode() |
Generate complete executable code | string |
Preview code before saving, custom post-processing |
dump() |
Generate and save code to file | void |
Production usage, automated workflows |
The optimizer outputs a complete .mjs file with all required components. The file is immediately executable via Node.js.
Generated File Structure:
Code Section Mapping:
| Section | Template Method | Purpose |
|---|---|---|
| Shebang + Imports | getTopBanner() |
src/lib/services/template/OptimizerTemplateService.ts:36-66 |
| dumpJson() | getJsonDumpTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:452-546 |
| text() | getTextTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:555-612 |
| json() | getJsonTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:629-712 |
| addExchange() | getExchangeTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:314-342 |
| addFrame() | getFrameTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:354-385 |
| addStrategy() | getStrategyTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:168-304 |
| addWalker() | getWalkerTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:122-157 |
| Walker.background() | getLauncherTemplate() |
src/lib/services/template/OptimizerTemplateService.ts:395-443 |
Strategy Template Highlights:
The getStrategyTemplate() generates a strategy with multi-timeframe analysis:
getCandles()json(messages) for structured signal output./dump/strategy/{uuid}/The generated signal schema enforces:
position: "wait" | "long" | "short"priceOpen, priceTakeProfit, priceStopLoss: entry/exit pricesminuteEstimatedTime: expected trade duration (max 360 minutes)note: strategy explanationThe template system allows partial overrides for custom code generation. Override any of the 11 template methods via the template field in IOptimizerSchema.
Template Methods:
| Method | Default Behavior | Common Customizations |
|---|---|---|
getTopBanner() |
Standard imports (Ollama, ccxt, backtest-kit) | Add custom dependencies, environment variables |
getUserMessage() |
"Прочитай данные и скажи ОК" | Format data differently, add analysis instructions |
getAssistantMessage() |
"ОК" | Add contextual acknowledgments |
getStrategyTemplate() |
Multi-timeframe (1h→15m→5m→1m) | Change timeframes, add custom indicators |
getTextTemplate() |
deepseek-v3.1:671b text mode | Use different model, modify system prompt |
getJsonTemplate() |
Signal schema with position/prices | Change schema, add custom fields |
getExchangeTemplate() |
CCXT Binance | Use different exchange, add authentication |
getFrameTemplate() |
Standard frame config | Add custom intervals |
getWalkerTemplate() |
Standard walker | Change comparison metrics |
getLauncherTemplate() |
Full event listeners | Add custom listeners, modify output |
getJsonDumpTemplate() |
Dump to ./dump/strategy | Change output directory, format |
Example Customization:
addOptimizer({
optimizerName: "custom-strategy",
// ... other fields ...
template: {
// Override strategy template to use custom timeframes
getStrategyTemplate: async (strategyName, interval, prompt) => {
return `
addStrategy({
strategyName: "${strategyName}",
interval: "${interval}",
getSignal: async (symbol) => {
// Custom logic: only use 4h and 1h timeframes
const longTermCandles = await getCandles(symbol, "4h", 24);
const mediumTermCandles = await getCandles(symbol, "1h", 48);
const messages = [
{ role: "user", content: formatCandles(longTermCandles) },
{ role: "assistant", content: "Long-term trend analyzed" },
{ role: "user", content: formatCandles(mediumTermCandles) },
{ role: "assistant", content: "Medium-term trend analyzed" },
{ role: "user", content: "${prompt}" }
];
return await json(messages);
}
});
`;
}
}
});
The optimizer emits progress events via progressOptimizerEmitter for real-time monitoring during data collection.
Progress Event Structure:
interface ProgressOptimizerContract {
optimizerName: string; // Optimizer identifier
symbol: string; // Trading pair
totalSources: number; // rangeTrain.length * source.length
processedSources: number; // Completed sources
progress: number; // 0.0 to 1.0
}
Monitoring Example:
import { listenOptimizerProgress, Optimizer } from "backtest-kit";
// Subscribe to progress events
listenOptimizerProgress((event) => {
console.log(`Optimizer: ${event.optimizerName}`);
console.log(`Symbol: ${event.symbol}`);
console.log(`Progress: ${(event.progress * 100).toFixed(2)}%`);
console.log(`Processed: ${event.processedSources}/${event.totalSources} sources`);
});
// Start optimization
await Optimizer.dump("BTCUSDT", { optimizerName: "my-optimizer" });
Progress events are emitted:
Common Use Cases:
Recommended Patterns:
// Pattern 1: Multiple Training Periods
addOptimizer({
optimizerName: "quarterly-strategies",
rangeTrain: [
{ startDate: new Date("2024-Q1-start"), endDate: new Date("2024-Q1-end") },
{ startDate: new Date("2024-Q2-start"), endDate: new Date("2024-Q2-end") },
{ startDate: new Date("2024-Q3-start"), endDate: new Date("2024-Q3-end") }
],
// Each range generates a separate strategy variant
// ...
});
// Pattern 2: Multiple Data Sources
addOptimizer({
optimizerName: "multi-source-strategy",
source: [
{ name: "candles", fetch: fetchCandles },
{ name: "indicators", fetch: fetchIndicators },
{ name: "sentiment", fetch: fetchSentiment },
{ name: "orderbook", fetch: fetchOrderbook }
],
// LLM sees all sources in conversation
// ...
});
// Pattern 3: Custom Lifecycle Hooks
addOptimizer({
optimizerName: "monitored-optimizer",
callbacks: {
onSourceData: async (symbol, sourceName, data) => {
console.log(`Fetched ${data.length} rows from ${sourceName}`);
},
onData: async (symbol, strategyData) => {
// Validate generated strategies
for (const strategy of strategyData) {
if (strategy.strategy.length < 100) {
throw new Error("Strategy prompt too short");
}
}
},
onCode: async (symbol, code) => {
// Run static analysis, linting
await validateGeneratedCode(code);
},
onDump: async (symbol, filepath) => {
// Upload to storage, send notifications
await uploadToS3(filepath);
}
},
// ...
});
The generated code includes a Walker configuration that automatically compares all generated strategies on the test range. This enables immediate performance evaluation without manual setup.
Walker Integration Flow:
The generated launcher code includes:
listenWalkerProgress() - Monitor comparison progresslistenWalkerComplete() - Receive best strategy resultsWalker.dump() - Export comparison statisticsGenerated Walker Structure:
// Generated by getWalkerTemplate()
addWalker({
walkerName: "{prefix}_walker",
exchangeName: "{prefix}_exchange",
frameName: "{prefix}_test_frame",
strategies: [
"{prefix}_strategy-1", // From rangeTrain[0]
"{prefix}_strategy-2", // From rangeTrain[1]
"{prefix}_strategy-3" // From rangeTrain[2]
]
});
// Generated by getLauncherTemplate()
Walker.background("BTCUSDT", { walkerName: "{prefix}_walker" });
listenWalkerComplete((results) => {
console.log("Best strategy:", results.bestStrategy);
console.log("Sharpe Ratio:", results.bestSharpeRatio);
Walker.dump("BTCUSDT", results.walkerName);
});