diff --git a/__pycache__/generate.cpython-312.pyc b/__pycache__/generate.cpython-312.pyc new file mode 100644 index 0000000..ed8ec12 Binary files /dev/null and b/__pycache__/generate.cpython-312.pyc differ diff --git a/generate.py b/generate.py index 0acab2c..c997c2e 100644 --- a/generate.py +++ b/generate.py @@ -83,6 +83,35 @@ BOND_ETFS = { "GOVT": {"name": "US Total Treasury", "country": "US", "duration": "mixed"}, } +# ─── EODHD GOVERNMENT BONDS ───────────────────────────────────────────────── +EODHD_BONDS = { + "US2Y.GBOND": {"key": "us2y", "label": "US 2Y", "country": "US"}, + "US10Y.GBOND": {"key": "us10y", "label": "US 10Y", "country": "US"}, + "US30Y.GBOND": {"key": "us30y", "label": "US 30Y", "country": "US"}, + "UK2Y.GBOND": {"key": "uk2y", "label": "UK 2Y", "country": "UK"}, + "UK10Y.GBOND": {"key": "uk10y", "label": "UK 10Y", "country": "UK"}, + "UK30Y.GBOND": {"key": "uk30y", "label": "UK 30Y", "country": "UK"}, + "DE2Y.GBOND": {"key": "de2y", "label": "Germany 2Y", "country": "Eurozone"}, + "DE10Y.GBOND": {"key": "de10y", "label": "Germany 10Y", "country": "Eurozone"}, + "DE30Y.GBOND": {"key": "de30y", "label": "Germany 30Y", "country": "Eurozone"}, + "JP2Y.GBOND": {"key": "jp2y", "label": "Japan 2Y", "country": "Japan"}, + "JP10Y.GBOND": {"key": "jp10y", "label": "Japan 10Y", "country": "Japan"}, + "JP30Y.GBOND": {"key": "jp30y", "label": "Japan 30Y", "country": "Japan"}, + "IT2Y.GBOND": {"key": "it2y", "label": "Italy 2Y", "country": "Eurozone"}, + "IT10Y.GBOND": {"key": "it10y", "label": "Italy 10Y", "country": "Eurozone"}, + "FR10Y.GBOND": {"key": "fr10y", "label": "France 10Y", "country": "Eurozone"}, + "ES10Y.GBOND": {"key": "es10y", "label": "Spain 10Y", "country": "Eurozone"}, + "CA2Y.GBOND": {"key": "ca2y", "label": "Canada 2Y", "country": "Canada"}, + "CA10Y.GBOND": {"key": "ca10y", "label": "Canada 10Y", "country": "Canada"}, + "AU2Y.GBOND": {"key": "au2y", "label": "Australia 2Y", "country": "Australia"}, + "AU10Y.GBOND": {"key": "au10y", "label": "Australia 10Y", "country": "Australia"}, + "NZ2Y.GBOND": {"key": "nz2y", "label": "New Zealand 2Y", "country": "New Zealand"}, + "NZ10Y.GBOND": {"key": "nz10y", "label": "New Zealand 10Y", "country": "New Zealand"}, + "SW10Y.GBOND": {"key": "ch10y", "label": "Switzerland 10Y", "country": "Switzerland"}, + "SE10Y.GBOND": {"key": "se10y", "label": "Sweden 10Y", "country": "Sweden"}, + "NO10Y.GBOND": {"key": "no10y", "label": "Norway 10Y", "country": "Norway"}, +} + # ─── COMMODITY TICKERS ─────────────────────────────────────────────────────── COMMODITIES = { "GC=F": {"key": "gold", "name": "Gold", "unit": "$/oz"}, @@ -140,25 +169,36 @@ FX_PAIRS = { } -def fetch_gov_data(endpoint, params=None): - """Fetch data from the government API.""" +def fetch_eodhd_eod(symbol): + """Fetch latest EOD data from EODHD for a bond/rate symbol.""" if not GOV_KEY: - print(" GOV API key missing") return None - - if params is None: - params = {} - try: - base_url = "https://YOUR-GOV-API-ENDPOINT-HERE" - params["api_key"] = GOV_KEY - - r = requests.get(base_url + endpoint, params=params, timeout=15) + url = f"https://eodhd.com/api/eod/{symbol}" + params = {"api_token": GOV_KEY, "fmt": "json", "order": "d", "limit": 2} + r = requests.get(url, params=params, timeout=15) r.raise_for_status() - return r.json() - + data = r.json() + if not isinstance(data, list) or len(data) < 1: + return None + latest = data[0] + prev = data[1] if len(data) >= 2 else None + value = latest.get("close") + prev_value = prev.get("close") if prev else None + change = round(value - prev_value, 4) if value is not None and prev_value is not None else None + pct_change = ( + round((value - prev_value) / prev_value * 100, 4) + if value is not None and prev_value is not None and prev_value != 0 + else None + ) + return { + "value": value, + "change": change, + "pct_change": pct_change, + "date": latest.get("date"), + } except Exception as e: - print(f" Government API error ({endpoint}): {e}") + print(f" EODHD error ({symbol}): {e}") return None @@ -477,6 +517,7 @@ def main(): "macro": {}, "sentiment": {}, "scenarios": {}, + "gov_bonds": {}, } # 1. US YIELD CURVE from FRED (most reliable) @@ -663,24 +704,23 @@ def main(): }] print(f" Final calendar rows: {len(output['calendar'])}") - # 11. Government API data - print("\n[11] Fetching government data...") - gov_data = fetch_gov_data("/some/endpoint", params={"country": "US"}) - if gov_data: - output["policy_rates"]["US"] = gov_data.get("policy_rate") - - output["macro"]["US"] = { - "cpi": f"{gov_data.get('cpi_yoy')}%" if gov_data.get("cpi_yoy") is not None else "\u2014", - "cpiP": "\u2014", - "gdp": f"{gov_data.get('gdp_qoq')}%" if gov_data.get("gdp_qoq") is not None else "\u2014", - "gdpT": "QoQ", - "unemp": f"{gov_data.get('unemployment')}%" if gov_data.get("unemployment") is not None else "\u2014", - "unempT": "latest", - "stance": "\u2014", - "last": "\u2014", - "next": "\u2014", - "pricing": "\u2014", - } + # 11. Government bonds (EODHD) + print("\n[11] Fetching government bonds from EODHD...") + if GOV_KEY: + for symbol, info in EODHD_BONDS.items(): + result = fetch_eodhd_eod(symbol) + if result: + result["label"] = info["label"] + result["country"] = info["country"] + output["gov_bonds"][info["key"]] = result + v = result["value"] + c = result.get("change") + chg_str = f" ({c:+.3f})" if c is not None else "" + print(f" {info['label']}: {v}%{chg_str}") + time.sleep(0.3) # Rate limit + print(f" Fetched {len(output['gov_bonds'])} bonds") + else: + print(" EODHD API key not set — skipping government bonds") # Write output output_path = Path(OUTPUT_FILE) @@ -696,6 +736,7 @@ def main(): print(f" FX: {len(output['fx'])} pairs") print(f" News: {len(output['news'])} articles") print(f" Calendar: {len(output['calendar'])} events") + print(f" Gov bonds: {len(output['gov_bonds'])} instruments") print(f"{'=' * 60}") diff --git a/index.html b/index.html index e16c04f..8e7f8a4 100644 --- a/index.html +++ b/index.html @@ -35,6 +35,7 @@ const pOf={US:"DXY",Eurozone:"EURUSD",UK:"GBPUSD",Japan:"USDJPY",Switzerland:"US const nKw={US:["fed","fomc","treasury","dollar","powell","nonfarm"],Eurozone:["ecb","lagarde","eurozone","bund"],UK:["boe","bailey","gilt","sterling","uk gdp","uk cpi"],Japan:["boj","ueda","yen","jgb","japan"],Switzerland:["snb","swiss","franc"],Australia:["rba","aussie","australia"],Canada:["boc","canada","loonie"],"New Zealand":["rbnz","kiwi","new zealand"],Sweden:["riksbank","sweden"],Norway:["norges","norway"],Brazil:["bcb","brazil","real"],Mexico:["banxico","mexico","peso"],"South Africa":["sarb","south africa","rand"],India:["rbi","india","rupee"],"South Korea":["bok","korea","won"],Turkey:["cbrt","turkey","lira"],Poland:["nbp","poland","zloty"]}; const MD={}; +const bondPfx={"US":["us"],"UK":["uk"],"Eurozone":["de","it","fr","es"],"Japan":["jp"],"Canada":["ca"],"Australia":["au"],"New Zealand":["nz"],"Switzerland":["ch"],"Sweden":["se"],"Norway":["no"]}; @@ -221,8 +222,9 @@ function Scenarios({risk,yld,g10,em,cmd}){ function CorrMx(){return
30-Day Correlation Matrix

Correlation data not available — requires a live feed
} /* ═══ DEEP DIVE ═══════════════════════════════════════════════════ */ -function Deep({ctry,g10,em,yld,news,nL,risk,macro,policyRates}){ +function Deep({ctry,g10,em,yld,news,nL,risk,macro,policyRates,govBonds}){ const ccy=cOf[ctry],fp=[...g10,...em].find(x=>x.pair===pOf[ctry]),y=yld[ctry]||{},tpl=RT.find(x=>x.c===ctry),m=macro?.[ctry]||{},cbRate=policyRates?.[ctry]??null; + const pfxList=bondPfx[ctry]||[];const cBonds=Object.entries(govBonds||{}).filter(([k])=>pfxList.some(p=>k.startsWith(p))).map(([k,v])=>({key:k,...v})); const kws=nKw[ctry]||[ccy.toLowerCase()];const cN=news.filter(n=>kws.some(kw=>(n.headline||"").toLowerCase().includes(kw)));const dN=cN.length>=2?cN:news; const ce=Object.entries(y).filter(([k,v])=>v!=null&&k.startsWith("y")).map(([k,v])=>[k.replace("y","").toUpperCase(),v]); const [ai,setAi]=useState(null); @@ -252,6 +254,7 @@ function Deep({ctry,g10,em,yld,news,nL,risk,macro,policyRates}){ } : null, macro: m, riskBar: risk, + govBonds: cBonds, headlines: dN.slice(0, 8).map(n => ({ headline: n.headline, source: n.source, @@ -435,6 +438,8 @@ function Deep({ctry,g10,em,yld,news,nL,risk,macro,policyRates}){ + {cBonds.length>0&&
🏦 Government Bonds
C.b},{k:"value",l:"Yield %",rn:r=>r.value!=null?Number(r.value).toFixed(3):"\u2014"},{k:"change",l:"Chg (bp)",rn:r=>r.change!=null?fb(r.change*100):"\u2014",cl:r=>cc(r.change)},{k:"date",l:"Date",a:"left",cl:()=>C.txM}]} rows={cBonds} sm/>
} +
Central Bank
@@ -508,14 +513,14 @@ function App(){ const [rL,sRL]=useState(false);const [cal,sCal]=useState([]);const [cL,sCL]=useState(false);const [news,sNews]=useState([]);const [nL,sNL]=useState(false); const [cmd,sCmd]=useState({gold:{},wti:{},brent:{}});const [ld,sLd]=useState(false);const [lr,sLR]=useState(null); const [etfs,setEtfs]=useState({});const [vol,setVol]=useState({});const [bondEtfs,setBondEtfs]=useState({}); - const [macro,setMacro]=useState({});const [policyRates,setPolicyRates]=useState({}); + const [macro,setMacro]=useState({});const [policyRates,setPolicyRates]=useState({});const [govBonds,setGovBonds]=useState({}); const svK=useCallback(nk=>{setKeys(nk);Object.entries(nk).forEach(([k,v])=>sk(k,v))},[]); const load=useCallback(async()=>{ sLd(true); try{ const data=await fData(); setEtfs(data.etfs||{});setVol(data.volatility||{});setBondEtfs(data.bond_etfs||{}); - setMacro(data.macro||{});setPolicyRates(data.policy_rates||{}); + setMacro(data.macro||{});setPolicyRates(data.policy_rates||{});setGovBonds(data.gov_bonds||{}); const mapFx=defs=>defs.map(d=>{const x=data.fx?.[d.p];return{pair:d.p,spot:x?.price??null,d1:x?.d1_pct??null,w1:x?.w1_pct??null,m1:x?.m1_pct??null,ytd:null}}); sG10(mapFx(G10));sEM(mapFx(EMP));sFxL(true); const mappedYields={}; @@ -544,7 +549,7 @@ function App(){ {sec==="commod"&&
Commodities
} {sec==="scenarios"&&} {sec==="corr"&&
} - {sec==="deep"&&
{RT.map(c=>)}
} + {sec==="deep"&&
{RT.map(c=>)}
} {sec==="cal"&&
Economic Calendar
}
FX RADAR v4 · {fxL?"Live":"..."}