Skip to content

Instantly share code, notes, and snippets.

@alxfordy
Last active May 10, 2022 09:54
Show Gist options
  • Save alxfordy/1af1c2404512bf511268307b1c6dbf47 to your computer and use it in GitHub Desktop.
Save alxfordy/1af1c2404512bf511268307b1c6dbf47 to your computer and use it in GitHub Desktop.
Backtrader DCA Strategy
import backtrader as bt
import math
class DollarCostAverage(bt.Strategy):
# This is the amount of money we will add monthly
params = dict(
monthly_cash=1000
)
def __init__(self):
# Define Values to track our strategy
self.order = None
self.total_cost = 0
self.buys = 0
self.sells = 0
self.total_shares = 0
self.com_cost = 0
self.invested_amount = 0
self.months_passed_since_start = 1
def log(self, txt, dt=None):
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def start(self):
# This is called when the strategy starts, after init
#Setup Fund Mode and Initialise the amount of cash available.
self.broker.set_fundmode(fundmode=True, fundstartval=1000.0)
self.cash_start = self.broker.get_cash()
# Order with the starting cash
self.order_target_value(target=self.cash_start)
# Add a timer to trigger on first day of month
# This timer will be used instead of "next" so we don't have to check the date
self.add_timer(
when=bt.timer.SESSION_START,
monthdays=[0],
monthcarry=True
)
def notify_timer(self, timer, when, *args):
# When timer triggers, add monthly cash and buy
self.broker.add_cash(self.p.monthly_cash)
target_value = self.broker.get_value()
self.order_target_value(target=target_value)
self.months_passed_since_start += 1
def notify_order(self, order):
# When an order is created, update our tracking variables
if order.status in [order.Submitted, order.Accepted]:
return
if order.status in [order.Completed]:
if order.isbuy():
self.buys += 1
self.total_shares += order.executed.size
self.invested_amount += order.executed.value
self.total_cost += order.executed.value + order.executed.comm
self.com_cost += order.executed.comm
elif order.issell():
self.sells += 1
self.total_shares -= order.executed.size
self.com_cost += order.executed.comm
self.total_cost += order.executed.comm
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
pass
# self.log('Order Canceled/Margin/Rejected')
# print(order.status, [order.Canceled, order.Margin, order.Rejected])
self.order = None
def stop(self):
# calculate actual returns
self.roi = (self.broker.get_value() / self.cash_start) - 1
self.froi = (self.broker.get_fundvalue() - self.cash_start)
value = self.datas[0].close * self.total_shares + self.broker.get_cash()
print('-'*50)
print('Dollar Cost Averaging')
print(f"Months Passed:\t\t{self.months_passed_since_start}")
print(f'Buy Orders:\t\t{self.buys:.0f}')
print(f'Sell Orders:\t\t{self.sells:.0f}')
print(f'Total Shares:\t\t{self.total_shares:.2f}')
print(f'Total Value:\t\t${value:,.2f}')
print(f'Total Shares Value:\t\t${(self.datas[0].close * self.total_shares):,.2f}')
print(f'Total Cash Value:\t\t${self.broker.get_cash():,.2f}')
print(f'Cost:\t\t${self.total_cost:,.2f}')
print(f'Gross Return:\t\t${(value - self.total_cost):,.2f}')
# print(f'Gross %:\t\t{((value/self.total_cost - 1) * 100):.2f}%')
print(f'ROI:\t\t{(100.0 * self.roi):.2f}%')
print(f'Fund Value:\t\t{self.froi:.2f}%')
print(f'Commission Cost:\t\t${self.com_cost:.2f}')
print('-'*50)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment