forked from animatedread/Warrior_EA
CFirstPassageLadder (RungFor/StoreBar/FirstTouch/OutcomeR/WinShare), CTripleBarrier+CLabelOverlap (ApplyMinStopWidening/ComputeLevels/SnapToLadder, the label-overlap effective-sample-size correction), CMetaFamilies (the classic-pattern taxonomy + table-naming rule), SGeometryScan::Reset() (guards against 7452bd1's partial-reset shape recurring) and System/BinomialStats.mqh (every deploy-gate/edge-floor formula this codebase shares). Each is a small .mq5 Expert Advisor under Tests\ printing PASS/FAIL per assertion via Print(), sharing Tests\TestHarness.mqh. All 5 self-compile-verified 0 errors/0 warnings. FirstPassageLadder.mqh/TripleBarrier.mqh expect BARRIER_LADDER_COUNT/BARRIER_LADDER/BARRIER_HORIZON_LADDER_COUNT predefined by their includer (normally ExpertSignalAIBase.mqh); the test EAs define copies matching production values rather than including the whole AIBase chain. SGeometryScan is reproduced verbatim from ExpertSignalAIBase.mqh for the same reason, flagged in-file as needing to stay byte-identical. Also adds Tests\convert_sample_data.py, which runs research/sqxbars.py's decoder against a COPY of SP500_the5ers_H1.dat (never the SQX install itself) so the operator has real sample data to point a manual tester run at. Decodes structurally (52,542 H1 bars, monotonic, 0 high<low violations) without calibrating a price scale - sqxbars.load()/sqx.calibrate_decimals() both require a validated reference series to do that safely, which this self-contained script does not have. Tests\sample_data\ (the raw copy + decoded .npz) is gitignored, same policy as the existing Market Data/ rule. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
56 lines
2.1 KiB
MQL5
56 lines
2.1 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Warrior_EA |
|
|
//| AnimateDread |
|
|
//| |
|
|
//| Shared PASS/FAIL harness for the unit-test EAs under Tests\. One |
|
|
//| assertion helper and one summary line, so every test EA prints in |
|
|
//| the same shape and a log grep for "FAIL:" or "ALL TESTS PASSED" |
|
|
//| works identically across all of them. |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_TESTS_TESTHARNESS_MQH
|
|
#define WARRIOR_TESTS_TESTHARNESS_MQH
|
|
|
|
int g_testPass = 0;
|
|
int g_testFail = 0;
|
|
|
|
//--- One assertion. Prints PASS/FAIL with the description so a failing line is self-explanatory
|
|
//--- without cross-referencing this file against the source it is testing.
|
|
void TAssert(const bool cond, const string what)
|
|
{
|
|
if(cond)
|
|
{
|
|
g_testPass++;
|
|
Print("PASS: " + what);
|
|
}
|
|
else
|
|
{
|
|
g_testFail++;
|
|
Print("FAIL: " + what);
|
|
}
|
|
}
|
|
|
|
//--- Float/double equality within an absolute tolerance - exact == is the wrong test for anything
|
|
//--- that passed through a division.
|
|
bool TNear(const double a, const double b, const double eps = 1e-6)
|
|
{
|
|
return MathAbs(a - b) <= eps;
|
|
}
|
|
|
|
void TAssertNear(const double got, const double want, const string what, const double eps = 1e-6)
|
|
{
|
|
TAssert(TNear(got, want, eps),
|
|
what + StringFormat(" (got %.10f, want %.10f, eps %.1e)", got, want, eps));
|
|
}
|
|
|
|
//--- One line per suite, deliberately grep-able: "ALL TESTS PASSED" or "TESTS FAILED".
|
|
void TSummary(const string suite)
|
|
{
|
|
PrintFormat("%s: SUMMARY %d passed, %d failed, %d total.", suite, g_testPass, g_testFail,
|
|
g_testPass + g_testFail);
|
|
if(g_testFail == 0)
|
|
Print(suite + ": ALL TESTS PASSED");
|
|
else
|
|
Print(suite + ": TESTS FAILED - see FAIL lines above");
|
|
}
|
|
#endif // WARRIOR_TESTS_TESTHARNESS_MQH
|
|
//+------------------------------------------------------------------+
|