feat(commodities): expand tracking to 23 symbols — agriculture and coal (#2135)

* feat(commodities): expand tracking to cover agricultural and coal futures

Adds 9 new commodity symbols to cover the price rally visible in our
intelligence feeds: Newcastle Coal (MTF=F), Wheat (ZW=F), Corn (ZC=F),
Soybeans (ZS=F), Rough Rice (ZR=F), Coffee (KC=F), Sugar (SB=F),
Cocoa (CC=F), and Cotton (CT=F).

Also fixes ais-relay seeder to use display names from commodities.json
instead of raw symbols, so seeded data is self-consistent.

* fix(commodities): gold standard cache, 3-col grid, cleanup

- Add upstashExpire on zero-quotes failure path so bootstrap key TTL
  extends during Yahoo outages (gold standard pattern)
- Remove unreachable fallback in retry loop (COMMODITY_META always has
  the symbol since it mirrors COMMODITY_SYMBOLS)
- Switch commodities panel to 3-column grid (19 items → ~7 rows vs 10)
This commit is contained in:
Elie Habib
2026-03-23 14:19:20 +04:00
committed by GitHub
parent 00f9ce7c19
commit 1d28c352da
4 changed files with 28 additions and 6 deletions

View File

@@ -1289,6 +1289,7 @@ const MARKET_SYMBOLS = [
const _commodityCfg = requireShared('commodities.json');
const COMMODITY_SYMBOLS = _commodityCfg.commodities.map(c => c.symbol);
const COMMODITY_META = new Map(_commodityCfg.commodities.map(c => [c.symbol, { name: c.name, display: c.display }]));
const SECTOR_SYMBOLS = ['XLK', 'XLF', 'XLE', 'XLV', 'XLY', 'XLI', 'XLP', 'XLU', 'XLB', 'XLRE', 'XLC', 'SMH'];
@@ -1409,8 +1410,9 @@ async function seedCommodityQuotes() {
const quotes = [];
const missing = [];
for (const s of COMMODITY_SYMBOLS) {
const meta = COMMODITY_META.get(s) || { name: s, display: s };
const yahoo = await fetchYahooChartDirect(s);
if (yahoo) quotes.push({ symbol: s, name: s, display: s, price: yahoo.price, change: yahoo.change, sparkline: yahoo.sparkline });
if (yahoo) quotes.push({ symbol: s, name: meta.name, display: meta.display, price: yahoo.price, change: yahoo.change, sparkline: yahoo.sparkline });
else missing.push(s);
await sleep(150);
}
@@ -1418,14 +1420,16 @@ async function seedCommodityQuotes() {
if (missing.length > 0) {
await sleep(3000);
for (const s of missing) {
const meta = COMMODITY_META.get(s);
const yahoo = await fetchYahooChartDirect(s);
if (yahoo) quotes.push({ symbol: s, name: s, display: s, price: yahoo.price, change: yahoo.change, sparkline: yahoo.sparkline });
if (yahoo) quotes.push({ symbol: s, name: meta.name, display: meta.display, price: yahoo.price, change: yahoo.change, sparkline: yahoo.sparkline });
await sleep(200);
}
}
if (quotes.length === 0) {
console.warn('[Market] No commodity quotes fetched — skipping Redis write');
console.warn('[Market] No commodity quotes fetched — extending existing key TTL, skipping write');
try { await upstashExpire('market:commodities-bootstrap:v1', MARKET_SEED_TTL); } catch {}
return 0;
}

View File

@@ -13,6 +13,15 @@
{ "symbol": "RB=F", "name": "Gasoline RBOB", "display": "GASOLINE" },
{ "symbol": "HO=F", "name": "Heating Oil", "display": "HEATING OIL" },
{ "symbol": "URA", "name": "Uranium (Global X)", "display": "URANIUM" },
{ "symbol": "LIT", "name": "Lithium & Battery", "display": "LITHIUM" }
{ "symbol": "LIT", "name": "Lithium & Battery", "display": "LITHIUM" },
{ "symbol": "MTF=F", "name": "Newcastle Coal", "display": "COAL" },
{ "symbol": "ZW=F", "name": "Wheat", "display": "WHEAT" },
{ "symbol": "ZC=F", "name": "Corn", "display": "CORN" },
{ "symbol": "ZS=F", "name": "Soybeans", "display": "SOYBEANS" },
{ "symbol": "ZR=F", "name": "Rough Rice", "display": "RICE" },
{ "symbol": "KC=F", "name": "Coffee", "display": "COFFEE" },
{ "symbol": "SB=F", "name": "Sugar No. 11", "display": "SUGAR" },
{ "symbol": "CC=F", "name": "Cocoa", "display": "COCOA" },
{ "symbol": "CT=F", "name": "Cotton", "display": "COTTON" }
]
}

View File

@@ -13,6 +13,15 @@
{ "symbol": "RB=F", "name": "Gasoline RBOB", "display": "GASOLINE" },
{ "symbol": "HO=F", "name": "Heating Oil", "display": "HEATING OIL" },
{ "symbol": "URA", "name": "Uranium (Global X)", "display": "URANIUM" },
{ "symbol": "LIT", "name": "Lithium & Battery", "display": "LITHIUM" }
{ "symbol": "LIT", "name": "Lithium & Battery", "display": "LITHIUM" },
{ "symbol": "MTF=F", "name": "Newcastle Coal", "display": "COAL" },
{ "symbol": "ZW=F", "name": "Wheat", "display": "WHEAT" },
{ "symbol": "ZC=F", "name": "Corn", "display": "CORN" },
{ "symbol": "ZS=F", "name": "Soybeans", "display": "SOYBEANS" },
{ "symbol": "ZR=F", "name": "Rough Rice", "display": "RICE" },
{ "symbol": "KC=F", "name": "Coffee", "display": "COFFEE" },
{ "symbol": "SB=F", "name": "Sugar No. 11", "display": "SUGAR" },
{ "symbol": "CC=F", "name": "Cocoa", "display": "COCOA" },
{ "symbol": "CT=F", "name": "Cotton", "display": "COTTON" }
]
}

View File

@@ -5935,7 +5935,7 @@ body.playback-mode .status-dot {
/* Commodities */
.commodities-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}