|
|
|
@ -9,9 +9,8 @@ import backtrader as bt
|
|
|
|
stockList = ["VOO"]
|
|
|
|
stockList = ["VOO"]
|
|
|
|
total_days_in_market = 365 * 10
|
|
|
|
total_days_in_market = 365 * 10
|
|
|
|
month_sum = 500 # usd
|
|
|
|
month_sum = 500 # usd
|
|
|
|
period_months = 1
|
|
|
|
|
|
|
|
reserve = 5 # usd for comms etc
|
|
|
|
reserve = 5 # usd for comms etc
|
|
|
|
period_sum = month_sum * period_months
|
|
|
|
startDate = datetime.datetime.fromisoformat("2018-01-01")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# import data
|
|
|
|
# import data
|
|
|
|
@ -21,7 +20,7 @@ def get_data(stocks, start, end):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
endDate = datetime.datetime.now()
|
|
|
|
endDate = datetime.datetime.now()
|
|
|
|
startDate = endDate - datetime.timedelta(days=total_days_in_market)
|
|
|
|
# startDate = endDate - datetime.timedelta(days=total_days_in_market)
|
|
|
|
stockData = get_data(stockList[0], startDate, endDate)
|
|
|
|
stockData = get_data(stockList[0], startDate, endDate)
|
|
|
|
|
|
|
|
|
|
|
|
actualStart: datetime.datetime = stockData.index[0]
|
|
|
|
actualStart: datetime.datetime = stockData.index[0]
|
|
|
|
@ -65,8 +64,6 @@ class PercentageCommisionScheme(bt.CommInfoBase):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FormulaInvesting(bt.Strategy):
|
|
|
|
class FormulaInvesting(bt.Strategy):
|
|
|
|
params = dict(monthly_cash=month_sum, monthly_range=[5, 20])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
def __init__(self):
|
|
|
|
self.order = None
|
|
|
|
self.order = None
|
|
|
|
self.totalcost = 0
|
|
|
|
self.totalcost = 0
|
|
|
|
@ -77,10 +74,10 @@ class FormulaInvesting(bt.Strategy):
|
|
|
|
|
|
|
|
|
|
|
|
def log(self, txt, dt=None):
|
|
|
|
def log(self, txt, dt=None):
|
|
|
|
dt = dt or self.datas[0].datetime.date(0)
|
|
|
|
dt = dt or self.datas[0].datetime.date(0)
|
|
|
|
# print("%s, %s" % (dt.isoformat(), txt))
|
|
|
|
print("%s, %s" % (dt.isoformat(), txt))
|
|
|
|
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
def start(self):
|
|
|
|
self.broker.set_fundmode(fundmode=True, fundstartval=100.0)
|
|
|
|
# self.broker.set_fundmode(fundmode=True, fundstartval=100.0)
|
|
|
|
|
|
|
|
|
|
|
|
self.cash_start = self.broker.get_cash()
|
|
|
|
self.cash_start = self.broker.get_cash()
|
|
|
|
self.val_start = 100.0
|
|
|
|
self.val_start = 100.0
|
|
|
|
@ -88,7 +85,7 @@ class FormulaInvesting(bt.Strategy):
|
|
|
|
# ADD A TIMER
|
|
|
|
# ADD A TIMER
|
|
|
|
self.add_timer(
|
|
|
|
self.add_timer(
|
|
|
|
when=bt.timer.SESSION_START,
|
|
|
|
when=bt.timer.SESSION_START,
|
|
|
|
monthdays=[i for i in self.p.monthly_range],
|
|
|
|
monthdays=[1],
|
|
|
|
monthcarry=True
|
|
|
|
monthcarry=True
|
|
|
|
# timername='buytimer',
|
|
|
|
# timername='buytimer',
|
|
|
|
)
|
|
|
|
)
|
|
|
|
@ -137,6 +134,9 @@ class FormulaInvesting(bt.Strategy):
|
|
|
|
self.order = None
|
|
|
|
self.order = None
|
|
|
|
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
def stop(self):
|
|
|
|
|
|
|
|
self.calc_params()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_params(self):
|
|
|
|
# calculate actual returns
|
|
|
|
# calculate actual returns
|
|
|
|
self.roi = (self.broker.get_value() / self.cash_start) - 1
|
|
|
|
self.roi = (self.broker.get_value() / self.cash_start) - 1
|
|
|
|
self.froi = self.broker.get_fundvalue() - self.val_start
|
|
|
|
self.froi = self.broker.get_fundvalue() - self.val_start
|
|
|
|
@ -160,13 +160,13 @@ class FormulaInvesting(bt.Strategy):
|
|
|
|
|
|
|
|
|
|
|
|
def notify_timer(self, timer, when, *args):
|
|
|
|
def notify_timer(self, timer, when, *args):
|
|
|
|
self.periods += 1
|
|
|
|
self.periods += 1
|
|
|
|
self.broker.add_cash(self.p.monthly_cash)
|
|
|
|
self.broker.add_cash(month_sum)
|
|
|
|
self.formula()
|
|
|
|
self.formula()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VA(FormulaInvesting):
|
|
|
|
class VA(FormulaInvesting):
|
|
|
|
def formula(self):
|
|
|
|
def formula(self):
|
|
|
|
target_value = (self.periods) * self.p.monthly_cash - reserve
|
|
|
|
target_value = min(self.periods * month_sum - reserve, self.broker.get_value())
|
|
|
|
self.order_target_value(target=target_value)
|
|
|
|
self.order_target_value(target=target_value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -176,6 +176,18 @@ class DCA(FormulaInvesting):
|
|
|
|
self.order_target_value(target=target_value)
|
|
|
|
self.order_target_value(target=target_value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class QVA(VA):
|
|
|
|
|
|
|
|
def formula(self):
|
|
|
|
|
|
|
|
if not self.periods % 3:
|
|
|
|
|
|
|
|
super().formula()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class QDCA(DCA):
|
|
|
|
|
|
|
|
def formula(self):
|
|
|
|
|
|
|
|
if not self.periods % 3:
|
|
|
|
|
|
|
|
super().formula()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run(strategy, data):
|
|
|
|
def run(strategy, data):
|
|
|
|
cerebro = bt.Cerebro(stdstats=False)
|
|
|
|
cerebro = bt.Cerebro(stdstats=False)
|
|
|
|
|
|
|
|
|
|
|
|
@ -193,12 +205,20 @@ def run(strategy, data):
|
|
|
|
comminfo = PercentageCommisionScheme()
|
|
|
|
comminfo = PercentageCommisionScheme()
|
|
|
|
cerebro.broker.addcommissioninfo(comminfo)
|
|
|
|
cerebro.broker.addcommissioninfo(comminfo)
|
|
|
|
|
|
|
|
|
|
|
|
cerebro.broker.set_cash(period_sum)
|
|
|
|
cerebro.broker.set_cash(month_sum)
|
|
|
|
cerebro.run()
|
|
|
|
# cerebro.addobserver(bt.observers.FundValue)
|
|
|
|
|
|
|
|
# cerebro.addobserver(bt.observers.FundShares)
|
|
|
|
|
|
|
|
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
|
|
|
|
|
|
|
|
cerebro.addanalyzer(bt.analyzers.VWR, _name="returns")
|
|
|
|
|
|
|
|
thestrats = cerebro.run()
|
|
|
|
|
|
|
|
thestrat = thestrats[0]
|
|
|
|
|
|
|
|
dd = thestrat.analyzers.drawdown
|
|
|
|
|
|
|
|
print(dd.get_analysis().max)
|
|
|
|
|
|
|
|
ret = thestrat.analyzers.returns
|
|
|
|
|
|
|
|
print(ret.get_analysis())
|
|
|
|
cerebro.plot(iplot=False, style="candlestick")
|
|
|
|
cerebro.plot(iplot=False, style="candlestick")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# run(LSI, 100000, data)
|
|
|
|
for strategy in (DCA, QDCA, VA, QVA):
|
|
|
|
run(DCA, data)
|
|
|
|
run(strategy, data)
|
|
|
|
run(VA, data)
|
|
|
|
|
|
|
|
|