dualea/Experts/Advisors/DualEA/Tests/Test_MarketModelFeatures.mq5
2026-10-28 00:31:51 -04:00
76 lines
5 KiB
MQL5
#property strict
#include "../Include/CMarketModelFeatures.mqh"
int g_feature_failures=0,g_feature_checks=0;
void FeatureCheck(bool passed,string label)
{
g_feature_checks++;
if(!passed) {g_feature_failures++;Print("[MARKET-FEATURE FAIL] ",label);}
}
int OnInit()
{
// Synthetic values exist only to test the feature contract, never training.
MqlRates rates[];
ArrayResize(rates,40);
ArraySetAsSeries(rates,false);
for(int i=0;i<40;i++)
{
ZeroMemory(rates[i]);
rates[i].time=D'2025.01.01 00:00:00'+i*3600;
rates[i].close=0.8+0.0002*i+0.0001*MathSin(i);
rates[i].open=rates[i].close-0.00005;
rates[i].high=rates[i].close+0.0002;
rates[i].low=rates[i].open-0.0002;
rates[i].spread=7;
}
datetime decision=rates[21].time;
double buy[],sell[],after[];
FeatureCheck(MarketModelFeaturesFromRates(rates,20,0,0.00001,decision,buy),"BUY features available");
FeatureCheck(MarketModelFeaturesFromRates(rates,20,1,0.00001,decision,sell),"SELL features available");
FeatureCheck(ArraySize(buy)==12 && ArraySize(sell)==12,"Twelve-feature schema");
string names[];
MarketModelFeatureNames(names);
FeatureCheck(ArraySize(names)==12 && names[0]=="mm_signed_logret1" && names[11]=="mm_atr14","Stable feature names/order");
if(ArraySize(buy)==12 && ArraySize(sell)==12)
{
for(int i=0;i<12;i++)
{
bool signed_feature=(i<=3 || i==6 || i==10);
FeatureCheck(MathAbs(sell[i]-(signed_feature ? -buy[i] : buy[i]))<1e-12,"Direction symmetry feature "+IntegerToString(i));
}
FeatureCheck(MathAbs(buy[0]-MathLog(rates[20].close/rates[19].close))<1e-12,"Return references completed bars");
FeatureCheck(MathAbs(buy[7]-7*0.00001/rates[20].close)<1e-12,"Recorded spread normalization");
for(int i=21;i<40;i++) {rates[i].close=9999;rates[i].open=1;rates[i].high=10000;rates[i].low=0.1;}
FeatureCheck(MarketModelFeaturesFromRates(rates,20,0,0.00001,decision,after),"Future/current values ignored");
bool identical=ArraySize(after)==12;
for(int i=0;i<MathMin(ArraySize(after),12);i++) identical=identical && MathAbs(after[i]-buy[i])<1e-12;
FeatureCheck(identical,"Future/current mutations do not change features");
}
FeatureCheck(!MarketModelFeaturesFromRates(rates,13,0,0.00001,decision,after),"Insufficient warmup rejected");
FeatureCheck(!MarketModelFeaturesFromRates(rates,20,2,0.00001,decision,after),"Invalid direction rejected");
FeatureCheck(!MarketModelFeaturesFromRates(rates,20,0,0.00001,rates[20].time,after),"Unclosed observation rejected");
FeatureCheck(!MarketModelFeaturesFromRates(rates,20,0,0.00001,rates[20].time+1800,after),"Partially completed H1 observation rejected");
FeatureCheck(!MarketModelFeaturesFromRates(rates,20,0,0.0,decision,after),"Invalid point rejected");
string symbols[]={"USDCHF","XAUUSD#"};
string universal_names[];
double expanded[],reversed[];
FeatureCheck(UniversalModelFeatureNames(symbols,universal_names),"Universal schema available");
FeatureCheck(ArraySize(universal_names)==20 && universal_names[12]=="sym_usdchf_flag" && universal_names[19]=="sym_xauusd__zscore","Universal dimension and safe names");
FeatureCheck(ExpandUniversalModelFeatures(buy,"USDCHF",symbols,expanded),"Known symbol expansion");
FeatureCheck(ArraySize(expanded)==20 && expanded[12]==1.0 && expanded[16]==0.0,"Symbol one-hot identity");
FeatureCheck(ArraySize(expanded)==20 && MathAbs(expanded[13]-buy[0]/MathMax(buy[4],1e-8))<1e-10,"Symbol-specific return normalization");
FeatureCheck(ExpandUniversalModelFeatures(sell,"USDCHF",symbols,reversed) && MathAbs(expanded[13]+reversed[13])<1e-10 && MathAbs(expanded[15]+reversed[15])<1e-10,"Universal directional features reverse");
FeatureCheck(ExpandUniversalModelFeatures(buy,"XAUUSD#",symbols,expanded) && expanded[12]==0.0 && expanded[16]==1.0,"Second broker symbol activates own block");
FeatureCheck(!ExpandUniversalModelFeatures(buy,"EURUSD",symbols,expanded) && ArraySize(expanded)==0,"Unknown symbol rejected");
FeatureCheck(!ExpandUniversalModelFeatures(buy,"XAUUSD!",symbols,expanded),"Unknown punctuation alias rejected");
string duplicates[]={"XAUUSD#","XAUUSD!"};
FeatureCheck(!UniversalModelFeatureNames(duplicates,universal_names) && ArraySize(universal_names)==0,"Colliding normalized symbol names rejected");
FeatureCheck(!ExpandUniversalModelFeatures(buy,"XAUUSD#",duplicates,expanded),"Duplicate symbol schema rejected at inference");
rates[19].close=0;
FeatureCheck(!MarketModelFeaturesFromRates(rates,20,0,0.00001,decision,after),"Invalid history rejected");
ArraySetAsSeries(rates,true);
FeatureCheck(!MarketModelFeaturesFromRates(rates,20,0,0.00001,decision,after),"Reversed array contract rejected");
PrintFormat("[MARKET-FEATURE RESULT] checks=%d failures=%d",g_feature_checks,g_feature_failures);
return g_feature_failures==0 ? INIT_SUCCEEDED : INIT_FAILED;
}
void OnTick() {}
double OnTester() {return g_feature_failures==0 ? g_feature_checks : -g_feature_failures;}