
A Quantitative Strategy Based on the Momentum Effect
The momentum effect is a common phenomenon in financial markets, referring to the tendency for assets that performed well in the past to continue performing well for a period of time in the future. This article will introduce a quantitative strategy based on the momentum effect and use…
The momentum effect is a common phenomenon in financial markets, referring to the tendency for assets that performed well in the past to continue performing well for a period of time in the future. This article will introduce a quantitative strategy based on the momentum effect and use Python code to demonstrate its implementation.
Strategy Logic
The core logic of the strategy is:Select the N stocks with the largest gains over a past period and hold them for a future period, expecting them to continue their upward trend.
Data Preparation
First, we obtain historical stock price data from theAllTickAPI.
import time
import requests
import pandas as pd
# Fetch historical candlestick data
def get_historical_data(symbol, kline_type=1, kline_timestamp_end=0, query_kline_num=100, adjust_type=0):
url = 'http://quote.alltick.io/quote-stock-b-api/kline'
token = 'YOUR_API_TOKEN'
# Build query parameters
query = {
"trace": "python_http_test1",
"data": {
"code": symbol,
"kline_type": kline_type, # Candlestick type: 1 means daily
"kline_timestamp_end": kline_timestamp_end, # End timestamp; 0 means latest
"query_kline_num": query_kline_num, # Number of candlesticks to retrieve
"adjust_type": adjust_type # Adjustment type: 0 means unadjusted
}
}
# Send request
response = requests.get(
url=url,
params={"token": token, "query": json.dumps(query)},
headers={'Content-Type': 'application/json'}
)
# Parse response data
if response.status_code == 200:
data = response.json()
if data.get("code") == 0: # Check whether the request succeeded
kline_data = data["data"]["kline"]
df = pd.DataFrame(kline_data, columns=["timestamp", "open", "high", "low", "close", "volume"])
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="s") # Convert the timestamp to a date
df.set_index("timestamp", inplace=True)
return df
else:
print(f"Error: {data.get('message')}")
return None
else:
print(f"Request failed with status code: {response.status_code}")
return None
# Fetch historical data for multiple stocks
symbols = ["700.HK", "UNH.US", "AAPL.US"] # Example stock symbols
start_date = "2022-01-01"
end_date = "2023-01-01"
# Fetch historical data and calculate returns
data = {}
for symbol in symbols:
df = get_historical_data(symbol, query_kline_num=252) # Fetch 252 trading days of data
if df is not None:
df['returns'] = df['close'].pct_change() # Calculate daily returns
data[symbol] = df
# Combine all stocks into one DataFrame
all_data = pd.concat(data.values(), keys=data.keys(), names=['symbol'])
Momentum Calculation
Next, we calculate the cumulative return of each stock over a past period as its momentum indicator.
lookback_period = 20 # Momentum lookback period
all_data['momentum'] = all_data.groupby('symbol')['returns'].rolling(window=lookback_period).apply(lambda x: (x + 1).prod() - 1).reset_index(level=0, drop=True)
Strategy Construction
On each rebalancing date, we select the N stocks with the highest momentum and hold them with equal weighting.
import numpy as np
N = 5 # Number of stocks held
rebalance_freq = 'M' # Rebalance monthly
# Rank momentum on each rebalance date
all_data['rank'] = all_data.groupby('date')['momentum'].rank(ascending=False)
# Generate trading signals
all_data['signal'] = np.where(all_data['rank'] <= N, 1, 0)
# Calculate strategy returns
all_data['strategy_return'] = all_data.groupby('symbol')['signal'].shift(1) * all_data['returns']
# Calculate portfolio returns
portfolio_return = all_data.groupby('date')['strategy_return'].sum() / N
Strategy Evaluation
Finally, we can calculate metrics such as the strategy's annualized return and maximum drawdown, and plot the strategy's net asset value curve.
# Calculate annualized return
annual_return = portfolio_return.mean() * 252
# Calculate maximum drawdown
cumulative_return = (1 + portfolio_return).cumprod()
peak = cumulative_return.cummax()
drawdown = (cumulative_return - peak) / peak
max_drawdown = drawdown.min()
# Print strategy performance
print(f"Annualized return: {annual_return:.2%}")
print(f"Maximum drawdown: {max_drawdown:.2%}")
# Plot the equity curve
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
plt.plot(cumulative_return)
plt.title('Strategy Cumulative Return')
plt.xlabel('Date')
plt.ylabel('Cumulative Return')
plt.show()
Summary
The momentum effect is a classic and effective strategy in quantitative trading. This article introduced a simple strategy based on the momentum effect and used Python code to demonstrate its implementation. It should be noted that this strategy is provided only as an example; in practical applications, more factors need to be considered, such as transaction costs and risk control.