CRITICAL BUG FIX: - Settings page saved MIN_SIGNAL_QUALITY_SCORE to .env but check-risk had hardcoded value - Now reads from config.minSignalQualityScore (defaults to 65, editable via /settings) - Prevents settings changes from being ignored after restart ANTI-CHOP FILTER FIXES: - Fixed volume breakout bonus conflicting with anti-chop filter - Volume breakout now requires ADX > 18 (trending market) - Prevents high volume + low ADX from getting rewarded instead of penalized - Anti-chop filter now properly blocks whipsaw traps at score 60 TESTING INFRASTRUCTURE: - Added backtest script showing +17.1% P&L improvement (saved $242 in losses) - Added test-signals.sh for comprehensive signal quality validation - Added test-recent-signals.sh for analyzing actual trading session signals - All tests passing: timeframe awareness, anti-chop, score thresholds CHANGES: - config/trading.ts: Added minSignalQualityScore to interface and defaults - app/api/trading/check-risk/route.ts: Use config value instead of hardcoded 65 - lib/trading/signal-quality.ts: Fixed volume breakout bonus logic - .env: Added MIN_SIGNAL_QUALITY_SCORE=65 - scripts/: Added comprehensive testing tools BACKTEST RESULTS (Last 30 trades): - Old system (score ≥60): $1,412.79 P&L - New system (score ≥65 + anti-chop): $1,654.79 P&L - Improvement: +$242.00 (+17.1%) - Blocked 5 losing trades, missed 0 winners
163 lines
5.0 KiB
Bash
Executable File
163 lines
5.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Test Script: Verify Anti-Chop Filter & Timeframe Scoring
|
|
# Tests the exact signals from today to verify correct behavior
|
|
|
|
API_URL="http://localhost:3001/api/trading/check-risk"
|
|
API_KEY="2a344f0149442c857fb56c038c0c7d1b113883b830bec792c76f1e0efa15d6bb"
|
|
|
|
echo "=========================================="
|
|
echo "SIGNAL QUALITY TEST SUITE"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Test 1: The SHORT that was TAKEN (should pass with ~85 score)
|
|
echo "TEST 1: SHORT at 10:05 (SHOULD PASS)"
|
|
echo "Signal: SOL sell .P 5 | ATR:0.26 | ADX:16.9 | RSI:41.6 | VOL:1.37 | POS:24.3"
|
|
echo ""
|
|
curl -X POST "$API_URL" \
|
|
-H "Authorization: Bearer $API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"symbol": "SOL-PERP",
|
|
"direction": "short",
|
|
"timeframe": "5",
|
|
"atr": 0.26,
|
|
"adx": 16.9,
|
|
"rsi": 41.6,
|
|
"volumeRatio": 1.37,
|
|
"pricePosition": 24.3
|
|
}' 2>/dev/null | jq -r '
|
|
if .allowed then
|
|
"✅ PASSED - Score: \(.qualityScore)/100"
|
|
else
|
|
"❌ BLOCKED - Score: \(.qualityScore)/100\nReason: \(.reason)\nDetails: \(.details)"
|
|
end
|
|
'
|
|
echo ""
|
|
echo "------------------------------------------"
|
|
echo ""
|
|
|
|
# Test 2: The LONG that was BLOCKED (should NOW PASS with ~70 score because timeframe aware)
|
|
echo "TEST 2: LONG at 10:10 (WAS BLOCKED AT 55, SHOULD NOW PASS AT 70)"
|
|
echo "Signal: SOL buy .P 5 | ATR:0.27 | ADX:16.1 | RSI:47.7 | VOL:1.45 | POS:42"
|
|
echo ""
|
|
curl -X POST "$API_URL" \
|
|
-H "Authorization: Bearer $API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"symbol": "SOL-PERP",
|
|
"direction": "long",
|
|
"timeframe": "5",
|
|
"atr": 0.27,
|
|
"adx": 16.1,
|
|
"rsi": 47.7,
|
|
"volumeRatio": 1.45,
|
|
"pricePosition": 42
|
|
}' 2>/dev/null | jq -r '
|
|
if .allowed then
|
|
"✅ PASSED - Score: \(.qualityScore)/100 (CORRECT - was wrongly blocked before)"
|
|
else
|
|
"❌ BLOCKED - Score: \(.qualityScore)/100 (WRONG - should pass!)\nReason: \(.reason)"
|
|
end
|
|
'
|
|
echo ""
|
|
echo "------------------------------------------"
|
|
echo ""
|
|
|
|
# Test 3: Anti-chop filter - High volume + low ADX (should be blocked)
|
|
echo "TEST 3: Anti-Chop Filter Test (SHOULD BLOCK)"
|
|
echo "Signal: SOL sell .P 5 | ATR:0.33 | ADX:14.8 | RSI:37.1 | VOL:2.29 | POS:12"
|
|
echo "Expected: BLOCKED by anti-chop filter (ADX<16 + VOL>1.5x = whipsaw trap)"
|
|
echo ""
|
|
curl -X POST "$API_URL" \
|
|
-H "Authorization: Bearer $API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"symbol": "SOL-PERP",
|
|
"direction": "short",
|
|
"timeframe": "5",
|
|
"atr": 0.33,
|
|
"adx": 14.8,
|
|
"rsi": 37.1,
|
|
"volumeRatio": 2.29,
|
|
"pricePosition": 12
|
|
}' 2>/dev/null | jq -r '
|
|
if .allowed then
|
|
"❌ PASSED - Score: \(.qualityScore)/100 (WRONG - anti-chop should block!)"
|
|
else
|
|
"✅ BLOCKED - Score: \(.qualityScore)/100 (CORRECT - whipsaw trap detected)"
|
|
end
|
|
'
|
|
echo ""
|
|
echo "------------------------------------------"
|
|
echo ""
|
|
|
|
# Test 4: Timeframe NOT passed (should treat as daily chart - different thresholds)
|
|
echo "TEST 4: Same signal WITHOUT timeframe (daily chart thresholds)"
|
|
echo "Signal: SOL buy | ATR:0.27 | ADX:16.1 | RSI:47.7 | VOL:1.45 | POS:42"
|
|
echo "Expected: Should be BLOCKED (ADX 16.1 is weak for daily charts)"
|
|
echo ""
|
|
curl -X POST "$API_URL" \
|
|
-H "Authorization: Bearer $API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"symbol": "SOL-PERP",
|
|
"direction": "long",
|
|
"atr": 0.27,
|
|
"adx": 16.1,
|
|
"rsi": 47.7,
|
|
"volumeRatio": 1.45,
|
|
"pricePosition": 42
|
|
}' 2>/dev/null | jq -r '
|
|
if .allowed then
|
|
"❌ PASSED - Score: \(.qualityScore)/100 (WRONG - should block without timeframe)"
|
|
else
|
|
"✅ BLOCKED - Score: \(.qualityScore)/100 (CORRECT - weak for daily chart)"
|
|
end
|
|
'
|
|
echo ""
|
|
echo "------------------------------------------"
|
|
echo ""
|
|
|
|
# Test 5: Strong signal (should definitely pass)
|
|
echo "TEST 5: Strong Signal (SHOULD PASS)"
|
|
echo "Signal: SOL buy .P 5 | ATR:0.52 | ADX:22.7 | RSI:53.1 | VOL:0.88 | POS:37.9"
|
|
echo ""
|
|
curl -X POST "$API_URL" \
|
|
-H "Authorization: Bearer $API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"symbol": "SOL-PERP",
|
|
"direction": "long",
|
|
"timeframe": "5",
|
|
"atr": 0.52,
|
|
"adx": 22.7,
|
|
"rsi": 53.1,
|
|
"volumeRatio": 0.88,
|
|
"pricePosition": 37.9
|
|
}' 2>/dev/null | jq -r '
|
|
if .allowed then
|
|
"✅ PASSED - Score: \(.qualityScore)/100 (CORRECT)"
|
|
else
|
|
"❌ BLOCKED - Score: \(.qualityScore)/100 (WRONG - this is a good signal!)\nReason: \(.reason)"
|
|
end
|
|
'
|
|
echo ""
|
|
echo "------------------------------------------"
|
|
echo ""
|
|
|
|
echo "=========================================="
|
|
echo "TEST SUMMARY"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Key things to verify from docker logs:"
|
|
echo " 1. '🔍 Risk check for:' shows timeframe field"
|
|
echo " 2. ADX scoring says 'Moderate trend for 5min' (not just 'Weak trend')"
|
|
echo " 3. Anti-chop filter shows '⚠️ Whipsaw trap' message"
|
|
echo " 4. Score threshold is 65 (not 60)"
|
|
echo ""
|
|
echo "Check logs with:"
|
|
echo " docker logs trading-bot-v4 --tail 100 | grep -A 10 'Risk check'"
|
|
echo ""
|