From 326192e75e616547d2784ea727e272305fd4e377 Mon Sep 17 00:00:00 2001 From: DanS Date: Sun, 12 Jul 2026 10:58:50 -0500 Subject: [PATCH] feat(market): per-exchange 24h volume in the hero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hero's "24H Vol" showed CoinGecko's cross-exchange aggregate even after the chart/price went per-venue. Capture the per-exchange converted_volume.usd from the tickers (previously discarded, alongside converted_last) and show the SELECTED exchange's own 24h volume; fall back to the aggregate when unknown. Market cap stays aggregate (it's coin-wide, not per-venue). This is a big real difference — e.g. Ourbit ~$14.3K vs NonKYC ~$253 for DRGX/USDT — so the header now reflects the venue you're actually looking at. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/data/exchange_info.cpp | 7 +++++-- src/data/exchange_info.h | 1 + src/ui/windows/market_tab.cpp | 9 ++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/data/exchange_info.cpp b/src/data/exchange_info.cpp index 2ef449a..46266d3 100644 --- a/src/data/exchange_info.cpp +++ b/src/data/exchange_info.cpp @@ -66,9 +66,12 @@ std::vector parseCoinGeckoTickers(const std::string& body) } const std::string tradeUrl = t.value("trade_url", std::string{}); if (base.empty() || target.empty() || market.empty()) continue; - double lastUsd = 0.0; // this venue's current USD price — differs per exchange + double lastUsd = 0.0; // this venue's current USD price + 24h volume — differ per exchange if (t.contains("converted_last") && t["converted_last"].is_object()) lastUsd = t["converted_last"].value("usd", 0.0); + double volumeUsd = 0.0; + if (t.contains("converted_volume") && t["converted_volume"].is_object()) + volumeUsd = t["converted_volume"].value("usd", 0.0); auto it = byName.find(market); if (it == byName.end()) { @@ -76,7 +79,7 @@ std::vector parseCoinGeckoTickers(const std::string& body) exchanges.push_back(ExchangeInfo{market, originOf(tradeUrl), {}}); it = byName.find(market); } - ExchangePair pair{base, target, base + "/" + target, tradeUrl, identifier, lastUsd}; + ExchangePair pair{base, target, base + "/" + target, tradeUrl, identifier, lastUsd, volumeUsd}; // Skip duplicate pairs on the same exchange. bool dup = false; for (const auto& p : exchanges[it->second].pairs) diff --git a/src/data/exchange_info.h b/src/data/exchange_info.h index a9c84a2..3973088 100644 --- a/src/data/exchange_info.h +++ b/src/data/exchange_info.h @@ -20,6 +20,7 @@ struct ExchangePair { std::string tradeUrl; ///< Link to the exchange pair page std::string identifier; ///< CoinGecko exchange id (e.g. "ourbit", "nonkyc_io") — keys the candle adapter double lastUsd = 0.0; ///< This venue's current price in USD (CoinGecko converted_last.usd); 0 if unknown + double volumeUsd = 0.0; ///< This venue's 24h volume in USD (CoinGecko converted_volume.usd); 0 if unknown }; /** diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index ea3a93d..abb4329 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -1440,8 +1440,15 @@ static void mktDrawPriceChart(const MktCtx& cx) sxr -= Layout::spacingMd(); }; if (market.price_usd > 0) { + // Market cap is coin-wide (stays aggregate); 24h volume is per-venue — show the SELECTED + // exchange's own volume when we have it, else the CoinGecko aggregate. drawStat(TR("market_cap_short"), FormatCompactUSD(market.market_cap)); - drawStat(TR("market_vol_short"), FormatCompactUSD(market.volume_24h)); + double vol = market.volume_24h; + const auto& exch = *cx.currentExchange; + if (!exch.pairs.empty() && s_mkt.pairIdx < (int)exch.pairs.size() + && exch.pairs[s_mkt.pairIdx].volumeUsd > 0.0) + vol = exch.pairs[s_mkt.pairIdx].volumeUsd; + drawStat(TR("market_vol_short"), FormatCompactUSD(vol)); } }