tickreplay 0.1.0: tick-accurate MT5 backtesting engine + research toolkit
Memory-mapped tickstores, a nogil numba replay kernel validated trade-for-trade against an independent reference implementation, purged walk-forward CV, an append-only FDR registry, tick-exact labels, and online changepoint detection. 129 tests, 4 runnable examples on synthetic data, CI for linux/macos.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""N-bar breakout on M5 bars, exiting on the opposite breakout.
|
||||
|
||||
Run: python examples/breakout.py
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
import tickreplay as tr
|
||||
|
||||
LOOKBACK = 20
|
||||
|
||||
|
||||
def main():
|
||||
time_ns, bid, ask = tr.synth.generate_ticks(days=30, seed=11)
|
||||
store = tr.TickStore(time_ns, bid, ask)
|
||||
bars = store.bars("M5")
|
||||
|
||||
# shift(1) keeps the current bar out of its own reference range.
|
||||
prior_high = bars["high"].shift(1).rolling(LOOKBACK).max()
|
||||
prior_low = bars["low"].shift(1).rolling(LOOKBACK).min()
|
||||
state = np.where(bars["close"] > prior_high, 1,
|
||||
np.where(bars["close"] < prior_low, -1, 0)).astype("int8")
|
||||
|
||||
# Fire on the bar that breaks out, not on every bar that stays beyond the range.
|
||||
signal = np.where(state != np.r_[np.int8(0), state[:-1]], state, 0).astype("int8")
|
||||
|
||||
config = tr.Config(sl=4.0, tp=8.0, max_hold="8h", exit_on_opposite=True, cost=0.05)
|
||||
result = tr.replay(store, bars, signal, config)
|
||||
|
||||
print(f"ticks {len(store):>12,}")
|
||||
print(f"M5 bars {len(bars):>12,}")
|
||||
print(f"signals {int((signal != 0).sum()):>12,} ({LOOKBACK}-bar breakout)")
|
||||
print("config sl=4.0 tp=8.0 max_hold=8h exit_on_opposite=True cost=0.05")
|
||||
print()
|
||||
for key, value in result.summary().items():
|
||||
print(f"{key:<14}{value:>12.4f}" if isinstance(value, float)
|
||||
else f"{key:<14}{value:>12,}")
|
||||
|
||||
print()
|
||||
print("exit reasons")
|
||||
counts = result.trades["exit_reason"].value_counts()
|
||||
for reason, count in counts.items():
|
||||
print(f" {reason:<10}{count:>6,}{count / counts.sum():>8.1%}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user