feat: Add distinction between regular SL and trailing SL
User Request: Distinguish between SL and Trailing SL in analytics overview Changes: 1. Position Manager: - Updated ExitResult interface to include 'TRAILING_SL' exit reason - Modified trailing stop exit (line 1457) to use 'TRAILING_SL' instead of 'SL' - Enhanced external closure detection (line 937) to identify trailing stops - Updated handleManualClosure to detect trailing SL at price target 2. Database: - Updated UpdateTradeExitParams interface to accept 'TRAILING_SL' 3. Frontend Analytics: - Updated last trade display to show 'Trailing SL' with special formatting - Purple background/border for TRAILING_SL vs blue for regular SL - Runner emoji (🏃) prefix for trailing stops Impact: - Users can now see when trades exit via trailing stop vs regular SL - Better understanding of runner system performance - Trailing stops visually distinct in analytics dashboard Files Modified: - lib/trading/position-manager.ts (4 locations) - lib/database/trades.ts (UpdateTradeExitParams interface) - app/analytics/page.tsx (exit reason display) - .github/copilot-instructions.md (Common Pitfalls #61, #62)
This commit is contained in:
@@ -82,7 +82,7 @@ export interface UpdateTradeStateParams {
|
||||
export interface UpdateTradeExitParams {
|
||||
positionId: string
|
||||
exitPrice: number
|
||||
exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'manual' | 'emergency'
|
||||
exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'TRAILING_SL' | 'manual' | 'emergency'
|
||||
realizedPnL: number
|
||||
exitOrderTx: string
|
||||
holdTimeSeconds: number
|
||||
|
||||
@@ -73,7 +73,7 @@ export interface ActiveTrade {
|
||||
|
||||
export interface ExitResult {
|
||||
success: boolean
|
||||
reason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'emergency' | 'manual' | 'error'
|
||||
reason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'TRAILING_SL' | 'emergency' | 'manual' | 'error'
|
||||
closePrice?: number
|
||||
closedSize?: number
|
||||
realizedPnL?: number
|
||||
@@ -176,7 +176,7 @@ export class PositionManager {
|
||||
console.log(`👤 Processing manual closure for ${trade.symbol}`)
|
||||
|
||||
// Determine exit reason based on price levels
|
||||
let exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'manual' | 'emergency' = 'manual'
|
||||
let exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'TRAILING_SL' | 'manual' | 'emergency' = 'manual'
|
||||
const profitPercent = this.calculateProfitPercent(trade.entryPrice, currentPrice, trade.direction)
|
||||
|
||||
// Check if price is at TP2 or SL levels
|
||||
@@ -187,8 +187,14 @@ export class PositionManager {
|
||||
exitReason = 'TP2'
|
||||
console.log(`✅ Manual closure was TP2 (price at target)`)
|
||||
} else if (isAtSL) {
|
||||
exitReason = 'SL'
|
||||
console.log(`🛑 Manual closure was SL (price at target)`)
|
||||
// Check if trailing stop was active
|
||||
if (trade.trailingStopActive && trade.tp2Hit) {
|
||||
exitReason = 'TRAILING_SL'
|
||||
console.log(`🏃 Manual closure was Trailing SL (price at trailing stop target)`)
|
||||
} else {
|
||||
exitReason = 'SL'
|
||||
console.log(`🛑 Manual closure was SL (price at target)`)
|
||||
}
|
||||
} else {
|
||||
console.log(`👤 Manual closure confirmed (price not at any target)`)
|
||||
console.log(` Current: $${currentPrice.toFixed(4)}, TP1: $${trade.takeProfitPrice1?.toFixed(4)}, TP2: $${trade.takeProfitPrice2?.toFixed(4)}, SL: $${trade.stopLossPrice?.toFixed(4)}`)
|
||||
@@ -920,7 +926,7 @@ export class PositionManager {
|
||||
|
||||
// Determine exit reason from P&L percentage and trade state
|
||||
// Use actual profit percent to determine what order filled
|
||||
let exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' = 'SL'
|
||||
let exitReason: 'TP1' | 'TP2' | 'SL' | 'SOFT_SL' | 'HARD_SL' | 'TRAILING_SL' = 'SL'
|
||||
|
||||
// CRITICAL (Nov 20, 2025): Check if trailing stop was active
|
||||
// If so, this is a trailing stop exit, not regular SL
|
||||
@@ -934,7 +940,7 @@ export class PositionManager {
|
||||
: currentPrice > trade.peakPrice * 1.01 // More than 1% above peak
|
||||
|
||||
if (isPullback) {
|
||||
exitReason = 'SL' // Trailing stop counts as SL
|
||||
exitReason = 'TRAILING_SL' // Distinguish from regular SL (Nov 24, 2025)
|
||||
console.log(` ✅ Confirmed: Trailing stop hit (pulled back from peak)`)
|
||||
} else {
|
||||
// Very close to peak - might be emergency close or manual
|
||||
@@ -1454,7 +1460,7 @@ export class PositionManager {
|
||||
// Check if trailing stop hit
|
||||
if (this.shouldStopLoss(currentPrice, trade)) {
|
||||
console.log(`🔴 TRAILING STOP HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`)
|
||||
await this.executeExit(trade, 100, 'SL', currentPrice)
|
||||
await this.executeExit(trade, 100, 'TRAILING_SL', currentPrice)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user