architect_api/orderflow/
modify.rs1use 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 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 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}