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:
mindesbunister
2025-11-03 10:28:48 +01:00
parent aa8e9f130a
commit 881a99242d
17 changed files with 1996 additions and 108 deletions

View File

@@ -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) {