feat: Extend 1-minute data retention from 4 weeks to 1 year

- Updated lib/maintenance/data-cleanup.ts retention period: 28 days → 365 days
- Storage requirements validated: 251 MB/year (negligible)
- Rationale: 13× more historical data for better pattern analysis
- Benefits: 260-390 blocked signals/year vs 20-30/month
- Cleanup cutoff: Now Dec 2, 2024 (vs Nov 4, 2025 previously)
- Deployment verified: Container restarted, cleanup scheduled for 3 AM daily
This commit is contained in:
mindesbunister
2025-12-02 11:55:36 +01:00
parent 4239c99057
commit 5773d7d36d
11 changed files with 1191 additions and 7 deletions

View File

@@ -6,11 +6,17 @@
* multi-timeframe analysis.
*
* Features:
* - Price tracking at 1min, 5min, 15min, 30min intervals
* - Price tracking at 1min, 5min, 15min, 30min, 1hr, 2hr, 4hr, 8hr intervals
* - TP1/TP2/SL hit detection using ATR-based targets
* - Max favorable/adverse excursion tracking
* - Automatic analysis completion after 30 minutes
* - Automatic analysis completion after 8 hours or TP/SL hit
* - Background job runs every 5 minutes
*
* EXTENDED TRACKING (Dec 2, 2025):
* - Previously tracked for 30 minutes only (missed slow developers)
* - Now tracks for 8 hours to capture low ADX signals that take 4+ hours
* - User directive: "30 minutes...simply not long enough to know whats going to happen"
* - Purpose: Accurate win rate data for quality 80-89 signals
*/
import { getPrismaClient } from '../database/trades'
@@ -29,6 +35,10 @@ interface BlockedSignalWithTracking {
priceAfter5Min: number | null
priceAfter15Min: number | null
priceAfter30Min: number | null
priceAfter1Hr: number | null
priceAfter2Hr: number | null
priceAfter4Hr: number | null
priceAfter8Hr: number | null
wouldHitTP1: boolean | null
wouldHitTP2: boolean | null
wouldHitSL: boolean | null
@@ -96,7 +106,7 @@ export class BlockedSignalTracker {
return
}
// Get all incomplete signals from last 24 hours
// Get all incomplete signals from last 48 hours (extended for 8hr tracking)
// Track BOTH quality-blocked AND data collection signals
const signals = await this.prisma.blockedSignal.findMany({
where: {
@@ -105,7 +115,7 @@ export class BlockedSignalTracker {
},
analysisComplete: false,
createdAt: {
gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours
gte: new Date(Date.now() - 48 * 60 * 60 * 1000) // Last 48 hours (8hr tracking + buffer)
}
},
orderBy: { createdAt: 'asc' }
@@ -189,8 +199,41 @@ export class BlockedSignalTracker {
if (elapsedMinutes >= 30 && !signal.priceAfter30Min) {
updates.priceAfter30Min = currentPrice
console.log(` 📍 ${signal.symbol} ${signal.direction} @ 30min: $${currentPrice.toFixed(2)} (${profitPercent.toFixed(2)}%)`)
}
// EXTENDED TRACKING (Dec 2, 2025): Track up to 8 hours for slow developers
if (elapsedMinutes >= 60 && !signal.priceAfter1Hr) {
updates.priceAfter1Hr = currentPrice
console.log(` 📍 ${signal.symbol} ${signal.direction} @ 1hr: $${currentPrice.toFixed(2)} (${profitPercent.toFixed(2)}%)`)
}
if (elapsedMinutes >= 120 && !signal.priceAfter2Hr) {
updates.priceAfter2Hr = currentPrice
console.log(` 📍 ${signal.symbol} ${signal.direction} @ 2hr: $${currentPrice.toFixed(2)} (${profitPercent.toFixed(2)}%)`)
}
if (elapsedMinutes >= 240 && !signal.priceAfter4Hr) {
updates.priceAfter4Hr = currentPrice
console.log(` 📍 ${signal.symbol} ${signal.direction} @ 4hr: $${currentPrice.toFixed(2)} (${profitPercent.toFixed(2)}%)`)
}
if (elapsedMinutes >= 480 && !signal.priceAfter8Hr) {
updates.priceAfter8Hr = currentPrice
console.log(` 📍 ${signal.symbol} ${signal.direction} @ 8hr: $${currentPrice.toFixed(2)} (${profitPercent.toFixed(2)}%)`)
}
// Mark complete after 8 hours OR if TP/SL already hit
if (elapsedMinutes >= 480 && !signal.analysisComplete) {
updates.analysisComplete = true
console.log(`${signal.symbol} ${signal.direction} @ 30min: $${currentPrice.toFixed(2)} (${profitPercent.toFixed(2)}%) - COMPLETE`)
console.log(`${signal.symbol} ${signal.direction} @ 8hr: TRACKING COMPLETE`)
}
// Early completion if TP1/TP2/SL hit (no need to wait full 8 hours)
if (!signal.analysisComplete && (signal.wouldHitTP1 || signal.wouldHitTP2 || signal.wouldHitSL)) {
updates.analysisComplete = true
const hitReason = signal.wouldHitTP1 ? 'TP1' : signal.wouldHitTP2 ? 'TP2' : 'SL'
console.log(`${signal.symbol} ${signal.direction}: ${hitReason} hit at ${profitPercent.toFixed(2)}% - TRACKING COMPLETE`)
}
// Update max favorable/adverse excursion