monthly_cash, monthly_params

master
Dmitry Maylarov 4 years ago
parent 7585d54eb0
commit 75e770960b

@ -18,13 +18,24 @@ def get_data(stocks, start, end):
cache_name="cache", backend="sqlite", expire_after=expire_after cache_name="cache", backend="sqlite", expire_after=expire_after
) )
session.headers = DEFAULT_HEADERS session.headers = DEFAULT_HEADERS
start = datetime.datetime.fromisoformat("1900-01-01") stockData = pdr.get_data_yahoo(
end = datetime.datetime.now() stocks,
stockData = pdr.get_data_yahoo(stocks, start, end, session=session) datetime.datetime.fromisoformat("1900-01-01"),
return stockData 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 run(strategy, data, fund_mode=False): def prepare_simulation(strategy, params, data, fund_mode=False):
if fund_mode: if fund_mode:
cerebro = bt.Cerebro() cerebro = bt.Cerebro()
cerebro.addobserver(bt.observers.FundShares) cerebro.addobserver(bt.observers.FundShares)
@ -34,7 +45,7 @@ def run(strategy, data, fund_mode=False):
cerebro.addobserver(bt.observers.BuySell) cerebro.addobserver(bt.observers.BuySell)
cerebro.adddata(data) cerebro.adddata(data)
cerebro.addstrategy(strategy) cerebro.addstrategy(strategy, params)
# Broker Information # Broker Information
broker_args = dict(coc=True) broker_args = dict(coc=True)
@ -45,68 +56,100 @@ def run(strategy, data, fund_mode=False):
if fund_mode: if fund_mode:
cerebro.broker.set_fundmode(True) cerebro.broker.set_fundmode(True)
cerebro.broker.set_cash(st.month_sum)
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown") cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
cerebro.addanalyzer(bt.analyzers.VWR, _name="returns") cerebro.addanalyzer(bt.analyzers.VWR, _name="returns")
cerebro.addanalyzer( cerebro.addanalyzer(
bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.NoTimeFrame, _name="tr" bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.NoTimeFrame, _name="tr"
) )
thestrats = cerebro.run() return cerebro
thestrat = thestrats[0]
# cerebro.plot(iplot=False, style="candlestick")
return thestrat
if __name__ == "__main__": def simulate(stockData, monthly_params):
pd.options.display.float_format = "{:,.2f}".format
df = pd.DataFrame( df = pd.DataFrame(
columns=[ columns=[
"froi", "froi%",
"cost", "cost",
"total_value", "total_value",
"times", "#deals",
"units", "#units",
"comms", "comms",
"monthly",
"annual%", "annual%",
"max_dd_len", "max_dd_days",
"max_dd", "max_dd%",
"max_md", "max_md",
"twr", "vwr",
"tr", "tr",
] ]
) )
stockList = ["^GSPC"]
# startDate = datetime.datetime.fromisoformat("2000-01-01")
# endDate = datetime.datetime.fromisoformat("2022-01-01")
print(stockList[0])
for period_years in (20, 50):
endDate = datetime.datetime.now()
startDate = endDate - datetime.timedelta(days=period_years * 365)
stockData = get_data(stockList[0], startDate, endDate)
actualStart: datetime.datetime = stockData.index[0] actualStart: datetime.datetime = stockData.index[0]
actualEnd: datetime.datetime = stockData.index[-1]
data = bt.feeds.PandasData(dataname=stockData) data = bt.feeds.PandasData(dataname=stockData)
for i, strategy in enumerate( for i, strategy in enumerate((st.DCA, st.QDCA, st.VA, st.QVA)):
(st.SmaCross, st.DCA, st.QDCA, st.VA, st.QVA, st.SmaVA) cerebro = prepare_simulation(strategy, monthly_params, data)
): cerebro.broker.set_cash(monthly_params["sum"])
therun = run(strategy, data) therun = cerebro.run()[0]
# cerebro.plot(iplot=False, style="candlestick")
dd = therun.analyzers.drawdown dd = therun.analyzers.drawdown
ret = therun.analyzers.returns ret = therun.analyzers.returns
tr = therun.analyzers.tr tr = therun.analyzers.tr
# print(next(reversed(tr.get_analysis()))) # print(next(reversed(tr.get_analysis())))
params = therun.calc_params() 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 * ( annual = 100 * (
(1 + params[0] / 100) ** (365 / (endDate - actualStart).days) - 1 (1 + params[0] / 100) ** (365 / (actualEnd - actualStart).days) - 1
) )
df.loc[strategy.__name__] = therun.calc_params() + ( df.loc[strategy.__name__] = params + [
annual, annual,
dd.get_analysis().max.len, dd.get_analysis().max.len,
dd.get_analysis().max.drawdown, dd.get_analysis().max.drawdown,
dd.get_analysis().max.moneydown, human_readable_size(dd.get_analysis().max.moneydown),
ret.get_analysis()["vwr"], ret.get_analysis()["vwr"],
list(tr.get_analysis().items())[0][1], 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,
) )
print("Starting from:", actualStart) )
print("Time in Market: {:.1f} years".format((endDate - actualStart).days / 365)) # print(df[["annual%", "froi%", "cost", "total_value", "max_dd%", "max_md"]])
# print(df[["annual%", "froi", "cost", "total_value", "max_dd", "max_md"]])
print(df) print(df)
if __name__ == "__main__":
stockList = ["^GSPC"]
monthly_params = dict(sum=1000, coef=1.001)
# startDate = datetime.datetime.fromisoformat("2000-01-01")
# endDate = datetime.datetime.fromisoformat("2022-01-01")
print(stockList[0])
for period_years in (1, 2, 5, 10, 20, 50, 100):
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 = 10
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)
start_date, end_date = (
end_date,
end_date + datetime.timedelta(days=period_years * 365),
)
"""

@ -7,8 +7,6 @@ from pandas_datareader import data as pdr
import backtrader as bt import backtrader as bt
reserve = 5 # usd for comms etc cause I cant math reserve = 5 # usd for comms etc cause I cant math
month_sum = 5000 # usd
""" """
class LSI(bt.Strategy): class LSI(bt.Strategy):
@ -47,7 +45,8 @@ class PercentageCommisionScheme(bt.CommInfoBase):
class Investing(bt.Strategy): class Investing(bt.Strategy):
def __init__(self): def __init__(self, params={"sum": 500, "coef": 1}):
self.monthly_params = params
self.order = None self.order = None
self.cost = 0 # no comms self.cost = 0 # no comms
self.comms = 0 self.comms = 0
@ -105,21 +104,26 @@ class Investing(bt.Strategy):
self.order = None self.order = None
@property
def monthly_cash(self):
return self.monthly_params["sum"] * self.monthly_params["coef"] ** (self.months)
def calc_params(self): def calc_params(self):
# calculate actual returns # calculate actual returns
self.froi = self.broker.get_fundvalue() - self.val_start self.froi = self.broker.get_fundvalue() - self.val_start
return ( return [
self.froi, self.froi,
self.cost, self.cost,
self.broker.get_value(), self.broker.get_value(),
self.times, self.times,
self.units, self.units,
self.comms, self.comms,
) self.monthly_cash,
]
def notify_timer(self, timer, when, *args): def notify_timer(self, timer, when, *args):
self.months += 1 self.months += 1
self.broker.add_cash(month_sum * 1.005 ** (self.months)) self.broker.add_cash(self.monthly_cash)
class FormulaInvesting(Investing): class FormulaInvesting(Investing):
@ -132,7 +136,7 @@ class VA(FormulaInvesting):
def formula(self): def formula(self):
target_value = ( target_value = (
min( min(
self.months * month_sum * 1.005 ** (self.months), self.months * self.monthly_cash,
self.broker.get_value(), self.broker.get_value(),
) )
- reserve - reserve
@ -159,14 +163,13 @@ class QDCA(DCA):
class SmaCross(Investing): class SmaCross(Investing):
# list of parameters which are configurable for the strategy
params = dict( params = dict(
pfast=50, # period for the fast moving average pfast=50, # period for the fast moving average
pslow=200, # period for the slow moving average pslow=200, # period for the slow moving average
) )
# list of parameters which are configurable for the strategy
def __init__(self): def __init__(self, *args, **kwargs):
super().__init__() super().__init__(*args, **kwargs)
sma1 = bt.ind.SMA(period=self.p.pfast) # fast moving average sma1 = bt.ind.SMA(period=self.p.pfast) # fast moving average
sma2 = bt.ind.SMA(period=self.p.pslow) # slow moving average sma2 = bt.ind.SMA(period=self.p.pslow) # slow moving average
self.crossover = bt.ind.CrossOver(sma1, sma2) # crossover signal self.crossover = bt.ind.CrossOver(sma1, sma2) # crossover signal

Loading…
Cancel
Save