fixed up rust system
This commit is contained in:
parent
063f4c8e27
commit
a58072cf93
7 changed files with 255 additions and 44 deletions
|
|
@ -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
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue