Sumitx369/Paper_Trading_Backtesting
2
1import numpy as np2import trading_engine3 4from app.dsl.catalog import OP5 6 7def _arr(xs):8 return np.array(xs, dtype=float)9 10 11def test_buy_and_hold_equivalence():12 """Always-buy / never-sell should match buy & hold (no fees)."""13 close = _arr([100, 101, 102, 103, 104])14 buy = [(OP["PUSH_CONST"], 1.0, 0, 0)] # always true15 sell = [(OP["PUSH_CONST"], 0.0, 0, 0)] # never true16 17 r = trading_engine.run_backtest(18 close, close, close, close, _arr([0] * 5),19 [], buy, sell, 10_000.0, 0.0, 0.0,20 )21 assert r["n_bars"] == 522 assert len(r["equity"]) == 523 # 100 shares bought at 100, closed at 104 -> 10,40024 assert abs(r["final_value"] - 10_400.0) < 1e-625 assert abs(r["buyhold"][-1] - 10_400.0) < 1e-626 assert len(r["trades"]) == 127 assert r["trades"][0]["reason"] == "eod"28 assert abs(r["trades"][0]["pnl"] - 400.0) < 1e-629 30 31def test_fees_reduce_return():32 close = _arr([100, 110])33 buy = [(OP["PUSH_CONST"], 1.0, 0, 0)]34 sell = [(OP["PUSH_CONST"], 0.0, 0, 0)]35 no_fee = trading_engine.run_backtest(close, close, close, close, _arr([0, 0]), [], buy, sell, 1000.0, 0.0, 0.0)36 with_fee = trading_engine.run_backtest(close, close, close, close, _arr([0, 0]), [], buy, sell, 1000.0, 50.0, 0.0)37 assert with_fee["final_value"] < no_fee["final_value"]38 39 40def test_stop_loss_triggers():41 # enter at 100; bar 1 low (94) breaches a 5% stop -> exit "stop_loss" at the 95 level42 close = _arr([100, 96, 94])43 high = _arr([100, 101, 96])44 low = _arr([100, 94, 90])45 buy = [(OP["PUSH_CONST"], 1.0, 0, 0)]46 sell = [(OP["PUSH_CONST"], 0.0, 0, 0)]47 r = trading_engine.run_backtest(close, high, low, close, _arr([0, 0, 0]), [], buy, sell,48 10_000.0, 0.0, 0.0, 5.0, 0.0)49 reasons = [t["reason"] for t in r["trades"]]50 assert "stop_loss" in reasons51 sl_trade = next(t for t in r["trades"] if t["reason"] == "stop_loss")52 assert abs(sl_trade["exit_price"] - 95.0) < 1e-6 # filled at the stop level53 54 55def test_take_profit_triggers():56 # enter at 100; bar 1 high (112) clears a 10% target -> exit "take_profit"57 close = _arr([100, 108, 109])58 high = _arr([100, 112, 109])59 low = _arr([100, 104, 108])60 buy = [(OP["PUSH_CONST"], 1.0, 0, 0)]61 sell = [(OP["PUSH_CONST"], 0.0, 0, 0)]62 r = trading_engine.run_backtest(close, high, low, close, _arr([0, 0, 0]), [], buy, sell,63 10_000.0, 0.0, 0.0, 0.0, 10.0)64 tp = next(t for t in r["trades"] if t["reason"] == "take_profit")65 assert abs(tp["exit_price"] - 110.0) < 1e-6 # filled at the target level66 67 68def test_indicator_column_referenced():69 # close rising; buy when close > sma(2). sma needs a warmup so first bar can't fire.70 close = _arr([10, 11, 12, 13, 14, 15])71 indicators = [("sma", 2.0, 0.0, 0.0)] # column index 572 buy = [(OP["PUSH_COL"], 0.0, 3, 0), (OP["PUSH_COL"], 0.0, 5, 0), (OP["GT"], 0.0, 0, 0)]73 sell = [(OP["PUSH_CONST"], 0.0, 0, 0)]74 r = trading_engine.run_backtest(close, close, close, close, _arr([0] * 6), indicators, buy, sell, 1000.0, 0.0, 0.0)75 assert r["n_bars"] == 676 assert len(r["trades"]) >= 1 # eventually enters and is closed at EOD77 