This repository contains a library-first Rust client for SSI FastConnect Data. The workspace
separates the primary library at crates/fc-data/ from the thin optional binary at
crates/fc-data-cli/.
The Rust client supports:
- typed configuration and secret handling;
- typed requests and capture-backed responses for all eight REST APIs documented by SSI v2.2;
- typed compatibility for the official .NET client's
IntradaybyTickREST operation; - an additional raw
BackTestrequest retained for compatibility; - typed channels and payloads for
F,X-QUOTE,X-TRADE,R,MI, andBstreams; - raw JSON and raw channel escape hatches for forward compatibility;
- bounded and persistent realtime subscriptions through
StreamClient, with opt-in reconnect and resubscribe support; - an optional JSON CLI in its own workspace crate.
crates/
├── fc-data/ # package: ssi-fc-data, public library
└── fc-data-cli/ # package: fc-data-cli, binary: fc-data
Build the library independently of the companion binary:
cargo build -p ssi-fc-data --libExecute a typed securities request:
use ssi_fc_data::{
api::{MarketDataClient, PageQuery, SecuritiesMarket, SecuritiesQuery, SecuritiesResponse},
config::Settings,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = MarketDataClient::new(Settings::load()?)?;
let page = PageQuery::new(1, 10)?;
let query = SecuritiesQuery::new(Some(SecuritiesMarket::Hose), page)?;
let response: SecuritiesResponse = client.execute_typed(&query).await?;
serde_json::to_writer_pretty(std::io::stdout(), &response)?;
Ok(())
}Every concrete typed REST request implements RestRequest, which fixes its response payload at
compile time. MarketDataClient::execute and ApiRequest remain available when a caller needs
the untyped SSI JSON envelope.
For realtime data, construct a Channel from a validated ChannelSelector, then use
collect_typed, subscribe_typed, recv_typed, and switch_typed. Unknown stream data types
are preserved by StreamMessage::Unknown instead of being discarded. Raw strings and JSON
remain available through the explicitly named raw methods.
Run a live typed quote decode with:
cargo run -p ssi-fc-data --example typed_streamRun a persistent same-session switch with the raw compatibility example:
cargo run -p ssi-fc-data --example live_switch -- MI:VN30 X-QUOTE:SSIRun an opt-in resilient typed subscription that restores its last channel after transport loss:
cargo run -p ssi-fc-data --example resilient_streamLibrary request structs have private fields. Use their new or parse functions so invalid
endpoint-specific page sizes, exact DD/MM/YYYY dates, date ranges, required symbols, market
codes, exchange codes, order values, and resolutions are rejected before authentication or
network I/O. Intraday dates are independently optional and are omitted from the query when not
provided.
Copy .env.example to the ignored root .env file and populate the SSI credentials.
Required runtime variables:
SSI_FCDATA_CONSUMER_ID=
SSI_FCDATA_CONSUMER_SECRET=
SSI_FCDATA_API_URL=https://fc-data.ssi.com.vn/
SSI_FCDATA_STREAM_URL=https://fc-datahub.ssi.com.vn/The Bitwarden PublicKey and PrivateKey fields are also retained locally as
SSI_FCDATA_PUBLIC_KEY and SSI_FCDATA_PRIVATE_KEY, but FCData market queries do not use them.
Never commit .env or print its values.
cargo build -p ssi-fc-data --lib --release
cargo build -p fc-data-cli --bin fc-data --release
cargo nextest run --workspace --all-targets --all-features
cargo clippy --workspace --all-targets --all-features -- -D warningsThe binary lives entirely under crates/fc-data-cli/; no CLI types are exported by the library.
Show its complete command surface:
cargo run -p fc-data-cli --bin fc-data -- --helpVerify credentials without exposing the eight-hour bearer token:
cargo run -p fc-data-cli --bin fc-data -- authQuery securities:
cargo run -p fc-data-cli --bin fc-data -- \
securities --market HOSE --page-index 1 --page-size 10Query historical data:
cargo run -p fc-data-cli --bin fc-data -- daily-ohlc \
--symbol SSI \
--from-date 13/08/2026 \
--to-date 14/08/2026 \
--page-size 10Query the official .NET client's intraday-by-tick operation:
cargo run -p fc-data-cli --bin fc-data -- intraday-by-tick \
--symbol SSI \
--from-date 14/08/2026 \
--to-date 14/08/2026 \
--page-size 10The official .NET v2.0.0 source exposes this operation, but SSI's production endpoint currently
returns HTTP 404 for its declared api/v2/Market/IntradaybyTick path.
Query the SSI BackTest endpoint:
cargo run -p fc-data-cli --bin fc-data -- backtest \
--selected-date 14/08/2026 \
--symbol SSIThe request model is retained for API parity, although the live SSI endpoint currently replies
with "Not support".
Collect one realtime quote broadcast, bounded to 20 seconds:
cargo run -p fc-data-cli --bin fc-data -- stream \
--channel X-QUOTE:ALL \
--max-messages 1 \
--timeout-seconds 20Available REST subcommands:
securitiessecurities-detailsindex-componentsindex-listdaily-ohlcintraday-ohlcintraday-by-tickdaily-indexdaily-stock-pricebacktest
Dates use SSI's exact DD/MM/YYYY format. Pagination and endpoint-specific request constraints
are validated before any network request.
SSI streaming uses the SignalR 1.3 /negotiate -> WebSocket /connect -> HTTP /start sequence,
not the ASP.NET Core SignalR handshake implemented by most modern SignalR crates. The Rust
client therefore uses reqwest and tokio-tungstenite directly with hub
fcmarketdatav2hub and method SwitchChannels. Existing subscriptions do not silently
reconnect. Callers can opt into ResilientSubscription, whose default policy matches the
official .NET client by retrying once after three seconds and restoring the latest channel.
Public enums are #[non_exhaustive]; downstream matches must include a wildcard arm so SSI
protocol and validation cases can evolve without a breaking release.
Licensed under the MIT License.