|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
this.currentRegime = regime;
|
|
|
this.confidence = confidence;
|
|
|
this.regimeHistory.push({
|
|
|
regime,
|
|
|
confidence,
|
|
|
timestamp: Date.now(),
|
|
|
metrics
|
|
|
});
|
|
|
|
|
|
|
|
|
if (this.regimeHistory.length > 50) {
|
|
|
this.regimeHistory.shift();
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
regime,
|
|
|
confidence,
|
|
|
characteristics: REGIME_CHARACTERISTICS[regime],
|
|
|
metrics,
|
|
|
transition: this.detectRegimeTransition(),
|
|
|
timestamp: Date.now()
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calculateTrend(closes) {
|
|
|
const period = Math.min(this.trendPeriod, closes.length);
|
|
|
const recentPrices = closes.slice(-period);
|
|
|
|
|
|
|
|
|
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
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
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) };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
analyzeMarketStructure(highs, lows) {
|
|
|
const swingPeriod = 5;
|
|
|
const recentHighs = highs.slice(-20);
|
|
|
const recentLows = lows.slice(-20);
|
|
|
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
if (priceRangePercent < 5 && volumeRatio > 1.2 && Math.abs(priceChange) < 3) {
|
|
|
return 'accumulation';
|
|
|
}
|
|
|
|
|
|
|
|
|
if (priceRangePercent < 5 && volumeRatio > 1.2 && priceChange < 0) {
|
|
|
return 'distribution';
|
|
|
}
|
|
|
|
|
|
|
|
|
if (priceChange > 5 && volumeRatio > 1) {
|
|
|
return 'markup';
|
|
|
}
|
|
|
|
|
|
|
|
|
if (priceChange < -5 && volumeRatio > 1) {
|
|
|
return 'markdown';
|
|
|
}
|
|
|
|
|
|
return 'neutral';
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classifyRegime(metrics) {
|
|
|
const { volatility, trend, trendStrength, momentum, volume, range, structure, phase } = metrics;
|
|
|
|
|
|
|
|
|
if (phase === 'accumulation') {
|
|
|
return MARKET_REGIMES.ACCUMULATION;
|
|
|
}
|
|
|
if (phase === 'distribution') {
|
|
|
return MARKET_REGIMES.DISTRIBUTION;
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (range.state === 'tight' || range.percent < 5) {
|
|
|
if (volatility < 2) {
|
|
|
return MARKET_REGIMES.CALM;
|
|
|
}
|
|
|
return MARKET_REGIMES.RANGING;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (volatility < 2 && trendStrength < 20) {
|
|
|
return MARKET_REGIMES.CALM;
|
|
|
}
|
|
|
|
|
|
|
|
|
return MARKET_REGIMES.RANGING;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calculateConfidence(metrics, regime) {
|
|
|
let confidence = 50;
|
|
|
|
|
|
const { volatility, trend, trendStrength, volume, range } = metrics;
|
|
|
|
|
|
|
|
|
confidence += trendStrength * 0.3;
|
|
|
|
|
|
|
|
|
confidence += trend.strength * 0.2;
|
|
|
|
|
|
|
|
|
if (volume.state === 'high' || volume.state === 'very-high') {
|
|
|
confidence += 10;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (range.state === 'tight') {
|
|
|
confidence += 5;
|
|
|
}
|
|
|
|
|
|
|
|
|
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));
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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';
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getRecommendedStrategies() {
|
|
|
if (!this.currentRegime) {
|
|
|
return ['ict-market-structure'];
|
|
|
}
|
|
|
|
|
|
return REGIME_CHARACTERISTICS[this.currentRegime]?.bestStrategies || ['ict-market-structure'];
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getHistory(limit = 20) {
|
|
|
return this.regimeHistory.slice(-limit);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export default AdaptiveRegimeDetector;
|
|
|
|
|
|
|