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";
|
|
}
|
|
}
|