From 35f4a992ff6e26cd8b8e72f2aa519e72aaf80c9f Mon Sep 17 00:00:00 2001 From: Dmitry Maylarov Date: Sun, 30 Jan 2022 22:13:15 +0300 Subject: [PATCH] create strategies.py --- compare.py | 145 ++----------------------------------------------- strategies.py | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 140 deletions(-) create mode 100644 strategies.py diff --git a/compare.py b/compare.py index 7d03269..534a49e 100644 --- a/compare.py +++ b/compare.py @@ -12,142 +12,7 @@ reserve = 5 # usd for comms etc # startDate = datetime.datetime.fromisoformat("2000-01-01") # endDate = datetime.datetime.fromisoformat("2022-01-01") - -class LSI(bt.Strategy): - def start(self): - self.val_start = self.broker.get_cash() - - def nextstart(self): - size = math.floor((self.broker.get_cash() - 10) / self.data[0]) - self.buy(size=size) - - def stop(self): - # calculate actual returns - self.roi = (self.broker.get_value() / self.val_start) - 1 - print("Starting Value: ${:,.2f}".format(self.val_start)) - print("ROI: {:.2f}%".format(self.roi * 100.0)) - print( - "Annualised: {:.2f}%".format( - 100 * ((1 + self.roi) ** (365 / (endDate - actualStart).days) - 1) - ) - ) - print( - "Gross Return: ${:,.2f}".format(self.broker.get_value() - self.val_start) - ) - - -class PercentageCommisionScheme(bt.CommInfoBase): - params = ( - ("commission", 0.004), - ("stocklike", True), - ("commtype", bt.CommInfoBase.COMM_PERC), - ) - - def _getcommission(self, size, price, pseudoexec): - return size * price * self.p.commission + 4 # 290rub/month - - -class FormulaInvesting(bt.Strategy): - def __init__(self): - self.order = None - self.cost = 0 # no comms - self.comms = 0 - self.units = 0 - self.times = 0 - self.periods = 0 - - def log(self, txt, dt=None): - dt = dt or self.datas[0].datetime.date(0) - # print("%s, %s" % (dt.isoformat(), txt)) - - def start(self): - self.broker.set_fundmode(fundmode=True, fundstartval=100.0) - - self.cash_start = self.broker.get_cash() - self.val_start = 100.0 - - # ADD A TIMER - self.add_timer( - when=bt.timer.SESSION_START, - monthdays=[1], - monthcarry=True - # timername='buytimer', - ) - - def notify_order(self, order): - if order.status in [order.Submitted, order.Accepted]: - return - - if order.status in [order.Completed]: - if order.isbuy(): - self.units += order.executed.size - self.cost += order.executed.value - - elif order.issell(): - self.units -= order.executed.size - - self.log( - "%s Price %.2f, Units %.0f, Value %.2f, Comm %.2f, " - % ( - "BUY" if order.isbuy else "SELL", - order.executed.price, - order.executed.size, - order.executed.value, - order.executed.comm, - ) - ) - - self.comms += order.executed.comm - self.times += 1 - - elif order.status in [order.Canceled, order.Margin, order.Rejected]: - self.log("Order Canceled/Margin/Rejected") - print(order.status, [order.Canceled, order.Margin, order.Rejected]) - - self.order = None - - def calc_params(self): - # calculate actual returns - self.froi = self.broker.get_fundvalue() - self.val_start - return ( - # Annual - 100 * ((1 + self.froi / 100) ** (365 / (endDate - actualStart).days) - 1), - self.froi, - self.cost, - self.broker.get_value() + self.broker.get_cash(), - self.times, - self.units, - self.comms, - ) - - def notify_timer(self, timer, when, *args): - self.periods += 1 - self.broker.add_cash(month_sum) - self.formula() - - -class VA(FormulaInvesting): - def formula(self): - target_value = min(self.periods * month_sum - reserve, self.broker.get_value()) - self.order_target_value(target=target_value) - - -class DCA(FormulaInvesting): - def formula(self): - target_value = self.broker.get_value() - reserve - 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() +import strategies as st def run(strategy, data, fund_mode=False): @@ -165,7 +30,7 @@ def run(strategy, data, fund_mode=False): # Broker Information broker_args = dict(coc=True) cerebro.broker = bt.brokers.BackBroker(**broker_args) - comminfo = PercentageCommisionScheme() + comminfo = st.PercentageCommisionScheme() cerebro.broker.addcommissioninfo(comminfo) if fund_mode: @@ -211,7 +76,7 @@ if __name__ == "__main__": actualStart: datetime.datetime = stockData.index[0] data = bt.feeds.PandasData(dataname=stockData) - for i, strategy in enumerate((DCA, QDCA)): # , VA, QVA)): + for i, strategy in enumerate((st.DCA, st.QDCA, st.VA, st.QVA)): therun = run(strategy, data) dd = therun.analyzers.drawdown ret = therun.analyzers.returns @@ -226,5 +91,5 @@ if __name__ == "__main__": ) print("Starting from:", actualStart) print("Time in Market: {:.1f} years".format((endDate - actualStart).days / 365)) - print(df[["annual%", "froi", "cost", "total_value"]]) - # print(df) + # print(df[["annual%", "froi", "cost", "total_value"]]) + print(df) diff --git a/strategies.py b/strategies.py new file mode 100644 index 0000000..70f1100 --- /dev/null +++ b/strategies.py @@ -0,0 +1,146 @@ +import datetime +import math +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from pandas_datareader import data as pdr +import backtrader as bt + + +class LSI(bt.Strategy): + def start(self): + self.val_start = self.broker.get_cash() + + def nextstart(self): + size = math.floor((self.broker.get_cash() - 10) / self.data[0]) + self.buy(size=size) + + def stop(self): + # calculate actual returns + self.roi = (self.broker.get_value() / self.val_start) - 1 + """ + print("Starting Value: ${:,.2f}".format(self.val_start)) + print("ROI: {:.2f}%".format(self.roi * 100.0)) + print( + "Annualised: {:.2f}%".format( + 100 * ((1 + self.roi) ** (365 / (endDate - actualStart).days) - 1) + ) + ) + print( + "Gross Return: ${:,.2f}".format(self.broker.get_value() - self.val_start) + ) + """ + + +class PercentageCommisionScheme(bt.CommInfoBase): + params = ( + ("commission", 0.004), + ("stocklike", True), + ("commtype", bt.CommInfoBase.COMM_PERC), + ) + + def _getcommission(self, size, price, pseudoexec): + return size * price * self.p.commission + 4 # 290rub/month + + +class FormulaInvesting(bt.Strategy): + def __init__(self): + self.order = None + self.cost = 0 # no comms + self.comms = 0 + self.units = 0 + self.times = 0 + self.periods = 0 + + def log(self, txt, dt=None): + dt = dt or self.datas[0].datetime.date(0) + # print("%s, %s" % (dt.isoformat(), txt)) + + def start(self): + self.broker.set_fundmode(fundmode=True, fundstartval=100.0) + + self.cash_start = self.broker.get_cash() + self.val_start = 100.0 + + # ADD A TIMER + self.add_timer( + when=bt.timer.SESSION_START, + monthdays=[1], + monthcarry=True + # timername='buytimer', + ) + + def notify_order(self, order): + if order.status in [order.Submitted, order.Accepted]: + return + + if order.status in [order.Completed]: + if order.isbuy(): + self.units += order.executed.size + self.cost += order.executed.value + + elif order.issell(): + self.units -= order.executed.size + + self.log( + "%s Price %.2f, Units %.0f, Value %.2f, Comm %.2f, " + % ( + "BUY" if order.isbuy else "SELL", + order.executed.price, + order.executed.size, + order.executed.value, + order.executed.comm, + ) + ) + + self.comms += order.executed.comm + self.times += 1 + + elif order.status in [order.Canceled, order.Margin, order.Rejected]: + self.log("Order Canceled/Margin/Rejected") + print(order.status, [order.Canceled, order.Margin, order.Rejected]) + + self.order = None + + def calc_params(self): + # calculate actual returns + self.froi = self.broker.get_fundvalue() - self.val_start + return ( + # Annual + 100 * ((1 + self.froi / 100) ** (365 / (endDate - actualStart).days) - 1), + self.froi, + self.cost, + self.broker.get_value() + self.broker.get_cash(), + self.times, + self.units, + self.comms, + ) + + def notify_timer(self, timer, when, *args): + self.periods += 1 + self.broker.add_cash(month_sum) + self.formula() + + +class VA(FormulaInvesting): + def formula(self): + target_value = min(self.periods * month_sum - reserve, self.broker.get_value()) + self.order_target_value(target=target_value) + + +class DCA(FormulaInvesting): + def formula(self): + target_value = self.broker.get_value() - reserve + 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()