feat: add quality score display and timezone fixes
- Add qualityScore to ExecuteTradeResponse interface and response object - Update analytics page to always show Signal Quality card (N/A if unavailable) - Fix n8n workflow to pass context metrics and qualityScore to execute endpoint - Fix timezone in Telegram notifications (Europe/Berlin) - Fix symbol normalization in /api/trading/close endpoint - Update Drift ETH-PERP minimum order size (0.002 ETH not 0.01) - Add transaction confirmation to closePosition() to prevent phantom closes - Add 30-second grace period for new trades in Position Manager - Fix execution order: database save before Position Manager.addTrade() - Update copilot instructions with transaction confirmation pattern
This commit is contained in:
@@ -522,9 +522,17 @@ export async function closePosition(
|
||||
|
||||
console.log(`✅ Close order placed! Transaction: ${txSig}`)
|
||||
|
||||
// Wait for confirmation (transaction is likely already confirmed by placeAndTakePerpOrder)
|
||||
console.log('⏳ Waiting for transaction confirmation...')
|
||||
console.log('✅ Transaction confirmed')
|
||||
// CRITICAL: Confirm transaction on-chain to prevent phantom closes
|
||||
console.log('⏳ Confirming transaction on-chain...')
|
||||
const connection = driftService.getConnection()
|
||||
const confirmation = await connection.confirmTransaction(txSig, 'confirmed')
|
||||
|
||||
if (confirmation.value.err) {
|
||||
console.error('❌ Transaction failed on-chain:', confirmation.value.err)
|
||||
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`)
|
||||
}
|
||||
|
||||
console.log('✅ Transaction confirmed on-chain')
|
||||
|
||||
// Calculate realized P&L
|
||||
const pnlPerUnit = oraclePrice - position.entryPrice
|
||||
|
||||
@@ -286,7 +286,17 @@ export class PositionManager {
|
||||
const marketConfig = getMarketConfig(trade.symbol)
|
||||
const position = await driftService.getPosition(marketConfig.driftMarketIndex)
|
||||
|
||||
// Calculate trade age in seconds
|
||||
const tradeAgeSeconds = (Date.now() - trade.entryTime) / 1000
|
||||
|
||||
if (position === null || position.size === 0) {
|
||||
// IMPORTANT: Skip "external closure" detection for NEW trades (<30 seconds old)
|
||||
// Drift positions may not be immediately visible after opening due to blockchain delays
|
||||
if (tradeAgeSeconds < 30) {
|
||||
console.log(`⏳ Trade ${trade.symbol} is new (${tradeAgeSeconds.toFixed(1)}s old) - skipping external closure check`)
|
||||
return // Skip this check cycle, position might still be propagating
|
||||
}
|
||||
|
||||
// Position closed externally (by on-chain TP/SL order)
|
||||
console.log(`⚠️ Position ${trade.symbol} was closed externally (by on-chain order)`)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user