| #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;}
|