52 lines
2.5 KiB
Python
# SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
| |||
"""Compare what MT5 drew (DumpIndicator CSVs) with the Python reference, bar by bar.
| |||
| |||
python3 tests/check_parity.py <dump dir> (needs numpy and pandas)
| |||
| |||
Each CONFIG is one DumpIndicator run (tag -> parameters). Dumps hold buffers b0 (stop line), b2 (buy), b3 (sell).
| |||
Pass: every closed bar's line within 1e-9 relative, and the same arrows on the same bars."""
| |||
import sys
| |||
from pathlib import Path
| |||
| |||
import numpy as np
| |||
import pandas as pd
| |||
| |||
sys.path.insert(0, str(Path(__file__).parent))
| |||
from reference import utbot # noqa: E402
| |||
| |||
CONFIGS = { # tag: (reference kwargs, DumpIndicator InpParams)
| |||
"ut_default": (dict(), ""),
| |||
"ut_k2_a14_ema": (dict(key=2.0, atr_period=14, use_ema=True, ema_period=200),
| |||
"d:2.0|i:14|b:false|b:true|i:200|b:true|b:true|i:12|b:false|b:false|s:alert.wav|b:false|b:false"),
| |||
"ut_k1_a10_ha": (dict(key=1.0, atr_period=10, heikin=True),
| |||
"d:1.0|i:10|b:true|b:false|i:200|b:true|b:true|i:12|b:false|b:false|s:alert.wav|b:false|b:false"),
| |||
}
| |||
| |||
| |||
def check(csv: Path, kw: dict) -> tuple[bool, str]:
| |||
d = pd.read_csv(csv)
| |||
o, h, l, c = (d[k].to_numpy(float) for k in ("open", "high", "low", "close"))
| |||
trail, buy, sell = utbot(o, h, l, c, **kw)
| |||
closed = slice(0, len(d) - 1)
| |||
mt_line, mt_buy, mt_sell = (d[k].to_numpy(float) for k in ("b0", "b2", "b3"))
| |||
ok_line = np.allclose(mt_line[closed], trail[closed], rtol=1e-9, atol=0, equal_nan=True)
| |||
bad_line = int((~np.isclose(mt_line[closed], trail[closed], rtol=1e-9, atol=0, equal_nan=True)).sum())
| |||
same_buy = np.array_equal(np.isnan(mt_buy[closed]), np.isnan(buy[closed]))
| |||
same_sell = np.array_equal(np.isnan(mt_sell[closed]), np.isnan(sell[closed]))
| |||
nb, ns = int((~np.isnan(buy[closed])).sum()), int((~np.isnan(sell[closed])).sum())
| |||
ok = ok_line and same_buy and same_sell
| |||
return ok, (f"{csv.stem:40} bars {len(d):>6} line {'OK' if ok_line else f'{bad_line} differ'} "
| |||
f"buys {nb:>4} {'OK' if same_buy else 'DIFFER'} sells {ns:>4} {'OK' if same_sell else 'DIFFER'}")
| |||
| |||
| |||
if __name__ == "__main__":
| |||
root = Path(sys.argv[1])
| |||
allok, n = True, 0
| |||
for tag, (kw, _) in CONFIGS.items():
| |||
for f in sorted(root.glob(f"{tag}_*.csv")):
| |||
ok, msg = check(f, kw)
| |||
allok &= ok
| |||
n += 1
| |||
print(("PASS " if ok else "FAIL ") + msg)
| |||
print(f"\n{'ALL PASS' if allok and n else 'FAILURES'} ({n} files)")
| |||
sys.exit(0 if allok and n else 1)
|