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](https://img.shields.io/crates/v/zrsclient.svg)](https://crates.io/crates/zrsclient)
[![docs.rs](https://docs.rs/zrsclient/badge.svg)](https://docs.rs/zrsclient)
[![license](https://img.shields.io/crates/l/zrsclient.svg)](#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](https://kite.trade/docs/connect/v3/). 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 requests**`OrderParams` (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 ticks**`on_ticks` yields `Vec<Tick>` (typed `Mode`, `Ohlc`,
  `MarketDepth`) instead of untyped JSON.

## Installation

```sh
cargo add zrsclient
```

Or add it to your `Cargo.toml`:

```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:

```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

```rust
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

```rust
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

```rust
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:

```sh
cargo test
```

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

```sh
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](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](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE) at
your option.