docs: Add Common Pitfall #55 - BlockedSignalTracker Pyth cache bug
- Documented Nov 20, 2025 fix for multi-timeframe data collection - Tracker was using Pyth cache (empty) instead of Drift oracle prices - Result: 30+ hours with no price tracking, all priceAfter* fields NULL - Fix: Changed to initializeDriftService() + getOraclePrice() - Now working: Price tracking every 5min, analysisComplete transitions, TP/SL detection - Impact: Multi-timeframe data collection now operational for Phase 1 analysis
This commit is contained in:
86
.github/copilot-instructions.md
vendored
86
.github/copilot-instructions.md
vendored
@@ -2798,6 +2798,92 @@ trade.realizedPnL += actualRealizedPnL // NOT: result.realizedPnL from SDK
|
||||
* `config/trading.ts` - Bot reads `MIN_SIGNAL_QUALITY_SCORE` (correct name)
|
||||
* `.env` file - Contains `MIN_SIGNAL_QUALITY_SCORE=60` (correct name)
|
||||
- **Fix:**
|
||||
- **Lesson:** When creating settings UI, always use EXACT ENV variable names from actual bot code. Mismatched names cause silent failures where user actions have no effect. Test settings changes end-to-end (UI → .env → bot behavior).
|
||||
|
||||
55. **BlockedSignalTracker using Pyth cache instead of Drift oracle (CRITICAL - Fixed Nov 20, 2025):**
|
||||
- **Symptom:** All `priceAfter1Min/5Min/15Min/30Min` fields staying NULL, no price tracking happening
|
||||
- **Root Cause:** BlockedSignalTracker was calling `getPythPriceMonitor().getCachedPrice()` which didn't have SOL-PERP prices
|
||||
- **Real incident (Nov 20):**
|
||||
* Multi-timeframe data collection running for 30+ hours
|
||||
* 4 signals saved to BlockedSignal table (15min: 2, 60min: 2)
|
||||
* Tracker running every 5 minutes: "📊 Tracking 4 blocked signals..."
|
||||
* But logs showed: "⚠️ No price available for SOL-PERP, skipping" (repeated 100+ times)
|
||||
* All priceAfter* fields remained NULL
|
||||
* No analysisComplete transitions
|
||||
* No wouldHitTP1/TP2/SL detection
|
||||
- **Why Pyth cache empty:**
|
||||
* Pyth price monitor used for Position Manager real-time monitoring
|
||||
* BlockedSignalTracker runs every 5 minutes (not real-time)
|
||||
* Cache may not have recent prices when tracker runs
|
||||
* Wrong data source for background job
|
||||
- **Impact:** Multi-timeframe data collection completely non-functional for Phase 1 analysis
|
||||
- **Fix (Nov 20, 2025):**
|
||||
```typescript
|
||||
// BEFORE (BROKEN - lib/analysis/blocked-signal-tracker.ts):
|
||||
import { getPythPriceMonitor } from '../pyth/price-monitor'
|
||||
|
||||
private async trackSignal(signal: BlockedSignalWithTracking): Promise<void> {
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
const latestPrice = priceMonitor.getCachedPrice(signal.symbol)
|
||||
|
||||
if (!latestPrice || !latestPrice.price) {
|
||||
console.log(`⚠️ No price available for ${signal.symbol}, skipping`)
|
||||
return
|
||||
}
|
||||
const currentPrice = latestPrice.price
|
||||
// ... rest of tracking
|
||||
}
|
||||
|
||||
// AFTER (FIXED):
|
||||
import { initializeDriftService } from '../drift/client'
|
||||
import { SUPPORTED_MARKETS } from '../../config/trading'
|
||||
|
||||
private async trackPrices(): Promise<void> {
|
||||
// Initialize Drift service ONCE before processing all signals
|
||||
const driftService = await initializeDriftService()
|
||||
if (!driftService) {
|
||||
console.log('⚠️ Drift service not available, skipping price tracking')
|
||||
return
|
||||
}
|
||||
// ... process signals
|
||||
}
|
||||
|
||||
private async trackSignal(signal: BlockedSignalWithTracking): Promise<void> {
|
||||
// Get current price from Drift oracle (always available)
|
||||
const driftService = await initializeDriftService()
|
||||
const marketConfig = SUPPORTED_MARKETS[signal.symbol]
|
||||
|
||||
if (!marketConfig) {
|
||||
console.log(`⚠️ No market config for ${signal.symbol}, skipping`)
|
||||
return
|
||||
}
|
||||
|
||||
const currentPrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex)
|
||||
const entryPrice = Number(signal.entryPrice)
|
||||
|
||||
if (entryPrice === 0) {
|
||||
console.log(`⚠️ Entry price is 0 for ${signal.symbol}, skipping`)
|
||||
return
|
||||
}
|
||||
// ... rest of tracking with actual prices
|
||||
}
|
||||
```
|
||||
- **Behavior now:**
|
||||
* Tracker gets fresh prices from Drift oracle every run
|
||||
* Logs show: "📍 SOL-PERP long @ 1min: $142.34 (4.10%)"
|
||||
* Database updates: priceAfter1Min, priceAfter5Min, priceAfter15Min, priceAfter30Min all populate
|
||||
* analysisComplete transitions to true after 30 minutes
|
||||
* wouldHitTP1/TP2/SL detection working based on ATR targets
|
||||
- **Verification (Nov 20):**
|
||||
* 2 signals now complete with full price tracking data
|
||||
* 15min signal: wouldHitTP1=true, wouldHitTP2=true (both targets hit)
|
||||
* 60min signal: wouldHitTP1=true (TP1 hit, TP2 pending)
|
||||
* analysisComplete=true for both after 30min window
|
||||
- **Files changed:**
|
||||
* `lib/analysis/blocked-signal-tracker.ts` - Changed price source + added Drift init
|
||||
- **Commits:** 6b00303 "fix: BlockedSignalTracker now uses Drift oracle prices"
|
||||
- **Impact:** Multi-timeframe data collection now operational for Phase 1 analysis (50+ signals per timeframe target)
|
||||
- **Lesson:** Background jobs should use Drift oracle prices (always available) not Pyth cache (real-time only). Always initialize external services before calling their methods. Verify background jobs are actually working by checking database state, not just logs.
|
||||
```typescript
|
||||
// In app/api/settings/route.ts (lines ~150, ~270)
|
||||
// BEFORE (BROKEN):
|
||||
|
||||
Reference in New Issue
Block a user