feat: Add per-symbol trading controls for SOL and ETH
- Add SymbolSettings interface with enabled/positionSize/leverage fields - Implement per-symbol ENV variables (SOLANA_*, ETHEREUM_*) - Add SOL and ETH sections to settings UI with enable/disable toggles - Add symbol-specific test buttons (SOL LONG/SHORT, ETH LONG/SHORT) - Update execute and test endpoints to check symbol enabled status - Add real-time risk/reward calculator per symbol - Rename 'Position Sizing' to 'Global Fallback' for clarity - Fix position manager P&L calculation for externally closed positions - Fix zero P&L bug affecting 12 historical trades - Add SQL scripts for recalculating historical P&L data - Move archive TypeScript files to .archive to fix build Defaults: - SOL: 10 base × 10x leverage = 100 notional (profit trading) - ETH: base × 1x leverage = notional (data collection) - Global: 10 × 10x for BTC and other symbols Configuration priority: Per-symbol ENV > Market config > Global ENV > Defaults
This commit is contained in:
@@ -369,12 +369,23 @@ export class PositionManager {
|
||||
console.log(`🗑️ This is a different/newer position - removing old trade from monitoring`)
|
||||
|
||||
// Mark the old trade as closed (we lost track of it)
|
||||
// Calculate approximate P&L using last known price
|
||||
const profitPercent = this.calculateProfitPercent(
|
||||
trade.entryPrice,
|
||||
trade.lastPrice,
|
||||
trade.direction
|
||||
)
|
||||
const accountPnL = profitPercent * trade.leverage
|
||||
const estimatedPnL = (trade.currentSize * accountPnL) / 100
|
||||
|
||||
console.log(`💰 Estimated P&L for lost trade: ${profitPercent.toFixed(2)}% price → ${accountPnL.toFixed(2)}% account → $${estimatedPnL.toFixed(2)} realized`)
|
||||
|
||||
try {
|
||||
await updateTradeExit({
|
||||
positionId: trade.positionId,
|
||||
exitPrice: trade.lastPrice,
|
||||
exitReason: 'SOFT_SL', // Unknown - just mark as closed
|
||||
realizedPnL: 0,
|
||||
realizedPnL: estimatedPnL,
|
||||
exitOrderTx: 'UNKNOWN_CLOSURE',
|
||||
holdTimeSeconds: Math.floor((Date.now() - trade.entryTime) / 1000),
|
||||
maxDrawdown: Math.abs(Math.min(0, trade.maxAdverseExcursion)),
|
||||
@@ -384,7 +395,7 @@ export class PositionManager {
|
||||
maxFavorablePrice: trade.maxFavorablePrice,
|
||||
maxAdversePrice: trade.maxAdversePrice,
|
||||
})
|
||||
console.log(`💾 Old trade marked as closed (lost tracking)`)
|
||||
console.log(`💾 Old trade marked as closed (lost tracking) with estimated P&L: $${estimatedPnL.toFixed(2)}`)
|
||||
} catch (dbError) {
|
||||
console.error('❌ Failed to save lost trade closure:', dbError)
|
||||
}
|
||||
@@ -404,8 +415,15 @@ export class PositionManager {
|
||||
|
||||
if (position === null || position.size === 0) {
|
||||
|
||||
// Save currentSize before it becomes 0
|
||||
const sizeBeforeClosure = trade.currentSize
|
||||
// CRITICAL: Use original position size for P&L calculation on external closures
|
||||
// trade.currentSize may already be 0 if on-chain orders closed the position before
|
||||
// Position Manager detected it, causing zero P&L bug
|
||||
const sizeForPnL = trade.currentSize > 0 ? trade.currentSize : trade.positionSize
|
||||
|
||||
console.log(`📊 External closure detected - Position size tracking:`)
|
||||
console.log(` Original size: $${trade.positionSize.toFixed(2)}`)
|
||||
console.log(` Tracked current size: $${trade.currentSize.toFixed(2)}`)
|
||||
console.log(` Using for P&L calc: $${sizeForPnL.toFixed(2)}`)
|
||||
|
||||
// Determine exit reason based on TP flags and realized P&L
|
||||
// CRITICAL: Use trade state flags, not current price (on-chain orders filled in the past!)
|
||||
@@ -418,7 +436,7 @@ export class PositionManager {
|
||||
trade.direction
|
||||
)
|
||||
const accountPnL = profitPercent * trade.leverage
|
||||
const realizedPnL = (sizeBeforeClosure * accountPnL) / 100
|
||||
const realizedPnL = (sizeForPnL * accountPnL) / 100
|
||||
|
||||
// Determine exit reason from trade state and P&L
|
||||
if (trade.tp2Hit) {
|
||||
|
||||
Reference in New Issue
Block a user