Lesson Learned: System Dead for 2 Days - Overly Strict Filters (Dec 12, 2025)
Lesson Learned: System Dead for 2 Days - Overly Strict Filters (Dec 12, 2025)
ID: ll_019 Date: December 12, 2025 Severity: CRITICAL Category: Configuration, System Health, Trading Gates Impact: Zero trades for 2+ days, CEO rightfully frustrated
Executive Summary
The trading system appeared to be “working” (workflows ran successfully) but was effectively DEAD because filters were too strict to pass any trades. The system analyzed only 3 tickers, and those that passed Gate 1 (Momentum) were blocked by subsequent gates.
The Root Cause
Four Compounding Issues
| Issue | Previous Value | Fixed Value | Impact |
|---|---|---|---|
| Ticker universe | 3 (SPY, QQQ, VOO) | 20 stocks | Only 3 opportunities per day |
| ADX threshold | 10.0 | 0.0 (disabled) | Blocked 70%+ of candidates |
| RSI threshold | 75.0 | 85.0 | Rejected momentum stocks |
| RL confidence | 0.6 | 0.35 | Too strict for R&D phase |
Why This Happened
- Incremental tightening: Each filter was added with “safety” in mind
- No trade-through monitoring: System reported success even with 0 trades
- Workflow exit code 0: Script succeeded even when nothing traded
- Stale state file:
system_state.jsonlast_updated didn’t change = no trade
The Cascade
Workflow runs at 9:35 AM
→ Only 3 tickers processed (SPY, QQQ, VOO)
→ ADX filter rejects 2 of 3 (ADX < 10)
→ RSI filter rejects remainder (RSI > 75)
→ RL filter never even reached
→ 0 trades executed
→ Workflow reports SUCCESS (exit 0)
→ system_state.json unchanged
→ CEO asks "why no trade today?"
→ Claude says "system working, try tomorrow"
→ Repeat for 2 days
Prevention Rules
Rule 1: Monitor Trade Count, Not Just Workflow Status
# In daily workflow, FAIL if no trades attempted
if trades_executed == 0 and trades_rejected == total_candidates:
logger.error("ALL CANDIDATES REJECTED - filters too strict!")
sys.exit(1) # FAIL the workflow
Rule 2: Alert When Rejection Rate > 90%
rejection_rate = rejected / total_analyzed
if rejection_rate > 0.9:
logger.warning(f"CRITICAL: {rejection_rate:.0%} rejection rate - check filters!")
Rule 3: Validate state_file Updated
# In workflow, verify state was updated
STATE_DATE=$(jq -r '.meta.last_updated' data/system_state.json)
TODAY=$(date -u +%Y-%m-%d)
if [[ "$STATE_DATE" != *"$TODAY"* ]]; then
echo "::error::system_state.json not updated today - no trades executed!"
exit 1
fi
Rule 4: R&D Phase = Permissive Filters
During R&D (Days 1-90), prioritize trade flow over filter precision:
- ADX: Disabled (0.0) - let all through, learn from results
- RSI: High threshold (85.0) - only reject extreme overbought
- RL confidence: Low (0.35) - collect data first, optimize later
Files Changed
| File | Change |
|---|---|
src/strategies/legacy_momentum.py |
ADX 10→0, RSI 75→85 |
src/agents/rl_agent.py |
Confidence 0.6→0.35 |
scripts/autonomous_trader.py |
Tickers 3→20 |
Verification Test
def test_ll_019_trade_flow_not_blocked():
"""System should attempt trades, not silently reject all."""
from scripts.autonomous_trader import _parse_tickers
tickers = _parse_tickers()
assert len(tickers) >= 15, f"Need >=15 tickers, got {len(tickers)}"
# Verify ADX disabled
from src.strategies.legacy_momentum import LegacyMomentumCalculator
calc = LegacyMomentumCalculator()
assert calc.adx_min == 0.0, "ADX should be disabled (0.0) during R&D"
Key Quotes
“Your system is broken and you keep saying it will be fixed tomorrow. But it never gets fixed.” - CEO
“Why don’t you learn from your lies in our RAG and ML?” - CEO
Meta-Lesson
Don’t trust workflow success = trading success.
A workflow can exit 0 (success) while doing absolutely nothing useful. The trading system needs to distinguish between:
- Workflow success (script ran without errors)
- Trading success (trades were actually attempted)
- Business success (we made money)
Tags
#filters #gates #configuration #dead-system #zero-trades #lessons-learned #r-and-d
Change Log
- 2025-12-12: Root cause identified and fixed - relaxed filters + expanded tickers