1use num_enum::{FromPrimitive, IntoPrimitive};
4use rust_decimal::Decimal;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use time::OffsetDateTime;
7
8use crate::serde_utils;
9
10mod opt_decimal_string {
15 use rust_decimal::Decimal;
16 use serde::{Deserialize, Deserializer, Serializer};
17
18 pub(crate) fn serialize<S: Serializer>(
19 value: &Option<Decimal>,
20 serializer: S,
21 ) -> Result<S::Ok, S::Error> {
22 match value {
23 Some(v) => serializer.serialize_str(&v.to_string()),
24 None => serializer.serialize_str(""),
25 }
26 }
27
28 pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
29 deserializer: D,
30 ) -> Result<Option<Decimal>, D::Error> {
31 let s = String::deserialize(deserializer)?;
32 if s.is_empty() {
33 Ok(None)
34 } else {
35 s.parse::<Decimal>()
36 .map(Some)
37 .map_err(serde::de::Error::custom)
38 }
39 }
40}
41
42#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, IntoPrimitive)]
44#[repr(i32)]
45pub enum TriggerPriceType {
46 Spread = 1,
48 Percent = 2,
50 #[num_enum(catch_all)]
52 Unknown(i32),
53}
54
55impl Default for TriggerPriceType {
56 fn default() -> Self {
57 Self::Unknown(0)
58 }
59}
60
61impl Serialize for TriggerPriceType {
62 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63 i32::from(*self).serialize(serializer)
64 }
65}
66
67impl<'de> Deserialize<'de> for TriggerPriceType {
68 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
69 Ok(Self::from(i32::deserialize(deserializer)?))
70 }
71}
72
73#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, IntoPrimitive)]
75#[repr(i32)]
76pub enum GridTimeInForce {
77 Day = 0,
79 GoodTilCanceled = 1,
81 GoodTilDate = 6,
83 #[num_enum(catch_all)]
85 Unknown(i32),
86}
87
88impl Default for GridTimeInForce {
89 fn default() -> Self {
90 Self::Day
91 }
92}
93
94impl Serialize for GridTimeInForce {
95 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
96 i32::from(*self).serialize(serializer)
97 }
98}
99
100impl<'de> Deserialize<'de> for GridTimeInForce {
101 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
102 Ok(Self::from(i32::deserialize(deserializer)?))
103 }
104}
105
106#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, IntoPrimitive)]
108#[repr(i32)]
109pub enum GridLimitEvent {
110 Ignore = 1,
112 CloseAtLast = 2,
114 #[num_enum(catch_all)]
116 Unknown(i32),
117}
118
119impl Default for GridLimitEvent {
120 fn default() -> Self {
121 Self::Unknown(0)
122 }
123}
124
125impl Serialize for GridLimitEvent {
126 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
127 i32::from(*self).serialize(serializer)
128 }
129}
130
131impl<'de> Deserialize<'de> for GridLimitEvent {
132 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
133 Ok(Self::from(i32::deserialize(deserializer)?))
134 }
135}
136
137#[derive(Debug, Clone, Default, Serialize, Deserialize)]
143pub struct GridTradeRule {
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub submitted_base_price: Option<Decimal>,
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub upper_limit_price: Option<Decimal>,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub lower_limit_price: Option<Decimal>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub trigger_price_type: Option<TriggerPriceType>,
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub trigger_spread_up: Option<Decimal>,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub trigger_spread_down: Option<Decimal>,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub trigger_percent_up: Option<Decimal>,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub trigger_percent_down: Option<Decimal>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub multiple_trigger: Option<bool>,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub time_in_force: Option<GridTimeInForce>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub upper_limit_quantity: Option<Decimal>,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub lower_limit_quantity: Option<Decimal>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub expire_time: Option<i64>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub upper_limit_event: Option<GridLimitEvent>,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub lower_limit_event: Option<GridLimitEvent>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub trigger_sell_depth: Option<i32>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub trigger_buy_depth: Option<i32>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub trigger_quantity: Option<Decimal>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub support_shortsell: Option<bool>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub rth: Option<i32>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub grid_order_type_up: Option<String>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub grid_order_type_down: Option<String>,
210}
211
212#[derive(Debug, Clone, Copy)]
216pub enum GridTrigger {
217 Percent {
219 up: Decimal,
221 down: Decimal,
223 },
224 Spread {
226 up: Decimal,
228 down: Decimal,
230 },
231}
232
233impl GridTradeRule {
234 #[allow(clippy::too_many_arguments)]
238 pub fn new(
239 base_price: Decimal,
240 upper_price: Decimal,
241 lower_price: Decimal,
242 trigger: GridTrigger,
243 quantity: Decimal,
244 upper_quantity: Decimal,
245 lower_quantity: Decimal,
246 time_in_force: GridTimeInForce,
247 ) -> Self {
248 let mut rule = GridTradeRule {
249 submitted_base_price: Some(base_price),
250 upper_limit_price: Some(upper_price),
251 lower_limit_price: Some(lower_price),
252 trigger_quantity: Some(quantity),
253 upper_limit_quantity: Some(upper_quantity),
254 lower_limit_quantity: Some(lower_quantity),
255 time_in_force: Some(time_in_force),
256 ..Default::default()
257 };
258 match trigger {
259 GridTrigger::Percent { up, down } => {
260 rule.trigger_price_type = Some(TriggerPriceType::Percent);
261 rule.trigger_percent_up = Some(up);
262 rule.trigger_percent_down = Some(down);
263 }
264 GridTrigger::Spread { up, down } => {
265 rule.trigger_price_type = Some(TriggerPriceType::Spread);
266 rule.trigger_spread_up = Some(up);
267 rule.trigger_spread_down = Some(down);
268 }
269 }
270 rule
271 }
272
273 #[must_use]
275 pub fn limit_events(mut self, upper: GridLimitEvent, lower: GridLimitEvent) -> Self {
276 self.upper_limit_event = Some(upper);
277 self.lower_limit_event = Some(lower);
278 self
279 }
280
281 #[must_use]
283 pub fn depths(mut self, sell: i32, buy: i32) -> Self {
284 self.trigger_sell_depth = Some(sell);
285 self.trigger_buy_depth = Some(buy);
286 self
287 }
288
289 #[must_use]
291 pub fn order_types(mut self, up: impl Into<String>, down: impl Into<String>) -> Self {
292 self.grid_order_type_up = Some(up.into());
293 self.grid_order_type_down = Some(down.into());
294 self
295 }
296
297 #[must_use]
299 pub fn multiple_trigger(mut self, value: bool) -> Self {
300 self.multiple_trigger = Some(value);
301 self
302 }
303
304 #[must_use]
306 pub fn support_shortsell(mut self, value: bool) -> Self {
307 self.support_shortsell = Some(value);
308 self
309 }
310
311 #[must_use]
313 pub fn rth(mut self, value: i32) -> Self {
314 self.rth = Some(value);
315 self
316 }
317
318 #[must_use]
320 pub fn expire_time(mut self, unix_seconds: i64) -> Self {
321 self.expire_time = Some(unix_seconds);
322 self
323 }
324}
325
326#[derive(Debug, Clone, Default, Serialize, Deserialize)]
332#[serde(default)]
333pub struct GridOrder {
334 pub order_id: String,
336 pub symbol: String,
338 pub stock_name: String,
340 pub market: String,
342 pub status: String,
344 pub grid_status: String,
346 #[serde(with = "opt_decimal_string")]
348 pub submitted_base_price: Option<Decimal>,
349 #[serde(with = "opt_decimal_string")]
351 pub current_base_price: Option<Decimal>,
352 #[serde(with = "opt_decimal_string")]
354 pub pre_trigger_base_price: Option<Decimal>,
355 #[serde(with = "opt_decimal_string")]
357 pub post_trigger_base_price: Option<Decimal>,
358 #[serde(with = "opt_decimal_string")]
360 pub upper_limit_price: Option<Decimal>,
361 #[serde(with = "opt_decimal_string")]
363 pub lower_limit_price: Option<Decimal>,
364 pub trigger_price_type: TriggerPriceType,
366 #[serde(with = "opt_decimal_string")]
368 pub trigger_spread_up: Option<Decimal>,
369 #[serde(with = "opt_decimal_string")]
371 pub trigger_spread_down: Option<Decimal>,
372 #[serde(with = "opt_decimal_string")]
374 pub trigger_percent_up: Option<Decimal>,
375 #[serde(with = "opt_decimal_string")]
377 pub trigger_percent_down: Option<Decimal>,
378 #[serde(with = "opt_decimal_string")]
380 pub pullback_percent: Option<Decimal>,
381 #[serde(with = "opt_decimal_string")]
383 pub pullback_spread: Option<Decimal>,
384 #[serde(with = "opt_decimal_string")]
386 pub rebound_percent: Option<Decimal>,
387 #[serde(with = "opt_decimal_string")]
389 pub rebound_spread: Option<Decimal>,
390 pub trigger_sell_order_type: String,
392 pub trigger_buy_order_type: String,
394 pub trigger_sell_depth: i32,
396 pub trigger_buy_depth: i32,
398 #[serde(with = "opt_decimal_string")]
400 pub trigger_quantity: Option<Decimal>,
401 #[serde(with = "opt_decimal_string")]
403 pub trigger_sell_quantity: Option<Decimal>,
404 #[serde(with = "opt_decimal_string")]
406 pub trigger_buy_quantity: Option<Decimal>,
407 #[serde(with = "opt_decimal_string")]
409 pub upper_limit_quantity: Option<Decimal>,
410 #[serde(with = "opt_decimal_string")]
412 pub lower_limit_quantity: Option<Decimal>,
413 pub upper_limit_event: GridLimitEvent,
415 pub lower_limit_event: GridLimitEvent,
417 pub multiple_trigger: bool,
419 pub trigger_times: i32,
421 #[serde(with = "opt_decimal_string")]
423 pub total_buy_quantity: Option<Decimal>,
424 #[serde(with = "opt_decimal_string")]
426 pub total_sell_quantity: Option<Decimal>,
427 #[serde(with = "opt_decimal_string")]
429 pub total_profit_balance: Option<Decimal>,
430 pub settlement_currency: String,
432 pub time_in_force: GridTimeInForce,
434 pub gtd: String,
436 #[serde(
438 deserialize_with = "serde_utils::timestamp_opt::deserialize",
439 serialize_with = "serde_utils::rfc3339_opt::serialize"
440 )]
441 pub created_at: Option<OffsetDateTime>,
442 pub rth: i32,
444 pub support_shortsell: bool,
446 pub grid_order_type_up: String,
448 pub grid_order_type_down: String,
450}
451
452#[derive(Debug, Clone, Default, Serialize, Deserialize)]
454#[serde(default)]
455pub struct GridOrderSubOrder {
456 pub id: String,
458 #[serde(with = "opt_decimal_string")]
460 pub price: Option<Decimal>,
461 pub order_type: String,
463 #[serde(with = "opt_decimal_string")]
465 pub quantity: Option<Decimal>,
466 #[serde(with = "opt_decimal_string")]
468 pub executed_qty: Option<Decimal>,
469 pub action: i32,
471 pub status: String,
473 #[serde(
475 deserialize_with = "serde_utils::timestamp_opt::deserialize",
476 serialize_with = "serde_utils::rfc3339_opt::serialize"
477 )]
478 pub submitted_at: Option<OffsetDateTime>,
479 pub rth: i32,
481}
482
483#[derive(Debug, Clone, Default, Serialize, Deserialize)]
485#[serde(default)]
486pub struct GridOrderHistory {
487 pub history_id: String,
489 #[serde(
491 deserialize_with = "serde_utils::timestamp_opt::deserialize",
492 serialize_with = "serde_utils::rfc3339_opt::serialize"
493 )]
494 pub created_at: Option<OffsetDateTime>,
495 pub status: String,
497 pub suspend_reason: String,
499 pub reason: String,
501}
502
503#[derive(Debug, Clone, Default, Serialize, Deserialize)]
505#[serde(default)]
506pub struct GridOrderDetail {
507 pub order_id: String,
509 pub symbol: String,
511 pub stock_name: String,
513 pub status: String,
515 pub grid_status: String,
517 pub suspend_reason: String,
519 pub sleeping_reason: String,
521 #[serde(with = "opt_decimal_string")]
523 pub submitted_base_price: Option<Decimal>,
524 #[serde(with = "opt_decimal_string")]
526 pub current_base_price: Option<Decimal>,
527 #[serde(with = "opt_decimal_string")]
529 pub upper_limit_price: Option<Decimal>,
530 #[serde(with = "opt_decimal_string")]
532 pub lower_limit_price: Option<Decimal>,
533 pub trigger_price_type: TriggerPriceType,
535 #[serde(with = "opt_decimal_string")]
537 pub trigger_spread_up: Option<Decimal>,
538 #[serde(with = "opt_decimal_string")]
540 pub trigger_spread_down: Option<Decimal>,
541 #[serde(with = "opt_decimal_string")]
543 pub trigger_percent_up: Option<Decimal>,
544 #[serde(with = "opt_decimal_string")]
546 pub trigger_percent_down: Option<Decimal>,
547 #[serde(with = "opt_decimal_string")]
549 pub pullback_percent: Option<Decimal>,
550 #[serde(with = "opt_decimal_string")]
552 pub pullback_spread: Option<Decimal>,
553 #[serde(with = "opt_decimal_string")]
555 pub rebound_percent: Option<Decimal>,
556 #[serde(with = "opt_decimal_string")]
558 pub rebound_spread: Option<Decimal>,
559 pub multiple_trigger: bool,
561 pub time_in_force: GridTimeInForce,
563 #[serde(with = "opt_decimal_string")]
565 pub trigger_quantity: Option<Decimal>,
566 #[serde(with = "opt_decimal_string")]
568 pub trigger_sell_quantity: Option<Decimal>,
569 #[serde(with = "opt_decimal_string")]
571 pub trigger_buy_quantity: Option<Decimal>,
572 #[serde(with = "opt_decimal_string")]
574 pub upper_limit_quantity: Option<Decimal>,
575 #[serde(with = "opt_decimal_string")]
577 pub lower_limit_quantity: Option<Decimal>,
578 pub upper_limit_event: GridLimitEvent,
580 pub lower_limit_event: GridLimitEvent,
582 pub trigger_sell_depth: i32,
584 pub trigger_buy_depth: i32,
586 #[serde(
588 deserialize_with = "serde_utils::timestamp_opt::deserialize",
589 serialize_with = "serde_utils::rfc3339_opt::serialize"
590 )]
591 pub created_at: Option<OffsetDateTime>,
592 #[serde(
594 deserialize_with = "serde_utils::timestamp_opt::deserialize",
595 serialize_with = "serde_utils::rfc3339_opt::serialize"
596 )]
597 pub updated_at: Option<OffsetDateTime>,
598 pub settlement_currency: String,
600 #[serde(
602 deserialize_with = "serde_utils::timestamp_opt::deserialize",
603 serialize_with = "serde_utils::rfc3339_opt::serialize"
604 )]
605 pub expire_time: Option<OffsetDateTime>,
606 pub gtd: String,
608 pub grid_sub_orders: Vec<GridOrderSubOrder>,
610 pub sub_has_more: bool,
612 pub grid_order_history: Vec<GridOrderHistory>,
614 pub history_has_more: bool,
616 pub support_shortsell: bool,
618 pub rth: i32,
620 pub grid_order_type_up: String,
622 pub grid_order_type_down: String,
624}
625
626#[derive(Debug, Clone, Default, Serialize, Deserialize)]
628#[serde(default)]
629pub struct TriggerOrder {
630 pub id: String,
632 pub status: String,
634 pub name: String,
636 pub symbol: String,
638 #[serde(with = "opt_decimal_string")]
640 pub price: Option<Decimal>,
641 #[serde(with = "opt_decimal_string")]
643 pub quantity: Option<Decimal>,
644 #[serde(with = "opt_decimal_string")]
646 pub executed_price: Option<Decimal>,
647 #[serde(with = "opt_decimal_string")]
649 pub executed_qty: Option<Decimal>,
650 #[serde(
652 deserialize_with = "serde_utils::timestamp_opt::deserialize",
653 serialize_with = "serde_utils::rfc3339_opt::serialize"
654 )]
655 pub submitted_at: Option<OffsetDateTime>,
656 pub action: i32,
658 pub order_type: String,
660 #[serde(with = "opt_decimal_string")]
662 pub trigger_price: Option<Decimal>,
663 pub msg: String,
665 pub currency: String,
667 #[serde(with = "opt_decimal_string")]
669 pub last_done: Option<Decimal>,
670 #[serde(
672 deserialize_with = "serde_utils::timestamp_opt::deserialize",
673 serialize_with = "serde_utils::rfc3339_opt::serialize"
674 )]
675 pub updated_at: Option<OffsetDateTime>,
676 pub time_in_force: GridTimeInForce,
678 pub gtd: String,
680 #[serde(
682 deserialize_with = "serde_utils::timestamp_opt::deserialize",
683 serialize_with = "serde_utils::rfc3339_opt::serialize"
684 )]
685 pub trigger_at: Option<OffsetDateTime>,
686 pub trigger_status: i32,
688}
689
690#[derive(Debug, Clone, Default, Serialize, Deserialize)]
692#[serde(default)]
693pub struct GridBidSize {
694 #[serde(with = "opt_decimal_string")]
696 pub str_proceed: Option<Decimal>,
697 #[serde(with = "opt_decimal_string")]
699 pub end_proceed: Option<Decimal>,
700 #[serde(with = "opt_decimal_string")]
702 pub bid_size: Option<Decimal>,
703}
704
705#[derive(Debug, Clone, Default, Serialize, Deserialize)]
708#[serde(default)]
709pub struct GridChannelInfo {
710 pub strategy_granted: bool,
712 pub support_rth: bool,
714 pub currency: String,
716 pub settlement_currency: Vec<String>,
718}
719
720#[derive(Debug, Clone, Default, Serialize, Deserialize)]
726#[serde(default)]
727pub struct GridSymbolInfo {
728 pub name: String,
730 #[serde(with = "opt_decimal_string")]
732 pub last_done: Option<Decimal>,
733 #[serde(with = "opt_decimal_string")]
735 pub lot_size: Option<Decimal>,
736 #[serde(with = "opt_decimal_string")]
738 pub buy_lot_size: Option<Decimal>,
739 #[serde(with = "opt_decimal_string")]
741 pub sell_lot_size: Option<Decimal>,
742 pub bid_sizes: Vec<GridBidSize>,
744 #[serde(alias = "channel_infos")]
750 pub channel_info: GridChannelInfo,
751}