zrsclient 0.3.0

Rust SDK for the Zerodha Kite Connect trading API — REST endpoints plus WebSocket market-data streaming, with built-in retry and reconnect.
Documentation

zrsclient

crates.io docs.rs license

Warning: This SDK is in active development. Please do not use it for live trading.

zrsclient is a Rust SDK over the API provided by the Indian stock broker Zerodha. It wraps the Kite Connect REST endpoints and the WebSocket market-data stream, and includes retry/reconnect logic so algorithm writers don't have to hand-handle recoverable failures.

Features

  • REST coverage — orders, GTT (read and write), alerts, portfolio (holdings/positions), margins & charges, mutual funds, market quotes, instruments, and historical data.
  • Builder-based requestsOrderParams (incl. iceberg / auction / TTL / market-protection / auto-slice), GttParams, and AlertParams.
  • Automatic retries on recoverable REST errors (throttling, timeouts) with a caller-supplied retry budget.
  • WebSocket streaming on tokio-tungstenite with automatic reconnect (exponential backoff) and subscription state that survives reconnects.
  • Typed tickson_ticks yields Vec<Tick> (typed Mode, Ohlc, MarketDepth) instead of untyped JSON.

Installation

cargo add zrsclient

Or add it to your Cargo.toml:

[dependencies]
zrsclient = "0.3"

Authentication

zrsclient consumes an existing Kite api_key / access_token (it does not implement the login/session flow). Provide them via a ZrsClientConfig, or place them in ~/.zrsclient/credentials as TOML:

[default]
api_key = "your_api_key"
access_token = "your_access_token"

KiteConnect::new(None) resolves credentials from that file; passing Some(config) supplies them directly.

REST example

use zrsclient::connect::{KiteConnect, OrderParams};
use zrsclient::config::ZrsClientConfig;
use serde_json::Value as JsonValue;

// Supply credentials directly, or pass `None` to read ~/.zrsclient/credentials.
let config = ZrsClientConfig {
    api_key: Some("API_KEY"),
    access_token: Some("ACCESS_TOKEN"),
    base_url: None,
};
let kite = KiteConnect::new(Some(config));

// Retry budget for recoverable errors (0 disables retries).
let retry = 10;

// Read portfolio.
let holdings: JsonValue = kite.holdings(retry).unwrap();

// Place an order with the builder.
let params = OrderParams::new("regular", "NSE", "INFY", "BUY", "LIMIT", 1, "CNC")
    .price(1500.0)
    .validity("DAY");
let placed: JsonValue = kite.place_order(&params, retry).unwrap();

GTT and alerts

use zrsclient::connect::{GttParams, GttOrder, AlertParams};

// A single-leg GTT.
let gtt = GttParams::new("single", "NSE", "INFY", 1500.0)
    .trigger_value(1450.0)
    .order(GttOrder::new("BUY", 1, "LIMIT", "CNC", 1450.0));
kite.place_gtt(&gtt, retry).unwrap();

// A simple price alert.
let alert = AlertParams::simple(
    "NIFTY above 27k", "INDICES", "NIFTY 50", "LastTradedPrice", ">=", 27000.0,
);
kite.create_alert(&alert, retry).unwrap();

WebSocket streaming

use zrsclient::ticker::{KiteTicker, KiteTickerHandler, WebSocketHandler, Tick};

struct CustomHandler {
    tokens: Vec<u32>,
    mode: String,
}

impl KiteTickerHandler for CustomHandler {
    fn on_open<T>(&mut self, ws: &mut WebSocketHandler<T>)
        where T: KiteTickerHandler {
        // Subscribe on connect. This also re-runs after an automatic reconnect.
        ws.subscribe(self.tokens.clone()).unwrap();
        ws.set_mode(&self.mode, self.tokens.clone()).unwrap();
    }

    fn on_ticks<T>(&mut self, _ws: &mut WebSocketHandler<T>, ticks: Vec<Tick>)
        where T: KiteTickerHandler {
        for tick in ticks {
            println!("{} -> {}", tick.instrument_token, tick.last_price);
        }
    }

    fn on_order_update<T>(&mut self, _ws: &mut WebSocketHandler<T>, order: serde_json::Value)
        where T: KiteTickerHandler {
        println!("order update: {:?}", order);
    }
}

fn main() {
    let mut ticker = KiteTicker::new("API_KEY", "ACCESS_TOKEN");
    // Reconnection is on by default; tune it if needed:
    // ticker.set_reconnect(true, 0, 1, 30);

    let handler = CustomHandler { tokens: vec![256265, 260105], mode: "full".to_string() };
    ticker.connect(handler, None).unwrap();

    // The ticker runs on a background thread; keep the process alive.
    loop { std::thread::sleep(std::time::Duration::from_secs(1)); }
}

Testing

Unit tests (mocked HTTP, in-process WebSocket) run offline:

cargo test

Integration tests hit the live Kite API and are #[ignore]d by default. Provide credentials via environment variables to run them:

KITE_API_KEY=xxx KITE_ACCESS_TOKEN=yyy cargo test --test immutable -- --ignored

Real-time market-data endpoints (quote / ohlc / ltp / trigger_range) require a Kite "market data" subscription; without it those calls return PermissionException and the integration tests treat them as reachable-but-gated.

Changelog

See CHANGELOG.md. 0.3.0 is a breaking release — see the migration notes there before upgrading from 0.2.x.

License

Licensed under either of MIT or Apache-2.0 at your option.