← Back to Algorithmic Glossary

Arbitrage in Python

The simultaneous purchase and sale of equivalent assets in different markets to profit from temporary price discrepancies with theoretically zero risk.

Definition

Pure Arbitrage is the simultaneous exploitation of price discrepancies of identical or theoretically equivalent assets across different venues, instruments, or time horizons. In classical finance theory, arbitrage is riskless and self-correcting — as arbitrageurs trade, price discrepancies are eliminated. In practice, all real-world arbitrage involves residual risks: execution risk (prices move before both legs are filled), model risk (the equivalence assumption fails), liquidity risk (positions cannot be unwound), and capital risk (margin calls during temporary spread widening). Modern quantitative arbitrage encompasses statistical arbitrage, convertible bond arbitrage, merger arbitrage, and cross-exchange crypto arbitrage.

Quantitative Formula

Π=PsellPbuyCtransaction>0\Pi = P_{sell} - P_{buy} - C_{transaction} > 0

Where Π\Pi is the riskless profit, PsellP_{sell} is the price received from the short leg, PbuyP_{buy} is the cost of the long leg in an equivalent instrument, and CtransactionC_{transaction} encompasses all round-trip transaction costs (commissions, spread, market impact, and financing costs). The no-arbitrage condition requires Π0\Pi \leq 0 in efficient markets. When Π>0\Pi > 0, a genuine arbitrage opportunity exists and should be exploited until the spread compresses to zero.

Why It Matters in Backtesting

In backtesting arbitrage strategies, the critical failure mode is ignoring execution simultaneity — assuming both legs fill at the observed prices simultaneously when in reality there is always latency between legs. Cross-exchange arbitrage that looks massively profitable on daily OHLC data is frequently zero-sum or negative after accounting for the microsecond-level execution infrastructure required. A valid arbitrage backtest must model partial fills, leg-in/leg-out risk, and the capital cost of holding open positions when one leg fills and the other does not.

Python Implementation

import numpy as np
    import pandas as pd

    def statistical_arbitrage_backtest(price_a: pd.Series, price_b: pd.Series,
                                        hedge_ratio: float = None,
                                        entry_z: float = 2.0,
                                        transaction_cost_bps: float = 10.0) -> dict:
        """
        Backtests a two-leg statistical arbitrage strategy with spread z-score signals.
        Automatically estimates hedge ratio via OLS if not provided.
        """
        if hedge_ratio is None:
            hedge_ratio = np.polyfit(price_b.dropna(), price_a.dropna(), 1)[0]
        spread = price_a - hedge_ratio * price_b
        spread_mean = spread.rolling(60).mean()
        spread_std = spread.rolling(60).std()
        z_score = (spread - spread_mean) / spread_std
        position = pd.Series(0.0, index=spread.index)
        position[z_score > entry_z] = -1.0   # Short spread: sell A, buy B
        position[z_score < -entry_z] = 1.0   # Long spread: buy A, sell B
        position[(z_score.abs() < 0.5)] = 0.0
        position = position.ffill()
        spread_returns = spread.pct_change()
        gross_returns = position.shift(1) * spread_returns
        # Apply transaction costs on position changes
        trades = position.diff().abs()
        net_returns = gross_returns - (trades * transaction_cost_bps / 10000)
        return {
            "net_returns": net_returns,
            "gross_returns": gross_returns,
            "spread": spread,
            "z_score": z_score,
            "hedge_ratio": hedge_ratio,
            "total_cost_drag": (trades * transaction_cost_bps / 10000).sum(),
            "sharpe_net": net_returns.mean() / net_returns.std() * np.sqrt(252)
        }

Test this in a live environment

Stop running Jupyter notebooks locally. Paste this Arbitrage code directly into Valetha's Strategy Lab and run a full historical backtest in seconds.

Open the Python Strategy Lab

Ready to find your edge ?

Start for Free