With the rise of algorithmic trading, Python has become an essential tool for quantitative developers. This is largely thanks to Python’s powerful ecosystem in scientific computing and data analysis, along with support from excellent third-party libraries. Libraries like Pandas, NumPy, and SciPy offer rich functionalities for data manipulation, numerical computations, and scientific analysis, allowing developers to perform quantitative research and strategy development more efficiently. Today, we’ll look at five classic quantitative trading strategies, along with their corresponding Python code examples.
#1 Mean Reversion Strategy
The mean reversion strategy is a type of statistical arbitrage based on the assumption that asset prices tend to fluctuate around a long-term average. Regardless of short-term volatility, prices are expected to revert to their mean over time. Below is a simple Python example of a mean reversion strategy, where we define the “mean” of the asset using a simple moving average and use the standard deviation to generate buy/sell signals:
import numpy as np import pandas as pd import matplotlib.pyplot as plt data = pd.DataFrame({ 'Date': pd.date_range(start='2023-01-01', periods=100), 'Close': np.random.normal(100, 10, 100) }) data.set_index('Date', inplace=True) window = 20 data['Moving Average'] = data['Close'].rolling(window=window).mean() data['Standard Deviation'] = data['Close'].rolling(window=window).std() data['Upper Bound'] = data['Moving Average'] + data['Standard Deviation'] data['Lower Bound'] = data['Moving Average'] - data['Standard Deviation'] data['Position'] = 0 data.loc[data['Close'] < data['Lower Bound'], 'Position'] = 1 data.loc[data['Close'] > data['Upper Bound'], 'Position'] = -1 plt.figure(figsize=(14, 7)) plt.plot(data['Close'], label='Close Price') plt.plot(data['Moving Average'], label='Moving Average') plt.fill_between(data.index, data['Upper Bound'], data['Lower Bound'], color='gray', alpha=0.3, label='Mean Reversion Band') plt.plot(data.index, data['Position'] * 50, label='Trading Signal', color='magenta') plt.legend() plt.show()
#2 Trend Following Strategy
Trend following involves comparing an asset’s short-term and long-term moving averages to identify the prevailing market trend and follow it until a reversal occurs. Simply put, if a stock is trending upward and the market sentiment is bullish, we follow the trend by buying and holding the stock until the trend reverses. In Python, this can be implemented using the Moving Average Convergence Divergence (MACD) indicator to identify short-term trends and generate corresponding buy/sell signals:
import numpy as np import pandas as pd import matplotlib.pyplot as plt data = pd.DataFrame({ 'Date': pd.date_range(start='2023-01-01', periods=200), 'Close': np.random.normal(100, 15, 200) }) data.set_index('Date', inplace=True) short_window = 40 long_window = 100 data['Short MA'] = data['Close'].rolling(window=short_window).mean() data['Long MA'] = data['Close'].rolling(window=long_window).mean() data['Signal'] = 0 data['Signal'][short_window:] = np.where(data['Short MA'][short_window:] > data['Long MA'][short_window:], 1, 0) data['Position'] = data['Signal'].diff() plt.figure(figsize=(14, 7)) plt.plot(data['Close'], label='Close Price') plt.plot(data['Short MA'], label='40-Day Moving Average') plt.plot(data['Long MA'], label='100-Day Moving Average') plt.plot(data.index, data['Position'] * 50, label='Trading Signal', color='magenta', marker='o', linestyle='None') plt.legend() plt.show()
#3 Pair Trading
Pair trading is a statistical arbitrage strategy based on the price difference between two strongly correlated assets. When the price spread between them deviates beyond a normal range, we buy the undervalued asset and sell the overvalued one. Over the long run, their prices should revert to the mean, presenting short-term opportunities for arbitrage.
We can analyze the historical price relationship between two assets and create trading signals based on deviations from the expected spread:
import numpy as np import pandas as pd import matplotlib.pyplot as plt np.random.seed(42) data = pd.DataFrame({ 'Date': pd.date_range(start='2023-01-01', periods=180), 'Asset_A': np.random.normal(100, 10, 180).cumsum() + 100, 'Asset_B': np.random.normal(100, 10, 180).cumsum() + 120 }) data.set_index('Date', inplace=True) data['Price_Diff'] = data['Asset_A'] - data['Asset_B'] window = 30 data['Mean_Diff'] = data['Price_Diff'].rolling(window=window).mean() data['Std_Diff'] = data['Price_Diff'].rolling(window=window).std() data['Upper_Bound'] = data['Mean_Diff'] + data['Std_Diff'] data['Lower_Bound'] = data['Mean_Diff'] - data['Std_Diff'] data['Position'] = 0 data.loc[data['Price_Diff'] > data['Upper_Bound'], 'Position'] = -1 # 做空Asset A,做多Asset B data.loc[data['Price_Diff'] < data['Lower_Bound'], 'Position'] = 1 # 做多Asset A,做空Asset B plt.figure(figsize=(14, 7)) plt.subplot(211) plt.plot(data['Asset_A'], label='Asset A') plt.plot(data['Asset_B'], label='Asset B') plt.legend() plt.subplot(212) plt.plot(data['Price_Diff'], label='Price Difference') plt.plot(data['Mean_Diff'], label='Mean Difference') plt.fill_between(data.index, data['Upper_Bound'], data['Lower_Bound'], color='gray', alpha=0.3, label='Trading Zone') plt.plot(data.index, data['Position'] * 20, label='Trading Signal', color='magenta', marker='o', linestyle='None') plt.legend() plt.show()
#4 Statistical Arbitrage
Statistical arbitrage involves exploiting price discrepancies among multiple assets. A common method is to identify pairs or groups of stocks that deviate from their historical relationships and trade them accordingly. Below is a basic example of a statistical arbitrage strategy in Python, based on the price spread between two stocks:
import numpy as np import pandas as pd import matplotlib.pyplot as plt np.random.seed(42) data = pd.DataFrame({ 'Date': pd.date_range(start='2023-01-01', periods=250), 'Stock_A': np.random.normal(0, 1, 250).cumsum() + 50, 'Stock_B': np.random.normal(0, 1, 250).cumsum() + 50 }) data.set_index('Date', inplace=True) data['Spread'] = data['Stock_A'] - data['Stock_B'] window = 20 data['Spread Mean'] = data['Spread'].rolling(window=window).mean() data['Spread Std'] = data['Spread'].rolling(window=window).std() entry_z = 2 exit_z = 0 data['Upper Threshold'] = data['Spread Mean'] + entry_z * data['Spread Std'] data['Lower Threshold'] = data['Spread Mean'] - entry_z * data['Spread Std'] data['Exit Threshold'] = data['Spread Mean'] + exit_z * data['Spread Std'] data['Position'] = 0 data.loc[data['Spread'] > data['Upper Threshold'], 'Position'] = -1 # 做空Stock A,做多Stock B data.loc[data['Spread'] < data['Lower Threshold'], 'Position'] = 1 # 做多Stock A,做空Stock B data.loc[data['Spread'] * data['Position'] < data['Exit Threshold'], 'Position'] = 0 # 退出信号 plt.figure(figsize=(14, 7)) plt.subplot(211) plt.plot(data['Stock_A'], label='Stock A') plt.plot(data['Stock_B'], label='Stock B') plt.title('Stock Prices') plt.legend() plt.subplot(212) plt.plot(data['Spread'], label='Spread') plt.plot(data['Spread Mean'], label='Mean Spread') plt.fill_between(data.index, data['Upper Threshold'], data['Lower Threshold'], color='gray', alpha=0.3, label='Entry Zone') plt.plot(data.index, data['Position'] * 10, label='Trading Signal', color='magenta', marker='o', linestyle='None') plt.title('Spread and Trading Signals') plt.legend() plt.show()
#5 Volatility Trading
Volatility trading strategies aim to profit from changes in market volatility. For example, we can calculate the daily returns and historical volatility (i.e., annualized standard deviation) of a stock, then set conditions such as: sell when volatility exceeds 1.2 times the average, and buy when it falls below 0.8 times the average. Here’s the corresponding Python implementation:
import numpy as np import pandas as pd import matplotlib.pyplot as plt np.random.seed(42) dates = pd.date_range(start='2023-01-01', periods=250) prices = np.random.normal(0, 1, 250).cumsum() + 100 data = pd.DataFrame({ 'Date': dates, 'Price': prices }) data.set_index('Date', inplace=True) data['Returns'] = data['Price'].pct_change() data.dropna(inplace=True) window = 20 data['Volatility'] = data['Returns'].rolling(window=window).std() * np.sqrt(252) # 年化波动性 threshold_high = data['Volatility'].mean() * 1.2 threshold_low = data['Volatility'].mean() * 0.8 data['Position'] = 0 data.loc[data['Volatility'] > threshold_high, 'Position'] = -1 data.loc[data['Volatility'] < threshold_low, 'Position'] = 1 plt.figure(figsize=(14, 10)) plt.subplot(211) plt.plot(data['Price'], label='Price') plt.title('Stock Price') plt.legend() plt.subplot(212) plt.plot(data['Volatility'], label='Volatility') plt.axhline(y=threshold_high, color='r', linestyle='--', label='High Threshold') plt.axhline(y=threshold_low, color='g', linestyle='--', label='Low Threshold') plt.plot(data.index, data['Position'] * 0.01, label='Trading Signal', color='magenta', marker='o', linestyle='None') plt.title('Volatility and Trading Signals') plt.legend() plt.show()