use crate::{Exchange, Price, Symbol};
use rustc_hash::FxHashMap;
#[derive(Clone, Debug, Default)]
pub struct MultiExchange {
exchanges: FxHashMap<Symbol, Exchange>,
}
impl MultiExchange {
pub fn new() -> Self {
Self::default()
}
pub fn get_or_create(&mut self, symbol: &Symbol) -> &mut Exchange {
self.exchanges.entry(*symbol).or_default()
}
pub fn get(&self, symbol: &Symbol) -> Option<&Exchange> {
self.exchanges.get(symbol)
}
pub fn get_mut(&mut self, symbol: &Symbol) -> Option<&mut Exchange> {
self.exchanges.get_mut(symbol)
}
pub fn symbols(&self) -> impl Iterator<Item = &Symbol> {
self.exchanges.keys()
}
pub fn len(&self) -> usize {
self.exchanges.len()
}
pub fn is_empty(&self) -> bool {
self.exchanges.is_empty()
}
pub fn best_prices(&self) -> Vec<(Symbol, Option<Price>, Option<Price>)> {
self.exchanges
.iter()
.map(|(sym, ex)| {
let (bid, ask) = ex.best_bid_ask();
(*sym, bid, ask)
})
.collect()
}
}
#[cfg(test)]
#[allow(clippy::inconsistent_digit_grouping)]
mod tests {
use super::*;
use crate::{Side, TimeInForce};
fn aapl() -> Symbol {
Symbol::new("AAPL")
}
fn msft() -> Symbol {
Symbol::new("MSFT")
}
#[test]
fn create_and_query() {
let mut multi = MultiExchange::new();
multi
.get_or_create(&aapl())
.submit_limit(Side::Sell, Price(150_00), 100, TimeInForce::GTC);
assert_eq!(multi.get(&aapl()).unwrap().best_ask(), Some(Price(150_00)));
assert!(multi.get(&msft()).is_none());
}
#[test]
fn multiple_symbols() {
let mut multi = MultiExchange::new();
multi
.get_or_create(&aapl())
.submit_limit(Side::Sell, Price(150_00), 100, TimeInForce::GTC);
multi
.get_or_create(&msft())
.submit_limit(Side::Sell, Price(300_00), 200, TimeInForce::GTC);
assert_eq!(multi.len(), 2);
let prices = multi.best_prices();
assert_eq!(prices.len(), 2);
}
#[test]
fn symbols_iter() {
let mut multi = MultiExchange::new();
multi.get_or_create(&aapl());
multi.get_or_create(&msft());
let syms: Vec<&Symbol> = multi.symbols().collect();
assert_eq!(syms.len(), 2);
}
#[test]
fn empty() {
let multi = MultiExchange::new();
assert!(multi.is_empty());
assert_eq!(multi.len(), 0);
}
#[test]
fn independent_books() {
let mut multi = MultiExchange::new();
multi
.get_or_create(&aapl())
.submit_limit(Side::Sell, Price(150_00), 100, TimeInForce::GTC);
multi
.get_or_create(&aapl())
.submit_limit(Side::Buy, Price(150_00), 100, TimeInForce::GTC);
multi
.get_or_create(&msft())
.submit_limit(Side::Sell, Price(300_00), 200, TimeInForce::GTC);
assert_eq!(multi.get(&aapl()).unwrap().trades().len(), 1);
assert_eq!(multi.get(&msft()).unwrap().trades().len(), 0);
}
}