fyers-rs 0.0.1

Idiomatic async Rust client for the Fyers API v3 broker APIs.
Documentation

fyers-rs

Idiomatic async Rust client for the documented Fyers broker APIs.

Status: pre-release. The market-data WebSocket and several REST endpoints have been live-verified end-to-end against the real Fyers V3 API. Other paths — most importantly order placement, modification, and cancellation — match the official Python SDK byte-for-byte but have never been live-fired. See the verification matrix below.

Safety warning: this project is entirely AI-generated at this stage. It has not been independently audited or battle-tested. Review, test, and verify behavior yourself before using it with real Fyers credentials, market data, or orders. Live tests and examples are opt-in; no example should place an order unless you explicitly change/run it to do so.

⚠️ Order placement has not been tested live

client.orders().place_sync(...), place_async, modify_sync, cancel_sync, the multi/multileg variants, GTT and smart-order endpoints have never sent a real order through this crate. The endpoint URLs and request payload shapes match the official Fyers Python SDK (fyers-apiv3), and the Fyers server returns expected validation errors when probed without algo-trading entitlement — but a live order on a real account has not yet been placed-and-cancelled to confirm round-trip behaviour.

If you intend to use this crate to place real orders, you must verify each path yourself first. Suggested approach:

  1. Place one tiny throwaway limit order (e.g. 1 share at an obviously-non-marketable price) on an algo-trading-enabled account.
  2. Cancel it immediately.
  3. Compare the request body the crate sent against the documented Fyers V3 API spec and the fyers-apiv3 Python SDK source.
  4. Repeat per order type (sync, async, multi, multileg, GTT, smart-order) before relying on each.

Treat every order-mutation call as irreversible broker interaction.

Verification status

Surface Status Notes
Auth flow (login URL → auth code → access/refresh token → refresh) ✅ Live-verified
Profile / funds / holdings (REST) ✅ Live-verified
Data WebSocket — connect, auth handshake, full mode, channel resume, subscribe, snapshot decode, ack flow control ✅ Live-verified end-to-end Real NSE:SBIN-EQ snapshot decoded with correct OHLC/bid/ask/circuit bands.
Data WebSocket — lite mode ✅ Live-verified Single-field LTP frame.
Data WebSocket — update frames (data_type=0x55) during market hours ⏳ Pending market open Snapshot path is byte-validated; updates use the same field layout per Python SDK.
Data WebSocket — index updates (if|...) ⚠️ Synthetic test only Field schema implemented from index_val; never seen a live index frame.
Data WebSocket — depth updates (dp|...) ⚠️ Synthetic test only Field schema implemented from depthvalue; never seen a live depth frame.
Order WebSocket — connect, subscribe, ping/pong ✅ Live-verified Subscribe ack received. No live order events tested.
TBT/depth WebSocket ⚠️ Implementation correct, untested live Subscribe wire format and protobuf decoder match Python SDK. Account lacks TBT entitlement to validate. Dynamic URL fetching from /indus/home/tbtws is not yet implemented.
REST order placement / modification / cancellation Never live-fired URLs match Python SDK; payload shapes have not been round-tripped against a real account.
GTT, smart orders, multi-leg orders ❌ Never live-fired Same as above.
Portfolio operations (exit/convert positions, margin calculators) ❌ Never live-fired
Market data REST (history, quotes, depth, option chain, symbol master) ⚠️ Compiled, fixture-tested Not live-fired in this session.
Postback payloads ⚠️ Models only No live webhook capture.
WebSocket auto-reconnect ❌ Not implemented DataSocketConfig::reconnect is accepted but no internal loop consumes it. See DataSocketConnection rustdoc for the manual reconnect pattern.

Coverage

  • Auth/session: login URL, auth-code validation, refresh token, logout.
  • User and account: profile, funds, holdings.
  • Transactions/reports: trades, orders, positions, order history, trade history.
  • Orders: sync/async single, multi, multi-leg, modify, cancel, GTT, smart orders. (See verification status above — none live-fired.)
  • Portfolio operations: exit/convert positions and margin calculators.
  • Market data REST: market status, history, quotes, depth, option chain, symbol master.
  • EDIS, price alerts, and incoming postback payload models.
  • WebSockets: data, order, and TBT/depth protocol models plus async managers.

The scope boundary is the rendered Fyers V3 docs. SDK-only surfaces not found in the rendered docs are tracked separately and are not implemented as public behavior unless the scope changes.

Install

This crate is not published yet. Use it from a local checkout:

[dependencies]
fyers-rs = { path = "../fyers-rs" }

Feature flags:

Feature Default Purpose
rest yes REST service accessors and typed models.
ws yes WebSocket protocol and manager APIs.
auth yes Auth helpers and token models.
rustls-tls yes Rustls TLS for HTTP/WebSocket clients.
native-tls no Native TLS alternative.
tracing yes Tracing integration points.
live-tests no Ignored live integration tests requiring credentials.

Quick start

use fyers_rs::FyersClient;

# fn main() -> Result<(), fyers_rs::FyersError> {
let client = FyersClient::builder()
    .client_id("APPID-100")
    .access_token("ACCESS_TOKEN")
    .build()?;

let _orders = client.orders();
let _market_data = client.market_data();
let _data_socket = client.data_socket();
# Ok(())
# }

REST calls and WebSocket managers are async:

use fyers_rs::FyersClient;
use fyers_rs::models::market_data::QuotesRequest;

# async fn run() -> Result<(), fyers_rs::FyersError> {
let client = FyersClient::builder()
    .client_id(std::env::var("FYERS_CLIENT_ID").unwrap())
    .access_token(std::env::var("FYERS_ACCESS_TOKEN").unwrap())
    .build()?;

let quotes = client
    .market_data()
    .quotes(&QuotesRequest {
        symbols: vec!["NSE:SBIN-EQ".to_owned()],
    })
    .await?;

println!("{quotes:?}");
# Ok(())
# }

Auth flow

  1. Build the login URL with auth.generate_auth_code_url(...).
  2. Redirect the user to Fyers and capture the returned auth code.
  3. Validate the auth code with auth.validate_auth_code(...).
  4. Store access/refresh tokens securely outside this crate.
  5. Refresh tokens with auth.refresh_access_token(...) when needed.

See the examples/auth_* files for compile-checked auth flow snippets.

REST example

use fyers_rs::FyersClient;
use fyers_rs::models::orders::OrderBookQuery;

# async fn run() -> Result<(), fyers_rs::FyersError> {
let client = FyersClient::builder()
    .client_id(std::env::var("FYERS_CLIENT_ID").unwrap())
    .access_token(std::env::var("FYERS_ACCESS_TOKEN").unwrap())
    .build()?;

let orders = client.orders().list(&OrderBookQuery::default()).await?;
println!("{orders:?}");
# Ok(())
# }

Order-placement examples in this repository construct request bodies but do not place live orders by default. No order-placement path in this crate has been live-fired against a real Fyers account. Treat any live order call as irreversible broker interaction; verify the wire format yourself before relying on it (see the order-placement warning above).

WebSocket example

use fyers_rs::FyersClient;
use fyers_rs::models::ws::{DataSubscribeRequest, DataSubscriptionKind};

# async fn run() -> Result<(), fyers_rs::FyersError> {
let client = FyersClient::builder()
    .client_id(std::env::var("FYERS_CLIENT_ID").unwrap())
    .access_token(std::env::var("FYERS_ACCESS_TOKEN").unwrap())
    .build()?;

let mut socket = client.data_socket().connect().await?;
socket
    .subscribe(&DataSubscribeRequest {
        symbols: vec!["NSE:SBIN-EQ".to_owned()],
        data_type: DataSubscriptionKind::SymbolUpdate,
    })
    .await?;

if let Some(event) = socket.next_event().await? {
    println!("{event:?}");
}

socket.close().await?;
# Ok(())
# }

See the WebSocket examples for data and order socket usage patterns.

Development

Enter the Nix shell:

nix develop

Run the full local validation:

cargo test --all-features
cargo clippy --all-targets --all-features -- -D warnings
cargo doc --all-features --no-deps
cargo fmt --all -- --check

In this repository, use the Nix/OpenSSL wrapper if your host cannot find libssl.so.3:

nix develop -c bash -lc 'export LD_LIBRARY_PATH="$(pkg-config --variable=libdir openssl):$LD_LIBRARY_PATH"; cargo test --all-features'

Live tests

Live tests are ignored by default and gated behind the live-tests feature, so normal cargo test does not hit the network.

cargo test --features live-tests --test live_rest -- --ignored --nocapture
cargo test --features live-tests --test live_ws -- --ignored --nocapture

Tests never place, modify, or cancel live orders. Order-mutation examples remain outside the test suite and require explicit opt-in flags.

Scope rule

This crate implements the documented broker API only. It does not include strategy engines, backtesting, persistence, dashboards, multi-broker abstractions, or order-management behavior beyond documented Fyers API calls.