Backtest Period
Markets Traded
Equities
Maximum Drawdown
Period of Rebalancing
Daily
Return (Annual)
Sharpe Ratio
Standard Deviation (Annual)
Original paper
SSRN-id268144.pdf220.7KB
Trading rules
- Focus on announced North American and European mergers
- Target company must have a market cap of over $500 million
- Target must have sufficient trading volume
- Acquirer’s stock must be easy to borrow in stock deals
- Ensure deal has arbitrage potential by checking:
- Positive acquisition premium
- Offer encompasses majority of target’s outstanding shares
- Acquirer does not already own a substantial portion of target’s shares
- Rebalance equal weight portfolio daily
- Utilize websites for updated merger information (e.g., http://www.mergerinvesting.com/pendingmergers)
Python code
Backtrader
import backtrader as bt
import requests
import pandas as pd
class MergerArbitrageStrategy(bt.Strategy):
def __init__(self):
self.pending_mergers_url = "http://www.mergerinvesting.com/pendingmergers"
def next(self):
# Get merger information
merger_data = self.get_merger_data()
# Filter merger information based on conditions
valid_mergers = self.filter_merger_data(merger_data)
# Calculate equal weights for valid mergers
weights = 1.0 / len(valid_mergers)
# Rebalance portfolio
self.rebalance_portfolio(valid_mergers, weights)
def get_merger_data(self):
response = requests.get(self.pending_mergers_url)
merger_data = pd.read_html(response.content)[0]
return merger_data
def filter_merger_data(self, merger_data):
filtered_mergers = merger_data[
(merger_data['Target Market Cap'] > 500e6) &
(merger_data['Target Volume'] > 0) &
(merger_data['Acquirer Borrowable']) &
(merger_data['Premium'] > 0) &
(merger_data['Offer Percentage'] > 0.5) &
(merger_data['Acquirer Ownership'] < 0.5)
]
return filtered_mergers
def rebalance_portfolio(self, valid_mergers, weights):
# Exit all positions
for position in self.getpositions():
self.sell(data=position)
# Enter new positions
for _, merger in valid_mergers.iterrows():
target = merger['Target Ticker']
acquirer = merger['Acquirer Ticker']
target_data = self.getdatabyname(target)
acquirer_data = self.getdatabyname(acquirer)
# Buy target and sell acquirer
self.buy(data=target_data, size=weights * self.broker.getvalue())
self.sell(data=acquirer_data, size=weights * self.broker.getvalue())
# Add data feeds, cerebro setup, and run the strategy
Keep in mind that this code is a basic template for implementing the merger arbitrage strategy in Backtrader, and may require additional modifications depending on the data source and format. The code assumes that the merger_data
DataFrame contains columns for the target market cap, target volume, acquirer borrowability, premium, offer percentage, and acquirer ownership. The columns’ names should be updated to match the actual data source.