diff --git a/.archive/test-drift-v4.ts b/.archive/test-drift-v4.ts new file mode 100644 index 0000000..a6decdd --- /dev/null +++ b/.archive/test-drift-v4.ts @@ -0,0 +1,102 @@ +#!/usr/bin/env tsx + +/** + * Test Drift v4 Integration + * + * Verifies connection to Drift Protocol and basic functionality + */ + +import { initializeDriftService, getDriftService } from '../lib/drift/client' +import { getMergedConfig } from '../config/trading' + +async function main() { + console.log('๐Ÿงช Testing Drift v4 Integration\n') + + try { + // Test 1: Configuration + console.log('๐Ÿ“‹ Test 1: Configuration') + const config = getMergedConfig() + console.log('โœ… Config loaded:') + console.log(` Position size: $${config.positionSize}`) + console.log(` Leverage: ${config.leverage}x`) + console.log(` Stop loss: ${config.stopLossPercent}%`) + console.log(` TP1: ${config.takeProfit1Percent}%`) + console.log(` TP2: ${config.takeProfit2Percent}%`) + console.log('') + + // Test 2: Drift Connection + console.log('๐Ÿ“ก Test 2: Drift Connection') + const drift = await initializeDriftService() + console.log('โœ… Drift service initialized') + console.log(` Wallet: ${drift.getClient().wallet.publicKey.toString()}`) + console.log('') + + // Test 3: Account Balance + console.log('๐Ÿ’ฐ Test 3: USDC Balance') + const balance = await drift.getUSDCBalance() + console.log(`โœ… USDC Balance: $${balance.toFixed(2)}`) + console.log('') + + // Test 4: Account Health + console.log('๐Ÿ’Š Test 4: Account Health') + const health = await drift.getAccountHealth() + console.log('โœ… Account health:') + console.log(` Total collateral: $${health.totalCollateral.toFixed(2)}`) + console.log(` Total liability: $${health.totalLiability.toFixed(2)}`) + console.log(` Free collateral: $${health.freeCollateral.toFixed(2)}`) + console.log(` Margin ratio: ${health.marginRatio === Infinity ? 'โˆž' : health.marginRatio.toFixed(2)}`) + console.log('') + + // Test 5: Active Positions + console.log('๐Ÿ“Š Test 5: Active Positions') + const positions = await drift.getAllPositions() + console.log(`โœ… Active positions: ${positions.length}`) + + if (positions.length > 0) { + for (const pos of positions) { + console.log(` ${pos.symbol}:`) + console.log(` Side: ${pos.side}`) + console.log(` Size: ${pos.size.toFixed(4)}`) + console.log(` Entry: $${pos.entryPrice.toFixed(4)}`) + console.log(` P&L: $${pos.unrealizedPnL.toFixed(2)}`) + } + } else { + console.log(' No active positions') + } + console.log('') + + // Test 6: Oracle Prices + console.log('๐Ÿ’น Test 6: Oracle Prices') + const solPrice = await drift.getOraclePrice(0) // SOL-PERP + console.log(`โœ… SOL/USD: $${solPrice.toFixed(4)}`) + console.log('') + + // Test 7: Disconnect + console.log('๐Ÿ”Œ Test 7: Disconnect') + await drift.disconnect() + console.log('โœ… Disconnected from Drift') + console.log('') + + console.log('โœ… All tests passed!') + console.log('') + console.log('๐ŸŽฏ Ready to trade!') + console.log('') + console.log('Next steps:') + console.log('1. Set up n8n workflow (import n8n-workflow-v4.json)') + console.log('2. Configure TradingView alerts') + console.log('3. Test with a manual alert trigger') + console.log('4. Start trading!') + + } catch (error) { + console.error('\nโŒ Test failed:', error) + console.error('\nCommon issues:') + console.error('- DRIFT_WALLET_PRIVATE_KEY not set or invalid') + console.error('- Wallet not initialized on Drift') + console.error('- Insufficient SOL for gas fees') + console.error('- RPC connection issues') + console.error('\nCheck v4/SETUP.md for troubleshooting') + process.exit(1) + } +} + +main() diff --git a/.archive/test-full-flow.ts b/.archive/test-full-flow.ts new file mode 100644 index 0000000..6428a51 --- /dev/null +++ b/.archive/test-full-flow.ts @@ -0,0 +1,206 @@ +/** + * Test Full Trading Flow + * + * End-to-end test: Execute trade โ†’ Monitor โ†’ Auto-exit + * + * WARNING: This executes a REAL trade on Drift! + * Make sure you have a small position size configured. + */ + +import 'dotenv/config' + +const API_URL = process.env.API_URL || 'http://localhost:3000' +const API_KEY = process.env.API_KEY || '' + +interface ExecuteResponse { + success: boolean + message: string + trade?: { + id: string + symbol: string + direction: string + entryPrice: number + positionSize: number + leverage: number + } + position?: any +} + +interface PositionsResponse { + success: boolean + monitoring: { + isActive: boolean + tradeCount: number + symbols: string[] + } + positions: Array<{ + id: string + symbol: string + direction: string + entryPrice: number + currentPrice: number + unrealizedPnL: number + profitPercent: number + accountPnL: number + tp1Hit: boolean + slMovedToBreakeven: boolean + }> +} + +async function testFullFlow() { + console.log('๐Ÿงช Testing Full Trading Flow (END-TO-END)\n') + console.log('โš ๏ธ WARNING: This will execute a REAL trade on Drift!') + console.log(' Make sure position size is small ($10-50)\n') + + if (!API_KEY) { + console.error('โŒ Error: API_KEY not set in .env') + console.log(' Add: API_KEY=your_secret_key_here') + process.exit(1) + } + + // Wait for user confirmation + console.log('Press Ctrl+C to cancel, or wait 5 seconds to continue...') + await new Promise(resolve => setTimeout(resolve, 5000)) + console.log() + + // Step 1: Execute trade + console.log('๐Ÿ“ Step 1: Executing trade...') + + const executePayload = { + symbol: 'SOLUSDT', + direction: 'long', + timeframe: '5', + } + + console.log(' Payload:', JSON.stringify(executePayload, null, 2)) + + const executeResponse = await fetch(`${API_URL}/api/trading/execute`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(executePayload), + }) + + if (!executeResponse.ok) { + const error = await executeResponse.text() + console.error('โŒ Execute failed:', error) + process.exit(1) + } + + const executeData: ExecuteResponse = await executeResponse.json() + + if (!executeData.success || !executeData.trade) { + console.error('โŒ Execute failed:', executeData.message) + process.exit(1) + } + + console.log('โœ… Trade executed!') + console.log(' ID:', executeData.trade.id) + console.log(' Symbol:', executeData.trade.symbol) + console.log(' Direction:', executeData.trade.direction.toUpperCase()) + console.log(' Entry Price: $', executeData.trade.entryPrice.toFixed(4)) + console.log(' Position Size: $', executeData.trade.positionSize.toFixed(2)) + console.log(' Leverage:', executeData.trade.leverage + 'x') + console.log() + + const tradeId = executeData.trade.id + + // Step 2: Monitor position + console.log('๐Ÿ“ Step 2: Monitoring position...') + console.log(' Duration: 120 seconds (2 minutes)') + console.log(' Updates: Every 10 seconds') + console.log(' Waiting for automatic exit...\n') + + const startTime = Date.now() + let lastStatus: PositionsResponse | null = null + + for (let i = 0; i < 12; i++) { // 12 x 10s = 120s + await new Promise(resolve => setTimeout(resolve, 10000)) + + const elapsed = (Date.now() - startTime) / 1000 + console.log(`โฑ๏ธ ${elapsed.toFixed(0)}s elapsed...`) + + // Fetch positions + const positionsResponse = await fetch(`${API_URL}/api/trading/positions`, { + headers: { + 'Authorization': `Bearer ${API_KEY}`, + }, + }) + + if (!positionsResponse.ok) { + console.error(' โš ๏ธ Failed to fetch positions') + continue + } + + const positionsData: PositionsResponse = await positionsResponse.json() + lastStatus = positionsData + + // Find our trade + const ourTrade = positionsData.positions.find(p => p.id === tradeId) + + if (!ourTrade) { + console.log(' โœ… TRADE CLOSED AUTOMATICALLY!') + console.log(' Position no longer in active list') + break + } + + // Display status + console.log(` Current Price: $${ourTrade.currentPrice.toFixed(4)}`) + console.log(` Unrealized P&L: $${ourTrade.unrealizedPnL.toFixed(2)} (${ourTrade.accountPnL.toFixed(2)}% account)`) + console.log(` TP1 Hit: ${ourTrade.tp1Hit ? 'YES โœ…' : 'No'}`) + console.log(` SL Moved: ${ourTrade.slMovedToBreakeven ? 'YES โœ…' : 'No'}`) + console.log() + } + + // Step 3: Final check + console.log('๐Ÿ“ Step 3: Final check...') + + const finalResponse = await fetch(`${API_URL}/api/trading/positions`, { + headers: { + 'Authorization': `Bearer ${API_KEY}`, + }, + }) + + if (finalResponse.ok) { + const finalData: PositionsResponse = await finalResponse.json() + const stillActive = finalData.positions.find(p => p.id === tradeId) + + if (stillActive) { + console.log('โš ๏ธ Trade still active after 2 minutes') + console.log(' This is normal if price hasn\'t hit targets yet') + console.log(' Position will auto-close when TP/SL is hit') + console.log() + console.log(' Current status:') + console.log(' Price:', stillActive.currentPrice) + console.log(' P&L:', stillActive.unrealizedPnL.toFixed(2)) + console.log(' TP1 Hit:', stillActive.tp1Hit) + } else { + console.log('โœ… Trade successfully closed automatically!') + console.log(' Check your Drift account for final P&L') + } + } + + console.log() + console.log('๐ŸŽ‰ End-to-end test complete!') + console.log() + console.log('๐Ÿ“Š What happened:') + console.log(' 1. Trade was executed via API') + console.log(' 2. Position manager started monitoring') + console.log(' 3. Pyth price monitor updated every 2s') + console.log(' 4. Exit conditions checked automatically') + console.log(' 5. Trade closed when TP/SL was hit') + console.log() + console.log('๐Ÿ’ก Next steps:') + console.log(' 1. Trigger more trades from TradingView') + console.log(' 2. Monitor the logs for auto-exits') + console.log(' 3. Verify P&L on Drift UI') + console.log(' 4. Adjust parameters if needed') +} + +// Run test +testFullFlow().catch(error => { + console.error('โŒ Test failed:', error) + process.exit(1) +}) diff --git a/.archive/test-position-manager.ts b/.archive/test-position-manager.ts new file mode 100644 index 0000000..4d0886c --- /dev/null +++ b/.archive/test-position-manager.ts @@ -0,0 +1,185 @@ +/** + * Test Position Manager + * + * Tests position tracking, monitoring, and automatic exit logic + */ + +import { getPositionManager } from '../lib/trading/position-manager' +import type { ActiveTrade } from '../lib/trading/position-manager' + +async function testPositionManager() { + console.log('๐Ÿงช Testing Position Manager...\n') + + const manager = getPositionManager() + + // Test 1: Add a simulated long trade + console.log('๐Ÿ“ Test 1: Adding simulated LONG trade...') + + const entryPrice = 140.0 + const positionSize = 10000 + const leverage = 10 + + const longTrade: ActiveTrade = { + id: `test-long-${Date.now()}`, + positionId: 'test-signature-long', + symbol: 'SOL-PERP', + direction: 'long', + entryPrice, + entryTime: Date.now(), + positionSize, + leverage, + + // Exit prices + stopLossPrice: entryPrice * 0.985, // -1.5% + tp1Price: entryPrice * 1.007, // +0.7% + tp2Price: entryPrice * 1.015, // +1.5% + emergencyStopPrice: entryPrice * 0.98, // -2.0% + + // Position state + currentSize: positionSize, + tp1Hit: false, + tp2Hit: false, + slMovedToBreakeven: false, + slMovedToProfit: false, + + // Trailing stop + trailingStopActive: false, + peakPrice: entryPrice, + + // P&L tracking + realizedPnL: 0, + unrealizedPnL: 0, + peakPnL: 0, + + // Monitoring + priceCheckCount: 0, + lastPrice: entryPrice, + lastUpdateTime: Date.now(), + } + + await manager.addTrade(longTrade) + console.log('โœ… Long trade added') + console.log(` Entry: $${entryPrice}`) + console.log(` SL: $${longTrade.stopLossPrice.toFixed(2)} (-1.5%)`) + console.log(` TP1: $${longTrade.tp1Price.toFixed(2)} (+0.7%)`) + console.log(` TP2: $${longTrade.tp2Price.toFixed(2)} (+1.5%)`) + console.log() + + // Test 2: Add a simulated short trade + console.log('๐Ÿ“ Test 2: Adding simulated SHORT trade...') + + const shortTrade: ActiveTrade = { + id: `test-short-${Date.now()}`, + positionId: 'test-signature-short', + symbol: 'BTC-PERP', + direction: 'short', + entryPrice: 43000, + entryTime: Date.now(), + positionSize: 10000, + leverage: 10, + + // Exit prices (reversed for short) + stopLossPrice: 43000 * 1.015, // +1.5% (loss on short) + tp1Price: 43000 * 0.993, // -0.7% (profit on short) + tp2Price: 43000 * 0.985, // -1.5% (profit on short) + emergencyStopPrice: 43000 * 1.02, // +2.0% (emergency) + + // Position state + currentSize: 10000, + tp1Hit: false, + tp2Hit: false, + slMovedToBreakeven: false, + slMovedToProfit: false, + + // Trailing stop + trailingStopActive: false, + peakPrice: 43000, + + // P&L tracking + realizedPnL: 0, + unrealizedPnL: 0, + peakPnL: 0, + + // Monitoring + priceCheckCount: 0, + lastPrice: 43000, + lastUpdateTime: Date.now(), + } + + await manager.addTrade(shortTrade) + console.log('โœ… Short trade added') + console.log(` Entry: $${shortTrade.entryPrice}`) + console.log(` SL: $${shortTrade.stopLossPrice.toFixed(2)} (+1.5%)`) + console.log(` TP1: $${shortTrade.tp1Price.toFixed(2)} (-0.7%)`) + console.log(` TP2: $${shortTrade.tp2Price.toFixed(2)} (-1.5%)`) + console.log() + + // Test 3: Check status + console.log('๐Ÿ“ Test 3: Checking manager status...') + const status = manager.getStatus() + console.log('โœ… Status:', JSON.stringify(status, null, 2)) + console.log() + + // Test 4: Monitor for 60 seconds + console.log('๐Ÿ“ Test 4: Monitoring positions for 60 seconds...') + console.log(' (Real prices from Pyth will update every 2s)') + console.log(' Watch for automatic exit conditions!\n') + + let updates = 0 + const startTime = Date.now() + const interval = setInterval(() => { + const elapsed = (Date.now() - startTime) / 1000 + const currentStatus = manager.getStatus() + + if (updates % 5 === 0) { // Print every 10 seconds + console.log(`โฑ๏ธ ${elapsed.toFixed(0)}s - Active trades: ${currentStatus.activeTradesCount}`) + + if (currentStatus.activeTradesCount === 0) { + console.log(' All trades closed!') + clearInterval(interval) + } + } + + updates++ + }, 2000) + + // Run for 60 seconds + await new Promise(resolve => setTimeout(resolve, 60000)) + clearInterval(interval) + + // Test 5: Check final status + console.log('\n๐Ÿ“ Test 5: Final status check...') + const finalStatus = manager.getStatus() + console.log('Status:', JSON.stringify(finalStatus, null, 2)) + console.log() + + // Test 6: Close all remaining positions + if (finalStatus.activeTradesCount > 0) { + console.log('๐Ÿ“ Test 6: Closing all remaining positions...') + await manager.closeAll() + console.log('โœ… All positions closed') + } else { + console.log('๐Ÿ“ Test 6: No positions to close (already closed automatically!)') + } + + console.log() + console.log('๐ŸŽ‰ Position manager test complete!') + console.log() + console.log('๐Ÿ“Š What to check:') + console.log(' โœ… Both trades were added successfully') + console.log(' โœ… Manager started monitoring (check logs)') + console.log(' โœ… Real prices were fetched from Pyth') + console.log(' โœ… Exit conditions were checked every 2s') + console.log(' โœ… If price hit targets, trades closed automatically') + console.log() + console.log('๐Ÿ’ก Next steps:') + console.log(' 1. Review the logs for price updates') + console.log(' 2. Check if any exits were triggered') + console.log(' 3. Run test-full-flow.ts for end-to-end test') +} + +// Run test +testPositionManager().catch(error => { + console.error('โŒ Test failed:', error) + process.exit(1) +}) diff --git a/.archive/test-price-monitor.ts b/.archive/test-price-monitor.ts new file mode 100644 index 0000000..88a7670 --- /dev/null +++ b/.archive/test-price-monitor.ts @@ -0,0 +1,141 @@ +/** + * Test Pyth Price Monitor + * + * Tests real-time price monitoring with WebSocket and polling fallback + */ + +import { getPythPriceMonitor } from './lib/pyth/price-monitor' + +interface PriceStats { + symbol: string + count: number + prices: number[] + minPrice: number + maxPrice: number + avgPrice: number + lastUpdate: number +} + +async function testPriceMonitor() { + console.log('๐Ÿงช Testing Pyth Price Monitor...\n') + + const monitor = getPythPriceMonitor() + const stats = new Map() + const symbols = ['SOL-PERP', 'BTC-PERP', 'ETH-PERP'] + + // Initialize stats + symbols.forEach(sym => { + stats.set(sym, { + symbol: sym, + count: 0, + prices: [], + minPrice: Infinity, + maxPrice: -Infinity, + avgPrice: 0, + lastUpdate: 0, + }) + }) + + console.log(`๐Ÿ“Š Monitoring: ${symbols.join(', ')}`) + console.log('โฑ๏ธ Duration: 30 seconds') + console.log('๐Ÿ“ก Source: Pyth Network (WebSocket + Polling)\n') + + // Start monitoring + await monitor.start({ + symbols, + onPriceUpdate: (update) => { + const stat = stats.get(update.symbol) + if (!stat) return + + // Update statistics + stat.count++ + stat.prices.push(update.price) + stat.minPrice = Math.min(stat.minPrice, update.price) + stat.maxPrice = Math.max(stat.maxPrice, update.price) + stat.avgPrice = stat.prices.reduce((a, b) => a + b, 0) / stat.prices.length + stat.lastUpdate = Date.now() + + // Display update + const changePercent = stat.prices.length > 1 + ? ((update.price - stat.prices[0]) / stat.prices[0] * 100).toFixed(3) + : '0.000' + + console.log( + `๐Ÿ’ฐ ${update.symbol.padEnd(10)} ` + + `$${update.price.toFixed(4).padStart(10)} ` + + `(${changePercent > '0' ? '+' : ''}${changePercent}%) ` + + `[${stat.count} updates]` + ) + }, + }) + + console.log('โœ… Price monitor started!\n') + + // Run for 30 seconds + const startTime = Date.now() + await new Promise(resolve => setTimeout(resolve, 30000)) + + // Stop monitoring + await monitor.stop() + const duration = (Date.now() - startTime) / 1000 + + console.log('\n๐Ÿ“Š Test Results:\n') + + // Display statistics + stats.forEach(stat => { + const priceRange = stat.maxPrice - stat.minPrice + const rangePercent = (priceRange / stat.minPrice * 100).toFixed(3) + const updatesPerSec = (stat.count / duration).toFixed(2) + const timeSinceUpdate = stat.lastUpdate ? (Date.now() - stat.lastUpdate) / 1000 : 0 + + console.log(`${stat.symbol}:`) + console.log(` Updates: ${stat.count} (${updatesPerSec}/sec)`) + console.log(` Avg Price: $${stat.avgPrice.toFixed(4)}`) + console.log(` Min Price: $${stat.minPrice.toFixed(4)}`) + console.log(` Max Price: $${stat.maxPrice.toFixed(4)}`) + console.log(` Range: $${priceRange.toFixed(4)} (${rangePercent}%)`) + console.log(` Last Update: ${timeSinceUpdate.toFixed(1)}s ago`) + console.log() + }) + + // Evaluate results + console.log('โœ… Evaluation:\n') + + const allSymbolsUpdated = Array.from(stats.values()).every(s => s.count > 0) + const avgUpdateRate = Array.from(stats.values()) + .reduce((sum, s) => sum + s.count, 0) / stats.size / duration + + if (!allSymbolsUpdated) { + console.log('โŒ FAIL: Not all symbols received updates') + process.exit(1) + } + + if (avgUpdateRate < 0.3) { + console.log(`โš ๏ธ WARNING: Low update rate (${avgUpdateRate.toFixed(2)}/sec)`) + console.log(' Expected: ~0.5-2 updates/sec per symbol') + } else { + console.log(`โœ… PASS: Good update rate (${avgUpdateRate.toFixed(2)}/sec)`) + } + + const maxGap = Array.from(stats.values()) + .map(s => s.lastUpdate ? (Date.now() - s.lastUpdate) / 1000 : Infinity) + .reduce((max, gap) => Math.max(max, gap), 0) + + if (maxGap > 5) { + console.log(`โš ๏ธ WARNING: Large gap since last update (${maxGap.toFixed(1)}s)`) + } else { + console.log(`โœ… PASS: Recent updates (${maxGap.toFixed(1)}s ago)`) + } + + console.log('\n๐ŸŽ‰ Price monitor test complete!') + console.log('\n๐Ÿ’ก Next steps:') + console.log(' 1. If WebSocket is working: Updates should be ~0.5-2/sec') + console.log(' 2. If polling fallback: Updates should be ~0.5/sec (every 2s)') + console.log(' 3. Run test-position-manager.ts to test exit logic') +} + +// Run test +testPriceMonitor().catch(error => { + console.error('โŒ Test failed:', error) + process.exit(1) +}) diff --git a/app/api/trading/execute/route.ts b/app/api/trading/execute/route.ts index e1ab193..5b05c4e 100644 --- a/app/api/trading/execute/route.ts +++ b/app/api/trading/execute/route.ts @@ -171,7 +171,7 @@ export async function POST(request: NextRequest): Promise 0) { + for (const pos of positions) { + console.log(` ${pos.symbol}:`) + console.log(` Side: ${pos.side}`) + console.log(` Size: ${pos.size.toFixed(4)}`) + console.log(` Entry: $${pos.entryPrice.toFixed(4)}`) + console.log(` P&L: $${pos.unrealizedPnL.toFixed(2)}`) + } + } else { + console.log(' No active positions') + } + console.log('') + + // Test 6: Oracle Prices + console.log('๐Ÿ’น Test 6: Oracle Prices') + const solPrice = await drift.getOraclePrice(0) // SOL-PERP + console.log(`โœ… SOL/USD: $${solPrice.toFixed(4)}`) + console.log('') + + // Test 7: Disconnect + console.log('๐Ÿ”Œ Test 7: Disconnect') + await drift.disconnect() + console.log('โœ… Disconnected from Drift') + console.log('') + + console.log('โœ… All tests passed!') + console.log('') + console.log('๐ŸŽฏ Ready to trade!') + console.log('') + console.log('Next steps:') + console.log('1. Set up n8n workflow (import n8n-workflow-v4.json)') + console.log('2. Configure TradingView alerts') + console.log('3. Test with a manual alert trigger') + console.log('4. Start trading!') + + } catch (error) { + console.error('\nโŒ Test failed:', error) + console.error('\nCommon issues:') + console.error('- DRIFT_WALLET_PRIVATE_KEY not set or invalid') + console.error('- Wallet not initialized on Drift') + console.error('- Insufficient SOL for gas fees') + console.error('- RPC connection issues') + console.error('\nCheck v4/SETUP.md for troubleshooting') + process.exit(1) + } +} + +main() diff --git a/archive/test-position-manager.ts b/archive/test-position-manager.ts new file mode 100644 index 0000000..bc60699 --- /dev/null +++ b/archive/test-position-manager.ts @@ -0,0 +1,207 @@ +/** + * Test Position Manager + * + * Tests position tracking, monitoring, and automatic exit logic + */ + +import { getPositionManager } from '../lib/trading/position-manager' +import type { ActiveTrade } from '../lib/trading/position-manager' + +async function testPositionManager() { + console.log('๐Ÿงช Testing Position Manager...\n') + + const manager = getPositionManager() + + // Test 1: Add a simulated long trade + console.log('๐Ÿ“ Test 1: Adding simulated LONG trade...') + + const entryPrice = 140.0 + const positionSize = 10000 + const leverage = 10 + + const longTrade: ActiveTrade = { + id: `test-long-${Date.now()}`, + positionId: 'test-signature-long', + symbol: 'SOL-PERP', + direction: 'long', + entryPrice, + entryTime: Date.now(), + positionSize, + leverage, + + // Exit prices + stopLossPrice: entryPrice * 0.985, // -1.5% + tp1Price: entryPrice * 1.007, // +0.7% + tp2Price: entryPrice * 1.015, // +1.5% + emergencyStopPrice: entryPrice * 0.98, // -2.0% + + // Position state + currentSize: positionSize, + tp1Hit: false, + tp2Hit: false, + slMovedToBreakeven: false, + slMovedToProfit: false, + + // Trailing stop + trailingStopActive: false, + peakPrice: entryPrice, + + // P&L tracking + realizedPnL: 0, + unrealizedPnL: 0, + peakPnL: 0, + + // MAE/MFE tracking + maxFavorableExcursion: 0, + maxAdverseExcursion: 0, + maxFavorablePrice: entryPrice, + maxAdversePrice: entryPrice, + + // Scaling tracking + originalAdx: 20, + timesScaled: 0, + totalScaleAdded: 0, + + // Monitoring + priceCheckCount: 0, + lastPrice: entryPrice, + lastUpdateTime: Date.now(), + } + + await manager.addTrade(longTrade) + console.log('โœ… Long trade added') + console.log(` Entry: $${entryPrice}`) + console.log(` SL: $${longTrade.stopLossPrice.toFixed(2)} (-1.5%)`) + console.log(` TP1: $${longTrade.tp1Price.toFixed(2)} (+0.7%)`) + console.log(` TP2: $${longTrade.tp2Price.toFixed(2)} (+1.5%)`) + console.log() + + // Test 2: Add a simulated short trade + console.log('๐Ÿ“ Test 2: Adding simulated SHORT trade...') + + const shortTrade: ActiveTrade = { + id: `test-short-${Date.now()}`, + positionId: 'test-signature-short', + symbol: 'BTC-PERP', + direction: 'short', + entryPrice: 43000, + entryTime: Date.now(), + positionSize: 10000, + leverage: 10, + + // Exit prices (reversed for short) + stopLossPrice: 43000 * 1.015, // +1.5% (loss on short) + tp1Price: 43000 * 0.993, // -0.7% (profit on short) + tp2Price: 43000 * 0.985, // -1.5% (profit on short) + emergencyStopPrice: 43000 * 1.02, // +2.0% (emergency) + + // Position state + currentSize: 10000, + tp1Hit: false, + tp2Hit: false, + slMovedToBreakeven: false, + slMovedToProfit: false, + + // Trailing stop + trailingStopActive: false, + peakPrice: 43000, + + // P&L tracking + realizedPnL: 0, + unrealizedPnL: 0, + peakPnL: 0, + + // MAE/MFE tracking + maxFavorableExcursion: 0, + maxAdverseExcursion: 0, + maxFavorablePrice: 43000, + maxAdversePrice: 43000, + + // Scaling tracking + originalAdx: 18, + timesScaled: 0, + totalScaleAdded: 0, + + // Monitoring + priceCheckCount: 0, + lastPrice: 43000, + lastUpdateTime: Date.now(), + } + + await manager.addTrade(shortTrade) + console.log('โœ… Short trade added') + console.log(` Entry: $${shortTrade.entryPrice}`) + console.log(` SL: $${shortTrade.stopLossPrice.toFixed(2)} (+1.5%)`) + console.log(` TP1: $${shortTrade.tp1Price.toFixed(2)} (-0.7%)`) + console.log(` TP2: $${shortTrade.tp2Price.toFixed(2)} (-1.5%)`) + console.log() + + // Test 3: Check status + console.log('๐Ÿ“ Test 3: Checking manager status...') + const status = manager.getStatus() + console.log('โœ… Status:', JSON.stringify(status, null, 2)) + console.log() + + // Test 4: Monitor for 60 seconds + console.log('๐Ÿ“ Test 4: Monitoring positions for 60 seconds...') + console.log(' (Real prices from Pyth will update every 2s)') + console.log(' Watch for automatic exit conditions!\n') + + let updates = 0 + const startTime = Date.now() + const interval = setInterval(() => { + const elapsed = (Date.now() - startTime) / 1000 + const currentStatus = manager.getStatus() + + if (updates % 5 === 0) { // Print every 10 seconds + console.log(`โฑ๏ธ ${elapsed.toFixed(0)}s - Active trades: ${currentStatus.activeTradesCount}`) + + if (currentStatus.activeTradesCount === 0) { + console.log(' All trades closed!') + clearInterval(interval) + } + } + + updates++ + }, 2000) + + // Run for 60 seconds + await new Promise(resolve => setTimeout(resolve, 60000)) + clearInterval(interval) + + // Test 5: Check final status + console.log('\n๐Ÿ“ Test 5: Final status check...') + const finalStatus = manager.getStatus() + console.log('Status:', JSON.stringify(finalStatus, null, 2)) + console.log() + + // Test 6: Close all remaining positions + if (finalStatus.activeTradesCount > 0) { + console.log('๐Ÿ“ Test 6: Closing all remaining positions...') + await manager.closeAll() + console.log('โœ… All positions closed') + } else { + console.log('๐Ÿ“ Test 6: No positions to close (already closed automatically!)') + } + + console.log() + console.log('๐ŸŽ‰ Position manager test complete!') + console.log() + console.log('๐Ÿ“Š What to check:') + console.log(' โœ… Both trades were added successfully') + console.log(' โœ… Manager started monitoring (check logs)') + console.log(' โœ… Real prices were fetched from Pyth') + console.log(' โœ… Exit conditions were checked every 2s') + console.log(' โœ… If price hit targets, trades closed automatically') + console.log() + console.log('๐Ÿ’ก Next steps:') + console.log(' 1. Review the logs for price updates') + console.log(' 2. Check if any exits were triggered') + console.log(' 3. Run test-full-flow.ts for end-to-end test') +} + +// Run test +testPositionManager().catch(error => { + console.error('โŒ Test failed:', error) + process.exit(1) +})