kiteticker_async_manager/lib.rs
1#![allow(
2 clippy::cognitive_complexity,
3 clippy::large_enum_variant,
4 clippy::needless_doctest_main
5)]
6#![warn(missing_debug_implementations, rust_2018_idioms, unreachable_pub)]
7#![doc(test(
8 no_crate_inject,
9 attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
10))]
11
12//! # KiteTicker Async Manager
13//!
14//! High-performance async WebSocket client for the [Kite Connect API](https://kite.trade/docs/connect/v3/websocket/#websocket-streaming)
15//! with multi-connection support and dynamic subscription management.
16//!
17//! ## Features
18//!
19//! - **๐ Multi-Connection Support** - Utilize all 3 allowed WebSocket connections (9,000 symbol capacity)
20//! - **โก High Performance** - Dedicated parser tasks, optimized buffers, sub-microsecond latency
21//! - **๐ Dynamic Subscriptions** - Add/remove symbols at runtime without reconnection
22//! - **๐ Load Balancing** - Automatic symbol distribution across connections
23//! - **๐ช Production Ready** - Comprehensive error handling, health monitoring, reconnection
24//! - **๐ง Async-First Design** - Built with Tokio, follows Rust async best practices
25//!
26//! ## Quick Start
27//!
28//! ### Multi-Connection Manager (Recommended)
29//!
30//! Using the builder:
31//!
32//! ```rust,no_run
33//! use kiteticker_async_manager::{KiteTickerManagerBuilder, Mode};
34//! #[tokio::main]
35//! async fn main() -> Result<(), String> {
36//! let api_key = std::env::var("KITE_API_KEY").unwrap();
37//! let access_token = std::env::var("KITE_ACCESS_TOKEN").unwrap();
38//! let mut manager = KiteTickerManagerBuilder::new(api_key, access_token)
39//! .max_connections(3)
40//! .max_symbols_per_connection(3000)
41//! .raw_only(true) // receive only raw frames if desired
42//! .default_mode(Mode::Quote)
43//! .enable_dedicated_parsers(true)
44//! .build();
45//! manager.start().await?;
46//! Ok(())
47//! }
48//! ```
49//!
50//! ```rust,no_run
51//! use kiteticker_async_manager::{KiteTickerManager, KiteManagerConfig, Mode, TickerMessage};
52//!
53//! #[tokio::main]
54//! async fn main() -> Result<(), String> {
55//! // Setup credentials
56//! let api_key = std::env::var("KITE_API_KEY").unwrap();
57//! let access_token = std::env::var("KITE_ACCESS_TOKEN").unwrap();
58//!
59//! // Create high-performance manager
60//! let config = KiteManagerConfig {
61//! max_connections: 3,
62//! max_symbols_per_connection: 3000,
63//! enable_dedicated_parsers: true,
64//! default_mode: Mode::LTP,
65//! ..Default::default()
66//! };
67//!
68//! // Start manager
69//! let mut manager = KiteTickerManager::new(api_key, access_token, config);
70//! manager.start().await?;
71//!
72//! // Subscribe to symbols (automatically distributed)
73//! let symbols = vec![256265, 408065, 738561]; // NIFTY 50, HDFC Bank, Reliance
74//! manager.subscribe_symbols(&symbols, Some(Mode::Quote)).await?;
75//!
76//! // Process data from independent channels
77//! let channels = manager.get_all_channels();
78//! for (channel_id, mut receiver) in channels {
79//! tokio::spawn(async move {
80//! while let Ok(message) = receiver.recv().await {
81//! if let TickerMessage::Ticks(ticks) = message {
82//! for tick in ticks {
83//! println!("Channel {:?}: {} @ โน{:.2}",
84//! channel_id,
85//! tick.instrument_token,
86//! tick.content.last_price.unwrap_or(0.0));
87//! }
88//! }
89//! }
90//! });
91//! }
92//!
93//! // Dynamic operations
94//! manager.subscribe_symbols(&[5633, 884737], Some(Mode::Full)).await?; // Add
95//! manager.unsubscribe_symbols(&[408065]).await?; // Remove
96//! manager.change_mode(&[256265], Mode::Full).await?; // Change mode
97//!
98//! Ok(())
99//! }
100//! ```
101//!
102//! ### Single Connection Usage
103//!
104//! ```rust,no_run
105//! use kiteticker_async_manager::{KiteTickerAsync, Mode, TickerMessage};
106//!
107//! #[tokio::main]
108//! async fn main() -> Result<(), String> {
109//! let api_key = std::env::var("KITE_API_KEY").unwrap();
110//! let access_token = std::env::var("KITE_ACCESS_TOKEN").unwrap();
111//!
112//! // Connect to WebSocket
113//! let mut ticker = KiteTickerAsync::connect(&api_key, &access_token).await?;
114//!
115//! // Subscribe to symbols
116//! let symbols = vec![256265, 408065]; // NIFTY 50, HDFC Bank
117//! let mut subscriber = ticker.subscribe(&symbols, Some(Mode::LTP)).await?;
118//!
119//! // Receive data
120//! while let Ok(Some(message)) = subscriber.next_message().await {
121//! if let TickerMessage::Ticks(ticks) = message {
122//! for tick in ticks {
123//! println!("Symbol {}: โน{:.2}",
124//! tick.instrument_token,
125//! tick.content.last_price.unwrap_or(0.0));
126//! }
127//! }
128//! }
129//!
130//! Ok(())
131//! }
132//! ```
133//!
134//! ## Performance Comparison
135//!
136//! | Feature | Single Connection | Multi-Connection Manager | Improvement |
137//! |---------|------------------|---------------------------|-------------|
138//! | **Max Symbols** | 3,000 | 9,000 | **3x capacity** |
139//! | **Throughput** | Limited by 1 connection | 3 parallel connections | **3x throughput** |
140//! | **Latency** | ~5-10ยตs | ~1-2ยตs | **5x faster** |
141//! | **Resilience** | Single point of failure | 3 independent connections | **High availability** |
142//! | **Dynamic Ops** | Manual reconnection | Runtime add/remove | **Zero downtime** |
143//!
144//! ## Architecture
145//!
146//! The library provides two main components:
147//!
148//! ### 1. `KiteTickerAsync` - Single WebSocket Connection
149//! - Direct WebSocket client for simple use cases
150//! - Up to 3,000 symbols per connection
151//! - Manual connection management
152//!
153//! ### 2. `KiteTickerManager` - Multi-Connection Manager (Recommended)
154//! - Manages up to 3 WebSocket connections automatically
155//! - Supports up to 9,000 symbols total
156//! - Dynamic subscription management
157//! - Load balancing and health monitoring
158//! - High-performance optimizations
159//!
160//! ## Subscription Modes
161//!
162//! The library supports three subscription modes:
163//!
164//! - **`Mode::LTP`** - Last traded price only (minimal bandwidth)
165//! - **`Mode::Quote`** - Price + volume + OHLC (standard data)
166//! - **`Mode::Full`** - Complete market depth (maximum data)
167//!
168//! ## Zero-copy raw access (advanced)
169//!
170//! For maximum throughput and minimal allocations, you can work directly with the raw
171//! WebSocket frame bytes and view individual packets using zero-copy, endian-safe types.
172//! This is fully safe and avoids undefined behavior by using `zerocopy::Ref` and
173//! big-endian field wrappers.
174//!
175//! Key points:
176//! - Subscribe to raw frames via `KiteTickerAsync::subscribe_raw_frames()` or
177//! `KiteTickerManager::get_raw_frame_channel(ChannelId)` / `get_all_raw_frame_channels()`,
178//! which yield `bytes::Bytes` frames.
179//! - Extract packet bodies (length-prefixed) from a frame and select the size you need.
180//! - Use helpers like `as_tick_raw`, `as_index_quote_32`, and `as_inst_header_64` to obtain
181//! `zerocopy::Ref<&[u8], T>` that dereferences to a typed view.
182//! - The `Ref` is valid as long as the backing bytes live; examples store `Bytes` to keep it alive.
183//!
184//! Example (snippets):
185//! ```rust,no_run
186//! use kiteticker_async_manager::{KiteTickerAsync, Mode, as_tick_raw};
187//! use bytes::Bytes;
188//!
189//! # #[tokio::main]
190//! # async fn main() -> Result<(), String> {
191//! let api_key = std::env::var("KITE_API_KEY").unwrap();
192//! let access_token = std::env::var("KITE_ACCESS_TOKEN").unwrap();
193//! let mut ticker = KiteTickerAsync::connect_with_options(&api_key, &access_token, true).await?;
194//! let _sub = ticker.subscribe(&[256265], Some(Mode::Full)).await?;
195//! let mut frames = ticker.subscribe_raw_frames();
196//!
197//! // Receive a frame and pull out a 184-byte Full packet body
198//! let frame: Bytes = frames.recv().await.unwrap();
199//! let num = u16::from_be_bytes([frame[0], frame[1]]) as usize;
200//! let mut off = 2usize;
201//! for _ in 0..num {
202//! let len = u16::from_be_bytes([frame[off], frame[off+1]]) as usize;
203//! let body = frame.slice(off+2..off+2+len);
204//! if len == 184 {
205//! if let Some(view_ref) = as_tick_raw(&body) {
206//! let tick = &*view_ref; // &TickRaw
207//! let token = tick.header.instrument_token.get();
208//! let ltp_scaled = tick.header.last_price.get();
209//! // ... use fields ...
210//! }
211//! }
212//! off += 2 + len;
213//! }
214//! # Ok(()) }
215//! ```
216//!
217//! Manager-level example (per-connection frames):
218//! ```rust,no_run
219//! use kiteticker_async_manager::{KiteTickerManagerBuilder, Mode, as_tick_raw};
220//! # #[tokio::main]
221//! # async fn main() -> Result<(), String> {
222//! let api_key = std::env::var("KITE_API_KEY").unwrap();
223//! let access_token = std::env::var("KITE_ACCESS_TOKEN").unwrap();
224//! let mut mgr = KiteTickerManagerBuilder::new(api_key, access_token)
225//! .raw_only(true)
226//! .build();
227//! mgr.start().await?;
228//! mgr.subscribe_symbols(&[256265], Some(Mode::Full)).await?;
229//! for (id, mut rx) in mgr.get_all_raw_frame_channels() {
230//! tokio::spawn(async move {
231//! while let Ok(frame) = rx.recv().await {
232//! if frame.len() < 2 { continue; }
233//! let mut off = 2usize;
234//! let num = u16::from_be_bytes([frame[0], frame[1]]) as usize;
235//! for _ in 0..num {
236//! if off + 2 > frame.len() { break; }
237//! let len = u16::from_be_bytes([frame[off], frame[off+1]]) as usize;
238//! let body = frame.slice(off+2..off+2+len);
239//! if len == 184 {
240//! if let Some(view) = as_tick_raw(&body) {
241//! let token = view.header.instrument_token.get();
242//! let _ = (id, token);
243//! }
244//! }
245//! off += 2 + len;
246//! }
247//! }
248//! });
249//! }
250//! # Ok(()) }
251//! ```
252//!
253//! Safety model: all raw structs derive `Unaligned` and use `big_endian` wrappers for integer fields.
254//! `as_*` helpers return `Option<zerocopy::Ref<&[u8], T>>` which validates size and alignment. No `unsafe` is required.
255//!
256//! ## Examples
257//!
258//! See the [examples directory](https://github.com/SPRAGE/kiteticker-async-manager/tree/main/examples) for:
259//!
260//! - **Basic Examples** - Simple usage patterns
261//! - **Advanced Examples** - Complex multi-connection scenarios
262//! - **Performance Examples** - Optimization and benchmarking
263//!
264//! ## Documentation
265//!
266//! - [Getting Started Guide](https://github.com/SPRAGE/kiteticker-async-manager/blob/main/docs/guides/getting-started.md)
267//! - [API Reference](https://github.com/SPRAGE/kiteticker-async-manager/tree/main/docs/api)
268//! - [Dynamic Subscriptions](https://github.com/SPRAGE/kiteticker-async-manager/blob/main/docs/guides/DYNAMIC_SUBSCRIPTION_GUIDE.md)
269//! - [Performance Guide](https://github.com/SPRAGE/kiteticker-async-manager/blob/main/docs/guides/PERFORMANCE_IMPROVEMENTS.md)
270mod errors;
271pub mod manager;
272mod models;
273mod parser;
274pub use errors::ParseTickError;
275pub use models::tick_raw::{
276 as_184 as tick_as_184, as_index_quote_32, as_inst_header_64, as_tick_raw,
277 DepthItemRaw, DepthRaw, IndexQuoteRaw32, InstHeaderRaw64, TickHeaderRaw,
278 TickRaw, INDEX_QUOTE_SIZE, INST_HEADER_SIZE, TICK_FULL_SIZE,
279};
280pub use models::{
281 Depth, DepthItem, Exchange, Mode, Order, OrderStatus, OrderTransactionType,
282 OrderValidity, Request, TextMessage, Tick, TickMessage, TickerMessage, OHLC,
283};
284
285pub mod ticker;
286pub use manager::{
287 ChannelId, HealthSummary, KiteManagerConfig, KiteTickerManager,
288 KiteTickerManagerBuilder, ManagerStats,
289};
290pub use ticker::{KiteTickerAsync, KiteTickerSubscriber};
291// Expose the raw 184-byte subscriber helper
292pub use ticker::KiteTickerRawSubscriber184;