What Schwab's Trader API can do, declared before anything was written against it.
Every value here is read out of the two OpenAPI documents committed under
docs/reference/schwab/openapi/, or out of the portal's Documentation tab beside them.
Nothing is recalled and nothing is carried over from another venue. The distinction
earns its keep: Gemini's own published candle-width list names three widths its API
rejects, and only measurement caught that.
docs/reference/schwab/spec-facts.md names the schema or parameter behind each value.
When something here changes, that file is what it must be checked against.
Nothing here is measured against the live API
Reading a specification is tier-1 evidence (D7). It supports :experimental and it can
never support :proven, because a specification describes what a venue is documented to
do rather than what it does. Every endpoint below is therefore :experimental or
:unsupported, and the one live observation on record is an unauthenticated 401 from
/marketdata/v1/quotes, which confirms the base URL and nothing else.
This venue is not shaped like the others
Five differences matter enough to state before the values:
A symbol is one instrument, not a pair. Every other venue in the family addresses a
pair and returns one quote shape. Schwab has seven quote shapes across AssetMainType
and a symbol names a single security.
The catalogue cannot be enumerated. /instruments has no list-everything projection,
so get_symbols/1 is declared active but requires a :query. It is not
:unsupported, and the difference is the point: "needs a search term" and "has no
endpoint" are different facts, and a caller has to be able to act on each.
There is no order book and no socket. No endpoint in either document returns depth,
and neither document describes a streaming surface. The feed is a REST poll served by
Core.PollingFeed.
The market closes. /markets answers isOpen directly. This is the venue
market_status/1 was added to the contract for — a feed that alarms on silence would
alarm every night, making a real outage indistinguishable from Saturday.
There is no sandbox. Schwab writes that Trader API sandboxes "will be available later this year" in a document published 2025-10-30, and neither spec declares a non-production server. Unlike Gemini, this venue cannot be exercised end to end without touching real money.
Summary
Types
A {name, arity} pair naming one Core.Venue callback.
Functions
The declaration.
The eight candle widths GET /pricehistory serves.
Endpoints this venue does not serve, as distinct from ones not yet written.
Types
Functions
@spec declaration() :: DpExchange.Core.Capabilities.t()
The declaration.
Built through Capabilities.new/1 so Core validates it: an endpoint naming a callback
that does not exist, an order type outside the contract's vocabulary, or a malformed
ceiling all raise here rather than being discovered by a consumer.
@spec timeframes() :: [String.t()]
The eight candle widths GET /pricehistory serves.
A width is not one parameter here. It is a (periodType, frequencyType, frequency)
triple, and the legal combinations are constrained in both directions — see
spec-facts.md §1 for the full table.
The minute widths are only reachable through periodType=day, which caps the
lookback at ten days. Ten days of one-minute candles is the most this venue will
serve, and no combination of the other parameters extends it. A consumer asking for a
year of one-minute data cannot be served and must be told so — handing back a coarser
series instead is the substitution this family exists to refuse.
@spec venue_does_not_serve() :: [endpoint()]
Endpoints this venue does not serve, as distinct from ones not yet written.
Everything named here is :unsupported in declaration/0, and the test suite asserts
that rather than trusting it.