Skip to content

Commit

Permalink
refactor: refactor the trivial details.
Browse files Browse the repository at this point in the history
  • Loading branch information
nkaz001 committed Aug 3, 2024
1 parent 7da8d85 commit ef078c0
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 90 deletions.
7 changes: 2 additions & 5 deletions hftbacktest/src/backtest/models/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ pub trait L3QueueModel {
/// Exchanges may have different matching algorithms, such as Pro-Rata, and may have exotic order
/// types that aren't executed in a FIFO manner. Therefore, you should carefully choose the queue
/// model, even when dealing with a Level 3 Market-By-Order feed.
#[derive(Default)]
pub struct L3FIFOQueueModel {
// Stores the location of the queue that holds the order by (side, price in ticks).
pub orders: HashMap<L3OrderId, (Side, i64)>,
Expand All @@ -399,11 +400,7 @@ pub struct L3FIFOQueueModel {
impl L3FIFOQueueModel {
/// Constructs an instance of `L3FIFOQueueModel`.
pub fn new() -> Self {
Self {
orders: Default::default(),
bid_queue: Default::default(),
ask_queue: Default::default(),
}
Default::default()
}
}

Expand Down
2 changes: 1 addition & 1 deletion hftbacktest/src/backtest/proc/l3_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
use crate::{
backtest::{
assettype::AssetType,
data::reader::{Data, Reader},
data::{Data, Reader},
models::LatencyModel,
order::OrderBus,
proc::traits::{LocalProcessor, Processor},
Expand Down
2 changes: 1 addition & 1 deletion hftbacktest/src/backtest/proc/l3_nopartialfillexchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::mem;
use crate::{
backtest::{
assettype::AssetType,
data::reader::{Data, Reader},
data::{Data, Reader},
models::{L3FIFOQueueModel, L3OrderId, L3OrderSource, L3QueueModel, LatencyModel},
order::OrderBus,
proc::traits::Processor,
Expand Down
44 changes: 10 additions & 34 deletions hftbacktest/src/depth/btreemarketdepth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ impl MarketDepth for BTreeMarketDepth {
}
}

impl ApplySnapshot<Event> for BTreeMarketDepth {
impl ApplySnapshot for BTreeMarketDepth {
fn apply_snapshot(&mut self, data: &Data<Event>) {
self.bid_depth.clear();
self.ask_depth.clear();
Expand All @@ -213,31 +213,7 @@ impl ApplySnapshot<Event> for BTreeMarketDepth {
}

fn snapshot(&self) -> Vec<Event> {
let mut events = Vec::new();

// for (&px_tick, &qty) in self.bid_depth.iter().rev() {
// events.push(Event {
// ev: EXCH_EVENT | LOCAL_EVENT | BUY | DEPTH_SNAPSHOT_EVENT,
// // todo: it's not a problem now, but it would be better to have valid timestamps.
// exch_ts: 0,
// local_ts: 0,
// px: px_tick as f64 * self.tick_size,
// qty,
// });
// }
//
// for (&px_tick, &qty) in self.ask_depth.iter() {
// events.push(Event {
// ev: EXCH_EVENT | LOCAL_EVENT | SELL | DEPTH_SNAPSHOT_EVENT,
// // todo: it's not a problem now, but it would be better to have valid timestamps.
// exch_ts: 0,
// local_ts: 0,
// px: px_tick as f64 * self.tick_size,
// qty,
// });
// }

events
todo!()
}
}

Expand Down Expand Up @@ -557,10 +533,10 @@ mod tests {
let lot_size = 0.001;
let mut depth = BTreeMarketDepth::new(0.1, lot_size);

let (prev_best, best) = depth.add_buy_order(1, 500.1, 0.001, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(2, 500.3, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(3, 500.1, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(4, 500.5, 0.005, 0).unwrap();
depth.add_buy_order(1, 500.1, 0.001, 0).unwrap();
depth.add_buy_order(2, 500.3, 0.005, 0).unwrap();
depth.add_buy_order(3, 500.1, 0.005, 0).unwrap();
depth.add_buy_order(4, 500.5, 0.005, 0).unwrap();

assert!(depth.modify_order(10, 500.5, 0.001, 0).is_err());

Expand Down Expand Up @@ -601,10 +577,10 @@ mod tests {
let lot_size = 0.001;
let mut depth = BTreeMarketDepth::new(0.1, lot_size);

let (prev_best, best) = depth.add_sell_order(1, 500.1, 0.001, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(2, 499.3, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(3, 500.1, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(4, 498.5, 0.005, 0).unwrap();
depth.add_sell_order(1, 500.1, 0.001, 0).unwrap();
depth.add_sell_order(2, 499.3, 0.005, 0).unwrap();
depth.add_sell_order(3, 500.1, 0.005, 0).unwrap();
depth.add_sell_order(4, 498.5, 0.005, 0).unwrap();

assert!(depth.modify_order(10, 500.5, 0.001, 0).is_err());

Expand Down
12 changes: 6 additions & 6 deletions hftbacktest/src/depth/fuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::{hash_map::Entry, HashMap};

use super::{ApplySnapshot, L1MarketDepth, L3Order, MarketDepth, INVALID_MAX, INVALID_MIN};
use crate::{
backtest::{data::reader::Data, BacktestError},
backtest::{data::Data, BacktestError},
prelude::{L2MarketDepth, Side, DEPTH_SNAPSHOT_EVENT, EXCH_EVENT, LOCAL_EVENT},
types::{Event, OrderId, BUY_EVENT, SELL_EVENT},
};
Expand Down Expand Up @@ -54,7 +54,7 @@ fn depth_below(depth: &HashMap<i64, QtyTimestamp>, start: i64, end: i64) -> i64
return t;
}
}
return INVALID_MIN;
INVALID_MIN
}

#[inline(always)]
Expand All @@ -64,7 +64,7 @@ fn depth_above(depth: &HashMap<i64, QtyTimestamp>, start: i64, end: i64) -> i64
return t;
}
}
return INVALID_MAX;
INVALID_MAX
}

impl FusedHashMapMarketDepth {
Expand Down Expand Up @@ -247,9 +247,9 @@ impl L2MarketDepth for FusedHashMapMarketDepth {
)
}

fn clear_depth(&mut self, side: i64, clear_upto_price: f64) {
fn clear_depth(&mut self, side: Side, clear_upto_price: f64) {
let clear_upto = (clear_upto_price / self.tick_size).round() as i64;
if side == BUY_EVENT {
if side == Side::Buy {
if self.best_bid_tick != INVALID_MIN {
for t in clear_upto..(self.best_bid_tick + 1) {
if self.bid_depth.contains_key(&t) {
Expand All @@ -261,7 +261,7 @@ impl L2MarketDepth for FusedHashMapMarketDepth {
if self.best_bid_tick == INVALID_MIN {
self.low_bid_tick = INVALID_MAX;
}
} else if side == SELL_EVENT {
} else if side == Side::Sell {
if self.best_ask_tick != INVALID_MAX {
for t in self.best_ask_tick..(clear_upto + 1) {
if self.ask_depth.contains_key(&t) {
Expand Down
18 changes: 9 additions & 9 deletions hftbacktest/src/depth/hashmapmarketdepth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ impl MarketDepth for HashMapMarketDepth {
}
}

impl ApplySnapshot<Event> for HashMapMarketDepth {
impl ApplySnapshot for HashMapMarketDepth {
fn apply_snapshot(&mut self, data: &Data<Event>) {
self.best_bid_tick = INVALID_MIN;
self.best_ask_tick = INVALID_MAX;
Expand Down Expand Up @@ -704,10 +704,10 @@ mod tests {
let lot_size = 0.001;
let mut depth = HashMapMarketDepth::new(0.1, lot_size);

let (prev_best, best) = depth.add_buy_order(1, 500.1, 0.001, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(2, 500.3, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(3, 500.1, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(4, 500.5, 0.005, 0).unwrap();
depth.add_buy_order(1, 500.1, 0.001, 0).unwrap();
depth.add_buy_order(2, 500.3, 0.005, 0).unwrap();
depth.add_buy_order(3, 500.1, 0.005, 0).unwrap();
depth.add_buy_order(4, 500.5, 0.005, 0).unwrap();

assert!(depth.modify_order(10, 500.5, 0.001, 0).is_err());

Expand Down Expand Up @@ -748,10 +748,10 @@ mod tests {
let lot_size = 0.001;
let mut depth = HashMapMarketDepth::new(0.1, lot_size);

let (prev_best, best) = depth.add_sell_order(1, 500.1, 0.001, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(2, 499.3, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(3, 500.1, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(4, 498.5, 0.005, 0).unwrap();
depth.add_sell_order(1, 500.1, 0.001, 0).unwrap();
depth.add_sell_order(2, 499.3, 0.005, 0).unwrap();
depth.add_sell_order(3, 500.1, 0.005, 0).unwrap();
depth.add_sell_order(4, 498.5, 0.005, 0).unwrap();

assert!(depth.modify_order(10, 500.5, 0.001, 0).is_err());

Expand Down
29 changes: 16 additions & 13 deletions hftbacktest/src/depth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ pub use fuse::FusedHashMapMarketDepth;

use crate::{
backtest::data::{Data, POD},
types::OrderId,
types::{Event, OrderId},
};

/// Represents no best bid.
/// Represents no best bid in ticks.
pub const INVALID_MIN: i64 = i64::MIN;

/// Represents no best ask.
/// Represents no best ask in ticks.
pub const INVALID_MAX: i64 = i64::MAX;

/// Provides MarketDepth interface.
Expand Down Expand Up @@ -86,22 +86,19 @@ pub trait L2MarketDepth {
timestamp: i64,
) -> (i64, i64, i64, f64, f64, i64);

/// Clears the market depth. If the `side` is neither [Side::Buy] nor [Side::Sell], both sides
/// are cleared. In this case, `clear_upto_price` is ignored.
/// Clears the market depth. If the side is [Side::None], both sides are cleared. In this case,
/// `clear_upto_price` is ignored.
fn clear_depth(&mut self, side: Side, clear_upto_price: f64);
}

/// Provides a method to initialize the `MarketDepth` from the given snapshot data, such as
/// Start-Of-Day snapshot or End-Of-Day snapshot, for backtesting purpose.
pub trait ApplySnapshot<EventT>
where
EventT: POD + Clone,
{
pub trait ApplySnapshot {
/// Applies the snapshot from the given data to this market depth.
fn apply_snapshot(&mut self, data: &Data<EventT>);
fn apply_snapshot(&mut self, data: &Data<Event>);

/// Returns the current market depth as the depth snapshot events.
fn snapshot(&self) -> Vec<EventT>;
fn snapshot(&self) -> Vec<Event>;
}

/// Level3 order from the market feed.
Expand Down Expand Up @@ -155,22 +152,28 @@ pub trait L3MarketDepth: MarketDepth {
timestamp: i64,
) -> Result<(Side, i64, i64), Self::Error>;

/// Clears the market depth. If the `side` is neither [Side::Buy] nor [Side::Sell], both sides
/// are cleared.
/// Clears the market depth. If the side is [Side::None], both sides are cleared.
fn clear_depth(&mut self, side: Side);

/// Returns the orders held in the order book.
fn orders(&self) -> &HashMap<OrderId, L3Order>;
}

/// Provides Level1-specific market depth functions.
pub trait L1MarketDepth {
/// Updates the best bid and returns a tuple containing (the price in ticks,
/// the previous best bid price in ticks, the current best bid price in ticks, the previous
/// quantity at the price, the current quantity at the price, and the timestamp).
fn update_best_bid(
&mut self,
px: f64,
qty: f64,
timestamp: i64,
) -> (i64, i64, i64, f64, f64, i64);

/// Updates the best ask and returns a tuple containing (the price in ticks,
/// the previous best ask price in ticks, the current best ask price in ticks, the previous
/// quantity at the price, the current quantity at the price, and the timestamp).
fn update_best_ask(
&mut self,
px: f64,
Expand Down
18 changes: 9 additions & 9 deletions hftbacktest/src/depth/roivectormarketdepth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ impl MarketDepth for ROIVectorMarketDepth {
}
}

impl ApplySnapshot<Event> for ROIVectorMarketDepth {
impl ApplySnapshot for ROIVectorMarketDepth {
fn apply_snapshot(&mut self, data: &Data<Event>) {
self.best_bid_tick = INVALID_MIN;
self.best_ask_tick = INVALID_MAX;
Expand Down Expand Up @@ -837,10 +837,10 @@ mod tests {
let lot_size = 0.001;
let mut depth = ROIVectorMarketDepth::new(0.1, lot_size, 0.0, 2000.0);

let (prev_best, best) = depth.add_buy_order(1, 500.1, 0.001, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(2, 500.3, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(3, 500.1, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_buy_order(4, 500.5, 0.005, 0).unwrap();
depth.add_buy_order(1, 500.1, 0.001, 0).unwrap();
depth.add_buy_order(2, 500.3, 0.005, 0).unwrap();
depth.add_buy_order(3, 500.1, 0.005, 0).unwrap();
depth.add_buy_order(4, 500.5, 0.005, 0).unwrap();

assert!(depth.modify_order(10, 500.5, 0.001, 0).is_err());

Expand Down Expand Up @@ -881,10 +881,10 @@ mod tests {
let lot_size = 0.001;
let mut depth = ROIVectorMarketDepth::new(0.1, lot_size, 0.0, 2000.0);

let (prev_best, best) = depth.add_sell_order(1, 500.1, 0.001, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(2, 499.3, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(3, 500.1, 0.005, 0).unwrap();
let (prev_best, best) = depth.add_sell_order(4, 498.5, 0.005, 0).unwrap();
depth.add_sell_order(1, 500.1, 0.001, 0).unwrap();
depth.add_sell_order(2, 499.3, 0.005, 0).unwrap();
depth.add_sell_order(3, 500.1, 0.005, 0).unwrap();
depth.add_sell_order(4, 498.5, 0.005, 0).unwrap();

assert!(depth.modify_order(10, 500.5, 0.001, 0).is_err());

Expand Down
25 changes: 13 additions & 12 deletions hftbacktest/src/live/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ async fn thread_main(
pub type ErrorHandler = Box<dyn Fn(ErrorEvent) -> Result<(), BotError>>;
pub type OrderRecvHook = Box<dyn Fn(&Order, &Order) -> Result<(), BotError>>;

pub type DepthBuilder<MD> = Box<dyn FnMut(&Asset) -> MD>;

/// Live [`LiveBot`] builder.
pub struct LiveBotBuilder<MD> {
conns: HashMap<String, Box<dyn Connector + Send + 'static>>,
assets: Vec<(String, Asset)>,
error_handler: Option<ErrorHandler>,
order_hook: Option<OrderRecvHook>,
depth_builder: Option<Box<dyn FnMut(&Asset) -> MD>>,
depth_builder: Option<DepthBuilder<MD>>,
trade_len: usize,
}

Expand Down Expand Up @@ -350,13 +352,12 @@ where
} else if event.is(LOCAL_ASK_DEPTH_EVENT) {
let depth = unsafe { self.depth.get_unchecked_mut(asset_no) };
depth.update_ask_depth(event.px, event.qty, event.exch_ts);
} else if event.is(LOCAL_BUY_TRADE_EVENT)
|| event.is(LOCAL_SELL_TRADE_EVENT)
} else if (event.is(LOCAL_BUY_TRADE_EVENT)
|| event.is(LOCAL_SELL_TRADE_EVENT))
&& self.trade_len > 0
{
if self.trade_len > 0 {
let trade = unsafe { self.trade.get_unchecked_mut(asset_no) };
trade.push(event);
}
let trade = unsafe { self.trade.get_unchecked_mut(asset_no) };
trade.push(event);
}
}
if WAIT_NEXT_FEED {
Expand All @@ -372,11 +373,11 @@ where
} else if event.is(LOCAL_ASK_DEPTH_EVENT) {
let depth = unsafe { self.depth.get_unchecked_mut(asset_no) };
depth.update_ask_depth(event.px, event.qty, event.exch_ts);
} else if event.is(LOCAL_BUY_TRADE_EVENT) || event.is(LOCAL_SELL_TRADE_EVENT) {
if self.trade_len > 0 {
let trade = unsafe { self.trade.get_unchecked_mut(asset_no) };
trade.push(event);
}
} else if (event.is(LOCAL_BUY_TRADE_EVENT) || event.is(LOCAL_SELL_TRADE_EVENT))
&& self.trade_len > 0
{
let trade = unsafe { self.trade.get_unchecked_mut(asset_no) };
trade.push(event);
}
}
Ok(LiveEvent::Order { asset_no, order }) => {
Expand Down

0 comments on commit ef078c0

Please sign in to comment.