This commit is contained in:
David Chen
2026-03-14 20:14:33 +08:00
committed by GitHub
parent ab91804be1
commit 05c17f6b5d
16 changed files with 3177 additions and 0 deletions

121
main.py Normal file
View File

@@ -0,0 +1,121 @@
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()