Your Name
feat: UI improvements and error suppression - Enhanced dashboard and market pages with improved header buttons, logo, and currency symbol display - Stopped animated ticker - Removed pie chart legends - Added error suppressor for external service errors (SSE, Permissions-Policy warnings) - Improved header button prominence and icon appearance - Enhanced logo with glow effects and better design - Fixed currency symbol visibility in market tables
8b7b267
| /** | |
| * Adaptive Market Regime Detection System | |
| * Identifies market conditions and adapts strategies accordingly | |
| * Regimes: Trending, Ranging, Volatile, Calm, Bullish, Bearish | |
| */ | |
| /** | |
| * Market regimes | |
| */ | |
| export const MARKET_REGIMES = { | |
| TRENDING_BULLISH: 'trending-bullish', | |
| TRENDING_BEARISH: 'trending-bearish', | |
| RANGING: 'ranging', | |
| VOLATILE_BULLISH: 'volatile-bullish', | |
| VOLATILE_BEARISH: 'volatile-bearish', | |
| CALM: 'calm', | |
| BREAKDOWN: 'breakdown', | |
| BREAKOUT: 'breakout', | |
| ACCUMULATION: 'accumulation', | |
| DISTRIBUTION: 'distribution' | |
| }; | |
| /** | |
| * Regime characteristics | |
| */ | |
| const REGIME_CHARACTERISTICS = { | |
| [MARKET_REGIMES.TRENDING_BULLISH]: { | |
| name: 'Trending Bullish', | |
| description: 'Strong upward trend with consistent higher highs and higher lows', | |
| bestStrategies: ['ict-market-structure', 'momentum-divergence-hunter', 'supply-demand-zones'], | |
| riskLevel: 'medium', | |
| profitPotential: 'high' | |
| }, | |
| [MARKET_REGIMES.TRENDING_BEARISH]: { | |
| name: 'Trending Bearish', | |
| description: 'Strong downward trend with consistent lower highs and lower lows', | |
| bestStrategies: ['ict-market-structure', 'liquidity-sweep-reversal'], | |
| riskLevel: 'high', | |
| profitPotential: 'high' | |
| }, | |
| [MARKET_REGIMES.RANGING]: { | |
| name: 'Ranging', | |
| description: 'Sideways movement between support and resistance', | |
| bestStrategies: ['supply-demand-zones', 'liquidity-sweep-reversal', 'mean-reversion-momentum'], | |
| riskLevel: 'low', | |
| profitPotential: 'medium' | |
| }, | |
| [MARKET_REGIMES.VOLATILE_BULLISH]: { | |
| name: 'Volatile Bullish', | |
| description: 'Upward movement with high volatility and large swings', | |
| bestStrategies: ['volatility-breakout-pro', 'fair-value-gap-strategy'], | |
| riskLevel: 'very-high', | |
| profitPotential: 'very-high' | |
| }, | |
| [MARKET_REGIMES.VOLATILE_BEARISH]: { | |
| name: 'Volatile Bearish', | |
| description: 'Downward movement with high volatility', | |
| bestStrategies: ['volatility-breakout-pro', 'liquidity-sweep-reversal'], | |
| riskLevel: 'very-high', | |
| profitPotential: 'very-high' | |
| }, | |
| [MARKET_REGIMES.CALM]: { | |
| name: 'Calm', | |
| description: 'Low volatility with minimal price movement', | |
| bestStrategies: ['ranging', 'supply-demand-zones'], | |
| riskLevel: 'very-low', | |
| profitPotential: 'low' | |
| }, | |
| [MARKET_REGIMES.BREAKOUT]: { | |
| name: 'Breakout', | |
| description: 'Price breaking above resistance', | |
| bestStrategies: ['volatility-breakout-pro', 'ict-market-structure', 'momentum-divergence-hunter'], | |
| riskLevel: 'high', | |
| profitPotential: 'very-high' | |
| }, | |
| [MARKET_REGIMES.BREAKDOWN]: { | |
| name: 'Breakdown', | |
| description: 'Price breaking below support', | |
| bestStrategies: ['liquidity-sweep-reversal', 'ict-market-structure'], | |
| riskLevel: 'high', | |
| profitPotential: 'high' | |
| }, | |
| [MARKET_REGIMES.ACCUMULATION]: { | |
| name: 'Accumulation', | |
| description: 'Smart money accumulating positions', | |
| bestStrategies: ['wyckoff-accumulation', 'supply-demand-zones', 'market-maker-profile'], | |
| riskLevel: 'medium', | |
| profitPotential: 'very-high' | |
| }, | |
| [MARKET_REGIMES.DISTRIBUTION]: { | |
| name: 'Distribution', | |
| description: 'Smart money distributing positions', | |
| bestStrategies: ['wyckoff-accumulation', 'liquidity-sweep-reversal'], | |
| riskLevel: 'high', | |
| profitPotential: 'medium' | |
| } | |
| }; | |
| /** | |
| * Adaptive Regime Detector | |
| */ | |
| export class AdaptiveRegimeDetector { | |
| constructor(config = {}) { | |
| this.lookbackPeriod = config.lookbackPeriod || 100; | |
| this.volatilityPeriod = config.volatilityPeriod || 20; | |
| this.trendPeriod = config.trendPeriod || 50; | |
| this.currentRegime = null; | |
| this.regimeHistory = []; | |
| this.confidence = 0; | |
| } | |
| /** | |
| * Detect current market regime | |
| * @param {Array<Object>} ohlcvData - OHLCV data | |
| * @returns {Object} Regime detection results | |
| */ | |
| detectRegime(ohlcvData) { | |
| if (!ohlcvData || ohlcvData.length < this.lookbackPeriod) { | |
| return { | |
| regime: MARKET_REGIMES.CALM, | |
| confidence: 0, | |
| error: 'Insufficient data' | |
| }; | |
| } | |
| const metrics = this.calculateMetrics(ohlcvData); | |
| const regime = this.classifyRegime(metrics); | |
| const confidence = this.calculateConfidence(metrics, regime); | |
| // Update history | |
| this.currentRegime = regime; | |
| this.confidence = confidence; | |
| this.regimeHistory.push({ | |
| regime, | |
| confidence, | |
| timestamp: Date.now(), | |
| metrics | |
| }); | |
| // Keep only recent history | |
| if (this.regimeHistory.length > 50) { | |
| this.regimeHistory.shift(); | |
| } | |
| return { | |
| regime, | |
| confidence, | |
| characteristics: REGIME_CHARACTERISTICS[regime], | |
| metrics, | |
| transition: this.detectRegimeTransition(), | |
| timestamp: Date.now() | |
| }; | |
| } | |
| /** | |
| * Calculate market metrics | |
| * @param {Array<Object>} ohlcvData - OHLCV data | |
| * @returns {Object} Metrics | |
| */ | |
| calculateMetrics(ohlcvData) { | |
| const closes = ohlcvData.map(c => c.close); | |
| const highs = ohlcvData.map(c => c.high); | |
| const lows = ohlcvData.map(c => c.low); | |
| const volumes = ohlcvData.map(c => c.volume); | |
| return { | |
| volatility: this.calculateVolatility(closes), | |
| trend: this.calculateTrend(closes), | |
| trendStrength: this.calculateTrendStrength(highs, lows, closes), | |
| momentum: this.calculateMomentum(closes), | |
| volume: this.analyzeVolume(volumes), | |
| range: this.calculateRange(highs, lows, closes), | |
| structure: this.analyzeMarketStructure(highs, lows), | |
| phase: this.detectWyckoffPhase(ohlcvData) | |
| }; | |
| } | |
| /** | |
| * Calculate volatility (ATR-based) | |
| * @param {Array<number>} closes - Close prices | |
| * @returns {number} Volatility percentage | |
| */ | |
| calculateVolatility(closes) { | |
| const period = Math.min(this.volatilityPeriod, closes.length - 1); | |
| const returns = []; | |
| for (let i = 1; i <= period; i++) { | |
| const ret = (closes[closes.length - i] - closes[closes.length - i - 1]) / closes[closes.length - i - 1]; | |
| returns.push(ret); | |
| } | |
| const mean = returns.reduce((a, b) => a + b, 0) / returns.length; | |
| const variance = returns.reduce((sum, r) => sum + Math.pow(r - mean, 2), 0) / returns.length; | |
| const stdDev = Math.sqrt(variance); | |
| return stdDev * 100; // Convert to percentage | |
| } | |
| /** | |
| * Calculate trend direction | |
| * @param {Array<number>} closes - Close prices | |
| * @returns {Object} Trend info | |
| */ | |
| calculateTrend(closes) { | |
| const period = Math.min(this.trendPeriod, closes.length); | |
| const recentPrices = closes.slice(-period); | |
| // Linear regression | |
| const { slope, r2 } = this.linearRegression(recentPrices); | |
| let direction = 'neutral'; | |
| if (slope > 0.001) direction = 'up'; | |
| else if (slope < -0.001) direction = 'down'; | |
| return { | |
| direction, | |
| slope, | |
| strength: r2 * 100 // R² as percentage | |
| }; | |
| } | |
| /** | |
| * Linear regression | |
| * @param {Array<number>} values - Values | |
| * @returns {Object} Slope and R² | |
| */ | |
| linearRegression(values) { | |
| const n = values.length; | |
| const indices = Array.from({ length: n }, (_, i) => i); | |
| const sumX = indices.reduce((a, b) => a + b, 0); | |
| const sumY = values.reduce((a, b) => a + b, 0); | |
| const sumXY = indices.reduce((sum, x, i) => sum + x * values[i], 0); | |
| const sumX2 = indices.reduce((sum, x) => sum + x * x, 0); | |
| const sumY2 = values.reduce((sum, y) => sum + y * y, 0); | |
| const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); | |
| const intercept = (sumY - slope * sumX) / n; | |
| // Calculate R² | |
| const meanY = sumY / n; | |
| const ssTotal = values.reduce((sum, y) => sum + Math.pow(y - meanY, 2), 0); | |
| const ssResidual = values.reduce((sum, y, i) => { | |
| const predicted = slope * i + intercept; | |
| return sum + Math.pow(y - predicted, 2); | |
| }, 0); | |
| const r2 = 1 - (ssResidual / ssTotal); | |
| return { slope, intercept, r2: Math.max(0, r2) }; | |
| } | |
| /** | |
| * Calculate trend strength (ADX-like) | |
| * @param {Array<number>} highs - High prices | |
| * @param {Array<number>} lows - Low prices | |
| * @param {Array<number>} closes - Close prices | |
| * @returns {number} Trend strength (0-100) | |
| */ | |
| calculateTrendStrength(highs, lows, closes) { | |
| const period = Math.min(14, closes.length - 1); | |
| let plusDM = 0; | |
| let minusDM = 0; | |
| for (let i = closes.length - period; i < closes.length; i++) { | |
| const highDiff = highs[i] - highs[i - 1]; | |
| const lowDiff = lows[i - 1] - lows[i]; | |
| if (highDiff > lowDiff && highDiff > 0) { | |
| plusDM += highDiff; | |
| } else if (lowDiff > highDiff && lowDiff > 0) { | |
| minusDM += lowDiff; | |
| } | |
| } | |
| const totalDM = plusDM + minusDM; | |
| if (totalDM === 0) return 0; | |
| const dx = Math.abs(plusDM - minusDM) / totalDM * 100; | |
| return Math.min(100, dx); | |
| } | |
| /** | |
| * Calculate momentum | |
| * @param {Array<number>} closes - Close prices | |
| * @returns {Object} Momentum info | |
| */ | |
| calculateMomentum(closes) { | |
| const period = 10; | |
| const current = closes[closes.length - 1]; | |
| const past = closes[closes.length - period]; | |
| const momentum = ((current - past) / past) * 100; | |
| let state = 'neutral'; | |
| if (momentum > 2) state = 'strong-positive'; | |
| else if (momentum > 0.5) state = 'positive'; | |
| else if (momentum < -2) state = 'strong-negative'; | |
| else if (momentum < -0.5) state = 'negative'; | |
| return { value: momentum, state }; | |
| } | |
| /** | |
| * Analyze volume | |
| * @param {Array<number>} volumes - Volume data | |
| * @returns {Object} Volume analysis | |
| */ | |
| analyzeVolume(volumes) { | |
| const period = 20; | |
| const recentVolumes = volumes.slice(-period); | |
| const avgVolume = recentVolumes.reduce((a, b) => a + b, 0) / recentVolumes.length; | |
| const currentVolume = volumes[volumes.length - 1]; | |
| const ratio = currentVolume / avgVolume; | |
| let state = 'normal'; | |
| if (ratio > 2) state = 'very-high'; | |
| else if (ratio > 1.5) state = 'high'; | |
| else if (ratio < 0.5) state = 'very-low'; | |
| else if (ratio < 0.75) state = 'low'; | |
| return { | |
| current: currentVolume, | |
| average: avgVolume, | |
| ratio, | |
| state | |
| }; | |
| } | |
| /** | |
| * Calculate price range | |
| * @param {Array<number>} highs - High prices | |
| * @param {Array<number>} lows - Low prices | |
| * @param {Array<number>} closes - Close prices | |
| * @returns {Object} Range info | |
| */ | |
| calculateRange(highs, lows, closes) { | |
| const period = 20; | |
| const recentHighs = highs.slice(-period); | |
| const recentLows = lows.slice(-period); | |
| const highestHigh = Math.max(...recentHighs); | |
| const lowestLow = Math.min(...recentLows); | |
| const currentPrice = closes[closes.length - 1]; | |
| const rangeSize = highestHigh - lowestLow; | |
| const rangePercent = (rangeSize / currentPrice) * 100; | |
| const position = (currentPrice - lowestLow) / rangeSize; | |
| let state = 'ranging'; | |
| if (rangePercent < 3) state = 'tight'; | |
| else if (rangePercent > 10) state = 'wide'; | |
| return { | |
| high: highestHigh, | |
| low: lowestLow, | |
| size: rangeSize, | |
| percent: rangePercent, | |
| position, | |
| state | |
| }; | |
| } | |
| /** | |
| * Analyze market structure | |
| * @param {Array<number>} highs - High prices | |
| * @param {Array<number>} lows - Low prices | |
| * @returns {Object} Structure analysis | |
| */ | |
| analyzeMarketStructure(highs, lows) { | |
| const swingPeriod = 5; | |
| const recentHighs = highs.slice(-20); | |
| const recentLows = lows.slice(-20); | |
| // Find swing points | |
| const swingHighIndices = []; | |
| const swingLowIndices = []; | |
| for (let i = swingPeriod; i < recentHighs.length - swingPeriod; i++) { | |
| let isSwingHigh = true; | |
| let isSwingLow = true; | |
| for (let j = i - swingPeriod; j <= i + swingPeriod; j++) { | |
| if (j !== i) { | |
| if (recentHighs[j] >= recentHighs[i]) isSwingHigh = false; | |
| if (recentLows[j] <= recentLows[i]) isSwingLow = false; | |
| } | |
| } | |
| if (isSwingHigh) swingHighIndices.push(i); | |
| if (isSwingLow) swingLowIndices.push(i); | |
| } | |
| // Analyze structure | |
| let structure = 'neutral'; | |
| if (swingHighIndices.length >= 2 && swingLowIndices.length >= 2) { | |
| const lastTwoHighs = swingHighIndices.slice(-2).map(i => recentHighs[i]); | |
| const lastTwoLows = swingLowIndices.slice(-2).map(i => recentLows[i]); | |
| const higherHighs = lastTwoHighs[1] > lastTwoHighs[0]; | |
| const higherLows = lastTwoLows[1] > lastTwoLows[0]; | |
| const lowerHighs = lastTwoHighs[1] < lastTwoHighs[0]; | |
| const lowerLows = lastTwoLows[1] < lastTwoLows[0]; | |
| if (higherHighs && higherLows) structure = 'bullish'; | |
| else if (lowerHighs && lowerLows) structure = 'bearish'; | |
| else if (higherHighs && lowerLows) structure = 'distribution'; | |
| else if (lowerHighs && higherLows) structure = 'accumulation'; | |
| } | |
| return { | |
| structure, | |
| swingHighs: swingHighIndices.length, | |
| swingLows: swingLowIndices.length | |
| }; | |
| } | |
| /** | |
| * Detect Wyckoff phase | |
| * @param {Array<Object>} ohlcvData - OHLCV data | |
| * @returns {string} Wyckoff phase | |
| */ | |
| detectWyckoffPhase(ohlcvData) { | |
| const volumes = ohlcvData.map(c => c.volume); | |
| const closes = ohlcvData.map(c => c.close); | |
| const highs = ohlcvData.map(c => c.high); | |
| const lows = ohlcvData.map(c => c.low); | |
| const priceRange = Math.max(...highs.slice(-20)) - Math.min(...lows.slice(-20)); | |
| const priceRangePercent = (priceRange / closes[closes.length - 1]) * 100; | |
| const avgVolume = volumes.slice(-20).reduce((a, b) => a + b, 0) / 20; | |
| const recentVolume = volumes.slice(-5).reduce((a, b) => a + b, 0) / 5; | |
| const volumeRatio = recentVolume / avgVolume; | |
| const priceChange = ((closes[closes.length - 1] - closes[closes.length - 20]) / closes[closes.length - 20]) * 100; | |
| // Accumulation: Low range + High volume + Flat price | |
| if (priceRangePercent < 5 && volumeRatio > 1.2 && Math.abs(priceChange) < 3) { | |
| return 'accumulation'; | |
| } | |
| // Distribution: Low range + High volume + Flat/Declining price | |
| if (priceRangePercent < 5 && volumeRatio > 1.2 && priceChange < 0) { | |
| return 'distribution'; | |
| } | |
| // Markup: Rising price + Increasing volume | |
| if (priceChange > 5 && volumeRatio > 1) { | |
| return 'markup'; | |
| } | |
| // Markdown: Falling price + Increasing volume | |
| if (priceChange < -5 && volumeRatio > 1) { | |
| return 'markdown'; | |
| } | |
| return 'neutral'; | |
| } | |
| /** | |
| * Classify regime based on metrics | |
| * @param {Object} metrics - Market metrics | |
| * @returns {string} Market regime | |
| */ | |
| classifyRegime(metrics) { | |
| const { volatility, trend, trendStrength, momentum, volume, range, structure, phase } = metrics; | |
| // Wyckoff phases take priority | |
| if (phase === 'accumulation') { | |
| return MARKET_REGIMES.ACCUMULATION; | |
| } | |
| if (phase === 'distribution') { | |
| return MARKET_REGIMES.DISTRIBUTION; | |
| } | |
| // Volatile regimes | |
| if (volatility > 5) { | |
| if (trend.direction === 'up' || momentum.state.includes('positive')) { | |
| return MARKET_REGIMES.VOLATILE_BULLISH; | |
| } | |
| if (trend.direction === 'down' || momentum.state.includes('negative')) { | |
| return MARKET_REGIMES.VOLATILE_BEARISH; | |
| } | |
| } | |
| // Breakout/Breakdown | |
| if (range.position > 0.95 && volume.state === 'high' && momentum.state.includes('positive')) { | |
| return MARKET_REGIMES.BREAKOUT; | |
| } | |
| if (range.position < 0.05 && volume.state === 'high' && momentum.state.includes('negative')) { | |
| return MARKET_REGIMES.BREAKDOWN; | |
| } | |
| // Trending regimes | |
| if (trendStrength > 40 && trend.strength > 60) { | |
| if (trend.direction === 'up' || structure.structure === 'bullish') { | |
| return MARKET_REGIMES.TRENDING_BULLISH; | |
| } | |
| if (trend.direction === 'down' || structure.structure === 'bearish') { | |
| return MARKET_REGIMES.TRENDING_BEARISH; | |
| } | |
| } | |
| // Ranging | |
| if (range.state === 'tight' || range.percent < 5) { | |
| if (volatility < 2) { | |
| return MARKET_REGIMES.CALM; | |
| } | |
| return MARKET_REGIMES.RANGING; | |
| } | |
| // Calm market | |
| if (volatility < 2 && trendStrength < 20) { | |
| return MARKET_REGIMES.CALM; | |
| } | |
| // Default to ranging | |
| return MARKET_REGIMES.RANGING; | |
| } | |
| /** | |
| * Calculate confidence in regime classification | |
| * @param {Object} metrics - Market metrics | |
| * @param {string} regime - Classified regime | |
| * @returns {number} Confidence (0-100) | |
| */ | |
| calculateConfidence(metrics, regime) { | |
| let confidence = 50; // Base confidence | |
| const { volatility, trend, trendStrength, volume, range } = metrics; | |
| // Adjust based on trend strength | |
| confidence += trendStrength * 0.3; | |
| // Adjust based on trend R² | |
| confidence += trend.strength * 0.2; | |
| // Adjust based on volume confirmation | |
| if (volume.state === 'high' || volume.state === 'very-high') { | |
| confidence += 10; | |
| } | |
| // Adjust based on range clarity | |
| if (range.state === 'tight') { | |
| confidence += 5; | |
| } | |
| // Regime-specific adjustments | |
| switch (regime) { | |
| case MARKET_REGIMES.TRENDING_BULLISH: | |
| case MARKET_REGIMES.TRENDING_BEARISH: | |
| if (trendStrength > 60) confidence += 15; | |
| break; | |
| case MARKET_REGIMES.RANGING: | |
| case MARKET_REGIMES.CALM: | |
| if (volatility < 2) confidence += 10; | |
| break; | |
| case MARKET_REGIMES.BREAKOUT: | |
| case MARKET_REGIMES.BREAKDOWN: | |
| if (volume.state === 'very-high') confidence += 20; | |
| break; | |
| } | |
| return Math.min(100, Math.max(0, confidence)); | |
| } | |
| /** | |
| * Detect regime transitions | |
| * @returns {Object|null} Transition info | |
| */ | |
| detectRegimeTransition() { | |
| if (this.regimeHistory.length < 2) { | |
| return null; | |
| } | |
| const current = this.regimeHistory[this.regimeHistory.length - 1]; | |
| const previous = this.regimeHistory[this.regimeHistory.length - 2]; | |
| if (current.regime !== previous.regime) { | |
| return { | |
| from: previous.regime, | |
| to: current.regime, | |
| timestamp: current.timestamp, | |
| significance: this.calculateTransitionSignificance(previous.regime, current.regime) | |
| }; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Calculate significance of regime transition | |
| * @param {string} from - Previous regime | |
| * @param {string} to - Current regime | |
| * @returns {string} Significance level | |
| */ | |
| calculateTransitionSignificance(from, to) { | |
| const highImpact = [ | |
| [MARKET_REGIMES.ACCUMULATION, MARKET_REGIMES.BREAKOUT], | |
| [MARKET_REGIMES.DISTRIBUTION, MARKET_REGIMES.BREAKDOWN], | |
| [MARKET_REGIMES.RANGING, MARKET_REGIMES.TRENDING_BULLISH], | |
| [MARKET_REGIMES.RANGING, MARKET_REGIMES.TRENDING_BEARISH] | |
| ]; | |
| for (const [fromRegime, toRegime] of highImpact) { | |
| if (from === fromRegime && to === toRegime) { | |
| return 'high'; | |
| } | |
| } | |
| return 'medium'; | |
| } | |
| /** | |
| * Get recommended strategies for current regime | |
| * @returns {Array<string>} Recommended strategies | |
| */ | |
| getRecommendedStrategies() { | |
| if (!this.currentRegime) { | |
| return ['ict-market-structure']; | |
| } | |
| return REGIME_CHARACTERISTICS[this.currentRegime]?.bestStrategies || ['ict-market-structure']; | |
| } | |
| /** | |
| * Get regime history | |
| * @param {number} limit - Number of items | |
| * @returns {Array<Object>} Regime history | |
| */ | |
| getHistory(limit = 20) { | |
| return this.regimeHistory.slice(-limit); | |
| } | |
| } | |
| export default AdaptiveRegimeDetector; | |