568 lines
21 KiB
MQL5
568 lines
21 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| AdvancedRegimeDetector.mqh |
| |||
//| Sophisticated regime detection with ADX/ATR/HHLL classification |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "DualEA Advanced Regime System"
| |||
#property version "1.00"
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| REGIME TYPES ENUM |
| |||
//+------------------------------------------------------------------+
| |||
enum ENUM_REGIME_TYPE
| |||
{
| |||
REGIME_TRENDING_UP, // Strong upward trend
| |||
REGIME_TRENDING_DOWN, // Strong downward trend
| |||
REGIME_RANGING_LOW, // Low volatility range
| |||
REGIME_RANGING_HIGH, // High volatility range
| |||
REGIME_VOLATILE_UP, // Volatile upward movement
| |||
REGIME_VOLATILE_DOWN, // Volatile downward movement
| |||
REGIME_CHOPPY, // Choppy/noisy market
| |||
REGIME_BREAKING_UP, // Breaking out upward
| |||
REGIME_BREAKING_DOWN, // Breaking out downward
| |||
REGIME_UNDEFINED // Undefined/transition
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| REGIME INDICATORS STRUCT |
| |||
//+------------------------------------------------------------------+
| |||
struct SRegimeIndicators
| |||
{
| |||
double adx_value; // ADX value
| |||
double adx_plus; // ADX+ value
| |||
double adx_minus; // ADX- value
| |||
double atr_value; // ATR value
| |||
double atr_percentile; // ATR percentile (0-100)
| |||
double hhll_range; // High-Low range
| |||
double avg_true_range; // Average true range
| |||
double volatility_percentile; // Volatility percentile
| |||
double trend_strength; // Trend strength (0-1)
| |||
double range_strength; // Range strength (0-1)
| |||
double breakout_strength; // Breakout strength (0-1)
| |||
| |||
SRegimeIndicators() :
| |||
adx_value(0), adx_plus(0), adx_minus(0), atr_value(0), atr_percentile(0),
| |||
hhll_range(0), avg_true_range(0), volatility_percentile(0),
| |||
trend_strength(0), range_strength(0), breakout_strength(0) {}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| REGIME RESULT STRUCT |
| |||
//+------------------------------------------------------------------+
| |||
struct SRegimeResult
| |||
{
| |||
ENUM_REGIME_TYPE current_regime;
| |||
ENUM_REGIME_TYPE previous_regime;
| |||
double confidence; // Confidence level (0-1)
| |||
string description; // Human-readable description
| |||
datetime timestamp; // Regime timestamp
| |||
int duration_bars; // Duration in bars
| |||
SRegimeIndicators indicators; // Supporting indicators
| |||
| |||
SRegimeResult() :
| |||
current_regime(REGIME_UNDEFINED), previous_regime(REGIME_UNDEFINED),
| |||
confidence(0), duration_bars(0), timestamp(0) {}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| ADVANCED REGIME DETECTOR CLASS |
| |||
//+------------------------------------------------------------------+
| |||
class CAdvancedRegimeDetector
| |||
{
| |||
private:
| |||
string m_symbol;
| |||
int m_timeframe;
| |||
int m_adx_period;
| |||
int m_atr_period;
| |||
int m_hhll_period;
| |||
int m_lookback_bars;
| |||
| |||
// Historical data
| |||
double m_adx_buffer[];
| |||
double m_atr_buffer[];
| |||
double m_high_buffer[];
| |||
double m_low_buffer[];
| |||
double m_close_buffer[];
| |||
| |||
// Thresholds
| |||
double m_adx_trend_threshold;
| |||
double m_adx_strong_trend_threshold;
| |||
double m_atr_high_threshold;
| |||
double m_atr_low_threshold;
| |||
double m_volatility_percentile_high;
| |||
double m_volatility_percentile_low;
| |||
| |||
// State tracking
| |||
SRegimeResult m_last_regime;
| |||
int m_regime_duration;
| |||
| |||
public:
| |||
CAdvancedRegimeDetector(const string symbol, const int timeframe);
| |||
~CAdvancedRegimeDetector();
| |||
| |||
// Configuration
| |||
void SetADXPeriod(const int period) { m_adx_period = period; }
| |||
void SetATRPeriod(const int period) { m_atr_period = period; }
| |||
void SetHHLLPeriod(const int period) { m_hhll_period = period; }
| |||
void SetLookbackBars(const int bars) { m_lookback_bars = bars; }
| |||
void SetADXThresholds(const double trend, const double strong)
| |||
{
| |||
m_adx_trend_threshold = trend;
| |||
m_adx_strong_trend_threshold = strong;
| |||
}
| |||
void SetATRThresholds(const double high, const double low)
| |||
{
| |||
m_atr_high_threshold = high;
| |||
m_atr_low_threshold = low;
| |||
}
| |||
void SetVolatilityThresholds(const double high, const double low)
| |||
{
| |||
m_volatility_percentile_high = high;
| |||
m_volatility_percentile_low = low;
| |||
}
| |||
| |||
// Main detection methods
| |||
SRegimeResult DetectCurrentRegime();
| |||
SRegimeResult GetLastRegime() const { return m_last_regime; }
| |||
| |||
// Indicator calculations
| |||
double CalculateADX(const int shift = 0);
| |||
double CalculateATR(const int shift = 0);
| |||
double CalculateHHLLRange(const int shift = 0);
| |||
double CalculateVolatilityPercentile(const int bars);
| |||
double CalculateTrendStrength();
| |||
double CalculateRangeStrength();
| |||
double CalculateBreakoutStrength();
| |||
| |||
// Utility methods
| |||
string RegimeToString(const ENUM_REGIME_TYPE regime) const;
| |||
ENUM_REGIME_TYPE DetermineRegime(const SRegimeIndicators& indicators);
| |||
double CalculateRegimeConfidence(const ENUM_REGIME_TYPE regime, const SRegimeIndicators& indicators);
| |||
| |||
private:
| |||
void UpdatePriceBuffers();
| |||
void CalculateIndicators(SRegimeIndicators& indicators);
| |||
bool IsTrending(const SRegimeIndicators& indicators);
| |||
bool IsRanging(const SRegimeIndicators& indicators);
| |||
bool IsVolatile(const SRegimeIndicators& indicators);
| |||
bool IsBreakingOut(const SRegimeIndicators& indicators);
| |||
bool IsChoppy(const SRegimeIndicators& indicators);
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CONSTRUCTOR |
| |||
//+------------------------------------------------------------------+
| |||
CAdvancedRegimeDetector::CAdvancedRegimeDetector(const string symbol, const int timeframe)
| |||
{
| |||
m_symbol = symbol;
| |||
m_timeframe = timeframe;
| |||
| |||
// Default parameters
| |||
m_adx_period = 14;
| |||
m_atr_period = 14;
| |||
m_hhll_period = 20;
| |||
m_lookback_bars = 100;
| |||
| |||
// Default thresholds
| |||
m_adx_trend_threshold = 25.0;
| |||
m_adx_strong_trend_threshold = 35.0;
| |||
m_atr_high_threshold = 2.0;
| |||
m_atr_low_threshold = 0.5;
| |||
m_volatility_percentile_high = 75.0;
| |||
m_volatility_percentile_low = 25.0;
| |||
| |||
m_regime_duration = 0;
| |||
m_last_regime.current_regime = REGIME_UNDEFINED;
| |||
m_last_regime.timestamp = 0;
| |||
| |||
ArrayResize(m_adx_buffer, m_lookback_bars);
| |||
ArrayResize(m_atr_buffer, m_lookback_bars);
| |||
ArrayResize(m_high_buffer, m_lookback_bars);
| |||
ArrayResize(m_low_buffer, m_lookback_bars);
| |||
ArrayResize(m_close_buffer, m_lookback_bars);
| |||
| |||
ArrayInitialize(m_adx_buffer, 0);
| |||
ArrayInitialize(m_atr_buffer, 0);
| |||
ArrayInitialize(m_high_buffer, 0);
| |||
ArrayInitialize(m_low_buffer, 0);
| |||
ArrayInitialize(m_close_buffer, 0);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| DESTRUCTOR |
| |||
//+------------------------------------------------------------------+
| |||
CAdvancedRegimeDetector::~CAdvancedRegimeDetector()
| |||
{
| |||
// Cleanup handled automatically
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| DETECT CURRENT REGIME |
| |||
//+------------------------------------------------------------------+
| |||
SRegimeResult CAdvancedRegimeDetector::DetectCurrentRegime()
| |||
{
| |||
SRegimeResult result;
| |||
result.timestamp = TimeCurrent();
| |||
result.previous_regime = m_last_regime.current_regime;
| |||
| |||
// Update price buffers
| |||
UpdatePriceBuffers();
| |||
| |||
// Calculate indicators
| |||
SRegimeIndicators indicators;
| |||
CalculateIndicators(indicators);
| |||
| |||
// Determine regime
| |||
result.current_regime = DetermineRegime(indicators);
| |||
result.confidence = CalculateRegimeConfidence(result.current_regime, indicators);
| |||
result.description = RegimeToString(result.current_regime);
| |||
result.indicators = indicators;
| |||
| |||
// Update duration
| |||
if(result.current_regime == result.previous_regime)
| |||
{
| |||
m_regime_duration++;
| |||
}
| |||
else
| |||
{
| |||
m_regime_duration = 1;
| |||
}
| |||
result.duration_bars = m_regime_duration;
| |||
| |||
// Store result
| |||
m_last_regime = result;
| |||
| |||
return result;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| UPDATE PRICE BUFFERS |
| |||
//+------------------------------------------------------------------+
| |||
void CAdvancedRegimeDetector::UpdatePriceBuffers()
| |||
{
| |||
// Shift arrays
| |||
for(int i = m_lookback_bars - 1; i > 0; i--)
| |||
{
| |||
m_high_buffer[i] = m_high_buffer[i-1];
| |||
m_low_buffer[i] = m_low_buffer[i-1];
| |||
m_close_buffer[i] = m_close_buffer[i-1];
| |||
}
| |||
| |||
// Add latest data
| |||
m_high_buffer[0] = iHigh(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, 0);
| |||
m_low_buffer[0] = iLow(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, 0);
| |||
m_close_buffer[0]= iClose(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, 0);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE INDICATORS |
| |||
//+------------------------------------------------------------------+
| |||
void CAdvancedRegimeDetector::CalculateIndicators(SRegimeIndicators& indicators)
| |||
{
| |||
// Calculate ADX
| |||
indicators.adx_value = CalculateADX();
| |||
{
| |||
int adx_handle = iADX(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, m_adx_period);
| |||
if(adx_handle != INVALID_HANDLE)
| |||
{
| |||
double plus[1], minus[1];
| |||
if(CopyBuffer(adx_handle, 1 /*PLUSDI*/, 0, 1, plus) > 0) indicators.adx_plus = plus[0];
| |||
if(CopyBuffer(adx_handle, 2 /*MINUSDI*/, 0, 1, minus) > 0) indicators.adx_minus = minus[0];
| |||
}
| |||
}
| |||
| |||
// Calculate ATR
| |||
indicators.atr_value = CalculateATR();
| |||
indicators.avg_true_range = indicators.atr_value;
| |||
| |||
// Calculate HHLL range
| |||
indicators.hhll_range = CalculateHHLLRange();
| |||
| |||
// Calculate percentiles
| |||
indicators.atr_percentile = CalculateVolatilityPercentile(m_lookback_bars);
| |||
indicators.volatility_percentile = indicators.atr_percentile;
| |||
| |||
// Calculate strengths
| |||
indicators.trend_strength = CalculateTrendStrength();
| |||
indicators.range_strength = CalculateRangeStrength();
| |||
indicators.breakout_strength = CalculateBreakoutStrength();
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE ADX |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateADX(const int shift)
| |||
{
| |||
int handle = iADX(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, m_adx_period);
| |||
if(handle == INVALID_HANDLE) return 0.0;
| |||
double buf[1];
| |||
if(CopyBuffer(handle, 0 /*MAIN*/, shift, 1, buf) <= 0) return 0.0;
| |||
return buf[0];
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE ATR |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateATR(const int shift)
| |||
{
| |||
int handle = iATR(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, m_atr_period);
| |||
if(handle == INVALID_HANDLE) return 0.0;
| |||
double buf[1];
| |||
if(CopyBuffer(handle, 0, shift, 1, buf) <= 0) return 0.0;
| |||
return buf[0];
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE HHLL RANGE |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateHHLLRange(const int shift)
| |||
{
| |||
int hi_index = iHighest(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, MODE_HIGH, m_hhll_period, shift);
| |||
int lo_index = iLowest(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, MODE_LOW, m_hhll_period, shift);
| |||
double highest = iHigh(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, hi_index);
| |||
double lowest = iLow(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, lo_index);
| |||
return highest - lowest;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE VOLATILITY PERCENTILE |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateVolatilityPercentile(const int bars)
| |||
{
| |||
if(bars <= 0) return 50.0;
| |||
| |||
double atr_values[];
| |||
ArrayResize(atr_values, bars);
| |||
| |||
for(int i = 0; i < bars; i++)
| |||
{
| |||
int h = iATR(m_symbol, (ENUM_TIMEFRAMES)m_timeframe, m_atr_period);
| |||
if(h == INVALID_HANDLE) { atr_values[i] = 0.0; continue; }
| |||
double b[1];
| |||
if(CopyBuffer(h, 0, i, 1, b) <= 0) atr_values[i] = 0.0; else atr_values[i] = b[0];
| |||
}
| |||
| |||
double current_atr = atr_values[0];
| |||
double percentile = 0;
| |||
| |||
for(int i = 0; i < bars; i++)
| |||
{
| |||
if(atr_values[i] <= current_atr)
| |||
percentile++;
| |||
}
| |||
| |||
return (percentile / bars) * 100.0;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE TREND STRENGTH |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateTrendStrength()
| |||
{
| |||
double adx = CalculateADX();
| |||
| |||
// Normalize ADX to 0-1 range
| |||
if(adx >= m_adx_strong_trend_threshold)
| |||
return 1.0;
| |||
else if(adx <= m_adx_trend_threshold)
| |||
return 0.0;
| |||
else
| |||
return (adx - m_adx_trend_threshold) / (m_adx_strong_trend_threshold - m_adx_trend_threshold);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE RANGE STRENGTH |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateRangeStrength()
| |||
{
| |||
double atr_percentile = CalculateVolatilityPercentile(m_lookback_bars);
| |||
| |||
// Lower volatility suggests ranging
| |||
if(atr_percentile <= m_volatility_percentile_low)
| |||
return 1.0;
| |||
else if(atr_percentile >= m_volatility_percentile_high)
| |||
return 0.0;
| |||
else
| |||
return 1.0 - ((atr_percentile - m_volatility_percentile_low) /
| |||
(m_volatility_percentile_high - m_volatility_percentile_low));
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE BREAKOUT STRENGTH |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateBreakoutStrength()
| |||
{
| |||
double atr_percentile = CalculateVolatilityPercentile(m_lookback_bars);
| |||
double adx = CalculateADX();
| |||
| |||
// High volatility + strong trend = breakout
| |||
double volatility_factor = (atr_percentile - m_volatility_percentile_low) /
| |||
(m_volatility_percentile_high - m_volatility_percentile_low);
| |||
| |||
double trend_factor = CalculateTrendStrength();
| |||
| |||
return MathMin(1.0, volatility_factor * trend_factor);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| DETERMINE REGIME |
| |||
//+------------------------------------------------------------------+
| |||
ENUM_REGIME_TYPE CAdvancedRegimeDetector::DetermineRegime(const SRegimeIndicators& indicators)
| |||
{
| |||
// Check for undefined regime
| |||
if(indicators.adx_value < 0 || indicators.atr_value <= 0)
| |||
return REGIME_UNDEFINED;
| |||
| |||
// Check for trending conditions
| |||
bool is_trending = IsTrending(indicators);
| |||
bool is_ranging = IsRanging(indicators);
| |||
bool is_volatile = IsVolatile(indicators);
| |||
bool is_breaking_out = IsBreakingOut(indicators);
| |||
bool is_choppy = IsChoppy(indicators);
| |||
| |||
// Determine regime based on conditions
| |||
if(is_breaking_out && indicators.breakout_strength > 0.7)
| |||
{
| |||
if(indicators.adx_plus > indicators.adx_minus)
| |||
return REGIME_BREAKING_UP;
| |||
else
| |||
return REGIME_BREAKING_DOWN;
| |||
}
| |||
| |||
if(is_trending && indicators.trend_strength > 0.8)
| |||
{
| |||
if(indicators.adx_plus > indicators.adx_minus)
| |||
return REGIME_TRENDING_UP;
| |||
else
| |||
return REGIME_TRENDING_DOWN;
| |||
}
| |||
| |||
if(is_volatile && indicators.volatility_percentile > 75)
| |||
{
| |||
if(indicators.adx_plus > indicators.adx_minus)
| |||
return REGIME_VOLATILE_UP;
| |||
else
| |||
return REGIME_VOLATILE_DOWN;
| |||
}
| |||
| |||
if(is_ranging && indicators.range_strength > 0.7)
| |||
{
| |||
if(indicators.volatility_percentile < 25)
| |||
return REGIME_RANGING_LOW;
| |||
else
| |||
return REGIME_RANGING_HIGH;
| |||
}
| |||
| |||
if(is_choppy)
| |||
{
| |||
return REGIME_CHOPPY;
| |||
}
| |||
| |||
return REGIME_UNDEFINED;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CHECK IF TRENDING |
| |||
//+------------------------------------------------------------------+
| |||
bool CAdvancedRegimeDetector::IsTrending(const SRegimeIndicators& indicators)
| |||
{
| |||
return indicators.adx_value >= m_adx_trend_threshold;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CHECK IF RANGING |
| |||
//+------------------------------------------------------------------+
| |||
bool CAdvancedRegimeDetector::IsRanging(const SRegimeIndicators& indicators)
| |||
{
| |||
return indicators.adx_value < m_adx_trend_threshold &&
| |||
indicators.volatility_percentile <= m_volatility_percentile_high;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CHECK IF VOLATILE |
| |||
//+------------------------------------------------------------------+
| |||
bool CAdvancedRegimeDetector::IsVolatile(const SRegimeIndicators& indicators)
| |||
{
| |||
return indicators.volatility_percentile > m_volatility_percentile_high;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CHECK IF BREAKING OUT |
| |||
//+------------------------------------------------------------------+
| |||
bool CAdvancedRegimeDetector::IsBreakingOut(const SRegimeIndicators& indicators)
| |||
{
| |||
return indicators.breakout_strength > 0.6 &&
| |||
indicators.adx_value >= m_adx_strong_trend_threshold;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CHECK IF CHOPPY |
| |||
//+------------------------------------------------------------------+
| |||
bool CAdvancedRegimeDetector::IsChoppy(const SRegimeIndicators& indicators)
| |||
{
| |||
return indicators.adx_value < m_adx_trend_threshold &&
| |||
indicators.volatility_percentile > m_volatility_percentile_low &&
| |||
indicators.volatility_percentile < m_volatility_percentile_high;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CALCULATE REGIME CONFIDENCE |
| |||
//+------------------------------------------------------------------+
| |||
double CAdvancedRegimeDetector::CalculateRegimeConfidence(const ENUM_REGIME_TYPE regime,
| |||
const SRegimeIndicators& indicators)
| |||
{
| |||
double confidence = 0.0;
| |||
| |||
switch(regime)
| |||
{
| |||
case REGIME_TRENDING_UP:
| |||
case REGIME_TRENDING_DOWN:
| |||
confidence = indicators.trend_strength;
| |||
break;
| |||
| |||
case REGIME_RANGING_LOW:
| |||
case REGIME_RANGING_HIGH:
| |||
confidence = indicators.range_strength;
| |||
break;
| |||
| |||
case REGIME_VOLATILE_UP:
| |||
case REGIME_VOLATILE_DOWN:
| |||
confidence = indicators.volatility_percentile / 100.0;
| |||
break;
| |||
| |||
case REGIME_BREAKING_UP:
| |||
case REGIME_BREAKING_DOWN:
| |||
confidence = indicators.breakout_strength;
| |||
break;
| |||
| |||
case REGIME_CHOPPY:
| |||
confidence = 0.5; // Medium confidence for choppy
| |||
break;
| |||
| |||
default:
| |||
confidence = 0.0;
| |||
break;
| |||
}
| |||
| |||
return MathMin(1.0, MathMax(0.0, confidence));
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| REGIME TO STRING |
| |||
//+------------------------------------------------------------------+
| |||
string CAdvancedRegimeDetector::RegimeToString(const ENUM_REGIME_TYPE regime) const
| |||
{
| |||
switch(regime)
| |||
{
| |||
case REGIME_TRENDING_UP: return "TRENDING_UP";
| |||
case REGIME_TRENDING_DOWN: return "TRENDING_DOWN";
| |||
case REGIME_RANGING_LOW: return "RANGING_LOW";
| |||
case REGIME_RANGING_HIGH: return "RANGING_HIGH";
| |||
case REGIME_VOLATILE_UP: return "VOLATILE_UP";
| |||
case REGIME_VOLATILE_DOWN: return "VOLATILE_DOWN";
| |||
case REGIME_CHOPPY: return "CHOPPY";
| |||
case REGIME_BREAKING_UP: return "BREAKING_UP";
| |||
case REGIME_BREAKING_DOWN: return "BREAKING_DOWN";
| |||
case REGIME_UNDEFINED: return "UNDEFINED";
| |||
default: return "UNKNOWN";
| |||
}
| |||
}
|