The relative momentum strategy
The relative momentum strategy compares the momentum of individual stocks and indexes to judge the relative strength of stocks to the broader market. It buys when the stock momentum is higher than that of the index…

Overview
The relative momentum strategy compares the momentum of individual stocks and indexes to judge the relative strength of stocks to the broader market. It buys when the stock momentum is higher than that of the index, and sells when the stock momentum is lower than that of the index, in order to capture the growth peak of individual stocks.
Principles
The core logic of this strategy is to judge the relative strength of individual stocks versus the market, specifically:
- Calculate the return over a period of time as the momentum of the stock
- Calculate index return over the same period as the index momentum
- Use moving average to smooth the stock and index momentum
- When the moving average of stock momentum crosses above that of the index, the stock momentum is considered stronger than the market overall - that is the buy signal
- When the stock momentum moving average crosses below the index momentum moving average, the stock momentum is considered weaker, triggering the sell signal
Through this logic, we can buy into stocks when their growth is thriving and sell out as the growth momentum fades, locking in excess returns during the growth peak period of stocks.
Advantage Analysis
The main advantages of the relative momentum strategy:
- Can dynamically capture the growth peak of stocks without concerning specific market conditions - just buy when the stock growth outpaces the overall market
- Smoothing with moving averages filters out short-term fluctuations and enhances signal reliability
- Simple direct buy and sell conditions, easy to understand and operate
- Flexibility to configure the time period for calculating relative momentum and optimize
Risk Analysis
There are also some risks with the relative momentum strategy:
- Stocks may pullback after growth peak ends, posing insufficient profit-taking risk
- Relative momentum signals can be false, identifying a fake instead of real peak
- Need to set stop loss to control maximum loss
These risks can be managed by reasonable profit-taking, stop losses, parameter tuning etc.
Optimization Directions
The relative momentum strategy can be optimized mainly from the following aspects:
- Test different time periods for computing momentum to find optimum
- Try out different types and lengths of moving averages for best parameters
- Add volume filter to avoid false breakouts due to lack of momentum
- Incorporate other indicators to confirm optimal entry timing
Conclusion
The relative momentum strategy captures the excess growth phases of individual stocks versus the overall market to generate alpha. With its simple, clear buy/sell logic and ease of operation, and when coupled with parameter optimization and risk control, this strategy can perform very well.
Parameters
| Name | Default | What it controls |
|---|---|---|
| v_input_1 | BTC_USDT:swap | index_ticker |
| v_input_2 | 40 | Loopback |
| v_input_3 | true | useStopAndIndexReturns |
| v_input_4 | true | useStopAndIndexReturnsMa |
| v_input_5 | 0 | Moving Average Type: sma | ema | hma | rma | vwma | wma |
| v_input_6 | 10 | MALength |
| v_input_7 | timestamp(01 Jan 2010 00:00 +0000) | Backtest Start Time |
| v_input_8 | timestamp(01 Jan 2099 00:00 +0000) | Backtest End Time |
Source Pine Script
/*backtest
start: 2024-01-21 00:00:00
end: 2024-01-28 00:00:00
period: 15m
basePeriod: 5m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed
//@version=4
strategy("Relative Returns Strategy", overlay=false, initial_capital = 100000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.percent, pyramiding = 1, commission_value = 0.01, calc_on_order_fills = true)
index_ticker=input("BTC_USDT:swap")
Loopback = input(40, step=20)
useStopAndIndexReturns = input(true)
useStopAndIndexReturnsMa = input(true)
useDifference = not useStopAndIndexReturns
MAType = input(title="Moving Average Type", defval="sma", options=["ema", "sma", "hma", "rma", "vwma", "wma"])
MALength = input(10, minval=10,step=10)
i_startTime = input(defval = timestamp("01 Jan 2010 00:00 +0000"), title = "Backtest Start Time", type = input.time)
i_endTime = input(defval = timestamp("01 Jan 2099 00:00 +0000"), title = "Backtest End Time", type = input.time)
inDateRange = true
f_secureSecurity(_symbol, _res, _src, _offset) => security(_symbol, _res, _src[_offset], lookahead = barmerge.lookahead_on)
f_getMovingAverage(source, MAType, length)=>
ma = sma(source, length)
if(MAType == "ema")
ma := ema(source,length)
if(MAType == "hma")
ma := hma(source,length)
if(MAType == "rma")
ma := rma(source,length)
if(MAType == "vwma")
ma := vwma(source,length)
if(MAType == "wma")
ma := wma(source,length)
ma
index = f_secureSecurity(index_ticker, '1D', close, 0)
stock_return = (close - close[Loopback])*100/close
index_return = (index - index[Loopback])*100/index
stock_return_ma = f_getMovingAverage(stock_return, MAType, MALength)
index_return_ma = f_getMovingAverage(index_return, MAType, MALength)
relativeReturns = stock_return - index_return
relativeReturns_ma = f_getMovingAverage(relativeReturns, MAType, MALength)
plot(useStopAndIndexReturns ? useStopAndIndexReturnsMa ? stock_return_ma : stock_return : na, title="StockReturn", color=color.green, linewidth=1)
plot(useStopAndIndexReturns ? useStopAndIndexReturnsMa ? index_return_ma : index_return : na, title="IndexReturn", color=color.red, linewidth=1)
plot(useDifference?relativeReturns:na, title="Relative-Returns", color=color.blue, linewidth=1)
plot(useDifference?relativeReturns_ma:na, title="MA", color=color.red, linewidth=1)
buyCondition = (useStopAndIndexReturns ? useStopAndIndexReturnsMa ? stock_return_ma > index_return_ma : stock_return > index_return : relativeReturns > relativeReturns_ma)
closeBuyCondition = (useStopAndIndexReturns ? useStopAndIndexReturnsMa ? stock_return_ma < index_return_ma : stock_return < index_return : relativeReturns < relativeReturns_ma)
strategy.entry("Buy", strategy.long, when=buyCondition and inDateRange, oca_name="oca")
strategy.close("Buy", when=closeBuyCondition)Published by ChaoZhang on the FMZ strategy square · last modified 2024-01-29. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.
Build this with AgenKit
A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/The-relative-momentum-strategy.md- npx agenkit install engineering-kit in your project
- Paste the command into your agent and approve each phase
- Get tests, backtest, risk controls and a paper-trading switch
Understand it first
Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.
Related specs
- KST EMA Momentum Trend Following StrategyPine Script · Momentum · 1d
- Multi EMA Trend Momentum Trading Strategy with Risk Management SystemPine Script · Momentum · 1h
- Multi Indicator Trend Momentum Trading Strategy with Dynamic Risk Management SystemPine Script · Momentum · 1h
- Multi Level Technical Indicator Momentum Trading Strategy Based on Enhanced MACD Volume Strength and EMA Signal SystemPine Script · Momentum · 1h
- Multi Timeframe Momentum Confluence Automated Trading StrategyPine Script · Momentum · 1h
- Triple EMA and Fisher Transform Trend Momentum StrategyPine Script · Momentum · 1d