Original paper
Abstract
This paper uses performance-evaluation methodology to estimate the returns earned by insiders when they trade their company's stock. Our methods are designed to estimate the returns earned by insiders themselves and thereby differ from the previous insider-trading literature, which focuses on the informativeness of insider trades for other investors. We find that insider purchases earn abnormal returns of more than 6 percent per year, and insider sales do not earn significant abnormal returns. We compute that the expected costs of insider trading to non-insiders are about 10 cents for a $10,000 transaction.
Trading rules
- Target stocks from NYSE, AMEX, NASDAQ priced above $2, but exclude closed-end funds, REITs, and ADRs.
- At April's end, determine the Net Purchase Ratio (NPR) for every stock, considering the past half-year.
- NPR is derived from: (Total insider purchases - Total insider sells) / Total transactions.
- Order stocks into ten groups according to their NPR values.
- Initiate a long position on the decile with the highest NPR and a short position on the one with the lowest.
- Rebalance yearly.
Python code
Backtrader
import backtrader as bt
import pandas as pd
import requests
class InsiderStrategy(bt.Strategy):
def __init__(self):
self.insider_data = None
def next(self):
if self.datetime.date().month == 4 and self.datetime.date().day == 30:
self.rebalance()
def rebalance(self):
self.rank_stocks()
long_stocks = self.insider_data.nlargest(10, 'NPR')
short_stocks = self.insider_data.nsmallest(10, 'NPR')
for stock in long_stocks['symbol']:
data = self.getdatabyname(stock)
size = self.broker.get_cash() * 0.1 / data.close[0]
self.buy(data=data, size=size)
for stock in short_stocks['symbol']:
data = self.getdatabyname(stock)
size = self.broker.get_cash() * 0.1 / data.close[0]
self.sell(data=data, size=size)
def rank_stocks(self):
self.insider_data = self.get_insider_data()
self.insider_data['NPR'] = (self.insider_data['purchases'] - self.insider_data['sells']) / self.insider_data['transactions']
self.insider_data.sort_values(by='NPR', ascending=False, inplace=True)
def get_insider_data(self):
# Implement data fetching from external sources and pre-processing as needed
pass
if __name__ == '__main__':
cerebro = bt.Cerebro()
# Implement data loading and filtering based on the specified criteria
# Replace 'filtered_data' with the actual data source
for symbol, data in filtered_data.items():
cerebro.adddata(data, name=symbol)
cerebro.addstrategy(InsiderStrategy)
cerebro.broker.set_cash(100000)
cerebro.broker.setcommission(commission=0.001)
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
results = cerebro.run()
final_value = results[0].broker.get_value()
print(f'Final Portfolio Value: {final_value}')
print('Sharpe Ratio:', results[0].analyzers.sharpe.get_analysis()['sharperatio'])
Note: This code is a template for the given strategy. You will need to implement the get_insider_data
function to fetch the insider trading data from an external source and preprocess it to calculate the NPR. Additionally, you will need to load and filter the stock data based on the specified criteria, and replace filtered_data
with the actual data source.