A Rust wrapper for the OpenAlgo API with WebSocket support for real-time market data streaming.
Add this to your Cargo.toml:
[dependencies]
openalgo = "1.0.5"
tokio = { version = "1", features = ["full"] }Or install using cargo:
cargo add openalgo tokio --features tokio/fulluse openalgo::OpenAlgo;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Simple initialization with just API key
let client = OpenAlgo::new("your_api_key");
// Get quotes
let quotes = client.quotes("RELIANCE", "NSE").await?;
println!("{:?}", quotes);
// Place a simple market order
let order = client.place_order("Strategy1", "RELIANCE", "BUY", "NSE", "MARKET", "MIS", "1").await?;
println!("{:?}", order);
Ok(())
}// Simple initialization (uses default host and WebSocket URL)
let client = OpenAlgo::new("your_api_key");
// Custom configuration
let client = OpenAlgo::with_config(
"your_api_key",
"http://127.0.0.1:5000", // Host
"v1", // API Version
"ws://127.0.0.1:8765", // WebSocket URL
);Place a simple market order. disclosed_quantity is optional (Option<&str>).
let order = client.place_order(
"Strategy1", // strategy
"RELIANCE", // symbol
"BUY", // action (BUY/SELL)
"NSE", // exchange (NSE/BSE/NFO/MCX/CDS/BFO)
"MARKET", // pricetype (MARKET/LIMIT/SL/SL-M)
"MIS", // product (CNC/NRML/MIS)
"1", // quantity
None, // disclosed_quantity: Option<&str>
).await?;Response:
{
"status": "success",
"orderid": "1234567890"
}Place a limit order with price. disclosed_quantity is optional (Option<&str>).
let order = client.place_limit_order(
"Strategy1", // strategy
"RELIANCE", // symbol
"BUY", // action
"NSE", // exchange
"MIS", // product
"1", // quantity
"2500.00", // price
None, // disclosed_quantity: Option<&str>
).await?;Place a stop-loss order with trigger price. disclosed_quantity is optional (Option<&str>).
let order = client.place_sl_order(
"Strategy1", // strategy
"RELIANCE", // symbol
"BUY", // action
"NSE", // exchange
"MIS", // product
"1", // quantity
"2500.00", // price
"2490.00", // trigger_price
None, // disclosed_quantity: Option<&str>
).await?;Place an order with position sizing logic.
let order = client.place_smart_order(
"Strategy1", // strategy
"RELIANCE", // symbol
"BUY", // action
"NSE", // exchange
"MARKET", // pricetype
"MIS", // product
"1", // quantity
"5", // position_size
).await?;Place an options order with automatic strike selection.
Note: there is no splitsize parameter on this endpoint (an earlier version of
this SDK mistakenly required one — the real OpenAlgo optionsorder endpoint has
no such field). expiry_date is optional (resolvable when underlying already
embeds an expiry, e.g. "NIFTY28OCT25FUT"), and strike_int is an optional,
deprecated parameter kept only for parity with the Python SDK. Use
OrderAPI::split_order separately if you need order splitting.
use std::collections::HashMap;
let order = client.options_order(
"Strategy1", // strategy
"NIFTY", // underlying
"NFO", // exchange
"0", // offset (0=ATM, 1=OTM1, -1=ITM1)
"CE", // option_type (CE/PE)
"BUY", // action
"50", // quantity
"MARKET", // pricetype
"MIS", // product
Some("241226"), // expiry_date (YYMMDD), optional
None, // strike_int (deprecated), optional
None, // extra: Option<HashMap<String, serde_json::Value>> forwarded verbatim
).await?;
// LIMIT order using `extra` to forward broker-specific / order-specific fields
// (mirrors Python's **kwargs: price, trigger_price, disclosed_quantity, ...)
let mut extra = HashMap::new();
extra.insert("price".to_string(), serde_json::json!("50.0"));
let limit_order = client.options_order(
"Strategy1", "NIFTY", "NFO", "0", "CE", "BUY", "50", "LIMIT", "MIS",
Some("241226"), None, Some(extra),
).await?;Response:
{
"status": "success",
"orderid": "1234567890",
"symbol": "NIFTY24DEC24000CE",
"exchange": "NFO",
"offset": "0",
"option_type": "CE",
"underlying": "NIFTY",
"underlying_ltp": 24000.50,
"mode": "live"
}Place multi-leg options orders (spreads, straddles, etc.).
use openalgo::OptionsLeg;
// Bull Call Spread
let legs = vec![
OptionsLeg::new("0", "CE", "BUY", "50"), // Buy ATM Call
OptionsLeg::new("2", "CE", "SELL", "50"), // Sell OTM Call
];
let order = client.options_multi_order(
"Strategy1", // strategy
"NIFTY", // underlying
"NFO", // exchange
"241226", // expiry_date
legs,
).await?;Response:
{
"status": "success",
"underlying": "NIFTY",
"underlying_ltp": 24000.50,
"results": [
{"leg": 1, "status": "success", "orderid": "1234567890", "symbol": "NIFTY24DEC24000CE"},
{"leg": 2, "status": "success", "orderid": "1234567891", "symbol": "NIFTY24DEC24100CE"}
]
}Place multiple orders at once.
use openalgo::BasketOrderItem;
let orders = vec![
BasketOrderItem::new("RELIANCE", "NSE", "BUY", 1, "MARKET", "MIS"),
BasketOrderItem::new("TCS", "NSE", "BUY", 1, "MARKET", "MIS"),
];
let result = client.basket_order("Strategy1", orders).await?;Response:
{
"status": "success",
"results": [
{"symbol": "RELIANCE", "status": "success", "orderid": "1234567890"},
{"symbol": "TCS", "status": "success", "orderid": "1234567891"}
]
}Split a large order into smaller chunks.
let result = client.split_order(
"Strategy1", // strategy
"RELIANCE", // symbol
"BUY", // action
"NSE", // exchange
100, // total quantity (i32)
25, // splitsize (i32)
"MARKET", // pricetype
"MIS", // product
).await?;Modify an existing order. disclosed_quantity, trigger_price, and extra
(broker-specific kwargs forwarded verbatim) are optional.
let result = client.modify_order(
"1234567890", // orderid
"Strategy1", // strategy
"RELIANCE", // symbol
"BUY", // action
"NSE", // exchange
"LIMIT", // pricetype
"MIS", // product
"1", // quantity
"2550.00", // price
None, // disclosed_quantity: Option<&str>
None, // trigger_price: Option<&str>
None, // extra: Option<HashMap<String, serde_json::Value>>
).await?;
// With disclosed quantity and trigger price
let result = client.modify_order(
"1234567890", "Strategy1", "RELIANCE", "BUY", "NSE", "SL", "MIS", "1", "2550.00",
Some("200"), Some("2545.00"), None,
).await?;Cancel a specific order.
let result = client.cancel_order("1234567890", "Strategy1").await?;Cancel all open orders for a strategy.
let result = client.cancel_all_order("Strategy1").await?;Close all positions for a strategy.
let result = client.close_position("Strategy1").await?;Get the status of an order.
let status = client.order_status("1234567890", "Strategy1").await?;Get current open position for a symbol.
let position = client.open_position("Strategy1", "RELIANCE", "NSE", "MIS").await?;Get real-time quotes for a symbol.
let quotes = client.quotes("RELIANCE", "NSE").await?;Response:
{
"status": "success",
"data": {
"ltp": 2500.50,
"open": 2480.00,
"high": 2510.00,
"low": 2475.00,
"prev_close": 2485.00,
"volume": 1234567,
"bid": 2500.00,
"ask": 2500.50,
"oi": 0
}
}Get quotes for multiple symbols.
let quotes = client.multi_quotes(&[
("RELIANCE", "NSE"),
("TCS", "NSE"),
("INFY", "NSE"),
]).await?;Get order book depth.
let depth = client.depth("RELIANCE", "NSE").await?;Get historical OHLCV data.
// Simple form - latest data
let history = client.history("RELIANCE", "NSE", "5m").await?;
// With date range
let history = client.history_range("RELIANCE", "NSE", "5m", "2024-01-01", "2024-01-31").await?;Get available intervals.
let intervals = client.intervals().await?;
// Legacy alias (mirrors Python's `interval()`)
let intervals = client.interval().await?;Get symbol information.
let info = client.symbol("RELIANCE", "NSE").await?;Search for symbols. exchange is optional — pass None to search across all exchanges.
let results = client.search("RELI", Some("NSE"), None).await?;
// Search across all exchanges
let results = client.search("RELI", None, None).await?;Get expiry dates.
let expiries = client.expiry("NIFTY", "NFO", "OPT").await?;Get option chain data.
let chain = client.option_chain("NIFTY", "NFO", "241226").await?;Get option symbol details by underlying + offset, without placing an order.
expiry_date is optional (resolvable when underlying already embeds an
expiry, e.g. "NIFTY28OCT25FUT"); strategy and strike_int are optional,
deprecated parameters kept only for parity with the Python SDK.
let symbol = client.option_symbol(
"NIFTY", "NFO", "0", "CE",
Some("241226"), // expiry_date, optional
None, // strategy (deprecated), optional
None, // strike_int (deprecated), optional
None, // extra kwargs, optional
).await?;Get synthetic future price.
let future = client.synthetic_future("NIFTY", "NFO", "241226").await?;Get option Greeks (Delta, Gamma, Theta, Vega, Rho) and implied volatility.
Only symbol/exchange are required — everything else is optional and
auto-detected or defaulted server-side (interest rate defaults to 0; the
underlying is auto-detected; live prices are fetched unless forward_price
is supplied).
let greeks = client.option_greeks(
"NIFTY24DEC24000CE",
"NFO",
Some(6.5), // interest_rate, optional
None, // forward_price, optional (skips underlying price fetch)
Some("NIFTY"), // underlying_symbol, optional (auto-detected otherwise)
Some("NSE"), // underlying_exchange, optional (auto-detected otherwise)
None, // expiry_time, optional (e.g. "19:00" for MCX)
None, // extra kwargs, optional
).await?;
// Simplest form — everything auto-detected
let greeks = client.option_greeks("NIFTY24DEC24000CE", "NFO", None, None, None, None, None, None).await?;Download instrument master data, with optional exchange filtering. Pass
exchange: None to download instruments for all supported exchanges
(NSE, BSE, NFO, BFO, MCX, CDS, BCD, NSE_INDEX, BSE_INDEX) combined into one
response, mirroring Python's instruments(exchange=None).
let instruments = client.instruments(Some("NSE")).await?;
// Download all exchanges
let all_instruments = client.instruments(None).await?;Get account funds.
let funds = client.funds().await?;Response:
{
"status": "success",
"data": {
"availablecash": "100000.00",
"collateral": "50000.00",
"m2mrealized": "1000.00",
"m2munrealized": "-500.00",
"utiliseddebits": "25000.00"
}
}Get all orders.
let orderbook = client.orderbook().await?;Get all trades.
let tradebook = client.tradebook().await?;Get all positions.
let positions = client.positionbook().await?;Get holdings.
let holdings = client.holdings().await?;Get margin requirement for positions.
use openalgo::MarginPosition;
let positions = vec![
MarginPosition::new("NIFTY24DEC24000CE", "NFO", "BUY", "MIS", "MARKET", "50"),
];
let margin = client.margin(positions).await?;Get market holidays. year is optional (2020-2050) — pass None to let the
server default to the current year.
let holidays = client.holidays(Some(2024)).await?;
// Current year (server default)
let holidays = client.holidays(None).await?;Get exchange timings for a date. date is optional — pass None to default
client-side to today's date (YYYY-MM-DD, Asia/Kolkata), matching Python's
datetime.now() default.
let timings = client.timings(Some("2024-12-25")).await?;
// Today's timings
let timings = client.timings(None).await?;Send a Telegram message.
let result = client.telegram("username", "Hello from OpenAlgo!").await?;
// Custom priority (1-10)
let result = client.telegram_priority("username", "Urgent alert!", 10).await?;Send WhatsApp notifications through the OpenAlgo paired device. Requires the
device to already be paired from the OpenAlgo web UI's /whatsapp page —
pairing itself is intentionally not exposed via the API.
The simplest form sends a plain-text message to the paired device itself (self).
let result = client.whatsapp_message("Build #482 deployed. P&L: +1.2%").await?;
// To a single phone number (E.164 digits)
let result = client.whatsapp_to("919876543210", "Stop-loss hit on BANKNIFTY!").await?;Response:
{
"status": "success",
"message": "Delivered to 1, failed 0",
"data": { "sent": ["<self>"], "failed": [], "skipped": 0 }
}For anything beyond a single self/phone text message — broadcasts (up to 5
recipients), username-based recipients, image/document attachments, or
wait_for_delivery=false — use client.whatsapp.whatsapp(recipient, options) directly.
use openalgo::whatsapp::{WhatsAppRecipient, WhatsAppOptions};
// Small broadcast (max 5 numbers; anything beyond is dropped server-side)
let result = client.whatsapp.whatsapp(
WhatsAppRecipient::Phones(vec!["919876543210".to_string(), "919812345678".to_string()]),
WhatsAppOptions {
message: Some("Server maintenance in 10 minutes".to_string()),
..Default::default()
},
).await?;
// Send to a linked OpenAlgo username, with an image attachment
let result = client.whatsapp.whatsapp(
WhatsAppRecipient::Username("alice".to_string()),
WhatsAppOptions {
message: Some("NIFTY end-of-day chart".to_string()),
image: Some("/srv/charts/nifty_eod.png".to_string()),
..Default::default()
},
).await?;
// Fire-and-forget (skip the delivery report) for time-critical alerts
let result = client.whatsapp.whatsapp(
WhatsAppRecipient::SelfDevice,
WhatsAppOptions { message: Some("Stop-loss hit!".to_string()), wait_for_delivery: Some(false), ..Default::default() },
).await?;Get analyzer status.
let status = client.analyzer_status().await?;Toggle analyzer mode.
let result = client.analyzer_toggle(true).await?;Strategy is a standalone TradingView-style webhook poster — unlike every
other API in this SDK, it is not part of OpenAlgo. It has no API key;
instead it POSTs directly to a strategy's webhook URL
({host_url}/strategy/webhook/{webhook_id}). The strategy mode (LONG_ONLY,
SHORT_ONLY, BOTH) is configured on the OpenAlgo server, not in the SDK call.
use openalgo::Strategy;
let strategy = Strategy::new("http://127.0.0.1:5000", "your-webhook-id");
// Simple signal
let result = strategy.strategy_order("RELIANCE", "BUY", None).await?;
// With an explicit position size (required for BOTH mode)
let result = strategy.strategy_order("NIFTY", "SELL", Some(50)).await?;use openalgo::{OpenAlgo, WsInstrument};
use openalgo::websocket::{WsSubscriber, WsData};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = OpenAlgo::new("your_api_key");
let ws = client.websocket();
// Connect
let (cmd_tx, mut data_rx) = ws.connect().await?;
let subscriber = WsSubscriber::new(cmd_tx);
// Define instruments
let instruments = vec![
WsInstrument::new("NSE", "RELIANCE"),
WsInstrument::new("NSE", "TCS"),
];
// Subscribe to LTP
subscriber.subscribe_ltp(instruments.clone()).await?;
// Receive data
while let Some(data) = data_rx.recv().await {
match data {
WsData::Ltp(ltp) => {
println!("LTP: {} - {}",
ltp.symbol.unwrap_or_default(),
ltp.ltp.unwrap_or_default()
);
}
WsData::Quote(quote) => {
println!("Quote: {} - LTP: {}, High: {}, Low: {}",
quote.symbol.unwrap_or_default(),
quote.ltp.unwrap_or_default(),
quote.high.unwrap_or_default(),
quote.low.unwrap_or_default()
);
}
WsData::Depth(depth) => {
println!("Depth: {} - Bids: {:?}",
depth.symbol.unwrap_or_default(),
depth.bids
);
}
_ => {}
}
}
Ok(())
}- LTP Mode: Last traded price only
- Quote Mode: OHLC + Volume data
- Depth Mode: Full order book depth
// Subscribe to different modes
subscriber.subscribe_ltp(instruments.clone()).await?;
subscriber.subscribe_quote(instruments.clone()).await?;
subscriber.subscribe_depth(instruments.clone()).await?;
// Unsubscribe
subscriber.unsubscribe_ltp(instruments.clone()).await?;
// Disconnect
subscriber.disconnect().await?;Alongside the channel-based data_rx stream, OpenAlgoWebSocket keeps a local
snapshot cache (keyed by "EXCHANGE:SYMBOL") that is updated as market_data
messages arrive, mirroring Python FeedAPI's get_ltp() / get_quotes() /
get_depth(). Call these any time on the same ws instance you connected
with — no need to consume data_rx yourself just to read the latest values.
Both exchange and symbol filters are optional; omit either (or both) to
get everything cached so far.
// After ws.connect() and subscribing...
// Nested JSON: {"ltp": {"NSE": {"RELIANCE": {"timestamp": ..., "ltp": ...}}}}
let ltp_snapshot = ws.get_ltp(None, None);
let ltp_reliance = ws.get_ltp(Some("NSE"), Some("RELIANCE"));
// Nested JSON: {"quote": {"NSE": {"RELIANCE": {"open", "high", "low", "close", "ltp", "volume", ...}}}}
let quotes_snapshot = ws.get_quotes(Some("NSE"), None);
// Nested JSON: {"depth": {"NSE": {"RELIANCE": {"timestamp", "ltp", "buyBook": {"1": {...}, ..., "5": {...}}, "sellBook": {...}}}}}
let depth_snapshot = ws.get_depth(Some("NSE"), Some("RELIANCE"));# Set your API key
export OPENALGO_API_KEY=your_api_key
# Run examples
cargo run --example place_order
cargo run --example options_order
cargo run --example quotes
cargo run --example account
cargo run --example websocketuse openalgo::client::OpenAlgoError;
match client.quotes("RELIANCE", "NSE").await {
Ok(result) => println!("Success: {:?}", result),
Err(OpenAlgoError::RequestError(e)) => println!("HTTP Error: {}", e),
Err(OpenAlgoError::ApiError(msg)) => println!("API Error: {}", msg),
Err(OpenAlgoError::JsonError(e)) => println!("JSON Error: {}", e),
Err(e) => println!("Other Error: {}", e),
}MIT License