Backtest Period
Markets Traded
Equities
Maximum Drawdown
Period of Rebalancing
Quarterly
Return (Annual)
Sharpe Ratio
Standard Deviation (Annual)
Original paper
SSRN-id1361738.pdf681.1KB
Trading rules
- Investment universe: NYSE, AMEX, and NASDAQ stocks (data from Compustat)
- Share repurchases: Calculated as spending on common and preferred stock repurchases minus any decrease in redeemable preferred stock, scaled by the percentage of shares outstanding
- Non-trivial repurchases: Defined as 0.5% of market capitalization
- Insider transactions: Calculate total dollar values of insider open market purchases and sales for each fiscal quarter
- Net buying insiders: Identified when insider purchases exceed insider sales by at least 0.005% of the firm’s market capitalization
- Stock selection: Buy stocks with non-trivial repurchases and net buying insiders in the previous quarter
- Holding period: Stocks held for one year
- Rebalancing: 1/4 of the portfolio rebalanced each quarter
- Portfolio weighting: Equally weighted stocks
Python code
Backtrader
import backtrader as bt
import datetime
class InsiderRepurchaseStrategy(bt.Strategy):
def __init__(self):
self.share_repurchase = None # To be calculated based on input data
self.insider_transactions = None # To be calculated based on input data
self.non_trivial_repurchase = 0.005
self.net_buying_insiders = 0.00005
def next(self):
to_buy = []
for data in self.datas:
if self.share_repurchase[data] > self.non_trivial_repurchase * data.close[0]:
if self.insider_transactions[data]['purchases'] - self.insider_transactions[data]['sales'] > self.net_buying_insiders * data.close[0] * data.open_interest[0]:
to_buy.append(data)
if to_buy:
self.rebalance_portfolio(to_buy)
def rebalance_portfolio(self, to_buy):
target_weight = 1.0 / len(to_buy)
for data in to_buy:
position_size = target_weight * self.broker.get_cash()
self.order_target_value(data, position_size)
if __name__ == '__main__':
cerebro = bt.Cerebro()
# Add stocks data from NYSE, AMEX, and NASDAQ here
# cerebro.adddata(...)
# cerebro.adddata(...)
# ...
cerebro.addstrategy(InsiderRepurchaseStrategy)
cerebro.broker.set_cash(100000)
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe_ratio')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='draw_down')
cerebro.addsizer(bt.sizers.PercentSizer, percents=25)
results = cerebro.run()
cerebro.plot()
print(f"Sharpe Ratio: {results[0].analyzers.sharpe_ratio.get_analysis()['sharperatio']}")
print(f"Maximum Drawdown: {results[0].analyzers.draw_down.get_analysis()['max']['drawdown']}")
Please note that this code is a basic outline of the strategy and assumes the necessary data is available for share repurchases and insider transactions. You’ll need to provide the data for each stock from the investment universe and also calculate the share_repurchase and insider_transactions variables using the provided data.