diff --git a/apps/stock/core/index.node b/apps/stock/core/index.node index 476af2c..e647a4e 100755 Binary files a/apps/stock/core/index.node and b/apps/stock/core/index.node differ diff --git a/apps/stock/core/src/positions/mod.rs b/apps/stock/core/src/positions/mod.rs index 48401eb..028c99c 100644 --- a/apps/stock/core/src/positions/mod.rs +++ b/apps/stock/core/src/positions/mod.rs @@ -110,10 +110,65 @@ impl PositionTracker { // Handle trade matching for closed trades match side { Side::Buy => { - // For buy orders, just add to open trades - self.open_trades.entry(symbol.to_string()) - .or_insert_with(Vec::new) - .push(trade_record); + // For buy orders, try to match with open sell trades (closing shorts) + if let Some(mut open_trades) = self.open_trades.get_mut(symbol) { + let mut remaining_quantity = fill.quantity; + let mut trades_to_remove = Vec::new(); + + // FIFO matching against short positions + for (idx, open_trade) in open_trades.iter_mut().enumerate() { + if open_trade.side == Side::Sell && remaining_quantity > 0.0 { + let close_quantity = remaining_quantity.min(open_trade.quantity); + + // Create closed trade record for short position + let closed_trade = ClosedTrade { + id: format!("CT{}", self.generate_trade_id()), + symbol: symbol.to_string(), + entry_time: open_trade.timestamp, + exit_time: fill.timestamp, + entry_price: open_trade.price, + exit_price: fill.price, + quantity: close_quantity, + side: Side::Sell, // Opening side (short) + pnl: close_quantity * (open_trade.price - fill.price) - (open_trade.commission + fill.commission * close_quantity / fill.quantity), + pnl_percent: ((open_trade.price - fill.price) / open_trade.price) * 100.0, + commission: open_trade.commission + fill.commission * close_quantity / fill.quantity, + duration_ms: (fill.timestamp - open_trade.timestamp).num_milliseconds(), + entry_fill_id: open_trade.id.clone(), + exit_fill_id: trade_record.id.clone(), + }; + + self.closed_trades.write().push(closed_trade); + + // Update quantities + remaining_quantity -= close_quantity; + open_trade.quantity -= close_quantity; + + if open_trade.quantity <= 0.0 { + trades_to_remove.push(idx); + } + } + } + + // Remove fully closed trades + for idx in trades_to_remove.into_iter().rev() { + open_trades.remove(idx); + } + + // If we still have quantity left, it's a new long position + if remaining_quantity > 0.0 { + let long_trade = TradeRecord { + quantity: remaining_quantity, + ..trade_record.clone() + }; + open_trades.push(long_trade); + } + } else { + // No open trades, start a new long position + self.open_trades.entry(symbol.to_string()) + .or_insert_with(Vec::new) + .push(trade_record); + } } Side::Sell => { // For sell orders, try to match with open buy trades @@ -169,6 +224,11 @@ impl PositionTracker { }; open_trades.push(short_trade); } + } else { + // No open trades, start a new short position + self.open_trades.entry(symbol.to_string()) + .or_insert_with(Vec::new) + .push(trade_record); } } } diff --git a/apps/stock/orchestrator/examples/debug-position-tracking.ts b/apps/stock/orchestrator/examples/debug-position-tracking.ts new file mode 100644 index 0000000..c6c83a4 --- /dev/null +++ b/apps/stock/orchestrator/examples/debug-position-tracking.ts @@ -0,0 +1,113 @@ +import { BacktestEngine } from '../src/backtest/BacktestEngine'; +import { StrategyManager } from '../src/strategies/StrategyManager'; +import { StorageService } from '../src/services/StorageService'; +import { ModeManager } from '../src/core/ModeManager'; +import { MarketDataService } from '../src/services/MarketDataService'; +import { ExecutionService } from '../src/services/ExecutionService'; +import { IServiceContainer } from '@stock-bot/di'; + +async function debugPositionTracking() { + // Create service container with detailed logging + const container: IServiceContainer = { + logger: { + info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args), + error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args), + warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args), + debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args), + } as any, + custom: {} + }; + + // Initialize services + const storageService = new StorageService(); + const marketDataService = new MarketDataService(container); + const executionService = new ExecutionService(container); + const modeManager = new ModeManager(container, marketDataService, executionService, storageService); + const strategyManager = new StrategyManager(container); + + // Set services in container + container.custom = { + MarketDataService: marketDataService, + ExecutionService: executionService, + ModeManager: modeManager, + StorageService: storageService + }; + + // Initialize backtest mode with full config + await modeManager.initializeMode({ + mode: 'backtest', + startDate: '2023-06-01T00:00:00Z', + endDate: '2023-09-01T00:00:00Z', // Just 3 months for debugging + speed: 'max', + symbols: ['AAPL'], + initialCapital: 100000, + dataFrequency: '1d', + strategy: 'sma-crossover' + }); + + // Create backtest engine + const backtestEngine = new BacktestEngine(container, storageService, strategyManager); + + // Listen to strategy events for debugging + strategyManager.on('order', (orderEvent: any) => { + console.log('\nšŸ“Š STRATEGY ORDER EVENT:', orderEvent); + }); + + // Configure backtest + const config = { + mode: 'backtest' as const, + name: 'Position Tracking Debug', + strategy: 'sma-crossover', + symbols: ['AAPL'], + startDate: '2023-06-01T00:00:00Z', + endDate: '2023-09-01T00:00:00Z', + initialCapital: 100000, + commission: 0.001, + slippage: 0.0001, + dataFrequency: '1d' as const, + speed: 'max' as const + }; + + console.log('Starting position tracking debug...\n'); + + try { + const result = await backtestEngine.runBacktest(config); + + console.log('\n=== FINAL RESULTS ==='); + console.log(`Total Trades: ${result.metrics.totalTrades}`); + console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`); + + console.log('\n=== TRADE DETAILS ==='); + let buyCount = 0; + let sellCount = 0; + + result.trades.forEach((trade, i) => { + console.log(`\nTrade ${i + 1}:`); + console.log(` Symbol: ${trade.symbol}`); + console.log(` Side: ${trade.side}`); + console.log(` Entry: ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Exit: ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` Quantity: ${trade.quantity}`); + console.log(` P&L: $${trade.pnl.toFixed(2)}`); + + if (trade.side === 'buy') buyCount++; + else if (trade.side === 'sell') sellCount++; + }); + + console.log(`\n=== TRADE SIDE SUMMARY ===`); + console.log(`Buy trades: ${buyCount}`); + console.log(`Sell trades: ${sellCount}`); + + // Check final positions + console.log('\n=== FINAL POSITIONS ==='); + result.positions.forEach(pos => { + console.log(`${pos.symbol}: ${pos.quantity} shares @ avg $${pos.averagePrice.toFixed(2)}`); + }); + + } catch (error) { + console.error('Debug failed:', error); + } +} + +// Run the debug +debugPositionTracking().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/examples/debug-rust-short-trades.ts b/apps/stock/orchestrator/examples/debug-rust-short-trades.ts new file mode 100644 index 0000000..19f3cf4 --- /dev/null +++ b/apps/stock/orchestrator/examples/debug-rust-short-trades.ts @@ -0,0 +1,129 @@ +import { TradingEngine } from '@stock-bot/core'; + +async function debugRustShortTrades() { + // Create engine config for backtest mode + const engineConfig = { + startTime: new Date('2023-01-01').getTime(), + endTime: new Date('2023-02-01').getTime(), + speedMultiplier: 0 + }; + + console.log('=== RUST CORE SHORT TRADE DEBUG ===\n'); + + console.log('1. Creating TradingEngine in backtest mode...'); + const engine = new TradingEngine('backtest', engineConfig); + + // Simulate opening a short position + console.log('\n2. Submitting SELL order to open short position...'); + const sellOrder = { + id: 'sell-order-001', + symbol: 'TEST', + side: 'sell', + quantity: 100, + orderType: 'market', + timeInForce: 'DAY' + }; + const sellOrderId = engine.submitOrder(sellOrder); + console.log(` Sell order ID: ${sellOrderId}`); + + // Process the sell fill + console.log('\n3. Processing SELL fill...'); + const sellFillResult = engine.processFillWithMetadata( + 'TEST', // symbol + 50.0, // price + 100, // quantity + 'sell', // side + 0.1, // commission + sellOrderId, // orderId + 'debug-strategy' // strategyId + ); + console.log(` Fill result: ${sellFillResult}`); + + // Check positions + console.log('\n4. Checking positions after SELL...'); + const positionsAfterSell = engine.getPosition('TEST'); + console.log(' Position:', positionsAfterSell); + + // Check open trades + const openTradesAfterSell = JSON.parse(engine.getOpenTrades()); + console.log(' Open trades:', openTradesAfterSell.length); + if (openTradesAfterSell.length > 0) { + console.log(' First open trade:', openTradesAfterSell[0]); + } + + // Check closed trades + const closedTradesAfterSell = JSON.parse(engine.getClosedTrades()); + console.log(' Closed trades:', closedTradesAfterSell.length); + + // Now close the short with a buy order + console.log('\n5. Submitting BUY order to close short position...'); + const buyOrder = { + id: 'buy-order-001', + symbol: 'TEST', + side: 'buy', + quantity: 100, + orderType: 'market', + timeInForce: 'DAY' + }; + const buyOrderId = engine.submitOrder(buyOrder); + console.log(` Buy order ID: ${buyOrderId}`); + + // Process the buy fill + console.log('\n6. Processing BUY fill...'); + const buyFillResult = engine.processFillWithMetadata( + 'TEST', // symbol + 48.0, // price (profit on short) + 100, // quantity + 'buy', // side + 0.1, // commission + buyOrderId, // orderId + 'debug-strategy' // strategyId + ); + console.log(` Fill result: ${buyFillResult}`); + + // Check positions after closing + console.log('\n7. Checking positions after BUY...'); + const positionsAfterBuy = engine.getPosition('TEST'); + console.log(' Position:', positionsAfterBuy); + + // Check open trades after closing + const openTradesAfterBuy = JSON.parse(engine.getOpenTrades()); + console.log(' Open trades:', openTradesAfterBuy.length); + + // Check closed trades after closing - THIS IS WHERE WE SHOULD SEE THE SHORT TRADE + const closedTradesAfterBuy = JSON.parse(engine.getClosedTrades()); + console.log(' Closed trades:', closedTradesAfterBuy.length); + + if (closedTradesAfterBuy.length > 0) { + console.log('\n8. Closed trade details:'); + closedTradesAfterBuy.forEach((trade: any, i: number) => { + console.log(` Trade ${i + 1}:`); + console.log(` ID: ${trade.id}`); + console.log(` Side: ${trade.side}`); + console.log(` Entry: ${trade.entry_price} @ ${trade.entry_time}`); + console.log(` Exit: ${trade.exit_price} @ ${trade.exit_time}`); + console.log(` P&L: ${trade.pnl} (${trade.pnl_percent}%)`); + console.log(` Quantity: ${trade.quantity}`); + }); + } else { + console.log('\nāŒ ERROR: No closed trades found! Short trade was not recorded.'); + } + + // Also check trade history + console.log('\n9. Trade history:'); + const tradeHistory = JSON.parse(engine.getTradeHistory()); + tradeHistory.forEach((trade: any, i: number) => { + console.log(` Trade ${i + 1}: ${trade.side} ${trade.quantity} @ ${trade.price}`); + }); + + // Check totals + console.log('\n10. Summary:'); + console.log(` Total trades: ${engine.getTradeCount()}`); + console.log(` Closed trades: ${engine.getClosedTradeCount()}`); + const [realizedPnl, unrealizedPnl] = engine.getTotalPnl(); + console.log(` Realized P&L: ${realizedPnl}`); + console.log(` Unrealized P&L: ${unrealizedPnl}`); +} + +// Run the debug +debugRustShortTrades().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/examples/force-both-trade-types.ts b/apps/stock/orchestrator/examples/force-both-trade-types.ts new file mode 100644 index 0000000..5f48b0a --- /dev/null +++ b/apps/stock/orchestrator/examples/force-both-trade-types.ts @@ -0,0 +1,161 @@ +import { BacktestEngine } from '../src/backtest/BacktestEngine'; +import { StrategyManager } from '../src/strategies/StrategyManager'; +import { StorageService } from '../src/services/StorageService'; +import { ModeManager } from '../src/core/ModeManager'; +import { MarketDataService } from '../src/services/MarketDataService'; +import { ExecutionService } from '../src/services/ExecutionService'; +import { IServiceContainer } from '@stock-bot/di'; + +// Modify the data generation to ensure we get both golden and death crosses +function generateMockDataWithBothCrosses() { + const data = []; + const startDate = new Date('2023-01-01'); + let price = 150; + + // Create 365 days of data with clear trends + for (let i = 0; i < 365; i++) { + const date = new Date(startDate); + date.setDate(date.getDate() + i); + + // Create alternating trends every ~30 days + const cycleDay = i % 60; + if (cycleDay < 30) { + // Uptrend - will create golden crosses + price += Math.random() * 2 - 0.5; // Bias upward + } else { + // Downtrend - will create death crosses + price -= Math.random() * 2 - 0.5; // Bias downward + } + + // Add some volatility + const dayChange = (Math.random() - 0.5) * 4; + price = Math.max(100, Math.min(200, price + dayChange)); + + data.push({ + timestamp: date.toISOString(), + open: price - Math.random() * 2, + high: price + Math.random() * 2, + low: price - Math.random() * 2, + close: price, + volume: Math.floor(Math.random() * 10000000) + 5000000 + }); + } + + return data; +} + +async function forceBothTradeTypes() { + // Create service container with minimal logging + const container: IServiceContainer = { + logger: { + info: (msg: string, ...args: any[]) => { + if (msg.includes('cross detected') || msg.includes('Generated') || msg.includes('Position update')) { + console.log('[INFO]', msg, ...args); + } + }, + error: console.error, + warn: () => {}, + debug: () => {}, + } as any, + custom: {} + }; + + // Initialize services + const storageService = new StorageService(); + const marketDataService = new MarketDataService(container); + const executionService = new ExecutionService(container); + const modeManager = new ModeManager(container, marketDataService, executionService, storageService); + const strategyManager = new StrategyManager(container); + + container.custom = { + MarketDataService: marketDataService, + ExecutionService: executionService, + ModeManager: modeManager, + StorageService: storageService + }; + + // Override the mock data generation + const originalGenerateMockData = marketDataService.generateMockData; + marketDataService.generateMockData = function(symbol: string, startDate: string, endDate: string) { + console.log('Generating mock data with forced crossovers...'); + return generateMockDataWithBothCrosses(); + }; + + // Initialize backtest mode + await modeManager.initializeMode({ + mode: 'backtest', + startDate: '2023-01-01T00:00:00Z', + endDate: '2024-01-01T00:00:00Z', + speed: 'max', + symbols: ['AAPL'], + initialCapital: 100000, + dataFrequency: '1d', + strategy: 'sma-crossover' + }); + + // Create backtest engine + const backtestEngine = new BacktestEngine(container, storageService, strategyManager); + + // Configure backtest + const config = { + mode: 'backtest' as const, + name: 'Force Both Trade Types', + strategy: 'sma-crossover', + symbols: ['AAPL'], + startDate: '2023-01-01T00:00:00Z', + endDate: '2024-01-01T00:00:00Z', + initialCapital: 100000, + commission: 0.001, + slippage: 0.0001, + dataFrequency: '1d' as const, + speed: 'max' as const + }; + + console.log('=== TESTING WITH FORCED CROSSOVERS ===\n'); + + try { + const result = await backtestEngine.runBacktest(config); + + console.log('\n=== BACKTEST COMPLETE ==='); + console.log(`Total Trades: ${result.metrics.totalTrades}`); + + // Analyze trades + let longTrades = 0; + let shortTrades = 0; + + result.trades.forEach((trade) => { + if (trade.side === 'buy') { + longTrades++; + console.log(`\nLONG trade found:`); + console.log(` Entry: ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Exit: ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` P&L: $${trade.pnl.toFixed(2)}`); + } else { + shortTrades++; + console.log(`\nSHORT trade found:`); + console.log(` Entry: ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Exit: ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` P&L: $${trade.pnl.toFixed(2)}`); + } + }); + + console.log('\n=== FINAL SUMMARY ==='); + console.log(`LONG trades: ${longTrades}`); + console.log(`SHORT trades: ${shortTrades}`); + console.log(`Total trades: ${result.metrics.totalTrades}`); + + if (longTrades > 0 && shortTrades > 0) { + console.log('\nāœ… SUCCESS: Both long and short trades are working!'); + } else { + console.log('\nāŒ ISSUE: Missing trade types'); + if (longTrades === 0) console.log(' - No long trades found'); + if (shortTrades === 0) console.log(' - No short trades found'); + } + + } catch (error) { + console.error('Test failed:', error); + } +} + +// Run the test +forceBothTradeTypes().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/examples/show-order-flow.ts b/apps/stock/orchestrator/examples/show-order-flow.ts new file mode 100644 index 0000000..c45300d --- /dev/null +++ b/apps/stock/orchestrator/examples/show-order-flow.ts @@ -0,0 +1,99 @@ +import { BacktestEngine } from '../src/backtest/BacktestEngine'; +import { StrategyManager } from '../src/strategies/StrategyManager'; +import { StorageService } from '../src/services/StorageService'; +import { ModeManager } from '../src/core/ModeManager'; +import { MarketDataService } from '../src/services/MarketDataService'; +import { ExecutionService } from '../src/services/ExecutionService'; +import { IServiceContainer } from '@stock-bot/di'; + +async function showOrderFlow() { + // Create service container + const container: IServiceContainer = { + logger: { + info: (msg: string, ...args: any[]) => { + // Only log order-related messages + if (msg.includes('order') || msg.includes('Order') || msg.includes('Filling') || msg.includes('cross detected')) { + console.log('[INFO]', msg, ...args); + } + }, + error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args), + warn: (msg: string, ...args: any[]) => {}, + debug: (msg: string, ...args: any[]) => {}, + } as any, + custom: {} + }; + + // Initialize services + const storageService = new StorageService(); + const marketDataService = new MarketDataService(container); + const executionService = new ExecutionService(container); + const modeManager = new ModeManager(container, marketDataService, executionService, storageService); + const strategyManager = new StrategyManager(container); + + // Set services in container + container.custom = { + MarketDataService: marketDataService, + ExecutionService: executionService, + ModeManager: modeManager, + StorageService: storageService + }; + + // Initialize backtest mode + await modeManager.initializeMode({ + mode: 'backtest', + startDate: '2023-07-01T00:00:00Z', + endDate: '2023-08-01T00:00:00Z', // Just 1 month + speed: 'max', + symbols: ['AAPL'], + initialCapital: 100000, + dataFrequency: '1d', + strategy: 'sma-crossover' + }); + + // Create backtest engine + const backtestEngine = new BacktestEngine(container, storageService, strategyManager); + + // Configure backtest + const config = { + mode: 'backtest' as const, + name: 'Order Flow Demo', + strategy: 'sma-crossover', + symbols: ['AAPL'], + startDate: '2023-07-01T00:00:00Z', + endDate: '2023-08-01T00:00:00Z', + initialCapital: 100000, + commission: 0.001, + slippage: 0.0001, + dataFrequency: '1d' as const, + speed: 'max' as const + }; + + console.log('\n=== ORDER FLOW DEMONSTRATION ===\n'); + console.log('This will show the actual BUY and SELL orders being generated and executed:\n'); + + try { + const result = await backtestEngine.runBacktest(config); + + console.log('\n=== COMPLETED TRADES (Round-trips) ==='); + console.log('Note: Each trade below represents a BUY order (entry) + SELL order (exit):\n'); + + result.trades.forEach((trade, i) => { + console.log(`Trade ${i + 1}:`); + console.log(` Opened with: BUY order on ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Closed with: SELL order on ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` Trade type: ${trade.side === 'buy' ? 'LONG' : 'SHORT'} position`); + console.log(` P&L: $${trade.pnl.toFixed(2)} (${trade.pnlPercent.toFixed(2)}%)\n`); + }); + + console.log('Summary:'); + console.log(`- Total completed trades: ${result.metrics.totalTrades}`); + console.log(`- Each trade = 1 BUY order + 1 SELL order`); + console.log(`- Total orders executed: ${result.metrics.totalTrades * 2}`); + + } catch (error) { + console.error('Failed:', error); + } +} + +// Run the demo +showOrderFlow().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/examples/test-short-trades.ts b/apps/stock/orchestrator/examples/test-short-trades.ts new file mode 100644 index 0000000..ce1f945 --- /dev/null +++ b/apps/stock/orchestrator/examples/test-short-trades.ts @@ -0,0 +1,103 @@ +import { BacktestEngine } from '../src/backtest/BacktestEngine'; +import { StrategyManager } from '../src/strategies/StrategyManager'; +import { StorageService } from '../src/services/StorageService'; +import { ModeManager } from '../src/core/ModeManager'; +import { MarketDataService } from '../src/services/MarketDataService'; +import { ExecutionService } from '../src/services/ExecutionService'; +import { IServiceContainer } from '@stock-bot/di'; + +async function testShortTrades() { + // Create service container + const container: IServiceContainer = { + logger: { + info: () => {}, + error: console.error, + warn: () => {}, + debug: () => {}, + } as any, + custom: {} + }; + + // Initialize services + const storageService = new StorageService(); + const marketDataService = new MarketDataService(container); + const executionService = new ExecutionService(container); + const modeManager = new ModeManager(container, marketDataService, executionService, storageService); + const strategyManager = new StrategyManager(container); + + container.custom = { + MarketDataService: marketDataService, + ExecutionService: executionService, + ModeManager: modeManager, + StorageService: storageService + }; + + // Initialize backtest mode + await modeManager.initializeMode({ + mode: 'backtest', + startDate: '2023-01-01T00:00:00Z', + endDate: '2024-01-01T00:00:00Z', + speed: 'max', + symbols: ['AAPL'], + initialCapital: 100000, + dataFrequency: '1d', + strategy: 'sma-crossover' + }); + + // Create backtest engine + const backtestEngine = new BacktestEngine(container, storageService, strategyManager); + + // Configure backtest + const config = { + mode: 'backtest' as const, + name: 'Short Trade Test', + strategy: 'sma-crossover', + symbols: ['AAPL'], + startDate: '2023-01-01T00:00:00Z', + endDate: '2024-01-01T00:00:00Z', + initialCapital: 100000, + commission: 0.001, + slippage: 0.0001, + dataFrequency: '1d' as const, + speed: 'max' as const + }; + + console.log('Testing short trade recording...\n'); + + try { + const result = await backtestEngine.runBacktest(config); + + console.log('=== BACKTEST RESULTS ==='); + console.log(`Total Trades: ${result.metrics.totalTrades}`); + console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`); + console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`); + + // Count long vs short trades + let longTrades = 0; + let shortTrades = 0; + + console.log('\n=== TRADE BREAKDOWN ==='); + result.trades.forEach((trade, i) => { + const tradeType = trade.side === 'buy' ? 'LONG' : 'SHORT'; + if (trade.side === 'buy') longTrades++; + else shortTrades++; + + console.log(`Trade ${i + 1} (${tradeType}):`); + console.log(` Entry: ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Exit: ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` P&L: $${trade.pnl.toFixed(2)} (${trade.pnlPercent.toFixed(2)}%)`); + console.log(''); + }); + + console.log('=== TRADE TYPE SUMMARY ==='); + console.log(`Long trades: ${longTrades}`); + console.log(`Short trades: ${shortTrades}`); + console.log(`Total trades: ${result.metrics.totalTrades}`); + + } catch (error) { + console.error('Test failed:', error); + } +} + +// Run the test +testShortTrades().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/examples/trace-short-positions.ts b/apps/stock/orchestrator/examples/trace-short-positions.ts new file mode 100644 index 0000000..68cd5be --- /dev/null +++ b/apps/stock/orchestrator/examples/trace-short-positions.ts @@ -0,0 +1,188 @@ +import { BacktestEngine } from '../src/backtest/BacktestEngine'; +import { StrategyManager } from '../src/strategies/StrategyManager'; +import { StorageService } from '../src/services/StorageService'; +import { ModeManager } from '../src/core/ModeManager'; +import { MarketDataService } from '../src/services/MarketDataService'; +import { ExecutionService } from '../src/services/ExecutionService'; +import { IServiceContainer } from '@stock-bot/di'; + +async function traceShortPositions() { + let orderCount = 0; + let fillCount = 0; + const orderTracker: any[] = []; + const fillTracker: any[] = []; + + // Create service container with detailed logging + const container: IServiceContainer = { + logger: { + info: (msg: string, ...args: any[]) => { + // Log all order and fill events + if (msg.includes('order') || msg.includes('Order') || + msg.includes('fill') || msg.includes('Fill') || + msg.includes('position') || msg.includes('Position')) { + console.log('[INFO]', msg, ...args); + } + + // Track order submissions + if (msg.includes('Submitting order')) { + orderCount++; + const orderData = args[0]; + orderTracker.push({ + count: orderCount, + symbol: orderData.symbol, + side: orderData.side, + quantity: orderData.quantity, + type: orderData.type + }); + console.log(`\nšŸ”µ ORDER #${orderCount}:`, orderData.side.toUpperCase(), orderData.symbol); + } + + // Track fills + if (msg.includes('Order filled')) { + fillCount++; + const fillData = args[0]; + fillTracker.push({ + count: fillCount, + orderId: fillData.orderId, + symbol: fillData.symbol, + side: fillData.side, + price: fillData.price, + quantity: fillData.quantity + }); + console.log(`\n🟢 FILL #${fillCount}:`, fillData.side.toUpperCase(), fillData.symbol, '@', fillData.price); + } + }, + error: console.error, + warn: () => {}, + debug: () => {}, + } as any, + custom: {} + }; + + // Initialize services + const storageService = new StorageService(); + const marketDataService = new MarketDataService(container); + const executionService = new ExecutionService(container); + const modeManager = new ModeManager(container, marketDataService, executionService, storageService); + const strategyManager = new StrategyManager(container); + + container.custom = { + MarketDataService: marketDataService, + ExecutionService: executionService, + ModeManager: modeManager, + StorageService: storageService + }; + + // Listen to strategy events + strategyManager.on('signal', (signal: any) => { + console.log('\nšŸŽÆ STRATEGY SIGNAL:', signal); + }); + + // Initialize backtest mode + await modeManager.initializeMode({ + mode: 'backtest', + startDate: '2023-06-01T00:00:00Z', + endDate: '2023-09-01T00:00:00Z', // 3 months + speed: 'max', + symbols: ['AAPL'], + initialCapital: 100000, + dataFrequency: '1d', + strategy: 'sma-crossover' + }); + + // Create backtest engine + const backtestEngine = new BacktestEngine(container, storageService, strategyManager); + + // Configure backtest + const config = { + mode: 'backtest' as const, + name: 'Short Position Trace', + strategy: 'sma-crossover', + symbols: ['AAPL'], + startDate: '2023-06-01T00:00:00Z', + endDate: '2023-09-01T00:00:00Z', + initialCapital: 100000, + commission: 0.001, + slippage: 0.0001, + dataFrequency: '1d' as const, + speed: 'max' as const + }; + + console.log('=== TRACING SHORT POSITION FLOW ===\n'); + + try { + const result = await backtestEngine.runBacktest(config); + + console.log('\n=== ORDER/FILL TRACKING SUMMARY ==='); + console.log(`Total orders submitted: ${orderCount}`); + console.log(`Total fills executed: ${fillCount}`); + + // Analyze order flow + let buyOrders = 0; + let sellOrders = 0; + orderTracker.forEach(order => { + if (order.side === 'buy') buyOrders++; + else sellOrders++; + }); + + console.log(`\nOrder breakdown:`); + console.log(`- BUY orders: ${buyOrders}`); + console.log(`- SELL orders: ${sellOrders}`); + + // Check for short positions + console.log('\n=== LOOKING FOR SHORT PATTERNS ==='); + console.log('(A short position starts with a SELL and closes with a BUY)\n'); + + let potentialShorts = 0; + for (let i = 0; i < orderTracker.length - 1; i++) { + if (orderTracker[i].side === 'sell') { + // Look for subsequent buy to close + for (let j = i + 1; j < orderTracker.length; j++) { + if (orderTracker[j].side === 'buy' && orderTracker[j].symbol === orderTracker[i].symbol) { + potentialShorts++; + console.log(`Potential short trade found:`); + console.log(` Open: Order #${orderTracker[i].count} (SELL)`); + console.log(` Close: Order #${orderTracker[j].count} (BUY)`); + break; + } + } + } + } + + console.log(`\nPotential short trades identified: ${potentialShorts}`); + + // Final results + console.log('\n=== BACKTEST RESULTS ==='); + console.log(`Total closed trades: ${result.metrics.totalTrades}`); + + let longTrades = 0; + let shortTrades = 0; + + result.trades.forEach((trade, i) => { + const tradeType = trade.side === 'buy' ? 'LONG' : 'SHORT'; + if (trade.side === 'buy') longTrades++; + else shortTrades++; + + console.log(`\nTrade ${i + 1} (${tradeType}):`); + console.log(` Entry: ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Exit: ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` P&L: $${trade.pnl.toFixed(2)}`); + }); + + console.log('\n=== FINAL SUMMARY ==='); + console.log(`Long trades recorded: ${longTrades}`); + console.log(`Short trades recorded: ${shortTrades}`); + console.log(`Expected short trades (from order flow): ${potentialShorts}`); + + if (shortTrades < potentialShorts) { + console.log('\nāŒ ISSUE: Not all short trades were recorded in closed trades!'); + console.log('This suggests the Rust core trade matching logic may have an issue.'); + } + + } catch (error) { + console.error('Trace failed:', error); + } +} + +// Run the trace +traceShortPositions().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/examples/verify-both-trade-types.ts b/apps/stock/orchestrator/examples/verify-both-trade-types.ts new file mode 100644 index 0000000..d4ae8c4 --- /dev/null +++ b/apps/stock/orchestrator/examples/verify-both-trade-types.ts @@ -0,0 +1,133 @@ +import { BacktestEngine } from '../src/backtest/BacktestEngine'; +import { StrategyManager } from '../src/strategies/StrategyManager'; +import { StorageService } from '../src/services/StorageService'; +import { ModeManager } from '../src/core/ModeManager'; +import { MarketDataService } from '../src/services/MarketDataService'; +import { ExecutionService } from '../src/services/ExecutionService'; +import { IServiceContainer } from '@stock-bot/di'; + +async function verifyBothTradeTypes() { + // Create service container + const container: IServiceContainer = { + logger: { + info: console.log, + error: console.error, + warn: console.warn, + debug: console.log, + } as any, + custom: {} + }; + + // Initialize services + const storageService = new StorageService(); + const marketDataService = new MarketDataService(container); + const executionService = new ExecutionService(container); + const modeManager = new ModeManager(container, marketDataService, executionService, storageService); + const strategyManager = new StrategyManager(container); + + container.custom = { + MarketDataService: marketDataService, + ExecutionService: executionService, + ModeManager: modeManager, + StorageService: storageService + }; + + // Initialize backtest mode + await modeManager.initializeMode({ + mode: 'backtest', + startDate: '2023-01-01T00:00:00Z', + endDate: '2024-01-01T00:00:00Z', + speed: 'max', + symbols: ['AAPL'], + initialCapital: 100000, + dataFrequency: '1d', + strategy: 'sma-crossover' + }); + + // Create backtest engine + const backtestEngine = new BacktestEngine(container, storageService, strategyManager); + + // Configure backtest + const config = { + mode: 'backtest' as const, + name: 'Trade Type Verification', + strategy: 'sma-crossover', + symbols: ['AAPL'], + startDate: '2023-01-01T00:00:00Z', + endDate: '2024-01-01T00:00:00Z', + initialCapital: 100000, + commission: 0.001, + slippage: 0.0001, + dataFrequency: '1d' as const, + speed: 'max' as const + }; + + console.log('=== VERIFYING BOTH LONG AND SHORT TRADES ===\n'); + + try { + const result = await backtestEngine.runBacktest(config); + + console.log('=== BACKTEST COMPLETE ==='); + console.log(`Total Trades: ${result.metrics.totalTrades}`); + console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`); + console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`); + + // Analyze trades + let longTrades = 0; + let shortTrades = 0; + let longPnL = 0; + let shortPnL = 0; + + console.log('\n=== DETAILED TRADE ANALYSIS ==='); + result.trades.forEach((trade, i) => { + const isLong = trade.side === 'buy'; + const tradeType = isLong ? 'LONG' : 'SHORT'; + + if (isLong) { + longTrades++; + longPnL += trade.pnl; + } else { + shortTrades++; + shortPnL += trade.pnl; + } + + console.log(`\nTrade ${i + 1}:`); + console.log(` Type: ${tradeType} position`); + console.log(` Opening side: ${trade.side.toUpperCase()}`); + console.log(` Entry: ${trade.entryDate} @ $${trade.entryPrice.toFixed(2)}`); + console.log(` Exit: ${trade.exitDate} @ $${trade.exitPrice.toFixed(2)}`); + console.log(` P&L: $${trade.pnl.toFixed(2)} (${trade.pnlPercent.toFixed(2)}%)`); + + // Explain the trade flow + if (isLong) { + console.log(` Order flow: BUY ${trade.quantity} shares → SELL ${trade.quantity} shares`); + } else { + console.log(` Order flow: SELL ${trade.quantity} shares → BUY ${trade.quantity} shares`); + } + }); + + console.log('\n=== SUMMARY BY TRADE TYPE ==='); + console.log(`LONG trades: ${longTrades}`); + console.log(` - Total P&L from longs: $${longPnL.toFixed(2)}`); + console.log(` - Average P&L per long: $${longTrades > 0 ? (longPnL / longTrades).toFixed(2) : '0.00'}`); + + console.log(`\nSHORT trades: ${shortTrades}`); + console.log(` - Total P&L from shorts: $${shortPnL.toFixed(2)}`); + console.log(` - Average P&L per short: $${shortTrades > 0 ? (shortPnL / shortTrades).toFixed(2) : '0.00'}`); + + console.log(`\nTotal trades: ${result.metrics.totalTrades}`); + + if (longTrades === 0) { + console.log('\nāš ļø WARNING: No long trades found!'); + } + if (shortTrades === 0) { + console.log('\nāš ļø WARNING: No short trades found!'); + } + + } catch (error) { + console.error('Test failed:', error); + } +} + +// Run the verification +verifyBothTradeTypes().catch(console.error); \ No newline at end of file diff --git a/apps/stock/orchestrator/src/strategies/StrategyManager.ts b/apps/stock/orchestrator/src/strategies/StrategyManager.ts index c869adf..745ae61 100644 --- a/apps/stock/orchestrator/src/strategies/StrategyManager.ts +++ b/apps/stock/orchestrator/src/strategies/StrategyManager.ts @@ -89,7 +89,7 @@ export class StrategyManager extends EventEmitter { case 'smacrossover': case 'sma-crossover': case 'moving-average': { - const { SimpleMovingAverageCrossover } = await import('./examples/SimpleMovingAverageCrossover'); + const { SimpleMovingAverageCrossover } = await import('./examples/SimpleMovingAverageCrossoverFixed'); strategy = new SimpleMovingAverageCrossover( config, this.container.custom?.ModeManager, diff --git a/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossover.ts b/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossover.ts index eb4c33d..cbe155c 100644 --- a/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossover.ts +++ b/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossover.ts @@ -110,9 +110,8 @@ export class SimpleMovingAverageCrossover extends BaseStrategy { } } - if (barsSinceLastTrade < this.MIN_HOLDING_BARS && lastTradeBar > 0) { - return null; // Too soon to trade again - } + // Note: We removed the MIN_HOLDING_BARS check here to allow closing and opening on same bar + // The check is now done individually for new position entries if (goldenCross) { logger.info(`🟢 Golden cross detected for ${symbol} @ ${timestamp}`); @@ -144,6 +143,7 @@ export class SimpleMovingAverageCrossover extends BaseStrategy { return signal; } else if (currentPosition === 0) { // No position, open long + logger.info(` No current position, opening long`); logger.info(` Price: ${currentPrice.toFixed(2)}, Fast MA: ${fastMA.toFixed(2)} > Slow MA: ${slowMA.toFixed(2)}`); logger.info(` Prev Fast MA: ${prevFastMA.toFixed(2)} <= Prev Slow MA: ${prevSlowMA.toFixed(2)}`); @@ -207,6 +207,8 @@ export class SimpleMovingAverageCrossover extends BaseStrategy { return signal; } else if (currentPosition === 0) { // Open short position for long/short strategy + logger.info(` No current position, opening short`); + const positionSize = this.calculatePositionSize(currentPrice); logger.info(` Opening short position: ${positionSize} shares`); diff --git a/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossoverFixed.ts b/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossoverFixed.ts new file mode 100644 index 0000000..8562770 --- /dev/null +++ b/apps/stock/orchestrator/src/strategies/examples/SimpleMovingAverageCrossoverFixed.ts @@ -0,0 +1,305 @@ +import { BaseStrategy, Signal } from '../BaseStrategy'; +import { MarketData } from '../../types'; +import { getLogger } from '@stock-bot/logger'; + +const logger = getLogger('SimpleMovingAverageCrossover'); + +export class SimpleMovingAverageCrossover extends BaseStrategy { + private priceHistory = new Map(); + private lastSignalBar = new Map(); + private barCount = new Map(); + private totalSignals = 0; + private lastCrossoverType = new Map(); + + // Strategy parameters + private readonly FAST_PERIOD = 5; // Changed from 10 to generate more signals + private readonly SLOW_PERIOD = 15; // Changed from 20 to generate more signals + private readonly POSITION_SIZE = 0.1; // 10% of capital per position + private readonly MIN_HOLDING_BARS = 3; // Minimum bars to hold position + private readonly DEBUG_INTERVAL = 10; // Log every N bars for debugging + + constructor(config: any, modeManager?: any, executionService?: any) { + super(config, modeManager, executionService); + logger.info(`SimpleMovingAverageCrossover initialized with Fast=${this.FAST_PERIOD}, Slow=${this.SLOW_PERIOD}`); + } + + protected updateIndicators(data: MarketData): void { + if (data.type !== 'bar') return; + + const symbol = data.data.symbol; + const price = data.data.close; + + // Initialize or get price history + if (!this.priceHistory.has(symbol)) { + this.priceHistory.set(symbol, []); + this.barCount.set(symbol, 0); + logger.info(`šŸ“Š Starting to track ${symbol} @ ${price.toFixed(2)}`); + } + + const history = this.priceHistory.get(symbol)!; + history.push(price); + + // Increment bar count + const currentBar = (this.barCount.get(symbol) || 0) + 1; + this.barCount.set(symbol, currentBar); + + // Keep only needed history + if (history.length > this.SLOW_PERIOD * 2) { + history.shift(); + } + + // Log when we have enough data to start trading + if (history.length === this.SLOW_PERIOD) { + logger.info(`āœ… ${symbol} now has enough history (${history.length} bars) to start trading`); + } + } + + protected async generateSignal(data: MarketData): Promise { + if (data.type !== 'bar') return null; + + const symbol = data.data.symbol; + const history = this.priceHistory.get(symbol); + + if (!history || history.length < this.SLOW_PERIOD) { + return null; + } + + const currentPrice = data.data.close; + const timestamp = data.data.timestamp; + const currentBar = this.barCount.get(symbol) || 0; + + // Calculate moving averages + const fastMA = this.calculateSMA(history, this.FAST_PERIOD); + const slowMA = this.calculateSMA(history, this.SLOW_PERIOD); + + // Get previous MAs for crossover detection + const historyWithoutLast = history.slice(0, -1); + const prevFastMA = this.calculateSMA(historyWithoutLast, this.FAST_PERIOD); + const prevSlowMA = this.calculateSMA(historyWithoutLast, this.SLOW_PERIOD); + + // Detect crossovers + const goldenCross = prevFastMA <= prevSlowMA && fastMA > slowMA; + const deathCross = prevFastMA >= prevSlowMA && fastMA < slowMA; + + // Get current position + const currentPosition = this.getPosition(symbol); + + // Track last signal bar + const lastSignalBar = this.lastSignalBar.get(symbol) || 0; + const barsSinceLastSignal = currentBar - lastSignalBar; + + // Calculate MA difference for debugging + const maDiff = fastMA - slowMA; + const maDiffPct = (maDiff / slowMA) * 100; + + // Debug logging every N bars + if (currentBar % this.DEBUG_INTERVAL === 0 || goldenCross || deathCross) { + logger.info(`${symbol} @ ${timestamp} [Bar ${currentBar}]:`); + logger.info(` Price: $${currentPrice.toFixed(2)}`); + logger.info(` Fast MA (${this.FAST_PERIOD}): $${fastMA.toFixed(2)}`); + logger.info(` Slow MA (${this.SLOW_PERIOD}): $${slowMA.toFixed(2)}`); + logger.info(` MA Diff: ${maDiff.toFixed(2)} (${maDiffPct.toFixed(2)}%)`); + logger.info(` Position: ${currentPosition} shares`); + logger.info(` Bars since last signal: ${barsSinceLastSignal}`); + + if (goldenCross) { + logger.info(` 🟢 GOLDEN CROSS DETECTED!`); + } + if (deathCross) { + logger.info(` šŸ”“ DEATH CROSS DETECTED!`); + } + } + + // Store crossover type for position management + if (goldenCross) { + this.lastCrossoverType.set(symbol, 'golden'); + } else if (deathCross) { + this.lastCrossoverType.set(symbol, 'death'); + } + + // Check if we should enter a position based on the current trend + const lastCrossover = this.lastCrossoverType.get(symbol); + + if (currentPosition === 0 && barsSinceLastSignal >= this.MIN_HOLDING_BARS) { + // No position - check if we should enter based on last crossover + if (lastCrossover === 'golden' && fastMA > slowMA) { + // Trend is still bullish after golden cross - open long + logger.info(`🟢 Opening LONG position - Bullish trend after golden cross`); + logger.info(` Current position: 0 shares`); + + const positionSize = this.calculatePositionSize(currentPrice); + logger.info(` Opening long position: ${positionSize} shares`); + + const signal: Signal = { + type: 'buy', + symbol, + strength: 0.8, + reason: 'Bullish trend - Opening long position', + metadata: { + fastMA, + slowMA, + prevFastMA, + prevSlowMA, + crossoverType: 'trend_long', + price: currentPrice, + quantity: positionSize + } + }; + + this.lastSignalBar.set(symbol, currentBar); + this.totalSignals++; + logger.info(`šŸ‘‰ Total signals generated: ${this.totalSignals}`); + return signal; + + } else if (lastCrossover === 'death' && fastMA < slowMA) { + // Trend is still bearish after death cross - open short + logger.info(`šŸ”“ Opening SHORT position - Bearish trend after death cross`); + logger.info(` Current position: 0 shares`); + + const positionSize = this.calculatePositionSize(currentPrice); + logger.info(` Opening short position: ${positionSize} shares`); + + const signal: Signal = { + type: 'sell', + symbol, + strength: 0.8, + reason: 'Bearish trend - Opening short position', + metadata: { + fastMA, + slowMA, + prevFastMA, + prevSlowMA, + crossoverType: 'trend_short', + price: currentPrice, + quantity: positionSize + } + }; + + this.lastSignalBar.set(symbol, currentBar); + this.totalSignals++; + logger.info(`šŸ‘‰ Total signals generated: ${this.totalSignals}`); + return signal; + } + } + + // Handle crossovers for position changes + if (goldenCross) { + logger.info(`🟢 Golden cross detected for ${symbol} @ ${timestamp}`); + logger.info(` Current position: ${currentPosition} shares`); + + if (currentPosition < 0) { + // Close short position + logger.info(` Closing short position of ${Math.abs(currentPosition)} shares`); + const signal: Signal = { + type: 'buy', + symbol, + strength: 0.8, + reason: 'Golden cross - Closing short position', + metadata: { + fastMA, + slowMA, + prevFastMA, + prevSlowMA, + crossoverType: 'golden', + price: currentPrice, + quantity: Math.abs(currentPosition) + } + }; + + this.lastSignalBar.set(symbol, currentBar); + this.totalSignals++; + logger.info(`šŸ‘‰ Total signals generated: ${this.totalSignals}`); + return signal; + } + } else if (deathCross) { + logger.info(`šŸ”“ Death cross detected for ${symbol} @ ${timestamp}`); + logger.info(` Current position: ${currentPosition} shares`); + + if (currentPosition > 0) { + // Close long position + logger.info(` Closing long position of ${currentPosition} shares`); + const signal: Signal = { + type: 'sell', + symbol, + strength: 0.8, + reason: 'Death cross - Closing long position', + metadata: { + fastMA, + slowMA, + prevFastMA, + prevSlowMA, + crossoverType: 'death', + price: currentPrice, + quantity: currentPosition + } + }; + + this.lastSignalBar.set(symbol, currentBar); + this.totalSignals++; + logger.info(`šŸ‘‰ Total signals generated: ${this.totalSignals}`); + return signal; + } + } + + return null; + } + + private calculateSMA(prices: number[], period: number): number { + if (prices.length < period) { + logger.warn(`Not enough data for SMA calculation: ${prices.length} < ${period}`); + return 0; + } + + const slice = prices.slice(-period); + const sum = slice.reduce((a, b) => a + b, 0); + const sma = sum / period; + + // Sanity check + if (isNaN(sma) || !isFinite(sma)) { + logger.error(`Invalid SMA calculation: sum=${sum}, period=${period}, prices=${slice.length}`); + return 0; + } + + return sma; + } + + private calculatePositionSize(price: number): number { + // Get account balance from trading engine + const tradingEngine = this.modeManager?.getTradingEngine(); + if (!tradingEngine) { + logger.warn('No trading engine available, using default position size'); + return 100; + } + + // Try to get account balance from trading engine + let accountBalance = 100000; // Default + try { + if (tradingEngine.getAccountBalance) { + accountBalance = tradingEngine.getAccountBalance(); + } else if (tradingEngine.getTotalPnl) { + const [realized, unrealized] = tradingEngine.getTotalPnl(); + accountBalance = 100000 + realized + unrealized; // Assuming 100k initial + } + } catch (error) { + logger.warn('Could not get account balance:', error); + } + + const positionValue = accountBalance * this.POSITION_SIZE; + const shares = Math.floor(positionValue / price); + + logger.debug(`Position sizing: Balance=$${accountBalance}, Position Size=${this.POSITION_SIZE}, Price=$${price}, Shares=${shares}`); + + return Math.max(1, shares); // At least 1 share + } + + protected onOrderFilled(fill: any): void { + const { symbol, side, quantity, price } = fill; + + logger.info(`āœ… ${side.toUpperCase()} filled: ${symbol} - ${quantity} shares @ ${price}`); + + // Update our internal position tracking to match + // This is redundant with BaseStrategy but helps with debugging + const currentPosition = this.getPosition(symbol); + logger.info(` Position after fill: ${currentPosition} shares`); + } +} \ No newline at end of file