1pub mod Status {
9 pub const PENDING: u8 = 0; pub const ACKED: u8 = 1; pub const PARTIAL: u8 = 2; pub const DEAD: u8 = 3; }
14
15#[derive(Debug, Clone, Copy, Default)]
17#[repr(C)]
18pub struct OpenOrder {
19 pub order_id: u64,
20 pub px_1e9: u64,
21 pub qty_1e8: i64, pub filled_1e8: i64, pub side: i8, pub status: u8,
25 pub _pad: [u8; 6],
26}
27
28impl OpenOrder {
29 pub const EMPTY: Self = Self {
30 order_id: 0,
31 px_1e9: 0,
32 qty_1e8: 0,
33 filled_1e8: 0,
34 side: 0,
35 status: 0,
36 _pad: [0; 6],
37 };
38
39 #[inline(always)]
40 pub fn is_live(&self) -> bool {
41 self.status == Status::ACKED || self.status == Status::PARTIAL
42 }
43
44 #[inline(always)]
45 pub fn is_pending(&self) -> bool {
46 self.status == Status::PENDING
47 }
48
49 #[inline(always)]
50 pub fn remaining_1e8(&self) -> i64 {
51 self.qty_1e8.abs() - self.filled_1e8.abs()
52 }
53}
54
55#[derive(Debug, Clone, Copy)]
63#[repr(C)]
64pub struct SymbolMeta {
65 pub tick_size_1e9: u64,
67 pub lot_size_1e8: u64,
69 pub min_qty_1e8: u64,
71 pub min_notional_1e9: u64,
73 pub price_precision: u8,
75 pub qty_precision: u8,
77 pub _pad: [u8; 6],
78}
79
80impl Default for SymbolMeta {
81 fn default() -> Self {
82 Self {
83 tick_size_1e9: 0,
84 lot_size_1e8: 0,
85 min_qty_1e8: 0,
86 min_notional_1e9: 0,
87 price_precision: 0,
88 qty_precision: 0,
89 _pad: [0; 6],
90 }
91 }
92}
93
94impl SymbolMeta {
95 pub const EMPTY: Self = Self {
96 tick_size_1e9: 0,
97 lot_size_1e8: 0,
98 min_qty_1e8: 0,
99 min_notional_1e9: 0,
100 price_precision: 0,
101 qty_precision: 0,
102 _pad: [0; 6],
103 };
104
105 #[inline(always)]
107 pub fn round_px(&self, px_1e9: u64) -> u64 {
108 if self.tick_size_1e9 == 0 {
109 return px_1e9;
110 }
111 (px_1e9 / self.tick_size_1e9) * self.tick_size_1e9
112 }
113
114 #[inline(always)]
116 pub fn round_qty(&self, qty_1e8: i64) -> i64 {
117 if self.lot_size_1e8 == 0 {
118 return qty_1e8;
119 }
120 let lot = self.lot_size_1e8 as i64;
121 (qty_1e8 / lot) * lot
122 }
123
124 #[inline(always)]
126 pub fn check_notional(&self, qty_1e8: i64, px_1e9: u64) -> bool {
127 if self.min_notional_1e9 == 0 {
128 return true;
129 }
130 let notional = (qty_1e8.unsigned_abs() as u128 * px_1e9 as u128 / 100_000_000) as u64;
131 notional >= self.min_notional_1e9
132 }
133
134 #[inline(always)]
136 pub fn check_min_qty(&self, qty_1e8: i64) -> bool {
137 if self.min_qty_1e8 == 0 {
138 return true;
139 }
140 qty_1e8.unsigned_abs() >= self.min_qty_1e8
141 }
142}
143
144#[derive(Debug, Clone, Copy)]
151#[repr(C)]
152pub struct RiskSnapshot {
153 pub max_position_1e8: i64,
155 pub max_daily_loss_1e9: i64,
157 pub max_order_notional_1e9: u64,
159 pub max_order_rate: u32,
161 pub reduce_only: u8,
163 pub _pad: [u8; 3],
164}
165
166impl Default for RiskSnapshot {
167 fn default() -> Self {
168 Self {
169 max_position_1e8: 0,
170 max_daily_loss_1e9: 0,
171 max_order_notional_1e9: 0,
172 max_order_rate: 0,
173 reduce_only: 0,
174 _pad: [0; 3],
175 }
176 }
177}
178
179impl RiskSnapshot {
180 pub const EMPTY: Self = Self {
181 max_position_1e8: 0,
182 max_daily_loss_1e9: 0,
183 max_order_notional_1e9: 0,
184 max_order_rate: 0,
185 reduce_only: 0,
186 _pad: [0; 3],
187 };
188
189 #[inline(always)]
192 pub fn check_position(&self, current_position_1e8: i64, delta_1e8: i64) -> bool {
193 if self.max_position_1e8 == 0 {
194 return true;
195 }
196 let projected = current_position_1e8.saturating_add(delta_1e8);
197 projected.abs() <= self.max_position_1e8
198 }
199}
200
201pub const MAX_ORDERS: usize = 32;
207
208#[derive(Clone, Copy)]
214#[repr(C)]
215pub struct AlgoState {
216 pub position_1e8: i64, pub avg_entry_1e9: u64, pub realized_pnl_1e9: i64, pub unrealized_pnl_1e9: i64, pub orders: [OpenOrder; MAX_ORDERS],
223 pub order_ct: u8,
224 pub _pad: [u8; 7],
225 pub session_pnl_1e9: i64, pub total_fill_count: u64, pub symbol: SymbolMeta, pub risk: RiskSnapshot, pub mesh_id: u64,
236 pub mesh_peer_count: u8,
238 pub _mesh_pad: [u8; 7],
239 pub mesh_peers: [u64; 16],
242 pub symbol_id: u16,
245 pub _sym_pad: [u8; 6],
246 pub symbol_name: [u8; 16],
249}
250
251impl Default for AlgoState {
252 fn default() -> Self {
253 Self {
254 position_1e8: 0,
255 avg_entry_1e9: 0,
256 realized_pnl_1e9: 0,
257 unrealized_pnl_1e9: 0,
258 orders: [OpenOrder::EMPTY; MAX_ORDERS],
259 order_ct: 0,
260 _pad: [0; 7],
261 session_pnl_1e9: 0,
262 total_fill_count: 0,
263 symbol: SymbolMeta::EMPTY,
264 risk: RiskSnapshot::EMPTY,
265 mesh_id: 0,
266 mesh_peer_count: 0,
267 _mesh_pad: [0; 7],
268 mesh_peers: [0; 16],
269 symbol_id: 0,
270 _sym_pad: [0; 6],
271 symbol_name: [0; 16],
272 }
273 }
274}
275
276impl AlgoState {
277 pub fn symbol_name(&self) -> &str {
279 let end = self.symbol_name.iter().position(|&b| b == 0).unwrap_or(16);
280 core::str::from_utf8(&self.symbol_name[..end]).unwrap_or("")
281 }
282}
283
284
285#[derive(Debug, Clone, Copy, Default)]
287pub struct PnlSnapshot {
288 pub realized_1e9: i64,
289 pub unrealized_1e9: i64,
290 pub total_1e9: i64,
291}
292
293impl AlgoState {
294 #[inline(always)]
295 pub fn is_flat(&self) -> bool {
296 self.position_1e8 == 0
297 }
298
299 #[inline(always)]
300 pub fn is_long(&self) -> bool {
301 self.position_1e8 > 0
302 }
303
304 #[inline(always)]
305 pub fn is_short(&self) -> bool {
306 self.position_1e8 < 0
307 }
308
309 #[inline(always)]
310 pub fn has_orders(&self) -> bool {
311 self.order_ct > 0
312 }
313
314 #[inline(always)]
315 pub fn live_order_count(&self) -> usize {
316 let mut ct = 0;
317 for i in 0..self.order_ct as usize {
318 if self.orders[i].is_live() {
319 ct += 1;
320 }
321 }
322 ct
323 }
324
325 #[inline(always)]
326 pub fn find_order(&self, order_id: u64) -> Option<&OpenOrder> {
327 for i in 0..self.order_ct as usize {
328 if self.orders[i].order_id == order_id {
329 return Some(&self.orders[i]);
330 }
331 }
332 None
333 }
334
335 #[inline(always)]
336 pub fn open_buy_qty_1e8(&self) -> i64 {
337 let mut sum = 0i64;
338 for i in 0..self.order_ct as usize {
339 let o = &self.orders[i];
340 if o.is_live() && o.side > 0 {
341 sum += o.remaining_1e8();
342 }
343 }
344 sum
345 }
346
347 #[inline(always)]
348 pub fn open_sell_qty_1e8(&self) -> i64 {
349 let mut sum = 0i64;
350 for i in 0..self.order_ct as usize {
351 let o = &self.orders[i];
352 if o.is_live() && o.side < 0 {
353 sum += o.remaining_1e8();
354 }
355 }
356 sum
357 }
358
359 #[inline(always)]
360 pub fn total_pnl_1e9(&self) -> i64 {
361 self.realized_pnl_1e9 + self.unrealized_pnl_1e9
362 }
363
364 #[inline(always)]
366 pub fn get_pnl(&self) -> PnlSnapshot {
367 PnlSnapshot {
368 realized_1e9: self.realized_pnl_1e9,
369 unrealized_1e9: self.unrealized_pnl_1e9,
370 total_1e9: self.total_pnl_1e9(),
371 }
372 }
373
374 #[inline(always)]
376 pub fn realized_pnl_usd(&self) -> f64 {
377 self.realized_pnl_1e9 as f64 / 1e9
378 }
379
380 #[inline(always)]
382 pub fn unrealized_pnl_usd(&self) -> f64 {
383 self.unrealized_pnl_1e9 as f64 / 1e9
384 }
385
386 #[inline(always)]
388 pub fn total_pnl_usd(&self) -> f64 {
389 self.total_pnl_1e9() as f64 / 1e9
390 }
391
392 #[inline(always)]
394 pub fn session_pnl_usd(&self) -> f64 {
395 self.session_pnl_1e9 as f64 / 1e9
396 }
397}