Backtest Period
2001-2011
Markets Traded
Equities
Maximum Drawdown
Period of Rebalancing
Monthly
Return (Annual)
23.09%
Sharpe Ratio
Standard Deviation (Annual)
Original paper
SSRN-id2042657.pdf1383.9KB
Trading rules
- Investment Universe: All US stocks excluding illiquid stocks (priced under $0.5) and closed-end funds
- Monthly Selection: Choose stocks with 50% or greater losses in the last 500 trading days compared to S&P 500 index
- Classification: Rank stocks based on Debt/Equity ratio within their industry
- Selection Criteria: Pick stocks with Debt/Equity ratio within the lowest 10% of the industry
- Portfolio Rebalancing: Conduct monthly rebalancing
- Stock Weighting: Apply equal weight to all stocks in the portfolio
Python code
Backtrader
import backtrader as bt
import pandas as pd
class FallingKnifeStrategy(bt.Strategy):
def __init__(self):
self.spy = self.datas_dict['SPY']
self.stocks = self.datas[1:]
def next(self):
if self.spy.datetime.date(0).month != self.spy.datetime.date(-1).month:
# Calculate performance for each stock
stock_performance = []
for stock in self.stocks:
stock_return = (stock.close[0] - stock.close[-500]) / stock.close[-500]
spy_return = (self.spy.close[0] - self.spy.close[-500]) / self.spy.close[-500]
relative_return = stock_return - spy_return
stock_performance.append((stock, relative_return))
# Filter stocks with losses greater than 50% compared to S&P 500 index
filtered_stocks = [stock for stock, perf in stock_performance if perf <= -0.5]
# Rank stocks based on Debt/Equity ratio within their industry
ranked_stocks = sorted(filtered_stocks, key=lambda stock: stock.debt_equity_ratio[0])
# Select stocks with Debt/Equity ratio within the lowest 10% of the industry
selected_stocks = ranked_stocks[:int(len(ranked_stocks) * 0.1)]
# Rebalance portfolio with equal weight
target_weight = 1 / len(selected_stocks)
for stock in self.stocks:
if stock in selected_stocks:
self.order_target_percent(stock, target_weight)
else:
self.order_target_percent(stock, 0)
cerebro = bt.Cerebro()
cerebro.broker.set_cash(100000)
# Add data feeds
spy_data = bt.feeds.YahooFinanceData(dataname='SPY', fromdate='2000-01-01', todate='2021-12-31')
cerebro.adddata(spy_data, name='SPY')
# Load stock data and add to cerebro
stocks_list = [...] # Replace with a list of stock symbols
for symbol in stocks_list:
data = bt.feeds.YahooFinanceData(dataname=symbol, fromdate='2000-01-01', todate='2021-12-31')
cerebro.adddata(data, name=symbol)
cerebro.addstrategy(FallingKnifeStrategy)
cerebro.run()
Note: You’ll need to replace stocks_list
with a list of US stock symbols excluding illiquid stocks and closed-end funds. Also, you need to add the Debt/Equity ratio to the data feed for each stock.