Regime to stradegy
This commit is contained in:
@@ -0,0 +1,758 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MARKET REGIME CLASSIFIER EA |
|
||||
//| REGIME ENGINE v1 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "MARKET REGIME CLASSIFIER"
|
||||
#property version "1.00"
|
||||
#property description "CLASSIFICATION TO STRATEGY"
|
||||
|
||||
//====================================================
|
||||
// ENUMS
|
||||
//====================================================
|
||||
|
||||
enum MarketRegime
|
||||
{
|
||||
REGIME_UNKNOWN = 0,
|
||||
REGIME_TREND_EXPANSION,
|
||||
REGIME_PULLBACK,
|
||||
REGIME_BALANCED_RANGE,
|
||||
REGIME_VOLATILE_RANGE,
|
||||
REGIME_COMPRESSION
|
||||
};
|
||||
|
||||
//====================================================
|
||||
// INPUTS
|
||||
//====================================================
|
||||
|
||||
input int ATR_Period = 14;
|
||||
input int EMA_Period = 50;
|
||||
input int ADX_Period = 14;
|
||||
|
||||
input int LookbackBars = 20;
|
||||
|
||||
input double MinConfidence = 0.30;
|
||||
|
||||
//====================================================
|
||||
// STRUCTS
|
||||
//====================================================
|
||||
|
||||
struct MarketFeatures
|
||||
{
|
||||
double atrRelative;
|
||||
double emaSlope;
|
||||
double overlapRatio;
|
||||
double wickRatio;
|
||||
double bbWidth;
|
||||
|
||||
double displacementCount;
|
||||
|
||||
double smallBodyRatio;
|
||||
double insideBarFrequency;
|
||||
|
||||
double adxValue;
|
||||
|
||||
bool htfTrendBullish;
|
||||
bool htfTrendBearish;
|
||||
};
|
||||
|
||||
struct RegimeScores
|
||||
{
|
||||
int trendExpansion;
|
||||
int pullback;
|
||||
int balancedRange;
|
||||
int volatileRange;
|
||||
int compression;
|
||||
};
|
||||
|
||||
//====================================================
|
||||
// GLOBALS
|
||||
//====================================================
|
||||
|
||||
int atrHandle;
|
||||
int emaHandle;
|
||||
int adxHandle;
|
||||
int bbHandle;
|
||||
|
||||
//====================================================
|
||||
// INIT
|
||||
//====================================================
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);
|
||||
|
||||
emaHandle = iMA(
|
||||
_Symbol,
|
||||
PERIOD_CURRENT,
|
||||
EMA_Period,
|
||||
0,
|
||||
MODE_EMA,
|
||||
PRICE_CLOSE
|
||||
);
|
||||
|
||||
adxHandle = iADX(
|
||||
_Symbol,
|
||||
PERIOD_CURRENT,
|
||||
ADX_Period
|
||||
);
|
||||
|
||||
bbHandle = iBands(
|
||||
_Symbol,
|
||||
PERIOD_CURRENT,
|
||||
20,
|
||||
0,
|
||||
2.0,
|
||||
PRICE_CLOSE
|
||||
);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// DEINIT
|
||||
//====================================================
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(atrHandle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(atrHandle);
|
||||
atrHandle = INVALID_HANDLE;
|
||||
}
|
||||
if(emaHandle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(emaHandle);
|
||||
emaHandle = INVALID_HANDLE;
|
||||
}
|
||||
if(adxHandle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(adxHandle);
|
||||
adxHandle = INVALID_HANDLE;
|
||||
}
|
||||
if(bbHandle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(bbHandle);
|
||||
bbHandle = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// MAIN
|
||||
//====================================================
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
static datetime lastBarTime = 0;
|
||||
|
||||
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
if(currentBar == lastBarTime)
|
||||
return;
|
||||
|
||||
lastBarTime = currentBar;
|
||||
|
||||
MarketFeatures features;
|
||||
|
||||
ExtractMarketFeatures(features);
|
||||
|
||||
RegimeScores scores;
|
||||
|
||||
CalculateRegimeScores(features, scores);
|
||||
|
||||
MarketRegime regime = GetHighestScoreRegime(scores);
|
||||
|
||||
double confidence = CalculateConfidence(scores);
|
||||
|
||||
if(confidence < MinConfidence)
|
||||
regime = REGIME_UNKNOWN;
|
||||
|
||||
PrintRegime(regime, confidence, scores);
|
||||
|
||||
//===========================================
|
||||
// ROUTER
|
||||
//===========================================
|
||||
|
||||
switch(regime)
|
||||
{
|
||||
case REGIME_TREND_EXPANSION:
|
||||
|
||||
Print("MODE = TREND EXPANSION");
|
||||
|
||||
break;
|
||||
|
||||
case REGIME_PULLBACK:
|
||||
|
||||
Print("MODE = PULLBACK");
|
||||
|
||||
break;
|
||||
|
||||
case REGIME_BALANCED_RANGE:
|
||||
|
||||
Print("MODE = BALANCED RANGE");
|
||||
|
||||
break;
|
||||
|
||||
case REGIME_VOLATILE_RANGE:
|
||||
|
||||
Print("MODE = VOLATILE RANGE");
|
||||
|
||||
break;
|
||||
|
||||
case REGIME_COMPRESSION:
|
||||
|
||||
Print("MODE = COMPRESSION");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// FEATURE EXTRACTION
|
||||
//====================================================
|
||||
|
||||
void ExtractMarketFeatures(MarketFeatures &f)
|
||||
{
|
||||
f.atrRelative = CalculateATRRelative();
|
||||
|
||||
f.emaSlope = CalculateEMASlope();
|
||||
|
||||
f.overlapRatio = CalculateOverlapRatio();
|
||||
|
||||
f.wickRatio = CalculateWickRatio();
|
||||
|
||||
f.bbWidth = CalculateBBWidth();
|
||||
|
||||
f.displacementCount = CountDisplacementCandles();
|
||||
|
||||
f.smallBodyRatio = CalculateSmallBodyRatio();
|
||||
|
||||
f.insideBarFrequency = CalculateInsideBarFrequency();
|
||||
|
||||
f.adxValue = CalculateADX();
|
||||
|
||||
f.htfTrendBullish = (f.emaSlope > 1.0);
|
||||
|
||||
f.htfTrendBearish = false;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// ATR RELATIVE
|
||||
//====================================================
|
||||
|
||||
double CalculateATRRelative()
|
||||
{
|
||||
double atr[];
|
||||
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
CopyBuffer(atrHandle, 0, 0, 60, atr);
|
||||
|
||||
double currentATR = atr[0];
|
||||
|
||||
double sum = 0;
|
||||
|
||||
for(int i=0; i<50; i++)
|
||||
sum += atr[i];
|
||||
|
||||
double avgATR = sum / 50.0;
|
||||
|
||||
if(avgATR == 0)
|
||||
return 0;
|
||||
|
||||
return currentATR / avgATR;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// EMA SLOPE
|
||||
//====================================================
|
||||
|
||||
double CalculateEMASlope()
|
||||
{
|
||||
double ema[];
|
||||
double atr[];
|
||||
|
||||
ArraySetAsSeries(ema, true);
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
CopyBuffer(emaHandle, 0, 0, 20, ema);
|
||||
|
||||
CopyBuffer(atrHandle, 0, 0, 20, atr);
|
||||
|
||||
if(atr[0] == 0)
|
||||
return 0;
|
||||
|
||||
return MathAbs(ema[0] - ema[10]) / atr[0];
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// OVERLAP RATIO
|
||||
//====================================================
|
||||
|
||||
double CalculateOverlapRatio()
|
||||
{
|
||||
double totalOverlap = 0;
|
||||
|
||||
double totalRange = 0;
|
||||
|
||||
for(int i=1; i<=LookbackBars; i++)
|
||||
{
|
||||
double high1 = iHigh(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double low1 = iLow(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double high2 = iHigh(_Symbol, PERIOD_CURRENT, i+1);
|
||||
|
||||
double low2 = iLow(_Symbol, PERIOD_CURRENT, i+1);
|
||||
|
||||
double overlapHigh = MathMin(high1, high2);
|
||||
|
||||
double overlapLow = MathMax(low1, low2);
|
||||
|
||||
double overlap = overlapHigh - overlapLow;
|
||||
|
||||
if(overlap < 0)
|
||||
overlap = 0;
|
||||
|
||||
totalOverlap += overlap;
|
||||
|
||||
totalRange += (high1 - low1);
|
||||
}
|
||||
|
||||
if(totalRange == 0)
|
||||
return 0;
|
||||
|
||||
return totalOverlap / totalRange;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// WICK RATIO
|
||||
//====================================================
|
||||
|
||||
double CalculateWickRatio()
|
||||
{
|
||||
double totalWick = 0;
|
||||
|
||||
double totalRange = 0;
|
||||
|
||||
for(int i=1; i<=LookbackBars; i++)
|
||||
{
|
||||
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double open = iOpen(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double close = iClose(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double upperWick = high - MathMax(open, close);
|
||||
|
||||
double lowerWick = MathMin(open, close) - low;
|
||||
|
||||
double wick = upperWick + lowerWick;
|
||||
|
||||
double range = high - low;
|
||||
|
||||
totalWick += wick;
|
||||
|
||||
totalRange += range;
|
||||
}
|
||||
|
||||
if(totalRange == 0)
|
||||
return 0;
|
||||
|
||||
return totalWick / totalRange;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// DISPLACEMENT COUNT
|
||||
//====================================================
|
||||
|
||||
int CountDisplacementCandles()
|
||||
{
|
||||
double atr[];
|
||||
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
CopyBuffer(atrHandle, 0, 0, 100, atr);
|
||||
|
||||
int count = 0;
|
||||
|
||||
for(int i=1; i<=LookbackBars; i++)
|
||||
{
|
||||
double open = iOpen(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double close = iClose(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double body = MathAbs(close - open);
|
||||
|
||||
double range = high - low;
|
||||
|
||||
if(range == 0)
|
||||
continue;
|
||||
|
||||
double bodyRatio = body / range;
|
||||
|
||||
if(
|
||||
body > atr[i] * 1.5
|
||||
&&
|
||||
bodyRatio > 0.7
|
||||
)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// SMALL BODY RATIO
|
||||
//====================================================
|
||||
|
||||
double CalculateSmallBodyRatio()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(int i=1; i<=LookbackBars; i++)
|
||||
{
|
||||
double body =
|
||||
MathAbs(
|
||||
iClose(_Symbol, PERIOD_CURRENT, i)
|
||||
-
|
||||
iOpen(_Symbol, PERIOD_CURRENT, i)
|
||||
);
|
||||
|
||||
double range =
|
||||
iHigh(_Symbol, PERIOD_CURRENT, i)
|
||||
-
|
||||
iLow(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
if(range == 0)
|
||||
continue;
|
||||
|
||||
double ratio = body / range;
|
||||
|
||||
if(ratio < 0.3)
|
||||
count++;
|
||||
}
|
||||
|
||||
return (double)count / LookbackBars;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// INSIDE BAR FREQUENCY
|
||||
//====================================================
|
||||
|
||||
double CalculateInsideBarFrequency()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(int i=1; i<=LookbackBars; i++)
|
||||
{
|
||||
double high1 = iHigh(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double low1 = iLow(_Symbol, PERIOD_CURRENT, i);
|
||||
|
||||
double high2 = iHigh(_Symbol, PERIOD_CURRENT, i+1);
|
||||
|
||||
double low2 = iLow(_Symbol, PERIOD_CURRENT, i+1);
|
||||
|
||||
bool insideBar =
|
||||
high1 < high2
|
||||
&&
|
||||
low1 > low2;
|
||||
|
||||
if(insideBar)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// ADX
|
||||
//====================================================
|
||||
|
||||
double CalculateADX()
|
||||
{
|
||||
double adx[];
|
||||
|
||||
ArraySetAsSeries(adx, true);
|
||||
|
||||
CopyBuffer(adxHandle, 0, 0, 10, adx);
|
||||
|
||||
return adx[0];
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// BOLLINGER WIDTH
|
||||
//====================================================
|
||||
|
||||
double CalculateBBWidth()
|
||||
{
|
||||
double upper[];
|
||||
double lower[];
|
||||
double atr[];
|
||||
|
||||
ArraySetAsSeries(upper, true);
|
||||
ArraySetAsSeries(lower, true);
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
CopyBuffer(bbHandle, 1, 0, 10, upper);
|
||||
|
||||
CopyBuffer(bbHandle, 2, 0, 10, lower);
|
||||
|
||||
CopyBuffer(atrHandle, 0, 0, 10, atr);
|
||||
|
||||
double width = upper[0] - lower[0];
|
||||
|
||||
if(atr[0] == 0)
|
||||
return 0;
|
||||
|
||||
return width / atr[0];
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// SCORE ENGINE
|
||||
//====================================================
|
||||
|
||||
void CalculateRegimeScores(
|
||||
MarketFeatures &f,
|
||||
RegimeScores &s
|
||||
)
|
||||
{
|
||||
s.trendExpansion = 0;
|
||||
s.pullback = 0;
|
||||
s.balancedRange = 0;
|
||||
s.volatileRange = 0;
|
||||
s.compression = 0;
|
||||
|
||||
//-----------------------------------------
|
||||
// TREND EXPANSION
|
||||
//-----------------------------------------
|
||||
|
||||
if(f.atrRelative > 1.3)
|
||||
s.trendExpansion += 2;
|
||||
|
||||
if(f.emaSlope > 1.0)
|
||||
s.trendExpansion += 2;
|
||||
|
||||
if(f.overlapRatio < 0.4)
|
||||
s.trendExpansion += 2;
|
||||
|
||||
if(f.displacementCount > 5)
|
||||
s.trendExpansion += 3;
|
||||
|
||||
if(f.wickRatio < 0.4)
|
||||
s.trendExpansion += 1;
|
||||
|
||||
//-----------------------------------------
|
||||
// PULLBACK
|
||||
//-----------------------------------------
|
||||
|
||||
if(f.htfTrendBullish || f.htfTrendBearish)
|
||||
s.pullback += 3;
|
||||
|
||||
if(
|
||||
f.atrRelative >= 0.8
|
||||
&&
|
||||
f.atrRelative <= 1.2
|
||||
)
|
||||
{
|
||||
s.pullback += 2;
|
||||
}
|
||||
|
||||
if(
|
||||
f.overlapRatio >= 0.4
|
||||
&&
|
||||
f.overlapRatio <= 0.7
|
||||
)
|
||||
{
|
||||
s.pullback += 2;
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
// BALANCED RANGE
|
||||
//-----------------------------------------
|
||||
|
||||
if(f.atrRelative < 0.9)
|
||||
s.balancedRange += 2;
|
||||
|
||||
if(f.adxValue < 20)
|
||||
s.balancedRange += 2;
|
||||
|
||||
if(f.overlapRatio > 0.7)
|
||||
s.balancedRange += 3;
|
||||
|
||||
if(f.wickRatio > 0.6)
|
||||
s.balancedRange += 1;
|
||||
|
||||
//-----------------------------------------
|
||||
// VOLATILE RANGE
|
||||
//-----------------------------------------
|
||||
|
||||
if(f.atrRelative > 1.3)
|
||||
s.volatileRange += 2;
|
||||
|
||||
if(f.wickRatio > 0.6)
|
||||
s.volatileRange += 2;
|
||||
|
||||
if(f.overlapRatio > 0.5)
|
||||
s.volatileRange += 2;
|
||||
|
||||
//-----------------------------------------
|
||||
// COMPRESSION
|
||||
//-----------------------------------------
|
||||
|
||||
if(f.atrRelative < 0.7)
|
||||
s.compression += 3;
|
||||
|
||||
if(f.bbWidth < 1.0)
|
||||
s.compression += 3;
|
||||
|
||||
if(f.insideBarFrequency > 5)
|
||||
s.compression += 2;
|
||||
|
||||
if(f.smallBodyRatio > 0.6)
|
||||
s.compression += 2;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// CLASSIFIER
|
||||
//====================================================
|
||||
|
||||
MarketRegime GetHighestScoreRegime(
|
||||
RegimeScores &s
|
||||
)
|
||||
{
|
||||
int maxScore = -1;
|
||||
|
||||
MarketRegime regime = REGIME_UNKNOWN;
|
||||
|
||||
if(s.trendExpansion > maxScore)
|
||||
{
|
||||
maxScore = s.trendExpansion;
|
||||
regime = REGIME_TREND_EXPANSION;
|
||||
}
|
||||
|
||||
if(s.pullback > maxScore)
|
||||
{
|
||||
maxScore = s.pullback;
|
||||
regime = REGIME_PULLBACK;
|
||||
}
|
||||
|
||||
if(s.balancedRange > maxScore)
|
||||
{
|
||||
maxScore = s.balancedRange;
|
||||
regime = REGIME_BALANCED_RANGE;
|
||||
}
|
||||
|
||||
if(s.volatileRange > maxScore)
|
||||
{
|
||||
maxScore = s.volatileRange;
|
||||
regime = REGIME_VOLATILE_RANGE;
|
||||
}
|
||||
|
||||
if(s.compression > maxScore)
|
||||
{
|
||||
maxScore = s.compression;
|
||||
regime = REGIME_COMPRESSION;
|
||||
}
|
||||
|
||||
return regime;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// CONFIDENCE
|
||||
//====================================================
|
||||
|
||||
double CalculateConfidence(
|
||||
RegimeScores &s
|
||||
)
|
||||
{
|
||||
int arr[5];
|
||||
|
||||
arr[0] = s.trendExpansion;
|
||||
arr[1] = s.pullback;
|
||||
arr[2] = s.balancedRange;
|
||||
arr[3] = s.volatileRange;
|
||||
arr[4] = s.compression;
|
||||
|
||||
int highest = 0;
|
||||
int second = 0;
|
||||
|
||||
for(int i=0; i<5; i++)
|
||||
{
|
||||
if(arr[i] > highest)
|
||||
{
|
||||
second = highest;
|
||||
highest = arr[i];
|
||||
}
|
||||
else if(arr[i] > second)
|
||||
{
|
||||
second = arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
if(highest == 0)
|
||||
return 0;
|
||||
|
||||
return (double)(highest - second) / highest;
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// PRINT
|
||||
//====================================================
|
||||
|
||||
void PrintRegime(
|
||||
MarketRegime regime,
|
||||
double confidence,
|
||||
RegimeScores &scores
|
||||
)
|
||||
{
|
||||
string name = "UNKNOWN";
|
||||
|
||||
switch(regime)
|
||||
{
|
||||
case REGIME_TREND_EXPANSION:
|
||||
name = "TREND_EXPANSION";
|
||||
break;
|
||||
|
||||
case REGIME_PULLBACK:
|
||||
name = "PULLBACK";
|
||||
break;
|
||||
|
||||
case REGIME_BALANCED_RANGE:
|
||||
name = "BALANCED_RANGE";
|
||||
break;
|
||||
|
||||
case REGIME_VOLATILE_RANGE:
|
||||
name = "VOLATILE_RANGE";
|
||||
break;
|
||||
|
||||
case REGIME_COMPRESSION:
|
||||
name = "COMPRESSION";
|
||||
break;
|
||||
}
|
||||
|
||||
Print(
|
||||
"REGIME = ",
|
||||
name,
|
||||
" | CONFIDENCE = ",
|
||||
DoubleToString(confidence, 2),
|
||||
" | EXP=",
|
||||
scores.trendExpansion,
|
||||
" | PB=",
|
||||
scores.pullback,
|
||||
" | BR=",
|
||||
scores.balancedRange,
|
||||
" | VR=",
|
||||
scores.volatileRange,
|
||||
" | COMP=",
|
||||
scores.compression
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user