$npx -y skills add himself65/finance-skills --skill etf-premiumCalculate ETF premium/discount vs NAV via Yahoo Finance, and decompose single-day surges into NAV-driven vs structural components (gamma squeeze, dealer hedging, blocked AP arbitrage). Use whenever the user asks about an ETF's premium or discount, NAV comparison, why an ETF diver
| 1 | # ETF Premium/Discount Analysis Skill |
| 2 | |
| 3 | Calculates the premium or discount of an ETF's market price relative to its Net Asset Value (NAV) using data from Yahoo Finance via [yfinance](https://github.com/ranaroussi/yfinance). |
| 4 | |
| 5 | **Why this matters:** An ETF's market price can diverge from the value of its underlying holdings (NAV). When you buy at a premium, you're overpaying relative to the assets; at a discount, you're getting a bargain. This divergence is typically small for liquid US equity ETFs but can be significant for bond ETFs, international ETFs, leveraged/inverse products, and crypto ETFs — especially during periods of market stress. |
| 6 | |
| 7 | **Important**: For research and educational purposes only. Not financial advice. yfinance is not affiliated with Yahoo, Inc. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Step 1: Ensure Dependencies Are Available |
| 12 | |
| 13 | **Current environment status:** |
| 14 | |
| 15 | ``` |
| 16 | !`python3 -c "import yfinance, pandas, numpy; print(f'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}')" 2>/dev/null || echo "DEPS_MISSING"` |
| 17 | ``` |
| 18 | |
| 19 | If `DEPS_MISSING`, install required packages: |
| 20 | |
| 21 | ```python |
| 22 | import subprocess, sys |
| 23 | subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"]) |
| 24 | ``` |
| 25 | |
| 26 | If already installed, skip and proceed. |
| 27 | |
| 28 | --- |
| 29 | |
| 30 | ## Step 2: Route to the Correct Sub-Skill |
| 31 | |
| 32 | Classify the user's request and jump to the matching section. If the user asks a general question about an ETF's premium or discount without specifying a particular analysis type, default to **Sub-Skill A** (Single ETF Snapshot). |
| 33 | |
| 34 | | User Request | Route To | Examples | |
| 35 | |---|---|---| |
| 36 | | Single ETF premium/discount | **Sub-Skill A: Single ETF Snapshot** | "is SPY at a premium?", "AGG premium to NAV", "BITO premium" | |
| 37 | | Compare multiple ETFs | **Sub-Skill B: Multi-ETF Comparison** | "compare bond ETF discounts", "which has bigger premium IBIT or BITO", "rank these ETFs by premium" | |
| 38 | | Screener / find extreme premiums | **Sub-Skill C: Premium Screener** | "which ETFs have biggest discount", "find ETFs trading below NAV", "premium screener" | |
| 39 | | Deep analysis with context | **Sub-Skill D: Premium Deep Dive** | "why is HYG at a discount", "is ARKK premium normal", "ETF premium analysis with context" | |
| 40 | | Sudden premium surge / gamma squeeze | **Sub-Skill E: Premium Surge Decomposition** | "why did KWEB jump 13% today", "is this ETF rally driven by gamma", "decompose today's ETF move", "dealer GEX for SOXL", "how long until the premium converges" | |
| 41 | |
| 42 | ### Defaults |
| 43 | |
| 44 | | Parameter | Default | |
| 45 | |---|---| |
| 46 | | Data source | yfinance `navPrice` field | |
| 47 | | Price field | `regularMarketPrice` (falls back to `previousClose`) | |
| 48 | | Screener universe | Common ETF list by category (see Sub-Skill C) | |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## Sub-Skill A: Single ETF Snapshot |
| 53 | |
| 54 | **Goal**: Show the current premium/discount for one ETF with context about what's normal, plus a peer comparison to show how it stacks up against similar ETFs. |
| 55 | |
| 56 | ### A1: Fetch and compute |
| 57 | |
| 58 | ```python |
| 59 | import yfinance as yf |
| 60 | |
| 61 | # Peer groups by category — used to automatically compare the target ETF against its closest peers |
| 62 | CATEGORY_PEERS = { |
| 63 | "Digital Assets": ["IBIT", "BITO", "FBTC", "ETHA", "ARKB", "GBTC"], |
| 64 | "Intermediate Core Bond": ["AGG", "BND", "SCHZ"], |
| 65 | "High Yield Bond": ["HYG", "JNK", "USHY"], |
| 66 | "Long Government": ["TLT", "VGLT", "SPTL"], |
| 67 | "Emerging Markets Bond": ["EMB", "VWOB", "PCY"], |
| 68 | "Large Growth": ["QQQ", "VUG", "IWF", "SCHG"], |
| 69 | "Large Blend": ["SPY", "VOO", "IVV", "VTI"], |
| 70 | "Commodities Focused": ["GLD", "IAU", "SLV", "DBC"], |
| 71 | "China Region": ["KWEB", "FXI", "MCHI"], |
| 72 | "Trading--Leveraged Equity": ["TQQQ", "UPRO", "SOXL", "JNUG"], |
| 73 | "Trading--Inverse Equity": ["SQQQ", "SPXU", "SOXS", "JDST"], |
| 74 | "Derivative Income": ["JEPI", "JEPQ", "QYLD"], |
| 75 | "Large Value": ["SCHD", "VYM", "DVY", "HDV"], |
| 76 | } |
| 77 | |
| 78 | def etf_premium_snapshot(ticker_symbol): |
| 79 | ticker = yf.Ticker(ticker_symbol) |
| 80 | info = ticker.info |
| 81 | |
| 82 | # Verify this is an ETF |
| 83 | quote_type = info.get("quoteType", "") |
| 84 | if quote_type != " |