Integrated MA gap analysis into signal quality evaluation pipeline: BACKEND SCORING (lib/trading/signal-quality.ts): - Added maGap?: number parameter to scoreSignalQuality interface - Implemented convergence/divergence scoring logic: * LONG: +15pts tight bullish (0-2%), +12pts converging (-2-0%), +8pts early momentum (-5--2%) * SHORT: +15pts tight bearish (-2-0%), +12pts converging (0-2%), +8pts early momentum (2-5%) * Penalties: -5pts for misaligned MA structure (>5% wrong direction) N8N PARSER (workflows/trading/parse_signal_enhanced.json): - Added MAGAP:([-\d.]+) regex pattern for negative number support - Extracts maGap from TradingView v9 alert messages - Returns maGap in parsed output (backward compatible with v8) - Updated comment to show v9 format API ENDPOINTS: - app/api/trading/check-risk/route.ts: Pass maGap to scoreSignalQuality (2 calls) - app/api/trading/execute/route.ts: Pass maGap to scoreSignalQuality (2 calls) FULL PIPELINE NOW COMPLETE: 1. TradingView v9 → Generates signal with MAGAP field 2. n8n webhook → Extracts maGap from alert message 3. Backend scoring → Evaluates MA gap convergence (+8 to +15 pts) 4. Quality threshold → Borderline signals (75-85) can reach 91+ 5. Execute decision → Only signals scoring ≥91 are executed MOTIVATION: Helps borderline quality signals reach execution threshold without overriding safety rules. Addresses Nov 25 missed opportunity where good signal had MA convergence but borderline quality score. TESTING REQUIRED: - Verify n8n parses MAGAP correctly from v9 alerts - Confirm backend receives maGap parameter - Validate MA gap scoring applied to quality calculation - Monitor first 10-20 v9 signals for scoring accuracy
333 lines
12 KiB
Markdown
333 lines
12 KiB
Markdown
# Indicator v9: MA Gap Quality Enhancement
|
|
|
|
**Status:** 📋 PLANNED
|
|
**Priority:** HIGH (addresses $380 missed profit from Nov 25 blocked signal)
|
|
**Motivation:** v8 indicator catches trend changes BEFORE classic MA crossovers, but quality filter blocks these early signals
|
|
|
|
---
|
|
|
|
## Problem Statement
|
|
|
|
**Real Incident (Nov 25, 2025 21:15 UTC):**
|
|
- v8 generated LONG signal at $136.91 (quality 75, ADX 17.9)
|
|
- Signal BLOCKED by quality threshold (75 < 90 required for LONGs)
|
|
- Chart showed 50 MA converging toward 200 MA (gap ≈ -1% to 0%)
|
|
- Golden cross occurred a few bars AFTER entry signal
|
|
- Price moved to $142+ = **$380 missed profit** (~4% move)
|
|
|
|
**Key Insight:**
|
|
- v8 indicator is **BETTER** than MA crossover timing - catches moves earlier
|
|
- BUT quality filter doesn't recognize when MAs are positioned for breakout
|
|
- Need to reward MA convergence/proximity, not just crossover events
|
|
|
|
---
|
|
|
|
## v9 Enhancement: MA Gap Analysis
|
|
|
|
### Core Concept
|
|
|
|
Instead of detecting exact crossover moment (lagging), measure **MA gap percentage**:
|
|
- **Tight gap** (0-2%) = Strong trend with momentum or imminent crossover
|
|
- **Converging gap** (-2% to 0%) = Potential golden cross brewing
|
|
- **Wide gap** (>2%) = Established trend, less explosive but stable
|
|
|
|
### TradingView Indicator Changes
|
|
|
|
**Add after context metrics calculation (~line 221):**
|
|
|
|
```pinescript
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 🎯 MA GAP ANALYSIS (v9 - for quality scoring)
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
// Calculate 50 and 200 period moving averages
|
|
ma50 = ta.sma(calcC, 50)
|
|
ma200 = ta.sma(calcC, 200)
|
|
|
|
// Calculate MA gap as percentage (negative = 50 below 200)
|
|
maGap = ((ma50 - ma200) / ma200) * 100
|
|
|
|
// Detect convergence (MAs getting closer = potential crossover)
|
|
maConverging = math.abs(maGap) < 2.0 // Within 2% = tight squeeze
|
|
|
|
// Current alignment
|
|
maAlignmentBullish = ma50 > ma200
|
|
|
|
// Optional: Plot MAs on chart for visual confirmation
|
|
plot(ma50, title="MA 50", color=color.yellow, linewidth=1)
|
|
plot(ma200, title="MA 200", color=color.red, linewidth=1)
|
|
```
|
|
|
|
**Update alert messages (~lines 257-258):**
|
|
|
|
```pinescript
|
|
longAlertMsg = baseCurrency + " buy " + timeframe.period + " | ATR:" + str.tostring(atrPercent, "#.##") + " | ADX:" + str.tostring(adxVal, "#.#") + " | RSI:" + str.tostring(rsi14, "#.#") + " | VOL:" + str.tostring(volumeRatio, "#.##") + " | POS:" + str.tostring(pricePosition, "#.#") + " | MAGAP:" + str.tostring(maGap, "#.##") + " | IND:v9"
|
|
|
|
shortAlertMsg = baseCurrency + " sell " + timeframe.period + " | ATR:" + str.tostring(atrPercent, "#.##") + " | ADX:" + str.tostring(adxVal, "#.#") + " | RSI:" + str.tostring(rsi14, "#.#") + " | VOL:" + str.tostring(volumeRatio, "#.##") + " | POS:" + str.tostring(pricePosition, "#.#") + " | MAGAP:" + str.tostring(maGap, "#.##") + " | IND:v9"
|
|
```
|
|
|
|
---
|
|
|
|
## Backend Quality Scoring Changes
|
|
|
|
### Update scoreSignalQuality() function
|
|
|
|
**File:** `lib/trading/signal-quality.ts`
|
|
|
|
**Add parameters:**
|
|
```typescript
|
|
export async function scoreSignalQuality(params: {
|
|
// ... existing params ...
|
|
maGap?: number // NEW: % gap between 50 and 200 MA
|
|
maAlignmentBullish?: boolean // NEW: is 50 above 200?
|
|
}): Promise<SignalQualityResult> {
|
|
```
|
|
|
|
**Add scoring logic (after existing metrics):**
|
|
|
|
```typescript
|
|
// ═══════════════════════════════════════════════════════════
|
|
// MA Gap Analysis (v9 - Nov 26, 2025)
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
if (params.maGap !== undefined) {
|
|
if (params.direction === 'long') {
|
|
// LONG scenarios
|
|
if (params.maGap >= 0 && params.maGap < 2.0) {
|
|
// 50 MA above 200 MA, tight gap (0-2%)
|
|
// = Bullish trend with momentum OR fresh golden cross
|
|
score += 15
|
|
reasons.push(`🎯 MA bullish + tight gap (${params.maGap.toFixed(2)}%) = strong momentum (+15 pts)`)
|
|
|
|
} else if (params.maGap < 0 && params.maGap > -2.0) {
|
|
// 50 MA below 200 MA but converging (-2% to 0%)
|
|
// = Potential golden cross brewing (early detection like Nov 25 signal!)
|
|
score += 12
|
|
reasons.push(`🌟 MA converging (${params.maGap.toFixed(2)}%) = golden cross potential (+12 pts)`)
|
|
|
|
} else if (params.maGap >= 2.0 && params.maGap < 5.0) {
|
|
// 50 MA well above 200 MA (2-5%)
|
|
// = Established bullish trend, stable but less explosive
|
|
score += 8
|
|
reasons.push(`📈 MA strong bullish trend (${params.maGap.toFixed(2)}%) (+8 pts)`)
|
|
|
|
} else if (params.maGap >= 5.0) {
|
|
// 50 MA far above 200 MA (>5%)
|
|
// = Very extended, potential exhaustion
|
|
score += 5
|
|
reasons.push(`⚠️ MA extended bullish (${params.maGap.toFixed(2)}%) = overbought risk (+5 pts)`)
|
|
|
|
} else if (params.maGap <= -2.0) {
|
|
// 50 MA significantly below 200 MA
|
|
// = Bearish trend, LONG signal is counter-trend (risky)
|
|
score -= 10
|
|
reasons.push(`❌ MA bearish divergence (${params.maGap.toFixed(2)}%) = counter-trend risk (-10 pts)`)
|
|
}
|
|
|
|
} else if (params.direction === 'short') {
|
|
// SHORT scenarios (inverse of LONG logic)
|
|
if (params.maGap <= 0 && params.maGap > -2.0) {
|
|
// 50 MA below 200 MA, tight gap (-2% to 0%)
|
|
// = Bearish trend with momentum OR fresh death cross
|
|
score += 15
|
|
reasons.push(`🎯 MA bearish + tight gap (${params.maGap.toFixed(2)}%) = strong momentum (+15 pts)`)
|
|
|
|
} else if (params.maGap > 0 && params.maGap < 2.0) {
|
|
// 50 MA above 200 MA but converging (0% to 2%)
|
|
// = Potential death cross brewing
|
|
score += 12
|
|
reasons.push(`🌟 MA converging (${params.maGap.toFixed(2)}%) = death cross potential (+12 pts)`)
|
|
|
|
} else if (params.maGap <= -2.0 && params.maGap > -5.0) {
|
|
// 50 MA well below 200 MA (-5% to -2%)
|
|
// = Established bearish trend
|
|
score += 8
|
|
reasons.push(`📉 MA strong bearish trend (${params.maGap.toFixed(2)}%) (+8 pts)`)
|
|
|
|
} else if (params.maGap <= -5.0) {
|
|
// 50 MA far below 200 MA (<-5%)
|
|
// = Very extended, potential bounce risk
|
|
score += 5
|
|
reasons.push(`⚠️ MA extended bearish (${params.maGap.toFixed(2)}%) = oversold risk (+5 pts)`)
|
|
|
|
} else if (params.maGap >= 2.0) {
|
|
// 50 MA significantly above 200 MA
|
|
// = Bullish trend, SHORT signal is counter-trend (risky)
|
|
score -= 10
|
|
reasons.push(`❌ MA bullish divergence (${params.maGap.toFixed(2)}%) = counter-trend risk (-10 pts)`)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Expected Impact
|
|
|
|
### Nov 25 21:15 Signal Reanalysis
|
|
|
|
**Original (v8):**
|
|
- Quality: 75 (blocked)
|
|
- ADX: 17.9 (weak)
|
|
- MA Gap: ≈ -1.0% (50 below 200, converging)
|
|
|
|
**With v9 MA Gap Enhancement:**
|
|
- Base quality: 75
|
|
- MA converging bonus: +12
|
|
- **New quality: 87** (closer but still blocked)
|
|
|
|
**Note:** Would still need ADX improvement OR slightly lower threshold (88-89?) to catch this specific signal. But the +12 points get us much closer and would catch signals with ADX 18-20.
|
|
|
|
### Alternative Scenarios
|
|
|
|
**Scenario A: MA Gap -0.5% (very tight convergence)**
|
|
- Quality 75 + 12 = **87** (close)
|
|
|
|
**Scenario B: MA Gap +0.5% (just crossed, tight)**
|
|
- Quality 75 + 15 = **90** ✅ **PASSES!**
|
|
|
|
**Scenario C: MA Gap +1.8% (recent golden cross, momentum strong)**
|
|
- Quality 75 + 15 = **90** ✅ **PASSES!**
|
|
|
|
---
|
|
|
|
## Implementation Checklist
|
|
|
|
### Phase 1: TradingView Indicator
|
|
- [ ] Add MA50 and MA200 calculations
|
|
- [ ] Calculate maGap percentage
|
|
- [ ] Add maGap to alert message payload
|
|
- [ ] Optional: Plot MA lines on chart
|
|
- [ ] Update indicatorVer from "v8" to "v9"
|
|
- [ ] Test on SOL-PERP 5min chart
|
|
|
|
### Phase 2: Backend Integration
|
|
- [ ] Update TypeScript interfaces for maGap parameter
|
|
- [ ] Modify scoreSignalQuality() with MA gap logic
|
|
- [ ] Update check-risk endpoint to accept maGap
|
|
- [ ] Update execute endpoint to accept maGap
|
|
- [ ] Add maGap to database fields (Trade table, BlockedSignal table)
|
|
|
|
### Phase 3: Testing & Validation
|
|
- [ ] Deploy v9 indicator to TradingView
|
|
- [ ] Trigger test signals manually
|
|
- [ ] Verify maGap calculation matches chart visual
|
|
- [ ] Check quality scores increase appropriately
|
|
- [ ] Monitor first 20-30 signals for validation
|
|
|
|
### Phase 4: Data Collection
|
|
- [ ] Collect 50+ v9 signals
|
|
- [ ] Compare v8 vs v9 win rates
|
|
- [ ] Analyze: Did MA gap bonus catch missed winners?
|
|
- [ ] SQL queries to validate improvement
|
|
- [ ] Adjust bonus points if needed (12/15 → 10/12 or 15/18)
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
**Target Improvements:**
|
|
1. **Catch signals like Nov 25 21:15:** Quality 75 + MA converging → 87-90 range
|
|
2. **Reduce false negatives:** Fewer blocked signals that would have been winners
|
|
3. **Maintain safety:** Don't add too many low-quality signals
|
|
4. **Win rate:** v9 should maintain or improve v8's 57.1% WR
|
|
|
|
**Validation Queries:**
|
|
|
|
```sql
|
|
-- Compare v9 MA gap bonus impact
|
|
SELECT
|
|
CASE
|
|
WHEN "signalQualityScore" >= 90 THEN 'Passed'
|
|
WHEN "signalQualityScore" >= 80 THEN 'Close (80-89)'
|
|
ELSE 'Blocked (<80)'
|
|
END as category,
|
|
COUNT(*) as signals,
|
|
AVG("scoreBreakdown"->>'maGap')::numeric as avg_ma_gap
|
|
FROM "BlockedSignal"
|
|
WHERE "indicatorVersion" = 'v9'
|
|
AND "scoreBreakdown"->>'maGap' IS NOT NULL
|
|
GROUP BY category;
|
|
|
|
-- Find signals that would pass with v9 but were blocked in v8
|
|
SELECT
|
|
TO_CHAR("createdAt", 'MM-DD HH24:MI') as time,
|
|
symbol, direction,
|
|
"signalQualityScore" as original_score,
|
|
-- Simulate v9 score (add 12 for converging, 15 for tight)
|
|
CASE
|
|
WHEN ("scoreBreakdown"->>'maGap')::numeric >= 0 AND ("scoreBreakdown"->>'maGap')::numeric < 2.0
|
|
THEN "signalQualityScore" + 15
|
|
WHEN ("scoreBreakdown"->>'maGap')::numeric < 0 AND ("scoreBreakdown"->>'maGap')::numeric > -2.0
|
|
THEN "signalQualityScore" + 12
|
|
ELSE "signalQualityScore"
|
|
END as v9_score,
|
|
"blockReason"
|
|
FROM "BlockedSignal"
|
|
WHERE "signalQualityScore" < 90 -- Was blocked
|
|
AND "indicatorVersion" = 'v8'
|
|
ORDER BY "createdAt" DESC
|
|
LIMIT 20;
|
|
```
|
|
|
|
---
|
|
|
|
## Risk Mitigation
|
|
|
|
### Potential Issues
|
|
|
|
1. **MA calculation lag:** 200-period MA requires significant history
|
|
- **Mitigation:** TradingView has full history, no issue
|
|
|
|
2. **Whipsaw during sideways markets:** MAs converge often in chop
|
|
- **Mitigation:** ADX filter still applies (weak ADX = less bonus effect)
|
|
|
|
3. **Over-optimization on single signal:** Nov 25 may be outlier
|
|
- **Mitigation:** Collect 50+ v9 signals before final judgment
|
|
|
|
4. **Bonus points too generous:** Could inflate scores artificially
|
|
- **Mitigation:** Start conservative (12/15), adjust based on data
|
|
|
|
### Rollback Plan
|
|
|
|
If v9 performs worse than v8:
|
|
1. Revert TradingView indicator to v8
|
|
2. Keep backend code but disable MA gap bonus
|
|
3. Analyze what went wrong (false positives? whipsaw signals?)
|
|
4. Redesign MA gap logic with tighter conditions
|
|
|
|
---
|
|
|
|
## Timeline
|
|
|
|
**Estimated Implementation Time:**
|
|
- TradingView changes: 30 minutes
|
|
- Backend integration: 1 hour
|
|
- Testing & deployment: 30 minutes
|
|
- **Total: ~2 hours**
|
|
|
|
**Data Collection:**
|
|
- Minimum 50 signals: 2-3 weeks (at ~3-5 signals/day)
|
|
- Comparative analysis: 1 week after 50 signals
|
|
|
|
**Decision Point:**
|
|
- After 50 v9 signals: Keep, adjust, or revert based on performance data
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- This enhancement preserves v8's early detection advantage
|
|
- Adds context awareness of MA positioning
|
|
- Rewards both imminent crossovers (converging) AND fresh crossovers (tight gap)
|
|
- Balances explosive potential (tight gaps) with trend stability (wider gaps)
|
|
- Counter-trend penalties prevent chasing wrong direction
|
|
|
|
**Key Insight:** v8 catches momentum shifts BEFORE visible MA crossovers. v9 validates those shifts by checking if MA structure supports the move.
|
|
|
|
---
|
|
|
|
**Created:** Nov 26, 2025
|
|
**Motivation:** $380 missed profit from Nov 25 21:15 blocked signal
|
|
**Expected Impact:** Catch 15-25% more profitable signals while maintaining quality standards
|