Claude Skill

stock-correlation

Analyze how stocks move together using Yahoo Finance price history (yfinance): find correlated peers for a ticker, measure correlation, beta, and spread between specific tickers, cluster a group into a correlation matrix, and track rolling or regime-dependent correlation. Use thi

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Download himself65-finance-skills-plugins_market-analysis_skills_stock-correlation-73d554d.zip · 8 KB

Install

skills CLI npx skills add https://github.com/himself65/finance-skills/tree/main/plugins/market-analysis/skills/stock-correlation
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install himself65-finance-skills@llmmart
Git git clone https://github.com/himself65/finance-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole himself65/finance-skills collection as a plugin from our marketplace. Git is the plain clone.

README

stock-correlation

Analyze stock correlations to find related companies, sector peers, and pair-trading candidates using historical price data.

What it does

Routes to four specialized sub-skills based on user intent:

  • Co-movement Discovery — given a single ticker, find the most correlated stocks from curated sector and thematic peer universes (e.g., "what correlates with NVDA?")
  • Return Correlation — deep-dive pairwise analysis between two tickers: Pearson correlation, beta, R-squared, spread Z-score, and rolling stability (e.g., "correlation between AMD and NVDA")
  • Sector Clustering — full NxN correlation matrix with hierarchical clustering to identify groups and outliers (e.g., "correlation matrix for FAANG")
  • Realized Correlation — time-varying and regime-conditional correlation: rolling windows (20/60/120-day), up vs down days, high-vol vs low-vol, drawdown regimes (e.g., "when NVDA drops what else drops?")

Triggers

  • "what correlates with NVDA", "find stocks related to AMD"
  • "correlation between AAPL and MSFT", "how do LITE and COHR move together"
  • "what moves with", "stocks that move together", "sympathy plays"
  • "sector peers", "pair trading", "hedging pair"
  • "when NVDA drops what else drops", "rolling correlation"
  • "correlation matrix for FAANG", "cluster these stocks"
  • Well-known pairs: AMD/NVDA, GOOGL/AVGO, LITE/COHR

Prerequisites

  • Python 3.8+
  • The skill auto-installs yfinance, pandas, and numpy via pip if not already present
  • scipy is optional (used for hierarchical clustering in Sector Clustering sub-skill; falls back to sorting if unavailable)

Platform

Works on all platforms (Claude Code, Claude.ai with code execution, etc.).

Setup

# Choose finance-market-analysis when prompted.
npx plugins add himself65/finance-skills

# Or install just this skill
npx skills add himself65/finance-skills --skill stock-correlation

See the main README for more installation options.

Reference files

  • references/sector_universes.md — Dynamic peer universe construction using yfinance Screener API, with fallback strategies

Skill manifest

Stock Correlation Analysis Skill

If your host has not already executed the shell probes below (for example, in Codex), run the commands inside the ! wrappers with its shell tool before choosing a setup path. Use the actual output; if shell access is unavailable, report that limitation.

Finds and analyzes correlated stocks using historical price data from Yahoo Finance via yfinance. Routes to specialized sub-skills based on user intent.

Important: This is for research and educational purposes only. Not financial advice. yfinance is not affiliated with Yahoo, Inc.


Step 1: Ensure Dependencies Are Available

Current environment status:

!`python3 -c "exec('try:\n import yfinance, pandas, numpy\n print(f\'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}\')\nexcept Exception:\n print(\'DEPS_MISSING\')')"`

If DEPS_MISSING, install required packages before running any code:

import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"])

If all dependencies are already installed, skip the install step and proceed directly.


Step 2: Route to the Correct Sub-Skill

Classify the user's request and jump to the matching sub-skill section below.

User Request Route To Examples
Single ticker, wants to find related stocks Sub-Skill A: Co-movement Discovery "what correlates with NVDA", "find stocks related to AMD", "sympathy plays for TSLA"
Two or more specific tickers, wants relationship details Sub-Skill B: Return Correlation "correlation between AMD and NVDA", "how do LITE and COHR move together", "compare AAPL vs MSFT"
Group of tickers, wants structure/grouping Sub-Skill C: Sector Clustering "correlation matrix for FAANG", "cluster these semiconductor stocks", "sector peers for AMD"
Wants time-varying or conditional correlation Sub-Skill D: Realized Correlation "rolling correlation AMD NVDA", "when NVDA drops what else drops", "how has correlation changed"

If ambiguous, default to Sub-Skill A (Co-movement Discovery) for single tickers, or Sub-Skill B (Return Correlation) for two tickers.

Defaults for all sub-skills

Parameter Default
Lookback period 1y (1 year)
Data interval 1d (daily)
Correlation method Pearson
Minimum correlation threshold 0.60
Number of results Top 10
Return type Daily log returns
Rolling window 60 trading days

Sub-Skill A: Co-movement Discovery

Goal: Given a single ticker, find stocks that move with it.

A1: Build the peer universe

You need 15-30 candidates. Do not use hardcoded ticker lists — build the universe dynamically at runtime. See references/sector_universes.md for the full implementation. The approach:

  1. Screen same-industry stocks using yf.screen() + yf.EquityQuery to find stocks in the same industry as the target
  2. Broaden to sector if the industry screen returns fewer than 10 peers
  3. Add thematic/adjacent industries — read the target's longBusinessSummary and screen 1-2 related industries (e.g., a semiconductor company → also screen semiconductor equipment)
  4. Combine, deduplicate, remove target ticker

A2: Compute correlations

import yfinance as yf
import pandas as pd
import numpy as np

def discover_comovement(target_ticker, peer_tickers, period="1y"):
    all_tickers = [target_ticker] + [t for t in peer_tickers if t != target_ticker]
    data = yf.download(all_tickers, period=period, auto_adjust=True, progress=False)

    # Extract close prices — yf.download returns MultiIndex (Price, Ticker) columns
    closes = data["Close"].dropna(axis=1, thresh=max(60, len(data) // 2))

    # Log returns
    returns = np.log(closes / closes.shift(1)).dropna()
    corr_series = returns.corr()[target_ticker].drop(target_ticker, errors="ignore")

    # Rank by absolute correlation
    ranked = corr_series.abs().sort_values(ascending=False)

    result = pd.DataFrame({
        "Ticker": ranked.index,
        "Correlation": [round(corr_series[t], 4) for t in ranked.index],
    })
    return result, returns

A3: Present results

Show a ranked table with company names and sectors (fetch via yf.Ticker(t).info.get("shortName")). Values below are illustrative:

Rank Ticker Company Correlation Why linked
1 AMD Advanced Micro Devices 0.82 Same industry — GPU/CPU
2 AVGO Broadcom 0.78 AI infrastructure peer

Include:

  • Top 10 positively correlated stocks
  • Any notable negatively correlated stocks (potential hedges)
  • Brief explanation of why each might be linked (sector, supply chain, customer overlap)

Sub-Skill B: Return Correlation

Goal: Deep-dive into the relationship between two (or a few) specific tickers.

B1: Download and compute

import yfinance as yf
import pandas as pd
import numpy as np

def return_correlation(ticker_a, ticker_b, period="1y"):
    data = yf.download([ticker_a, ticker_b], period=period, auto_adjust=True, progress=False)
    closes = data["Close"][[ticker_a, ticker_b]].dropna()

    returns = np.log(closes / closes.shift(1)).dropna()
    corr = returns[ticker_a].corr(returns[ticker_b])

    # Beta: how much does B move per unit move of A
    cov_matrix = returns.cov()
    beta = cov_matrix.loc[ticker_b, ticker_a] / cov_matrix.loc[ticker_a, ticker_a]

    # R-squared
    r_squared = corr ** 2

    # Rolling 60-day correlation for stability
    rolling_corr = returns[ticker_a].rolling(60).corr(returns[ticker_b])

    # Spread (log price ratio) for mean-reversion
    spread = np.log(closes[ticker_a] / closes[ticker_b])
    spread_z = (spread - spread.mean()) / spread.std()

    return {
        "correlation": round(corr, 4),
        "beta": round(beta, 4),
        "r_squared": round(r_squared, 4),
        "rolling_corr_mean": round(rolling_corr.mean(), 4),
        "rolling_corr_std": round(rolling_corr.std(), 4),
        "rolling_corr_min": round(rolling_corr.min(), 4),
        "rolling_corr_max": round(rolling_corr.max(), 4),
        "spread_z_current": round(spread_z.iloc[-1], 4),
        "observations": len(returns),
    }

B2: Present results

Show a summary card (illustrative values):

Metric Value
Pearson Correlation 0.82
Beta (B vs A) 1.15
R-squared 0.67
Rolling Corr (60d avg) 0.80
Rolling Corr Range [0.55, 0.94]
Rolling Corr Std Dev 0.08
Spread Z-Score (current) +1.2
Observations 250

Interpretation guide:

  • Correlation > 0.80: Strong co-movement — these stocks are tightly linked
  • Correlation 0.50–0.80: Moderate — shared sector drivers but independent factors too
  • Correlation < 0.50: Weak — limited co-movement despite possible sector overlap
  • High rolling std: Unstable relationship — correlation varies significantly over time
  • Spread Z > |2|: Unusual divergence from historical relationship

Sub-Skill C: Sector Clustering

Goal: Given a group of tickers, show the full correlation structure and identify clusters.

C1: Build the correlation matrix

import yfinance as yf
import pandas as pd
import numpy as np

def sector_clustering(tickers, period="1y"):
    data = yf.download(tickers, period=period, auto_adjust=True, progress=False)

    # yf.download returns MultiIndex (Price, Ticker) columns
    closes = data["Close"].dropna(axis=1, thresh=max(60, len(data) // 2))
    returns = np.log(closes / closes.shift(1)).dropna()
    corr_matrix = returns.corr()

    # Hierarchical clustering order
    from scipy.cluster.hierarchy import linkage, leaves_list
    from scipy.spatial.distance import squareform

    dist_matrix = 1 - corr_matrix.abs()
    np.fill_diagonal(dist_matrix.values, 0)
    condensed = squareform(dist_matrix)
    linkage_matrix = linkage(condensed, method="ward")
    order = leaves_list(linkage_matrix)
    ordered_tickers = [corr_matrix.columns[i] for i in order]

    # Reorder matrix
    clustered = corr_matrix.loc[ordered_tickers, ordered_tickers]

    return clustered, returns

Note: if scipy is not available, fall back to sorting by average correlation instead of hierarchical clustering.

C2: Present results

  1. Full correlation matrix — formatted as a table. For more than 8 tickers, show as a heatmap description or highlight only the strongest/weakest pairs.

  2. Identified clusters — group tickers that have high intra-group correlation:

    • Cluster 1: [NVDA, AMD, AVGO] — avg intra-correlation 0.82
    • Cluster 2: [AAPL, MSFT] — avg intra-correlation 0.75
  3. Outliers — tickers with low average correlation to the group (potential diversifiers).

  4. Strongest pairs — top 5 highest-correlation pairs in the matrix.

  5. Weakest pairs — top 5 lowest/negative-correlation pairs (hedging candidates).


Sub-Skill D: Realized Correlation

Goal: Show how correlation changes over time and under different market conditions.

D1: Rolling correlation

import yfinance as yf
import pandas as pd
import numpy as np

def realized_correlation(ticker_a, ticker_b, period="2y", windows=[20, 60, 120]):
    data = yf.download([ticker_a, ticker_b], period=period, auto_adjust=True, progress=False)
    closes = data["Close"][[ticker_a, ticker_b]].dropna()

    returns = np.log(closes / closes.shift(1)).dropna()

    rolling = {}
    for w in windows:
        rolling[f"{w}d"] = returns[ticker_a].rolling(w).corr(returns[ticker_b])

    return rolling, returns

D2: Regime-conditional correlation

def regime_correlation(returns, ticker_a, ticker_b, condition_ticker=None):
    """Compare correlation across up/down/volatile regimes."""
    if condition_ticker is None:
        condition_ticker = ticker_a

    ret = returns[condition_ticker]

    regimes = {
        "All Days": pd.Series(True, index=returns.index),
        "Up Days (target > 0)": ret > 0,
        "Down Days (target < 0)": ret < 0,
        "High Vol (top 25%)": ret.abs() > ret.abs().quantile(0.75),
        "Low Vol (bottom 25%)": ret.abs() < ret.abs().quantile(0.25),
        "Large Drawdown (< -2%)": ret < -0.02,
    }

    results = {}
    for name, mask in regimes.items():
        subset = returns[mask]
        if len(subset) >= 20:
            results[name] = {
                "correlation": round(subset[ticker_a].corr(subset[ticker_b]), 4),
                "days": int(mask.sum()),
            }

    return results

D3: Present results

  1. Rolling correlation summary table (illustrative values here and in the regime table):
Window Current Mean Min Max Std
20-day 0.88 0.76 0.32 0.95 0.12
60-day 0.82 0.78 0.55 0.92 0.08
120-day 0.80 0.79 0.68 0.88 0.05
  1. Regime correlation table:
Regime Correlation Days
All Days 0.82 250
Up Days 0.75 132
Down Days 0.87 118
High Vol (top 25%) 0.90 63
Large Drawdown (< -2%) 0.93 28
  1. Key insight: Highlight whether correlation increases during sell-offs (very common — "correlations go to 1 in a crisis"). This is critical for risk management.

  2. Trend: Is correlation trending higher or lower recently vs. its historical average?


Step 3: Respond to the User

After running the appropriate sub-skill, present results clearly:

Always include

  • The lookback period and data interval used
  • The number of observations (trading days)
  • Any tickers dropped due to insufficient data

Always caveat

  • Correlation is not causation — co-movement does not imply a causal link
  • Past correlation does not guarantee future correlation — regimes shift
  • Short lookback windows produce noisy estimates; longer windows smooth but may miss regime changes

Practical applications (mention when relevant)

  • Sympathy plays: Stocks likely to follow a peer's earnings/news move
  • Pair trading: High-correlation pairs where the spread has diverged from its mean
  • Portfolio diversification: Finding low-correlation assets to reduce risk
  • Hedging: Identifying inversely correlated instruments
  • Sector rotation: Understanding which sectors move together
  • Risk management: Correlation spikes during stress — diversification may fail when needed most

Present the data and let the user draw conclusions; don't recommend specific trades.


Reference Files

  • references/sector_universes.md — Dynamic peer universe construction using yfinance Screener API

Read the reference file when you need to build a peer universe for a given ticker.

Files (finance-skills)
  • references
    • sector_universes.md 3.7 KB
      Loading…
  • README.md 2.1 KB
    # stock-correlation
    
    Analyze stock correlations to find related companies, sector peers, and pair-trading candidates using historical price data.
    
    ## What it does
    
    Routes to four specialized sub-skills based on user intent:
    
    - **Co-movement Discovery** — given a single ticker, find the most correlated stocks from curated sector and thematic peer universes (e.g., "what correlates with NVDA?")
    - **Return Correlation** — deep-dive pairwise analysis between two tickers: Pearson correlation, beta, R-squared, spread Z-score, and rolling stability (e.g., "correlation between AMD and NVDA")
    - **Sector Clustering** — full NxN correlation matrix with hierarchical clustering to identify groups and outliers (e.g., "correlation matrix for FAANG")
    - **Realized Correlation** — time-varying and regime-conditional correlation: rolling windows (20/60/120-day), up vs down days, high-vol vs low-vol, drawdown regimes (e.g., "when NVDA drops what else drops?")
    
    ## Triggers
    
    - "what correlates with NVDA", "find stocks related to AMD"
    - "correlation between AAPL and MSFT", "how do LITE and COHR move together"
    - "what moves with", "stocks that move together", "sympathy plays"
    - "sector peers", "pair trading", "hedging pair"
    - "when NVDA drops what else drops", "rolling correlation"
    - "correlation matrix for FAANG", "cluster these stocks"
    - Well-known pairs: AMD/NVDA, GOOGL/AVGO, LITE/COHR
    
    ## Prerequisites
    
    - Python 3.8+
    - The skill auto-installs `yfinance`, `pandas`, and `numpy` via pip if not already present
    - `scipy` is optional (used for hierarchical clustering in Sector Clustering sub-skill; falls back to sorting if unavailable)
    
    ## Platform
    
    Works on **all platforms** (Claude Code, Claude.ai with code execution, etc.).
    
    ## Setup
    
    ```bash
    # Choose finance-market-analysis when prompted.
    npx plugins add himself65/finance-skills
    
    # Or install just this skill
    npx skills add himself65/finance-skills --skill stock-correlation
    ```
    
    See the [main README](../../../../README.md) for more installation options.
    
    ## Reference files
    
    - `references/sector_universes.md` — Dynamic peer universe construction using yfinance Screener API, with fallback strategies
    
  • SKILL.md 13.4 KB
    ---
    name: stock-correlation
    description: >
      Analyze how stocks move together using Yahoo Finance price history (yfinance): find
      correlated peers for a ticker, measure correlation, beta, and spread between
      specific tickers, cluster a group into a correlation matrix, and track rolling or
      regime-dependent correlation. Use this skill whenever the user asks what moves with
      a stock, what else drops when it drops, related tickers or sympathy plays, sector or
      supply-chain peers, pair trading or hedging pairs, beta or relative performance,
      correlation matrices, co-movement, or rolling/realized correlation — including
      well-known pairs like AMD/NVDA, GOOGL/AVGO, or LITE/COHR. With a single ticker,
      assume the user wants its correlated peers.
    ---
    
    # Stock Correlation Analysis Skill
    
    If your host has not already executed the shell probes below (for example, in Codex), run the commands inside the `!` wrappers with its shell tool before choosing a setup path. Use the actual output; if shell access is unavailable, report that limitation.
    
    Finds and analyzes correlated stocks using historical price data from Yahoo Finance via [yfinance](https://github.com/ranaroussi/yfinance). Routes to specialized sub-skills based on user intent.
    
    **Important**: This is for research and educational purposes only. Not financial advice. yfinance is not affiliated with Yahoo, Inc.
    
    ---
    
    ## Step 1: Ensure Dependencies Are Available
    
    **Current environment status:**
    
    ```
    !`python3 -c "exec('try:\n import yfinance, pandas, numpy\n print(f\'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}\')\nexcept Exception:\n print(\'DEPS_MISSING\')')"`
    ```
    
    If `DEPS_MISSING`, install required packages before running any code:
    
    ```python
    import subprocess, sys
    subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"])
    ```
    
    If all dependencies are already installed, skip the install step and proceed directly.
    
    ---
    
    ## Step 2: Route to the Correct Sub-Skill
    
    Classify the user's request and jump to the matching sub-skill section below.
    
    | User Request | Route To | Examples |
    |---|---|---|
    | Single ticker, wants to find related stocks | **Sub-Skill A: Co-movement Discovery** | "what correlates with NVDA", "find stocks related to AMD", "sympathy plays for TSLA" |
    | Two or more specific tickers, wants relationship details | **Sub-Skill B: Return Correlation** | "correlation between AMD and NVDA", "how do LITE and COHR move together", "compare AAPL vs MSFT" |
    | Group of tickers, wants structure/grouping | **Sub-Skill C: Sector Clustering** | "correlation matrix for FAANG", "cluster these semiconductor stocks", "sector peers for AMD" |
    | Wants time-varying or conditional correlation | **Sub-Skill D: Realized Correlation** | "rolling correlation AMD NVDA", "when NVDA drops what else drops", "how has correlation changed" |
    
    If ambiguous, default to **Sub-Skill A** (Co-movement Discovery) for single tickers, or **Sub-Skill B** (Return Correlation) for two tickers.
    
    ### Defaults for all sub-skills
    
    | Parameter | Default |
    |---|---|
    | Lookback period | `1y` (1 year) |
    | Data interval | `1d` (daily) |
    | Correlation method | Pearson |
    | Minimum correlation threshold | 0.60 |
    | Number of results | Top 10 |
    | Return type | Daily log returns |
    | Rolling window | 60 trading days |
    
    ---
    
    ## Sub-Skill A: Co-movement Discovery
    
    **Goal**: Given a single ticker, find stocks that move with it.
    
    ### A1: Build the peer universe
    
    You need 15-30 candidates. **Do not use hardcoded ticker lists** — build the universe dynamically at runtime. See `references/sector_universes.md` for the full implementation. The approach:
    
    1. **Screen same-industry stocks** using `yf.screen()` + `yf.EquityQuery` to find stocks in the same industry as the target
    2. **Broaden to sector** if the industry screen returns fewer than 10 peers
    3. **Add thematic/adjacent industries** — read the target's `longBusinessSummary` and screen 1-2 related industries (e.g., a semiconductor company → also screen semiconductor equipment)
    4. **Combine, deduplicate, remove target ticker**
    
    ### A2: Compute correlations
    
    ```python
    import yfinance as yf
    import pandas as pd
    import numpy as np
    
    def discover_comovement(target_ticker, peer_tickers, period="1y"):
        all_tickers = [target_ticker] + [t for t in peer_tickers if t != target_ticker]
        data = yf.download(all_tickers, period=period, auto_adjust=True, progress=False)
    
        # Extract close prices — yf.download returns MultiIndex (Price, Ticker) columns
        closes = data["Close"].dropna(axis=1, thresh=max(60, len(data) // 2))
    
        # Log returns
        returns = np.log(closes / closes.shift(1)).dropna()
        corr_series = returns.corr()[target_ticker].drop(target_ticker, errors="ignore")
    
        # Rank by absolute correlation
        ranked = corr_series.abs().sort_values(ascending=False)
    
        result = pd.DataFrame({
            "Ticker": ranked.index,
            "Correlation": [round(corr_series[t], 4) for t in ranked.index],
        })
        return result, returns
    ```
    
    ### A3: Present results
    
    Show a ranked table with company names and sectors (fetch via `yf.Ticker(t).info.get("shortName")`). Values below are illustrative:
    
    | Rank | Ticker | Company | Correlation | Why linked |
    |---|---|---|---|---|
    | 1 | AMD | Advanced Micro Devices | 0.82 | Same industry — GPU/CPU |
    | 2 | AVGO | Broadcom | 0.78 | AI infrastructure peer |
    
    Include:
    - Top 10 positively correlated stocks
    - Any notable negatively correlated stocks (potential hedges)
    - Brief explanation of **why** each might be linked (sector, supply chain, customer overlap)
    
    ---
    
    ## Sub-Skill B: Return Correlation
    
    **Goal**: Deep-dive into the relationship between two (or a few) specific tickers.
    
    ### B1: Download and compute
    
    ```python
    import yfinance as yf
    import pandas as pd
    import numpy as np
    
    def return_correlation(ticker_a, ticker_b, period="1y"):
        data = yf.download([ticker_a, ticker_b], period=period, auto_adjust=True, progress=False)
        closes = data["Close"][[ticker_a, ticker_b]].dropna()
    
        returns = np.log(closes / closes.shift(1)).dropna()
        corr = returns[ticker_a].corr(returns[ticker_b])
    
        # Beta: how much does B move per unit move of A
        cov_matrix = returns.cov()
        beta = cov_matrix.loc[ticker_b, ticker_a] / cov_matrix.loc[ticker_a, ticker_a]
    
        # R-squared
        r_squared = corr ** 2
    
        # Rolling 60-day correlation for stability
        rolling_corr = returns[ticker_a].rolling(60).corr(returns[ticker_b])
    
        # Spread (log price ratio) for mean-reversion
        spread = np.log(closes[ticker_a] / closes[ticker_b])
        spread_z = (spread - spread.mean()) / spread.std()
    
        return {
            "correlation": round(corr, 4),
            "beta": round(beta, 4),
            "r_squared": round(r_squared, 4),
            "rolling_corr_mean": round(rolling_corr.mean(), 4),
            "rolling_corr_std": round(rolling_corr.std(), 4),
            "rolling_corr_min": round(rolling_corr.min(), 4),
            "rolling_corr_max": round(rolling_corr.max(), 4),
            "spread_z_current": round(spread_z.iloc[-1], 4),
            "observations": len(returns),
        }
    ```
    
    ### B2: Present results
    
    Show a summary card (illustrative values):
    
    | Metric | Value |
    |---|---|
    | Pearson Correlation | 0.82 |
    | Beta (B vs A) | 1.15 |
    | R-squared | 0.67 |
    | Rolling Corr (60d avg) | 0.80 |
    | Rolling Corr Range | [0.55, 0.94] |
    | Rolling Corr Std Dev | 0.08 |
    | Spread Z-Score (current) | +1.2 |
    | Observations | 250 |
    
    Interpretation guide:
    - **Correlation > 0.80**: Strong co-movement — these stocks are tightly linked
    - **Correlation 0.50–0.80**: Moderate — shared sector drivers but independent factors too
    - **Correlation < 0.50**: Weak — limited co-movement despite possible sector overlap
    - **High rolling std**: Unstable relationship — correlation varies significantly over time
    - **Spread Z > |2|**: Unusual divergence from historical relationship
    
    ---
    
    ## Sub-Skill C: Sector Clustering
    
    **Goal**: Given a group of tickers, show the full correlation structure and identify clusters.
    
    ### C1: Build the correlation matrix
    
    ```python
    import yfinance as yf
    import pandas as pd
    import numpy as np
    
    def sector_clustering(tickers, period="1y"):
        data = yf.download(tickers, period=period, auto_adjust=True, progress=False)
    
        # yf.download returns MultiIndex (Price, Ticker) columns
        closes = data["Close"].dropna(axis=1, thresh=max(60, len(data) // 2))
        returns = np.log(closes / closes.shift(1)).dropna()
        corr_matrix = returns.corr()
    
        # Hierarchical clustering order
        from scipy.cluster.hierarchy import linkage, leaves_list
        from scipy.spatial.distance import squareform
    
        dist_matrix = 1 - corr_matrix.abs()
        np.fill_diagonal(dist_matrix.values, 0)
        condensed = squareform(dist_matrix)
        linkage_matrix = linkage(condensed, method="ward")
        order = leaves_list(linkage_matrix)
        ordered_tickers = [corr_matrix.columns[i] for i in order]
    
        # Reorder matrix
        clustered = corr_matrix.loc[ordered_tickers, ordered_tickers]
    
        return clustered, returns
    ```
    
    Note: if `scipy` is not available, fall back to sorting by average correlation instead of hierarchical clustering.
    
    ### C2: Present results
    
    1. **Full correlation matrix** — formatted as a table. For more than 8 tickers, show as a heatmap description or highlight only the strongest/weakest pairs.
    
    2. **Identified clusters** — group tickers that have high intra-group correlation:
       - Cluster 1: [NVDA, AMD, AVGO] — avg intra-correlation 0.82
       - Cluster 2: [AAPL, MSFT] — avg intra-correlation 0.75
    
    3. **Outliers** — tickers with low average correlation to the group (potential diversifiers).
    
    4. **Strongest pairs** — top 5 highest-correlation pairs in the matrix.
    
    5. **Weakest pairs** — top 5 lowest/negative-correlation pairs (hedging candidates).
    
    ---
    
    ## Sub-Skill D: Realized Correlation
    
    **Goal**: Show how correlation changes over time and under different market conditions.
    
    ### D1: Rolling correlation
    
    ```python
    import yfinance as yf
    import pandas as pd
    import numpy as np
    
    def realized_correlation(ticker_a, ticker_b, period="2y", windows=[20, 60, 120]):
        data = yf.download([ticker_a, ticker_b], period=period, auto_adjust=True, progress=False)
        closes = data["Close"][[ticker_a, ticker_b]].dropna()
    
        returns = np.log(closes / closes.shift(1)).dropna()
    
        rolling = {}
        for w in windows:
            rolling[f"{w}d"] = returns[ticker_a].rolling(w).corr(returns[ticker_b])
    
        return rolling, returns
    ```
    
    ### D2: Regime-conditional correlation
    
    ```python
    def regime_correlation(returns, ticker_a, ticker_b, condition_ticker=None):
        """Compare correlation across up/down/volatile regimes."""
        if condition_ticker is None:
            condition_ticker = ticker_a
    
        ret = returns[condition_ticker]
    
        regimes = {
            "All Days": pd.Series(True, index=returns.index),
            "Up Days (target > 0)": ret > 0,
            "Down Days (target < 0)": ret < 0,
            "High Vol (top 25%)": ret.abs() > ret.abs().quantile(0.75),
            "Low Vol (bottom 25%)": ret.abs() < ret.abs().quantile(0.25),
            "Large Drawdown (< -2%)": ret < -0.02,
        }
    
        results = {}
        for name, mask in regimes.items():
            subset = returns[mask]
            if len(subset) >= 20:
                results[name] = {
                    "correlation": round(subset[ticker_a].corr(subset[ticker_b]), 4),
                    "days": int(mask.sum()),
                }
    
        return results
    ```
    
    ### D3: Present results
    
    1. **Rolling correlation summary table** (illustrative values here and in the regime table):
    
    | Window | Current | Mean | Min | Max | Std |
    |---|---|---|---|---|---|
    | 20-day | 0.88 | 0.76 | 0.32 | 0.95 | 0.12 |
    | 60-day | 0.82 | 0.78 | 0.55 | 0.92 | 0.08 |
    | 120-day | 0.80 | 0.79 | 0.68 | 0.88 | 0.05 |
    
    2. **Regime correlation table**:
    
    | Regime | Correlation | Days |
    |---|---|---|
    | All Days | 0.82 | 250 |
    | Up Days | 0.75 | 132 |
    | Down Days | 0.87 | 118 |
    | High Vol (top 25%) | 0.90 | 63 |
    | Large Drawdown (< -2%) | 0.93 | 28 |
    
    3. **Key insight**: Highlight whether correlation **increases during sell-offs** (very common — "correlations go to 1 in a crisis"). This is critical for risk management.
    
    4. **Trend**: Is correlation trending higher or lower recently vs. its historical average?
    
    ---
    
    ## Step 3: Respond to the User
    
    After running the appropriate sub-skill, present results clearly:
    
    ### Always include
    
    - The **lookback period** and **data interval** used
    - The **number of observations** (trading days)
    - Any tickers **dropped due to insufficient data**
    
    ### Always caveat
    
    - **Correlation is not causation** — co-movement does not imply a causal link
    - **Past correlation does not guarantee future correlation** — regimes shift
    - **Short lookback windows** produce noisy estimates; longer windows smooth but may miss regime changes
    
    ### Practical applications (mention when relevant)
    
    - **Sympathy plays**: Stocks likely to follow a peer's earnings/news move
    - **Pair trading**: High-correlation pairs where the spread has diverged from its mean
    - **Portfolio diversification**: Finding low-correlation assets to reduce risk
    - **Hedging**: Identifying inversely correlated instruments
    - **Sector rotation**: Understanding which sectors move together
    - **Risk management**: Correlation spikes during stress — diversification may fail when needed most
    
    Present the data and let the user draw conclusions; don't recommend specific trades.
    
    ---
    
    ## Reference Files
    
    - `references/sector_universes.md` — Dynamic peer universe construction using yfinance Screener API
    
    Read the reference file when you need to build a peer universe for a given ticker.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.