import datetime import pandas as pd import backtrader as bt import strategies as st COLUMNS = [ "froi%", "cost", "total_value", "#deals", "#units", "comms", "monthly", "annual%", "max_dd_days", "max_dd%", "max_md", "vwr", ] # import data def get_data( stocks, start=datetime.datetime.fromisoformat("1928-01-01"), end=datetime.datetime.now(), ): from pandas_datareader.yahoo.daily import YahooDailyReader import requests_cache expire_after = datetime.timedelta(days=1) session = requests_cache.CachedSession( cache_name="cache", backend="sqlite", expire_after=expire_after ) reader = YahooDailyReader( stocks, datetime.datetime.fromisoformat("1900-01-01"), datetime.datetime.now(), session=session, ) session.headers = reader.headers return reader.read().loc[start:end] def get_data_borders(stockData): actual_start: datetime.datetime = stockData.index[0] actual_end: datetime.datetime = stockData.index[-1] return actual_start, actual_end def human_readable_size(size, decimal_places=3): for unit in ["$", "K$", "M$", "G$", "T$", "P$"]: if size < 1000.0 or unit == "P$": return f"{size:.{decimal_places}f}{unit}" break size /= 1000.0 def prepare_simulation(strategy, params, data, fund_mode=False): if fund_mode: cerebro = bt.Cerebro() cerebro.addobserver(bt.observers.FundShares) else: cerebro = bt.Cerebro(stdstats=False) cerebro.addobserver(bt.observers.Broker) cerebro.addobserver(bt.observers.BuySell) cerebro.adddata(data) cerebro.addstrategy(strategy, params) # Broker Information broker_args = dict(coc=True, cash=params["sum"]) cerebro.broker = bt.brokers.BackBroker(**broker_args) comminfo = st.PercentageCommisionScheme() cerebro.broker.addcommissioninfo(comminfo) if fund_mode: cerebro.broker.set_fundmode(True) cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown") cerebro.addanalyzer(bt.analyzers.VWR, _name="returns") return cerebro def test_strategy(stockData, strategy, monthly_params): pd.options.display.float_format = "{:,.2f}".format df = pd.DataFrame(columns=COLUMNS) actual_start, actual_end = get_data_borders(stockData) data = bt.feeds.PandasData(dataname=stockData) cerebro = prepare_simulation(strategy, monthly_params, data) therun = cerebro.run()[0] # cerebro.plot(iplot=False, style="candlestick") dd = therun.analyzers.drawdown ret = therun.analyzers.returns params = therun.calc_params() # omg IM so sorry for this, ironically this is here to get human readable size for i in 1, 2, 5, 6: params[i] = human_readable_size(params[i]) annual = 100 * ( (1 + params[0] / 100) ** (365 / (actual_end - actual_start).days) - 1 ) df.loc[strategy.__name__] = params + [ annual, dd.get_analysis().max.len, dd.get_analysis().max.drawdown, human_readable_size(dd.get_analysis().max.moneydown), ret.get_analysis()["vwr"], ] return df