use crate::trade::error::TradeError;
use crate::trade::orders::limit::limit_on;
use crate::trade::orders::{Order, Side, Tpsl};
use crate::trade::ticket::{Market, Refusal, positive};
use crate::utils::rounding::Grid;
use crate::utils::{Dec, Round};
pub const TRIGGER_SLIPPAGE: Dec = Dec::lit::<1>(1);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitPrice {
Market { slippage: Dec },
Limit(Dec),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Exit {
pub trigger: Dec,
pub price: ExitPrice,
}
impl Exit {
#[must_use]
pub const fn market(trigger: Dec) -> Exit {
Exit::market_within(trigger, TRIGGER_SLIPPAGE)
}
#[must_use]
pub const fn market_within(trigger: Dec, slippage: Dec) -> Exit {
Exit { trigger, price: ExitPrice::Market { slippage } }
}
#[must_use]
pub const fn limit(trigger: Dec, price: Dec) -> Exit {
Exit { trigger, price: ExitPrice::Limit(price) }
}
}
pub(crate) fn trigger_on(grid: Grid, trigger: Dec) -> Result<Dec, Refusal> {
let trigger = positive(trigger, "a trigger price")?;
grid.tick(trigger, Round::Nearest).ok_or(Refusal::Trade(TradeError::NoPrice(trigger)))
}
pub(crate) fn fill_price(grid: Grid, trigger: Dec, price: ExitPrice, side: Side) -> Result<(Dec, bool), Refusal> {
match price {
ExitPrice::Market { slippage } => Ok((grid.market_bound(trigger, slippage, side)?, true)),
ExitPrice::Limit(price) => Ok((limit_on(grid, price, side, "a limit price")?, false)),
}
}
pub(crate) fn exit_order(market: &Market, side: Side, size: Dec, tpsl: Tpsl, exit: Exit) -> Result<Order, Refusal> {
let trigger = trigger_on(market.grid(), exit.trigger)?;
let (price, is_market) = fill_price(market.grid(), trigger, exit.price, side)?;
Ok(Order::trigger(market.asset(), side, price, size, trigger, tpsl, is_market).reduce_only())
}
#[cfg(test)]
#[path = "tests/trigger.rs"]
mod tests;