Skip to content

Commit

Permalink
feat(rust): add L3MultiAssetSingleExchangebacktest.
Browse files Browse the repository at this point in the history
  • Loading branch information
nkaz001 committed Jun 17, 2024
1 parent a40922e commit 87a6ae5
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 50 deletions.
4 changes: 2 additions & 2 deletions rust/examples/algo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn gridtrading<MD, I, R>(
) -> Result<(), i64>
where
MD: MarketDepth,
I: Interface + BotDepth<MD>,
I: Interface + BotTypedDepth<MD>,
<I as Interface>::Error: Debug,
R: Recorder,
<R as Recorder>::Error: Debug,
Expand All @@ -29,7 +29,7 @@ where
recorder.record(hbt).unwrap();
}

let depth = hbt.depth_concrete(0);
let depth = hbt.depth_typed(0);
let position = hbt.position(0);

if depth.best_bid_tick() == INVALID_MIN || depth.best_ask_tick() == INVALID_MAX {
Expand Down
11 changes: 6 additions & 5 deletions rust/examples/gridtrading_backtest_args.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use algo::gridtrading;
use clap::Parser;

use algo::gridtrading;
use hftbacktest::{
backtest::{
assettype::LinearAsset,
models::{IntpOrderLatency, PowerProbQueueFunc3, ProbQueueModel, QueuePos},
reader::read_npz,
recorder::BacktestRecorder,
AssetBuilder,
assettype::LinearAsset,
DataSource,
ExchangeKind,
models::{IntpOrderLatency, PowerProbQueueFunc3, ProbQueueModel},
MultiAssetMultiExchangeBacktest,
reader::read_npz,
recorder::BacktestRecorder,
},
prelude::{ApplySnapshot, HashMapMarketDepth, Interface},
};
Expand Down
45 changes: 25 additions & 20 deletions rust/src/backtest/backtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use crate::{
BacktestError,
},
depth::{HashMapMarketDepth, MarketDepth},
prelude::{BotDepth, OrderRequest},
prelude::{BotTypedDepth, OrderRequest},
types::{
BotTrade,
BotTypedTrade,
BuildError,
Event,
Interface,
Expand All @@ -23,16 +23,21 @@ use crate::{
WAIT_ORDER_RESPONSE_NONE,
},
};
#[cfg(feature = "unstable_l3")]
use crate::{
backtest::proc::GenLocalProcessor,
depth::L3MarketDepth
};

/// [`MultiAssetMultiExchangeBacktest`] builder.
pub struct MultiAssetMultiExchangeBacktestBuilder<MD> {
local: Vec<Box<dyn LocalProcessor<MD>>>,
local: Vec<Box<dyn LocalProcessor<MD, Event>>>,
exch: Vec<Box<dyn Processor>>,
}

impl<MD> MultiAssetMultiExchangeBacktestBuilder<MD> {
/// Adds [`Asset`], which will undergo simulation within the backtester.
pub fn add(self, asset: Asset<dyn LocalProcessor<MD>, dyn Processor>) -> Self {
pub fn add(self, asset: Asset<dyn LocalProcessor<MD, Event>, dyn Processor>) -> Self {
let mut self_ = Self { ..self };
self_.local.push(asset.local);
self_.exch.push(asset.exch);
Expand Down Expand Up @@ -60,7 +65,7 @@ impl<MD> MultiAssetMultiExchangeBacktestBuilder<MD> {
pub struct MultiAssetMultiExchangeBacktest<MD> {
cur_ts: i64,
evs: EventSet,
local: Vec<Box<dyn LocalProcessor<MD>>>,
local: Vec<Box<dyn LocalProcessor<MD, Event>>>,
exch: Vec<Box<dyn Processor>>,
}

Expand All @@ -75,7 +80,7 @@ where
}
}

pub fn new(local: Vec<Box<dyn LocalProcessor<MD>>>, exch: Vec<Box<dyn Processor>>) -> Self {
pub fn new(local: Vec<Box<dyn LocalProcessor<MD, Event>>>, exch: Vec<Box<dyn Processor>>) -> Self {
let num_assets = local.len();
if local.len() != num_assets || exch.len() != num_assets {
panic!();
Expand Down Expand Up @@ -509,22 +514,22 @@ where
}
}

impl<MD> BotDepth<MD> for MultiAssetMultiExchangeBacktest<MD>
impl<MD> BotTypedDepth<MD> for MultiAssetMultiExchangeBacktest<MD>
where
MD: MarketDepth,
{
#[inline]
fn depth_concrete(&self, asset_no: usize) -> &MD {
fn depth_typed(&self, asset_no: usize) -> &MD {
&self.local.get(asset_no).unwrap().depth()
}
}

impl<MD> BotTrade<Event> for MultiAssetMultiExchangeBacktest<MD>
impl<MD> BotTypedTrade<Event> for MultiAssetMultiExchangeBacktest<MD>
where
MD: MarketDepth,
{
#[inline]
fn trade_concrete(&self, asset_no: usize) -> &Vec<Event> {
fn trade_typed(&self, asset_no: usize) -> &Vec<Event> {
let local = self.local.get(asset_no).unwrap();
local.trade()
}
Expand All @@ -538,7 +543,7 @@ pub struct MultiAssetSingleExchangeBacktestBuilder<Local, Exchange> {

impl<Local, Exchange> MultiAssetSingleExchangeBacktestBuilder<Local, Exchange>
where
Local: LocalProcessor<HashMapMarketDepth> + 'static,
Local: LocalProcessor<HashMapMarketDepth, Event> + 'static,
Exchange: Processor + 'static,
{
/// Adds [`Asset`], which will undergo simulation within the backtester.
Expand Down Expand Up @@ -583,7 +588,7 @@ pub struct MultiAssetSingleExchangeBacktest<MD, Local, Exchange> {
impl<MD, Local, Exchange> MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
where
MD: MarketDepth,
Local: LocalProcessor<MD>,
Local: LocalProcessor<MD, Event>,
Exchange: Processor,
{
pub fn builder() -> MultiAssetSingleExchangeBacktestBuilder<Local, Exchange> {
Expand Down Expand Up @@ -716,7 +721,7 @@ where
impl<MD, Local, Exchange> Interface for MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
where
MD: MarketDepth,
Local: LocalProcessor<MD>,
Local: LocalProcessor<MD, Event>,
Exchange: Processor,
{
type Error = BacktestError;
Expand Down Expand Up @@ -1025,27 +1030,27 @@ where
}
}

impl<MD, Local, Exchange> BotDepth<MD> for MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
impl<MD, Local, Exchange> BotTypedDepth<MD> for MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
where
MD: MarketDepth,
Local: LocalProcessor<MD>,
Local: LocalProcessor<MD, Event>,
Exchange: Processor,
{
#[inline]
fn depth_concrete(&self, asset_no: usize) -> &MD {
fn depth_typed(&self, asset_no: usize) -> &MD {
&self.local.get(asset_no).unwrap().depth()
}
}

impl<MD, Local, Exchange> BotTrade<Event> for MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
impl<MD, Local, Exchange> BotTypedTrade<Event> for MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
where
MD: MarketDepth,
Local: LocalProcessor<MD>,
Local: LocalProcessor<MD, Event>,
Exchange: Processor,
{
#[inline]
fn trade_concrete(&self, asset_no: usize) -> &Vec<Event> {
fn trade_typed(&self, asset_no: usize) -> &Vec<Event> {
let local = self.local.get(asset_no).unwrap();
local.trade()
}
}
}
5 changes: 4 additions & 1 deletion rust/src/backtest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub mod recorder;

mod evs;

#[cfg(feature = "unstable_l3")]
mod l3backtest;

#[derive(Error, Debug)]
pub enum BacktestError {
#[error("Order related to a given order id already exists")]
Expand Down Expand Up @@ -206,7 +209,7 @@ where
}

/// Builds an `Asset`.
pub fn build(self) -> Result<Asset<dyn LocalProcessor<MD>, dyn Processor>, BuildError> {
pub fn build(self) -> Result<Asset<dyn LocalProcessor<MD, Event>, dyn Processor>, BuildError> {
let ob_local_to_exch = OrderBus::new();
let ob_exch_to_local = OrderBus::new();

Expand Down
7 changes: 3 additions & 4 deletions rust/src/backtest/proc/l3_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ where
}
}

impl<AT, LM, MD> LocalProcessor<MD> for L3Local<AT, LM, MD>
impl<AT, LM, MD> LocalProcessor<MD, L3Event> for L3Local<AT, LM, MD>
where
AT: AssetType,
LM: LatencyModel,
Expand Down Expand Up @@ -215,9 +215,8 @@ where
&self.orders
}

fn trade(&self) -> &Vec<Event> {
todo!()
// &self.trades
fn trade(&self) -> &Vec<L3Event> {
&self.trades
}

fn clear_last_trades(&mut self) {
Expand Down
2 changes: 1 addition & 1 deletion rust/src/backtest/proc/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ where
}
}

impl<AT, LM, MD> LocalProcessor<MD> for Local<AT, LM, MD>
impl<AT, LM, MD> LocalProcessor<MD, Event> for Local<AT, LM, MD>
where
AT: AssetType,
LM: LatencyModel,
Expand Down
2 changes: 2 additions & 0 deletions rust/src/backtest/proc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ mod l3_nopartialfillexchange;
pub use l3_local::L3Local;
#[cfg(feature = "unstable_l3")]
pub use l3_nopartialfillexchange::L3NoPartialFillExchange;
#[cfg(feature = "unstable_l3")]
pub use proc::GenLocalProcessor;
64 changes: 62 additions & 2 deletions rust/src/backtest/proc/proc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::any::Any;
use std::collections::HashMap;

use crate::{
Expand All @@ -7,7 +8,7 @@ use crate::{
};

/// Provides local-specific interaction.
pub trait LocalProcessor<MD>: Processor
pub trait LocalProcessor<MD, EventT> : Processor
where
MD: MarketDepth,
{
Expand Down Expand Up @@ -56,7 +57,7 @@ where
fn orders(&self) -> &HashMap<i64, Order>;

/// Returns the last market trades.
fn trade(&self) -> &Vec<Event>;
fn trade(&self) -> &Vec<EventT>;

/// Clears the last market trades from the buffer.
fn clear_last_trades(&mut self);
Expand Down Expand Up @@ -96,3 +97,62 @@ pub trait Processor {
/// the corresponding processor.
fn earliest_send_order_timestamp(&self) -> i64;
}

pub trait GenLocalProcessor : Processor {
/// Submits a new order.
///
/// * `order_id` - The unique order ID; there should not be any existing order with the same ID
/// on both local and exchange sides.
/// * `price` - Order price.
/// * `qty` - Quantity to buy.
/// * `order_type` - Available [`OrdType`] options vary depending on the exchange model. See to
/// the exchange model for details.
/// * `time_in_force` - Available [`TimeInForce`] options vary depending on the exchange model.
/// See to the exchange model for details.
/// * `current_timestamp` - The current backtesting timestamp.
fn submit_order(
&mut self,
order_id: i64,
side: Side,
price: f32,
qty: f32,
order_type: OrdType,
time_in_force: TimeInForce,
current_timestamp: i64,
) -> Result<(), BacktestError>;

/// Cancels the specified order.
///
/// * `order_id` - Order ID to cancel.
/// * `current_timestamp` - The current backtesting timestamp.
fn cancel(&mut self, order_id: i64, current_timestamp: i64) -> Result<(), BacktestError>;

/// Clears inactive orders from the local orders whose status is neither
/// [`Status::New`] nor [`Status::PartiallyFilled`].
fn clear_inactive_orders(&mut self);

/// Returns the position you currently hold.
fn position(&self) -> f64;

/// Returns the state's values such as balance, fee, and so on.
fn state_values(&self) -> StateValues;

/// Returns the [`MarketDepth`].
fn depth(&self) -> &dyn Any;

/// Returns a hash map of order IDs and their corresponding [`Order`]s.
fn orders(&self) -> &HashMap<i64, Order>;

/// Returns the last market trades.
fn trade(&self) -> Vec<&dyn Any>;

/// Clears the last market trades from the buffer.
fn clear_last_trades(&mut self);

/// Returns the last feed's exchange timestamp and local receipt timestamp.
fn feed_latency(&self) -> Option<(i64, i64)>;

/// Returns the last order's request timestamp, exchange timestamp, and response receipt
/// timestamp.
fn order_latency(&self) -> Option<(i64, i64, i64)>;
}
6 changes: 3 additions & 3 deletions rust/src/backtest/recorder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{

use crate::{
depth::MarketDepth,
types::{BotDepth, Interface, Recorder},
types::{BotTypedDepth, Interface, Recorder},
};

/// Provides recording of the backtesting strategy's state values, which are needed to compute
Expand All @@ -20,12 +20,12 @@ impl Recorder for BacktestRecorder {

fn record<MD, I>(&mut self, hbt: &mut I) -> Result<(), Self::Error>
where
I: Interface + BotDepth<MD>,
I: Interface + BotTypedDepth<MD>,
MD: MarketDepth,
{
let timestamp = hbt.current_timestamp();
for asset_no in 0..hbt.num_assets() {
let depth = hbt.depth_concrete(asset_no);
let depth = hbt.depth_typed(asset_no);
let mid_price = (depth.best_bid() + depth.best_ask()) / 2.0;
let state_values = hbt.state_values(asset_no);
let values = unsafe { self.values.get_unchecked_mut(asset_no) };
Expand Down
2 changes: 1 addition & 1 deletion rust/src/depth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub trait ApplySnapshot {
}

#[cfg(feature = "unstable_l3")]
pub trait L3MarketDepth: MarketDepth {
pub trait L3MarketDepth : MarketDepth {
type Error;

fn add_buy_order(
Expand Down
12 changes: 6 additions & 6 deletions rust/src/live/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ use crate::{
live::Asset,
prelude::OrderRequest,
types::{
BotDepth,
BotTrade,
BotTypedDepth,
BotTypedTrade,
BuildError,
Error as ErrorEvent,
Error,
Expand Down Expand Up @@ -710,20 +710,20 @@ where
}
}

impl<MD> BotDepth<MD> for Bot<MD>
impl<MD> BotTypedDepth<MD> for Bot<MD>
where
MD: MarketDepth,
{
fn depth_concrete(&self, asset_no: usize) -> &MD {
fn depth_typed(&self, asset_no: usize) -> &MD {
self.depth.get(asset_no).unwrap()
}
}

impl<MD> BotTrade<Event> for Bot<MD>
impl<MD> BotTypedTrade<Event> for Bot<MD>
where
MD: MarketDepth,
{
fn trade_concrete(&self, asset_no: usize) -> &Vec<Event> {
fn trade_typed(&self, asset_no: usize) -> &Vec<Event> {
self.trade.get(asset_no).unwrap()
}
}
Loading

0 comments on commit 87a6ae5

Please sign in to comment.