feat(market): per-exchange price chart from each venue's own candle API
The market chart was hardwired to CoinGecko's cross-exchange USD aggregate (vs_currency=usd), so selecting a trading pair only changed the "Trade" link — never the chart or price. Now the SELECTED exchange drives both. CoinGecko gives us which venues list DRGX (the ticker `market.identifier`) but not their APIs, so add data/exchange_candles.h: a hand-maintained map from that identifier to each venue's public candle endpoint, with two adapters verified against the live APIs — Ourbit (MEXC-style /api/v3/klines, array format) and NonKYC (TradingView-UDF /market/candles, `bars`). Unmapped venues return no URL, so the chart falls back to the CoinGecko aggregate and nothing regresses. - App::refreshExchangeChart() resolves the selected pair, fetches its intraday (5-min) + daily candles via the TLS-verified httpGetString on the worker, and stores them on MarketInfo (exchange_chart_intraday/daily + exchange_chart_active). Re-fetches on pair change; self-throttled to ~30 min otherwise; falls back to aggregate on any failure. - chartSeries() draws the per-exchange series when active (portfolio sparklines stay on the aggregate). The hero shows the selected venue's real price (converted_last) — Ourbit vs NonKYC differ ~7%. parseCoinGeckoTickers now captures the identifier + per-exchange USD price (previously discarded). Adapters unit-tested against real captured responses (URL builder + both parsers + the chartSeries switch) and validated against the full live feeds (Ourbit 20d, NonKYC 370d, ascending, correct closes). Build + ctest + hygiene green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
11
src/app.h
11
src/app.h
@@ -335,7 +335,11 @@ public:
|
||||
// Fetch historical USD price series (CoinGecko market_chart) that back the portfolio
|
||||
// sparkline intervals; self-throttled to ~30 min. Safe to call every frame.
|
||||
void refreshMarketChart();
|
||||
|
||||
// Fetch the SELECTED pair's candles from that exchange's own API (data/exchange_candles.h) so the
|
||||
// Market chart shows the real per-exchange price. Re-fetches on pair change; falls back to the
|
||||
// CoinGecko aggregate for unmapped venues / failed fetches. Safe to call every frame.
|
||||
void refreshExchangeChart();
|
||||
|
||||
/// @brief Per-category refresh intervals, adjusted by active tab
|
||||
using RefreshIntervals = services::NetworkRefreshService::Intervals;
|
||||
|
||||
@@ -675,6 +679,11 @@ private:
|
||||
bool balance_snapshot_seen_ = false; // the current in-flight snapshot was applied
|
||||
bool exchanges_fetch_started_ = false; // once-per-session CoinGecko tickers fetch
|
||||
bool chart_fetch_in_flight_ = false; // a market_chart history fetch is on the worker
|
||||
// Per-exchange candle chart (refreshExchangeChart): which pair the loaded series is for, an
|
||||
// in-flight guard, and a slow refresh timer (candles move slowly, like the aggregate chart).
|
||||
std::string exchange_chart_key_; // "<identifier>:<BASE>/<QUOTE>" of the loaded series ("" = none)
|
||||
bool exchange_chart_fetch_in_flight_ = false;
|
||||
std::chrono::steady_clock::time_point exchange_chart_last_fetch_{};
|
||||
util::AsyncTaskManager async_tasks_;
|
||||
bool pending_antivirus_dialog_ = false; // Show Windows Defender help dialog
|
||||
|
||||
|
||||
Reference in New Issue
Block a user