import { supabaseService } from '../src/services/SupabaseService.js'; import { AutoTrader } from '../src/services/AutoTrader.js'; import { TradeExecutor } from '../src/services/TradeExecutor.js'; import { AlpacaConnector } from '../src/connectors/alpaca.js'; import { config } from '../src/config/index.js'; import { SignalDirection, MarketContext, RuleResult, StrategyAnalysisResult } from '../src/strategies/rules/types.js'; import logger from '../src/utils/logger.js'; // Setup environment and global config overrides for testing config.ENABLE_TRADING = true; // Force Paper Trading for safety config.PAPER_TRADING = true; const TEST_SYMBOL = 'BTC/USD'; // Alpaca Paper usually supports this for crypto const SLEEP_MS = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); async function runFullE2ETest() { logger.info('==================================================='); logger.info('๐Ÿš€ STARTING END-TO-END BACKEND SYSTEM TEST (FINAL VERIFICATION)'); logger.info(' Scope: Configs -> Signal -> Order -> Position -> DB -> Exit -> PnL'); logger.info(' Goal: Confirm DB Logging uses correct lowercase syntax.'); logger.info('==================================================='); // 1. Load Profiles (Simulating "Multiple Configurations") logger.info('\n๐Ÿ“ก 1. Loading Configurations from Database...'); const profiles = await supabaseService.getActiveProfiles(); if (profiles.length === 0) { logger.error('โŒ No active profiles found in DB. Please create a Strategy Cluster in the Dashboard first.'); process.exit(1); } logger.info(`โœ… Found ${profiles.length} Active Profiles.`); // 2. Iterate Profiles and Execute Test Cycle for (const profile of profiles) { logger.info(`\n---------------------------------------------------`); logger.info(`๐Ÿ‘ค Testing Profile: [${profile.name}] (ID: ${profile.id})`); logger.info(` Allocated Capital: $${profile.allocated_capital}`); const user = profile.users; if (!user) { logger.warn(' โš ๏ธ User data missing for this profile. Skipping.'); continue; } // Initialize Services for this specific User/Profile const userKey = config.PAPER_TRADING ? user.ALPACA_API_KEY : user.REAL_ALPACA_API_KEY; const userSecret = config.PAPER_TRADING ? user.ALPACA_SECRET_KEY : user.REAL_ALPACA_SECRET_KEY; if (!userKey || !userSecret) { logger.warn(' โš ๏ธ Alpaca Credentials missing. Skipping.'); continue; } const exchange = new AlpacaConnector(userKey, userSecret); const executor = new TradeExecutor(exchange, undefined, user.user_id, profile.id); const trader = new AutoTrader(executor, exchange, profile); // SYNC: Ensure we start clean logger.info(' ๐Ÿ”„ Syncing existing positions...'); await executor.syncPositions([TEST_SYMBOL]); // Close any existing position on Test Symbol to ensure clean test const existingPos = executor.getActivePosition(TEST_SYMBOL); if (existingPos) { logger.info(` ๐Ÿงน Closing pre-existing position on ${TEST_SYMBOL}...`); await executor.closePosition(TEST_SYMBOL, 'E2E PRE-CLEANUP'); await SLEEP_MS(5000); } // 3. Simulate Market Context const currentPrice = 65000; const mockContext: MarketContext = { symbol: TEST_SYMBOL, currentPrice: currentPrice, candles1h: [], candles15m: [], candles4h: [], rsi_1h: 30, // Oversold ema20_1h: 64000, change24h: 1.5, changeToday: 0.5, volatility: 'Low', session: 'NY', isMajorSession: true, latestSignal: SignalDirection.NONE }; // 4. Simulate BUY Signal logger.info(' ๐ŸŸข Simulating BUY Signal (Trend + Momentum)...'); const ruleResults: Record = {}; const rulesToMock = ['TrendBiasRule', 'MomentumRule', 'ZoneRule', 'RiskManagementRule']; rulesToMock.forEach(rId => { ruleResults[rId] = { ruleName: rId, passed: true, signal: SignalDirection.BUY, reason: 'Test Pass', metadata: {} }; }); const buyAnalysis: StrategyAnalysisResult = { symbol: TEST_SYMBOL, globalSignal: SignalDirection.BUY, rules: ruleResults, context: mockContext }; await trader.handleSignal(TEST_SYMBOL, buyAnalysis); // 5. Verify Order & Position Creation logger.info(' โณ Waiting for Order Fill & DB Sync (10s)...'); await SLEEP_MS(10000); const activePos = executor.getActivePosition(TEST_SYMBOL); if (!activePos) { logger.error(` โŒ FAIL: Position not created for ${profile.name}`); continue; } logger.info(` โœ… SUCCESS: Position Created! Qty: ${activePos.size}`); // 6. Simulate Profit Update (Price Move Up) logger.info(' ๐Ÿ“ˆ Simulating Price Move (+10%)...'); const newPrice = currentPrice * 1.10; mockContext.currentPrice = newPrice; // 7. Simulate SELL/EXIT Signal logger.info(' ๐Ÿ”ด Simulating SELL/EXIT Signal (Trend Reversal)...'); const sellAnalysis: StrategyAnalysisResult = { symbol: TEST_SYMBOL, globalSignal: SignalDirection.SELL, // Reversal triggers exit rules: ruleResults, context: mockContext }; await trader.handleSignal(TEST_SYMBOL, sellAnalysis); // 8. Verify Exit & PnL Realization logger.info(' โณ Waiting for Close & Profit Realization (10s)...'); await SLEEP_MS(10000); const closedPos = executor.getActivePosition(TEST_SYMBOL); if (!closedPos) { logger.info(` โœ… SUCCESS: Position Closed.`); logger.info(` โœ… CHECK LOGS: Ensure no '[Supabase] Error' messages appeared above.`); } else { logger.error(` โŒ FAIL: Position still active!`); } } logger.info('\n==================================================='); logger.info('โœ… FINAL E2E CHECK COMPLETED'); logger.info('==================================================='); process.exit(0); } runFullE2ETest().catch(err => { logger.error('CRITICAL TEST FAILURE:', err); process.exit(1); });