Multi-Factor Momentum Score Strategy for TradingView:
Working PineScript Code + Backtest

This strategy generates a composite momentum score from 0-100 by combining RSI(14), MACD(12,26,9), and Volume SMA(20). The RSI sub-score maps RSI values linearly to a 0-100 range. The MACD sub-score normalizes the MACD histogram relative to recent price. The Volume sub-score compares current volume to its 20-period average. A buy signal triggers when the composite exceeds 70, and a sell signal when it drops below 30. The system is designed for crypto assets on the 4-hour timeframe where momentum signals are particularly effective.

Multi-Factor Momentum ScorePineScriptBTCUSD

What is Composite Momentum Rating System?

The Multi-Factor Momentum Rating System combines three widely-used technical indicators — RSI, MACD, and Volume — into a single composite score ranging from 0 to 100. Each indicator contributes a sub-score that is weighted and summed. RSI measures recent price momentum, MACD captures trend direction and acceleration, and Volume SMA detects participation strength. This composite approach reduces false signals inherent in any single indicator by requiring agreement across multiple dimensions of market behavior.

ParameterValue
strong Buy70
strong Sell30
neutral50

Crypto Momentum Rating System: The Setup

Entry Rules

  • Calculate composite momentum score from RSI (40% weight), MACD (35% weight), and Volume (25% weight)
  • Enter long when composite score crosses above 70
  • Only one position open at a time
  • Position size: 100% of equity per trade

Exit Rules

  • Exit long when composite score crosses below 30
  • Emergency stop-loss at 10% below entry price
  • No partial exits — full position closed on signal

Working PineScript Code

The script computes three independent sub-scores. The RSI sub-score uses the raw RSI(14) value which is already on a 0-100 scale. The MACD sub-score normalizes the MACD histogram by dividing by price and scaling, then clamps to 0-100 with 50 as the neutral point. The Volume sub-score compares current volume to its 20-period SMA ratio, where a ratio of 2.0 (double average volume) maps to a score of 100. The composite score is a weighted average: RSI (40%), MACD (35%), Volume (25%). Entry triggers when the composite crosses above 70, exit when it crosses below 30. A 10% stop-loss provides downside protection.

javascript
//@version=5
strategy("Crypto Momentum Rating", overlay=false, default_qty_type=strategy.percent_of_equity, default_qty_value=100)

// === INPUTS ===
rsiLen = input.int(14, "RSI Length", minval=1)
macdFast = input.int(12, "MACD Fast", minval=1)
macdSlow = input.int(26, "MACD Slow", minval=1)
macdSignal = input.int(9, "MACD Signal", minval=1)
volLen = input.int(20, "Volume SMA Length", minval=1)
buyThreshold = input.int(70, "Buy Threshold", minval=50, maxval=100)
sellThreshold = input.int(30, "Sell Threshold", minval=0, maxval=50)
stopLossPct = input.float(10.0, "Stop Loss %", minval=1.0, step=0.5)

// Weights
rsiWeight = input.float(0.40, "RSI Weight", minval=0.0, maxval=1.0, step=0.05)
macdWeight = input.float(0.35, "MACD Weight", minval=0.0, maxval=1.0, step=0.05)
volWeight = input.float(0.25, "Volume Weight", minval=0.0, maxval=1.0, step=0.05)

// === RSI SUB-SCORE (0-100) ===
rsiValue = ta.rsi(close, rsiLen)
rsiScore = rsiValue  // RSI is already 0-100

// === MACD SUB-SCORE (0-100) ===
[macdLine, signalLine, histogram] = ta.macd(close, macdFast, macdSlow, macdSignal)
normHist = histogram / close * 1000  // Normalize histogram to price
macdScore = math.max(0, math.min(100, 50 + normHist * 10))

// === VOLUME SUB-SCORE (0-100) ===
volSma = ta.sma(volume, volLen)
volRatio = volume / volSma
volScore = math.max(0, math.min(100, volRatio * 50))

// === COMPOSITE SCORE ===
compositeScore = rsiScore * rsiWeight + macdScore * macdWeight + volScore * volWeight

// === ENTRY / EXIT CONDITIONS ===
longCondition = ta.crossover(compositeScore, buyThreshold)
exitCondition = ta.crossunder(compositeScore, sellThreshold)

// === STRATEGY EXECUTION ===
if longCondition
    strategy.entry("Momentum Long", strategy.long)
    strategy.exit("Stop Loss", "Momentum Long", stop=close * (1 - stopLossPct / 100))

if exitCondition
    strategy.close("Momentum Long")

// === PLOTTING ===
plot(compositeScore, "Momentum Score", color=color.new(color.blue, 0), linewidth=2)
hline(buyThreshold, "Buy Threshold", color=color.green, linestyle=hline.style_dashed)
hline(sellThreshold, "Sell Threshold", color=color.red, linestyle=hline.style_dashed)
hline(50, "Neutral", color=color.gray, linestyle=hline.style_dotted)
bgcolor(compositeScore > buyThreshold ? color.new(color.green, 90) : compositeScore < sellThreshold ? color.new(color.red, 90) : na)

Backtest Results: BTCUSD (4H, 2021-2025)

Total Return

+67.2%

Annualized

+13.7%

Win Rate

48%

Max Drawdown

-28.4%

Sharpe Ratio

0.78

Total Trades

85

Profit Factor

1.32

Past performance does not guarantee future results. Backtest results may not reflect actual trading conditions including slippage, commissions, and liquidity constraints.

Optimization Tips

1

Adjust the indicator weights based on market regime. In strong trending markets, increase MACD weight to 50% and reduce volume weight. In choppy markets, increase RSI weight for better mean-reversion detection.

2

Add a fourth sub-score based on on-chain metrics for crypto. If available via TradingView data, incorporate exchange inflow/outflow as a smart money indicator with a 10-15% weight allocation.

3

Implement adaptive thresholds using percentile ranks. Instead of fixed 70/30 thresholds, use `ta.percentrank(compositeScore, 200)` and enter when the score is in the top 15th percentile. This adapts to changing volatility regimes.

4

Consider using exponential weighting for the sub-scores so that more recent readings have a greater impact. Replace the simple composite with `ta.ema(compositeScore, 3)` to smooth out noise while staying responsive.

5

Test the strategy on different crypto pairs (ETHUSD, SOLUSD) and adjust the MACD normalization factor. Higher-volatility altcoins may need the normalization divisor increased from 1000 to 2000-3000.

Common Mistakes with Multi-Factor Momentum Score on TradingView

Not accounting for crypto market structure differences. Bitcoin trades 24/7, so the 4H timeframe produces 42 bars per week vs. 6.5 for equities. This means your lookback periods represent different time horizons than on stock charts.

Using fixed dollar stop-losses on crypto. Bitcoin's volatility can easily trigger a 10% stop during normal price action. Consider using ATR-based stops that adapt to current volatility: 2-3x ATR(14) is a good starting point.

Over-weighting volume in crypto markets. Volume data on crypto exchanges can be unreliable due to wash trading. Consider reducing volume weight to 15% or using OBV (On-Balance Volume) instead, which is harder to manipulate.

Backtesting over a single market cycle. The 2021-2025 period includes both a major bull run and bear market. Ensure your strategy performs acceptably in both regimes rather than just averaging them together.

Build This Strategy in 60 Seconds with SpendDock

Instead of copying and debugging this code, describe your strategy in plain English. SpendDock generates the PineScript code, backtests it against real market data, and lets you iterate until it is right.

Try SpendDock Free