kiteticker_async_manager/models/
tick_raw.rs

1//! Zero-copy raw views for Kite tick packets.
2//!
3//! This module provides endian-safe, zero-allocation views over WebSocket packet bodies
4//! without copying, built on `zerocopy::Ref` and big-endian integer wrappers.
5//! All structs derive `Unaligned`, ensuring that references are valid even when the
6//! underlying buffer is not naturally aligned.
7//!
8//! Highlights:
9//! - `TickRaw` — 184-byte Full quote (header + 10-depth)
10//! - `IndexQuoteRaw32` — 32-byte index quote snapshot
11//! - `InstHeaderRaw64` — 64-byte instrument header (no depth)
12//! - `as_*` helpers return `Option<zerocopy::Ref<&[u8], T>>` after validating slice size
13//!
14//! Example:
15//! ```rust
16//! # use kiteticker_async_manager::as_tick_raw;
17//! # let bytes = [0u8; 184];
18//! if let Some(view_ref) = as_tick_raw(&bytes) {
19//!   let v = &*view_ref; // &TickRaw
20//!   let token = v.header.instrument_token.get();
21//!   let ltp = v.header.last_price.get();
22//!   let b0_qty = v.depth.buy[0].qty.get();
23//!   let b0_price = v.depth.buy[0].price.get();
24//!   let _ = (token, ltp, b0_qty, b0_price);
25//! }
26//! ```
27
28use zerocopy::big_endian::{I32 as BeI32, U16 as BeU16, U32 as BeU32};
29use zerocopy::{FromBytes, Immutable, KnownLayout, Ref, Unaligned};
30
31/// Size of a full quote packet body used by parser Tick (not including the 2-byte length prefix).
32/// Our raw view targets the 184-byte payload region per packet for Mode::Full on equities.
33pub const TICK_FULL_SIZE: usize = 184;
34
35/// Size of index quote packet body (common snapshot when market closed)
36pub const INDEX_QUOTE_SIZE: usize = 32;
37/// Size of instrument header (non-index) without depth
38pub const INST_HEADER_SIZE: usize = 64;
39
40/// First 64 bytes of Full payload contain header/meta before market depth.
41#[repr(C)]
42#[derive(
43  Clone, Copy, Debug, Default, Unaligned, KnownLayout, Immutable, FromBytes,
44)]
45pub struct TickHeaderRaw {
46  pub instrument_token: BeU32, // 0..4
47  pub last_price: BeI32,       // 4..8 (scaled by exchange divisor)
48  pub last_traded_qty: BeU32,  // 8..12
49  pub avg_traded_price: BeI32, // 12..16
50  pub volume_traded: BeU32,    // 16..20
51  pub total_buy_qty: BeU32,    // 20..24
52  pub total_sell_qty: BeU32,   // 24..28
53  pub ohlc_be: [u8; 16], // 28..44 (open high low close - order depends on index/equity)
54  pub last_traded_ts: BeU32, // 44..48 secs
55  pub oi: BeU32,         // 48..52
56  pub oi_day_high: BeU32, // 52..56
57  pub oi_day_low: BeU32, // 56..60
58  pub exchange_ts: BeU32, // 60..64 secs
59}
60
61/// A single depth entry: qty(u32), price_be(`[u8; 4]` i32), orders(u16), pad(u16)
62#[repr(C)]
63#[derive(
64  Clone, Copy, Debug, Default, Unaligned, KnownLayout, Immutable, FromBytes,
65)]
66pub struct DepthItemRaw {
67  pub qty: BeU32,
68  pub price: BeI32,
69  pub orders: BeU16,
70  pub _pad: BeU16, // protocol packs 12 bytes per entry; we keep struct at 12 bytes
71}
72
73/// 5 buy + 5 sell entries = 120 bytes
74#[repr(C)]
75#[derive(
76  Clone, Copy, Debug, Default, Unaligned, KnownLayout, Immutable, FromBytes,
77)]
78pub struct DepthRaw {
79  pub buy: [DepthItemRaw; 5],
80  pub sell: [DepthItemRaw; 5],
81}
82
83/// Complete 184-byte Full packet body
84#[repr(C)]
85#[derive(
86  Clone, Copy, Debug, Default, Unaligned, KnownLayout, Immutable, FromBytes,
87)]
88pub struct TickRaw {
89  pub header: TickHeaderRaw, // 64 bytes
90  pub depth: DepthRaw,       // 120 bytes
91}
92
93// No inherent methods needed; prefer free functions that return zerocopy::Ref
94
95/// Try get a fixed array reference of 184 bytes from a slice (for APIs that prefer arrays)
96#[inline]
97pub fn as_184(slice: &[u8]) -> Option<&[u8; TICK_FULL_SIZE]> {
98  <&[u8; TICK_FULL_SIZE]>::try_from(slice).ok()
99}
100
101/// Try view as `TickRaw` from a slice (zero-copy, unaligned-safe).
102///
103/// Returns `None` if the slice is not exactly 184 bytes.
104/// The resulting `Ref` dereferences to `&TickRaw` and is valid as long as the input slice lives.
105#[inline]
106pub fn as_tick_raw(slice: &[u8]) -> Option<Ref<&[u8], TickRaw>> {
107  Ref::<_, TickRaw>::from_bytes(slice).ok()
108}
109
110/// 32-byte Index Quote packet (token + LTP + HLOC + price_change + exch ts)
111#[repr(C)]
112#[derive(
113  Clone, Copy, Debug, Default, Unaligned, KnownLayout, Immutable, FromBytes,
114)]
115pub struct IndexQuoteRaw32 {
116  pub token: BeU32,        // 0..4
117  pub ltp: BeI32,          // 4..8
118  pub high: BeI32,         // 8..12
119  pub low: BeI32,          // 12..16
120  pub open: BeI32,         // 16..20
121  pub close: BeI32,        // 20..24
122  pub price_change: BeI32, // 24..28
123  pub exch_ts: BeU32,      // 28..32
124}
125
126#[inline]
127/// Try view as `IndexQuoteRaw32` from a 32-byte slice.
128/// Returns `None` if the length is not 32 bytes.
129pub fn as_index_quote_32(slice: &[u8]) -> Option<Ref<&[u8], IndexQuoteRaw32>> {
130  Ref::<_, IndexQuoteRaw32>::from_bytes(slice).ok()
131}
132
133/// 64-byte instrument header (equity/derivative) without depth
134#[repr(C)]
135#[derive(
136  Clone, Copy, Debug, Default, Unaligned, KnownLayout, Immutable, FromBytes,
137)]
138pub struct InstHeaderRaw64 {
139  pub instrument_token: BeU32, // 0..4
140  pub ltp: BeI32,              // 4..8
141  pub ltq: BeU32,              // 8..12 (qty)
142  pub atp: BeI32,              // 12..16
143  pub vol: BeU32,              // 16..20
144  pub tbq: BeU32,              // 20..24
145  pub tsq: BeU32,              // 24..28
146  pub open: BeI32,             // 28..32
147  pub high: BeI32,             // 32..36
148  pub low: BeI32,              // 36..40
149  pub close: BeI32,            // 40..44
150  pub last_traded_ts: BeU32,   // 44..48
151  pub oi: BeU32,               // 48..52
152  pub oi_day_high: BeU32,      // 52..56
153  pub oi_day_low: BeU32,       // 56..60
154  pub exch_ts: BeU32,          // 60..64
155}
156
157#[inline]
158/// Try view as `InstHeaderRaw64` from a 64-byte slice.
159/// Returns `None` if the length is not 64 bytes.
160pub fn as_inst_header_64(slice: &[u8]) -> Option<Ref<&[u8], InstHeaderRaw64>> {
161  Ref::<_, InstHeaderRaw64>::from_bytes(slice).ok()
162}