Rajandran R Creator of OpenAlgo - OpenSource Algo Trading framework for Indian Traders. Building GenAI Applications. Telecom Engineer turned Full-time Derivative Trader. Mostly Trading Nifty, Banknifty, High Liquid Stock Derivatives. Trading the Markets Since 2006 onwards. Using Market Profile and Orderflow for more than a decade. Designed and published 100+ open source trading systems on various trading tools. Strongly believe that market understanding and robust trading frameworks are the key to the trading success. Building Algo Platforms, Writing about Markets, Trading System Design, Market Sentiment, Trading Softwares & Trading Nuances since 2007 onwards. Author of Marketcalls.in

NIFTY 50 Rolling CAGR Heatmap – Python Code

6 min read

Are your equity investments actually beating Fixed Deposits?

Most retail investors park their money in Fixed Deposits earning 6-7% annually, assuming it’s the “safe” choice. But with inflation eating into returns, FDs often deliver negative real returns. The NIFTY 50 Rolling CAGR Heatmap helps you visualize which stocks have consistently outperformed FD returns over 1, 3, and 5-year periods.

Nifty 50 – 5 Year CAGR Heatmap

What is Rolling CAGR?

CAGR (Compound Annual Growth Rate) measures the annualized return of an investment over a specified period. Unlike absolute returns, CAGR accounts for compounding and provides a standardized way to compare investments across different time horizons.

Formula:

CAGR = (End Value / Start Value)^(1/Years) – 1

A “rolling” CAGR calculates this metric continuously, giving you the annualized return from any point in time to the present day.

Nifty 50 3% CAGR Heatmap

Why Heatmaps for Stock Analysis?

Heatmaps transform complex numerical data into intuitive visual patterns. At a glance, you can identify:

  • Green zones: Strong performers with high CAGR
  • Yellow zones: Moderate performers around FD-equivalent returns
  • Red zones: Underperformers that failed to beat inflation

This visual approach eliminates the need to scan through spreadsheets and instantly highlights where the real wealth creators are.

Nifty 50 – 1 Year CAGR Heatmap

Benchmarking Against FD Returns

Here’s the critical question every investor should ask: Is my stock portfolio beating what I’d earn in a Fixed Deposit?

Current FD rates hover around 6-7% annually. Use this as your benchmark:

CAGR RangeInterpretation
> 15%Excellent – Strong wealth creation
10-15%Good – Beating inflation comfortably
7-10%Average – Marginally better than FDs
< 7%Poor – FD would have been better

When you look at the heatmap:

  • Stocks in deep green (>15% CAGR) are your wealth creators
  • Stocks in yellow/orange (7-10%) barely justify the equity risk
  • Stocks in red (<7%) have destroyed opportunity cost – FDs were the better choice

How to Use the CAGR Heatmap

For Long-Term Investors

  1. Identify Consistent Performers: Look for stocks that are green across 1Y, 3Y, and 5Y heatmaps. Consistency matters more than one exceptional year.
  2. Spot Turnaround Candidates: A stock red in 5Y but green in 1Y might indicate a turnaround – worth investigating further.
  3. Avoid Value Traps: Stocks red across all timeframes despite being “cheap” might be value traps. The market is often right.

For Portfolio Review

  1. Benchmark Your Holdings: Check where your current holdings fall on the heatmap. If most are in red/yellow zones, consider rebalancing.
  2. Sector Rotation Insights: Notice which sectors dominate the green zones. This reveals where long-term money is being made.
  3. Risk Assessment: A portfolio concentrated in red-zone stocks carries higher risk of underperformance.

Key Insights from NIFTY 50 CAGR Analysis

Analyzing the NIFTY 50 constituents over rolling periods reveals interesting patterns:

  • Defensive sectors (FMCG, IT) often show moderate but consistent CAGRs
  • Cyclical stocks (Metals, Infra) can swing from deep red to bright green
  • New-age stocks may show N/A for 5Y CAGR due to recent listing
  • PSU stocks have shown remarkable turnarounds in recent years

The FD Investor’s Dilemma

Consider this scenario:

  • FD Investment: ₹10 lakhs at 7% for 5 years = ₹14.02 lakhs
  • Stock with 15% CAGR: ₹10 lakhs for 5 years = ₹20.11 lakhs
  • Difference: ₹6.09 lakhs (43% more wealth)

But here’s the catch – not all stocks deliver 15% CAGR. The heatmap helps you identify which ones historically have, reducing the guesswork in stock selection.

From the output above:

Top Performers (5Y CAGR):

  • BEL: 59.89% – Defence sector rally, massive wealth creation
  • MAXHEALTH: 50.11% – Healthcare sector strength
  • TRENT: 45.47% – Retail consumption growth

Underperformers (5Y CAGR):

  • HINDUNILVR: 1.79% – Below FD returns despite being a “safe” FMCG stock
  • ASIANPAINT: 2.60% – Premium valuation compressed returns
  • KOTAKBANK: 3.52% – Banking sector challenges

Key Insight: HINDUNILVR, often considered a “safe blue-chip,” delivered only 1.79% CAGR over 5 years – significantly worse than a 7% FD. This highlights why benchmarking matters.

Identifying Investment Patterns

Pattern 1: Consistent Performers Look for stocks green across all three timeframes (1Y, 3Y, 5Y):

  • SHRIRAMFIN, BEL, BHARTIARTL, M&M

Pattern 2: Recent Turnarounds Green in 1Y but yellow/red in 5Y indicates recent momentum:

  • MARUTI (57% 1Y, 16.8% 5Y)
  • HINDALCO (54% 1Y, 29.65% 5Y)

Pattern 3: Fading Stars Red in 1Y but green in 5Y suggests weakening momentum:

  • TRENT (-36.59% 1Y, 45.47% 5Y)
  • ITC (-26.61% 1Y, 12.05% 5Y)

Pattern 4: New Entrants N/A in 3Y or 5Y indicates recently listed stocks:

  • JIOFIN (only 1Y data available)
  • ETERNAL (only 1Y and 3Y data)

Practical Use Cases

Portfolio Review

  1. List your current holdings
  2. Check their position on the heatmap
  3. If majority are in red/yellow zones, consider rebalancing

Stock Selection

  1. Focus on stocks in the green zone across multiple timeframes
  2. Avoid stocks consistently in red zones
  3. Research yellow-zone stocks for potential entry points

Sector Analysis

Group stocks by sector and observe patterns:

  • IT Sector (TCS, INFY, WIPRO): Mostly yellow/red in 1Y
  • PSU/Defence (BEL, NTPC, COALINDIA): Strong green across timeframes
  • Auto (M&M, MARUTI, EICHERMOT): Recent strong performance

Technical Implementation

The heatmap is built using Python and OpenAlgo, fetching historical data directly from your broker.

# ---------------------------------------------------
# Python Code to Compute Rolling CAGR Heatmap for NIFTY 50
# Recommended to use Daily Historical Data more than 5 Years
# Minor variations in Rolling Returns might occur due to data source differences
# Coded by Rajandran R - Creator of OpenAlgo (https://openalgo.in)
# Author - www.marketcalls.in
# ---------------------------------------------------
# NOTE: This code requires OpenAlgo to be running locally or on a server.
# Get your API key from your self-hosted OpenAlgo platform.
# OpenAlgo GitHub: https://github.com/marketcalls/openalgo
# ---------------------------------------------------

from openalgo import api
import pandas as pd
import numpy as np
import plotly.express as px
from datetime import datetime, timedelta

# ---------------------------------------------------
# Initialize OpenAlgo Client
# ---------------------------------------------------
client = api(
    api_key="your_api_key_here",
    host="http://127.0.0.1:5000"
)

print("🔁 OpenAlgo Python Bot is running.")

# ---------------------------------------------------
# NIFTY 50 SYMBOLS
# ---------------------------------------------------
symbols = [
    "INDIGO", "TRENT", "HINDUNILVR", "HCLTECH", "WIPRO", "INFY", "TATACONSUM",
    "TATASTEEL", "ITC", "ASIANPAINT", "SBILIFE", "LT", "SHRIRAMFIN", "BEL", "SBIN",
    "COALINDIA", "KOTAKBANK", "TCS", "SUNPHARMA", "MAXHEALTH", "NESTLEIND",
    "RELIANCE", "ETERNAL", "APOLLOHOSP", "ICICIBANK", "GRASIM", "ULTRACEMCO",
    "ADANIENT", "AXISBANK", "DRREDDY", "TECHM", "TMPV", "JIOFIN", "NTPC",
    "BAJFINANCE", "BHARTIARTL", "POWERGRID", "HINDALCO", "HDFCBANK", "TITAN",
    "HDFCLIFE", "MARUTI", "BAJAJFINSV", "ADANIPORTS", "CIPLA", "JSWSTEEL",
    "BAJAJ-AUTO", "ONGC", "EICHERMOT", "M&M"
]

TRADING_DAYS_PER_YEAR = 252

# ---------------------------------------------------
# CAGR Calculation (matching TradingView logic)
# ---------------------------------------------------
def calc_cagr(start_price, end_price, years):
    if pd.isna(start_price) or pd.isna(end_price) or start_price <= 0 or end_price <= 0:
        return np.nan
    return ((end_price / start_price) ** (1 / years) - 1) * 100


def get_price_by_trading_days(df, bars_back):
    """Get price exactly N trading bars back (like TradingView)"""
    if len(df) <= bars_back:
        return np.nan
    return df["close"].iloc[-(bars_back + 1)]


# ---------------------------------------------------
# Fetch Historical Data & Calculate CAGRs
# ---------------------------------------------------
results = []

for symbol in symbols:
    try:
        end_date = datetime.now()
        start_date = end_date - timedelta(days=365 * 6)  # Extra buffer

        df = client.history(
            symbol=symbol,
            exchange="NSE",
            interval="D",
            start_date=start_date.strftime("%Y-%m-%d"),
            end_date=end_date.strftime("%Y-%m-%d")
        )

        if not isinstance(df, pd.DataFrame) or df.empty:
            print(f"{symbol}: No data received")
            results.append([symbol, np.nan, np.nan, np.nan])
            continue

        df.index = pd.to_datetime(df.index)
        df = df.sort_index()

        price_now = df["close"].iloc[-1]
        total_bars = len(df)

        if total_bars < TRADING_DAYS_PER_YEAR:
            print(f"{symbol}: Insufficient data ({total_bars} bars)")
            results.append([symbol, np.nan, np.nan, np.nan])
            continue

        # Get prices using trading days (like TradingView)
        bars_1y = TRADING_DAYS_PER_YEAR
        bars_3y = TRADING_DAYS_PER_YEAR * 3
        bars_5y = TRADING_DAYS_PER_YEAR * 5

        price_1y = get_price_by_trading_days(df, bars_1y)
        price_3y = get_price_by_trading_days(df, bars_3y)
        price_5y = get_price_by_trading_days(df, bars_5y)

        # Calculate returns
        abs_1y = ((price_now / price_1y) - 1) * 100 if not pd.isna(price_1y) else np.nan
        cagr_3y = calc_cagr(price_3y, price_now, 3)
        cagr_5y = calc_cagr(price_5y, price_now, 5)

        # Display status
        abs_1y_str = f"{abs_1y:7.2f}%" if not pd.isna(abs_1y) else "N/A"
        cagr_3y_str = f"{cagr_3y:7.2f}%" if not pd.isna(cagr_3y) else "N/A"
        cagr_5y_str = f"{cagr_5y:7.2f}%" if not pd.isna(cagr_5y) else "N/A"
        print(f"{symbol:12s} | 1Y: {abs_1y_str:>8s} | 3Y: {cagr_3y_str:>8s} | 5Y: {cagr_5y_str:>8s}")

        results.append([symbol, abs_1y, cagr_3y, cagr_5y])

    except Exception as e:
        print(f"Error fetching {symbol}: {e}")
        results.append([symbol, np.nan, np.nan, np.nan])

# ---------------------------------------------------
# Create DataFrame
# ---------------------------------------------------
df_cagr = pd.DataFrame(results, columns=["Symbol", "1Y", "3Y", "5Y"])


# ---------------------------------------------------
# Function to Create Heatmap
# ---------------------------------------------------
def create_heatmap(df, period, label):
    df_period = df[["Symbol", period]].copy()

    # Sort by value, putting NaN at the bottom
    df_period = df_period.sort_values(period, ascending=False, na_position='last').reset_index(drop=True)

    if df_period.empty:
        print(f"⚠️ No data for {label}")
        return

    cols = 10
    df_period["row"] = df_period.index // cols
    df_period["col"] = df_period.index % cols

    # Create display text: "SYMBOL\nValue%" or "SYMBOL\nN/A"
    df_period["display_text"] = df_period.apply(
        lambda row: f"{row['Symbol']}<br>{row[period]:.2f}%" if pd.notna(row[period]) else f"{row['Symbol']}<br>N/A",
        axis=1
    )

    pivot_values = df_period.pivot(index="row", columns="col", values=period)
    pivot_labels = df_period.pivot(index="row", columns="col", values="display_text")

    fig = px.imshow(
        pivot_values,
        color_continuous_scale="RdYlGn",
        aspect="auto"
    )

    fig.update_traces(
        text=pivot_labels.values,
        texttemplate="%{text}",
        hovertemplate="%{text}<extra></extra>"
    )

    fig.update_layout(
        title=f"NIFTY 50 {label} Heatmap (%)",
        xaxis=dict(showticklabels=False, title=""),
        yaxis=dict(showticklabels=False, autorange="reversed", title=""),
        template="plotly_dark",
        height=600,
        width=1200
    )

    filename = f"nifty50_{period.lower()}_heatmap.png"
    fig.write_image(filename, width=1200, height=600, scale=2)
    print(f"✅ {label} Heatmap saved as {filename}")


# ---------------------------------------------------
# Generate Heatmaps
# ---------------------------------------------------
create_heatmap(df_cagr, "1Y", "1-Year Absolute Return")
create_heatmap(df_cagr, "3Y", "3-Year CAGR")
create_heatmap(df_cagr, "5Y", "5-Year CAGR")

print("\n✅ All heatmaps generated successfully!")

Key Features:

  • Trading Days Based: Uses 252 trading days/year for accurate calculations
  • Multiple Timeframes: 1-Year absolute returns, 3-Year and 5-Year CAGR
  • Real-Time Data: Fetches live data from your connected broker via OpenAlgo
  • Visual Export: Generates publication-ready PNG heatmaps

Getting Started

To generate your own CAGR heatmaps:

  1. Setup OpenAlgo: Install and configure OpenAlgo with your broker
  2. Get API Key: Obtain your API key from the OpenAlgo dashboard
  3. Run the Script: Execute the Python script to generate heatmaps
  4. Analyze: Use the heatmaps to make informed investment decisions

Conclusion

The NIFTY 50 Rolling CAGR Heatmap is a powerful tool for visualizing long-term stock performance. By benchmarking against FD returns, you can quickly identify which stocks justify the equity risk and which ones are better replaced with safer alternatives.

Remember: Time in the market beats timing the market – but only if you’re in the right stocks. Let the heatmap guide your selection.


Download the complete Python code from the link below and start generating your own CAGR heatmaps today.

Code Requirements:

  • Python 3.10+
  • OpenAlgo (self-hosted)
  • Libraries: pandas, numpy, plotly

Rajandran R Creator of OpenAlgo - OpenSource Algo Trading framework for Indian Traders. Building GenAI Applications. Telecom Engineer turned Full-time Derivative Trader. Mostly Trading Nifty, Banknifty, High Liquid Stock Derivatives. Trading the Markets Since 2006 onwards. Using Market Profile and Orderflow for more than a decade. Designed and published 100+ open source trading systems on various trading tools. Strongly believe that market understanding and robust trading frameworks are the key to the trading success. Building Algo Platforms, Writing about Markets, Trading System Design, Market Sentiment, Trading Softwares & Trading Nuances since 2007 onwards. Author of Marketcalls.in

Leave a Reply

Get Notifications, Alerts on Market Updates, Trading Tools, Automation & More