use crate::trade::builder::BuilderSetting;
use crate::trade::error::TradeError;
use crate::trade::orders::{Cloid, Order, PlaceOrder, Side};
use crate::trade::ticket::{Market, Refusal, at_least, positive, routed};
use crate::utils::Dec;
use crate::utils::rounding::MINIMUM_ORDER_VALUE;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Close {
pub position: Dec,
pub size: Option<Dec>,
pub reference: Dec,
pub slippage: Dec,
pub cloid: Option<Cloid>,
}
impl Close {
#[must_use]
pub const fn new(position: Dec, reference: Dec, slippage: Dec) -> Close {
Close { position, size: None, reference, slippage, cloid: None }
}
#[must_use]
pub const fn with_cloid(mut self, cloid: Cloid) -> Close {
self.cloid = Some(cloid);
self
}
}
pub fn close(market: &Market, intent: &Close, builder: Option<&BuilderSetting>) -> Result<PlaceOrder, Refusal> {
if market.is_spot() {
return Err(Refusal::Spot("a position"));
}
if intent.position.is_zero() {
return Err(Refusal::NoPosition);
}
let side = match intent.position.is_positive() {
true => Side::Sell,
false => Side::Buy,
};
let held = intent.position.abs();
let wanted = match intent.size {
None => held,
Some(size) if positive(size, "a size")? > held => {
return Err(Refusal::CloseTooLarge { size, position: held });
}
Some(size) => size,
};
let size = market.grid().size(wanted).ok_or(Refusal::Trade(TradeError::NoSize(wanted)))?;
let reference = positive(intent.reference, "a reference price")?;
let bound = market.grid().market_bound(reference, intent.slippage, side)?;
if size != held {
at_least(size, reference, MINIMUM_ORDER_VALUE)?;
}
let order = Order::market(market.asset(), side, bound, size).reduce_only();
let order = match intent.cloid {
Some(cloid) => order.with_cloid(cloid),
None => order,
};
routed(PlaceOrder::one(order), builder)
}
#[cfg(test)]
#[path = "tests/close.rs"]
mod tests;