Investigate Bitget as an additional USTC/LUNC external oracle source #520

Closed
opened 2026-08-15 09:36:28 +00:00 by PlasticDigits · 6 comments
PlasticDigits commented 2026-08-15 09:36:28 +00:00 (Migrated from gitlab.com)

Summary

Investigate whether Bitget should be added as a fourth poll source for the indexer external USD oracle (ustc / lunc), alongside KuCoin, MEXC, and CoinGecko.

This is a research / go-no-go ticket, not an implement-by-default request. Feeds remain advisory (invariant X5) — not settlement.

Related: #515 (ticker-scoped routes + distinct CEX symbols). Runbook: docs/runbooks/indexer-external-oracle.md. Fetcher: indexer/src/indexer/oracle.rs.

Why look at Bitget

Today fetch_all_sources is a simple average of whatever KuCoin + MEXC (+ CoinGecko on alternate ticks) return. Quality and availability degrade when:

  • one CEX is down, rate-limited, or returns a stale/outlier last trade
  • KuCoin and MEXC print the same book (correlated venues) and CoinGecko 429s (already treated as soft-fail)
  • a single-venue print pulls the mean (no median / outlier reject today)

Bitget is a large spot venue that already lists both TerraClassic pairs on a public, unauthenticated ticker API. A live probe (2026-08-15) confirmed both symbols resolve and print in the same order of magnitude as KuCoin/MEXC:

Venue USTC/USDT last LUNC/USDT last Notes
Bitget v2 USTCUSDT / LUNCUSDT 0.004872 0.00004746 24h USDT vol ~$10.5k / ~$87k
KuCoin USTC-USDT / LUNC-USDT 0.00482 0.00004703 current oracle source
MEXC USTCUSDT / LUNCUSDT 0.004872 0.00004753 current oracle source

Candidate public endpoints (no API key):

  • v2 (widely used): GET https://api.bitget.com/api/v2/spot/market/tickers?symbol=USTCUSDT — field lastPr
  • v3 UTA: GET https://api.bitget.com/api/v3/market/tickers?category=SPOT&symbol=USTCUSDT — field lastPrice

Docs: Bitget Get Tickers (v3). Success code is "00000".

Investigation questions

  1. Listings / symbols (X3) — Confirm USTCUSDT and LUNCUSDT are the TerraClassic pairs (not a namesake / wrapped / futures contract). Reject any cross-wire of LUNC into USTC storage.
  2. API choice — Prefer v2 vs v3 for stability, deprecation, and rate limit (v3 ticker is documented at 20/sec/IP). Record required query params and the exact last-price field.
  3. Liquidity / print quality — Compare 24h USDT volume and bid-ask vs KuCoin/MEXC over several days. Thin USTC (~$10k/day on this snapshot) may be too noisy to include in an unweighted mean.
  4. Aggregation impact — Current code is sum / n with no median or outlier clip. Adding Bitget changes the average and may double-count if CoinGecko already includes Bitget. Decide: add as a CEX peer, replace CoinGecko on some ticks, or introduce median / max-deviation before adding any venue.
  5. Failure modes — HTTP errors, empty data, non-"00000" codes, non-finite prices (X6), 429 mapping (reuse OracleError::RateLimited). ToS / scraping constraints for unauthenticated polling at the existing oracle interval.
  6. Ops / product — sources[] on GET /api/v1/oracle/price/{ticker} would grow a bitget row. Confirm Protocol UI / integrators do not assume a fixed source set. No API contract change if we only add a source name.

Go / no-go

Go if Bitget symbols are TerraClassic, public ticker is stable, 24h volume is competitive with (or better than) the weaker of KuCoin/MEXC, and we have a written aggregation rule that does not let a thin print dominate the mean.

No-go / defer if volume is persistently thin, prints diverge from KuCoin/MEXC by more than a small band without a reject rule, v2/v3 is deprecated or geo-blocked from our indexer hosts, or CoinGecko already covers Bitget well enough that a fourth poll only adds correlation.

Out of scope (unless go)

  • Implementation in oracle.rs, new unit/integration tests, runbook/skill updates, make verify-issue-515 extension
  • Using Bitget as settlement or on-chain TWAP input
  • Adding other venues (Binance, Gate, …) in the same change — call those out as follow-ups if the aggregation design should be venue-generic

Suggested investigation output

A short comment on this issue with: recommended API + symbols, volume/spread table vs KuCoin/MEXC, aggregation recommendation (mean vs median vs outlier reject), and an explicit go or no-go.

## Summary Investigate whether **Bitget** should be added as a fourth poll source for the indexer external USD oracle (`ustc` / `lunc`), alongside KuCoin, MEXC, and CoinGecko. This is a **research / go-no-go** ticket, not an implement-by-default request. Feeds remain **advisory** (invariant **X5**) — not settlement. Related: [#515](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/515) (ticker-scoped routes + distinct CEX symbols). Runbook: [`docs/runbooks/indexer-external-oracle.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/runbooks/indexer-external-oracle.md). Fetcher: [`indexer/src/indexer/oracle.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/indexer/oracle.rs). ## Why look at Bitget Today `fetch_all_sources` is a **simple average** of whatever KuCoin + MEXC (+ CoinGecko on alternate ticks) return. Quality and availability degrade when: - one CEX is down, rate-limited, or returns a stale/outlier last trade - KuCoin and MEXC print the same book (correlated venues) and CoinGecko 429s (already treated as soft-fail) - a single-venue print pulls the mean (no median / outlier reject today) Bitget is a large spot venue that already lists both TerraClassic pairs on a **public, unauthenticated** ticker API. A live probe (2026-08-15) confirmed both symbols resolve and print in the same order of magnitude as KuCoin/MEXC: | Venue | USTC/USDT last | LUNC/USDT last | Notes | |---|---|---|---| | Bitget v2 `USTCUSDT` / `LUNCUSDT` | 0.004872 | 0.00004746 | 24h USDT vol ~$10.5k / ~$87k | | KuCoin `USTC-USDT` / `LUNC-USDT` | 0.00482 | 0.00004703 | current oracle source | | MEXC `USTCUSDT` / `LUNCUSDT` | 0.004872 | 0.00004753 | current oracle source | Candidate public endpoints (no API key): - v2 (widely used): `GET https://api.bitget.com/api/v2/spot/market/tickers?symbol=USTCUSDT` — field `lastPr` - v3 UTA: `GET https://api.bitget.com/api/v3/market/tickers?category=SPOT&symbol=USTCUSDT` — field `lastPrice` Docs: [Bitget Get Tickers (v3)](https://www.bitget.com/api-doc/uta/public/Tickers). Success `code` is `"00000"`. ## Investigation questions 1. **Listings / symbols (X3)** — Confirm `USTCUSDT` and `LUNCUSDT` are the TerraClassic pairs (not a namesake / wrapped / futures contract). Reject any cross-wire of LUNC into USTC storage. 2. **API choice** — Prefer v2 vs v3 for stability, deprecation, and rate limit (v3 ticker is documented at 20/sec/IP). Record required query params and the exact last-price field. 3. **Liquidity / print quality** — Compare 24h USDT volume and bid-ask vs KuCoin/MEXC over several days. Thin USTC (~$10k/day on this snapshot) may be too noisy to include in an unweighted mean. 4. **Aggregation impact** — Current code is `sum / n` with no median or outlier clip. Adding Bitget changes the average and may **double-count** if CoinGecko already includes Bitget. Decide: add as a CEX peer, replace CoinGecko on some ticks, or introduce median / max-deviation before adding any venue. 5. **Failure modes** — HTTP errors, empty `data`, non-`"00000"` codes, non-finite prices (**X6**), 429 mapping (reuse `OracleError::RateLimited`). ToS / scraping constraints for unauthenticated polling at the existing oracle interval. 6. **Ops / product** — `sources[]` on `GET /api/v1/oracle/price/{ticker}` would grow a `bitget` row. Confirm Protocol UI / integrators do not assume a fixed source set. No API contract change if we only add a source name. ## Go / no-go **Go** if Bitget symbols are TerraClassic, public ticker is stable, 24h volume is competitive with (or better than) the weaker of KuCoin/MEXC, and we have a written aggregation rule that does not let a thin print dominate the mean. **No-go / defer** if volume is persistently thin, prints diverge from KuCoin/MEXC by more than a small band without a reject rule, v2/v3 is deprecated or geo-blocked from our indexer hosts, or CoinGecko already covers Bitget well enough that a fourth poll only adds correlation. ## Out of scope (unless go) - Implementation in `oracle.rs`, new unit/integration tests, runbook/skill updates, `make verify-issue-515` extension - Using Bitget as settlement or on-chain TWAP input - Adding other venues (Binance, Gate, …) in the same change — call those out as follow-ups if the aggregation design should be venue-generic ## Suggested investigation output A short comment on this issue with: recommended API + symbols, volume/spread table vs KuCoin/MEXC, aggregation recommendation (mean vs median vs outlier reject), and an explicit **go** or **no-go**.
PlasticDigits commented 2026-08-15 09:36:29 +00:00 (Migrated from gitlab.com)

marked as related to #515

marked as related to #515
PlasticDigits commented 2026-08-15 09:42:33 +00:00 (Migrated from gitlab.com)

Investigation findings (2026-08-15)

Research probe from a US egress (187.13.182.64, Los Angeles / PacketHub). Production indexer indexer.dex.cl8y.com resolves to 168.119.120.255 (Hetzner, Nuremberg, DE). Single-day live snapshot (~09:39–09:41 UTC), not a multi-day volume series.

Verdict: no-go / defer as a fourth peer in today’s unweighted sum/n mean.

Conditional go only after a venue-generic aggregation change (median of CEX lasts, or max-deviation reject). Then Bitget is a reasonable third CEX vote. Do not add it to the current mean.


1. Listings / symbols (X3) — pass

Check Result
Spot symbols USTCUSDT, LUNCUSDT (status=online)
Base/quote USTC/USDT, LUNC/USDT
Coin metadata GET /api/v2/spot/public/coins?coin=USTC|LUNC → chain=Terra, deposit/withdraw enabled
Namesakes Distinct UST (coinId 290, transfer=false) and LUNA (coinId 280). Do not use those ids.
Futures collision USDT-FUTURES also lists USTCUSDT / LUNCUSDT (last ~0.004877 / 0.00004736). Coin-M / USDC-M empty.

TerraClassic spot pairs are real. Any v3 client must send category=SPOT or it can silently take a perp print.


Prefer v3 (UTA), not v2.

Bitget is migrating Classic v2 → UTA v3. v2 still answers today and is spot-only (safer against the futures collision), but v3 is the documented long-term public ticker.

v3 (recommended) v2 (works now)
URL GET https://api.bitget.com/api/v3/market/tickers GET https://api.bitget.com/api/v2/spot/market/tickers
Params category=SPOT (required) + symbol=USTCUSDT / LUNCUSDT symbol=USTCUSDT / LUNCUSDT (no hyphen)
Last field lastPrice lastPr
Success code == "00000" same
Missing category HTTP 400, code=400172 n/a
Bad symbol HTTP 400, code=40034, data=null same
Docs https://www.bitget.com/api-doc/uta/public/Tickers Classic spot ticker (still live)

Parse: require code=="00000", non-empty data[0], finite lastPrice (X6). Map HTTP 429 → existing OracleError::RateLimited. Treat non-00000 / empty data as parse/soft-fail, same as KuCoin code != 200000.

No API key. Default ORACLE_POLL_INTERVAL_MS=30000 → 2 symbols × 2 ticks/min ≈ 4 req/min, far under Bitget’s public cap.


3. Rate limits vs current sources

Source Documented cap What this poll uses Headroom at 30s
Bitget public market 20 req/s/IP (v3 ticker page + Classic “public interface” rule); global 6000 req/min/IP. HTTP 429 when exceeded. 2 GETs / 30s (or 1 unfiltered tickers call) Huge
KuCoin public pool VIP0 2000 / 30s (IP). Level-1 ticker weight 2. Live header: gw-ratelimit-limit: 2000, remaining: 1998. Exceed → 429 + ~10s lockout. 2 GETs / 30s Huge
MEXC Weight pool 300 / 10s / IP (plus account pool if keyed). Continued 429s can escalate to IP bans (minutes–days) with Retry-After. 2 GETs / 30s Huge
CoinGecko keyless ~10–30 / min / IP (dynamic). Demo key 100/min + 10k/mo. Free data often 60s+ stale. Already every other tick; 429 is soft-fail Tightest today

Bitget is not the rate-limit risk. CoinGecko already is — that is why it is alternate-tick + RateLimited debug, not a hard outage.

Authenticated Bitget UID tiers (60–250/s) and IP whitelists apply to API keys (withdraw whitelist is mandatory). Irrelevant for this unauthenticated ticker.


4. IP / geo / ToS

Topic Finding
Public ticker auth None. No IP whitelist.
This probe (US) All Bitget v2/v3 spot tickers HTTP 200. Public market data is not geo-blocked from a US IP today.
Prod indexer Coolify host is Germany (Hetzner Nuremberg).
Bitget ToS “Prohibited Countries” Includes United States, Germany, France, Austria, Japan, Singapore, Canada, Hong Kong, and others (Terms of Use § definitions + §8.6 / §11(xvii)). Trading/app access is geo-fenced; circumvention via VPN is prohibited.
Practical API risk Public REST tickers answered from a prohibited-country IP in this probe. Residual risk: Bitget could start 403-ing api.bitget.com from DE/US cloud ranges. Before any implement ticket: curl v3 SPOT tickers from the Coolify egress (not only from a laptop).
Scraping / reuse Public market endpoints are documented for unauthenticated use. ToS IP-rights clause is generic. Same class of advisory poll we already do for KuCoin/MEXC (X5 — not settlement). Interval 30s is not abusive vs 20/s.

Not a hard no-go on today’s evidence, but Germany-hosted prod + ToS prohibited list is a real ops footnote. KuCoin/MEXC public APIs also answered 200 from the same US probe.


5. Data quality vs KuCoin / MEXC / CoinGecko

24h USDT turnover (this snapshot)

Venue USTC/USDT 24h quote LUNC/USDT 24h quote vs weaker current CEX
Bitget spot $10,516 $87,406 USTC ≈ KuCoin; LUNC ≈ MEXC
KuCoin $9,398 $225,189 —
MEXC $104,033 $82,884 —
CoinGecko (global) $1.50M reported $8.32M reported Aggregator, not a venue

Issue go-bar (“competitive with the weaker of KuCoin/MEXC”): met on this snapshot for both tickers. USTC is still thin (~$10k, same order as the issue’s own probe). MEXC is ~10× Bitget/KuCoin on USTC. Not a multi-day persistence check.

CoinGecko /coins/{id}/tickers already has a Bitget row for both terrausd and terra-luna (USTC/USDT last 0.004872, LUNC/USDT last 4.744e-5). A fourth poll double-counts Bitget on even ticks if CoinGecko stays in the mean.

CG also shows much larger books on Binance / LBank (USTC Binance ~$333k; LUNC LBank ~$1.7M). Out of scope here; better liquidity follow-ups than Bitget if the goal is volume.

Last vs book (3 samples, 10s apart)

Venue USTC last vs mid USTC spread LUNC last vs mid LUNC spread
Bitget −4 to −9 bps 14–16 bps +5 to +7 bps 2.1 bps (tightest)
KuCoin −72 bps then −10 bps (stale last) 21 bps −5 to +15 bps 11–13 bps
MEXC −4 to +2 bps 8–12 bps 0 to +12 bps 6–8 bps

KuCoin USTC last 0.00482 vs bid 0.00485 on sample 1 is the stale-print problem the ticket describes. Bitget last stayed inside a few bps of mid.

KuCoin LUNC last ~0.00004694 was ~107 bps below Bitget/MEXC (~0.00004744 / 0.00004746). Bitget vs MEXC LUNC: ~4 bps. Bitget is aligned with MEXC; KuCoin is the outlier on this window.

USTC top-of-book notional is thin on Bitget (best bid ~2.5k USTC / ~$12; best ask ~914 USTC). A single clip moves lastPr. Same class of risk as KuCoin USTC, worse than MEXC.

Mean impact if added today (sample 3 lasts)

Ticker Current mean (KC+MEXC) +Bitget mean +Bitget+CG mean Median {KC,MEXC,BG}
USTC 0.004866 0.004870 0.004871 0.004879
LUNC 0.00004720 0.00004728 0.00004732 0.00004744

On USTC, adding Bitget gives two thin books (KuCoin+Bitget) 2/3 weight vs MEXC. That fails the ticket’s own rule (“thin print must not dominate the mean”). On LUNC, the mean still drags toward the cheap KuCoin last; median would drop KuCoin and sit on Bitget/MEXC.


6. Failure modes (if ever implemented)

  • HTTP 429 → OracleError::RateLimited (reuse CoinGecko path).
  • HTTP 400 + 40034 / 400172 / data=null → parse/soft-fail, do not average.
  • code != "00000" → parse/soft-fail.
  • Non-finite lastPrice → reject (X6); do not f64_to_bd a NaN into 0.
  • Empty data array (wrong category / delist) → fail closed.
  • Never omit category=SPOT on v3.
  • Never use KuCoin-style USTC-USDT on Bitget (40034).

7. Ops / product

  • GET /api/v1/oracle/price/{ticker} sources[] is an open string list. Protocol UI maps it dynamically (ProtocolPage table). Adding bitget is not an API contract break.
  • Catalog metadata currently names “KuCoin, MEXC, CoinGecko” in indexer/src/api/oracle.rs — docs-only string, update if a source is added.
  • Still X5 advisory. Not TWAP / settlement.

Aggregation recommendation

Do not extend ok_prices.sum() / n with Bitget.

Written rule for a later implement ticket (venue-generic, not Bitget-specific):

  1. Poll CEX peers in parallel: KuCoin, MEXC, optional Bitget.
  2. Keep CoinGecko on alternate ticks as diagnostic / soft-fail only — exclude from average (it already embeds Bitget + many other venues).
  3. Publish average as the median of finite CEX lasts (or reject any print > ~50–100 bps from the median, then mean the rest).
  4. Optional later: prefer bid/ask mid when |last-mid| is large (KuCoin USTC sample 1).

That rule is the actual quality fix. Bitget is optional insurance when KuCoin or MEXC 429s/dies — not a reason to overweight two ~$10k USTC books.


Go / no-go

Bar (from ticket) Result
TerraClassic symbols, not namesake/futures Pass (use SPOT only)
Public ticker stable Pass (v2+v3 200; v3 needs category)
24h vol ≥ weaker of KuCoin/MEXC Pass this snapshot; USTC still thin; no multi-day series
Aggregation that blocks thin-print dominance Fail today (sum/n)
Not geo-blocked from indexer hosts Public API OK from US; prod is DE (ToS prohibited). Confirm Coolify egress before coding.
CoinGecko already covers Bitget Yes — fourth mean peer is mostly correlation

No-go / defer for implementation on current oracle.rs.

Follow-ups (not this ticket): (1) median / outlier-reject aggregation + tests; (2) optional Bitget v3 SPOT fetcher behind that rule; (3) Coolify egress probe to api.bitget.com; (4) if the goal is liquidity, evaluate Binance/LBank before more thin CEXes.

## Investigation findings (2026-08-15) Research probe from a **US** egress (`187.13.182.64`, Los Angeles / PacketHub). Production indexer `indexer.dex.cl8y.com` resolves to **`168.119.120.255`** (Hetzner, Nuremberg, **DE**). Single-day live snapshot (~09:39–09:41 UTC), not a multi-day volume series. **Verdict: no-go / defer** as a fourth peer in today’s unweighted `sum/n` mean. **Conditional go** only after a venue-generic aggregation change (median of CEX lasts, or max-deviation reject). Then Bitget is a reasonable third CEX vote. Do not add it to the current mean. --- ### 1. Listings / symbols (X3) — pass | Check | Result | |---|---| | Spot symbols | `USTCUSDT`, `LUNCUSDT` (`status=online`) | | Base/quote | `USTC`/`USDT`, `LUNC`/`USDT` | | Coin metadata | `GET /api/v2/spot/public/coins?coin=USTC\|LUNC` → `chain=Terra`, deposit/withdraw enabled | | Namesakes | Distinct `UST` (coinId 290, `transfer=false`) and `LUNA` (coinId 280). Do not use those ids. | | Futures collision | **USDT-FUTURES** also lists `USTCUSDT` / `LUNCUSDT` (last ~0.004877 / 0.00004736). Coin-M / USDC-M empty. | TerraClassic spot pairs are real. Any v3 client **must** send `category=SPOT` or it can silently take a perp print. --- ### 2. Recommended API **Prefer v3 (UTA), not v2.** Bitget is migrating Classic v2 → UTA v3. v2 still answers today and is spot-only (safer against the futures collision), but v3 is the documented long-term public ticker. | | v3 (recommended) | v2 (works now) | |---|---|---| | URL | `GET https://api.bitget.com/api/v3/market/tickers` | `GET https://api.bitget.com/api/v2/spot/market/tickers` | | Params | **`category=SPOT` (required)** + `symbol=USTCUSDT` / `LUNCUSDT` | `symbol=USTCUSDT` / `LUNCUSDT` (no hyphen) | | Last field | `lastPrice` | `lastPr` | | Success | `code == "00000"` | same | | Missing category | HTTP 400, `code=400172` | n/a | | Bad symbol | HTTP 400, `code=40034`, `data=null` | same | | Docs | https://www.bitget.com/api-doc/uta/public/Tickers | Classic spot ticker (still live) | Parse: require `code=="00000"`, non-empty `data[0]`, finite `lastPrice` (**X6**). Map HTTP **429** → existing `OracleError::RateLimited`. Treat non-`00000` / empty `data` as parse/soft-fail, same as KuCoin `code != 200000`. No API key. Default `ORACLE_POLL_INTERVAL_MS=30000` → 2 symbols × 2 ticks/min ≈ **4 req/min**, far under Bitget’s public cap. --- ### 3. Rate limits vs current sources | Source | Documented cap | What this poll uses | Headroom at 30s | |---|---|---|---| | **Bitget** public market | **20 req/s/IP** (v3 ticker page + Classic “public interface” rule); global **6000 req/min/IP**. HTTP **429** when exceeded. | 2 GETs / 30s (or 1 unfiltered tickers call) | Huge | | **KuCoin** public pool | VIP0 **2000 / 30s** (IP). Level-1 ticker weight **2**. Live header: `gw-ratelimit-limit: 2000`, `remaining: 1998`. Exceed → 429 + ~10s lockout. | 2 GETs / 30s | Huge | | **MEXC** | Weight pool **300 / 10s / IP** (plus account pool if keyed). Continued 429s can escalate to IP bans (minutes–days) with `Retry-After`. | 2 GETs / 30s | Huge | | **CoinGecko** keyless | **~10–30 / min / IP** (dynamic). Demo key 100/min + 10k/mo. Free data often **60s+** stale. | Already every **other** tick; 429 is soft-fail | **Tightest today** | Bitget is not the rate-limit risk. CoinGecko already is — that is why it is alternate-tick + `RateLimited` debug, not a hard outage. Authenticated Bitget UID tiers (60–250/s) and IP **whitelists** apply to **API keys** (withdraw whitelist is mandatory). Irrelevant for this unauthenticated ticker. --- ### 4. IP / geo / ToS | Topic | Finding | |---|---| | Public ticker auth | None. No IP whitelist. | | This probe (US) | All Bitget v2/v3 spot tickers **HTTP 200**. Public market data is **not** geo-blocked from a US IP today. | | Prod indexer | Coolify host is **Germany** (Hetzner Nuremberg). | | Bitget ToS “Prohibited Countries” | Includes **United States**, **Germany**, France, Austria, Japan, Singapore, Canada, Hong Kong, and others ([Terms of Use](https://www.bitget.com/support/articles/360014944032-terms-of-use) § definitions + §8.6 / §11(xvii)). Trading/app access is geo-fenced; circumvention via VPN is prohibited. | | Practical API risk | Public REST tickers answered from a prohibited-country IP in this probe. Residual risk: Bitget could start 403-ing `api.bitget.com` from DE/US cloud ranges. **Before any implement ticket:** curl v3 SPOT tickers from the Coolify egress (not only from a laptop). | | Scraping / reuse | Public market endpoints are documented for unauthenticated use. ToS IP-rights clause is generic. Same class of advisory poll we already do for KuCoin/MEXC (**X5** — not settlement). Interval 30s is not abusive vs 20/s. | **Not a hard no-go** on today’s evidence, but Germany-hosted prod + ToS prohibited list is a real ops footnote. KuCoin/MEXC public APIs also answered 200 from the same US probe. --- ### 5. Data quality vs KuCoin / MEXC / CoinGecko #### 24h USDT turnover (this snapshot) | Venue | USTC/USDT 24h quote | LUNC/USDT 24h quote | vs weaker current CEX | |---|---:|---:|---| | **Bitget** spot | **$10,516** | **$87,406** | USTC ≈ KuCoin; LUNC ≈ MEXC | | KuCoin | $9,398 | **$225,189** | — | | MEXC | **$104,033** | $82,884 | — | | CoinGecko (global) | $1.50M reported | $8.32M reported | Aggregator, not a venue | Issue go-bar (“competitive with the weaker of KuCoin/MEXC”): **met on this snapshot** for both tickers. USTC is still **thin** (~$10k, same order as the issue’s own probe). MEXC is ~10× Bitget/KuCoin on USTC. **Not** a multi-day persistence check. CoinGecko `/coins/{id}/tickers` already has a **Bitget** row for both `terrausd` and `terra-luna` (USTC/USDT last 0.004872, LUNC/USDT last 4.744e-5). A fourth poll **double-counts Bitget on even ticks** if CoinGecko stays in the mean. CG also shows much larger books on **Binance / LBank** (USTC Binance ~$333k; LUNC LBank ~$1.7M). Out of scope here; better liquidity follow-ups than Bitget if the goal is volume. #### Last vs book (3 samples, 10s apart) | Venue | USTC last vs mid | USTC spread | LUNC last vs mid | LUNC spread | |---|---:|---:|---:|---:| | **Bitget** | −4 to −9 bps | 14–16 bps | +5 to +7 bps | **2.1 bps** (tightest) | | KuCoin | **−72 bps** then −10 bps (stale last) | 21 bps | −5 to +15 bps | 11–13 bps | | MEXC | −4 to +2 bps | 8–12 bps | 0 to +12 bps | 6–8 bps | KuCoin **USTC last 0.00482 vs bid 0.00485** on sample 1 is the stale-print problem the ticket describes. Bitget last stayed inside a few bps of mid. KuCoin **LUNC last ~0.00004694** was **~107 bps below** Bitget/MEXC (~0.00004744 / 0.00004746). Bitget vs MEXC LUNC: **~4 bps**. Bitget is aligned with MEXC; KuCoin is the outlier on this window. USTC top-of-book notional is thin on Bitget (best bid ~2.5k USTC / ~$12; best ask ~914 USTC). A single clip moves `lastPr`. Same class of risk as KuCoin USTC, worse than MEXC. #### Mean impact if added today (sample 3 lasts) | Ticker | Current mean (KC+MEXC) | +Bitget mean | +Bitget+CG mean | Median {KC,MEXC,BG} | |---|---:|---:|---:|---:| | USTC | 0.004866 | 0.004870 | 0.004871 | 0.004879 | | LUNC | 0.00004720 | 0.00004728 | 0.00004732 | 0.00004744 | On USTC, adding Bitget gives **two thin books (KuCoin+Bitget) 2/3 weight** vs MEXC. That fails the ticket’s own rule (“thin print must not dominate the mean”). On LUNC, the mean still drags toward the cheap KuCoin last; **median would drop KuCoin** and sit on Bitget/MEXC. --- ### 6. Failure modes (if ever implemented) - HTTP 429 → `OracleError::RateLimited` (reuse CoinGecko path). - HTTP 400 + `40034` / `400172` / `data=null` → parse/soft-fail, do not average. - `code != "00000"` → parse/soft-fail. - Non-finite `lastPrice` → reject (**X6**); do not `f64_to_bd` a NaN into `0`. - Empty `data` array (wrong category / delist) → fail closed. - Never omit `category=SPOT` on v3. - Never use KuCoin-style `USTC-USDT` on Bitget (`40034`). --- ### 7. Ops / product - `GET /api/v1/oracle/price/{ticker}` `sources[]` is an open string list. Protocol UI maps it dynamically (`ProtocolPage` table). Adding `bitget` is **not** an API contract break. - Catalog `metadata` currently names “KuCoin, MEXC, CoinGecko” in `indexer/src/api/oracle.rs` — docs-only string, update if a source is added. - Still **X5** advisory. Not TWAP / settlement. --- ### Aggregation recommendation Do **not** extend `ok_prices.sum() / n` with Bitget. Written rule for a later implement ticket (venue-generic, not Bitget-specific): 1. Poll CEX peers in parallel: KuCoin, MEXC, optional Bitget. 2. Keep CoinGecko on alternate ticks as **diagnostic / soft-fail only** — exclude from `average` (it already embeds Bitget + many other venues). 3. Publish `average` as the **median** of finite CEX lasts (or reject any print > ~50–100 bps from the median, then mean the rest). 4. Optional later: prefer bid/ask mid when `|last-mid|` is large (KuCoin USTC sample 1). That rule is the actual quality fix. Bitget is optional insurance when KuCoin or MEXC 429s/dies — not a reason to overweight two ~$10k USTC books. --- ### Go / no-go | Bar (from ticket) | Result | |---|---| | TerraClassic symbols, not namesake/futures | **Pass** (use SPOT only) | | Public ticker stable | **Pass** (v2+v3 200; v3 needs `category`) | | 24h vol ≥ weaker of KuCoin/MEXC | **Pass this snapshot**; USTC still thin; no multi-day series | | Aggregation that blocks thin-print dominance | **Fail today** (`sum/n`) | | Not geo-blocked from indexer hosts | **Public API OK from US**; prod is **DE** (ToS prohibited). Confirm Coolify egress before coding. | | CoinGecko already covers Bitget | **Yes** — fourth mean peer is mostly correlation | **No-go / defer** for implementation on current `oracle.rs`. Follow-ups (not this ticket): (1) median / outlier-reject aggregation + tests; (2) optional Bitget v3 SPOT fetcher behind that rule; (3) Coolify egress probe to `api.bitget.com`; (4) if the goal is liquidity, evaluate Binance/LBank before more thin CEXes.
PlasticDigits commented 2026-08-15 09:45:21 +00:00 (Migrated from gitlab.com)

Items 1, 2, 4 approved for followup, but deferred as low priority

Items 1, 2, 4 approved for followup, but deferred as low priority
PlasticDigits commented 2026-08-17 10:35:57 +00:00 (Migrated from gitlab.com)

mentioned in issue #550

mentioned in issue #550
PlasticDigits commented 2026-08-19 12:11:17 +00:00 (Migrated from gitlab.com)

mentioned in issue #579

mentioned in issue #579
PlasticDigits commented 2026-08-19 12:11:19 +00:00 (Migrated from gitlab.com)

marked as related to #579

marked as related to #579
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-21 12:17:00 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#520
No description provided.