from typing import List from historical_data.historical_data import historical_data_tradingview_csv from internal_types.types import Instrument, Position, SecurityType from strategy.buy_and_hold import BuyAndHold from strategy.sma_crossover import SMACrossover from strategy.strategy import Strategy from strategy.turtle_system_1 import TurtleSystem1 from trading_gateway.trading_gateway import BacktestGateway, TradingGateway from utils.utils import ( SEC_1_DAY, SEC_1_HOUR, SEC_15_MINUTES, SEC_30_MINUTES, log_sharpe_ratio, simple_sharpe_ratio, ) def backtest(strategy: Strategy, instr: Instrument, interval_sec: int, initial_balance: float, drawdown_limit: float, trading_gateway: TradingGateway): balance_history: List[float] = [] for ohlc in trading_gateway.next_ohlc(): balance = strategy.net_liquid_value(ohlc.close) balance_history.append(balance) if balance <= initial_balance * (1 - drawdown_limit): break strategy.process_ohlc(ohlc) unfilled_pos = strategy.unfilled_positions(instr) if unfilled_pos.quantity: if not (ohlc.low <= unfilled_pos.price <= ohlc.high): # unfilled_pos = Position(unfilled_pos.instr, unfilled_pos.quantity, ohlc.open) unfilled_pos = Position(unfilled_pos.instr, unfilled_pos.quantity, ohlc.close) strategy.order_filled(unfilled_pos) simple_sharpe = round(simple_sharpe_ratio(balance_history, interval_sec), 4) log_sharpe = round(log_sharpe_ratio(balance_history, interval_sec), 4) return_pct = round((balance_history[-1] / initial_balance - 1) * 100, 4) return f'simple_sharpe: {simple_sharpe}, log_sharpe: {log_sharpe}, return_pct: {return_pct}%' def backtest_turtle(csv: str, instr: Instrument, interval_sec: int, initial_balance: float, drawdown_limit: float, warmup_t0: str, t0: str, t1: str): warmup_historical_data = historical_data_tradingview_csv(csv, instr, warmup_t0, t0) trading_gateway: TradingGateway = BacktestGateway(csv, instr, t0, t1) strategy: Strategy = TurtleSystem1(initial_balance, instr) strategy.warmup(warmup_historical_data) return backtest(strategy, instr, interval_sec, initial_balance, drawdown_limit, trading_gateway) def backtest_buy_and_hold(csv: str, instr: Instrument, interval_sec: int, initial_balance: float, drawdown_limit: float, t0: str, t1: str): trading_gateway: TradingGateway = BacktestGateway(csv, instr, t0, t1) strategy: Strategy = BuyAndHold(initial_balance, instr) return backtest(strategy, instr, interval_sec, initial_balance, drawdown_limit, trading_gateway) def backtest_sma_crossover(csv: str, instr: Instrument, interval_sec: int, initial_balance: float, drawdown_limit: float, warmup_t0: str, t0: str, t1: str): warmup_historical_data = historical_data_tradingview_csv(csv, instr, warmup_t0, t0) trading_gateway: TradingGateway = BacktestGateway(csv, instr, t0, t1) strategy: Strategy = SMACrossover(initial_balance, instr, interval_sec, 12 * SEC_1_HOUR, 26 * SEC_1_HOUR) strategy.warmup(warmup_historical_data) return backtest(strategy, instr, interval_sec, initial_balance, drawdown_limit, trading_gateway) def main(): # csv = './csv/qqq_1999_03_10_1_day.csv' # csv = './csv/qqq_2023_02_01_15_min.csv' csv = './csv/slv_2023_01_03_30_min.csv' # instr = Instrument('QQQ', SecurityType.EQUITY, 1) instr = Instrument('SLV', SecurityType.EQUITY, 1) # interval_sec = SEC_15_MINUTES interval_sec = SEC_30_MINUTES # interval_sec = SEC_1_DAY initial_balance = 1_000_000 drawdown_limit = 1.00 # allow 100% loss # warmup_t0 = '2023-02-01' # t0, t1 = '2023-03-15', '2026-03-01' # warmup_t0 = '2025-10-01' # t0, t1 = '2026-02-01', '2025-03-10' # print(backtest_buy_and_hold(csv, instr, interval_sec, initial_balance, drawdown_limit, t0, t1)) # print( # backtest_turtle(csv, instr, interval_sec, initial_balance, drawdown_limit, warmup_t0, t0, t1)) # print( # backtest_sma_crossover(csv, instr, interval_sec, initial_balance, drawdown_limit, warmup_t0, t0, # t1)) for m in range(1, 12): warmup_t0 = '2023-02-01' t0, t1 = f'2025-{m:02}-01', f'2025-{m+1:02}-01' print(f'---- {t0} ----') print('buy and hold:', backtest_buy_and_hold(csv, instr, interval_sec, initial_balance, drawdown_limit, t0, t1)) print( '12-16 SMA: ', backtest_sma_crossover(csv, instr, interval_sec, initial_balance, drawdown_limit, warmup_t0, t0, t1)) # for y in range(2000, 2026): # warmup_t0, t0, t1 = f'{y-1}-10-01', f'{y}-01-01', f'{y+1}-01-01' # print(f'---- {y} ----') # print('buy and hold:', # backtest_buy_and_hold(csv, instr, interval_sec, initial_balance, drawdown_limit, t0, t1)) # print( # 'turtle :', # backtest_turtle(csv, instr, interval_sec, initial_balance, drawdown_limit, warmup_t0, t0, t1)) if __name__ == '__main__': main()