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 reserve = 5 # usd for comms etc cause I cant math """ 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.0004), ("stocklike", True), ("commtype", bt.CommInfoBase.COMM_PERC), ) def _getcommission(self, size, price, pseudoexec): return abs(size * price * self.p.commission) + 4 # 290rub/month + 0.04% class Investing(bt.Strategy): def __init__(self, params={"sum": 500, "coef": 1}): self.monthly_params = params self.order = None self.cost = 0 # no comms self.comms = 0 self.units = 0 self.times = 0 self.months = 0 def log(self, txt, dt=None): dt = dt or self.datas[0].datetime.date(0) """ print( "%s, %sm, V:%.2f, C:%.2f, %s" % ( dt.isoformat(), self.months, self.broker.get_value(), self.broker.get_cash(), 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 else: 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, ) ) 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.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, order.status, [order.Canceled, order.Margin, order.Rejected]) self.order = None @property def monthly_cash(self): return self.monthly_params["sum"] * self.monthly_params["coef"] ** (self.months) def calc_params(self): # calculate actual returns self.froi = self.broker.get_fundvalue() - self.val_start return [ self.froi, self.cost, self.broker.get_value(), self.times, self.units, self.comms, self.monthly_cash, ] def notify_timer(self, timer, when, *args): self.months += 1 self.broker.add_cash(self.monthly_cash) class FormulaInvesting(Investing): def notify_timer(self, timer, when, *args): super().notify_timer(timer, when, *args) self.formula() self.prev_value = self.broker.get_value() class VA(FormulaInvesting): """ When market is down, buy When up, sell. Shitty strategy: https://en.wikipedia.org/wiki/Value_averaging """ def formula(self): target_value = ( min( self.monthly_cash * self.months, self.broker.get_value(), ) - reserve ) self.order_target_value(target=target_value), # self.broker.set_cash(self.broker.get_cash() * self.monthly_params["t_rate"]) class QVA(VA): """ Same but quarterly """ def formula(self): if not self.months % 3: super().formula() class DCA(FormulaInvesting): """ Buy everything monthly """ def formula(self): target_value = self.broker.get_value() - reserve self.order_target_value(target=target_value) class QDCA(DCA): """ Buy everything quarterly """ def formula(self): if not self.months % 3: super().formula() class EDCA(Investing): """ When market is down, BUY THE DIP When up, leave some cash """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.prev_value = 0 def notify_timer(self, timer, when, *args): self.months += 1 if self.prev_value >= self.broker.get_value(): self.broker.add_cash(1.1 * self.monthly_cash) else: self.broker.add_cash(0.9 * self.monthly_cash) target_value = self.broker.get_value() self.order_target_value(target=target_value - reserve) self.prev_value = self.broker.get_value() class SmaCross(Investing): """ Buy when fast moving average crosses slow upwards """ params = dict( pfast=8, # period for the fast moving average pslow=17, # period for the slow moving average ) # list of parameters which are configurable for the strategy def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) sma1 = bt.ind.SMA(period=self.p.pfast) # fast moving average sma2 = bt.ind.SMA(period=self.p.pslow) # slow moving average self.crossover = bt.ind.CrossOver(sma1, sma2) # crossover signal def next(self): if self.crossover > 0: # if fast crosses slow to the upside self.order_target_value( target=self.broker.get_value() - reserve ) # enter long