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:
89
fix_zero_pnl_trades_v2.sql
Normal file
89
fix_zero_pnl_trades_v2.sql
Normal file
@@ -0,0 +1,89 @@
|
||||
-- Fix Zero P&L Trades (CORRECTED VERSION)
|
||||
-- This script recalculates P&L for trades that were incorrectly recorded as $0.00
|
||||
-- IMPORTANT: positionSizeUSD already includes leverage, so we must divide by leverage
|
||||
-- to get the actual capital at risk, then multiply by price change %
|
||||
-- Created: 2025-11-03
|
||||
-- Backup: backup_before_pnl_fix_20251103_091248.sql
|
||||
|
||||
-- First, let's see what we're fixing
|
||||
SELECT
|
||||
id,
|
||||
symbol,
|
||||
direction,
|
||||
ROUND("entryPrice"::numeric, 2) as entry,
|
||||
ROUND("exitPrice"::numeric, 2) as exit,
|
||||
"positionSizeUSD",
|
||||
leverage,
|
||||
"realizedPnL" as current_pnl,
|
||||
"exitReason"
|
||||
FROM "Trade"
|
||||
WHERE "realizedPnL" = 0
|
||||
AND "exitReason" IS NOT NULL
|
||||
AND "exitPrice" IS NOT NULL
|
||||
AND "exitOrderTx" IN ('ON_CHAIN_ORDER', 'UNKNOWN_CLOSURE')
|
||||
ORDER BY "entryTime" DESC;
|
||||
|
||||
-- CORRECT P&L Formula:
|
||||
-- 1. actualCapital = positionSizeUSD / leverage (remove leverage to get base capital)
|
||||
-- 2. priceChange% = (exitPrice - entryPrice) / entryPrice * 100
|
||||
-- 3. accountReturn% = priceChange% * leverage (leverage amplifies returns)
|
||||
-- 4. realizedPnL = actualCapital * (accountReturn% / 100)
|
||||
--
|
||||
-- Simplified: realizedPnL = (positionSizeUSD / leverage) * (priceChange% * leverage) / 100
|
||||
-- = positionSizeUSD * priceChange% / 100
|
||||
-- (leverage cancels out!)
|
||||
|
||||
UPDATE "Trade"
|
||||
SET
|
||||
"realizedPnL" = CASE
|
||||
WHEN direction = 'long' THEN
|
||||
-- Long: profit when exit > entry
|
||||
("positionSizeUSD" * ((("exitPrice" - "entryPrice") / "entryPrice") * 100)) / 100
|
||||
WHEN direction = 'short' THEN
|
||||
-- Short: profit when exit < entry
|
||||
("positionSizeUSD" * ((("entryPrice" - "exitPrice") / "entryPrice") * 100)) / 100
|
||||
ELSE 0
|
||||
END,
|
||||
"realizedPnLPercent" = CASE
|
||||
WHEN direction = 'long' THEN
|
||||
((("exitPrice" - "entryPrice") / "entryPrice") * 100)
|
||||
WHEN direction = 'short' THEN
|
||||
((("entryPrice" - "exitPrice") / "entryPrice") * 100)
|
||||
ELSE 0
|
||||
END,
|
||||
"updatedAt" = NOW()
|
||||
WHERE "realizedPnL" = 0
|
||||
AND "exitReason" IS NOT NULL
|
||||
AND "exitPrice" IS NOT NULL
|
||||
AND "exitOrderTx" IN ('ON_CHAIN_ORDER', 'UNKNOWN_CLOSURE');
|
||||
|
||||
-- Show the results after fix
|
||||
SELECT
|
||||
id,
|
||||
symbol,
|
||||
direction,
|
||||
ROUND("entryPrice"::numeric, 4) as entry,
|
||||
ROUND("exitPrice"::numeric, 4) as exit,
|
||||
ROUND("positionSizeUSD"::numeric, 2) as notional,
|
||||
leverage,
|
||||
ROUND(("positionSizeUSD" / leverage)::numeric, 2) as capital,
|
||||
ROUND("realizedPnL"::numeric, 2) as fixed_pnl,
|
||||
ROUND("realizedPnLPercent"::numeric, 2) as pnl_pct,
|
||||
"exitReason"
|
||||
FROM "Trade"
|
||||
WHERE "exitOrderTx" IN ('ON_CHAIN_ORDER', 'UNKNOWN_CLOSURE')
|
||||
AND "exitReason" IS NOT NULL
|
||||
ORDER BY "entryTime" DESC
|
||||
LIMIT 15;
|
||||
|
||||
-- Show new total P&L
|
||||
SELECT
|
||||
COUNT(*) as total_trades,
|
||||
SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins,
|
||||
SUM(CASE WHEN "realizedPnL" <= 0 THEN 1 ELSE 0 END) as losses,
|
||||
ROUND(SUM("realizedPnL")::numeric, 2) as total_pnl,
|
||||
ROUND(AVG(CASE WHEN "realizedPnL" > 0 THEN "realizedPnL" END)::numeric, 2) as avg_win,
|
||||
ROUND(AVG(CASE WHEN "realizedPnL" <= 0 THEN "realizedPnL" END)::numeric, 2) as avg_loss
|
||||
FROM "Trade"
|
||||
WHERE "entryTime" >= NOW() - INTERVAL '7 days'
|
||||
AND "exitReason" IS NOT NULL;
|
||||
Reference in New Issue
Block a user