#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
mod inner {
use crate::{
Actions, AlgoState, Algo, Fill, L2Book, Level, OnlineFeatures,
Reject,
};
pub fn make_book(bid_px: u64, bid_sz: u64, ask_px: u64, ask_sz: u64) -> L2Book {
let mut book = L2Book::default();
if bid_px > 0 {
book.bids[0] = Level { px_1e9: bid_px, sz_1e8: bid_sz };
book.bid_ct = 1;
}
if ask_px > 0 {
book.asks[0] = Level { px_1e9: ask_px, sz_1e8: ask_sz };
book.ask_ct = 1;
}
book.recv_ns = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
book
}
pub fn make_depth(bids: &[(u64, u64)], asks: &[(u64, u64)]) -> L2Book {
let mut book = L2Book::default();
for (i, &(px, sz)) in bids.iter().enumerate().take(20) {
book.bids[i] = Level { px_1e9: px, sz_1e8: sz };
}
book.bid_ct = bids.len().min(20) as u8;
for (i, &(px, sz)) in asks.iter().enumerate().take(20) {
book.asks[i] = Level { px_1e9: px, sz_1e8: sz };
}
book.ask_ct = asks.len().min(20) as u8;
book.recv_ns = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
book
}
fn now_ns() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64
}
pub fn make_fill(order_id: u64, px_1e9: u64, qty_1e8: i64, side: i8) -> Fill {
Fill {
order_id,
px_1e9,
qty_1e8,
recv_ns: now_ns(),
side,
_pad: [0; 7],
}
}
pub fn make_reject(order_id: u64, code: u8) -> Reject {
Reject {
order_id,
code,
_pad: [0; 7],
}
}
pub struct AlgoHarness<A: Algo> {
pub algo: A,
pub state: AlgoState,
pub book: L2Book,
pub features: OnlineFeatures,
actions: Actions,
}
impl<A: Algo> AlgoHarness<A> {
pub fn new(algo: A) -> Self {
Self {
algo,
state: AlgoState::default(),
book: L2Book::default(),
features: OnlineFeatures::default(),
actions: Actions::new(),
}
}
pub fn set_book(&mut self, book: L2Book) { self.book = book; }
pub fn set_position(&mut self, qty_1e8: i64, avg_entry_1e9: u64) {
self.state.position_1e8 = qty_1e8;
self.state.avg_entry_1e9 = avg_entry_1e9;
}
pub fn tick(&mut self) -> ActionSnapshot {
self.actions.clear();
self.algo.on_book(&self.book, &self.state, &self.features, &mut self.actions);
ActionSnapshot::from_actions(&self.actions)
}
pub fn fill(&mut self, fill: Fill) {
self.algo.on_fill(&fill, &self.state);
}
pub fn reject(&mut self, reject: Reject) {
self.algo.on_reject(&reject);
}
pub fn shutdown(&mut self) -> ActionSnapshot {
self.actions.clear();
self.algo.on_shutdown(&self.state, &mut self.actions);
ActionSnapshot::from_actions(&self.actions)
}
}
use crate::Action;
#[derive(Debug)]
pub struct ActionSnapshot {
actions: [Action; 16],
count: usize,
}
impl ActionSnapshot {
pub(crate) fn from_actions(a: &Actions) -> Self {
let mut snap = Self {
actions: [Action::default(); 16],
count: a.len(),
};
for i in 0..a.len() {
if let Some(action) = a.get(i) {
snap.actions[i] = *action;
}
}
snap
}
pub fn len(&self) -> usize { self.count }
pub fn is_empty(&self) -> bool { self.count == 0 }
pub fn get(&self, idx: usize) -> Option<&Action> {
if idx < self.count { Some(&self.actions[idx]) } else { None }
}
pub fn iter(&self) -> impl Iterator<Item = &Action> {
self.actions[..self.count].iter()
}
pub fn buy_count(&self) -> usize {
self.iter().filter(|a| a.side > 0 && a.is_cancel == 0).count()
}
pub fn sell_count(&self) -> usize {
self.iter().filter(|a| a.side < 0 && a.is_cancel == 0).count()
}
pub fn cancel_count(&self) -> usize {
self.iter().filter(|a| a.is_cancel == 1).count()
}
pub fn assert_count(&self, expected: usize) {
assert_eq!(self.count, expected, "expected {} actions, got {}", expected, self.count);
}
pub fn assert_empty(&self) {
assert!(self.is_empty(), "expected no actions, got {}", self.count);
}
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
pub use inner::*;