You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
5.0 KiB

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",
]
)
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"],
]
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),
)