# Indicator Version Tracking System **Date:** November 11, 2025 **Purpose:** Track which Pine Script version generated each signal for comparative analysis ## Changes Made ### 1. Database Schema (`prisma/schema.prisma`) Added `indicatorVersion` field to both tables: ```prisma model Trade { // ... existing fields ... indicatorVersion String? // Pine Script version (v5, v6, etc.) } model BlockedSignal { // ... existing fields ... indicatorVersion String? // Pine Script version (v5, v6, etc.) } ``` ### 2. Pine Script v6 (`moneyline_v6_improved.pinescript`) Added version identifier to alert messages: ```pinescript // Line 245-247 indicatorVer = "v6" // Alert messages now include: | IND:v6 longAlertMsg = "SOL buy 5 | ATR:0.45 | ADX:28.3 | RSI:62.5 | VOL:1.45 | POS:75.3 | IND:v6" shortAlertMsg = "SOL sell 5 | ATR:0.45 | ADX:28.3 | RSI:62.5 | VOL:1.45 | POS:75.3 | IND:v6" ``` ### 3. n8n Workflow Update (REQUIRED) **File:** `workflows/trading/Money_Machine.json` **Node:** `Parse Signal Enhanced` (JavaScript code) **Add this code after the pricePosition extraction:** ```javascript // Extract indicator version (v5, v6, etc.) const indicatorMatch = body.match(/IND:([a-z0-9]+)/i); const indicatorVersion = indicatorMatch ? indicatorMatch[1] : 'v5'; // Default to v5 for old signals return { rawMessage: body, symbol, direction, timeframe, // Context fields atr, adx, rsi, volumeRatio, pricePosition, // NEW: Indicator version indicatorVersion }; ``` **Then update the HTTP request nodes to include it:** **Check Risk Request:** ```json { "symbol": "{{ $('Parse Signal Enhanced').item.json.symbol }}", "direction": "{{ $('Parse Signal Enhanced').item.json.direction }}", "timeframe": "{{ $('Parse Signal Enhanced').item.json.timeframe }}", "atr": {{ $('Parse Signal Enhanced').item.json.atr }}, "adx": {{ $('Parse Signal Enhanced').item.json.adx }}, "rsi": {{ $('Parse Signal Enhanced').item.json.rsi }}, "volumeRatio": {{ $('Parse Signal Enhanced').item.json.volumeRatio }}, "pricePosition": {{ $('Parse Signal Enhanced').item.json.pricePosition }}, "indicatorVersion": "{{ $('Parse Signal Enhanced').item.json.indicatorVersion }}" } ``` **Execute Trade Request:** (same addition) ### 4. API Endpoints Update (REQUIRED) **Files to update:** - `app/api/trading/check-risk/route.ts` - `app/api/trading/execute/route.ts` **Add to request body interface:** ```typescript interface RequestBody { symbol: string direction: 'long' | 'short' timeframe?: string atr?: number adx?: number rsi?: number volumeRatio?: number pricePosition?: number indicatorVersion?: string // NEW } ``` **Pass to database functions:** ```typescript await createTrade({ // ... existing params ... indicatorVersion: body.indicatorVersion || 'v5' }) await createBlockedSignal({ // ... existing params ... indicatorVersion: body.indicatorVersion || 'v5' }) ``` ## Database Migration Run this to apply schema changes: ```bash # Generate Prisma client with new fields npx prisma generate # Push schema to database npx prisma db push # Rebuild Docker container docker compose build trading-bot docker compose up -d trading-bot ``` ## Analysis Queries ### Compare v5 vs v6 Performance ```sql -- Executed trades by indicator version SELECT indicatorVersion, COUNT(*) as trades, ROUND(AVG(realizedPnL)::numeric, 2) as avg_pnl, ROUND(SUM(realizedPnL)::numeric, 2) as total_pnl, ROUND(100.0 * SUM(CASE WHEN realizedPnL > 0 THEN 1 ELSE 0 END) / COUNT(*)::numeric, 1) as win_rate FROM "Trade" WHERE exitReason IS NOT NULL AND indicatorVersion IS NOT NULL GROUP BY indicatorVersion ORDER BY indicatorVersion; ``` ### Blocked signals by version ```sql -- Blocked signals by indicator version SELECT indicatorVersion, COUNT(*) as blocked_count, ROUND(AVG(signalQualityScore)::numeric, 1) as avg_score, blockReason, COUNT(*) as count_per_reason FROM "BlockedSignal" WHERE indicatorVersion IS NOT NULL GROUP BY indicatorVersion, blockReason ORDER BY indicatorVersion, count_per_reason DESC; ``` ### v6 effectiveness check ```sql -- Did v6 reduce blocked signals at range extremes? SELECT indicatorVersion, CASE WHEN pricePosition < 15 OR pricePosition > 85 THEN 'Range Extreme' ELSE 'Normal Range' END as position_type, COUNT(*) as count FROM "BlockedSignal" WHERE indicatorVersion IN ('v5', 'v6') AND pricePosition IS NOT NULL GROUP BY indicatorVersion, position_type ORDER BY indicatorVersion, position_type; ``` ## Expected Results **v5 signals:** - Should show more blocked signals at range extremes (< 15% or > 85%) - Higher percentage of signals blocked for QUALITY_SCORE_TOO_LOW **v6 signals:** - Should show fewer/zero blocked signals at range extremes (filtered in Pine Script) - Higher average quality scores - Most signals should score 70+ ## Rollback Plan If v6 performs worse: 1. **Revert Pine Script:** Change `indicatorVer = "v5"` in v6 script 2. **Or use v5 script:** Just switch back to `moneyline_v5_final.pinescript` 3. **Database keeps working:** Old signals tagged as v5, new as v6 4. **Analysis remains valid:** Can compare both versions historically ## Testing Checklist - [ ] Database schema updated (`npx prisma db push`) - [ ] Prisma client regenerated (`npx prisma generate`) - [ ] Docker container rebuilt - [ ] n8n workflow updated (Parse Signal Enhanced node) - [ ] n8n HTTP requests updated (Check Risk + Execute Trade) - [ ] v6 Pine Script deployed to TradingView - [ ] Test signal fires and `indicatorVersion` appears in database - [ ] SQL queries return v6 data correctly ## Notes - **Backward compatible:** Old signals without version default to 'v5' - **No data loss:** Existing trades remain unchanged - **Immediate effect:** Once n8n updated, all new signals tagged with version - **Analysis ready:** Can compare v5 vs v6 after 10+ signals each --- **Status:** Database and Pine Script updated. n8n workflow update REQUIRED before v6 tracking works.