fugle-marketdata
Rust SDK for Fugle market data. Provides REST API and WebSocket clients for Taiwan stock and futures/options market data.
Features
- REST Client: Synchronous HTTP client for market data queries
- Stock intraday data (quote, ticker, candles, trades, volumes)
- FutOpt (futures/options) intraday data
- WebSocket Client: real-time streaming, sync by default, optional
tokio variant under
aio::behind thetokio-compfeature- Stock channels: trades, candles, books, aggregates, indices
- FutOpt channels: trades, candles, books, aggregates
- Automatic reconnection with exponential backoff
- Health check monitoring
- Authentication: API key, bearer token, or SDK token
- Optional
tokio-compfeature — opt in for the async client. Sync consumers pay zero tokio in theirCargo.lock.
Installation
# sync (no tokio in the dependency tree)
= "0.7"
# async (tokio + tokio-tungstenite)
= { = "0.7", = ["tokio-comp"] }
# opt in to structured tracing (no-op when off; zero binary cost)
= { = "0.7", = ["tokio-comp", "tracing"] }
# opt in to the `metrics` crate integration for Prometheus / OTLP / statsd
# exporters. Registers `fugle_marketdata_ws_messages_dropped_total` +
# `fugle_marketdata_ws_events_dropped_total` counters automatically.
= { = "0.7", = ["tokio-comp", "metrics"] }
# in-process WebSocket mock server (`testing::MockWsServer`) for tests
= { = "0.7", = ["test-utils"] }
Upgrading from 0.6? See MIGRATION-0.7.md. Zero
breaking changes; four opt-in additions (metrics feature, multi-client
mock, transport-drop intent injection, REST/WebSocket dual-host docs)
plus doc/hygiene improvements (WebSocketErrorKind::Http mapping table,
tracing_compat internal-macro doc, cargo public-api snapshot).
Upgrading from 0.3? See MIGRATION-0.4.md. The
notable defaults that changed: ReconnectionConfig::default().enabled is
now true, disconnect() is now a graceful 5 s drain (not
fire-and-forget), message_buffer default is 4096, Auth /
ConnectionConfig Debug redact secrets, and SubscribeRequest::trades
/ ::candles / ::books / ::aggregates were removed in favour of
SubscribeRequest::new(Channel::*, symbol).
Quick Start
REST API
use ;
#
WebSocket Streaming (sync, default)
use ;
use Duration;
#
WebSocket Streaming (async, features = ["tokio-comp"])
Same API surface — replace WebSocketClient with aio::WebSocketClient
and add .await:
use WebSocketClient;
use ;
# async
The async client lives under fugle_marketdata::aio:: and is only available
when the tokio-comp feature is enabled. Upgrading from 0.2: see
MIGRATION-0.3.md.
Authentication
use ;
// REST authentication
let _ = ApiKey;
let _ = BearerToken;
let _ = SdkToken;
// WebSocket authentication
let _ = with_api_key;
let _ = with_token;
let _ = with_sdk_token;
Configuration
Reconnection
ReconnectionConfig::default().enabled is true as of 0.4. Rust
callers on the WebSocketClient::new(config) happy path get
auto-reconnect with no opt-in; bindings (Python / Node / UniFFI / Go /
Java / C++ / C#) call ReconnectionConfig::disabled() at the FFI
boundary so end-user behaviour is preserved.
use ReconnectionConfig;
use Duration;
#
Health Check
The SDK uses passive activity detection at the WebSocket read site — no
background task, no protocol-level pings. The dispatch loop wraps each
ws_read.next() in tokio::time::timeout(heartbeat_timeout, ...) and emits
ConnectionEvent::HeartbeatTimeout when the timer fires, which then triggers
the auto-reconnect path.
use HealthCheckConfig;
use Duration;
#
Full Configuration
For complete control over connection, reconnection, and health-check
parameters, use WebSocketClient::with_full_config:
use ;
use Duration;
#
Custom endpoints (staging / proxy / mock server)
Both transports accept a custom URL.
REST — chainable setter:
use ;
let client = new
.base_url;
# drop;
WebSocket — WebSocketFactory mirrors the JS / Python SDK shape:
supply one base URL, get both stock and futopt endpoints.
use ;
#
For full control, ConnectionConfig::new(url, auth) accepts the
fully-qualified WebSocket URL. The urls module exposes the canonical
constants and roots:
use urls;
let _ = STOCK_WS; // wss://api.fugle.tw/marketdata/v1.0/stock/streaming
let _ = WS_BASE_ROOT; // wss://api.fugle.tw/marketdata
let _ = API_VERSION; // v1.0
REST retry
use ;
let client = new
.with_retry; // 3 attempts, 100 ms initial, 2 s ceiling
// or build your own
use Duration;
let client = new
.with_retry;
# drop;
Off by default — observability use cases need real failures visible.
Retries only errors classified by MarketDataError::is_retryable()
(HTTP 429, HTTP 5xx, transport timeouts, connection errors). Exhausted
retries return the last error verbatim.
Graceful shutdown
WebSocketClient::disconnect() (both sync and async) defaults to a
5 second drain timeout: it signals the dispatch loop to stop, drops
the writer-side sender so any queued frames flush, sends the WebSocket
Close frame, awaits the peer's Close acknowledgement, and only then
forcibly aborts the background tasks. Pass an explicit budget for
SIGTERM-style scenarios:
use WebSocketClient;
use Duration;
#
Duration::ZERO is valid — same fire-and-forget shape as 0.3.
Tracing
Opt in via the tracing feature. Hot-path debug! for received frames,
lifecycle info! / warn! for connect / auth / reconnect /
heartbeat / saturation, error! for runtime-init / close-frame
failures. #[tracing::instrument] spans on
ws.connect / ws.subscribe / ws.unsubscribe / ws.disconnect
(cold path only — zero overhead per frame).
Backpressure & introspection
use ;
#
Default message-channel cap is 4096 (drop-newest backpressure). Tune
with ConnectionConfig::builder(...).message_buffer(N).
Error Handling
All operations return Result<T, MarketDataError>:
use ;
#
Error codes (for FFI consumers):
| Code | Variant |
|---|---|
| 1001 | InvalidSymbol |
| 1002 | DeserializationError |
| 1003 | RuntimeError |
| 1004 | ConfigError |
| 2001 | ConnectionError |
| 2002 | AuthError |
| 2003 | ApiError |
| 2010 | ClientClosed |
| 3001 | TimeoutError |
| 3002 | WebSocketError |
| 9999 | Other |
API Reference
See the API documentation on docs.rs for the full type catalogue.
REST Endpoints
Stock intraday — client.stock().intraday():
.quote()— real-time quote.ticker()— symbol information.candles()— OHLCV candles.trades()— trade history.volumes()— volume by price
FutOpt intraday — client.futopt().intraday():
.quote(),.ticker(),.tickers(),.candles(),.trades(),.volumes(),.products()
WebSocket Channels
| Channel | Description |
|---|---|
Trades |
Real-time trade executions |
Candles |
Real-time candlestick updates |
Books |
Order book (5 levels bid/ask) |
Aggregates |
Aggregated market data |
Indices |
Index values (stock only) |
Architecture
fugle-marketdata is a thin facade over the fugle-marketdata-core kernel crate. The kernel is the same library used by the Python, Node.js, and other language bindings of the official Fugle SDK. End users should depend on fugle-marketdata; advanced users who need direct kernel access can depend on fugle-marketdata-core.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.