Files

47 lines
1.6 KiB
Python
Raw Permalink Normal View History

"""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()