architect_api/orderflow/
modify.rs

1use crate::{
2    orderflow::{Order, OrderStatus, OrderType},
3    OrderId,
4};
5use anyhow::{bail, Result};
6use chrono::{DateTime, Utc};
7use rust_decimal::Decimal;
8use schemars::{JsonSchema, JsonSchema_repr};
9use serde::{Deserialize, Serialize};
10use serde_repr::{Deserialize_repr, Serialize_repr};
11use serde_with::skip_serializing_none;
12use uuid::Uuid;
13
14#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
15pub struct Modify {
16    #[serde(rename = "mid")]
17    #[schemars(title = "modify_id")]
18    pub modify_id: Uuid,
19    #[serde(rename = "id")]
20    #[schemars(title = "order_id")]
21    pub order_id: OrderId,
22    #[serde(rename = "nid")]
23    #[schemars(title = "new_order_id")]
24    /// The new order ID that will be assigned to the order after modification.
25    pub new_order_id: OrderId,
26    #[serde(rename = "p")]
27    #[schemars(title = "new_price")]
28    pub new_price: Option<Decimal>,
29    #[serde(rename = "q")]
30    #[schemars(title = "new_quantity")]
31    pub new_quantity: Option<Decimal>,
32    #[serde(rename = "ts")]
33    #[schemars(title = "recv_time")]
34    pub recv_time: i64,
35    #[serde(rename = "tn")]
36    #[schemars(title = "recv_time_ns")]
37    pub recv_time_ns: u32,
38    #[serde(rename = "o")]
39    #[schemars(title = "status")]
40    pub status: ModifyStatus,
41    #[serde(rename = "r")]
42    #[schemars(title = "reject_reason")]
43    pub reject_reason: Option<String>,
44}
45
46impl Modify {
47    pub fn recv_time(&self) -> Option<DateTime<Utc>> {
48        DateTime::from_timestamp(self.recv_time, self.recv_time_ns)
49    }
50
51    pub fn reject(&self, message: Option<String>) -> ModifyReject {
52        ModifyReject { modify_id: self.modify_id, order_id: self.order_id, message }
53    }
54
55    /// Return a new order with the modified fields.
56    pub fn modify(&self, mut order: Order) -> Result<Order> {
57        order.id = self.new_order_id;
58        order.quantity = self.new_quantity.unwrap_or(order.quantity);
59        order.status = OrderStatus::Pending;
60
61        if let Some(price) = self.new_price {
62            match order.order_type {
63                OrderType::Limit(ref mut l) => {
64                    l.limit_price = price;
65                }
66                OrderType::StopLossLimit(ref mut p) => {
67                    p.limit_price = price;
68                }
69                OrderType::TakeProfitLimit(ref mut p) => {
70                    p.limit_price = price;
71                }
72                OrderType::Market => {
73                    bail!("cannot modify market order with price")
74                }
75                OrderType::Bracket(ref mut b) => {
76                    b.limit_price = price;
77                }
78            };
79        }
80
81        Ok(order)
82    }
83}
84
85#[derive(
86    Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, JsonSchema_repr,
87)]
88#[serde(rename_all = "snake_case")]
89#[cfg_attr(feature = "juniper", derive(juniper::GraphQLEnum))]
90#[repr(u8)]
91pub enum ModifyStatus {
92    Pending = 0,
93    Acked = 1,
94    Rejected = 2,
95    Out = 127,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
99#[cfg_attr(feature = "juniper", derive(juniper::GraphQLObject))]
100pub struct ModifyReject {
101    #[serde(rename = "mid")]
102    pub modify_id: Uuid,
103    #[serde(rename = "id")]
104    pub order_id: OrderId,
105    #[serde(rename = "rm", skip_serializing_if = "Option::is_none")]
106    pub message: Option<String>,
107}
108
109impl ModifyReject {
110    pub fn to_error_string(&self) -> String {
111        format!(
112            "modify {} rejected: {}",
113            self.modify_id,
114            self.message.as_deref().unwrap_or("--")
115        )
116    }
117}
118
119#[skip_serializing_none]
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct ModifyUpdate {
122    pub modify_id: Uuid,
123    pub order_id: OrderId,
124    pub timestamp: i64,
125    pub timestamp_ns: u32,
126    pub status: Option<ModifyStatus>,
127    pub reject_message: Option<String>,
128}
129
130impl ModifyUpdate {
131    pub fn timestamp(&self) -> Option<DateTime<Utc>> {
132        DateTime::from_timestamp(self.timestamp, self.timestamp_ns)
133    }
134}