Market data providers
Choosing a price feed for the desk — why the WebSocket entitlement is the only thing that matters, what each of the four supported vendors actually sells, and how to test a key before you commit it.
The terminal quotes from a WebSocket and from nothing else.
That one sentence decides your provider choice, your budget and most of your
support load. There is no REST polling fallback anywhere in this extension.
handleQuote — the stream's message handler — is the only writer of a symbol's
QUOTING state, and every open and every close is refused while a symbol is not
QUOTING. A plan that serves REST beautifully and no socket produces an install
where credentials verify, prices render once, and every single order comes back
"Pricing is temporarily unavailable for this instrument".
The single-active rule
Exactly one market-data provider is active at a time. Activating one deactivates every other. That is deliberate: one feed owns the marks that fills are priced against, and two feeds disagreeing at the moment of a fill is a dealing-desk hazard, not a redundancy feature.
Two things are not bound by that rule, because a bar is the same bar whoever serves it:
- Historical candles. The chart back-fill tries the active provider first, then up to two other providers that hold credentials. This is why you can run a feed that streams beautifully but sells no history alongside one that has deep history and no live socket.
- The economic calendar and market news. Any configured provider capable of serving them is tried. Only the Finnhub adapter implements either, and news is free on its tier — so keeping a Finnhub key configured is worth doing even if Finnhub is not your quote source.
Which provider
The comparison below is the same data the admin Providers page renders, and the costs are the vendors' published list prices — indicative only, and none of them include a redistribution licence.
| Provider | Streams free | Coverage | Watch out for |
|---|---|---|---|
| Twelve Data (recommended) | No | FX, US and global stocks, commodities, indices, crypto | The free tier has 8 trial WebSocket credits — enough to test, not to run a desk. |
| Finnhub | No, for FX | US equities, FX, crypto, news | Forex candles return 403 on free. The free socket carries US equities only. |
| TraderMade | No | FX pairs and precious metals | No stocks at all. Streaming only on streaming-enabled plans. |
| Polygon / Massive | No | NYSE/Nasdaq equities and ETFs, plus currencies and metals on higher plans | Entry plans serve daily bars only and answer the forex WebSocket with a plan refusal, which opens the circuit breaker and leaves every symbol unquotable. |
None of the four streams on a free tier, and the terminal cannot quote a tradable price without a stream. Budget for a streaming-enabled plan before you plan a launch date: a free key will verify, render a price once, and then refuse every order. Twelve Data is the broadest single-vendor option — FX, equities, indices and crypto from one key — which is why it leads the table.
Every vendor here treats showing their prices to third parties as a separate licence from consuming them yourself. Free and entry tiers generally do not include it. This is a contractual matter between you and the vendor — nothing in the software enforces or waives it.
Test a key before you save it
Configuring a provider used to be a four-step loop with a process restart in the
middle: paste the key into .env, restart, click Verify, discover the plan has
no WebSocket, start again. The admin Providers page collapses that into one
round trip.
Paste the candidate key into the card and press test. The backend builds a throwaway adapter from it, runs the vendor's credential check, opens the quote stream, waits for the first tick or a connected status, and closes it again. The value is never cached, never persisted and never logged; it exists for the duration of the call.
The stream half is the point. A REST credential check is a plain
GET /quote, which a non-streaming plan answers perfectly well. Only the socket
probe tells you whether the desk can trade.
Getting each provider's credentials
Register at twelvedata.com, confirm your email, then open Account → API
Keys. There is one key per account and it carries whatever plan you are on.
Test it before you save. The free tier answers the REST credential check happily and still cannot stream, and the streaming half is the one that decides whether the desk can trade. Streaming and external display rights both start at the Venture tier.
Set APP_TWELVEDATA_API_KEY and restart.
Register at finnhub.io; the free key is issued immediately. Copy the API key
from the dashboard — the plain key, not a webhook secret. Set
APP_FINNHUB_API_KEY and restart.
Expect the stream probe to fail on forex. The free WebSocket carries US
equities only and /forex/candle answers 403. That is a plan limit, not a bad
key. Keep Finnhub configured anyway: its market news is free and feeds the
terminal regardless of which provider is active for quotes.
Sign up at tradermade.com; the trial issues a key immediately. Copy it from
the dashboard — the WebSocket docs call it a "user key", it is the same value.
Set APP_TRADERMADE_API_KEY and restart.
Test the stream explicitly. REST live rates work on the free trial while the WebSocket does not, so a passing credential check alone does not mean the desk can quote. There are no stocks on any TraderMade plan.
Register at polygon.io, then take the key from Dashboard → API Keys. Set
APP_POLYGON_API_KEY and restart.
Read your plan carefully. Entry plans serve daily aggregates only, have no
WebSocket, and have no real-time forex snapshot — the adapter degrades to
previous close, which is not a tradable price. Polygon also answers
NOT_AUTHORIZED for recent minute aggregates on entry plans, which is one
ordinary chart request away rather than a corner case.
What happens when a provider misbehaves
The manager applies a cool-off rather than hammering a failing vendor, and the scope of that cool-off is chosen deliberately.
| Failure | Cool-off | Scope |
|---|---|---|
| Rate limited (429) | The vendor's Retry-After, clamped to 1–15 minutes |
The whole provider |
| Credential rejected (auth) | 1 hour | The whole provider |
| Plan limit (403 on a specific endpoint) | 1 hour | History only — the live quote feed is untouched |
The last row matters. A plan limit means "the key is fine, this data is not sold to you" — an endpoint verdict, not an account verdict. Letting a 403 on one chart timeframe pause the price feed would take the marks that fills are priced against down with it.
Cool-offs are stored in Redis and are per provider, so one vendor's quota exhaustion cannot blind another. Repeated identical states are logged once, not once per symbol per interval.
After changing providers
Switching the active provider does not re-map your instruments. Import never
deletes another provider's mapping and never changes a status, because an
ACTIVE instrument may hold open client positions.
So the sequence is: activate the new provider, run Import Instrument Catalog
again, then read the strandedActive list in the response. Those instruments
are live for customers and have no mapping for the feed that is now running —
they will go quiet. Either map them by hand under Instruments, or move them
to CLOSE_ONLY until they are empty.
Next
- Instruments, groups and sessions — what to do with the catalog once it is linked
- Troubleshooting — symbols that never quote, flat charts, repeating plan errors
- Settings reference — every environment variable in one table