fixed up rust system

This commit is contained in:
Boki 2025-07-03 21:45:08 -04:00
parent 063f4c8e27
commit a58072cf93
7 changed files with 255 additions and 44 deletions

View file

@ -56,6 +56,10 @@ export class RustBacktestAdapter extends EventEmitter {
// For now, use a simple strategy mapping
// In the future, strategies should be written in Rust or use a common interface
this.container.logger.info('About to register strategy', {
strategy: config.strategy,
config: config.config
});
this.registerStrategy(config.strategy, config.config || {});
// Load historical data
@ -68,8 +72,17 @@ export class RustBacktestAdapter extends EventEmitter {
});
// Run the backtest in Rust
this.container.logger.info('Starting Rust engine execution');
const resultJson = this.currentEngine.run();
this.container.logger.info('Rust engine execution completed');
const rustResult = JSON.parse(resultJson);
this.container.logger.info('Rust backtest result summary', {
totalTrades: rustResult.metrics?.total_trades,
equityCurveLength: rustResult.equity_curve?.length,
tradesLength: rustResult.trades?.length,
finalPositions: rustResult.final_positions
});
// Store OHLC data for each symbol
const ohlcData: Record<string, any[]> = {};
@ -124,7 +137,7 @@ export class RustBacktestAdapter extends EventEmitter {
timestamp: new Date(point[0]).getTime(),
value: point[1],
})),
trades: rustResult.trades || [],
trades: this.transformFillsToTrades(rustResult.trades || []),
dailyReturns: this.calculateDailyReturns(rustResult.equity_curve),
finalPositions: rustResult.final_positions || {},
executionTime: Date.now() - startTime,
@ -183,7 +196,9 @@ export class RustBacktestAdapter extends EventEmitter {
private async loadHistoricalData(config: BacktestConfig): Promise<void> {
const startDate = new Date(config.startDate);
const endDate = new Date(config.endDate);
const allMarketData = [];
// Collect all data for all symbols
for (const symbol of config.symbols) {
const bars = await this.storageService.getHistoricalBars(
symbol,
@ -205,11 +220,17 @@ export class RustBacktestAdapter extends EventEmitter {
vwap: bar.vwap || (bar.high + bar.low + bar.close) / 3,
}));
// Load into Rust engine
if (this.currentEngine) {
this.container.logger.info(`Loading ${marketData.length} bars for ${symbol} into Rust engine`);
this.currentEngine.loadMarketData(marketData);
}
allMarketData.push(...marketData);
this.container.logger.info(`Collected ${marketData.length} bars for ${symbol}`);
}
// Sort all data by timestamp to ensure chronological order
allMarketData.sort((a, b) => a.timestamp - b.timestamp);
// Load all data at once into Rust engine
if (this.currentEngine) {
this.container.logger.info(`Loading ${allMarketData.length} total bars into Rust engine`);
this.currentEngine.loadMarketData(allMarketData);
}
}
@ -229,29 +250,53 @@ export class RustBacktestAdapter extends EventEmitter {
});
// Create a TypeScript strategy callback
let callCount = 0;
const callback = (callJson: string) => {
callCount++;
const call = JSON.parse(callJson);
// Log every 10th call to see if we're getting data
if (callCount % 10 === 1) {
this.container.logger.info(`Strategy callback called ${callCount} times, method: ${call.method}`);
}
if (call.method === 'on_market_data') {
const marketData = call.data;
const signals: any[] = [];
// Debug log first few data points
if (priceHistory.size === 0) {
this.container.logger.debug('First market data received:', marketData);
this.container.logger.info('First market data received:', JSON.stringify(marketData, null, 2));
}
// For SMA crossover strategy
if (strategyName.toLowerCase().includes('sma') || strategyName.toLowerCase().includes('crossover')) {
// Check if it's bar data
const isBar = marketData.data?.Bar || (marketData.data && 'close' in marketData.data);
// Log the structure to understand the data format
if (callCount === 1) {
this.container.logger.info('Market data structure:', {
hasData: !!marketData.data,
hasBar: !!marketData.data?.Bar,
hasClose: !!marketData.data?.close,
dataKeys: marketData.data ? Object.keys(marketData.data) : [],
});
}
// Check if it's bar data - handle different possible structures
const isBar = marketData.data?.Bar ||
(marketData.data && 'close' in marketData.data) ||
(marketData && 'close' in marketData);
if (isBar) {
const symbol = marketData.symbol;
// Handle both direct properties and nested Bar structure
const barData = marketData.data.Bar || marketData.data;
const barData = marketData.data?.Bar || marketData.data || marketData;
const price = barData.close;
// Log that we're processing bar data
if (callCount <= 3) {
this.container.logger.info(`Processing bar data for ${symbol}, price: ${price}`);
}
// Update price history
if (!priceHistory.has(symbol)) {
priceHistory.set(symbol, []);
@ -271,6 +316,11 @@ export class RustBacktestAdapter extends EventEmitter {
const fastSMA = history.slice(-fastPeriod).reduce((a, b) => a + b, 0) / fastPeriod;
const slowSMA = history.reduce((a, b) => a + b, 0) / slowPeriod;
// Log SMA values periodically
if (history.length % 5 === 0 || history.length === slowPeriod) {
this.container.logger.debug(`SMAs for ${symbol}: Fast(${fastPeriod})=${fastSMA.toFixed(2)}, Slow(${slowPeriod})=${slowSMA.toFixed(2)}, Price=${price.toFixed(2)}, History length=${history.length}`);
}
// Previous SMAs (if we have enough history)
if (history.length > slowPeriod) {
const prevHistory = history.slice(0, -1);
@ -279,6 +329,11 @@ export class RustBacktestAdapter extends EventEmitter {
const currentPosition = positions.get(symbol) || 0;
// Log crossover checks periodically
if (history.length % 10 === 0) {
this.container.logger.debug(`Crossover check for ${symbol}: prevFast=${prevFastSMA.toFixed(2)}, prevSlow=${prevSlowSMA.toFixed(2)}, currFast=${fastSMA.toFixed(2)}, currSlow=${slowSMA.toFixed(2)}, position=${currentPosition}`);
}
// Golden cross - buy signal
if (prevFastSMA <= prevSlowSMA && fastSMA > slowSMA && currentPosition <= 0) {
this.container.logger.info(`Golden cross detected for ${symbol} at price ${price}`);
@ -305,6 +360,11 @@ export class RustBacktestAdapter extends EventEmitter {
positions.set(symbol, -1);
}
}
} else {
// Log while building up history
if (history.length % 5 === 0 || history.length === 1) {
this.container.logger.debug(`Building history for ${symbol}: ${history.length}/${slowPeriod} bars collected`);
}
}
}
}
@ -377,4 +437,27 @@ export class RustBacktestAdapter extends EventEmitter {
sortinoRatio: 0,
};
}
private transformFillsToTrades(completedTrades: any[]): any[] {
// Now we have CompletedTrade objects with symbol and side information
return completedTrades.map((trade, index) => {
const timestamp = new Date(trade.timestamp);
const side = trade.side === 'Buy' ? 'buy' : 'sell';
return {
id: `trade-${index}`,
symbol: trade.symbol,
entryDate: timestamp.toISOString(),
exitDate: timestamp.toISOString(), // Same as entry for individual fills
entryPrice: trade.price,
exitPrice: trade.price,
quantity: trade.quantity,
side,
pnl: 0, // Would need to calculate from paired trades
pnlPercent: 0,
commission: trade.commission,
duration: 0, // Would need to calculate from paired trades
};
});
}
}