Building A Merger Arbitrage Trading Strategy with Python
- Nikhil Adithyan
- 1 hour ago
- 8 min read
An event-driven M&A quantitative strategy

In a world obsessed with predicting the next market move, there’s a certain elegance in strategies that don’t try. Event-driven arbitrage, particularly merger arbitrage, belongs to this quiet, process-driven corner of finance. It’s less about outsmarting the market and more about systematically capturing a well-defined risk premium: the spread between a target company’s current price and the acquirer’s offer price.
The logic is simple enough to sketch on a napkin. When Company A announces plans to acquire Company B at a fixed price, B’s shares jump — but not all the way to the offer level. The remaining gap reflects uncertainty: Will regulators approve? Will financing hold? Will the deal even close? Arbitrageurs step in to earn that spread, accepting deal risk in exchange for a potential premium that, over time, tends to stay positive. It is this risk that calls for a positive premium.
The goal here isn’t to build a magic alpha engine. It’s to construct a disciplined, transparent framework — one that uses data, structure, and repetition to systematically identify and act on these opportunities. With the EODHD API providing daily market data and corporate event feeds, we can design a live script that scans for merger announcements and allocates accordingly.
This is not a quest for market-beating returns, but for consistency. A strategy that stands apart from market beta, grounded in the logic of transaction risk rather than price momentum. It’s the financial equivalent of a craftsman’s work: steady, incremental, and cumulative.
The challenge, as always, lies in execution: liquidity constraints, deal timing, and incomplete information. Yet for the inquisitive quant, this is where the intellectual satisfaction resides — in transforming event uncertainty into a systematic, repeatable process. Arbitrage, after all, is less about prediction and more about process.
Disclaimer: This exploration is for educational and research purposes only and does not constitute financial advice. Trading or implementing event-driven strategies involves significant risk, including potential loss of capital.
Designing a Simple, Long-Only Strategy
In the spirit of disciplined simplicity, the goal is to construct a trading rule that responds to corporate event activity without drifting into prediction. The core assumption is straightforward: when merger and acquisition (M&A) news becomes more frequent, it signals heightened corporate confidence and potential short-term revaluation dynamics in equity markets. Rather than forecast which deal will succeed or which company will rally, we position to capture the general risk premium that tends to accompany active M&A periods.
The universe is intentionally broad — the strategy should work in any equity market where reliable daily news and price data are available. This makes the framework portable, not tied to a single geography or index. The only requirement is access to an updated feed of corporate news tagged with “M&A,” which the EODHD API can provide.
The exposure mechanism is binary and event-driven. Each day, the system counts the number of MA-tagged news items published in the previous 21 trading days — effectively, one market month of information flow. When the count exceeds five, we interpret it as a signal of increased event intensity. The rule is simple: if that threshold is met, we deploy capital across equities; if not, we stay in cash.
Capital allocation follows an equally weighted approach, with each position representing 5% of the total portfolio capital. This keeps the system balanced and transparent: the portfolio automatically adapts to the number of available opportunities, while ensuring that no single name dominates exposure. The model rebalances daily to maintain these weights and to adjust as the news count fluctuates.
This design emphasizes structure over speculation. There are no forecasts, no sentiment scores, and no hidden factors — only the mechanical observation of event frequency. It’s an approach that recognizes a fundamental rhythm in markets: when corporate deal-making accelerates, so does risk-taking appetite. The strategy seeks to harvest that rhythm in its simplest, most transparent form.
From Headlines to Trades with the EODHD API
Having the strategic logic set, the next step is to make it operational. The aim is not to engineer a complex trading engine, but to create a reliable daily script that can scan for relevant M&A activity. The intensity of corporate events must be assessing, to decide whether the portfolio should be invested or parked in cash.
The EODHD API makes this straightforward. It provides both news data and exchange symbol lists, allowing us to define a global, dynamic universe and monitor recent news. The process rests on two pillars: building the universe and fetching the news.
Building the Trading Universe
Using the EODHD endpoint for exchange symbol lists, we can extract all active listings from any exchange (for example, NYSE). From there, we isolate ISINs to create a set with each unique instrument.
API_KEY = 'YOUR_API_KEY'
import requests
def get_exchange_isins(exchange):
url = f'https://eodhd.com/api/exchange-symbol-list/{exchange}?api_token={API_KEY}&fmt=json'
data = requests.get(url).json()
isins = [stock_info['Isin'] for stock_info in data]
isins = set([isin for isin in isins if isin is not None])
return isins
isins = get_exchange_isins('NYSE')
print(isins)
{'US25434V6829', 'US16115Q3083', ..., 'US08774B5084',}
This provides a clean, up-to-date list of tradable securities. The same logic can be applied to other exchanges, allowing the system to adapt to any market. From these ISINs we can then create a set of associated ticker.
def get_symbol(isin):
url = f'https://eodhd.com/api/id-mapping?filter[isin]={isin}&page[limit]=1&page[offset]=0&api_token={API_KEY}&fmt=json'
return requests.get(url).json()['data'][0]['symbol']
symbols = {get_symbol(isin) for isin in isins}
print(symbols)
{'DFSE.US', 'BETR.US', ..., 'GTLS.US'}
Fetching and Screening M&A News
Next, we focus on the event signals. The goal is to create a measure of the likelihood of an M&A deal. Since the strategy aims only to capture the associated risk premium, using lastest headlines should be a good start. Using the EODHD news endpoint, the system queries the latest items for each ticker and stores them locally.
from eodhd import APIClient
import pandas as pd
MERGERS_TAGS = ['MERGERS-ACQUISITIONS', 'M&A']
api = APIClient(API_KEY)
def get_news(ticker):
url = f'https://eodhd.com/api/news?s={ticker}&offset=0&limit=1000&api_token={API_KEY}&fmt=json'
return requests.get(url).json()
news = {symbol:get_news(symbol) for symbol in symbols}
Each day, this script will update the news dictionary with publication date and tag, which can then be use. In this article, we consider the following strategy: if the count of M&A-tagged articles within the last 21 trading days exceeds five, the strategy switches to active exposure mode — deploying capital according to the rule defined earlier (5% of the capital under management).
Overall Pipeline
With both the universe and the event stream in place, the daily workflow becomes fully automated:
Update the exchange list and filter valid securities.
Pull recent news and count M&A-tagged items.
Determine whether exposure should be active or neutral.
Trade accordingly
This minimal infrastructure provides a transparent, rule-based link between corporate activity and portfolio exposure. No forecasting, no sentiment scoring, just consistent observation. It transforms raw event data into a structured trading discipline, ready to be tested in real time.
A Case Study on Warner Bros. Discovery
Steps 3 and 4 in the pipeline focus on a single stock rather than the entire universe. To keep the process transparent, we illustrate and test their implementation using one concrete example — Warner Bros. Discovery (WBD). This focused approach allows us to validate the mechanics of the strategy in detail: how exposure is triggered, how news flow translates into trading signals, and how daily rebalancing behaves in practice.
Once the logic is confirmed on this single name, it can later be scaled to the broader universe. Starting small ensures that the foundation — data handling, timing alignment, and exposure control — is solid before extending it to multiple symbols and markets.
import pandas as pd
import pandas_market_calendars as mcal
from datetime import timedelta, datetime
import pytz
wbd_news = get_news('WBD.US')
data = pd.DataFrame(wbd_news)[['date', 'tags']]
data['date'] = pd.to_datetime(data['date'])
data = data.sort_values(by='date')
data = data.reset_index(drop=True)
data['merger_announced'] = data['tags'].apply(lambda x: 'MERGERS-ACQUISITIONS' in x or 'M&A' in x).astype(int)
A key step in building a usable dataset is cleaning it into something the strategy can actually trade on. News arrives at irregular times — sometimes mid-morning, sometimes late at night — but the market only reacts during trading hours. A headline published at 10 p.m. isn’t actionable until the next session, so we need to align every piece of information with the correct trading day.
This means restructuring the dataset so that each observation corresponds to a market date rather than a raw timestamp. We also fill in any missing sessions — days when no news was published but trading still occurred — to maintain a continuous daily sequence. Once this adjustment is done, each trading day has a clear, consolidated view of event activity: the number of M&A items that could have been acted upon that morning.
The result is a clean, reliable foundation — a dataset that reflects what could actually have been known at each trading open, turning the chaotic stream of headlines into an orderly series fit for systematic decisions.
data['adjusted_date'] = data['date'].apply(adjust_to_trading_day)
data = data.groupby('adjusted_date')[['merger_announced']].sum()
trading_days = schedule.index.normalize() # Series of valid trading dates
# Reindex DataFrame to include all trading days
data = data.reindex(trading_days)
data.index.name = 'date'
data = data.fillna(0)
We can now look at our signal.
import seaborn as sns
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, figsize=(7, 5))
sns.lineplot(x=data.index, y=[5 for _ in range(len(data))], color='red', ax=ax, label='activation threshold')
sns.lineplot(x=data.index, y=data['merger_announced'].rolling(21, min_periods=0).sum(), ax=ax, label='signal')
ax.lines[0].set_linestyle("--")
ax.set_ylabel('')
ax.set_title('WBD signal', fontweight='bold')
plt.show()

Using the EODHD historical stock data API, we can retrieve historical return data for Warner Bros. Discovery and analyze how the strategy would have behaved in the past. We implement our trading strategy, where we enter and exit trades according to the signal value.
def get_stock_data(symbol):
url = f'https://eodhd.com/api/eod/{symbol}?api_token={API_KEY}&fmt=json'
return requests.get(url).json()
stock_prices = get_stock_data('WBD.US')
stock_prices = pd.DataFrame(stock_prices)
stock_prices['date'] = pd.to_datetime(stock_prices['date'])
stock_prices = stock_prices.set_index('date')
df = data.join(stock_prices, how='left')
df['signal'] = df['merger_announced'].rolling(21, min_periods=0).sum()
portfolio_value = [0]
trades = []
open_position = 0 # 0 = no position, 1 = open position
current_value = 0
for _, row in df.iterrows():
close_price = row['close']
signal = row['signal']
if open_position == 1:
# Track portfolio value as current close
portfolio_value.append(close_price)
# Exit condition
if signal < 5:
open_position = 0
else:
# Entry condition
if signal >= 5:
open_position = 1
# Keep last value if no open position
portfolio_value.append(portfolio_value[-1])
trades.append(open_position)
# Optional: attach results back to DataFrame
df['portfolio_value'] = portfolio_value[1:] # drop the initial placeholder
df['open_position'] = trades
Let’s take a look at the strategy performance.
fig, ax1 = plt.subplots(figsize=(10, 5))
# Left axis → portfolio value
color = 'tab:blue'
ax1.set_xlabel('Date')
ax1.set_ylabel('Portfolio Value', color=color)
ax1.plot(df.index, df['portfolio_value'], color=color, linewidth=2, label='Portfolio Value')
ax1.tick_params(axis='y', labelcolor=color)
# Right axis → open/closed position (0 or 1)
ax2 = ax1.twinx() # create secondary y-axis
color = 'tab:orange'
ax2.set_ylabel('Position', color=color)
ax2.plot(df.index, df['open_position'], color=color, linestyle='--', linewidth=1.5, label='Position')
ax2.tick_params(axis='y', labelcolor=color)
ax2.set_ylim(-0.1, 1.1) # for clarity (binary variable)
# Optional formatting
fig.suptitle('Portfolio Value and Position Status Over Time', fontsize=14, fontweight='bold')
# Combine legends from both axes
lines_1, labels_1 = ax1.get_legend_handles_labels()
lines_2, labels_2 = ax2.get_legend_handles_labels()
ax1.legend(lines_1 + lines_2, labels_1 + labels_2, loc='upper left')
plt.tight_layout()
plt.show()
Conclusion
What begins as a collection of scattered headlines can, with a bit of structure, become a systematic framework for decision-making. By using the EODHD API to bridge market data and corporate news, we’ve shown that it’s possible to turn qualitative “event noise” into a quantitative trading process.
The Warner Bros. Discovery case study illustrates that merger activity is not just an occasional headline — it’s a recurring rhythm in market behavior. Each announcement carries a measurable signal, and when those signals accumulate, they reveal something about corporate confidence, liquidity conditions, and the broader appetite for risk.
The strategy outlined here is intentionally simple: no predictive models, no black-box optimizers, just rules. Its value lies not in forecasting which deal will succeed, but in consistently responding to observable patterns in market events. Over time, such repetition builds discipline — a key ingredient of any sustainable quantitative approach.
Ultimately, merger arbitrage and related event-driven strategies remind us that finance isn’t always about speed or prediction. Sometimes, it’s about patience, clarity, and the quiet elegance of turning uncertainty into structure.