- Add BlockedSignal table with 25 fields for comprehensive signal analysis - Track all blocked signals with metrics (ATR, ADX, RSI, volume, price position) - Store quality scores, block reasons, and detailed breakdowns - Include future fields for automated price analysis (priceAfter1/5/15/30Min) - Restore signalQualityVersion field to Trade table Database changes: - New table: BlockedSignal with indexes on symbol, createdAt, score, blockReason - Fixed schema drift from manual changes API changes: - Modified check-risk endpoint to save blocked signals automatically - Fixed hasContextMetrics variable scope (moved to line 209) - Save blocks for: quality score too low, cooldown period, hourly limit - Use config.minSignalQualityScore instead of hardcoded 60 Database helpers: - Added createBlockedSignal() function with try/catch safety - Added getRecentBlockedSignals(limit) for queries - Added getBlockedSignalsForAnalysis(olderThanMinutes) for automation Documentation: - Created BLOCKED_SIGNALS_TRACKING.md with SQL queries and analysis workflow - Created SIGNAL_QUALITY_OPTIMIZATION_ROADMAP.md with 5-phase plan - Documented data-first approach: collect 10-20 signals before optimization Rationale: Only 2 historical trades scored 60-64 (insufficient sample size for threshold decision). Building data collection infrastructure before making premature optimizations. Phase 1 (current): Collect blocked signals for 1-2 weeks Phase 2 (next): Analyze patterns and make data-driven threshold decision Phase 3-5 (future): Automation and ML optimization
5.9 KiB
5.9 KiB
Blocked Signals Tracking System
Date Implemented: November 11, 2025
Status: ✅ ACTIVE
Overview
Automatically tracks all signals that get blocked by the trading bot's risk checks. This data allows us to analyze whether blocked signals would have been profitable, helping optimize the signal quality thresholds over time.
What Gets Tracked
Every time a signal is blocked, the system saves:
Signal Metrics
- Symbol (e.g., SOL-PERP)
- Direction (long/short)
- Timeframe (5min, 15min, 1H, etc.)
- Price at signal time
- ATR, ADX, RSI, volume ratio, price position
Quality Score
- Calculated score (0-100)
- Score version (v4 = current)
- Detailed breakdown of scoring reasons
- Minimum score required (currently 65)
Block Reason
QUALITY_SCORE_TOO_LOW- Score below thresholdCOOLDOWN_PERIOD- Too soon after last tradeHOURLY_TRADE_LIMIT- Too many trades in last hourDAILY_DRAWDOWN_LIMIT- Max daily loss reached
Future Analysis Fields (NOT YET IMPLEMENTED)
priceAfter1Min,priceAfter5Min,priceAfter15Min,priceAfter30MinwouldHitTP1,wouldHitTP2,wouldHitSLanalysisComplete
These will be filled by a monitoring job that tracks what happened after each blocked signal.
Database Table
Table: BlockedSignal
- id (PK)
- createdAt (timestamp)
- symbol, direction, timeframe
- signalPrice, atr, adx, rsi, volumeRatio, pricePosition
- signalQualityScore, signalQualityVersion, scoreBreakdown (JSON)
- minScoreRequired, blockReason, blockDetails
- priceAfter1Min/5Min/15Min/30Min (for future analysis)
- wouldHitTP1/TP2/SL, analysisComplete
Query Examples
Recent Blocked Signals
SELECT
symbol,
direction,
signalQualityScore as score,
minScoreRequired as threshold,
blockReason,
createdAt
FROM "BlockedSignal"
ORDER BY createdAt DESC
LIMIT 20;
Blocked by Quality Score (60-64 range)
SELECT
symbol,
direction,
signalQualityScore,
ROUND(atr::numeric, 2) as atr,
ROUND(adx::numeric, 1) as adx,
ROUND(rsi::numeric, 1) as rsi,
ROUND(pricePosition::numeric, 1) as pos,
blockDetails
FROM "BlockedSignal"
WHERE blockReason = 'QUALITY_SCORE_TOO_LOW'
AND signalQualityScore >= 60
AND signalQualityScore < 65
ORDER BY createdAt DESC;
Breakdown by Block Reason
SELECT
blockReason,
COUNT(*) as count,
ROUND(AVG(signalQualityScore)::numeric, 1) as avg_score,
MIN(signalQualityScore) as min_score,
MAX(signalQualityScore) as max_score
FROM "BlockedSignal"
GROUP BY blockReason
ORDER BY count DESC;
Today's Blocked Signals
SELECT
TO_CHAR(createdAt, 'HH24:MI:SS') as time,
symbol,
direction,
signalQualityScore,
blockReason
FROM "BlockedSignal"
WHERE createdAt >= CURRENT_DATE
ORDER BY createdAt DESC;
Analysis Workflow
Step 1: Collect Data (Current Phase)
- Bot automatically saves blocked signals
- Wait for 10-20 blocked signals to accumulate
- No action needed - runs automatically
Step 2: Manual Analysis (When Ready)
-- Check how many blocked signals we have
SELECT COUNT(*) FROM "BlockedSignal";
-- Analyze score distribution
SELECT
CASE
WHEN signalQualityScore >= 60 THEN '60-64 (Close Call)'
WHEN signalQualityScore >= 55 THEN '55-59 (Marginal)'
WHEN signalQualityScore >= 50 THEN '50-54 (Weak)'
ELSE '0-49 (Very Weak)'
END as score_tier,
COUNT(*) as count,
ROUND(AVG(atr)::numeric, 2) as avg_atr,
ROUND(AVG(adx)::numeric, 1) as avg_adx
FROM "BlockedSignal"
WHERE blockReason = 'QUALITY_SCORE_TOO_LOW'
GROUP BY score_tier
ORDER BY MIN(signalQualityScore) DESC;
Step 3: Future Automation (Not Yet Built)
Create a monitoring job that:
- Fetches
BlockedSignalrecords whereanalysisComplete = falseandcreatedAt> 30min ago - Gets price history for those timestamps
- Calculates if TP1/TP2/SL would have been hit
- Updates the record with analysis results
- Sets
analysisComplete = true
Integration Points
Code Files Modified
prisma/schema.prisma- AddedBlockedSignalmodellib/database/trades.ts- AddedcreateBlockedSignal()functionapp/api/trading/check-risk/route.ts- Saves blocked signals
Where Blocking Happens
- Quality score check (line ~311-350)
- Cooldown period check (line ~281-303)
- Hourly trade limit (line ~235-258)
- Daily drawdown limit (line ~211-223)
Next Steps
Phase 1: Data Collection (CURRENT)
- ✅ Database table created
- ✅ Automatic saving implemented
- ✅ Bot deployed and running
- ⏳ Collect 10-20 blocked signals (wait ~1-2 weeks)
Phase 2: Analysis
- Query blocked signal patterns
- Identify "close calls" (score 60-64)
- Compare with executed trades that had similar scores
- Determine if threshold adjustment is warranted
Phase 3: Automation (Future)
- Build price monitoring job
- Auto-calculate would-be outcomes
- Generate reports on missed opportunities
- Feed data into threshold optimization algorithm
Benefits
- Data-Driven Decisions - No guessing, only facts
- Prevents Over-Optimization - Wait for statistically significant sample
- Tracks All Block Reasons - Not just quality score
- Historical Record - Can review past decisions
- Continuous Improvement - System learns from what it blocks
Important Notes
⚠️ Don't change thresholds prematurely!
- 2 trades is NOT enough data
- Wait for 10-20 blocked signals minimum
- Analyze patterns before making changes
✅ System is working correctly if:
- Blocked signals appear in database
- Each has metrics (ATR, ADX, RSI, etc.)
- Block reason is recorded
- Timestamp is correct
❌ Troubleshooting:
- If no blocked signals appear: Check bot is receiving TradingView alerts with metrics
- If missing metrics: Ensure TradingView webhook includes ATR/ADX/RSI/volume/pricePosition
- If database errors: Check Prisma client is regenerated after schema changes
Last Updated: November 11, 2025
Version: 1.0
Maintained By: Trading Bot v4 Development Team