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 tick.set_instrument_token(input);
73 if let Some(bs) = input.get(4..8) {
74 tick.mode = Mode::LTP;
75 tick.last_price = price(bs, &tick.exchange);
76 }
77
78 let is_index = !tick.exchange.is_tradable();
79 tick.is_index = is_index;
80 tick.is_tradable = !is_index;
81
82 if is_index {
84 if let Some(bs) = input.get(8..28) {
85 tick.mode = Mode::Quote;
86 tick.ohlc = OHLC::from_index(&bs[0..16], &tick.exchange);
88 tick.net_change = price(&bs[16..20], &tick.exchange);
90 }
91 } else if let Some(bs) = input.get(8..44) {
92 tick.mode = Mode::Quote;
93 tick.last_traded_qty = value(&bs[0..4]);
95 tick.avg_traded_price = price(&bs[4..8], &tick.exchange);
97 tick.volume_traded = value(&bs[8..12]);
99 tick.total_buy_qty = value(&bs[12..16]);
101 tick.total_sell_qty = value(&bs[16..20]);
103 tick.ohlc = OHLC::from(&bs[20..36], &tick.exchange);
105 }
106
107 if is_index {
109 if let Some(bs) = input.get(28..32) {
110 tick.mode = Mode::Full;
111 tick.exchange_timestamp =
113 value(bs).map(|x| Duration::from_secs(x.into()));
114 }
115 } else if let Some(bs) = input.get(44..184) {
116 tick.mode = Mode::Full;
117 tick.set_change();
118
119 tick.last_traded_timestamp =
121 value(&bs[0..4]).map(|x| Duration::from_secs(x.into()));
122
123 tick.oi = value(&bs[4..8]);
125 tick.oi_day_high = value(&bs[8..12]);
127 tick.oi_day_low = value(&bs[12..16]);
129 tick.exchange_timestamp =
131 value(&bs[16..20]).map(|x| Duration::from_secs(x.into()));
132 tick.depth = Depth::from(&bs[20..140], &tick.exchange);
134 }
135
136 tick
137 }
138}
139
140impl TryFrom<&[u8]> for Tick {
141 type Error = ParseTickError;
142 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
143 match value.len() {
144 8 | 28 | 32 | 44 | 184 => Ok(Tick::from_bytes(value)),
145 len => Err(ParseTickError(format!("invalid tick size: {}", len))),
146 }
147 }
148}