// BogieNN_v3_RegimeHybridML.c
// -----------------------------------------------------------------------------
// Direction 3: Bogie-NN Regime-Adaptive Hybrid ML
// Zorro S 3.11+ lite-C
//
// This branch trains TWO separate native models:
//
// adviseLong -> TREND continuation model
// adviseShort -> MEAN-REVERSION model
//
// The Zorro manual permits adviseLong/adviseShort to generate two different
// models for the same asset/algo when an explicit Objective is supplied.
//
// Regime selector:
// TREND regime:
// ADX >= TrendADXMin and MMI <= TrendMMIMax
//
// MEAN regime:
// ADX <= MeanADXMax and MMI >= MeanMMIMin
//
// otherwise:
// NEUTRAL, no new trade.
//
// This is deliberately a model-of-models architecture:
// market regime -> choose specialist ML score -> trade.
// -----------------------------------------------------------------------------
string BogieAsset = "EUR/USD";
int BacktestStart = 2010;
int BacktestEnd = 2026;
int StrategyBarPeriod = 60;
int WFOCycles = 8;
int WFOTrainPercent = 85;
int TrendPredictionHorizonBars = 12;
int MeanPredictionHorizonBars = 4;
var TrendConfidenceThreshold = 20.0;
var MeanConfidenceThreshold = 20.0;
int NocPeriod = 12;
int EmaPeriod = 5;
int FeatureCount = 8;
int ADXPeriod = 14;
int AroonPeriod = 25;
int MMIPeriod = 100;
int RSIPeriod = 14;
int BBPeriod = 20;
int MeanPeriod = 20;
var TrendADXMin = 25.0;
var TrendMMIMax = 64.0;
var MeanADXMax = 20.0;
var MeanMMIMin = 60.0;
var MeanLongExtreme = -0.45;
var MeanShortExtreme = 0.45;
var MeanLongRSIMax = 40.0;
var MeanShortRSIMin = 60.0;
var TrendStopATRMult = 2.5;
var TrendTrailATRMult = 3.0;
var MeanStopATRMult = 1.6;
var TrailGuardPips = 0.5;
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 BuildTrendSignals(
var* SmoothSeries,
var ATRNow,
var PlusNow,
var MinusNow,
var ADXNow,
var AroonNow,
var MMINow,
var* Signals)
{
var ATRSafe;
var Momentum6;
var DISpread;
var TrendStrength;
var Trendiness;
ATRSafe = max(ATRNow,PIP);
Momentum6 = (priceC(0)-priceC(6))/(3.0*ATRSafe);
DISpread = (PlusNow-MinusNow)/100.0;
TrendStrength = (ADXNow-25.0)/25.0;
Trendiness = (75.0-MMINow)/25.0;
Signals[0] = clamp(SmoothSeries[0],-1.0,1.0);
Signals[1] = clamp(2.0*(SmoothSeries[0]-SmoothSeries[1]),-1.0,1.0);
Signals[2] = clamp(SmoothSeries[0]-SmoothSeries[3],-1.0,1.0);
Signals[3] = clamp(Momentum6,-1.0,1.0);
Signals[4] = clamp(DISpread,-1.0,1.0);
Signals[5] = clamp(TrendStrength,-1.0,1.0);
Signals[6] = clamp(AroonNow/100.0,-1.0,1.0);
Signals[7] = clamp(Trendiness,-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 ConfigureTrendTrade(var ATRNow)
{
Amount = LegacyBogieAmount();
Risk = 0;
Stop = TrendStopATRMult*ATRNow;
TakeProfit = 0;
Trail = 0;
}
void ConfigureMeanTrade(var ATRNow)
{
Amount = LegacyBogieAmount();
Risk = 0;
Stop = MeanStopATRMult*ATRNow;
TakeProfit = 0;
Trail = 0;
}
int HybridTrendTrailTMF(var TrailDistance,var GuardDistance)
{
var Candidate;
if(!TradeIsOpen)
return 0;
if(TrailDistance <= 0.0)
return 0;
if(TradeIsShort)
{
Candidate = priceC(0)+TrailDistance;
if(TradeStopLimit > Candidate+GuardDistance)
TradeStopLimit = Candidate;
}
else
{
Candidate = priceC(0)-TrailDistance;
if(TradeStopLimit < Candidate-GuardDistance)
TradeStopLimit = Candidate;
}
return 0;
}
void LogHybrid(
string Side,
string RegimeName,
var ActiveScore,
var ADXNow,
var MMINow)
{
if(!UseDiagnostics)
return;
printf(
"\n%s Bar %i %s | Regime %s | Score %.2f | ADX %.2f | MMI %.2f | Amount %.3f",
Asset,Bar,Side,RegimeName,ActiveScore,ADXNow,MMINow,Amount);
}
function run()
{
var RawBogie;
var SmoothNow;
var ATRNow;
var PlusNow;
var MinusNow;
var ADXNow;
var AroonNow;
var MMINow;
var RSINow;
var BBOscNow;
var MeanNow;
var TrendTarget;
var MeanTarget;
var TrendScore;
var MeanScore;
var TrendSignals[8];
var MeanSignals[8];
var* PriceSeries;
var* RawSeries;
var* SmoothSeries;
var TrailDistance;
var GuardDistance;
int Allowed;
int Regime;
int LongSignal;
int ShortSignal;
if(is(FIRSTINITRUN))
require(-3.11);
set(RULES);
set(TICKS);
set(RECALCULATE);
set(LOGFILE);
if(Train)
set(PEEK);
BarPeriod = StrategyBarPeriod;
LookBack = 300;
Capital = 10000;
StartDate = BacktestStart;
EndDate = BacktestEnd;
NumWFOCycles = WFOCycles;
DataSplit = WFOTrainPercent;
// The longest future target determines the leakage guard.
DataHorizon = TrendPredictionHorizonBars;
asset(BogieAsset);
algo("BogieHybridML");
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
PriceSeries = series(priceC(0),256);
RawBogie = BogieRangePosition();
RawSeries = series(RawBogie,64);
SmoothNow = EMA(RawSeries,EmaPeriod);
SmoothSeries = series(SmoothNow,64);
ATRNow = ATR(14);
PlusNow = PlusDI(ADXPeriod);
MinusNow = MinusDI(ADXPeriod);
ADXNow = ADX(ADXPeriod);
AroonNow = AroonOsc(AroonPeriod);
MMINow = MMI(PriceSeries,MMIPeriod);
RSINow = RSI(PriceSeries,RSIPeriod);
BBOscNow = BBOsc(PriceSeries,BBPeriod,2.0,MAType_SMA);
MeanNow = EMA(PriceSeries,MeanPeriod);
BuildTrendSignals(
SmoothSeries,
ATRNow,
PlusNow,
MinusNow,
ADXNow,
AroonNow,
MMINow,
TrendSignals);
BuildMeanSignals(
RawBogie,
SmoothNow,
RSINow,
BBOscNow,
MeanNow,
ATRNow,
MMINow,
ADXNow,
MeanSignals);
TrendTarget = 0.0;
MeanTarget = 0.0;
if(Train)
{
if(priceC(-TrendPredictionHorizonBars) > priceC(0))
TrendTarget = 1.0;
else
TrendTarget = -1.0;
if(priceC(-MeanPredictionHorizonBars) > priceC(0))
MeanTarget = 1.0;
else
MeanTarget = -1.0;
}
// Explicit objectives are supplied. Therefore adviseLong and adviseShort
// act as two separately trained models rather than long/short trade-return
// targets.
TrendScore = adviseLong(
PERCEPTRON+FUZZY+BALANCED,
TrendTarget,
TrendSignals,
FeatureCount);
MeanScore = adviseShort(
PERCEPTRON+FUZZY+BALANCED,
MeanTarget,
MeanSignals,
FeatureCount);
if(Train)
return;
if(is(LOOKBACK))
return;
plot("Trend Model",TrendScore,NEW,BLUE);
plot("Mean Model",MeanScore,0,RED);
Regime = 0;
if(ADXNow >= TrendADXMin && MMINow <= TrendMMIMax)
Regime = 1;
else if(ADXNow <= MeanADXMax && MMINow >= MeanMMIMin)
Regime = -1;
// Mean-reversion regime positions exit at the center. Since no new mean
// trade can be opened outside Regime -1, this also prevents stale mean
// positions from remaining after the snap-back has completed.
if(Regime == -1)
{
if(NumOpenLong > 0 && priceC(0) >= MeanNow)
{
exitLong();
return;
}
if(NumOpenShort > 0 && priceC(0) <= MeanNow)
{
exitShort();
return;
}
}
LongSignal = 0;
ShortSignal = 0;
// Specialist 1: continuation.
if(Regime == 1)
{
if(TrendScore > TrendConfidenceThreshold && PlusNow > MinusNow)
LongSignal = 1;
else if(TrendScore < -TrendConfidenceThreshold && MinusNow > PlusNow)
ShortSignal = 1;
}
// Specialist 2: snap-back.
if(Regime == -1)
{
if(
MeanScore > MeanConfidenceThreshold
&& SmoothNow <= MeanLongExtreme
&& RSINow <= MeanLongRSIMax)
LongSignal = 1;
else if(
MeanScore < -MeanConfidenceThreshold
&& SmoothNow >= MeanShortExtreme
&& RSINow >= MeanShortRSIMin)
ShortSignal = 1;
}
Allowed = TradeAllowedToday();
if(!Allowed)
{
if(CloseOnNoTradeDay)
{
if(NumOpenLong > 0)
exitLong();
if(NumOpenShort > 0)
exitShort();
}
return;
}
if(LongSignal)
{
if(NumOpenShort > 0)
{
exitShort();
return;
}
if(NumOpenLong == 0)
{
if(Regime == 1)
{
ConfigureTrendTrade(ATRNow);
TrailDistance = TrendTrailATRMult*ATRNow;
GuardDistance = TrailGuardPips*PIP;
LogHybrid("LONG","TREND",TrendScore,ADXNow,MMINow);
enterLong(HybridTrendTrailTMF,TrailDistance,GuardDistance);
}
else if(Regime == -1)
{
ConfigureMeanTrade(ATRNow);
LogHybrid("LONG","MEAN",MeanScore,ADXNow,MMINow);
enterLong();
}
}
return;
}
if(ShortSignal)
{
if(NumOpenLong > 0)
{
exitLong();
return;
}
if(NumOpenShort == 0)
{
if(Regime == 1)
{
ConfigureTrendTrade(ATRNow);
TrailDistance = TrendTrailATRMult*ATRNow;
GuardDistance = TrailGuardPips*PIP;
LogHybrid("SHORT","TREND",TrendScore,ADXNow,MMINow);
enterShort(HybridTrendTrailTMF,TrailDistance,GuardDistance);
}
else if(Regime == -1)
{
ConfigureMeanTrade(ATRNow);
LogHybrid("SHORT","MEAN",MeanScore,ADXNow,MMINow);
enterShort();
}
}
return;
}
// Neutral regime: no new entries.
// Existing trend trades keep their stop/trailing protection.
// Existing mean trades keep their ATR stop and wait for the next bar's
// regime/mean-exit evaluation.
}