In the world of investing and trading, identifying market leaders and laggards is crucial. Traditional price charts and indicators provide valuable insights, but they often fail to visualize sector or stock rotation in a clear and intuitive manner. This is where Relative Rotation Graphs (RRG) come into play.
RRG charts allow traders and investors to compare the relative strength and momentum of multiple securities against a benchmark, revealing how different stocks, sectors, or asset classes are rotating through various performance phases. This article explores how to interpret RRGs effectively and use them for making informed investment decisions.
What is a Relative Rotation Graph (RRG)?
RRG is a visualization tool that plots securities based on two key indicators:
- JdK RS-Ratio (Relative Strength Ratio) – Measures a security’s relative strength against a benchmark. Higher values indicate outperformance, while lower values suggest underperformance.
- JdK RS-Momentum (Relative Strength Momentum) – Measures the rate of change of the relative strength. Increasing momentum suggests improving strength, while decreasing momentum signals weakening performance.

By plotting these values on a two-dimensional plane, RRG divides the chart into four quadrants:
Improving (Look for Buys) – Top-left quadrant
- Securities in this quadrant have weak relative strength but are gaining momentum.
- These are potential turnaround candidates that may soon transition into the leading quadrant.
- Ideal for investors looking for early entry points into emerging leaders.
Leading (Hold) – Top-right quadrant
- Securities in this quadrant have both high relative strength and strong momentum.
- These are the outperformers of the market.
- Typically, stocks or sectors here continue to perform well, making them attractive for holding or further accumulation.
Weakening (Look for Sells) – Bottom-right quadrant
- Securities in this quadrant still have high relative strength but are losing momentum.
- This often indicates a mature uptrend that may be slowing down.
- If a security remains in this quadrant for an extended period, it may transition into the lagging quadrant, signaling a potential exit point.
Lagging (Avoid) – Bottom-left quadrant
- Securities in this quadrant exhibit both weak relative strength and weak momentum.
- These are underperformers that should be avoided or shorted.
- Investors should be cautious about bottom fishing unless there is a clear sign of reversal.

Python Implementation
Following is the python – Streamlit implementation of the RRG chart.
import streamlit as st import pandas as pd import numpy as np import matplotlib.pyplot as plt from datetime import timedelta, date st.set_page_config(layout="wide") st.subheader("Relative Rotation Graph (RRG) Analysis") st.markdown("""<style> header[data-testid="stHeader"] {display:none;} </style>""", unsafe_allow_html=True) # Sidebar inputs st.sidebar.header("Input Parameters") #### Initial setup - Broker Connection #### pd.set_option("display.max_rows", None, "display.max_columns", None) # Benchmark ticker input benchmark_ticker = st.sidebar.text_input("Benchmark Ticker :", "NIFTY 50") # Ticker input - multiple tickers separated by commas ticker_input = st.sidebar.text_area( "Enter Tickers (comma separated):", value="NIFTY AUTO,NIFTY BANK,NIFTY ENERGY,NIFTY FMCG,NIFTY IT,NIFTY MEDIA,NIFTY METAL,NIFTY PHARMA, NIFTY PSU BANK, NIFTY REALTY", height=200 ) tickers = [ticker.strip() for ticker in ticker_input.split(",")] # Period selector with slider max_days = 365 period_days = st.sidebar.slider( "Period (days):", min_value=30, max_value=max_days, value=180, step=10 ) # Tail length selector tail_length = st.sidebar.slider( "Tail Length (days):", min_value=1, max_value=60, value=5, step=1 ) show_label = st.sidebar.checkbox("Show Ticker Label", value=True) # Function to calculate RRG data def calculate_rrg(tickers, benchmark, period_days, tail_length): end_date = date.today() start_date = end_date - timedelta(days=period_days + 25) # Extra buffer for calculations # Download data try: tickers_data = pd.DataFrame() ## Replace this code below with your code to fetch historical candlestick data benchmark_data = Instruments.get_historical_data(benchmark, start_date, end_date)['Close'] ## Replace this code below with your code to fetch historical candlestick data for symbol in tickers: df = Instruments.get_historical_data(symbol, start_date, end_date) if not df.empty: tickers_data[symbol] = df['Close'] # Ensure we have data for all tickers if isinstance(tickers_data, pd.Series): tickers_data = pd.DataFrame(tickers_data) tickers_data.columns = [tickers[0]] # Calculate relative strength rs_data = pd.DataFrame() jdK_data = pd.DataFrame() jdD_data = pd.DataFrame() for ticker in tickers_data.columns: # Relative Strength = Price of Stock / Price of Benchmark * 100 rs_data[ticker] = (tickers_data[ticker] / benchmark_data) * 100 # Normalize to 100 at the start rs_data[ticker] = 100 * rs_data[ticker] / rs_data[ticker].iloc[30] # JdK RS-Ratio (X-axis): 13-period EMA of RS divided by 21-period EMA of RS jdK_data[ticker] = 100 * (rs_data[ticker].ewm(span=13).mean() / rs_data[ticker].ewm(span=21).mean()) # JdK RS-Momentum (Y-axis): 13-period RoC of RS-Ratio jdD_data[ticker] = 100 * (jdK_data[ticker] / jdK_data[ticker].shift(13) - 1) # Remove NaN values jdK_data = jdK_data.dropna() jdD_data = jdD_data.dropna() # Get the most recent data points recent_data = pd.DataFrame({ 'RS-Ratio': jdK_data.iloc[-1], 'RS-Momentum': jdD_data.iloc[-1] }) # Get tail data for each ticker tail_data = {} for ticker in tickers_data.columns: tail_data[ticker] = { 'x': jdK_data[ticker].iloc[-tail_length:].values, 'y': jdD_data[ticker].iloc[-tail_length:].values } return recent_data, tail_data, True except Exception as e: st.error(f"Error fetching or calculating data: {e}") return None, None, False # Calculate RRG data recent_data, tail_data, success = calculate_rrg(tickers, benchmark_ticker, period_days, tail_length) # Main content if success and recent_data is not None: st.write(f"RRG Analysis: Relative to {benchmark_ticker}") # Create RRG plot fig, ax = plt.subplots(figsize=(12, 6)) # Enhance data points by scaling from the center (100, 0) enhanced_data = recent_data.copy() # Also scale tail data enhanced_tail_data = {} for ticker in tail_data: enhanced_tail_data[ticker] = { 'x': tail_data[ticker]['x'], 'y': tail_data[ticker]['y'] } # Draw quadrant lines and labels ax.axhline(y=0, color='gray', linestyle='-', alpha=0.5) ax.axvline(x=100, color='gray', linestyle='-', alpha=0.5) # Set quadrant labels - positioned further out to accommodate scaled data label_offset = 4 ax.text(100 + label_offset, label_offset / 2, "Leading\n(Hold)", ha='center', va='center', fontsize=7, bbox=dict(facecolor='yellow', alpha=0.2)) ax.text(100 - label_offset, label_offset / 2, "Improving\n(Look to Buy)", ha='center', va='center', fontsize=7, bbox=dict(facecolor='red', alpha=0.2)) ax.text(100 - label_offset, -label_offset / 2, "Lagging\n(Avoid)", ha='center', va='center', fontsize=7, bbox=dict(facecolor='gray', alpha=0.2)) ax.text(100 + label_offset, -label_offset / 2, "Weakening\n(Look to Sell)", ha='center', va='center', fontsize=7, bbox=dict(facecolor='green', alpha=0.2)) # Plot current positions colors = plt.cm.tab10(np.linspace(0, 1, len(tickers))) for i, ticker in enumerate(tickers): if ticker in enhanced_data.index: # Plot tails if ticker in enhanced_tail_data: ax.plot(enhanced_tail_data[ticker]['x'], enhanced_tail_data[ticker]['y'], '-', color=colors[i], alpha=0.6, linewidth=1) # Plot current position x = enhanced_data.loc[ticker, 'RS-Ratio'] y = enhanced_data.loc[ticker, 'RS-Momentum'] ax.scatter(x, y, color=colors[i], s=100, label=ticker) if show_label: ax.text(x , y , ticker, fontsize=7, ha='center') # Set axis limits to create equal quadrants - with wider boundaries for scaled data ratio_max = max(abs(enhanced_data['RS-Ratio'].max() - 100), 1) momentum_max = max(abs(enhanced_data['RS-Momentum'].max()), abs(enhanced_data['RS-Momentum'].min()), 2) ax.set_xlim(100 - ratio_max - 5, 100 + ratio_max + 5) ax.set_ylim(-momentum_max-0.25, momentum_max+0.25) # Add details ax.set_title( f'Relative Rotation Graph (Period: {period_days} days, Tail: {tail_length} days', fontsize=9) ax.set_xlabel('RS-Ratio (Relative Strength)', fontsize=7) ax.set_ylabel('RS-Momentum', fontsize=7) ax.grid(True, alpha=0.3) ax.legend(loc='upper left', bbox_to_anchor=(1, 1)) st.pyplot(fig) # Add explanation with st.expander("Understanding RRG Quadrants"): st.markdown(""" **Leading (Top Right)** - Strong relative strength and positive momentum. These are typically your leaders and best performers. **Weakening (Bottom Right)** - Still strong but losing momentum. These might be stocks to consider selling as they rotate into the lagging quadrant. **Lagging (Bottom Left)** - Weak relative strength and negative momentum. These are typically underperformers. **Improving (Top Left)** - Still weak but gaining momentum. These might be stocks to consider buying as they rotate into the leading quadrant. """) else: st.info("Enter valid tickers and parameters to generate the RRG plot.")
How to run this streamlit script

How to Interpret RRG Rotation Patterns

RRG charts provide more than just a snapshot; they also illustrate how securities rotate through different phases over time. By analyzing the tail movement, investors can gain deeper insights into market dynamics.
- Clockwise Rotation – The most common pattern
- Securities tend to move from improving → leading → weakening → lagging in a clockwise fashion.
- This rotation aligns with market cycles and sector rotation.
- Sharp Turns in Momentum
- A sudden move from lagging to improving suggests a strong reversal.
- Conversely, a sharp drop from leading to weakening may indicate an overbought condition.
- Length of the Tail
- A long tail signifies high volatility and strong trends.
- A short tail suggests stability but slower movement.
- Securities Crossing Quadrants
- Stocks moving from improving to leading are prime buy candidates.
- Stocks moving from weakening to lagging should be avoided.
Practical Applications of RRG
- Sector Rotation Strategy
- Investors can use RRG to identify which sectors are gaining strength and allocate capital accordingly.
- For example, if technology is moving from improving to leading, it may be a good time to increase exposure to tech stocks.
- Stock Selection
- RRG helps traders focus on stocks with strong relative performance rather than just absolute price movements.
- Portfolio Diversification
- By identifying sector rotations, investors can balance their portfolios across leading and improving sectors.
- Market Timing
- Watching how securities rotate through quadrants helps investors time their entries and exits more effectively.
Limitations of RRG
While RRG is a powerful tool, it should not be used in isolation. Here are a few things to keep in mind:
- Lagging Indicator – Since it relies on historical data, RRG may not always predict sudden market reversals.
- Works Best for Groups – It is most effective when comparing multiple securities rather than analyzing a single stock.
- Needs Confirmation – Combining RRG with other technical indicators like moving averages, RSI, or MACD can improve accuracy.
Conclusion
Relative Rotation Graphs provide a dynamic and insightful way to analyze market trends and sector rotation. By understanding the four quadrants and monitoring rotation patterns, traders and investors can make better-informed decisions about which stocks or sectors to buy, hold, or avoid.
Whether you’re a short-term trader or a long-term investor, integrating RRG into your strategy can offer a big-picture view of market trends, helping you stay ahead of the curve.
Support this community : FabTrader.in is a one-person initiative dedicated to helping individuals on their F.I.R.E. journey. Running and maintaining this community takes time, effort, and resources. If you’ve found value in the content, consider making a donation to support this mission.
Disclaimer: The information provided in this article is for educational and informational purposes only and should not be construed as financial, investment, or legal advice. The content is based on publicly available information and personal opinions and may not be suitable for all investors. Investing involves risks, including the loss of principal. Always conduct your own research and consult a qualified financial advisor before making any investment decisions. The author and website assume no liability for any financial losses or decisions made based on the information presented.