use std::{collections::HashMap, io::Error as IoError, marker::PhantomData};
pub use data::DataSource;
use data::{Cache, Reader};
use models::fee::FeeModel;
use thiserror::Error;
use crate::{
backtest::{
assettype::AssetType,
evs::{EventIntentKind, EventSet},
models::{LatencyModel, QueueModel},
order::OrderBus,
proc::{Local, LocalProcessor, NoPartialFillExchange, PartialFillExchange, Processor},
state::State,
},
depth::{HashMapMarketDepth, L2MarketDepth, MarketDepth},
prelude::{
Bot,
OrdType,
Order,
OrderId,
OrderRequest,
Side,
StateValues,
TimeInForce,
WaitOrderResponse,
UNTIL_END_OF_DATA,
},
types::{BuildError, Event},
};
pub mod assettype;
pub mod models;
pub mod order;
pub mod proc;
pub mod state;
pub mod recorder;
pub mod data;
mod evs;
#[derive(Error, Debug)]
pub enum BacktestError {
#[error("Order related to a given order id already exists")]
OrderIdExist,
#[error("Order request is in process")]
OrderRequestInProcess,
#[error("Order not found")]
OrderNotFound,
#[error("order request is invalid")]
InvalidOrderRequest,
#[error("order status is invalid to proceed the request")]
InvalidOrderStatus,
#[error("end of data")]
EndOfData,
#[error("data error: {0:?}")]
DataError(#[from] IoError),
}
pub struct Asset<L: ?Sized, E: ?Sized> {
pub local: Box<L>,
pub exch: Box<E>,
}
impl<L, E> Asset<L, E> {
pub fn new(local: L, exch: E) -> Self {
Self {
local: Box::new(local),
exch: Box::new(exch),
}
}
pub fn builder<Q, LM, AT, QM, MD, FM>() -> AssetBuilder<LM, AT, QM, MD, FM>
where
AT: AssetType + Clone + 'static,
MD: MarketDepth + L2MarketDepth + 'static,
QM: QueueModel<MD> + 'static,
LM: LatencyModel + Clone + 'static,
FM: FeeModel + Clone + 'static,
{
AssetBuilder::new()
}
}
pub enum ExchangeKind {
NoPartialFillExchange,
PartialFillExchange,
}
pub struct AssetBuilder<LM, AT, QM, MD, FM> {
latency_model: Option<LM>,
asset_type: Option<AT>,
queue_model: Option<QM>,
depth_builder: Option<Box<dyn Fn() -> MD>>,
reader: Reader<Event>,
fee_model: Option<FM>,
exch_kind: ExchangeKind,
last_trades_cap: usize,
}
impl<LM, AT, QM, MD, FM> AssetBuilder<LM, AT, QM, MD, FM>
where
AT: AssetType + Clone + 'static,
MD: MarketDepth + L2MarketDepth + 'static,
QM: QueueModel<MD> + 'static,
LM: LatencyModel + Clone + 'static,
FM: FeeModel + Clone + 'static,
{
pub fn new() -> Self {
let cache = Cache::new();
let reader = Reader::new(cache);
Self {
latency_model: None,
asset_type: None,
queue_model: None,
depth_builder: None,
reader,
fee_model: None,
exch_kind: ExchangeKind::NoPartialFillExchange,
last_trades_cap: 0,
}
}
pub fn data(mut self, data: Vec<DataSource<Event>>) -> Self {
for item in data {
match item {
DataSource::File(filename) => {
self.reader.add_file(filename);
}
DataSource::Data(data) => {
self.reader.add_data(data);
}
}
}
self
}
pub fn latency_model(self, latency_model: LM) -> Self {
Self {
latency_model: Some(latency_model),
..self
}
}
pub fn asset_type(self, asset_type: AT) -> Self {
Self {
asset_type: Some(asset_type),
..self
}
}
pub fn queue_model(self, queue_model: QM) -> Self {
Self {
queue_model: Some(queue_model),
..self
}
}
pub fn fee_model(self, fee_model: FM) -> Self {
Self {
fee_model: Some(fee_model),
..self
}
}
pub fn depth<Builder>(self, builder: Builder) -> Self
where
Builder: Fn() -> MD + 'static,
{
Self {
depth_builder: Some(Box::new(builder)),
..self
}
}
pub fn exchange(self, exch_kind: ExchangeKind) -> Self {
Self { exch_kind, ..self }
}
pub fn last_trades_capacity(self, capacity: usize) -> Self {
Self {
last_trades_cap: capacity,
..self
}
}
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();
let create_depth = self
.depth_builder
.as_ref()
.ok_or(BuildError::BuilderIncomplete("depth"))?;
let order_latency = self
.latency_model
.clone()
.ok_or(BuildError::BuilderIncomplete("order_latency"))?;
let asset_type = self
.asset_type
.clone()
.ok_or(BuildError::BuilderIncomplete("asset_type"))?;
let fee_model = self
.fee_model
.clone()
.ok_or(BuildError::BuilderIncomplete("fee_model"))?;
let local = Local::new(
self.reader.clone(),
create_depth(),
State::new(asset_type, fee_model),
order_latency,
self.last_trades_cap,
ob_local_to_exch.clone(),
ob_exch_to_local.clone(),
);
let order_latency = self
.latency_model
.clone()
.ok_or(BuildError::BuilderIncomplete("order_latency"))?;
let queue_model = self
.queue_model
.ok_or(BuildError::BuilderIncomplete("queue_model"))?;
let asset_type = self
.asset_type
.clone()
.ok_or(BuildError::BuilderIncomplete("asset_type"))?;
let fee_model = self
.fee_model
.clone()
.ok_or(BuildError::BuilderIncomplete("fee_model"))?;
match self.exch_kind {
ExchangeKind::NoPartialFillExchange => {
let exch = NoPartialFillExchange::new(
self.reader.clone(),
create_depth(),
State::new(asset_type, fee_model),
order_latency,
queue_model,
ob_exch_to_local,
ob_local_to_exch,
);
Ok(Asset {
local: Box::new(local),
exch: Box::new(exch),
})
}
ExchangeKind::PartialFillExchange => {
let exch = PartialFillExchange::new(
self.reader.clone(),
create_depth(),
State::new(asset_type, fee_model),
order_latency,
queue_model,
ob_exch_to_local,
ob_local_to_exch,
);
Ok(Asset {
local: Box::new(local),
exch: Box::new(exch),
})
}
}
}
pub fn build_single(
self,
) -> Result<Asset<Local<AT, LM, MD, FM>, NoPartialFillExchange<AT, LM, QM, MD, FM>>, BuildError>
{
let ob_local_to_exch = OrderBus::new();
let ob_exch_to_local = OrderBus::new();
let create_depth = self
.depth_builder
.as_ref()
.ok_or(BuildError::BuilderIncomplete("depth"))?;
let order_latency = self
.latency_model
.clone()
.ok_or(BuildError::BuilderIncomplete("order_latency"))?;
let asset_type = self
.asset_type
.clone()
.ok_or(BuildError::BuilderIncomplete("asset_type"))?;
let fee_model = self
.fee_model
.clone()
.ok_or(BuildError::BuilderIncomplete("fee_model"))?;
let local = Local::new(
self.reader.clone(),
create_depth(),
State::new(asset_type, fee_model),
order_latency,
self.last_trades_cap,
ob_local_to_exch.clone(),
ob_exch_to_local.clone(),
);
let order_latency = self
.latency_model
.clone()
.ok_or(BuildError::BuilderIncomplete("order_latency"))?;
let queue_model = self
.queue_model
.ok_or(BuildError::BuilderIncomplete("queue_model"))?;
let asset_type = self
.asset_type
.clone()
.ok_or(BuildError::BuilderIncomplete("asset_type"))?;
let fee_model = self
.fee_model
.clone()
.ok_or(BuildError::BuilderIncomplete("fee_model"))?;
let exch = NoPartialFillExchange::new(
self.reader.clone(),
create_depth(),
State::new(asset_type, fee_model),
order_latency,
queue_model,
ob_exch_to_local,
ob_local_to_exch,
);
Ok(Asset {
local: Box::new(local),
exch: Box::new(exch),
})
}
}
impl<LM, AT, QM, MD, FM> Default for AssetBuilder<LM, AT, QM, MD, FM>
where
AT: AssetType + Clone + 'static,
MD: MarketDepth + L2MarketDepth + 'static,
QM: QueueModel<MD> + 'static,
LM: LatencyModel + Clone + 'static,
FM: FeeModel + Clone + 'static,
{
fn default() -> Self {
Self::new()
}
}
pub struct BacktestBuilder<MD> {
local: Vec<Box<dyn LocalProcessor<MD, Event>>>,
exch: Vec<Box<dyn Processor>>,
}
impl<MD> BacktestBuilder<MD> {
pub fn add_asset(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);
self_
}
pub fn build(self) -> Result<Backtest<MD>, BuildError> {
let num_assets = self.local.len();
if self.local.len() != num_assets || self.exch.len() != num_assets {
panic!();
}
Ok(Backtest {
cur_ts: i64::MAX,
evs: EventSet::new(num_assets),
local: self.local,
exch: self.exch,
})
}
}
pub struct Backtest<MD> {
cur_ts: i64,
evs: EventSet,
local: Vec<Box<dyn LocalProcessor<MD, Event>>>,
exch: Vec<Box<dyn Processor>>,
}
impl<MD> Backtest<MD>
where
MD: MarketDepth,
{
pub fn builder() -> BacktestBuilder<MD> {
BacktestBuilder {
local: vec![],
exch: vec![],
}
}
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!();
}
Self {
cur_ts: i64::MAX,
evs: EventSet::new(num_assets),
local,
exch,
}
}
fn initialize_evs(&mut self) -> Result<(), BacktestError> {
for (asset_no, local) in self.local.iter_mut().enumerate() {
match local.initialize_data() {
Ok(ts) => self.evs.update_local_data(asset_no, ts),
Err(BacktestError::EndOfData) => {
self.evs.invalidate_local_data(asset_no);
}
Err(e) => {
return Err(e);
}
}
}
for (asset_no, exch) in self.exch.iter_mut().enumerate() {
match exch.initialize_data() {
Ok(ts) => self.evs.update_exch_data(asset_no, ts),
Err(BacktestError::EndOfData) => {
self.evs.invalidate_exch_data(asset_no);
}
Err(e) => {
return Err(e);
}
}
}
Ok(())
}
pub fn goto_end(&mut self) -> Result<bool, BacktestError> {
if self.cur_ts == i64::MAX {
self.initialize_evs()?;
match self.evs.next() {
Some(ev) => {
self.cur_ts = ev.timestamp;
}
None => {
return Ok(false);
}
}
}
self.goto::<false>(UNTIL_END_OF_DATA, WaitOrderResponse::None)
}
fn goto<const WAIT_NEXT_FEED: bool>(
&mut self,
timestamp: i64,
wait_order_response: WaitOrderResponse,
) -> Result<bool, BacktestError> {
let mut timestamp = timestamp;
for (asset_no, local) in self.local.iter().enumerate() {
self.evs
.update_exch_order(asset_no, local.earliest_send_order_timestamp());
self.evs
.update_local_order(asset_no, local.earliest_recv_order_timestamp());
}
loop {
match self.evs.next() {
Some(ev) => {
if ev.timestamp > timestamp {
self.cur_ts = timestamp;
return Ok(true);
}
match ev.kind {
EventIntentKind::LocalData => {
let local = unsafe { self.local.get_unchecked_mut(ev.asset_no) };
match local.process_data() {
Ok((next_ts, _)) => {
self.evs.update_local_data(ev.asset_no, next_ts);
}
Err(BacktestError::EndOfData) => {
self.evs.invalidate_local_data(ev.asset_no);
}
Err(e) => {
return Err(e);
}
}
if WAIT_NEXT_FEED {
timestamp = ev.timestamp;
}
}
EventIntentKind::LocalOrder => {
let local = unsafe { self.local.get_unchecked_mut(ev.asset_no) };
let wait_order_resp_id = match wait_order_response {
WaitOrderResponse::Specified {
asset_no: wait_order_asset_no,
order_id: wait_order_id,
} if ev.asset_no == wait_order_asset_no => Some(wait_order_id),
_ => None,
};
if local.process_recv_order(ev.timestamp, wait_order_resp_id)?
|| wait_order_response == WaitOrderResponse::Any
{
timestamp = ev.timestamp;
}
self.evs.update_local_order(
ev.asset_no,
local.earliest_recv_order_timestamp(),
);
}
EventIntentKind::ExchData => {
let exch = unsafe { self.exch.get_unchecked_mut(ev.asset_no) };
match exch.process_data() {
Ok((next_ts, _)) => {
self.evs.update_exch_data(ev.asset_no, next_ts);
}
Err(BacktestError::EndOfData) => {
self.evs.invalidate_exch_data(ev.asset_no);
}
Err(e) => {
return Err(e);
}
}
self.evs.update_local_order(
ev.asset_no,
exch.earliest_send_order_timestamp(),
);
}
EventIntentKind::ExchOrder => {
let exch = unsafe { self.exch.get_unchecked_mut(ev.asset_no) };
let _ = exch.process_recv_order(ev.timestamp, None)?;
self.evs.update_exch_order(
ev.asset_no,
exch.earliest_recv_order_timestamp(),
);
}
}
}
None => {
return Ok(false);
}
}
}
}
}
impl<MD> Bot<MD> for Backtest<MD>
where
MD: MarketDepth,
{
type Error = BacktestError;
#[inline]
fn current_timestamp(&self) -> i64 {
self.cur_ts
}
#[inline]
fn num_assets(&self) -> usize {
self.local.len()
}
#[inline]
fn position(&self, asset_no: usize) -> f64 {
self.local.get(asset_no).unwrap().position()
}
#[inline]
fn state_values(&self, asset_no: usize) -> &StateValues {
self.local.get(asset_no).unwrap().state_values()
}
fn depth(&self, asset_no: usize) -> &MD {
self.local.get(asset_no).unwrap().depth()
}
fn last_trades(&self, asset_no: usize) -> &[Event] {
self.local.get(asset_no).unwrap().last_trades()
}
#[inline]
fn clear_last_trades(&mut self, asset_no: Option<usize>) {
match asset_no {
Some(an) => {
let local = self.local.get_mut(an).unwrap();
local.clear_last_trades();
}
None => {
for local in self.local.iter_mut() {
local.clear_last_trades();
}
}
}
}
#[inline]
fn orders(&self, asset_no: usize) -> &HashMap<u64, Order> {
self.local.get(asset_no).unwrap().orders()
}
#[inline]
fn submit_buy_order(
&mut self,
asset_no: usize,
order_id: OrderId,
price: f64,
qty: f64,
time_in_force: TimeInForce,
order_type: OrdType,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.submit_order(
order_id,
Side::Buy,
price,
qty,
order_type,
time_in_force,
self.cur_ts,
)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified { asset_no, order_id },
);
}
Ok(true)
}
#[inline]
fn submit_sell_order(
&mut self,
asset_no: usize,
order_id: OrderId,
price: f64,
qty: f64,
time_in_force: TimeInForce,
order_type: OrdType,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.submit_order(
order_id,
Side::Sell,
price,
qty,
order_type,
time_in_force,
self.cur_ts,
)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified { asset_no, order_id },
);
}
Ok(true)
}
fn submit_order(
&mut self,
asset_no: usize,
order: OrderRequest,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.submit_order(
order.order_id,
Side::Sell,
order.price,
order.qty,
order.order_type,
order.time_in_force,
self.cur_ts,
)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified {
asset_no,
order_id: order.order_id,
},
);
}
Ok(true)
}
#[inline]
fn cancel(
&mut self,
asset_no: usize,
order_id: OrderId,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.cancel(order_id, self.cur_ts)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified { asset_no, order_id },
);
}
Ok(true)
}
#[inline]
fn clear_inactive_orders(&mut self, asset_no: Option<usize>) {
match asset_no {
Some(asset_no) => {
self.local
.get_mut(asset_no)
.unwrap()
.clear_inactive_orders();
}
None => {
for local in self.local.iter_mut() {
local.clear_inactive_orders();
}
}
}
}
#[inline]
fn wait_order_response(
&mut self,
asset_no: usize,
order_id: OrderId,
timeout: i64,
) -> Result<bool, BacktestError> {
self.goto::<false>(
self.cur_ts + timeout,
WaitOrderResponse::Specified { asset_no, order_id },
)
}
#[inline]
fn wait_next_feed(
&mut self,
include_order_resp: bool,
timeout: i64,
) -> Result<bool, Self::Error> {
if self.cur_ts == i64::MAX {
self.initialize_evs()?;
match self.evs.next() {
Some(ev) => {
self.cur_ts = ev.timestamp;
}
None => {
return Ok(false);
}
}
}
if include_order_resp {
self.goto::<true>(self.cur_ts + timeout, WaitOrderResponse::Any)
} else {
self.goto::<true>(self.cur_ts + timeout, WaitOrderResponse::None)
}
}
#[inline]
fn elapse(&mut self, duration: i64) -> Result<bool, Self::Error> {
if self.cur_ts == i64::MAX {
self.initialize_evs()?;
match self.evs.next() {
Some(ev) => {
self.cur_ts = ev.timestamp;
}
None => {
return Ok(false);
}
}
}
self.goto::<false>(self.cur_ts + duration, WaitOrderResponse::None)
}
#[inline]
fn elapse_bt(&mut self, duration: i64) -> Result<bool, Self::Error> {
self.elapse(duration)
}
#[inline]
fn close(&mut self) -> Result<(), Self::Error> {
Ok(())
}
#[inline]
fn feed_latency(&self, asset_no: usize) -> Option<(i64, i64)> {
self.local.get(asset_no).unwrap().feed_latency()
}
#[inline]
fn order_latency(&self, asset_no: usize) -> Option<(i64, i64, i64)> {
self.local.get(asset_no).unwrap().order_latency()
}
}
pub struct MultiAssetSingleExchangeBacktestBuilder<Local, Exchange> {
local: Vec<Local>,
exch: Vec<Exchange>,
}
impl<Local, Exchange> MultiAssetSingleExchangeBacktestBuilder<Local, Exchange>
where
Local: LocalProcessor<HashMapMarketDepth, Event> + 'static,
Exchange: Processor + 'static,
{
pub fn add_asset(self, asset: Asset<Local, Exchange>) -> Self {
let mut self_ = Self { ..self };
self_.local.push(*asset.local);
self_.exch.push(*asset.exch);
self_
}
pub fn build(
self,
) -> Result<MultiAssetSingleExchangeBacktest<HashMapMarketDepth, Local, Exchange>, BuildError>
{
let num_assets = self.local.len();
if self.local.len() != num_assets || self.exch.len() != num_assets {
panic!();
}
Ok(MultiAssetSingleExchangeBacktest {
cur_ts: i64::MAX,
evs: EventSet::new(num_assets),
local: self.local,
exch: self.exch,
_md_marker: Default::default(),
})
}
}
pub struct MultiAssetSingleExchangeBacktest<MD, Local, Exchange> {
cur_ts: i64,
evs: EventSet,
local: Vec<Local>,
exch: Vec<Exchange>,
_md_marker: PhantomData<MD>,
}
impl<MD, Local, Exchange> MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
where
MD: MarketDepth,
Local: LocalProcessor<MD, Event>,
Exchange: Processor,
{
pub fn builder() -> MultiAssetSingleExchangeBacktestBuilder<Local, Exchange> {
MultiAssetSingleExchangeBacktestBuilder {
local: vec![],
exch: vec![],
}
}
pub fn new(local: Vec<Local>, exch: Vec<Exchange>) -> Self {
let num_assets = local.len();
if local.len() != num_assets || exch.len() != num_assets {
panic!();
}
Self {
cur_ts: i64::MAX,
evs: EventSet::new(num_assets),
local,
exch,
_md_marker: Default::default(),
}
}
fn initialize_evs(&mut self) -> Result<(), BacktestError> {
for (asset_no, local) in self.local.iter_mut().enumerate() {
match local.initialize_data() {
Ok(ts) => self.evs.update_local_data(asset_no, ts),
Err(BacktestError::EndOfData) => {
self.evs.invalidate_local_data(asset_no);
}
Err(e) => {
return Err(e);
}
}
}
for (asset_no, exch) in self.exch.iter_mut().enumerate() {
match exch.initialize_data() {
Ok(ts) => self.evs.update_exch_data(asset_no, ts),
Err(BacktestError::EndOfData) => {
self.evs.invalidate_exch_data(asset_no);
}
Err(e) => {
return Err(e);
}
}
}
Ok(())
}
pub fn goto<const WAIT_NEXT_FEED: bool>(
&mut self,
timestamp: i64,
wait_order_response: WaitOrderResponse,
) -> Result<bool, BacktestError> {
let mut timestamp = timestamp;
for (asset_no, local) in self.local.iter().enumerate() {
self.evs
.update_exch_order(asset_no, local.earliest_send_order_timestamp());
self.evs
.update_local_order(asset_no, local.earliest_recv_order_timestamp());
}
loop {
match self.evs.next() {
Some(ev) => {
if ev.timestamp > timestamp {
self.cur_ts = timestamp;
return Ok(true);
}
match ev.kind {
EventIntentKind::LocalData => {
let local = unsafe { self.local.get_unchecked_mut(ev.asset_no) };
match local.process_data() {
Ok((next_ts, _)) => {
self.evs.update_local_data(ev.asset_no, next_ts);
}
Err(BacktestError::EndOfData) => {
self.evs.invalidate_local_data(ev.asset_no);
}
Err(e) => {
return Err(e);
}
}
if WAIT_NEXT_FEED {
timestamp = ev.timestamp;
}
}
EventIntentKind::LocalOrder => {
let local = unsafe { self.local.get_unchecked_mut(ev.asset_no) };
let wait_order_resp_id = match wait_order_response {
WaitOrderResponse::Specified {
asset_no: wait_order_asset_no,
order_id: wait_order_id,
} if ev.asset_no == wait_order_asset_no => Some(wait_order_id),
_ => None,
};
if local.process_recv_order(ev.timestamp, wait_order_resp_id)?
|| wait_order_response == WaitOrderResponse::Any
{
timestamp = ev.timestamp;
}
self.evs.update_local_order(
ev.asset_no,
local.earliest_recv_order_timestamp(),
);
}
EventIntentKind::ExchData => {
let exch = unsafe { self.exch.get_unchecked_mut(ev.asset_no) };
match exch.process_data() {
Ok((next_ts, _)) => {
self.evs.update_exch_data(ev.asset_no, next_ts);
}
Err(BacktestError::EndOfData) => {
self.evs.invalidate_exch_data(ev.asset_no);
}
Err(e) => {
return Err(e);
}
}
self.evs.update_local_order(
ev.asset_no,
exch.earliest_send_order_timestamp(),
);
}
EventIntentKind::ExchOrder => {
let exch = unsafe { self.exch.get_unchecked_mut(ev.asset_no) };
let _ = exch.process_recv_order(ev.timestamp, None)?;
self.evs.update_exch_order(
ev.asset_no,
exch.earliest_recv_order_timestamp(),
);
}
}
}
None => {
return Ok(false);
}
}
}
}
}
impl<MD, Local, Exchange> Bot<MD> for MultiAssetSingleExchangeBacktest<MD, Local, Exchange>
where
MD: MarketDepth,
Local: LocalProcessor<MD, Event>,
Exchange: Processor,
{
type Error = BacktestError;
#[inline]
fn current_timestamp(&self) -> i64 {
self.cur_ts
}
#[inline]
fn num_assets(&self) -> usize {
self.local.len()
}
#[inline]
fn position(&self, asset_no: usize) -> f64 {
self.local.get(asset_no).unwrap().position()
}
#[inline]
fn state_values(&self, asset_no: usize) -> &StateValues {
self.local.get(asset_no).unwrap().state_values()
}
fn depth(&self, asset_no: usize) -> &MD {
self.local.get(asset_no).unwrap().depth()
}
fn last_trades(&self, asset_no: usize) -> &[Event] {
self.local.get(asset_no).unwrap().last_trades()
}
#[inline]
fn clear_last_trades(&mut self, asset_no: Option<usize>) {
match asset_no {
Some(an) => {
let local = self.local.get_mut(an).unwrap();
local.clear_last_trades();
}
None => {
for local in self.local.iter_mut() {
local.clear_last_trades();
}
}
}
}
#[inline]
fn orders(&self, asset_no: usize) -> &HashMap<OrderId, Order> {
self.local.get(asset_no).unwrap().orders()
}
#[inline]
fn submit_buy_order(
&mut self,
asset_no: usize,
order_id: OrderId,
price: f64,
qty: f64,
time_in_force: TimeInForce,
order_type: OrdType,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.submit_order(
order_id,
Side::Buy,
price,
qty,
order_type,
time_in_force,
self.cur_ts,
)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified { asset_no, order_id },
);
}
Ok(true)
}
#[inline]
fn submit_sell_order(
&mut self,
asset_no: usize,
order_id: OrderId,
price: f64,
qty: f64,
time_in_force: TimeInForce,
order_type: OrdType,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.submit_order(
order_id,
Side::Sell,
price,
qty,
order_type,
time_in_force,
self.cur_ts,
)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified { asset_no, order_id },
);
}
Ok(true)
}
fn submit_order(
&mut self,
asset_no: usize,
order: OrderRequest,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.submit_order(
order.order_id,
Side::Sell,
order.price,
order.qty,
order.order_type,
order.time_in_force,
self.cur_ts,
)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified {
asset_no,
order_id: order.order_id,
},
);
}
Ok(true)
}
#[inline]
fn cancel(
&mut self,
asset_no: usize,
order_id: OrderId,
wait: bool,
) -> Result<bool, Self::Error> {
let local = self.local.get_mut(asset_no).unwrap();
local.cancel(order_id, self.cur_ts)?;
if wait {
return self.goto::<false>(
UNTIL_END_OF_DATA,
WaitOrderResponse::Specified { asset_no, order_id },
);
}
Ok(true)
}
#[inline]
fn clear_inactive_orders(&mut self, asset_no: Option<usize>) {
match asset_no {
Some(asset_no) => {
self.local
.get_mut(asset_no)
.unwrap()
.clear_inactive_orders();
}
None => {
for local in self.local.iter_mut() {
local.clear_inactive_orders();
}
}
}
}
#[inline]
fn wait_order_response(
&mut self,
asset_no: usize,
order_id: OrderId,
timeout: i64,
) -> Result<bool, BacktestError> {
self.goto::<false>(
self.cur_ts + timeout,
WaitOrderResponse::Specified { asset_no, order_id },
)
}
fn wait_next_feed(
&mut self,
include_order_resp: bool,
timeout: i64,
) -> Result<bool, Self::Error> {
if self.cur_ts == i64::MAX {
self.initialize_evs()?;
match self.evs.next() {
Some(ev) => {
self.cur_ts = ev.timestamp;
}
None => {
return Ok(false);
}
}
}
if include_order_resp {
self.goto::<true>(self.cur_ts + timeout, WaitOrderResponse::Any)
} else {
self.goto::<true>(self.cur_ts + timeout, WaitOrderResponse::None)
}
}
#[inline]
fn elapse(&mut self, duration: i64) -> Result<bool, Self::Error> {
if self.cur_ts == i64::MAX {
self.initialize_evs()?;
match self.evs.next() {
Some(ev) => {
self.cur_ts = ev.timestamp;
}
None => {
return Ok(false);
}
}
}
self.goto::<false>(self.cur_ts + duration, WaitOrderResponse::None)
}
#[inline]
fn elapse_bt(&mut self, duration: i64) -> Result<bool, Self::Error> {
self.elapse(duration)
}
#[inline]
fn close(&mut self) -> Result<(), Self::Error> {
Ok(())
}
#[inline]
fn feed_latency(&self, asset_no: usize) -> Option<(i64, i64)> {
self.local.get(asset_no).unwrap().feed_latency()
}
#[inline]
fn order_latency(&self, asset_no: usize) -> Option<(i64, i64, i64)> {
self.local.get(asset_no).unwrap().order_latency()
}
}