// BogieNN_v3_MeanReversionML.c
// -----------------------------------------------------------------------------
// Direction 2: Bogie-NN Mean-Reversion ML
// Zorro S 3.11+ lite-C
//
// Goal:
// Trade snap-back moves from short-term extremes rather than continuation.
//
// ML:
// PERCEPTRON + FUZZY + BALANCED
//
// 8 engineered features, approximately normalized to -1..+1:
// 1 smoothed Bogie range position
// 2 raw Bogie range position
// 3 RSI(14)
// 4 Bollinger-band oscillator
// 5 price deviation from EMA(20), normalized by ATR
// 6 3-bar momentum, normalized by ATR
// 7 MMI mean-reversion tendency
// 8 inverse ADX (positive when trend strength is low)
//
// Entry regime:
// MMI >= MeanMMIMin
// ADX <= MeanADXMax
// Bogie + RSI must be at an extreme
// ML must predict the reversal direction
//
// Exit:
// Price reaches EMA(20), or ML strongly predicts the opposite direction.
//
// Trade management:
// ATR stop. No trailing stop; the mean itself is the profit objective.
// -----------------------------------------------------------------------------
string BogieAsset = "EUR/USD";
int BacktestStart = 2010;
int BacktestEnd = 2026;
int StrategyBarPeriod = 60;
int WFOCycles = 8;
int WFOTrainPercent = 85;
int PredictionHorizonBars = 4;
var ConfidenceThreshold = 20.0;
var OppositeExitThreshold = 15.0;
int NocPeriod = 12;
int EmaPeriod = 5;
int FeatureCount = 8;
int RSIPeriod = 14;
int BBPeriod = 20;
int MeanPeriod = 20;
int MMIPeriod = 100;
int ADXPeriod = 14;
var MeanMMIMin = 58.0;
var MeanADXMax = 28.0;
var LongBogieExtreme = -0.45;
var ShortBogieExtreme = 0.45;
var LongRSIMax = 40.0;
var ShortRSIMin = 60.0;
var StopATRMult = 1.6;
int UseMM = 1;
int MiniAcct = 0;
var RiskPercent = 5.0;
var FixedAmount = 0.10;
int NoTradeDay_1 = 0;
int NoTradeDay_2 = 0;
int CloseOnNoTradeDay = 0;
int UseDiagnostics = 1;
var BogieRangePosition()
{
var Highest;
var Lowest;
var Range;
var Position;
Highest = HH(NocPeriod,0);
Lowest = LL(NocPeriod,0);
Range = Highest-Lowest;
if(Range <= 0.0)
return 0.0;
Position = 2.0*(priceC(0)-Lowest)/Range-1.0;
return clamp(Position,-1.0,1.0);
}
void BuildMeanSignals(
var RawBogie,
var SmoothNow,
var RSINow,
var BBOscNow,
var MeanNow,
var ATRNow,
var MMINow,
var ADXNow,
var* Signals)
{
var ATRSafe;
var MeanDeviation;
var Momentum3;
var MeanTendency;
var LowTrendStrength;
ATRSafe = max(ATRNow,PIP);
MeanDeviation = (priceC(0)-MeanNow)/(2.0*ATRSafe);
Momentum3 = (priceC(0)-priceC(3))/(2.0*ATRSafe);
MeanTendency = (MMINow-50.0)/25.0;
LowTrendStrength = (25.0-ADXNow)/25.0;
Signals[0] = clamp(SmoothNow,-1.0,1.0);
Signals[1] = clamp(RawBogie,-1.0,1.0);
Signals[2] = clamp((RSINow-50.0)/50.0,-1.0,1.0);
Signals[3] = clamp((BBOscNow-50.0)/50.0,-1.0,1.0);
Signals[4] = clamp(MeanDeviation,-1.0,1.0);
Signals[5] = clamp(Momentum3,-1.0,1.0);
Signals[6] = clamp(MeanTendency,-1.0,1.0);
Signals[7] = clamp(LowTrendStrength,-1.0,1.0);
}
int MQLDayToZorro(int MqlDay)
{
if(MqlDay == 0)
return 7;
return MqlDay;
}
int TradeAllowedToday()
{
int DayNow;
DayNow = dow(0);
if(DayNow == MQLDayToZorro(NoTradeDay_1))
return 0;
if(DayNow == MQLDayToZorro(NoTradeDay_2))
return 0;
return 1;
}
var LegacyBogieAmount()
{
var AmountValue;
var FreeMargin;
var Step;
if(!UseMM)
return FixedAmount;
FreeMargin = Equity-MarginVal;
if(FreeMargin < 0.0)
FreeMargin = 0.0;
AmountValue = FreeMargin*RiskPercent/100.0/1000.0;
if(MiniAcct)
{
Step = 0.01;
AmountValue = roundto(AmountValue,Step);
if(AmountValue < 0.01)
AmountValue = 0.01;
}
else
{
Step = 0.10;
AmountValue = roundto(AmountValue,Step);
if(AmountValue < 0.10)
AmountValue = 0.10;
}
if(AmountValue > 50.0)
AmountValue = 50.0;
return AmountValue;
}
void ConfigureMeanTrade(var ATRNow)
{
Amount = LegacyBogieAmount();
Risk = 0;
Stop = StopATRMult*ATRNow;
TakeProfit = 0;
Trail = 0;
}
void LogEntry(
string Side,
var Score,
var RawBogie,
var RSINow,
var ADXNow,
var MMINow)
{
if(!UseDiagnostics)
return;
printf(
"\n%s Bar %i %s | MeanScore %.2f | Bogie %.3f | RSI %.2f | ADX %.2f | MMI %.2f | Amount %.3f",
Asset,Bar,Side,Score,RawBogie,RSINow,ADXNow,MMINow,Amount);
}
function run()
{
var RawBogie;
var SmoothNow;
var ATRNow;
var RSINow;
var BBOscNow;
var MeanNow;
var MMINow;
var ADXNow;
var MLTarget;
var MLScore;
var Signals[8];
var* PriceSeries;
var* RawSeries;
int Allowed;
int MeanRegime;
int LongSignal;
int ShortSignal;
if(is(FIRSTINITRUN))
require(-3.11);
set(RULES);
set(RECALCULATE);
set(LOGFILE);
if(Train)
set(PEEK);
BarPeriod = StrategyBarPeriod;
LookBack = 300;
Capital = 10000;
StartDate = BacktestStart;
EndDate = BacktestEnd;
NumWFOCycles = WFOCycles;
DataSplit = WFOTrainPercent;
DataHorizon = PredictionHorizonBars;
asset(BogieAsset);
algo("BogieMeanML");
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
PriceSeries = series(priceC(0),256);
RawBogie = BogieRangePosition();
RawSeries = series(RawBogie,64);
SmoothNow = EMA(RawSeries,EmaPeriod);
ATRNow = ATR(14);
RSINow = RSI(PriceSeries,RSIPeriod);
BBOscNow = BBOsc(PriceSeries,BBPeriod,2.0,MAType_SMA);
MeanNow = EMA(PriceSeries,MeanPeriod);
MMINow = MMI(PriceSeries,MMIPeriod);
ADXNow = ADX(ADXPeriod);
BuildMeanSignals(
RawBogie,
SmoothNow,
RSINow,
BBOscNow,
MeanNow,
ATRNow,
MMINow,
ADXNow,
Signals);
MLTarget = 0.0;
if(Train)
{
if(priceC(-PredictionHorizonBars) > priceC(0))
MLTarget = 1.0;
else
MLTarget = -1.0;
}
MLScore = adviseLong(
PERCEPTRON+FUZZY+BALANCED,
MLTarget,
Signals,
FeatureCount);
if(Train)
return;
if(is(LOOKBACK))
return;
plot("Mean ML",MLScore,NEW,BLUE);
plot("Long Gate",ConfidenceThreshold,0,BLACK);
plot("Short Gate",-ConfidenceThreshold,0,BLACK);
// Mean-reversion positions have an explicit economic exit:
// close when price has returned to its EMA center.
if(NumOpenLong > 0)
{
if(priceC(0) >= MeanNow || MLScore < -OppositeExitThreshold)
{
exitLong();
return;
}
}
if(NumOpenShort > 0)
{
if(priceC(0) <= MeanNow || MLScore > OppositeExitThreshold)
{
exitShort();
return;
}
}
MeanRegime = 0;
if(MMINow >= MeanMMIMin && ADXNow <= MeanADXMax)
MeanRegime = 1;
LongSignal = 0;
ShortSignal = 0;
if(MeanRegime)
{
if(
MLScore > ConfidenceThreshold
&& SmoothNow <= LongBogieExtreme
&& RSINow <= LongRSIMax)
LongSignal = 1;
else if(
MLScore < -ConfidenceThreshold
&& SmoothNow >= ShortBogieExtreme
&& RSINow >= ShortRSIMin)
ShortSignal = 1;
}
Allowed = TradeAllowedToday();
if(!Allowed)
{
if(CloseOnNoTradeDay)
{
if(NumOpenLong > 0)
exitLong();
if(NumOpenShort > 0)
exitShort();
}
return;
}
if(NumOpenLong > 0 || NumOpenShort > 0)
return;
if(LongSignal)
{
ConfigureMeanTrade(ATRNow);
LogEntry("LONG",MLScore,RawBogie,RSINow,ADXNow,MMINow);
enterLong();
}
else if(ShortSignal)
{
ConfigureMeanTrade(ATRNow);
LogEntry("SHORT",MLScore,RawBogie,RSINow,ADXNow,MMINow);
enterShort();
}
}