Skip to content

Add market cap and supply, from a second data source - #17

Merged
ralyodio merged 1 commit into
mainfrom
crypto-fundamentals
Aug 8, 2026
Merged

ralyodio merged 1 commit into
mainfrom
crypto-fundamentals

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

You said use another provider — this adds CoinGecko, the first non-Alpaca source on the crypto path.

Market cap cannot be derived from a price without a circulating supply, and Alpaca carries neither. CoinGecko is keyless like the rest of this path: one batched request covering every asset, cached five minutes, and the page degrades to "—" rather than failing if it is unreachable.

What lands

New Supply & valuation section on a pair page, and on /api/crypto/<PAIR> tagged source: "coingecko":

market cap · rank · fully diluted valuation · circulating / total / max supply · all-time high with date and distance from it · aggregate 24h volume

Live for BTC: $1.29T cap, rank #1, 20.07M of 21M supply, ATH $126,080, -48.97% from it.

Provenance, since two vendors now share a page

The section is badged CoinGecko and carries its own as-of timestamp; the Alpaca figures say so too. The important one: aggregate 24h volume sits near the venue volume under Performance and is explicitly called out as not comparable — for BTC that is $18.36B against a few hundred thousand. Conflating them would overstate liquidity by orders of magnitude.

Two guards, both firing on assets we actually list

CoinGecko keeps serving records for tokens that have moved on, and a plausible-looking number is worse than a blank.

Asset What upstream returns What we show
MKR live-looking $1,272 price, zero circulating supply (migrated to SKY) "no circulating supply… usually means it has migrated to a successor token"
MATIC figures last updated 2026-02-03, 185 days ago (migrated to POL) "the upstream record has not updated in 185 days"

Rendering $0.00 market cap for MKR would be a false statement; a six-month-old supply beside a live price is the same failure mode this codebase avoids everywhere else. Both render the reason rather than a bare dash, which would read as a bug rather than as the asset having moved on.

Neither is fixed by pointing at the successor token — SKY and POL are different assets from the ones Alpaca prices. Both guards are verified against the real API, not just mocks.

Other notes

  • CoinGecko ids are explicit, never resolved by symbol: tickers collide across listings and "UNI" or "GRT" would eventually match an impostor. All 27 verified.
  • Concurrent misses collapse into one request so a rate-limited free tier is not stampeded by parallel page loads.

Verification

🤖 Generated with Claude Code

Alpaca carries no supply figure, and market cap cannot be derived from a price
without one, so this adds the first non-Alpaca source on the crypto path:
CoinGecko. Keyless like the rest of it, one batched request covering every
asset, cached five minutes, and a page that degrades to "—" rather than failing
when the source is unreachable.

New on a pair page, under "Supply & valuation": market cap and rank, fully
diluted valuation, circulating / total / max supply, all-time high with its
date and the distance from it, and aggregate 24h volume. Also on
/api/crypto/<PAIR>, tagged source: "coingecko".

Provenance is the thing to get right here, because two vendors now appear on
one page. The section is badged CoinGecko and carries its own as-of timestamp,
and the venue figures say so too. In particular the aggregate 24h volume sits
near the venue volume under Performance and is explicitly called out as not
comparable — for BTC that is $18.36B against a few hundred thousand.

Two guards, both of which fire on real assets we list:

  - Non-positive supply is treated as absent. MKR migrated to SKY and now
    reports zero circulating supply against a live-looking $1,272 price;
    rendering "$0.00 market cap" would be a false statement.
  - Stale records are treated as absent. MATIC migrated to POL and its record
    last updated on 2026-02-03 — 185 days — while still returning figures. A
    six-month-old supply beside a live price is the same "stale data wearing
    the costume of live data" failure this codebase avoids elsewhere.

Both render an explanation rather than a bare dash, which would read as a bug
rather than as the asset having moved on. Neither is fixed by pointing at the
successor token: SKY and POL are different assets from the ones Alpaca prices.

Ids are explicit rather than resolved by symbol — tickers collide across
listings and "UNI" or "GRT" would eventually match an impostor. All 27 verified
against the API.

470 tests pass (17 new), tsc clean, verified live: BTC ($1.29T cap, 20.07M of
21M supply, -48.97% from ATH), SOL (#7, 582.05M supply), and both guards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

56 finding(s)

HIGH/CRITICAL: 6 | MEDIUM: 50

Severity Rule Location
HIGH js-unescaped-html-sink public/auth.js:189
HIGH js-ssrf-outbound-request src/crypto/client.ts:109
HIGH js-ssrf-outbound-request src/crypto/fundamentals.ts:137
HIGH js-ssrf-outbound-request src/providers/alpaca.ts:76
HIGH secret-generic-api-key test/auth.test.ts:190
HIGH secret-generic-credential test/credits.test.ts:24
MEDIUM js-unescaped-html-sink public/app.js:41
MEDIUM js-unescaped-html-sink public/app.js:97
MEDIUM js-unescaped-html-sink public/app.js:232
MEDIUM js-unescaped-html-sink public/app.js:244
MEDIUM js-unescaped-html-sink public/app.js:246
MEDIUM js-unescaped-html-sink public/app.js:338
MEDIUM js-unescaped-html-sink public/app.js:354
MEDIUM js-unescaped-html-sink public/app.js:358
MEDIUM js-unescaped-html-sink public/app.js:362
MEDIUM js-unescaped-html-sink public/app.js:381
MEDIUM js-unescaped-html-sink public/app.js:385
MEDIUM js-unescaped-html-sink public/app.js:389
MEDIUM js-unescaped-html-sink public/app.js:572
MEDIUM js-unescaped-html-sink public/app.js:657
MEDIUM js-unescaped-html-sink public/app.js:678
MEDIUM js-unescaped-html-sink public/app.js:800
MEDIUM js-unescaped-html-sink public/app.js:801
MEDIUM js-unescaped-html-sink public/app.js:810
MEDIUM js-unescaped-html-sink public/app.js:848
MEDIUM js-unescaped-html-sink public/app.js:908
MEDIUM js-unescaped-html-sink public/app.js:913
MEDIUM js-unescaped-html-sink public/app.js:926
MEDIUM js-unescaped-html-sink public/app.js:977
MEDIUM js-unescaped-html-sink public/app.js:1056
MEDIUM js-unescaped-html-sink public/app.js:1062
MEDIUM js-unescaped-html-sink public/app.js:1129
MEDIUM js-unescaped-html-sink public/app.js:1169
MEDIUM js-unescaped-html-sink public/app.js:1218
MEDIUM js-unescaped-html-sink public/app.js:1234
MEDIUM js-unescaped-html-sink public/app.js:1377
MEDIUM js-unescaped-html-sink public/app.js:1379
MEDIUM js-unescaped-html-sink public/app.js:1396
MEDIUM js-unescaped-html-sink public/app.js:1546
MEDIUM js-unescaped-html-sink public/app.js:1550
MEDIUM js-unescaped-html-sink public/app.js:1560
MEDIUM js-unescaped-html-sink public/auth.js:41
MEDIUM js-unescaped-html-sink public/auth.js:59
MEDIUM js-unescaped-html-sink public/auth.js:67
MEDIUM js-unescaped-html-sink public/auth.js:106
MEDIUM js-unescaped-html-sink public/auth.js:193
MEDIUM js-unescaped-html-sink public/auth.js:256
MEDIUM js-unescaped-html-sink public/auth.js:258
MEDIUM js-unescaped-html-sink public/auth.js:275
MEDIUM sql-template-interpolation src/cli.ts:552

…and 6 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 46684bd into main Aug 8, 2026
4 checks passed
@ralyodio
ralyodio deleted the crypto-fundamentals branch August 8, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant