chart/docs/esquotes.md

88 lines
5.9 KiB
Markdown

# /ES futures-options quote finder
**Status:** future feature; no quote-finder UI, API route, or futures-options
subscription is implemented yet.
## Purpose
Find a small, user-selected set of `/ES` futures options more quickly than the
thinkorswim option-chain UI, show their live tradable quotes, and produce an
unambiguous leg or spread description that the user can copy into thinkorswim.
The app assists research and pricing only. The user reviews and enters every
order in thinkorswim; it must not submit, simulate, or automate orders.
The first useful workflow is deliberately narrow:
1. Select a futures-option expiration and call/put side.
2. Select one or more strikes, including the two legs of a vertical spread.
3. Fetch a current snapshot of bid, ask, mark, last, volume, and open interest
for that small set of exact contracts.
4. Copy a thinkorswim-tested contract or spread description for manual entry.
Do not subscribe to or poll the whole option universe. Discovery should narrow
the candidates first, then one REST request obtains the selected quotes. A user
may explicitly refresh a stale snapshot; continuous streaming is out of scope.
## Schwab API status
Checked against the configured live Schwab credentials on 2026-08-14. These
were read-only requests; no account or order endpoint was called.
| Capability | Result | Implication |
|---|---|---|
| OAuth token and Schwab client | Available | The existing app already authenticates and streams `/ES` futures. |
| `get_quotes(["/ES"])` | Works: HTTP 200, resolving to active future `/ESU26` with `assetMainType: FUTURE` | Use the plural quote endpoint. `get_quote("/ES")` puts the slash in a URL path and can return the `ES` equity instead. |
| `get_option_expiration_chain("/ES")` | Works: HTTP 200; it returned four `/ES` expiry entries and option root `ES` | Use it to enumerate available expiration dates and roots. |
| `get_option_chain("/ES")` | Fails: HTTP 400 | It is not a usable `/ES` futures-options chain/discovery endpoint. |
| `get_option_chain("/ESU26")` | Fails: HTTP 400 | Resolving the active underlying contract does not make the chain endpoint work. |
| `get_quotes()` for the supplied thinkorswim text `./E3AQ26P7780:XCME` | Fails semantically: HTTP 200 with `errors.invalidSymbols` | A thinkorswim identifier is not automatically a valid Schwab REST symbol. Resolve the API equivalent explicitly. |
| `get_quotes(["./E3AQ26P7780"])` | Works: `assetMainType: FUTURE_OPTION`; its reference description is `./E3AQ26P7780:XCME` | For this verified contract, the API form is the thinkorswim text with the `:XCME` exchange suffix removed. The snapshot included bid, ask, mark, last, volume, and open interest. |
| `LEVEL_ONE_FUTURES_OPTIONS` for `./E3AQ26P7780:XCME` | The subscription command was accepted, but no quote handler message arrived during a 15-second premarket probe | Not needed for this feature: it uses snapshots, not continuous updates. This result is retained only as future reference. |
| Historical futures-options prices | Not available | Schwab price history is not available for futures or options; the finder is a current-quote tool, not a historical-pricing system. |
| Futures-options order entry | Not available for this workflow | Keep execution in thinkorswim. Schwab Trader API support for equities and standard options must not be mistaken for `/ES` futures-options routing. |
The expiration endpoint narrows the date/root, but it does **not** return every
strike or a quoteable option symbol. Exact futures-option symbol resolution is
therefore the feature's critical discovery problem, not a formatting detail.
## Symbol and thinkorswim contract
Do not invent a futures-option symbol from a guessed `ES`, month, strike, and
call/put pattern. The supplied thinkorswim string `./E3AQ26P7780:XCME` maps to
the verified API symbol `./E3AQ26P7780`: removing `:XCME` returned the intended
`FUTURE_OPTION`, whose description returned the original thinkorswim form. This
is one tested mapping, not yet a general rule for every exchange, product,
expiration, or option root. Before building the finder, prove the following for
representative current `/ES` options:
1. Schwab `get_quotes()` returns the intended futures option, including a valid
bid and ask rather than an equity or an error.
2. The finder output can be pasted or searched in thinkorswim to select the
same leg. A two-leg spread must preserve buy/sell direction and quantity as
well as strike, expiration, and call/put side.
Store separately any Schwab API symbol and the thinkorswim copy text. They may
be identical, but that is an acceptance criterion to prove, not an assumption.
## Implementation constraints
- Keep future-options code isolated with the existing broker integration in
`app/market/`; no analysis, bars, or UI code should call Schwab directly.
- Request REST snapshots only after a user has selected exact contracts or
explicitly asked to refresh. Batch all displayed contracts into one
`get_quotes()` request; do not open a futures-options WebSocket subscription.
- Show quote freshness and whether data is delayed. A stale, wide, or missing
market is more important than a calculated spread mark.
- Treat a spread mark as a display calculation from the two current legs, not
an executable price. Preserve both bid/ask combinations so the UI can show
realistic debit/credit bounds.
- Do not persist credentials, account details, or order state in this feature.
## First implementation gate
Add a small read-only probe using representative current `/ES` options copied
from thinkorswim and their mapped Schwab API identifiers. Capture both forms,
the REST response type and quote fields, snapshot timestamp or delay status,
and the expiration/strike shown by each system. Add a regression fixture only
after that probe establishes a stable real payload and mapping rule. Until
then, a full-chain UI or automatic symbol construction would be speculative.