DJ Scrooge

A stock backtesting API, designed for hackers who love money

Setup

Make a backtest

1. Create a strategy

You specify a strategy in DJ Scrooge by extending the Strategy class. The execute method gets called once each day by the backtest object. This object stores all the data about the current simulation, like the current simulation date, the portfolio. You call its buy_shares and sell_shares methods to simulation transactions.

class BuyHoldSPY(Strategy):

  def execute(self):
    backtest = self.backtest
    cash = backtest.portfolio.cash
    data = backtest.get_end_of_day('SPY')
    sdate = backtest.simulation_date
    index = data.get_index_from_date(sdate)
    open_price = data.open_prices[index]
    if cash > open_price:
      backtest.buy_shares('SPY', cash / open_price, open_price)

2. Specify the tax rules

Extend the Taxes class to specify how sales and dividends are taxed.

class CaliforniaTaxes(Taxes):
  
  def is_long_term(self, purchase_date):
    if purchase_date.year % 4 == 0:
      delta = timedelta(366)
    else:
      delta = timedelta(365)
    sdate = self.backtest.simulation_date
    one_year_off = purchase_date + delta
    if (sdate >= one_year_off):
      return True
    return False
  
  def sell_tax(self, symbol, shares, gain_per_share, purchase_date):
    tax = 0.35 + 0.093
    if self.is_long_term(purchase_date):
      tax = 0.15 + 0.093
    return int(tax * shares * gain_per_share)

  def dividend_tax(self, symbol, amount, purchase_date):
    tax = 0.35 + 0.093
    if self.is_long_term(purchase_date):
      tax = 0.15 + 0.093
    return int(tax * amount)

3. Specify the commissions

Subclasses of the Commissions class specify the trade commissions and fees of your broker.

class WellsPMACommissions(Commissions):
  
  year = 1900
  trades_this_year = 0
  
  def buy_commissions(self, symbol, shares, price_per_share):
    syear = self.backtest.simulation_date.year
    if self.year != syear:
      self.year = syear
      self.trades_this_year = 0
    self.trades_this_year += 1
    if self.trades_this_year >= 100:
      return 495
    return 0

  def sell_commissions(self, symbol, shares, price_per_share):
    return self.buy_commission(symbol, shares, price_per_share)

4. Put it all together

The Backtest class weaves all these peices together to form a simulation. The chart_backtest method gives a convenient way to see the results. The full code and resulting chart for this example follows:

def main():
  start_date = date(2002,1,1)
  end_date = date(2012, 5, 15)
  buy_hold_test = Backtest(start_date, end_date, commissions_class=WellsPMACommissions, 
                      taxes_class=CaliforniaTaxes, strategy_class=BuyHoldSPY, 
                      end_of_day_class=Yahoo)
  chart_backtest(buy_hold_test, title='Buy & Hold S&P 500 Index (SPY)')
        
if __name__ == '__main__':
    main()

5. Explore the results

Now you can see how well your strategy did.

chart

Learn more