fix: Remove v9 label from 1-minute data collection
- 1-minute data is pure market sampling, not trading signals - signalQualityVersion now null for timeframe='1' - Other timeframes still labeled with v9 - Prevents confusion in analytics/reporting
This commit is contained in:
102
.archive/test-drift-v4.ts
Normal file
102
.archive/test-drift-v4.ts
Normal file
@@ -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()
|
||||||
206
.archive/test-full-flow.ts
Normal file
206
.archive/test-full-flow.ts
Normal file
@@ -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)
|
||||||
|
})
|
||||||
185
.archive/test-position-manager.ts
Normal file
185
.archive/test-position-manager.ts
Normal file
@@ -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)
|
||||||
|
})
|
||||||
141
.archive/test-price-monitor.ts
Normal file
141
.archive/test-price-monitor.ts
Normal file
@@ -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<string, PriceStats>()
|
||||||
|
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)
|
||||||
|
})
|
||||||
@@ -171,7 +171,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
timeframe: timeframe,
|
timeframe: timeframe,
|
||||||
signalPrice: currentPrice,
|
signalPrice: currentPrice,
|
||||||
signalQualityScore: qualityResult.score, // CRITICAL: Real quality score for analysis
|
signalQualityScore: qualityResult.score, // CRITICAL: Real quality score for analysis
|
||||||
signalQualityVersion: 'v9', // Current indicator version
|
signalQualityVersion: timeframe === '1' ? null : 'v9', // 1-minute data = pure market sampling, no indicator version
|
||||||
minScoreRequired: minQualityScore,
|
minScoreRequired: minQualityScore,
|
||||||
scoreBreakdown: { reasons: qualityResult.reasons },
|
scoreBreakdown: { reasons: qualityResult.reasons },
|
||||||
indicatorVersion: body.indicatorVersion || 'v5',
|
indicatorVersion: body.indicatorVersion || 'v5',
|
||||||
|
|||||||
102
archive/test-drift-v4.ts
Normal file
102
archive/test-drift-v4.ts
Normal file
@@ -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()
|
||||||
207
archive/test-position-manager.ts
Normal file
207
archive/test-position-manager.ts
Normal file
@@ -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)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user