import datetime import math import numpy as np import pandas as pd import matplotlib.pyplot as plt import backtrader as bt import strategies as st # import data def get_data(stocks, start, end): from pandas_datareader.yahoo.headers import DEFAULT_HEADERS from pandas_datareader import data as pdr import requests_cache expire_after = datetime.timedelta(days=1) session = requests_cache.CachedSession( cache_name="cache", backend="sqlite", expire_after=expire_after ) session.headers = DEFAULT_HEADERS stockData = pdr.get_data_yahoo( stocks, datetime.datetime.fromisoformat("1900-01-01"), datetime.datetime.now(), session=session, ) return stockData.loc[start: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) 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") cerebro.addanalyzer( bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.NoTimeFrame, _name="tr" ) return cerebro def simulate(stockData, monthly_params): pd.options.display.float_format = "{:,.2f}".format df = pd.DataFrame( columns=[ "froi%", "cost", "total_value", "#deals", "#units", "comms", "monthly", "annual%", "max_dd_days", "max_dd%", "max_md", "vwr", "tr", ] ) actualStart: datetime.datetime = stockData.index[0] actualEnd: datetime.datetime = stockData.index[-1] data = bt.feeds.PandasData(dataname=stockData) for i, strategy in enumerate((st.DCA, st.QDCA, st.VA, st.QVA)): cerebro = prepare_simulation(strategy, monthly_params, data) cerebro.broker.set_cash(monthly_params["sum"]) therun = cerebro.run()[0] # cerebro.plot(iplot=False, style="candlestick") dd = therun.analyzers.drawdown ret = therun.analyzers.returns tr = therun.analyzers.tr # print(next(reversed(tr.get_analysis()))) 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 / (actualEnd - actualStart).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"], list(tr.get_analysis().items())[0][1], ] print( "Investing monthly, increasing {:.2f}%, starting from ${}, from {} to {}, {:.1f} years".format( (monthly_params["coef"] * 100) - 100, monthly_params["sum"], actualStart.date(), actualEnd.date(), (actualEnd - actualStart).days / 365, ) ) with pd.option_context("display.max_rows", None, "display.max_columns", None): # print(df[["annual%", "froi%", "cost", "total_value", "max_dd%", "max_md"]]) print(df) if __name__ == "__main__": stockList = ["^GSPC"] monthly_params = dict(sum=1000, coef=1, t_rate=1 + 0.02 / 12) # startDate = datetime.datetime.fromisoformat("2000-01-01") # endDate = datetime.datetime.fromisoformat("2022-01-01") print(stockList[0]) """ for period_years in (10, 20): end_date = datetime.datetime.now() start_date = end_date - datetime.timedelta(days=period_years * 365) stockData = get_data(stockList[0], start_date, end_date) simulate(stockData, monthly_params) """ period_years = 5 stockData = get_data( stockList[0], datetime.datetime.fromisoformat("1900-01-01"), datetime.datetime.now(), ) start_date = stockData.index[0] end_date = start_date + datetime.timedelta(days=period_years * 365) while start_date < datetime.datetime.today(): stockData = get_data(stockList[0], start_date, end_date) simulate(stockData, monthly_params) start_date, end_date = ( end_date, end_date + datetime.timedelta(days=period_years * 365), )