use crate::limits::RiskLimits;
use dashmap::DashMap;
use fx_utils::{OrderId, Quantity, Result, Side};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Position {
pub instrument: String,
pub quantity: i64, }
pub struct RiskEngine {
limits: Arc<RiskLimits>,
positions: DashMap<String, Position>,
open_orders: DashMap<OrderId, Quantity>,
}
impl RiskEngine {
pub fn new(limits: RiskLimits) -> Self {
Self {
limits: Arc::new(limits),
positions: DashMap::new(),
open_orders: DashMap::new(),
}
}
pub fn check_order(
&self,
instrument: &str,
side: Side,
quantity: Quantity,
order_id: OrderId,
) -> Result<()> {
if quantity.0 > self.limits.max_order_size.0 {
return Err(fx_utils::Error::InvalidInput(format!(
"Order size {} exceeds limit {}",
quantity.0, self.limits.max_order_size.0
)));
}
if self.open_orders.len() >= self.limits.max_open_orders {
return Err(fx_utils::Error::InvalidInput(
"Maximum open orders limit reached".to_string(),
));
}
let current_position = self
.positions
.get(instrument)
.map(|p| p.quantity)
.unwrap_or(0);
let new_position = match side {
Side::Buy => current_position + quantity.0 as i64,
Side::Sell => current_position - quantity.0 as i64,
};
#[allow(clippy::cast_abs_to_unsigned)]
#[allow(clippy::cast_abs_to_unsigned)]
if new_position.abs() as u64 > self.limits.max_position_size.0 {
return Err(fx_utils::Error::InvalidInput(format!(
"Position limit would be exceeded: {}",
new_position
)));
}
self.open_orders.insert(order_id, quantity);
Ok(())
}
pub fn update_position(&self, instrument: &str, side: Side, quantity: Quantity) {
let mut position = self
.positions
.entry(instrument.to_string())
.or_insert_with(|| Position {
instrument: instrument.to_string(),
quantity: 0,
});
match side {
Side::Buy => position.quantity += quantity.0 as i64,
Side::Sell => position.quantity -= quantity.0 as i64,
}
}
pub fn remove_order(&self, order_id: OrderId) {
self.open_orders.remove(&order_id);
}
pub fn get_position(&self, instrument: &str) -> i64 {
self.positions
.get(instrument)
.map(|p| p.quantity)
.unwrap_or(0)
}
pub fn positions(&self) -> &DashMap<String, Position> {
&self.positions
}
pub fn open_orders(&self) -> &DashMap<OrderId, Quantity> {
&self.open_orders
}
pub fn limits(&self) -> &Arc<RiskLimits> {
&self.limits
}
}