use crate::trade::error::TradeError;
use crate::trade::orders::intent::within;
use crate::utils::MarketId;
use crate::utils::ids::NO_MARKET;
use crate::utils::json::Json;
use crate::utils::sign::goldilocks::Fp;
use crate::utils::sign::tx::{Attributes, BadTx, Tx, TxType, element, number};
use crate::utils::time::{DAY_MS, MINUTE_MS};
pub const CANCEL_ALL_LEAST_MS: u64 = 5 * MINUTE_MS;
pub const CANCEL_ALL_MOST_MS: u64 = 15 * DAY_MS;
const MOST_TIME: u64 = i64::MAX.unsigned_abs();
const CANCEL_ALL_MARKET: u8 = 5;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CancelAllTime {
Now,
At(u64),
Abort,
}
impl CancelAllTime {
#[must_use]
pub const fn code(self) -> u8 {
match self {
CancelAllTime::Now => 0,
CancelAllTime::At(_) => 1,
CancelAllTime::Abort => 2,
}
}
#[must_use]
pub const fn time(self) -> u64 {
match self {
CancelAllTime::At(at) => at,
CancelAllTime::Now | CancelAllTime::Abort => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CancelAllOrders {
pub when: CancelAllTime,
pub attributes: Attributes,
}
impl CancelAllOrders {
#[must_use]
pub const fn now() -> CancelAllOrders {
CancelAllOrders { when: CancelAllTime::Now, attributes: Attributes::NONE }
}
#[must_use]
pub const fn in_market(market: MarketId) -> CancelAllOrders {
CancelAllOrders { when: CancelAllTime::Now, attributes: Attributes::NONE.cancel_market(market) }
}
#[must_use]
pub fn in_markets(markets: &[MarketId]) -> Vec<CancelAllOrders> {
let mut seen: Vec<MarketId> = Vec::with_capacity(markets.len());
for market in markets {
if !seen.contains(market) {
seen.push(*market);
}
}
seen.into_iter().map(CancelAllOrders::in_market).collect()
}
#[must_use]
pub const fn at(at: u64) -> CancelAllOrders {
CancelAllOrders { when: CancelAllTime::At(at), attributes: Attributes::NONE }
}
pub fn scheduled(at: u64, now_ms: u64) -> Result<CancelAllOrders, TradeError> {
within("Time", "a scheduled cancel-all", at, now_ms, CANCEL_ALL_LEAST_MS, CANCEL_ALL_MOST_MS)
.map(CancelAllOrders::at)
}
#[must_use]
pub const fn abort() -> CancelAllOrders {
CancelAllOrders { when: CancelAllTime::Abort, attributes: Attributes::NONE }
}
#[must_use]
pub const fn with(mut self, attributes: Attributes) -> CancelAllOrders {
self.attributes = attributes;
self
}
#[must_use]
pub fn market(&self) -> Option<MarketId> {
self.attributes
.entries()
.into_iter()
.find(|(kind, value)| *kind == CANCEL_ALL_MARKET && *value != NO_MARKET)
.and_then(|(_, value)| MarketId::new(value).ok())
}
}
impl Tx for CancelAllOrders {
fn tx_type(&self) -> TxType {
TxType::CancelAllOrders
}
fn hash_fields(&self) -> Vec<Fp> {
vec![element(self.when.code()), element(self.when.time())]
}
fn info_fields(&self) -> Vec<(&'static str, Json)> {
vec![("TimeInForce", number(self.when.code())), ("Time", number(self.when.time()))]
}
fn attributes(&self) -> Attributes {
self.attributes
}
fn check(&self) -> Result<(), BadTx> {
self.attributes.check()?;
if self.market().is_some() && !matches!(self.when, CancelAllTime::Now) {
return Err(BadTx::new(
"TimeInForce",
"a cancel-all limited to one market runs now; it can't be scheduled or abort one",
));
}
if let CancelAllTime::At(at) = self.when
&& (at == 0 || at > MOST_TIME)
{
return Err(BadTx::new("Time", "a scheduled cancel-all's time is unix milliseconds above 0"));
}
Ok(())
}
}
#[cfg(test)]
#[path = "tests/cancel_all.rs"]
mod tests;