1use num_enum::{FromPrimitive, IntoPrimitive};
2use rust_decimal::Decimal;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use strum_macros::{Display, EnumString};
5use time::{Date, OffsetDateTime};
6
7use crate::{Market, serde_utils};
8
9#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
11#[allow(clippy::upper_case_acronyms)]
12pub enum OrderType {
13 Unknown,
15 #[strum(serialize = "LO")]
17 LO,
18 #[strum(serialize = "ELO")]
20 ELO,
21 #[strum(serialize = "MO")]
23 MO,
24 #[strum(serialize = "AO")]
26 AO,
27 #[strum(serialize = "ALO")]
29 ALO,
30 #[strum(serialize = "ODD")]
32 ODD,
33 #[strum(serialize = "LIT")]
35 LIT,
36 #[strum(serialize = "MIT")]
38 MIT,
39 #[strum(serialize = "TSLPAMT")]
41 TSLPAMT,
42 #[strum(serialize = "TSLPPCT")]
44 TSLPPCT,
45 #[strum(serialize = "TSMAMT")]
47 TSMAMT,
48 #[strum(serialize = "TSMPCT")]
50 TSMPCT,
51 #[strum(serialize = "SLO")]
53 SLO,
54}
55
56#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
58pub enum OrderStatus {
59 Unknown,
61 #[strum(serialize = "NotReported")]
63 NotReported,
64 #[strum(serialize = "ReplacedNotReported")]
66 ReplacedNotReported,
67 #[strum(serialize = "ProtectedNotReported")]
69 ProtectedNotReported,
70 #[strum(serialize = "VarietiesNotReported")]
72 VarietiesNotReported,
73 #[strum(serialize = "FilledStatus")]
75 Filled,
76 #[strum(serialize = "WaitToNew")]
78 WaitToNew,
79 #[strum(serialize = "NewStatus")]
81 New,
82 #[strum(serialize = "WaitToReplace")]
84 WaitToReplace,
85 #[strum(serialize = "PendingReplaceStatus")]
87 PendingReplace,
88 #[strum(serialize = "ReplacedStatus")]
90 Replaced,
91 #[strum(serialize = "PartialFilledStatus")]
93 PartialFilled,
94 #[strum(serialize = "WaitToCancel")]
96 WaitToCancel,
97 #[strum(serialize = "PendingCancelStatus")]
99 PendingCancel,
100 #[strum(serialize = "RejectedStatus")]
102 Rejected,
103 #[strum(serialize = "CanceledStatus")]
105 Canceled,
106 #[strum(serialize = "ExpiredStatus")]
108 Expired,
109 #[strum(serialize = "PartialWithdrawal")]
111 PartialWithdrawal,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct Execution {
117 pub order_id: String,
119 pub trade_id: String,
121 pub symbol: String,
123 #[serde(
125 serialize_with = "time::serde::rfc3339::serialize",
126 deserialize_with = "serde_utils::timestamp::deserialize"
127 )]
128 pub trade_done_at: OffsetDateTime,
129 pub quantity: Decimal,
131 pub price: Decimal,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct AllExecutionsResponse {
138 pub has_more: bool,
140 #[serde(default)]
142 pub trades: Vec<Execution>,
143}
144
145#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
147pub enum OrderSide {
148 Unknown,
150 #[strum(serialize = "Buy")]
152 Buy,
153 #[strum(serialize = "Sell")]
155 Sell,
156}
157
158#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
160pub enum TriggerPriceType {
161 Unknown,
163 #[strum(serialize = "LIT")]
165 LimitIfTouched,
166 #[strum(serialize = "MIT")]
168 MarketIfTouched,
169}
170
171#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
173pub enum OrderTag {
174 Unknown,
176 #[strum(serialize = "Normal")]
178 Normal,
179 #[strum(serialize = "Gtc")]
181 LongTerm,
182 #[strum(serialize = "Grey")]
184 Grey,
185}
186
187#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
189pub enum TimeInForceType {
190 Unknown,
192 #[strum(serialize = "Day")]
194 Day,
195 #[strum(serialize = "GTC")]
197 GoodTilCanceled,
198 #[strum(serialize = "GTD")]
200 GoodTilDate,
201}
202
203#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
205pub enum TriggerStatus {
206 Unknown,
208 #[strum(serialize = "DEACTIVE")]
210 Deactive,
211 #[strum(serialize = "ACTIVE")]
213 Active,
214 #[strum(serialize = "RELEASED")]
216 Released,
217}
218
219#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
221pub enum OutsideRTH {
222 Unknown,
224 #[strum(serialize = "RTH_ONLY")]
226 RTHOnly,
227 #[strum(serialize = "ANY_TIME")]
229 AnyTime,
230 #[strum(serialize = "OVERNIGHT")]
232 Overnight,
233 #[strum(serialize = "OPTION_PRE_MARKET")]
235 OptionPreMarket,
236}
237
238#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
240pub enum AttachedOrderType {
241 Unknown,
243 #[strum(serialize = "PROFIT_TAKER")]
245 ProfitTaker,
246 #[strum(serialize = "STOP_LOSS")]
248 StopLoss,
249 #[strum(serialize = "BRACKET")]
251 Bracket,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct AttachedOrderDetail {
257 pub order_id: String,
259 pub attached_type_display: AttachedOrderType,
261 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
263 pub trigger_price: Option<Decimal>,
264 pub quantity: Decimal,
266 pub executed_qty: Decimal,
268 pub status: OrderStatus,
270 #[serde(
272 serialize_with = "time::serde::rfc3339::serialize",
273 deserialize_with = "serde_utils::timestamp::deserialize"
274 )]
275 pub updated_at: OffsetDateTime,
276 pub withdrawn: bool,
278 #[serde(with = "serde_utils::date_opt")]
280 pub gtd: Option<Date>,
281 pub time_in_force: TimeInForceType,
283 pub counter_id: String,
285 #[serde(with = "serde_utils::trigger_status")]
287 pub trigger_status: Option<TriggerStatus>,
288 pub executed_amount: Decimal,
290 pub tag: OrderTag,
292 #[serde(
294 serialize_with = "time::serde::rfc3339::serialize",
295 deserialize_with = "serde_utils::timestamp::deserialize"
296 )]
297 pub submitted_at: OffsetDateTime,
298 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
300 pub executed_price: Option<Decimal>,
301 #[serde(with = "serde_utils::outside_rth")]
303 pub force_only_rth: Option<OutsideRTH>,
304 pub reviewed: bool,
306 pub activate_order_type: OrderType,
308 #[serde(with = "serde_utils::outside_rth")]
310 pub activate_rth: Option<OutsideRTH>,
311 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
313 pub submit_price: Option<Decimal>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct Order {
319 pub order_id: String,
321 pub status: OrderStatus,
323 pub stock_name: String,
325 pub quantity: Decimal,
327 pub executed_quantity: Decimal,
329 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
331 pub price: Option<Decimal>,
332 #[serde(with = "serde_utils::decimal_opt_0_is_none")]
334 pub executed_price: Option<Decimal>,
335 #[serde(
337 serialize_with = "time::serde::rfc3339::serialize",
338 deserialize_with = "serde_utils::timestamp::deserialize"
339 )]
340 pub submitted_at: OffsetDateTime,
341 pub side: OrderSide,
343 pub symbol: String,
345 pub order_type: OrderType,
347 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
349 pub last_done: Option<Decimal>,
350 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
352 pub trigger_price: Option<Decimal>,
353 pub msg: String,
355 pub tag: OrderTag,
357 pub time_in_force: TimeInForceType,
359 #[serde(with = "serde_utils::date_opt")]
361 pub expire_date: Option<Date>,
362 #[serde(
364 deserialize_with = "serde_utils::timestamp_opt::deserialize",
365 serialize_with = "serde_utils::rfc3339_opt::serialize"
366 )]
367 pub updated_at: Option<OffsetDateTime>,
368 #[serde(
370 deserialize_with = "serde_utils::timestamp_opt::deserialize",
371 serialize_with = "serde_utils::rfc3339_opt::serialize"
372 )]
373 pub trigger_at: Option<OffsetDateTime>,
374 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
376 pub trailing_amount: Option<Decimal>,
377 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
379 pub trailing_percent: Option<Decimal>,
380 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
382 pub limit_offset: Option<Decimal>,
383 #[serde(with = "serde_utils::trigger_status")]
385 pub trigger_status: Option<TriggerStatus>,
386 pub currency: String,
388 #[serde(with = "serde_utils::outside_rth")]
390 pub outside_rth: Option<OutsideRTH>,
391 #[serde(with = "serde_utils::int32_opt_0_is_none")]
393 pub limit_depth_level: Option<i32>,
394 #[serde(with = "serde_utils::int32_opt_0_is_none")]
396 pub trigger_count: Option<i32>,
397 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
399 pub monitor_price: Option<Decimal>,
400 pub remark: String,
402 #[serde(default)]
404 pub attached_orders: Vec<AttachedOrderDetail>,
405}
406
407#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
409pub enum CommissionFreeStatus {
410 Unknown,
412 None,
414 Calculated,
416 Pending,
418 Ready,
420}
421
422#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
424pub enum DeductionStatus {
425 Unknown,
427 #[strum(serialize = "NONE")]
429 None,
430 #[strum(serialize = "NO_DATA")]
432 NoData,
433 #[strum(serialize = "PENDING")]
435 Pending,
436 #[strum(serialize = "DONE")]
438 Done,
439}
440
441#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
443pub enum ChargeCategoryCode {
444 Unknown,
446 #[strum(serialize = "BROKER_FEES")]
448 Broker,
449 #[strum(serialize = "THIRD_FEES")]
451 Third,
452}
453
454#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct OrderHistoryDetail {
457 #[serde(with = "serde_utils::decimal_empty_is_0")]
460 pub price: Decimal,
461 #[serde(with = "serde_utils::decimal_empty_is_0")]
464 pub quantity: Decimal,
465 pub status: OrderStatus,
467 pub msg: String,
469 #[serde(
471 serialize_with = "time::serde::rfc3339::serialize",
472 deserialize_with = "serde_utils::timestamp::deserialize"
473 )]
474 pub time: OffsetDateTime,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
479pub struct OrderChargeFee {
480 pub code: String,
482 pub name: String,
484 #[serde(with = "serde_utils::decimal_empty_is_0")]
486 pub amount: Decimal,
487 pub currency: String,
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct OrderChargeItem {
494 pub code: ChargeCategoryCode,
496 pub name: String,
498 pub fees: Vec<OrderChargeFee>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct OrderChargeDetail {
505 pub total_amount: Decimal,
507 pub currency: String,
509 pub items: Vec<OrderChargeItem>,
511}
512
513#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct OrderDetail {
516 pub order_id: String,
518 pub status: OrderStatus,
520 pub stock_name: String,
522 pub quantity: Decimal,
524 pub executed_quantity: Decimal,
526 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
528 pub price: Option<Decimal>,
529 #[serde(with = "serde_utils::decimal_opt_0_is_none")]
531 pub executed_price: Option<Decimal>,
532 #[serde(
534 serialize_with = "time::serde::rfc3339::serialize",
535 deserialize_with = "serde_utils::timestamp::deserialize"
536 )]
537 pub submitted_at: OffsetDateTime,
538 pub side: OrderSide,
540 pub symbol: String,
542 pub order_type: OrderType,
544 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
546 pub last_done: Option<Decimal>,
547 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
549 pub trigger_price: Option<Decimal>,
550 pub msg: String,
552 pub tag: OrderTag,
554 pub time_in_force: TimeInForceType,
556 #[serde(with = "serde_utils::date_opt")]
558 pub expire_date: Option<Date>,
559 #[serde(
561 deserialize_with = "serde_utils::timestamp_opt::deserialize",
562 serialize_with = "serde_utils::rfc3339_opt::serialize"
563 )]
564 pub updated_at: Option<OffsetDateTime>,
565 #[serde(
567 deserialize_with = "serde_utils::timestamp_opt::deserialize",
568 serialize_with = "serde_utils::rfc3339_opt::serialize"
569 )]
570 pub trigger_at: Option<OffsetDateTime>,
571 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
573 pub trailing_amount: Option<Decimal>,
574 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
576 pub trailing_percent: Option<Decimal>,
577 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
579 pub limit_offset: Option<Decimal>,
580 #[serde(with = "serde_utils::trigger_status")]
582 pub trigger_status: Option<TriggerStatus>,
583 pub currency: String,
585 #[serde(with = "serde_utils::outside_rth")]
587 pub outside_rth: Option<OutsideRTH>,
588 #[serde(with = "serde_utils::int32_opt_0_is_none")]
590 pub limit_depth_level: Option<i32>,
591 #[serde(with = "serde_utils::int32_opt_0_is_none")]
593 pub trigger_count: Option<i32>,
594 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
596 pub monitor_price: Option<Decimal>,
597 pub remark: String,
599 pub free_status: CommissionFreeStatus,
601 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
603 pub free_amount: Option<Decimal>,
604 #[serde(with = "serde_utils::symbol_opt")]
606 pub free_currency: Option<String>,
607 pub deductions_status: DeductionStatus,
609 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
611 pub deductions_amount: Option<Decimal>,
612 #[serde(with = "serde_utils::symbol_opt")]
614 pub deductions_currency: Option<String>,
615 pub platform_deducted_status: DeductionStatus,
617 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
619 pub platform_deducted_amount: Option<Decimal>,
620 #[serde(with = "serde_utils::symbol_opt")]
622 pub platform_deducted_currency: Option<String>,
623 pub history: Vec<OrderHistoryDetail>,
625 pub charge_detail: Option<OrderChargeDetail>,
627 #[serde(default)]
629 pub attached_orders: Vec<AttachedOrderDetail>,
630}
631
632#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct CashInfo {
635 pub withdraw_cash: Decimal,
637 pub available_cash: Decimal,
639 pub frozen_cash: Decimal,
641 pub settling_cash: Decimal,
643 pub currency: String,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
649pub struct FrozenTransactionFee {
650 pub currency: String,
652 pub frozen_transaction_fee: Decimal,
654}
655
656#[derive(Debug, Clone, Serialize, Deserialize)]
658pub struct AccountBalance {
659 pub total_cash: Decimal,
661 pub max_finance_amount: Decimal,
663 pub remaining_finance_amount: Decimal,
665 #[serde(with = "serde_utils::risk_level")]
667 pub risk_level: i32,
668 pub margin_call: Decimal,
670 pub currency: String,
672 #[serde(default)]
674 pub cash_infos: Vec<CashInfo>,
675 #[serde(with = "serde_utils::decimal_empty_is_0")]
677 pub net_assets: Decimal,
678 #[serde(with = "serde_utils::decimal_empty_is_0")]
680 pub init_margin: Decimal,
681 #[serde(with = "serde_utils::decimal_empty_is_0")]
683 pub maintenance_margin: Decimal,
684 #[serde(with = "serde_utils::decimal_empty_is_0")]
686 pub buy_power: Decimal,
687 pub frozen_transaction_fees: Vec<FrozenTransactionFee>,
689}
690
691#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, IntoPrimitive)]
693#[repr(i32)]
694pub enum BalanceType {
695 #[num_enum(default)]
697 Unknown = 0,
698 Cash = 1,
700 Stock = 2,
702 Fund = 3,
704}
705
706impl Serialize for BalanceType {
707 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
708 let value: i32 = (*self).into();
709 value.serialize(serializer)
710 }
711}
712
713impl<'de> Deserialize<'de> for BalanceType {
714 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
715 let value = i32::deserialize(deserializer)?;
716 Ok(BalanceType::from(value))
717 }
718}
719
720#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, Serialize)]
722#[repr(i32)]
723pub enum CashFlowDirection {
724 #[num_enum(default)]
726 Unknown,
727 Out = 1,
729 In = 2,
731}
732
733impl<'de> Deserialize<'de> for CashFlowDirection {
734 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
735 let value = i32::deserialize(deserializer)?;
736 Ok(CashFlowDirection::from(value))
737 }
738}
739
740#[derive(Debug, Clone, Serialize, Deserialize)]
742pub struct CashFlow {
743 pub transaction_flow_name: String,
745 pub direction: CashFlowDirection,
747 pub business_type: BalanceType,
749 pub balance: Decimal,
751 pub currency: String,
753 #[serde(
755 serialize_with = "time::serde::rfc3339::serialize",
756 deserialize_with = "serde_utils::timestamp::deserialize"
757 )]
758 pub business_time: OffsetDateTime,
759 #[serde(with = "serde_utils::symbol_opt")]
761 pub symbol: Option<String>,
762 pub description: String,
764}
765
766#[derive(Debug, Clone, Serialize, Deserialize)]
768pub struct FundPositionsResponse {
769 #[serde(rename = "list")]
771 pub channels: Vec<FundPositionChannel>,
772}
773
774#[derive(Debug, Clone, Serialize, Deserialize)]
776pub struct FundPositionChannel {
777 pub account_channel: String,
779
780 #[serde(default, rename = "fund_info")]
782 pub positions: Vec<FundPosition>,
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize)]
787pub struct FundPosition {
788 pub symbol: String,
790 #[serde(with = "serde_utils::decimal_empty_is_0")]
792 pub current_net_asset_value: Decimal,
793 #[serde(
795 serialize_with = "time::serde::rfc3339::serialize",
796 deserialize_with = "serde_utils::timestamp::deserialize"
797 )]
798 pub net_asset_value_day: OffsetDateTime,
799 pub symbol_name: String,
801 pub currency: String,
803 #[serde(with = "serde_utils::decimal_empty_is_0")]
805 pub cost_net_asset_value: Decimal,
806 #[serde(with = "serde_utils::decimal_empty_is_0")]
808 pub holding_units: Decimal,
809}
810
811#[derive(Debug, Clone, Serialize, Deserialize)]
813pub struct StockPositionsResponse {
814 #[serde(rename = "list")]
816 pub channels: Vec<StockPositionChannel>,
817}
818
819#[derive(Debug, Clone, Serialize, Deserialize)]
821pub struct StockPositionChannel {
822 pub account_channel: String,
824
825 #[serde(default, rename = "stock_info")]
827 pub positions: Vec<StockPosition>,
828}
829
830#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct StockPosition {
833 pub symbol: String,
835 pub symbol_name: String,
837 pub quantity: Decimal,
839 pub available_quantity: Decimal,
841 pub currency: String,
843 #[serde(with = "serde_utils::decimal_empty_is_0")]
846 pub cost_price: Decimal,
847 pub market: Market,
849 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
851 pub init_quantity: Option<Decimal>,
852}
853
854#[derive(Debug, Clone, Serialize, Deserialize)]
856pub struct MarginRatio {
857 pub im_factor: Decimal,
859 pub mm_factor: Decimal,
861 pub fm_factor: Decimal,
863}
864
865impl_serde_for_enum_string!(
866 OrderType,
867 OrderStatus,
868 OrderSide,
869 TriggerPriceType,
870 OrderTag,
871 TimeInForceType,
872 TriggerStatus,
873 OutsideRTH,
874 CommissionFreeStatus,
875 DeductionStatus,
876 ChargeCategoryCode
877);
878impl_serde_for_enum_string!(AttachedOrderType);
879impl_default_for_enum_string!(AttachedOrderType);
880impl_default_for_enum_string!(
881 OrderType,
882 OrderStatus,
883 OrderSide,
884 TriggerPriceType,
885 OrderTag,
886 TimeInForceType,
887 TriggerStatus,
888 OutsideRTH,
889 CommissionFreeStatus,
890 DeductionStatus,
891 ChargeCategoryCode
892);
893
894#[derive(Debug, Clone, Default)]
906pub struct GetUSHistoryOrders {
907 pub symbol: Option<String>,
910 pub side: OrderSide,
912 pub start_at: i64,
914 pub end_at: i64,
916 pub query_type: i32,
918 pub page: i32,
920 pub limit: i32,
922}
923
924pub type QueryUSOrdersOptions = GetUSHistoryOrders;
926
927#[derive(Debug, Clone, Serialize)]
929pub(crate) struct USQueryOrdersBody {
930 pub account_channel: String,
931 pub action: i32,
932 pub start_at: f64,
933 pub end_at: f64,
934 pub counter_ids: Vec<String>,
935 pub security_types: Vec<String>,
936 pub query_type: i32,
937 pub page: i32,
938 pub limit: i32,
939 pub query_version: f64,
940}
941
942#[derive(Debug, Clone, Serialize, Deserialize, Default)]
944pub struct QueryUSOrdersResponse {
945 #[serde(default)]
948 pub orders: Vec<serde_json::Value>,
949 #[serde(default)]
951 pub total_count: i32,
952}
953
954#[derive(Debug, Clone, Serialize, Deserialize, Default)]
956pub struct USOrderHistory {
957 #[serde(default)]
958 pub exec_type: i32,
959 #[serde(default)]
960 pub status: String,
961 #[serde(default)]
962 pub price: String,
963 #[serde(default)]
964 pub qty: String,
965 #[serde(default)]
966 pub time: String,
967 #[serde(default)]
968 pub msg: String,
969 #[serde(default)]
970 pub is_manually: bool,
971 #[serde(default)]
972 pub opp_party_id: String,
973 #[serde(default)]
974 pub trd_match_id: String,
975 #[serde(default)]
976 pub operator: String,
977 #[serde(default)]
978 pub op_entrust_way: String,
979 #[serde(default)]
980 pub cxl_rej_response_to: i32,
981 #[serde(default)]
982 pub withdrawal_reason: String,
983 #[serde(default)]
984 pub opp_name: String,
985 #[serde(default)]
986 pub exec_id: String,
987}
988
989#[derive(Debug, Clone, Serialize, Deserialize, Default)]
991pub struct USButtonControl {
992 #[serde(default)]
993 pub withdraw: i32,
994 #[serde(default)]
995 pub replace: i32,
996 #[serde(default)]
997 pub exceptionable: Vec<String>,
998}
999
1000#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1002pub struct USChargeItem {
1003 #[serde(default)]
1004 pub code: i32,
1005 #[serde(default)]
1006 pub name: String,
1007 #[serde(default)]
1008 pub fees: Vec<String>,
1009}
1010
1011#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1013pub struct USChargeDetail {
1014 #[serde(default)]
1015 pub currency: String,
1016 #[serde(default)]
1017 pub total_amount: String,
1018 #[serde(default)]
1019 pub items: Vec<USChargeItem>,
1020}
1021
1022#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1024pub struct USAttachedOrder {
1025 #[serde(default)]
1026 pub attached_type_display: i32,
1027 #[serde(default)]
1028 pub executed_qty: String,
1029 #[serde(default)]
1030 pub quantity: String,
1031 #[serde(default)]
1032 pub status: String,
1033 #[serde(default)]
1034 pub trigger_price: String,
1035 #[serde(default)]
1036 pub order_id: String,
1037 #[serde(default)]
1038 pub gtd: String,
1039 #[serde(default)]
1040 pub time_in_force: i32,
1041 #[serde(default)]
1042 pub tag: i32,
1043 #[serde(default)]
1044 pub activate_order_type: String,
1045 #[serde(default)]
1046 pub activate_rth: i32,
1047 #[serde(default)]
1048 pub submit_price: String,
1049 #[serde(
1052 default,
1053 rename = "counter_id",
1054 deserialize_with = "crate::utils::counter::deserialize_counter_id_as_symbol"
1055 )]
1056 pub symbol: String,
1057 #[serde(default)]
1058 pub withdrawn: bool,
1059}
1060
1061#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1066pub struct USOrderDetail {
1067 #[serde(default)]
1068 pub id: String,
1069 #[serde(default)]
1070 pub aaid: String,
1071 #[serde(default)]
1072 pub account_channel: String,
1073 #[serde(default)]
1074 pub action: i32,
1075 #[serde(
1078 default,
1079 rename = "counter_id",
1080 deserialize_with = "crate::utils::counter::deserialize_counter_id_as_symbol"
1081 )]
1082 pub symbol: String,
1083 #[serde(
1086 default,
1087 rename = "underlying_counter_id",
1088 deserialize_with = "crate::utils::counter::deserialize_counter_id_as_symbol"
1089 )]
1090 pub underlying_symbol: String,
1091 #[serde(default)]
1092 pub security_type: String,
1093 #[serde(default)]
1094 pub name: String,
1095 #[serde(default)]
1096 pub currency: String,
1097 #[serde(default)]
1098 pub trade_currency: String,
1099 #[serde(default)]
1100 pub order_type: String,
1101 #[serde(default)]
1102 pub status: String,
1103 #[serde(default)]
1104 pub price: String,
1105 #[serde(default)]
1106 pub quantity: String,
1107 #[serde(default)]
1108 pub executed_qty: String,
1109 #[serde(default)]
1110 pub executed_price: String,
1111 #[serde(default)]
1112 pub executed_amount: String,
1113 #[serde(default)]
1114 pub operate_direction: String,
1115 #[serde(default)]
1116 pub time_in_force: i32,
1117 #[serde(default)]
1118 pub gtd: String,
1119 #[serde(default)]
1120 pub tag: i32,
1121 #[serde(default)]
1122 pub msg: String,
1123 #[serde(default)]
1124 pub force_only_rth: i32,
1125 #[serde(default)]
1126 pub submitted_at: String,
1127 #[serde(default)]
1128 pub done_at: String,
1129 #[serde(default)]
1130 pub trigger_price: String,
1131 #[serde(default)]
1132 pub trigger_at: String,
1133 #[serde(default)]
1134 pub trigger_status: i32,
1135 #[serde(default)]
1136 pub trigger_exchange: String,
1137 #[serde(default)]
1138 pub trigger_last_done: String,
1139 #[serde(default)]
1140 pub trigger_count: i32,
1141 #[serde(default)]
1142 pub tailing_amount: String,
1143 #[serde(default)]
1144 pub tailing_percent: String,
1145 #[serde(default)]
1146 pub limit_offset: String,
1147 #[serde(default)]
1148 pub limit_depth_level: i32,
1149 #[serde(default)]
1150 pub market_price: String,
1151 #[serde(default)]
1152 pub submitted_amount: String,
1153 #[serde(default)]
1154 pub estimated_fee: String,
1155 #[serde(default)]
1156 pub free_status: i32,
1157 #[serde(default)]
1158 pub free_amount: String,
1159 #[serde(default)]
1160 pub free_currency: String,
1161 #[serde(default)]
1162 pub deductions_status: i32,
1163 #[serde(default)]
1164 pub deductions_amount: String,
1165 #[serde(default)]
1166 pub deductions_currency: String,
1167 #[serde(default)]
1168 pub platform_deductions_status: i32,
1169 #[serde(default)]
1170 pub platform_deductions_amount: String,
1171 #[serde(default)]
1172 pub platform_deductions_currency: String,
1173 #[serde(default)]
1174 pub display_account: String,
1175 #[serde(default)]
1176 pub settlement_account: String,
1177 #[serde(default)]
1178 pub settlement_channel: String,
1179 #[serde(default)]
1180 pub customer_name: String,
1181 #[serde(default)]
1182 pub real_name: String,
1183 #[serde(default)]
1184 pub en_name: String,
1185 #[serde(default)]
1186 pub joint_real_name: String,
1187 #[serde(default)]
1188 pub joint_en_name: String,
1189 #[serde(default)]
1190 pub org_id: String,
1191 #[serde(default)]
1192 pub bcan: String,
1193 #[serde(default)]
1194 pub op_entrust_way: i32,
1195 #[serde(default)]
1196 pub op_entrust_way_name: String,
1197 #[serde(default)]
1198 pub remark: String,
1199 #[serde(default)]
1200 pub notice: String,
1201 #[serde(default)]
1202 pub short_sell_type: i32,
1203 #[serde(default)]
1204 pub ploy_type: String,
1205 #[serde(default)]
1206 pub ploy_id: String,
1207 #[serde(default)]
1208 pub ploy_status: String,
1209 #[serde(default)]
1210 pub trend: i32,
1211 #[serde(default)]
1212 pub withdrawal_reason: String,
1213 #[serde(default)]
1214 pub activate_order_type: String,
1215 #[serde(default)]
1216 pub activate_rth: i32,
1217 #[serde(default)]
1218 pub submit_price: String,
1219 #[serde(default)]
1220 pub contract_direction: String,
1221 #[serde(default)]
1222 pub strike_price: String,
1223 #[serde(default)]
1224 pub contract_size: String,
1225 #[serde(default)]
1226 pub monitor_price: String,
1227 #[serde(default)]
1228 pub button_control: USButtonControl,
1229 pub charge_detail: Option<USChargeDetail>,
1230 #[serde(default)]
1231 pub attached_orders: Vec<USAttachedOrder>,
1232 #[serde(default)]
1233 pub order_histories: Vec<USOrderHistory>,
1234}
1235
1236#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1239pub struct USOrderDetailResponse {
1240 pub order: Option<USOrderDetail>,
1242 pub current_attached_order: Option<USOrderDetail>,
1244 #[serde(default)]
1246 pub current_millisecond: String,
1247}
1248
1249#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1251pub struct USCashEntry {
1252 #[serde(default)]
1253 pub currency: String,
1254 #[serde(default)]
1255 pub frozen_buy_cash: String,
1256 #[serde(default)]
1257 pub outstanding: String,
1258 #[serde(default)]
1259 pub settled_cash: String,
1260 #[serde(default)]
1261 pub total_amount: String,
1262 #[serde(default)]
1263 pub total_cash: String,
1264}
1265
1266#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1268pub struct USCryptoEntry {
1269 #[serde(default)]
1270 pub asset_type: String,
1271 #[serde(default)]
1272 pub average_cost: String,
1273 #[serde(
1276 default,
1277 rename = "counter_id",
1278 deserialize_with = "crate::utils::counter::deserialize_counter_id_as_symbol"
1279 )]
1280 pub symbol: String,
1281 #[serde(default)]
1282 pub currency: String,
1283 #[serde(default)]
1284 pub industry_name: String,
1285}
1286
1287#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1289pub struct USStockEntry {
1290 #[serde(default)]
1293 pub symbol: String,
1294 #[serde(
1297 default,
1298 rename = "counter_id",
1299 deserialize_with = "crate::utils::counter::deserialize_counter_id_as_symbol"
1300 )]
1301 pub full_symbol: String,
1302 #[serde(default)]
1303 pub asset_type: String,
1304 #[serde(default)]
1305 pub quantity: String,
1306 #[serde(default)]
1307 pub currency: String,
1308 #[serde(default)]
1309 pub average_cost: String,
1310 #[serde(default)]
1311 pub market: String,
1312 #[serde(default)]
1313 pub trade_status: String,
1314 #[serde(default)]
1315 pub prev_close: String,
1316 #[serde(default)]
1317 pub last_done: String,
1318 #[serde(default)]
1319 pub market_price: String,
1320 #[serde(default)]
1321 pub pretrade_close: String,
1322 #[serde(default)]
1323 pub stock_invest_of_today: String,
1324 #[serde(default)]
1325 pub today_pl: String,
1326 #[serde(default)]
1327 pub pretrade_stock_invest_of_today: String,
1328 #[serde(default)]
1329 pub pretrade_today_pl: String,
1330 #[serde(default)]
1331 pub night_last_done: String,
1332 #[serde(default)]
1333 pub night_prev_close: String,
1334 #[serde(default)]
1335 pub position_side: String,
1336 #[serde(default)]
1337 pub open_position_time: String,
1338 #[serde(default)]
1339 pub name: String,
1340 #[serde(default)]
1341 pub industry_counter_id: String,
1342 #[serde(default)]
1343 pub industry_name: String,
1344}
1345
1346#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1349pub struct USAssetOverview {
1350 #[serde(default)]
1351 pub account_type: String,
1352 #[serde(default, with = "crate::serde_utils::timestamp_opt")]
1354 pub asset_timestamp: Option<time::OffsetDateTime>,
1355 #[serde(default)]
1357 pub cash_buy_power: String,
1358 #[serde(default)]
1359 pub overnight_buy_power: String,
1360 #[serde(default)]
1361 pub currency: String,
1362 #[serde(default)]
1363 pub cash_list: Vec<USCashEntry>,
1364 #[serde(default)]
1365 pub stock_list: Vec<USStockEntry>,
1366 #[serde(default)]
1367 pub option_list: Vec<serde_json::Value>,
1368 #[serde(default)]
1369 pub crypto_list: Vec<USCryptoEntry>,
1370 #[serde(default)]
1371 pub multi_leg: serde_json::Value,
1372}
1373
1374#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1376pub struct USRealizedPLMetric {
1377 #[serde(default)]
1378 pub amount: String,
1379 #[serde(default)]
1381 pub period: i32,
1382 #[serde(default)]
1383 pub rate: String,
1384}
1385
1386#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1389pub struct USRealizedPLEntry {
1390 #[serde(default)]
1391 pub category: i32,
1392 #[serde(default)]
1393 pub currency: String,
1394 #[serde(default)]
1395 pub metrics: Vec<USRealizedPLMetric>,
1396}
1397
1398#[derive(Debug, Clone, Default)]
1401pub struct GetUSRealizedPLOptions {
1402 pub currency: String,
1404 pub category: String,
1406}
1407
1408#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1412pub struct USRealizedPL {
1413 #[serde(default)]
1414 pub realized_pl_list: Vec<USRealizedPLEntry>,
1415}
1416
1417#[cfg(test)]
1418mod tests {
1419 use time::macros::datetime;
1420
1421 use super::*;
1422
1423 #[test]
1424 fn fund_position_response() {
1425 let data = r#"
1426 {
1427 "list": [{
1428 "account_channel": "lb",
1429 "fund_info": [{
1430 "symbol": "HK0000447943",
1431 "symbol_name": "高腾亚洲收益基金",
1432 "currency": "USD",
1433 "holding_units": "5.000",
1434 "current_net_asset_value": "0",
1435 "cost_net_asset_value": "0.00",
1436 "net_asset_value_day": "1649865600"
1437 }]
1438 }]
1439 }
1440 "#;
1441
1442 let resp: FundPositionsResponse = serde_json::from_str(data).unwrap();
1443 assert_eq!(resp.channels.len(), 1);
1444
1445 let channel = &resp.channels[0];
1446 assert_eq!(channel.account_channel, "lb");
1447 assert_eq!(channel.positions.len(), 1);
1448
1449 let position = &channel.positions[0];
1450 assert_eq!(position.symbol, "HK0000447943");
1451 assert_eq!(position.symbol_name, "高腾亚洲收益基金");
1452 assert_eq!(position.currency, "USD");
1453 assert_eq!(position.current_net_asset_value, decimal!(0i32));
1454 assert_eq!(position.cost_net_asset_value, decimal!(0i32));
1455 assert_eq!(position.holding_units, decimal!(5i32));
1456 assert_eq!(position.net_asset_value_day, datetime!(2022-4-14 0:00 +8));
1457 }
1458
1459 #[test]
1460 fn stock_position_response() {
1461 let data = r#"
1462 {
1463 "list": [
1464 {
1465 "account_channel": "lb",
1466 "stock_info": [
1467 {
1468 "symbol": "700.HK",
1469 "symbol_name": "腾讯控股",
1470 "currency": "HK",
1471 "quantity": "650",
1472 "available_quantity": "-450",
1473 "cost_price": "457.53",
1474 "market": "HK",
1475 "init_quantity": "2000"
1476 },
1477 {
1478 "symbol": "9991.HK",
1479 "symbol_name": "宝尊电商-SW",
1480 "currency": "HK",
1481 "quantity": "200",
1482 "available_quantity": "0",
1483 "cost_price": "32.25",
1484 "market": "HK",
1485 "init_quantity": ""
1486 }
1487 ]
1488 }
1489 ]
1490 }
1491 "#;
1492
1493 let resp: StockPositionsResponse = serde_json::from_str(data).unwrap();
1494 assert_eq!(resp.channels.len(), 1);
1495
1496 let channel = &resp.channels[0];
1497 assert_eq!(channel.account_channel, "lb");
1498 assert_eq!(channel.positions.len(), 2);
1499
1500 let position = &channel.positions[0];
1501 assert_eq!(position.symbol, "700.HK");
1502 assert_eq!(position.symbol_name, "腾讯控股");
1503 assert_eq!(position.currency, "HK");
1504 assert_eq!(position.quantity, decimal!(650));
1505 assert_eq!(position.available_quantity, decimal!(-450));
1506 assert_eq!(position.cost_price, decimal!(457.53f32));
1507 assert_eq!(position.market, Market::HK);
1508 assert_eq!(position.init_quantity, Some(decimal!(2000)));
1509
1510 let position = &channel.positions[0];
1511 assert_eq!(position.symbol, "700.HK");
1512 assert_eq!(position.symbol_name, "腾讯控股");
1513 assert_eq!(position.currency, "HK");
1514 assert_eq!(position.quantity, decimal!(650));
1515 assert_eq!(position.available_quantity, decimal!(-450));
1516 assert_eq!(position.cost_price, decimal!(457.53f32));
1517 assert_eq!(position.market, Market::HK);
1518
1519 let position = &channel.positions[1];
1520 assert_eq!(position.symbol, "9991.HK");
1521 assert_eq!(position.symbol_name, "宝尊电商-SW");
1522 assert_eq!(position.currency, "HK");
1523 assert_eq!(position.quantity, decimal!(200));
1524 assert_eq!(position.available_quantity, decimal!(0));
1525 assert_eq!(position.cost_price, decimal!(32.25f32));
1526 assert_eq!(position.init_quantity, None);
1527 }
1528
1529 #[test]
1530 fn cash_flow() {
1531 let data = r#"
1532 {
1533 "list": [
1534 {
1535 "transaction_flow_name": "BuyContract-Stocks",
1536 "direction": 1,
1537 "balance": "-248.60",
1538 "currency": "USD",
1539 "business_type": 1,
1540 "business_time": "1621507957",
1541 "symbol": "AAPL.US",
1542 "description": "AAPL"
1543 },
1544 {
1545 "transaction_flow_name": "BuyContract-Stocks",
1546 "direction": 1,
1547 "balance": "-125.16",
1548 "currency": "USD",
1549 "business_type": 2,
1550 "business_time": "1621504824",
1551 "symbol": "AAPL.US",
1552 "description": "AAPL"
1553 }
1554 ]
1555 }
1556 "#;
1557
1558 #[derive(Debug, Deserialize)]
1559 struct Response {
1560 list: Vec<CashFlow>,
1561 }
1562
1563 let resp: Response = serde_json::from_str(data).unwrap();
1564 assert_eq!(resp.list.len(), 2);
1565
1566 let cashflow = &resp.list[0];
1567 assert_eq!(cashflow.transaction_flow_name, "BuyContract-Stocks");
1568 assert_eq!(cashflow.direction, CashFlowDirection::Out);
1569 assert_eq!(cashflow.balance, decimal!(-248.60f32));
1570 assert_eq!(cashflow.currency, "USD");
1571 assert_eq!(cashflow.business_type, BalanceType::Cash);
1572 assert_eq!(cashflow.business_time, datetime!(2021-05-20 18:52:37 +8));
1573 assert_eq!(cashflow.symbol.as_deref(), Some("AAPL.US"));
1574 assert_eq!(cashflow.description, "AAPL");
1575
1576 let cashflow = &resp.list[1];
1577 assert_eq!(cashflow.transaction_flow_name, "BuyContract-Stocks");
1578 assert_eq!(cashflow.direction, CashFlowDirection::Out);
1579 assert_eq!(cashflow.balance, decimal!(-125.16f32));
1580 assert_eq!(cashflow.currency, "USD");
1581 assert_eq!(cashflow.business_type, BalanceType::Stock);
1582 assert_eq!(cashflow.business_time, datetime!(2021-05-20 18:00:24 +8));
1583 assert_eq!(cashflow.symbol.as_deref(), Some("AAPL.US"));
1584 assert_eq!(cashflow.description, "AAPL");
1585 }
1586
1587 #[test]
1588 fn account_balance() {
1589 let data = r#"
1590 {
1591 "list": [
1592 {
1593 "total_cash": "1759070010.72",
1594 "max_finance_amount": "977582000",
1595 "remaining_finance_amount": "0",
1596 "risk_level": "1",
1597 "margin_call": "2598051051.50",
1598 "currency": "HKD",
1599 "cash_infos": [
1600 {
1601 "withdraw_cash": "97592.30",
1602 "available_cash": "195902464.37",
1603 "frozen_cash": "11579339.13",
1604 "settling_cash": "207288537.81",
1605 "currency": "HKD"
1606 },
1607 {
1608 "withdraw_cash": "199893416.74",
1609 "available_cash": "199893416.74",
1610 "frozen_cash": "28723.76",
1611 "settling_cash": "-276806.51",
1612 "currency": "USD"
1613 }
1614 ],
1615 "net_assets": "11111.12",
1616 "init_margin": "2222.23",
1617 "maintenance_margin": "3333.45",
1618 "buy_power": "1234.67",
1619 "frozen_transaction_fees": [
1620 {
1621 "currency": "HKD",
1622 "frozen_transaction_fee": "123"
1623 }
1624 ]
1625 }
1626 ]
1627 }"#;
1628
1629 #[derive(Debug, Deserialize)]
1630 struct Response {
1631 list: Vec<AccountBalance>,
1632 }
1633
1634 let resp: Response = serde_json::from_str(data).unwrap();
1635 assert_eq!(resp.list.len(), 1);
1636
1637 let balance = &resp.list[0];
1638 assert_eq!(balance.total_cash, "1759070010.72".parse().unwrap());
1639 assert_eq!(balance.max_finance_amount, "977582000".parse().unwrap());
1640 assert_eq!(balance.remaining_finance_amount, decimal!(0i32));
1641 assert_eq!(balance.risk_level, 1);
1642 assert_eq!(balance.margin_call, "2598051051.50".parse().unwrap());
1643 assert_eq!(balance.currency, "HKD");
1644 assert_eq!(balance.net_assets, "11111.12".parse().unwrap());
1645 assert_eq!(balance.init_margin, "2222.23".parse().unwrap());
1646 assert_eq!(balance.maintenance_margin, "3333.45".parse().unwrap());
1647 assert_eq!(balance.buy_power, "1234.67".parse().unwrap());
1648
1649 assert_eq!(balance.cash_infos.len(), 2);
1650
1651 let cash_info = &balance.cash_infos[0];
1652 assert_eq!(cash_info.withdraw_cash, "97592.30".parse().unwrap());
1653 assert_eq!(cash_info.available_cash, "195902464.37".parse().unwrap());
1654 assert_eq!(cash_info.frozen_cash, "11579339.13".parse().unwrap());
1655 assert_eq!(cash_info.settling_cash, "207288537.81".parse().unwrap());
1656 assert_eq!(cash_info.currency, "HKD");
1657
1658 let cash_info = &balance.cash_infos[1];
1659 assert_eq!(cash_info.withdraw_cash, "199893416.74".parse().unwrap());
1660 assert_eq!(cash_info.available_cash, "199893416.74".parse().unwrap());
1661 assert_eq!(cash_info.frozen_cash, "28723.76".parse().unwrap());
1662 assert_eq!(cash_info.settling_cash, "-276806.51".parse().unwrap());
1663 assert_eq!(cash_info.currency, "USD");
1664
1665 assert_eq!(balance.frozen_transaction_fees.len(), 1);
1666
1667 let frozen_transaction_fee = &balance.frozen_transaction_fees[0];
1668 assert_eq!(frozen_transaction_fee.currency, "HKD");
1669 assert_eq!(
1670 frozen_transaction_fee.frozen_transaction_fee,
1671 "123".parse().unwrap()
1672 );
1673 }
1674
1675 #[test]
1676 fn history_orders() {
1677 let data = r#"
1678 {
1679 "orders": [
1680 {
1681 "currency": "HKD",
1682 "executed_price": "0.000",
1683 "executed_quantity": "0",
1684 "expire_date": "",
1685 "last_done": "",
1686 "limit_offset": "",
1687 "msg": "",
1688 "order_id": "706388312699592704",
1689 "order_type": "ELO",
1690 "outside_rth": "UnknownOutsideRth",
1691 "price": "11.900",
1692 "quantity": "200",
1693 "side": "Buy",
1694 "status": "RejectedStatus",
1695 "stock_name": "Bank of East Asia Ltd/The",
1696 "submitted_at": "1651644897",
1697 "symbol": "23.HK",
1698 "tag": "Normal",
1699 "time_in_force": "Day",
1700 "trailing_amount": "",
1701 "trailing_percent": "",
1702 "trigger_at": "0",
1703 "trigger_price": "",
1704 "trigger_status": "NOT_USED",
1705 "updated_at": "1651644898",
1706 "limit_depth_level": 0,
1707 "trigger_count": 0,
1708 "monitor_price": "",
1709 "remark": "abc"
1710 }
1711 ]
1712 }
1713 "#;
1714
1715 #[derive(Deserialize)]
1716 struct Response {
1717 orders: Vec<Order>,
1718 }
1719
1720 let resp: Response = serde_json::from_str(data).unwrap();
1721 assert_eq!(resp.orders.len(), 1);
1722
1723 let order = &resp.orders[0];
1724 assert_eq!(order.currency, "HKD");
1725 assert!(order.executed_price.is_none());
1726 assert_eq!(order.executed_quantity, decimal!(0));
1727 assert!(order.expire_date.is_none());
1728 assert!(order.last_done.is_none());
1729 assert!(order.limit_offset.is_none());
1730 assert_eq!(order.msg, "");
1731 assert_eq!(order.order_id, "706388312699592704");
1732 assert_eq!(order.order_type, OrderType::ELO);
1733 assert!(order.outside_rth.is_none());
1734 assert_eq!(order.price, Some("11.900".parse().unwrap()));
1735 assert_eq!(order.quantity, decimal!(200));
1736 assert_eq!(order.side, OrderSide::Buy);
1737 assert_eq!(order.status, OrderStatus::Rejected);
1738 assert_eq!(order.stock_name, "Bank of East Asia Ltd/The");
1739 assert_eq!(order.submitted_at, datetime!(2022-05-04 14:14:57 +8));
1740 assert_eq!(order.symbol, "23.HK");
1741 assert_eq!(order.tag, OrderTag::Normal);
1742 assert_eq!(order.time_in_force, TimeInForceType::Day);
1743 assert!(order.trailing_amount.is_none());
1744 assert!(order.trailing_percent.is_none());
1745 assert!(order.trigger_at.is_none());
1746 assert!(order.trigger_price.is_none());
1747 assert!(order.trigger_status.is_none());
1748 assert_eq!(order.updated_at, Some(datetime!(2022-05-04 14:14:58 +8)));
1749 assert_eq!(order.remark, "abc");
1750 }
1751
1752 #[test]
1753 fn today_orders() {
1754 let data = r#"
1755 {
1756 "orders": [
1757 {
1758 "currency": "HKD",
1759 "executed_price": "0.000",
1760 "executed_quantity": "0",
1761 "expire_date": "",
1762 "last_done": "",
1763 "limit_offset": "",
1764 "msg": "",
1765 "order_id": "706388312699592704",
1766 "order_type": "ELO",
1767 "outside_rth": "UnknownOutsideRth",
1768 "price": "11.900",
1769 "quantity": "200",
1770 "side": "Buy",
1771 "status": "RejectedStatus",
1772 "stock_name": "Bank of East Asia Ltd/The",
1773 "submitted_at": "1651644897",
1774 "symbol": "23.HK",
1775 "tag": "Normal",
1776 "time_in_force": "Day",
1777 "trailing_amount": "",
1778 "trailing_percent": "",
1779 "trigger_at": "0",
1780 "trigger_price": "",
1781 "trigger_status": "NOT_USED",
1782 "updated_at": "1651644898",
1783 "limit_depth_level": 0,
1784 "trigger_count": 0,
1785 "monitor_price": "",
1786 "remark": "abc"
1787 }
1788 ]
1789 }
1790 "#;
1791
1792 #[derive(Deserialize)]
1793 struct Response {
1794 orders: Vec<Order>,
1795 }
1796
1797 let resp: Response = serde_json::from_str(data).unwrap();
1798 assert_eq!(resp.orders.len(), 1);
1799
1800 let order = &resp.orders[0];
1801 assert_eq!(order.currency, "HKD");
1802 assert!(order.executed_price.is_none());
1803 assert_eq!(order.executed_quantity, decimal!(0));
1804 assert!(order.expire_date.is_none());
1805 assert!(order.last_done.is_none());
1806 assert!(order.limit_offset.is_none());
1807 assert_eq!(order.msg, "");
1808 assert_eq!(order.order_id, "706388312699592704");
1809 assert_eq!(order.order_type, OrderType::ELO);
1810 assert!(order.outside_rth.is_none());
1811 assert_eq!(order.price, Some("11.900".parse().unwrap()));
1812 assert_eq!(order.quantity, decimal!(200));
1813 assert_eq!(order.side, OrderSide::Buy);
1814 assert_eq!(order.status, OrderStatus::Rejected);
1815 assert_eq!(order.stock_name, "Bank of East Asia Ltd/The");
1816 assert_eq!(order.submitted_at, datetime!(2022-05-04 14:14:57 +8));
1817 assert_eq!(order.symbol, "23.HK");
1818 assert_eq!(order.tag, OrderTag::Normal);
1819 assert_eq!(order.time_in_force, TimeInForceType::Day);
1820 assert!(order.trailing_amount.is_none());
1821 assert!(order.trailing_percent.is_none());
1822 assert!(order.trigger_at.is_none());
1823 assert!(order.trigger_price.is_none());
1824 assert!(order.trigger_status.is_none());
1825 assert_eq!(order.updated_at, Some(datetime!(2022-05-04 14:14:58 +8)));
1826 assert_eq!(order.remark, "abc");
1827 }
1828
1829 #[test]
1830 fn history_executions() {
1831 let data = r#"
1832 {
1833 "has_more": false,
1834 "trades": [
1835 {
1836 "order_id": "693664675163312128",
1837 "price": "388",
1838 "quantity": "100",
1839 "symbol": "700.HK",
1840 "trade_done_at": "1648611351",
1841 "trade_id": "693664675163312128-1648611351433741210"
1842 }
1843 ]
1844 }
1845 "#;
1846
1847 #[derive(Deserialize)]
1848 struct Response {
1849 trades: Vec<Execution>,
1850 }
1851
1852 let resp: Response = serde_json::from_str(data).unwrap();
1853 assert_eq!(resp.trades.len(), 1);
1854
1855 let execution = &resp.trades[0];
1856 assert_eq!(execution.order_id, "693664675163312128");
1857 assert_eq!(execution.price, "388".parse().unwrap());
1858 assert_eq!(execution.quantity, decimal!(100));
1859 assert_eq!(execution.symbol, "700.HK");
1860 assert_eq!(execution.trade_done_at, datetime!(2022-03-30 11:35:51 +8));
1861 assert_eq!(execution.trade_id, "693664675163312128-1648611351433741210");
1862 }
1863
1864 #[test]
1865 fn order_detail() {
1866 let data = r#"
1867 {
1868 "order_id": "828940451093708800",
1869 "status": "FilledStatus",
1870 "stock_name": "Apple",
1871 "quantity": "10",
1872 "executed_quantity": "10",
1873 "price": "200.000",
1874 "executed_price": "164.660",
1875 "submitted_at": "1680863604",
1876 "side": "Buy",
1877 "symbol": "AAPL.US",
1878 "order_type": "LO",
1879 "last_done": "164.660",
1880 "trigger_price": "0.0000",
1881 "msg": "",
1882 "tag": "Normal",
1883 "time_in_force": "Day",
1884 "expire_date": "2023-04-10",
1885 "updated_at": "1681113000",
1886 "trigger_at": "0",
1887 "trailing_amount": "",
1888 "trailing_percent": "",
1889 "limit_offset": "",
1890 "trigger_status": "NOT_USED",
1891 "outside_rth": "ANY_TIME",
1892 "currency": "USD",
1893 "limit_depth_level": 0,
1894 "trigger_count": 0,
1895 "monitor_price": "",
1896 "remark": "1680863603.927165",
1897 "free_status": "None",
1898 "free_amount": "",
1899 "free_currency": "",
1900 "deductions_status": "NONE",
1901 "deductions_amount": "",
1902 "deductions_currency": "",
1903 "platform_deducted_status": "NONE",
1904 "platform_deducted_amount": "",
1905 "platform_deducted_currency": "",
1906 "history": [{
1907 "price": "164.6600",
1908 "quantity": "10",
1909 "status": "FilledStatus",
1910 "msg": "Execution of 10",
1911 "time": "1681113000"
1912 }, {
1913 "price": "200.0000",
1914 "quantity": "10",
1915 "status": "NewStatus",
1916 "msg": "",
1917 "time": "1681113000"
1918 }],
1919 "charge_detail": {
1920 "items": [{
1921 "code": "BROKER_FEES",
1922 "name": "Broker Fees",
1923 "fees": []
1924 }, {
1925 "code": "THIRD_FEES",
1926 "name": "Third-party Fees",
1927 "fees": []
1928 }],
1929 "total_amount": "0",
1930 "currency": "USD"
1931 }
1932 }
1933 "#;
1934
1935 _ = serde_json::from_str::<OrderDetail>(data).unwrap();
1936 }
1937}