kiteticker_async_manager/models/
tick.rs1use std::time::Duration;
2
3use crate::{
4 errors::ParseTickError,
5 parser::{price, value},
6 Depth, Exchange, Mode, OHLC,
7};
8
9#[derive(Debug, Clone, Default, PartialEq)]
10pub struct Tick {
14 pub mode: Mode,
15 pub instrument_token: u32,
16 pub exchange: Exchange,
17 pub is_tradable: bool,
18 pub is_index: bool,
19
20 pub last_traded_qty: Option<u32>,
21 pub avg_traded_price: Option<f64>,
22 pub last_price: Option<f64>,
23 pub volume_traded: Option<u32>,
24 pub total_buy_qty: Option<u32>,
25 pub total_sell_qty: Option<u32>,
26 pub ohlc: Option<OHLC>,
27
28 pub last_traded_timestamp: Option<Duration>,
29 pub oi: Option<u32>,
30 pub oi_day_high: Option<u32>,
31 pub oi_day_low: Option<u32>,
32 pub exchange_timestamp: Option<Duration>,
33
34 pub net_change: Option<f64>,
35 pub depth: Option<Depth>,
36}
37
38impl Tick {
39 fn set_instrument_token(&mut self, input: &[u8]) -> &mut Self {
40 self.instrument_token = value(&input[0..=3]).unwrap();
41 self.exchange = ((self.instrument_token & 0xFF) as usize).into();
42 self
43 }
44
45 fn set_change(&mut self) -> &mut Self {
46 self.net_change = self
47 .ohlc
48 .as_ref()
49 .map(|o| o.close)
50 .map(|close_price| {
51 if let Some(last_price) = self.last_price {
52 if close_price == 0_f64 {
53 return None;
54 } else {
55 Some(last_price - close_price)
57 }
58 } else {
59 None
60 }
61 })
62 .unwrap_or_default();
63 self
64 }
65}
66
67impl Tick {
68 pub(crate) fn from_bytes(input: &[u8]) -> Self {
69 let mut tick = Tick::default();
70
71 let parse_ltp = |t: &mut Tick, i: &[u8]| {
72 t.set_instrument_token(i);
74 if let Some(bs) = i.get(4..8) {
76 t.mode = Mode::LTP;
77 t.last_price = price(bs, &t.exchange);
78 }
79 };
80
81 let parse_quote = |t: &mut Tick, i: &[u8], is_index: bool| {
82 if is_index {
83 if let Some(bs) = i.get(8..28) {
84 t.mode = Mode::Quote;
85 t.ohlc = OHLC::from(&bs[0..16], &t.exchange);
87 t.set_change();
90 }
91 } else {
92 if let Some(bs) = i.get(8..44) {
93 t.mode = Mode::Quote;
94 t.last_traded_qty = value(&bs[0..4]);
96 t.avg_traded_price = price(&bs[4..8], &t.exchange);
98 t.volume_traded = value(&bs[8..12]);
100 t.total_buy_qty = value(&bs[12..16]);
102 t.total_sell_qty = value(&bs[16..20]);
104 t.ohlc = OHLC::from(&bs[20..36], &t.exchange);
106 }
107 }
108 };
109
110 let parse_full = |t: &mut Tick, i: &[u8], is_index: bool| {
111 if is_index {
112 if let Some(bs) = i.get(28..32) {
113 t.mode = Mode::Full;
114 t.exchange_timestamp =
116 value(bs).map(|x| Duration::from_secs(x.into()));
117 }
118 } else {
119 if let Some(bs) = i.get(44..184) {
120 t.mode = Mode::Full;
121 t.set_change();
122
123 t.last_traded_timestamp =
125 value(&bs[0..4]).map(|x| Duration::from_secs(x.into()));
126
127 t.oi = value(&bs[4..8]);
129 t.oi_day_high = value(&bs[8..12]);
131 t.oi_day_low = value(&bs[12..16]);
133 t.exchange_timestamp =
135 value(&bs[16..20]).map(|x| Duration::from_secs(x.into()));
136 t.depth = Depth::from(&bs[20..140], &t.exchange);
138 }
139 }
140 };
141
142 parse_ltp(&mut tick, input);
143 if !tick.exchange.is_tradable() {
144 tick.is_index = true;
145 tick.is_tradable = false;
146
147 parse_quote(&mut tick, input, true);
148 parse_full(&mut tick, input, true);
149 } else {
150 tick.is_index = false;
151 tick.is_tradable = true;
152
153 parse_quote(&mut tick, input, false);
154 parse_full(&mut tick, input, false);
155 }
156
157 tick
158 }
159}
160
161impl TryFrom<&[u8]> for Tick {
162 type Error = ParseTickError;
163 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
164 crate::parser::parse_tick(value)
165 }
166}