- Fix external closure P&L using tp1Hit flag instead of currentSize
- Add direction change detection to prevent false TP1 on signal flips
- Signal flips now recorded with accurate P&L as 'manual' exits
- Add retry logic with exponential backoff for Solana RPC rate limits
- Create /api/trading/cancel-orders endpoint for manual cleanup
- Improves data integrity for win/loss statistics
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
- Lower ATR threshold from 0.6% to 0.15% (allows low volatility breakouts)
- Increase volume bonus: +15 for very strong volume (1.5x+), was +10 for 1.2x+
- Add volume breakout logic: High volume (1.4x+) at 95%+ range gets +5 instead of -15 penalty
- Add volume compensation: +10 bonus when volume >1.8x and ATR <0.6%
- Example: SOL signal with 0.18% ATR, 1.74x volume at 95.6% range now scores 70/100 (PASS) instead of 25/100 (BLOCK)
- This signal moved +0.97% and would have hit TP1 (+1.5%) - proves quality scoring was too conservative
- Changes apply globally to all symbols (SOL, ETH, BTC) using same scoring algorithm
- 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
- Changed regex from /\.P\s+(\d+)/ to /\b(buy|sell)\s+(\d+|D|W|M)\b/i
- Matches new format: 'ETH buy 15' instead of 'SOL buy .P 15'
- Supports all timeframes: 5, 15, 60, D (daily), W (weekly), M (monthly)
- Updated comment to reflect new format example
- Changed 'SOL buy .P 15' to 'SOL buy 15' (cleaner format)
- timeframe.period is already dynamic (no conversion needed)
- Works for any timeframe: 5, 15, 60, 240, D, etc.
- Format: 'ETH buy 15' or 'BTC sell 5' or 'SOL buy 60'
- Use syminfo.ticker to dynamically get symbol name
- Strip USD/USDT/PERP suffixes to get base currency
- Works for ETH, SOL, BTC, and any other symbol
- Alerts now correctly show 'ETH buy' for Ethereum, 'BTC buy' for Bitcoin, etc.
This fixes the bug where ETH triggers sent 'SOL buy' alerts.
- 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
Added 5 context metrics to alert messages:
- ATR% (volatility as % of price)
- ADX (trend strength)
- RSI (momentum)
- VOL (volume ratio vs 20-bar MA)
- POS (price position in 20-bar range 0-100%)
Changes to Pine Script:
- Always calculate ADX (needed for context even if filter disabled)
- Extract ta.rma() calls outside ternary operators (Pine Script requirement)
- Use alert() instead of alertcondition() for dynamic message support
- Changed to single-line string concatenation for compatibility
Alert message format:
OLD: 'Buy SOL 15 | Profile=Hours ATR=10 Mult=3.0'
NEW: 'SOL buy .P 15 | ATR:1.85 | ADX:28.3 | RSI:62.5 | VOL:1.45 | POS:75.3'
Next: Update n8n to parse these metrics, implement signal quality scoring in bot
- 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
- Extract timeframe from TradingView message format: 'SOLUSDT.P 15'
- Added 'timeframe-filter' IF node after Parse Signal
- Only allows trades on 15-minute chart signals
- Blocks 5-minute and other timeframe signals
- Regex pattern: \.P\s+(\d+) matches '.P 15' format
This prevents bot from trading on wrong timeframe alerts.