kiteticker_async_manager/models/
order.rs1use chrono::{DateTime, NaiveDateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use serde_with::{serde_as, DefaultOnNull};
5
6use crate::Exchange;
7
8#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
9#[serde(rename_all = "UPPERCASE")]
10pub enum OrderTransactionType {
11 Buy,
12 Sell,
13}
14
15#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
16pub enum OrderValidity {
17 DAY,
18 IOC,
19 TTL,
20}
21
22#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
23#[serde(rename_all = "UPPERCASE")]
24pub enum OrderStatus {
25 COMPLETE,
26 REJECTED,
27 CANCELLED,
28 UPDATE,
29}
30
31#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
32#[serde(transparent)]
33pub struct TimeStamp(i64);
34
35impl From<String> for TimeStamp {
36 fn from(value: String) -> Self {
37 let secs = NaiveDateTime::parse_from_str(&value, "%Y-%m-%d %H:%M:%S")
38 .unwrap()
39 .and_utc()
40 .timestamp();
41 TimeStamp(secs)
42 }
43}
44
45impl From<TimeStamp> for String {
46 fn from(value: TimeStamp) -> Self {
47 DateTime::<Utc>::from_timestamp(value.0, 0)
48 .unwrap_or_default()
49 .naive_utc()
50 .format("%Y-%m-%d %H:%M:%S")
51 .to_string()
52 }
53}
54
55#[serde_with::serde_as]
56#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
57pub struct Order {
58 pub order_id: String,
59
60 #[serde_as(as = "DefaultOnNull")]
61 pub exchange_order_id: Option<String>,
62
63 #[serde_as(as = "DefaultOnNull")]
64 pub parent_order_id: Option<String>,
65
66 pub placed_by: String,
67 pub app_id: u64,
68
69 pub status: OrderStatus,
70
71 #[serde_as(as = "DefaultOnNull")]
72 pub status_message: Option<String>,
73
74 #[serde_as(as = "DefaultOnNull")]
75 pub status_message_raw: Option<String>,
76
77 pub tradingsymbol: String,
78 pub instrument_token: u32,
79
80 #[serde_as(as = "serde_with::FromInto<String>")]
81 pub exchange: Exchange,
82
83 pub order_type: String,
84 pub transaction_type: OrderTransactionType,
85
86 pub validity: OrderValidity,
87 pub variety: String,
88 pub product: Option<String>,
89
90 #[serde(default)]
91 pub average_price: f64,
92
93 #[serde(default)]
94 pub disclosed_quantity: f64,
95
96 pub price: f64,
97 pub quantity: u64,
98 pub filled_quantity: u64,
99
100 #[serde(default)]
101 pub unfilled_quantity: u64,
102
103 #[serde(default)]
104 pub pending_quantity: u64,
105
106 #[serde(default)]
107 pub cancelled_quantity: u64,
108
109 #[serde(default)]
110 pub trigger_price: f64,
111
112 pub user_id: String,
113
114 #[serde_as(as = "serde_with::FromInto<String>")]
115 pub order_timestamp: TimeStamp,
116 #[serde_as(as = "serde_with::FromInto<String>")]
117 pub exchange_timestamp: TimeStamp,
118 #[serde_as(as = "serde_with::FromInto<String>")]
119 pub exchange_update_timestamp: TimeStamp,
120
121 pub checksum: String,
122 #[serde(default)]
123 pub meta: Option<serde_json::Map<String, Value>>,
124
125 #[serde_as(as = "DefaultOnNull")]
126 #[serde(default)]
127 pub tag: Option<String>,
128}
129
130#[cfg(test)]
131mod tests {
132
133 use sha2::{Digest, Sha256};
134
135 use super::*;
136
137 #[test]
138 fn test_order() {
139 let postback_json = include_str!("../../kiteconnect-mocks/postback.json");
140 let exp_order = Order {
141 order_id: "220303000308932".to_string(),
142 exchange_order_id: Some("1000000001482421".to_string()),
143 parent_order_id: None,
144 placed_by: "AB1234".to_string(),
145 app_id: 1234,
146 status: OrderStatus::COMPLETE,
147 status_message: None,
148 status_message_raw: None,
149 tradingsymbol: "SBIN".to_string(),
150 instrument_token: 779521,
151 exchange: Exchange::NSE,
152 order_type: "MARKET".to_string(),
153 transaction_type: OrderTransactionType::Buy,
154 validity: OrderValidity::DAY,
155 variety: "regular".to_string(),
156 product: Some("CNC".to_string()),
157 average_price: 470.0,
158 disclosed_quantity: 0.0,
159 price: 0.0,
160 quantity: 1,
161 filled_quantity: 1,
162 unfilled_quantity: 0,
163 pending_quantity: 0,
164 cancelled_quantity: 0,
165 trigger_price: 0.0,
166 user_id: "AB1234".to_string(),
167 order_timestamp: TimeStamp(1646299465),
168 exchange_timestamp: TimeStamp(1646299465),
169 exchange_update_timestamp: TimeStamp(1646299465),
170 checksum:
171 "2011845d9348bd6795151bf4258102a03431e3bb12a79c0df73fcb4b7fde4b5d"
172 .to_string(),
173 meta: Some(serde_json::Map::new()),
174 tag: None,
175 };
176 let order = serde_json::from_str::<Order>(postback_json).unwrap();
177 assert_eq!(order.clone(), exp_order);
178
179 let mut hasher = Sha256::new();
180 hasher.update(order.order_id.as_bytes());
181 hasher.update(Into::<String>::into(order.order_timestamp));
182 hasher.update(b"0hdv7iw5examplesecret");
183 let expected = hasher.finalize();
184 let actual = hex::decode(order.checksum).unwrap();
185 assert_eq!(expected[..], actual[..]);
186 }
187}