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