use super::{OrderFlags, OrderKind};
use crate::{LevelId, Price, Quantity, QuantityPolicy, SequenceNumber};
use std::{
fmt,
ops::{Deref, DerefMut},
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RestingLimitOrder {
time_priority: SequenceNumber,
level_id: LevelId,
inner: LimitOrder,
}
impl RestingLimitOrder {
pub fn new(time_priority: SequenceNumber, level_id: LevelId, inner: LimitOrder) -> Self {
Self {
time_priority,
level_id,
inner,
}
}
pub fn time_priority(&self) -> SequenceNumber {
self.time_priority
}
pub(crate) fn update_time_priority(&mut self, new_time_priority: SequenceNumber) {
self.time_priority = new_time_priority;
}
pub fn level_id(&self) -> LevelId {
self.level_id
}
pub(crate) fn update_level_id(&mut self, new_level_id: LevelId) {
self.level_id = new_level_id;
}
pub fn inner(&self) -> &LimitOrder {
&self.inner
}
pub fn into_inner(self) -> LimitOrder {
self.inner
}
}
impl Deref for RestingLimitOrder {
type Target = LimitOrder;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for RestingLimitOrder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LimitOrder {
price: Price,
quantity_policy: QuantityPolicy,
flags: OrderFlags,
}
impl LimitOrder {
pub fn new(price: Price, quantity_policy: QuantityPolicy, flags: OrderFlags) -> Self {
Self {
price,
quantity_policy,
flags,
}
}
pub fn kind(&self) -> OrderKind {
OrderKind::Limit
}
pub fn price(&self) -> Price {
self.price
}
pub(crate) fn update_price(&mut self, new_price: Price) {
self.price = new_price;
}
pub fn quantity_policy(&self) -> QuantityPolicy {
self.quantity_policy
}
pub fn visible_quantity(&self) -> Quantity {
self.quantity_policy.visible_quantity()
}
pub fn hidden_quantity(&self) -> Quantity {
self.quantity_policy.hidden_quantity()
}
pub fn replenish_quantity(&self) -> Quantity {
self.quantity_policy.replenish_quantity()
}
pub fn total_quantity(&self) -> Quantity {
self.quantity_policy.total_quantity()
}
pub fn is_filled(&self) -> bool {
self.quantity_policy.is_filled()
}
pub(crate) fn update_quantity_policy(&mut self, new_quantity_policy: QuantityPolicy) {
self.quantity_policy = new_quantity_policy;
}
pub fn flags(&self) -> &OrderFlags {
&self.flags
}
pub(crate) fn match_against(&mut self, incoming_quantity: Quantity) -> (Quantity, Quantity) {
match self.quantity_policy {
QuantityPolicy::Standard { quantity } => {
let new_quantity = quantity.saturating_sub(incoming_quantity);
let consumed = quantity - new_quantity;
self.quantity_policy.update_visible_quantity(new_quantity);
(consumed, Quantity(0))
}
QuantityPolicy::Iceberg {
visible_quantity, ..
} => {
let new_visible = visible_quantity.saturating_sub(incoming_quantity);
let consumed = visible_quantity - new_visible;
self.quantity_policy.update_visible_quantity(new_visible);
if !new_visible.is_zero() {
(consumed, Quantity(0))
} else {
let replenished = self.quantity_policy.replenish();
(consumed, replenished)
}
}
}
}
}
impl Deref for LimitOrder {
type Target = OrderFlags;
fn deref(&self) -> &Self::Target {
&self.flags
}
}
impl DerefMut for LimitOrder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.flags
}
}
impl fmt::Display for LimitOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.quantity_policy {
QuantityPolicy::Standard { quantity } => {
write!(
f,
"Standard: price={} quantity={} side={} post_only={} time_in_force={}",
self.price,
quantity,
self.side(),
self.post_only(),
self.time_in_force()
)
}
QuantityPolicy::Iceberg {
visible_quantity,
hidden_quantity,
replenish_quantity,
} => {
write!(
f,
"Iceberg: price={} visible_quantity={} hidden_quantity={} replenish_quantity={} side={} post_only={} time_in_force={}",
self.price,
visible_quantity,
hidden_quantity,
replenish_quantity,
self.side(),
self.post_only(),
self.time_in_force()
)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Quantity, QuantityPolicy, Side, TimeInForce, Timestamp, orders::OrderFlags};
fn create_standard_order() -> LimitOrder {
LimitOrder::new(
Price(90),
QuantityPolicy::Standard {
quantity: Quantity(10),
},
OrderFlags::new(Side::Buy, true, TimeInForce::Gtc),
)
}
fn create_iceberg_order() -> LimitOrder {
LimitOrder::new(
Price(100),
QuantityPolicy::Iceberg {
visible_quantity: Quantity(20),
hidden_quantity: Quantity(40),
replenish_quantity: Quantity(20),
},
OrderFlags::new(Side::Sell, false, TimeInForce::Gtc),
)
}
#[test]
fn test_time_priority() {
let mut order = RestingLimitOrder::new(SequenceNumber(0), 0, create_standard_order());
assert_eq!(order.time_priority(), SequenceNumber(0));
order.update_time_priority(SequenceNumber(1));
assert_eq!(order.time_priority(), SequenceNumber(1));
}
#[test]
fn test_price() {
let mut order = create_standard_order();
assert_eq!(order.price(), Price(90));
order.update_price(Price(95));
assert_eq!(order.price(), Price(95));
assert_eq!(create_iceberg_order().price(), Price(100));
}
#[test]
fn test_quantity_policy() {
{
let mut order = create_standard_order();
assert_eq!(order.visible_quantity(), Quantity(10));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(0));
assert_eq!(order.total_quantity(), Quantity(10));
assert!(!order.is_filled());
order.update_quantity_policy(QuantityPolicy::Iceberg {
visible_quantity: Quantity(1),
hidden_quantity: Quantity(10),
replenish_quantity: Quantity(1),
});
assert_eq!(order.visible_quantity(), Quantity(1));
assert_eq!(order.hidden_quantity(), Quantity(10));
assert_eq!(order.replenish_quantity(), Quantity(1));
assert_eq!(order.total_quantity(), Quantity(11));
assert!(!order.is_filled());
}
{
let mut order = create_iceberg_order();
assert_eq!(order.visible_quantity(), Quantity(20));
assert_eq!(order.hidden_quantity(), Quantity(40));
assert_eq!(order.replenish_quantity(), Quantity(20));
assert_eq!(order.total_quantity(), Quantity(60));
assert!(!order.is_filled());
order.update_quantity_policy(QuantityPolicy::Standard {
quantity: Quantity(100),
});
assert_eq!(order.visible_quantity(), Quantity(100));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(0));
assert_eq!(order.total_quantity(), Quantity(100));
assert!(!order.is_filled());
}
}
#[test]
fn test_side() {
assert_eq!(create_standard_order().side(), Side::Buy);
assert_eq!(create_iceberg_order().side(), Side::Sell);
}
#[test]
fn test_post_only() {
assert!(create_standard_order().post_only());
assert!(!create_iceberg_order().post_only());
}
#[test]
fn test_time_in_force() {
let mut order = create_standard_order();
assert_eq!(order.time_in_force(), TimeInForce::Gtc);
assert!(!order.is_immediate());
assert!(!order.has_expiry());
assert!(!order.is_expired(Timestamp(1771180000)));
order.update_time_in_force(TimeInForce::Ioc);
assert!(order.is_immediate());
assert!(!order.has_expiry());
assert!(!order.is_expired(Timestamp(1771180000)));
order.update_time_in_force(TimeInForce::Fok);
assert!(order.is_immediate());
assert!(!order.has_expiry());
assert!(!order.is_expired(Timestamp(1771180000)));
order.update_time_in_force(TimeInForce::Gtd(Timestamp(1771180000 + 1000)));
assert!(!order.is_immediate());
assert!(order.has_expiry());
assert!(!order.is_expired(Timestamp(1771180000)));
assert!(order.is_expired(Timestamp(1771180000 + 1000)));
}
#[test]
fn test_match_against() {
{
let mut order = create_standard_order();
assert_eq!(order.visible_quantity(), Quantity(10));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(0));
let (consumed, replenished) = order.match_against(Quantity(2));
assert_eq!(consumed, Quantity(2));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(8));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(0));
let (consumed, replenished) = order.match_against(Quantity(10));
assert_eq!(consumed, Quantity(8));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(0));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(0));
let (consumed, replenished) = order.match_against(Quantity(10));
assert_eq!(consumed, Quantity(0));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(0));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(0));
}
{
let mut order = create_iceberg_order();
assert_eq!(order.visible_quantity(), Quantity(20));
assert_eq!(order.hidden_quantity(), Quantity(40));
assert_eq!(order.replenish_quantity(), Quantity(20));
let (consumed, replenished) = order.match_against(Quantity(5));
assert_eq!(consumed, Quantity(5));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(15));
assert_eq!(order.hidden_quantity(), Quantity(40));
assert_eq!(order.replenish_quantity(), Quantity(20));
let (consumed, replenished) = order.match_against(Quantity(20));
assert_eq!(consumed, Quantity(15));
assert_eq!(replenished, Quantity(20));
assert_eq!(order.visible_quantity(), Quantity(20));
assert_eq!(order.hidden_quantity(), Quantity(20));
assert_eq!(order.replenish_quantity(), Quantity(20));
let (consumed, replenished) = order.match_against(Quantity(20));
assert_eq!(consumed, Quantity(20));
assert_eq!(replenished, Quantity(20));
assert_eq!(order.visible_quantity(), Quantity(20));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(20));
let (consumed, replenished) = order.match_against(Quantity(1));
assert_eq!(consumed, Quantity(1));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(19));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(20));
let (consumed, replenished) = order.match_against(Quantity(19));
assert_eq!(consumed, Quantity(19));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(0));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(20));
let (consumed, replenished) = order.match_against(Quantity(1));
assert_eq!(consumed, Quantity(0));
assert_eq!(replenished, Quantity(0));
assert_eq!(order.visible_quantity(), Quantity(0));
assert_eq!(order.hidden_quantity(), Quantity(0));
assert_eq!(order.replenish_quantity(), Quantity(20));
}
}
#[test]
fn test_display() {
assert_eq!(
create_standard_order().to_string(),
"Standard: price=90 quantity=10 side=BUY post_only=true time_in_force=GTC"
);
assert_eq!(
create_iceberg_order().to_string(),
"Iceberg: price=100 visible_quantity=20 hidden_quantity=40 replenish_quantity=20 side=SELL post_only=false time_in_force=GTC"
);
}
#[cfg(feature = "serde")]
#[test]
fn test_roundtrip_serialization() {
for order in [create_standard_order(), create_iceberg_order()] {
let serialized = serde_json::to_string(&order).unwrap();
let deserialized: LimitOrder = serde_json::from_str(&serialized).unwrap();
assert_eq!(order, deserialized);
}
}
}