PROBLEM:
- Trades with quality score 35 and 45 were executed (threshold: 60)
- Position opened without risk management after signal flips
- "Parse Signal" node didn't extract ATR/ADX/RSI/volumeRatio/pricePosition
- "Check Risk" node only sent symbol+direction, skipped quality validation
- "Execute Trade" node didn't forward metrics to backend
ROOT CAUSE:
n8n workflow had TWO paths:
1. NEW: Parse Signal Enhanced → Check Risk1 → Execute Trade1 (working)
2. OLD: Parse Signal → Check Risk → Execute Trade (broken)
Old path bypassed quality check because check-risk endpoint saw
hasContextMetrics=false and allowed trade without validation.
FIX:
1. Changed "Parse Signal" from 'set' to 'code' node with metric extraction
2. Updated "Check Risk" to send atr/adx/rsi/volumeRatio/pricePosition
3. Updated "Execute Trade" to forward all metrics to backend
IMPACT:
- All trades now validated against quality score threshold (60)
- Low-quality signals properly blocked before execution
- Prevents positions opening without proper risk management
Evidence from database showed 3 trades in 2 hours with scores <60:
- 10:00:31 SOL LONG - Score 35 (phantom detected)
- 09:55:30 SOL SHORT - Score 35 (executed)
- 09:35:14 SOL LONG - Score 45 (executed)
All three should have been blocked. Fix prevents future bypasses.
- 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
- Added qualityScore field to Execute Trade node JSON body
- Pulls value from Check Risk response: .item.json.qualityScore
- This enables quality score to be saved in database and displayed on analytics dashboard
- Execute Trade node was trying to access qualityScore from Check Risk node
- This caused syntax error in n8n when Check Risk blocks the trade
- Backend API calculates qualityScore from the provided metrics (atr, adx, rsi, etc.)
- No need to pass it explicitly in the request body
- 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
- Updated to Set node v3.4 with proper assignments format
- Explicitly reference Parse Signal Enhanced for rawMessage
- Use $json for Check Risk output (reason, details, score, reasons)
- Properly formatted message with all data fields populated
- Added seconds to timestamp for better tracking
- Removed ugly escaped syntax with $('Parse Signal').item.json references
- Use $json directly (cleaner and works correctly)
- Issues now display as bullet points instead of comma-separated
- Proper line breaks and formatting
- Professional looking blocked trade notifications
Enhanced 'Format Risk' node in n8n workflow to display:
- Specific blocking reason (duplicate, drawdown, cooldown, quality, etc.)
- Details about what triggered the block
- Quality score if low quality was the reason
- Quality issues breakdown (ATR too low, weak ADX, etc.)
Example output:
TRADE BLOCKED
SHORT | ATR:0.30 | ADX:19.1 | RSI:46
Issues: ATR too low (0.30% - dead market), Moderate trend (ADX 19.1), RSI supports short (46.0)
14:23
- Save currentSize before it becomes 0 in external closure detection
- Use sizeBeforeClosure for P&L calculation instead of trade.currentSize
- Prevents /bin/bash.00 P&L for TP2 exits when position closes externally
- Ensures win/loss analytics counts TP trades correctly