Skip to content

feat(quote): add Transport to serve quote pull APIs over HTTP - #130

Open
hogan-yuan wants to merge 2 commits into
mainfrom
feat/quote-http-transport
Open

hogan-yuan wants to merge 2 commits into
mainfrom
feat/quote-http-transport

Conversation

@hogan-yuan

Copy link
Copy Markdown
Member

What

Ports longbridge/openapi#604 to the Go SDK. Adds quote.Transport (quote.TransportWebSocket, default / quote.TransportHTTP), set with quote.WithTransport(...), config.Config.QuoteTransport or env LONGBRIDGE_QUOTE_TRANSPORT=ws|http. With TransportHTTP the 19 QuoteContext pull APIs that have a REST equivalent (StaticInfo, Quote, OptionQuote, WarrantQuote, Depth, Brokers, Participants, Trades, Intraday, Candlesticks, HistoryCandlesticksByOffset/ByDate, OptionChainExpiryDateList, WarrantIssuers, WarrantList, TradingSession, TradingDays, CapitalFlow, CapitalDistribution, CalcIndex) go over POST /quote/* instead of the quote WebSocket. Method signatures and return types are unchanged.

Why

Same motivation as the Rust PR: longbridge/longbridge-mcp#162 re-implemented every quote tool on the REST endpoints inside the MCP to get rid of its per-user WebSocket pool. That belongs in the SDK, and Go consumers should get the same switch.

How

  • quote/transport.go: httpPaths maps WebSocket commands to REST paths; core.do branches on the transport. Requests are the same protobuf messages encoded with protojson (proto field names, absent sub-messages omitted); responses are decoded with protojson (DiscardUnknown, so the gateway's extra volume_str is ignored; int64-as-string and null sub-messages are handled natively).
  • Lazy WebSocket: newCore no longer dials unconditionally. With TransportWebSocket it dials and fetches the profile up front exactly as before; with TransportHTTP the socket is dialed on first need (Subscribe, On* callbacks, Profile(), Subscriptions, or any command without a REST path) via core.ws(). Push handlers registered before the dial are queued and attached when it happens. Close() is a no-op if never dialed.
  • quote/overnight.go aligns the one known server-side difference: REST always returns US overnight data and has no opt-out, while the WebSocket only returns it with WithEnableOvernight(true). On the HTTP path (and only there) the SDK clears OverNightQuote, filters overnight Intraday lines for US equities, and for CandlestickTradeSessionAll history queries drops OVERNIGHT_TRADE bars and tops up count/range-capped windows with offset queries (≤1000 per request, ≤8 rounds). The WebSocket path is behaviourally identical to main.
  • examples/quote_http_compare calls every pull API over both transports and diffs the JSON output.

Verification

  • go test ./quote/ passes (new tests: refill paging scenarios, US overnight window in EDT/EST, command→path map completeness, gateway proto-JSON decode replay, request encoding). The two failing config package tests (Test_YamlConfig / Test_TomlConfig) fail identically on main (missing fixture files) and are unrelated.
  • go vet ./quote/ reports only the pre-existing copylocks warnings from util.Copy (same 8 on main).
  • Canary (openapi-global.longbridge.xyz), overnight off: 30/32 cases identical between transports; the 2 remaining are the gateway error-code gap below. Production and overnight-on runs plus the full input/output report follow in a comment.

Known gateway gaps (not fixable in the SDK)

  1. REST reports business errors as 500 internal server error instead of passing the code through (WS: 301600, 301604, 301607).
  2. Production /quote/* replicas are not consistent with each other (value-level differences in avg_price/volume; two consecutive REST calls can disagree while the WS is stable).
  3. No REST opt-out for US overnight data (handled client-side here).

🤖 Generated with Claude Code

Port longbridge/openapi#604. Add `quote.Transport` (`TransportWebSocket`,
default, or `TransportHTTP`), set with `quote.WithTransport`,
`config.Config.QuoteTransport` or env `LONGBRIDGE_QUOTE_TRANSPORT=ws|http`.
With `TransportHTTP` the 19 `QuoteContext` pull APIs that have a REST
equivalent are sent as `POST /quote/*` instead of over the quote WebSocket:
the request is the same protobuf message encoded as proto-JSON and the
response is decoded back into the same message, so method signatures and
return types are unchanged. The WebSocket is dialed lazily, only when
subscriptions, push callbacks, `Profile()` or an API without a REST
equivalent is first used, so a context that only pulls data never opens a
connection.

Results are aligned with the WebSocket, including US overnight data, which
REST always returns but the WebSocket only returns with
`WithEnableOvernight(true)`: on the HTTP path the SDK clears
`OverNightQuote`, filters overnight intraday lines for US equities and, for
`CandlestickTradeSessionAll` history queries, drops overnight bars and tops
up count/range-capped windows with offset queries (unit-tested).

Add `examples/quote_http_compare`, which calls every pull API over both
transports and diffs the results.
- normalise gateway proto-JSON before protojson: "" on numeric/enum
  fields, "true"/"false" on bool fields, numbers on string fields and
  null elements in scalar lists (descriptor-driven, unit-tested)
- WithTransport validates its value and warns on unknown names; New fails
  fast when no HTTP client is configured instead of panicking later
- refill: once a page yields no usable bar the cursor is inside the
  contiguous overnight block; fetch a full page to jump it (regression
  tests for both directions)
- extract alignCandlesticks/windowTarget as pure functions and unit-test
  the target / capped / early-exit rules
- docs: Profile() semantics, CHANGELOG split (CalcIndex name, top-up
  endpoint, nil-client error), brokers region label
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