Files
trading_bot_v4/lib/trading/market-data-cache.ts
mindesbunister d637aac2d7 feat: Deploy HA auto-failover with database promotion
- Enhanced DNS failover monitor on secondary (72.62.39.24)
- Auto-promotes database: pg_ctl promote on failover
- Creates DEMOTED flag on primary via SSH (split-brain protection)
- Telegram notifications with database promotion status
- Startup safety script ready (integration pending)
- 90-second automatic recovery vs 10-30 min manual
- Zero-cost 95% enterprise HA benefit

Status: DEPLOYED and MONITORING (14:52 CET)
Next: Controlled failover test during maintenance
2025-12-12 15:54:03 +01:00

121 lines
3.3 KiB
TypeScript

import { logger } from '../utils/logger'
/**
* Market Data Cache Service
*
* Purpose: Stores real-time TradingView metrics for manual trade validation.
* Data flows: TradingView → /api/trading/market-data → Cache → Re-entry checks
*
* Cache expiry: 5 minutes (configurable)
*/
export interface MarketMetrics {
symbol: string // "SOL-PERP", "ETH-PERP", "BTC-PERP"
atr: number // Average True Range (volatility %)
adx: number // Average Directional Index (trend strength)
rsi: number // Relative Strength Index (momentum)
volumeRatio: number // Current volume / average volume
pricePosition: number // Position in recent range (0-100%)
maGap?: number // MA50-MA200 gap percentage (v9+)
currentPrice: number // Latest close price
timestamp: number // Unix timestamp (ms)
timeframe: string // "5" for 5min, "60" for 1h, etc.
}
class MarketDataCache {
private cache: Map<string, MarketMetrics> = new Map()
private readonly MAX_AGE_MS = 5 * 60 * 1000 // 5 minutes
/**
* Store fresh market data from TradingView
*/
set(symbol: string, metrics: MarketMetrics): void {
this.cache.set(symbol, metrics)
logger.log(
`📊 Cached market data for ${symbol}: ` +
`ADX=${metrics.adx.toFixed(1)} ` +
`ATR=${metrics.atr.toFixed(2)}% ` +
`RSI=${metrics.rsi.toFixed(1)} ` +
`Vol=${metrics.volumeRatio.toFixed(2)}x`
)
}
/**
* Retrieve cached data if still fresh (<5min old)
* Returns null if stale or missing
*/
get(symbol: string): MarketMetrics | null {
const data = this.cache.get(symbol)
if (!data) {
console.log(`⚠️ No cached data for ${symbol}`)
return null
}
const ageSeconds = Math.round((Date.now() - data.timestamp) / 1000)
if (Date.now() - data.timestamp > this.MAX_AGE_MS) {
console.log(`⏰ Cached data for ${symbol} is stale (${ageSeconds}s old, max 300s)`)
return null
}
console.log(`✅ Using fresh TradingView data for ${symbol} (${ageSeconds}s old)`)
return data
}
/**
* Check if fresh data exists without retrieving it
*/
has(symbol: string): boolean {
const data = this.cache.get(symbol)
if (!data) return false
return Date.now() - data.timestamp <= this.MAX_AGE_MS
}
/**
* Get all cached symbols with fresh data
*/
getAvailableSymbols(): string[] {
const now = Date.now()
const freshSymbols: string[] = []
for (const [symbol, data] of this.cache.entries()) {
if (now - data.timestamp <= this.MAX_AGE_MS) {
freshSymbols.push(symbol)
}
}
return freshSymbols
}
/**
* Get age of cached data in seconds (for debugging)
*/
getDataAge(symbol: string): number | null {
const data = this.cache.get(symbol)
if (!data) return null
return Math.round((Date.now() - data.timestamp) / 1000)
}
/**
* Clear all cached data (for testing)
*/
clear(): void {
this.cache.clear()
logger.log('🗑️ Market data cache cleared')
}
}
// Singleton instance
let marketDataCache: MarketDataCache | null = null
export function getMarketDataCache(): MarketDataCache {
if (!marketDataCache) {
marketDataCache = new MarketDataCache()
logger.log('🔧 Initialized Market Data Cache (5min expiry)')
}
return marketDataCache
}