import { useState, useMemo, useEffect, useCallback } from "react"; // ── Shared style values ─────────────────────────────────────────────────────── const S = { sans: "'DM Sans','Segoe UI',sans-serif", mono: "'Space Mono',monospace", bg: "#080c12", bg1: "#0b1118", bg2: "#0d1520", bg3: "#0d1a26", bdr: "#1e2d3d", bdr2: "#1a2535", dim: "#4a6278", dim2: "#2e4560", txt: "#e2e8f0", txt2: "#7a8fa3", acc: "#38bdf8", grn: "#22c55e", red: "#ef4444", }; const FINNHUB_KEY = "d6ittn9r01qleu95i6tgd6ittn9r01qleu95i6u0"; const FRED_KEY = "d89c1f35b1f2338af784ee9599731d52"; // Symbol maps const FH = (t) => ({ "BRK/B":"BRK.B","BTC":"BINANCE:BTCUSDT","ETH":"BINANCE:ETHUSDT" })[t] || t; const isCrypto = (t) => t === "BTC" || t === "ETH"; // ── Finnhub: daily candles — free tier supports resolution=D ───────────────── // One call per ticker. 1 year of daily data covers all metrics. // 5Y sparkline uses sampled 1Y data (best available on free tier). async function fetchTicker(ticker) { try { const sym=FH(ticker); const to=Math.floor(Date.now()/1000); const from1y=to-Math.round(1.05*365*86400); const ep=isCrypto(ticker)?"crypto":"stock"; const url=`https://finnhub.io/api/v1/${ep}/candle?symbol=${sym}&resolution=D&from=${from1y}&to=${to}&token=${FINNHUB_KEY}`; const d=await (await fetch(url)).json(); if (d.s!=="ok"||!d.c?.length) return null; const closes=d.c, stamps=d.t, n=closes.length; const price=closes[n-1], prev=closes[n-2]; const now=Date.now()/1000; const find=(cutoff)=>{ let i=stamps.findIndex(t=>t>=cutoff); return i<0?0:i; }; const pct=(i)=>closes[i]?((price-closes[i])/closes[i])*100:null; // Sparkline from full year of daily data (~250 points → downsample to 80) const step=Math.max(1,Math.floor(n/80)); const sparkRaw=[]; for(let i=0;isMin?sparkRaw.map(v=>(v-sMin)/(sMax-sMin)):sparkRaw.map(()=>0.5); return { price, spark, dailyPct: prev?((price-prev)/prev)*100:null, ytdPct: pct(find(new Date(new Date().getFullYear(),0,1).getTime()/1000)), sixMoPct: pct(find(now-180*86400)), oneyPct: pct(find(now-365*86400)), fiveyPct: null, // 5Y needs premium tier — shown as — for now }; } catch(e) { console.error("fetchTicker",ticker,e?.message||e); return null; } } // ── Yahoo Finance: 5Y return only (free, supplements Finnhub) ──────────────── const YF_SYM = (t) => ({"BRK/B":"BRK-B","BTC":"BTC-USD","ETH":"ETH-USD"})[t] || t; async function fetch5Y(ticker) { try { const url = "https://corsproxy.io/?url=" + encodeURIComponent( `https://query1.finance.yahoo.com/v8/finance/chart/${YF_SYM(ticker)}?interval=1mo&range=5y` ); const d = await (await fetch(url)).json(); const closes = d?.chart?.result?.[0]?.indicators?.quote?.[0]?.close?.filter(Boolean); if (!closes?.length) return null; const first = closes[0], last = closes[closes.length-1]; return first ? ((last-first)/first)*100 : null; } catch { return null; } } // ── FRED: generic series fetch (desc, most recent first) ────────────────────── async function fredSeries(id, limit=5) { try { const url=`https://api.stlouisfed.org/fred/series/observations?series_id=${id}&api_key=${FRED_KEY}&sort_order=desc&limit=${limit}&file_type=json`; const d=await (await fetch(url)).json(); return (d.observations||[]).filter(o=>o.value!==".").map(o=>parseFloat(o.value)); } catch { return []; } } // ── FRED: yield curve ───────────────────────────────────────────────────────── async function fetchYieldCurve() { const SERIES={"3M":"DGS3MO","2Y":"DGS2","5Y":"DGS5","10Y":"DGS10","30Y":"DGS30"}; const results={}; await Promise.all(Object.entries(SERIES).map(async ([label,id])=>{ const v=await fredSeries(id,3); results[label]={ cur:v[0]??null, prev:v[1]??null }; })); return results; } // ── FRED: macro regime data ─────────────────────────────────────────────────── async function fetchMacroData() { const [be,indpro,ff,cpi,m2,will,gdp,lei,ig,hy,cn,eu]=await Promise.all([ fredSeries("T10YIE",35), // 10Y breakeven inflation, daily fredSeries("INDPRO",6), // industrial production fredSeries("FEDFUNDS",2), // fed funds rate fredSeries("CPIAUCSL",14), // CPI index 14mo for YoY fredSeries("M2SL",14), // M2 money supply 14mo fredSeries("WILL5000INDFC",2), // Wilshire 5000 mkt cap ($B) fredSeries("GDP",2), // US nominal GDP ($B, quarterly) fredSeries("USSLIND",7), // Leading Economic Index fredSeries("BAMLC0A0CM",2), // IG credit OAS spread fredSeries("BAMLH0A0HYM2",2), // HY credit OAS spread fredSeries("CHPMINDX",2), // China Manufacturing PMI fredSeries("GEPMI",2), // Eurozone Composite PMI ]); const inflationRising = be.length>=30 ? be[0]>be[29] : null; const growthRising = indpro.length>=4 ? indpro[0]>indpro[3] : null; const regime = (inflationRising===null||growthRising===null) ? null : ( inflationRising&& growthRising) ? "Reflation" : ( inflationRising&&!growthRising) ? "Stagflation" : (!inflationRising&& growthRising) ? "Goldilocks" : "Deflation"; const cpiYoY = cpi.length>=13 ? ((cpi[0]/cpi[12])-1)*100 : null; const m2YoY = m2.length>=13 ? ((m2[0]/m2[12])-1)*100 : null; const m2Prev = m2.length>=14 ? ((m2[1]/m2[13])-1)*100 : null; const realFF = (ff[0]!=null&&cpiYoY!=null) ? ff[0]-cpiYoY : null; const buffett = (will[0]&&gdp[0]) ? (will[0]/gdp[0])*100 : null; const buffettPrev = (will[1]&&gdp[0]) ? (will[1]/gdp[0])*100 : null; const leiChg = lei.length>=7 ? lei[0]-lei[6] : null; return { regime, inflationRising, growthRising, fedFunds:ff[0]??null, fedFundsPrev:ff[1]??null, breakeven:be[0]??null, cpiYoY, realFF, m2YoY, m2Prev, buffett, buffettPrev, leiLatest:lei[0]??null, leiChg, igSpread:ig[0]??null, igPrev:ig[1]??null, hySpread:hy[0]??null, hyPrev:hy[1]??null, cnPMI:cn[0]??null, cnPrev:cn[1]??null, euPMI:eu[0]??null, euPrev:eu[1]??null, }; } // ── Dalio: tags favored in each regime ──────────────────────────────────────── const REGIME_TAGS={ "Goldilocks": new Set(["Tech","Financials","Insurance","Healthcare","Consumer Staples","Industrial","Defense & Aerospace","Small Cap","Homebuilder"]), "Reflation": new Set(["Energy","Pipelines","Mining & Metals","Precious Metals","Streaming & Royalty","Copper","Land & Royalty","Agriculture","Farmland","International","Energy Transition","Uranium","REIT"]), "Stagflation":new Set(["Precious Metals","Streaming & Royalty","Energy","Land & Royalty","Farmland","Agriculture","Copper","Uranium","Crypto"]), "Deflation": new Set(["Fixed Income","Healthcare","Consumer Staples","REIT","Water"]), }; const REGIME_COLOR={"Goldilocks":"#22c55e","Reflation":"#f97316","Stagflation":"#ef4444","Deflation":"#3b82f6"}; const REGIME_ASSETS={ "Goldilocks": "Equities · Credit · Growth stocks", "Reflation": "Commodities · EM · TIPS · Energy", "Stagflation":"Gold · Commodities · Cash · TIPS", "Deflation": "Treasuries · Gold · Utilities", }; function isInSeason(tags,regime){ if(!regime||!REGIME_TAGS[regime]) return false; return tags.some(t=>REGIME_TAGS[regime].has(t)); } // ── Helpers ──────────────────────────────────────────────────────────────────── function pctColor(v){ if(v==null) return "#4a6278"; return v>=0?"#22c55e":"#ef4444"; } function pctFmt(v) { if(v==null) return "—"; return (v>=0?"+":"")+v.toFixed(2)+"%"; } // ── YieldCurve component ─────────────────────────────────────────────────────── function YieldCurve({ yields }) { const NODES=[["3M",10],["2Y",36],["5Y",62],["10Y",88],["30Y",114]]; const vals=NODES.map(([k])=>yields[k]?.cur).filter(v=>v!=null); const sans="'DM Sans','Segoe UI',sans-serif", mono="'Space Mono',monospace"; if (vals.length<2) return Loading…; const H=44,W=124,pad=4; const mn=Math.min(...vals)-0.05, mx=Math.max(...vals)+0.05; const toY=v=>H-((v-mn)/(mx-mn))*(H-pad*2)-pad; const pathD=NODES.map(([k,x],i)=>{ const v=yields[k]?.cur; if(v==null)return null; return `${i===0?"M":"L"}${x},${toY(v).toFixed(1)}`; }).filter(Boolean).join(" "); const [twoY,tenY,threeM]=[yields["2Y"]?.cur,yields["10Y"]?.cur,yields["3M"]?.cur]; const s2s10=(twoY&&tenY)?tenY-twoY:null; const s3m10=(threeM&&tenY)?tenY-threeM:null; const shape=s2s10===null?"—":s2s10>0.5?"Normal ▲":s2s10<-0.1?"Inverted ▼":"Flat →"; const shapeColor=s2s10===null?"#4a6278":s2s10>0.5?"#22c55e":s2s10<-0.1?"#ef4444":"#f59e0b"; return (
{NODES.map(([k,x])=>{ const v=yields[k]?.cur; if(v==null)return null; const prev=yields[k]?.prev; const chg=prev!=null?v-prev:0; return ( {k} {v.toFixed(2)} {Math.abs(chg)>0.001&&0?"#ef4444":"#22c55e"} fontSize="7" fontFamily={S.sans}>{chg>0?"+":""}{chg.toFixed(2)}} ); })}
CURVE SHAPE
{shape}
{s2s10!==null&&
2s/10s: {s2s10>=0?"+":""}{s2s10.toFixed(2)}%
} {s3m10!==null&&
3m/10y: {s3m10>=0?"+":""}{s3m10.toFixed(2)}%
}
); } // ── Macro helpers ───────────────────────────────────────────────────────────── function arrow(cur, prev) { if (cur==null||prev==null) return ""; return cur > prev ? " ↑" : cur < prev ? " ↓" : " →"; } function signal(val, goodAbove, warnAbove) { // Returns color: green / yellow / red if (val==null) return S.dim; if (goodAbove!=null && val>=goodAbove) return S.grn; if (warnAbove!=null && val>=warnAbove) return "#f59e0b"; return S.red; } function MacroTile({ label, value, sub, color, wide }) { return (
{label}
{value??}
{sub &&
{sub}
}
); } function Divider() { return
; } // ── Dalio Regime Widget (unchanged logic) ───────────────────────────────────── function DalioRegime({ macro }) { const { regime, inflationRising, growthRising } = macro||{}; const color=REGIME_COLOR[regime]||S.dim; const assets=REGIME_ASSETS[regime]||""; const QUADS=[["Stagflation",0,0],["Reflation",0,1],["Deflation",1,0],["Goldilocks",1,1]]; return (
DALIO ECONOMIC MACHINE
↑ INFLATION
{QUADS.map(([r])=>{ const active=r===regime; const qc=REGIME_COLOR[r]||S.dim2; return (
{r.toUpperCase()}
); })}
↓ Growth Growth ↑
{regime||Loading…}
{regime&&<>
Growth {growthRising?"↑":"↓"} · Inflation {inflationRising?"↑":"↓"}
{assets}
}
); } // ── Full Macro Panel (2 rows) ───────────────────────────────────────────────── function MacroPanel({ yields, macro, loading, progress, lastUpdated, onRefresh }) { const m = macro || {}; // Row 1: Regime indicators // Buffett: >140% expensive, 100-140 fair, <100 cheap const buffColor = m.buffett==null ? S.dim : m.buffett>140 ? S.red : m.buffett>100 ? "#f59e0b" : S.grn; const buffPctChg = (m.buffett&&m.buffettPrev) ? m.buffett-m.buffettPrev : null; // Real FF: >2 tight (red), 0-2 neutral (yellow), <0 loose (green) const realFFColor = m.realFF==null ? S.dim : m.realFF>2 ? S.red : m.realFF>0 ? "#f59e0b" : S.grn; // ERP: S&P earnings yield (~1/26 fwd PE ≈ 3.8%) minus 10Y yield. Approx with breakeven // We derive ERP = rough estimated earnings yield (use 4.2% as S&P proxy) - 10Y yield const tenY = yields["10Y"]?.cur; const snpEarnYield = 4.2; // approximate S&P 500 forward earnings yield (update manually or via finnhub later) const erp = tenY!=null ? snpEarnYield - tenY : null; const erpColor = erp==null ? S.dim : erp>2 ? S.grn : erp>0.5 ? "#f59e0b" : S.red; // Credit spreads: IG normal <150bp, HY normal <400bp const igColor = m.igSpread==null ? S.dim : m.igSpread<1.5 ? S.grn : m.igSpread<2.5 ? "#f59e0b" : S.red; const hyColor = m.hySpread==null ? S.dim : m.hySpread<4 ? S.grn : m.hySpread<6 ? "#f59e0b" : S.red; // Row 2: Cycle const m2Color = m.m2YoY==null ? S.dim : Math.abs(m.m2YoY)<3 ? S.grn : Math.abs(m.m2YoY)<8 ? "#f59e0b" : S.red; const leiColor = m.leiChg==null ? S.dim : m.leiChg>0 ? S.grn : m.leiChg>-1 ? "#f59e0b" : S.red; const cnColor = m.cnPMI==null ? S.dim : m.cnPMI>52 ? S.grn : m.cnPMI>50 ? "#f59e0b" : S.red; const euColor = m.euPMI==null ? S.dim : m.euPMI>52 ? S.grn : m.euPMI>50 ? "#f59e0b" : S.red; const row = { display:"flex", alignItems:"flex-start", gap:"0", flexWrap:"wrap", borderBottom:`1px solid ${S.bdr2}`, paddingBottom:"14px", marginBottom:"14px" }; const gap = { width:"28px", flexShrink:0 }; return (
{/* Panel title row */}
MACRO DASHBOARD — DALIO ALL-WEATHER VIEW
{loading && progress < STOCKS.length && Loading {progress}/{STOCKS.length}…} {lastUpdated && {lastUpdated.toLocaleTimeString()} {loading?"…":"↻"}}
{/* Row 1: Valuations + Monetary */}
VALUATIONS & MONETARY CONDITIONS
=0?"+":""}${buffPctChg.toFixed(1)}pp prev` : ">140% = expensive"} color={buffColor} wide />
=0?"+":"")+erp.toFixed(2)+"%" : null} sub={tenY!=null ? `10Y: ${tenY.toFixed(2)}% · EY: ~${snpEarnYield}%` : "S&P earn yield − 10Y"} color={erpColor} />
=0?"+":"")+m.realFF.toFixed(2)+"%" : null} sub={m.cpiYoY!=null ? `CPI YoY: ${m.cpiYoY.toFixed(1)}%` : "Nominal − CPI YoY"} color={realFFColor} />
=0?"+":"")+m.m2YoY.toFixed(1)+"%" : null} sub={m.m2Prev!=null ? `Prev: ${m.m2Prev>=0?"+":""}${m.m2Prev.toFixed(1)}%` : "Money supply"} color={m2Color} />
{/* Row 2: Yield Curve + Cycle */}
YIELD CURVE & ECONOMIC CYCLE
US TREASURY CURVE
=0?"+":""}${m.leiChg.toFixed(1)}` : "USSLIND"} color={leiColor} />
); } // ── Bucket structure ───────────────────────────────────────────────────────── const BUCKETS = [ { id: "commodities", label: "Commodities & Real Assets", emoji: "🪨", tags: ["Energy","Pipelines","Mining & Metals","Precious Metals","Streaming & Royalty","Uranium","Copper","Land & Royalty","Energy Transition"] }, { id: "food-water", label: "Food & Water Security", emoji: "🌾", tags: ["Agriculture","Water","Farmland"] }, { id: "core", label: "Core Equities", emoji: "💻", tags: ["Tech","Healthcare","Consumer Staples","Financials","Insurance"] }, { id: "industrial", label: "Industrials & Defense", emoji: "🏭", tags: ["Defense & Aerospace","Industrial","Homebuilder"] }, { id: "reits", label: "REITs", emoji: "🏢", tags: ["Net Lease","Retail REIT","Industrial REIT","Residential REIT","Experiential REIT","Healthcare REIT"] }, { id: "global", label: "Global & Macro", emoji: "🌍", tags: ["International","Small Cap"] }, { id: "income", label: "Income & Alternatives", emoji: "💵", tags: ["Fixed Income","ETF","Crypto"] }, ]; const TAG_COLORS = { "Tech":"#3b82f6","Energy":"#f97316","Pipelines":"#d97706","Mining & Metals":"#a8a29e", "Precious Metals":"#eab308","Streaming & Royalty":"#ca8a04","Uranium":"#84cc16", "Copper":"#ea580c","Agriculture":"#16a34a","Water":"#06b6d4","Farmland":"#15803d", "REIT":"#a855f7","Net Lease":"#9333ea","Retail REIT":"#c026d3","Industrial REIT":"#7c3aed","Residential REIT":"#8b5cf6","Experiential REIT":"#a21caf","Healthcare REIT":"#d946ef","Defense & Aerospace":"#ef4444","Industrial":"#64748b", "Financials":"#2563eb","Insurance":"#1d4ed8","Healthcare":"#ec4899", "Consumer Staples":"#0d9488","International":"#6366f1","Energy Transition":"#65a30d", "Homebuilder":"#b45309","Land & Royalty":"#d97706","Fixed Income":"#475569", "Small Cap":"#8b5cf6","Crypto":"#f59e0b","ETF":"#52525b", }; // Account preference const ACCT = { T: "Pre-Tax (401k)", R: "Roth", B: "Taxable", A: "Any" }; const ACCT_COLOR = { T:"#f59e0b", R:"#22c55e", B:"#38bdf8", A:"#6b7280" }; // entry:[lo,hi] acct:T=401k R=Roth B=Taxable A=Any const THESIS = { "AAPL": {t:"Consumer ecosystem + services flywheel; buy on dips.", e:[230,255], a:"B"}, "ABBV": {t:"Humira cliff priced in; Skyrizi/Rinvoq building new revenue floor.", e:[195,215], a:"B"}, "ADC": {t:"Net lease REIT, best-in-class tenant quality; grocery/dollar anchor.", e:[60,70], a:"B"}, "AFL": {t:"Supplemental insurance moat Japan/US; steady dividend grower.", e:[95,110], a:"B"}, "AMZN": {t:"AWS + advertising = high-margin engines; retail optionality.", e:[170,195], a:"B"}, "AVB": {t:"Coastal apartment REIT; supply-constrained markets, strong NOI growth.", e:[180,210], a:"B"}, "AWK": {t:"Water utility monopoly; inflation-linked rate base growth.", e:[115,135], a:"B"}, "BABA": {t:"Deep value China tech; speculative, geopolitical risk acknowledged.", e:[90,120], a:"B"}, "BHP": {t:"Diversified mining giant — copper/iron/potash, strong dividend.", e:[55,68], a:"B"}, "BN": {t:"Brookfield asset management; compounding fee streams + real asset upside.", e:[38,44], a:"B"}, "BP": {t:"Transition energy at value; dividend support, undervalued vs peers.", e:[30,36], a:"B"}, "BRK/B": {t:"Forever hold — Buffett capital allocation + diversified quality compounder.", e:[430,480], a:"B"}, "BTC": {t:"Digital gold — store of value, institutional adoption, fixed supply.", e:[60000,80000],a:"R"}, "CAH": {t:"Healthcare distribution oligopoly; undervalued, strong FCF, buybacks.", e:[180,210], a:"B"}, "CAT": {t:"Infrastructure + global construction supercycle; pricing power proven.", e:[310,350], a:"B"}, "CCJ": {t:"Uranium supply deficit + nuclear renaissance for AI data centers.", e:[40,50], a:"R"}, "CF": {t:"Pure nitrogen play; US nat gas cost advantage, European capacity offline.", e:[65,80], a:"B"}, "CHD": {t:"Recession-resistant staples compounder; Church & Dwight brand portfolio.", e:[90,105], a:"B"}, "CTVA": {t:"Seed genetics IP moat — one of four global holders; precision ag tailwind.", e:[52,62], a:"B"}, "CVX": {t:"Integrated oil major — Permian + LNG + Tengiz; strong capital returns.", e:[135,155], a:"B"}, "DE": {t:"Farm equipment + AI/autonomy subscription business; durable pricing power.", e:[380,440], a:"B"}, "DBC": {t:"Broad commodity ETF — inflation hedge + real asset diversification.", e:[22,26], a:"B"}, "ECL": {t:"Industrial water efficiency moat; pricing power, 30yr dividend growth.", e:[215,245], a:"B"}, "EPR": {t:"Experiential REIT (theaters/education); high yield, COVID survivor.", e:[42,52], a:"B"}, "EQR": {t:"Coastal apartment REIT; high-quality residential, urban rent recovery.", e:[58,68], a:"B"}, "ESS": {t:"West Coast multifamily; supply-constrained markets, strong demographics.", e:[240,270], a:"B"}, "ETH": {t:"Programmable money + DeFi; post-merge yield + network effect.", e:[2500,3500],a:"R"}, "FCX": {t:"Largest public copper producer; EV/grid/AI demand, 15yr mine timeline.", e:[35,45], a:"B"}, "FNV": {t:"Gold streaming royalty — no operating risk, infinite mine life optionality.", e:[220,255], a:"B"}, "FPI": {t:"US farmland REIT — finite productive land vs infinite food demand.", e:[10,13], a:"B"}, "FRT": {t:"Dividend King REIT — 55yr consecutive increases; mixed-use urban centers.", e:[85,100], a:"B"}, "GE": {t:"Aerospace pure-play; jet engine aftermarket = durable recurring revenue.", e:[150,175], a:"B"}, "GEV": {t:"Grid infrastructure + offshore wind; energy transition pick-and-shovel.", e:[200,250], a:"B"}, "GOLD": {t:"World's largest gold miner by reserves; Tier 1 mines, gold price leverage.", e:[16,20], a:"B"}, "GOOGL": {t:"Search moat + cloud + AI integration; reasonable valuation vs megacap peers.", e:[155,185], a:"B"}, "IAU": {t:"Physical gold — inflation, dollar weakness, geopolitical tail risk hedge.", e:[38,44], a:"B"}, "IBM": {t:"Hybrid cloud + AI enterprise (watsonx); steady dividend, turnaround building.", e:[195,220], a:"B"}, "IEF": {t:"7-10yr Treasuries — recession hedge, negative equity correlation in stress.", e:[90,97], a:"T"}, "IJR": {t:"S&P 600 small-cap; quality screen outperforms raw Russell 2000.", e:[100,115], a:"R"}, "JPM": {t:"Best-in-class bank — fortress balance sheet, Dimon capital discipline.", e:[210,240], a:"B"}, "KIM": {t:"Grocery-anchored REIT; defensive tenant mix, mixed-use redevelopment.", e:[20,24], a:"B"}, "KMI": {t:"Largest nat gas pipeline network US; toll-road model, rising dividend.", e:[23,28], a:"B"}, "KRG": {t:"Open-air retail REIT; grocery-anchored centers, Inland merger upside.", e:[21,25], a:"B"}, "LEN": {t:"Homebuilder asset-light pivot; rate-sensitive, buy on mortgage rate relief.", e:[95,115], a:"B"}, "LMT": {t:"Defense spending cycle + F-35 global demand; mission-critical pricing power.", e:[420,470], a:"B"}, "META": {t:"Social advertising duopoly + AI infra; FCF machine.", e:[530,610], a:"B"}, "MSFT": {t:"Azure cloud + Copilot AI monetization; highest-quality compounder in tech.", e:[360,400], a:"B"}, "NEM": {t:"Largest gold miner — gold price leverage + Newcrest synergies unwinding.", e:[38,48], a:"B"}, "NFLX": {t:"Streaming market share leader; ad tier monetization + live content expansion.", e:[750,900], a:"B"}, "NNN": {t:"Triple-net lease — 34yr consecutive dividend growth, defensive retail.", e:[38,44], a:"B"}, "NTR": {t:"Potash oligopoly; Russia/Belarus sanctions = structural supply constraint.", e:[48,58], a:"B"}, "NVDA": {t:"AI compute monopoly — data center GPU dominance; valuation demanding.", e:[95,125], a:"R"}, "O": {t:"Monthly dividend aristocrat — 30yr track record, global net lease.", e:[52,62], a:"B"}, "OHI": {t:"Healthcare REIT — SNF/senior housing; high yield, watch coverage ratios.", e:[32,38], a:"B"}, "OKE": {t:"ONEOK/Magellan merger creates fee-based midstream giant.", e:[70,82], a:"B"}, "PFE": {t:"Post-COVID trough valuation; pipeline optionality + Seagen oncology.", e:[22,27], a:"B"}, "PG": {t:"Consumer staples compounder; pricing power proven through inflation cycle.", e:[150,170], a:"B"}, "QCOM": {t:"Semiconductor diversification beyond mobile; auto/IoT revenue mix shift.", e:[120,145], a:"R"}, "REXR": {t:"Southern CA industrial REIT; irreplaceable infill locations, e-commerce.", e:[30,38], a:"B"}, "RTX": {t:"Defense + commercial aerospace; Pratt aftermarket + missile systems demand.", e:[115,135], a:"B"}, "SGOV": {t:"Ultra-short Treasury buffer — earns yield while waiting for deployment.", e:[100,100], a:"B"}, "SPG": {t:"Dominant mall REIT; premium centers resilient, international growth.", e:[155,180], a:"B"}, "STAG": {t:"Industrial/logistics REIT — monthly dividend, e-commerce demand.", e:[33,38], a:"B"}, "TGT": {t:"Retail at trough; inventory cycle normalizing, loyal customer base.", e:[95,115], a:"B"}, "TFPM": {t:"Triple Flag streaming — base metals, 4 consecutive 5% dividend hikes.", e:[16,20], a:"B"}, "TPL": {t:"Permian Basin toll road — land + water royalties, essentially zero cost.", e:[400,480], a:"B"}, "TSLA": {t:"EV market share + energy storage; hold position size disciplined.", e:[220,280], a:"R"}, "UNM": {t:"Group benefits insurer at value; disability + life insurance cash flows.", e:[55,68], a:"B"}, "VBR": {t:"Small-cap value tilt; historically strong long-term factor premium.", e:[195,220], a:"R"}, "VEA": {t:"Developed international at multi-decade discount; dollar weakness hedge.", e:[45,55], a:"B"}, "VICI": {t:"Gaming/experiential REIT, investment-grade tenants; inflation-linked NNN.", e:[26,30], a:"B"}, "VT": {t:"Total world equity — ideal custodial Roth, 50yr horizon, no bonds needed.", e:null, a:"R"}, "VWO": {t:"Emerging markets — long-term demographic + growth tailwind.", e:[42,50], a:"B"}, "WPM": {t:"Silver/gold streaming — no mine operating risk, precious metals upside.", e:[55,68], a:"B"}, "XOM": {t:"Integrated oil major; Permian dominance + Pioneer acquisition transformative.", e:[100,120], a:"B"}, "XYL": {t:"Water infrastructure tech — municipal capex cycle, climate resilience.", e:[100,120], a:"B"}, }; // Accessors const getTh = (k) => THESIS[k]?.t || null; const getEn = (k) => THESIS[k]?.e || null; const getAc = (k) => THESIS[k]?.a || "A"; // ── Stock list ─────────────────────────────────────────────────────────────── const STOCKS = [ { ticker:"AAPL", name:"Apple Inc", tags:["Tech"] }, { ticker:"CVX", name:"Chevron Corp", tags:["Energy"] }, { ticker:"GOLD", name:"Barrick Gold Corp", tags:["Precious Metals","Mining & Metals"] }, { ticker:"NVDA", name:"NVIDIA Corp", tags:["Tech"] }, { ticker:"ABBV", name:"AbbVie Inc", tags:["Healthcare"] }, { ticker:"ADC", name:"Agree Realty", tags:["Net Lease"] }, { ticker:"AFL", name:"Aflac Inc", tags:["Financials","Insurance"] }, { ticker:"AMZN", name:"Amazon.com Inc", tags:["Tech"] }, { ticker:"AVB", name:"AvalonBay Communities", tags:["Residential REIT"] }, { ticker:"AWK", name:"American Water Works", tags:["Water"] }, { ticker:"BABA", name:"Alibaba Group ADR", tags:["Tech","International"] }, { ticker:"BHP", name:"BHP Group ADR", tags:["Mining & Metals","Copper","International"] }, { ticker:"BN", name:"Brookfield Corp", tags:["Financials","International"] }, { ticker:"BP", name:"BP PLC ADR", tags:["Energy","International"] }, { ticker:"BRK/B", name:"Berkshire Hathaway Cl B", tags:["Financials"] }, { ticker:"BTC", name:"Bitcoin", tags:["Crypto"] }, { ticker:"CAH", name:"Cardinal Health", tags:["Healthcare"] }, { ticker:"CAT", name:"Caterpillar Inc", tags:["Industrial","Agriculture"] }, { ticker:"CCJ", name:"Cameco Corp", tags:["Uranium","Energy Transition"] }, { ticker:"CF", name:"CF Industries", tags:["Agriculture"] }, { ticker:"CHD", name:"Church & Dwight", tags:["Consumer Staples"] }, { ticker:"CTVA", name:"Corteva Inc", tags:["Agriculture"] }, { ticker:"DBC", name:"Invesco DB Commodity ETF", tags:["ETF","Agriculture","Energy"] }, { ticker:"DE", name:"Deere & Co", tags:["Agriculture","Industrial"] }, { ticker:"ECL", name:"Ecolab Inc", tags:["Water","Industrial"] }, { ticker:"EPR", name:"EPR Properties", tags:["Experiential REIT"] }, { ticker:"EQR", name:"Equity Residential", tags:["Residential REIT"] }, { ticker:"ESS", name:"Essex Property Trust", tags:["Residential REIT"] }, { ticker:"ETH", name:"Ethereum", tags:["Crypto"] }, { ticker:"FCX", name:"Freeport-McMoRan", tags:["Copper","Mining & Metals"] }, { ticker:"FNV", name:"Franco-Nevada Corp", tags:["Precious Metals","Streaming & Royalty"] }, { ticker:"FPI", name:"Farmland Partners", tags:["Farmland"] }, { ticker:"FRT", name:"Federal Realty Trust", tags:["Net Lease"] }, { ticker:"GE", name:"GE Aerospace", tags:["Defense & Aerospace","Industrial"] }, { ticker:"GEV", name:"GE Vernova Inc", tags:["Energy Transition","Industrial"] }, { ticker:"GOOGL", name:"Alphabet Inc Cl A", tags:["Tech"] }, { ticker:"IAU", name:"iShares Gold Trust", tags:["ETF","Precious Metals"] }, { ticker:"IBM", name:"IBM Corp", tags:["Tech","Industrial"] }, { ticker:"IEF", name:"iShares 7-10Y Treasury ETF", tags:["ETF","Fixed Income"] }, { ticker:"IJR", name:"iShares Core S&P Small-Cap", tags:["ETF","Small Cap"] }, { ticker:"JPM", name:"JPMorgan Chase", tags:["Financials"] }, { ticker:"KIM", name:"Kimco Realty", tags:["Retail REIT"] }, { ticker:"KMI", name:"Kinder Morgan", tags:["Energy","Pipelines"] }, { ticker:"KRG", name:"Kite Realty Group", tags:["Retail REIT"] }, { ticker:"LEN", name:"Lennar Corp", tags:["Homebuilder"] }, { ticker:"LMT", name:"Lockheed Martin", tags:["Defense & Aerospace"] }, { ticker:"META", name:"Meta Platforms", tags:["Tech"] }, { ticker:"MSFT", name:"Microsoft Corp", tags:["Tech"] }, { ticker:"NEM", name:"Newmont Corp", tags:["Precious Metals","Mining & Metals"] }, { ticker:"NFLX", name:"Netflix Inc", tags:["Tech"] }, { ticker:"NNN", name:"NNN REIT Inc", tags:["Net Lease"] }, { ticker:"NTR", name:"Nutrien Ltd", tags:["Agriculture"] }, { ticker:"O", name:"Realty Income Corp", tags:["Net Lease"] }, { ticker:"OHI", name:"Omega Healthcare", tags:["Healthcare REIT"] }, { ticker:"OKE", name:"ONEOK Inc", tags:["Energy","Pipelines"] }, { ticker:"PFE", name:"Pfizer Inc", tags:["Healthcare"] }, { ticker:"PG", name:"Procter & Gamble", tags:["Consumer Staples"] }, { ticker:"QCOM", name:"Qualcomm Inc", tags:["Tech"] }, { ticker:"REXR", name:"Rexford Industrial", tags:["Industrial REIT"] }, { ticker:"RTX", name:"RTX Corp", tags:["Defense & Aerospace","Industrial"] }, { ticker:"SGOV", name:"iShares 0-3M Treasury Bond", tags:["ETF","Fixed Income"] }, { ticker:"SPG", name:"Simon Property Group", tags:["Retail REIT"] }, { ticker:"STAG", name:"STAG Industrial", tags:["Industrial REIT"] }, { ticker:"TGT", name:"Target Corp", tags:["Consumer Staples"] }, { ticker:"TFPM", name:"Triple Flag Precious Metals", tags:["Precious Metals","Streaming & Royalty"] }, { ticker:"TPL", name:"Texas Pacific Land", tags:["Land & Royalty","Energy"] }, { ticker:"TSLA", name:"Tesla Inc", tags:["Tech","Energy Transition"] }, { ticker:"UNM", name:"Unum Group", tags:["Financials","Insurance"] }, { ticker:"VBR", name:"Vanguard Small-Cap Value ETF",tags:["ETF","Small Cap"] }, { ticker:"VEA", name:"Vanguard FTSE Dev Markets", tags:["ETF","International"] }, { ticker:"VICI", name:"VICI Properties", tags:["Experiential REIT"] }, { ticker:"VT", name:"Vanguard Total World Stock", tags:["ETF","International"] }, { ticker:"VWO", name:"Vanguard FTSE EM ETF", tags:["ETF","International"] }, { ticker:"WPM", name:"Wheaton Precious Metals", tags:["Precious Metals","Streaming & Royalty"] }, { ticker:"XOM", name:"Exxon Mobil", tags:["Energy"] }, { ticker:"XYL", name:"Xylem Inc", tags:["Water","Industrial"] }, ]; // ── CSV export ─────────────────────────────────────────────────────────────── function exportCSV(stocks) { const rows = [["Ticker","Name","Tags","Thesis","Entry Low","Entry High","Account"]]; stocks.forEach(s => { const t = THESIS[s.ticker] || {}; rows.push([ s.ticker, `"${s.name}"`, `"${s.tags.join(", ")}"`, `"${(t.thesis || "").replace(/"/g, "'")}"`, getEn(_th) ? t.entry[0] : "", getEn(_th) ? t.entry[1] : "", t.acct ? ACCT[t.acct] : "", ]); }); const csv = rows.map(r => r.join(",")).join("\n"); const blob = new Blob([csv], { type: "text/csv" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "portfolio-watchlist.csv"; a.click(); URL.revokeObjectURL(url); } // ── Row Sparkline ───────────────────────────────────────────────────────────── function Spark({ points, pct }) { if (!points || points.length < 2) return ; const W = 80, H = 28; const xs = points.map((_, i) => (i / (points.length - 1)) * W); const ys = points.map(v => H - v * (H - 2) - 1); const d = xs.map((x, i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${ys[i].toFixed(1)}`).join(" "); const color = pct == null ? "#38bdf8" : pct >= 0 ? "#22c55e" : "#ef4444"; // Fill area under curve const fill = d + ` L${W},${H} L0,${H} Z`; return ( ); } // ── Main component ─────────────────────────────────────────────────────────── export default function StockTracker() { const [activeFilters, setActiveFilters] = useState(new Set()); const [openBuckets, setOpenBuckets] = useState(new Set(BUCKETS.map(b => b.id))); const [search, setSearch] = useState(""); const [sortBy, setSortBy] = useState("ticker"); const [sortDir, setSortDir] = useState(1); const [expandedRow, setExpandedRow] = useState(null); const [priceData, setPriceData] = useState({}); const [yields, setYields] = useState({}); const [macro, setMacro] = useState({}); const [loading, setLoading] = useState(false); const [progress, setProgress] = useState(0); const [lastUpdated, setLastUpdated] = useState(null); useEffect(() => { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = "https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap"; document.head.appendChild(link); }, []); const loadAll = useCallback(async () => { setLoading(true); setProgress(0); // FRED fires independently — different API, no Finnhub rate impact fetchYieldCurve().then(v => { if (v) setYields(v); }).catch(()=>{}); fetchMacroData().then(v => { if (v) setMacro(v); }).catch(()=>{}); // Finnhub: 1 call/ticker (candles only) = 83 calls total // 60/min limit → BATCH=15 (15 calls/batch), DELAY=15s → ~6 batches ≈ 75s const tickers = STOCKS.map(s => s.ticker); const BATCH=12, DELAY=13000; const all={}; for (let i=0; i { try { const r=await fetchTicker(t); if (r) all[t]=r; } catch {} })); setPriceData({...all}); setProgress(Math.min(i+BATCH, tickers.length)); if (i+BATCH < tickers.length) await new Promise(r => setTimeout(r, DELAY)); } // 5Y returns via Yahoo Finance (monthly, free, runs in parallel after main load) Promise.allSettled(Object.keys(all).map(async t => { const pct = await fetch5Y(t); if (pct!=null) all[t] = {...all[t], fiveyPct:pct}; })).then(() => setPriceData({...all})); setLastUpdated(new Date()); setLoading(false); }, []); useEffect(() => { loadAll(); const interval = setInterval(loadAll, 5 * 60 * 1000); // refresh every 5 min return () => clearInterval(interval); }, []); const toggleFilter = (tag) => setActiveFilters(prev => { const n = new Set(prev); n.has(tag) ? n.delete(tag) : n.add(tag); return n; }); const toggleBucketOpen = (id) => setOpenBuckets(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); const toggleRow = (t) => setExpandedRow(v => v === t ? null : t); const getBucketState = (bucket) => { const active = bucket.tags.filter(t => activeFilters.has(t)); if (active.length === 0) return "none"; if (active.length === bucket.tags.length) return "all"; return "partial"; }; const toggleBucketCheck = (bucket) => { const state = getBucketState(bucket); setActiveFilters(prev => { const n = new Set(prev); if (state === "all") bucket.tags.forEach(t => n.delete(t)); else bucket.tags.forEach(t => n.add(t)); return n; }); }; const handleSort = (col) => { if (sortBy === col) setSortDir(d => -d); else { setSortBy(col); setSortDir(1); } }; const filtered = useMemo(() => { let list = STOCKS; if (activeFilters.size > 0) list = list.filter(s => s.tags.some(t => activeFilters.has(t))); if (search.trim()) { const q = search.toLowerCase(); list = list.filter(s => s.ticker.toLowerCase().includes(q) || s.name.toLowerCase().includes(q) || s.tags.some(t => t.toLowerCase().includes(q)) ); } return [...list].sort((a, b) => { if (sortBy === "ticker") return sortDir * a.ticker.localeCompare(b.ticker); if (sortBy === "name") return sortDir * a.name.localeCompare(b.name); return 0; }); }, [activeFilters, search, sortBy, sortDir]); const activeCount = activeFilters.size; return (
{/* ── Header ── */}

PORTFOLIO WATCHLIST

All-Weather Tracker

{STOCKS.length} INSTRUMENTS
{/* ── Macro Panel ── */} {/* ── Search + Actions ── */}
setSearch(e.target.value)} placeholder="Search ticker, name, or tag…" /> {/* Always-visible filter button */} {/* Export */} {filtered.length} of {STOCKS.length} shown
{/* ── Bucket Accordions ── */}
{BUCKETS.map(bucket => { const isOpen = openBuckets.has(bucket.id); const state = getBucketState(bucket); const accentColor = TAG_COLORS[bucket.tags[0]] || S.dim; const isActive = state !== "none"; return (
{/* Header row */}
{/* Checkbox */}
toggleBucketCheck(bucket)} title="Select / deselect all tags" style={{ width:"15px", height:"15px", flexShrink:0, cursor:"pointer", border:`1.5px solid ${isActive ? accentColor : "#2a3d52"}`, background: state === "all" ? accentColor : state === "partial" ? accentColor+"33" : "transparent", display:"flex", alignItems:"center", justifyContent:"center", transition:"all 0.15s", }} > {state === "all" && } {state === "partial" &&
}
{/* Label */}
toggleBucketOpen(bucket.id)} style={{ flex:1, display:"flex", alignItems:"center", gap:"7px", cursor:"pointer", userSelect:"none", minWidth:0 }}> {bucket.emoji} {bucket.label} {(() => { const sel = bucket.tags.filter(t => activeFilters.has(t)).length; const tot = bucket.tags.reduce((n,t) => n + STOCKS.filter(s=>s.tags.includes(t)).length, 0); const uniq = [...new Set(STOCKS.filter(s=>s.tags.some(t=>bucket.tags.includes(t))).map(s=>s.ticker))].length; return ( 0 ? S.acc : S.dim2, marginLeft:"auto", paddingLeft:"8px", flexShrink:0, fontFamily:S.mono }}> {sel>0 ? `${bucket.tags.filter(t=>activeFilters.has(t)).length} filter${sel>1?"s":""} · ` : ""}{uniq} ); })()}
{/* Triangle */}
toggleBucketOpen(bucket.id)} style={{ cursor:"pointer", color: isOpen ? "#4a7090" : S.dim2, fontSize:"9px", transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", transition:"transform 0.2s, color 0.15s", paddingLeft:"4px", flexShrink:0 }} >▼
{/* Sub-tags */} {isOpen && (
{bucket.tags.map(tag => { const active = activeFilters.has(tag); const color = TAG_COLORS[tag] || "#6b7280"; const count = STOCKS.filter(s => s.tags.includes(tag)).length; return ( ); })}
)}
); })}
{/* ── Table ── */} {[["ticker","Ticker"],["name","Name"]].map(([col, label]) => ( ))} {filtered.map((stock, i) => { const _th = stock.ticker; const isOpen = expandedRow === stock.ticker; const acctKey = getAc(_th); const acctLabel = ACCT[acctKey]; const acctColor = ACCT_COLOR[acctKey]; return ( <> toggleRow(stock.ticker)} style={{ borderBottom: isOpen ? "none" : "1px solid #0d1520", background: isOpen ? S.bg3 : i % 2 === 0 ? "transparent" : S.bg, cursor:"pointer", }} onMouseEnter={e => { if (!isOpen) e.currentTarget.style.background = S.bg3; }} onMouseLeave={e => { if (!isOpen) e.currentTarget.style.background = i % 2 === 0 ? "transparent" : S.bg; }} > {/* Price + return columns */} {["price","dailyPct","ytdPct","sixMoPct","oneyPct","fiveyPct"].map(field => { const pd = priceData[stock.ticker]; const val = pd?.[field]; const isPrice = field === "price"; return ( ); })} {/* 5Y sparkline */} {/* ── Drawer ── */} {isOpen && ( )} ); })} {filtered.length === 0 && ( )}
handleSort(col)} style={{ textAlign:"left", padding:"10px 14px", fontSize:"11px", color: sortBy === col ? S.acc : S.dim, fontWeight:600, cursor:"pointer", userSelect:"none", borderBottom:"1px solid #1e2d3d", whiteSpace:"nowrap", fontFamily:S.sans, }}> {label} {sortBy === col ? (sortDir === 1 ? "↑" : "↓") : ""} Price 1D % YTD % 6M % 1Y % 5Y % 1Y Chart Tags Ideal Account
{stock.ticker} {isInSeason(stock.tags, macro?.regime) && ( )} {stock.name} {pd === undefined ? : val === null || val === undefined ? "—" : isPrice ? (val < 1 ? val.toFixed(4) : val < 100 ? val.toFixed(2) : val.toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2})) : pctFmt(val) }
{stock.tags.map(tag => { const color = TAG_COLORS[tag] || "#6b7280"; const active = activeFilters.has(tag); return ( { e.stopPropagation(); toggleFilter(tag); }} style={{ background: active ? color : `${color}18`, border:`1px solid ${active ? color : color+"45"}`, color: active ? "#fff" : color, padding:"2px 7px", fontSize:"10px", fontFamily:S.sans, cursor:"pointer", transition:"all 0.1s", whiteSpace:"nowrap", }} >{tag} ); })}
{acctLabel}
{/* Thesis */} {getTh(_th) ? (

THESIS {getTh(_th)}

) : (

No thesis recorded yet.

)} {/* Entry zone */}
{getEn(_th) ? (
TARGET ENTRY ZONE ${getEn(_th)[0].toLocaleString()} – ${getEn(_th)[1].toLocaleString()}
) : (
TARGET ENTRY ZONE Index / no specific entry target
)}
PREFERRED ACCOUNT {ACCT[acctKey]}
No instruments match current filters
All-Weather Portfolio Tracker {new Date().toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"})}
); }