Code
// 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();
	}
}