dualea/Experts/Advisors/DualEA/Tests/Test_TrainedModelParity.mq5
2026-10-28 00:31:51 -04:00
68 satır
3,2 KiloByte
MQL5
#property strict
#property tester_file "DualEA\\signal_model.onnx"
#property tester_file "DualEA\\onnx_config.ini"
#property tester_file "DualEA\\onnx_config.json"
#property tester_file "DualEA\\parity_samples.csv"
#include "../Include/ModelPredictor.mqh"
#include "../Include/CMarketModelFeatures.mqh"
int checks=0,failures=0;
double max_probability_error=0.0,max_feature_error=0.0;
void Check(bool ok,string label)
{
checks++;
if(!ok){failures++;Print("[MODEL-PARITY FAIL] ",label);}
}
int OnInit()
{
CModelPredictor model;
if(!model.Init("DualEA\\signal_model.onnx","DualEA\\onnx_config.json","DualEA\\onnx_config.ini")) return INIT_FAILED;
string symbols[],names[],expected_names[];
Check(model.GetSupportedSymbols(symbols),"Supported symbol metadata");
Check(model.FeatureContract()=="market_universal_v1","Universal feature contract");
Check(model.TrainedTimeframe()==PERIOD_H1,"H1 timeframe metadata");
Check(!model.SupportsSymbol("NOT_A_TRAINED_SYMBOL"),"Unknown symbols rejected");
model.GetFeatureNames(names);
Check(UniversalModelFeatureNames(symbols,expected_names),"Shared schema generated");
Check(ArraySize(names)==ArraySize(expected_names),"Schema dimension");
for(int i=0;i<ArraySize(names) && i<ArraySize(expected_names);i++)
Check(names[i]==expected_names[i],"Feature ordering");
int file=FileOpen("DualEA\\parity_samples.csv",FILE_READ|FILE_CSV|FILE_ANSI,',',CP_UTF8);
if(file==INVALID_HANDLE) return INIT_FAILED;
int width=model.FeatureCount();
for(int i=0;i<5+width;i++) FileReadString(file);
int rows=0;
while(!FileIsEnding(file))
{
string split=FileReadString(file);
if(split=="" && FileIsEnding(file)) break;
datetime decision_time=StringToTime(FileReadString(file));
string symbol=FileReadString(file);
int direction=(int)FileReadNumber(file);
double expected=FileReadNumber(file);
double raw[];
ArrayResize(raw,width);
for(int i=0;i<width;i++) raw[i]=FileReadNumber(file);
double actual=model.Predict(raw,width);
double error=MathAbs(actual-expected);
max_probability_error=MathMax(max_probability_error,error);
Check(actual>=0.0 && actual<=1.0 && error<0.00001,"Trained-double/native-ONNX probability parity: "+symbol);
double base[],expanded[];
bool built=BuildMarketModelFeatures(symbol,PERIOD_H1,decision_time,direction,base) &&
ExpandUniversalModelFeatures(base,symbol,symbols,expanded);
Check(built,"Runtime feature generation: "+symbol);
if(built)
{
double feature_error=0.0;
for(int i=0;i<width;i++) feature_error=MathMax(feature_error,MathAbs(raw[i]-expanded[i]));
max_feature_error=MathMax(max_feature_error,feature_error);
Check(feature_error<0.000001,"Historical/runtime feature parity: "+symbol);
}
rows++;
}
FileClose(file);
Check(rows>=ArraySize(symbols)*6,"Multiple chronological samples per symbol");
PrintFormat("[MODEL-PARITY] rows=%d checks=%d failures=%d max_probability_error=%.10g max_feature_error=%.10g",rows,checks,failures,max_probability_error,max_feature_error);
return failures==0 ? INIT_SUCCEEDED : INIT_FAILED;
}
void OnTick() {}
double OnTester(){return failures==0 ? checks : -failures;}