futu_cache/trd_cache/types/position.rs
1/// 缓存的持仓 (对齐 C++ Ndt_Trd_AccPosition 全字段)
2#[derive(Debug, Clone, Default)]
3pub struct CachedPosition {
4 pub position_id: u64,
5 /// Backend business position id used by JP combo close/order paths.
6 ///
7 /// C++ `NNProto_Trd_AccReal.cpp:262-269` stores
8 /// `asset_query.AccPstnInfo.business_position_id` as
9 /// `Ndt_Trd_AccPosition.sBusinessPositionID` and, for FutuJP, exposes
10 /// `Position.positionID = HashStrToU64(sBusinessPositionID)`. Combo
11 /// trade-write paths must reverse that mapping before sending backend
12 /// CMD2297/CMD4701.
13 pub business_position_id: Option<String>,
14 /// C++ `Ndt_Trd_AccPosition.nPositionAccID`; used by JP combo legs as
15 /// backend `pos_account_id`.
16 pub position_acc_id: Option<u64>,
17 /// C++ `Ndt_Trd_AccPosition.nSubAccountID`; used by JP combo legs as
18 /// backend `pos_sub_account_id`.
19 pub sub_account_id: Option<u64>,
20 pub position_side: i32, // 0=多仓, 1=空仓
21 pub code: String,
22 pub name: String,
23 pub qty: f64,
24 pub can_sell_qty: f64,
25 pub price: f64, // 当前价
26 pub cost_price: f64, // 摊薄成本价
27 pub val: f64, // 市值
28 pub pl_val: f64, // 盈亏金额
29 pub pl_ratio: Option<f64>, // 盈亏比例
30 pub sec_market: Option<i32>, // 证券市场
31 pub td_pl_val: Option<f64>, // 今日盈亏
32 pub td_trd_val: Option<f64>, // 今日成交额
33 pub td_buy_val: Option<f64>, // 今日买入金额
34 pub td_buy_qty: Option<f64>, // 今日买入数量
35 pub td_sell_val: Option<f64>, // 今日卖出金额
36 pub td_sell_qty: Option<f64>, // 今日卖出数量
37 pub unrealized_pl: Option<f64>, // 未实现盈亏 (期货)
38 pub realized_pl: Option<f64>, // 已实现盈亏 (期货)
39 pub currency: Option<i32>, // 货币
40 pub trd_market: Option<i32>, // 交易市场
41 pub diluted_cost_price: Option<f64>, // 摊薄成本
42 pub average_cost_price: Option<f64>, // 平均成本
43 pub average_pl_ratio: Option<f64>, // 平均盈亏比例
44 /// C++ 10.7 `Ndt_Trd_AccPosition.nComboIDHash`, projected as
45 /// `Trd_Common.Position.comboID` only for combo summary/leg rows.
46 pub combo_id: Option<u64>,
47 /// Backend asset-system combo id string (`Ndt_Trd_AccPosition.sComboIDSvr`).
48 ///
49 /// Public `comboID` is a hash, but C++ JP combo close/order paths write the
50 /// original string back to backend `OrderNewReq.combo_id`; keep both
51 /// representations so public projection and backend write paths do not
52 /// fight each other.
53 pub business_combo_id: Option<String>,
54 /// C++ 10.7 option strategy type after backend `combo_identify` ->
55 /// NN -> public `Qot_Common.OptionStrategyType` mapping.
56 pub strategy_type: Option<i32>,
57 /// C++ 10.7 `NN_PositionType`, projected as public
58 /// `Trd_Common.PositionType` (`Combined=1`, `Leg=2`).
59 pub position_type: Option<i32>,
60 /// C++ 10.7 position account id. JP sub-account rows may use a different
61 /// long account id; otherwise the handler falls back to request acc_id.
62 pub acc_id: Option<u64>,
63 /// C++ 10.7 JP sub-account type.
64 pub jp_acc_type: Option<i32>,
65}
66
67/// PositionList account / asset-category / optional currency key.
68///
69/// C++ `APIServer_Trd_GetPositionList.cpp::FillPositionList` reads positions
70/// by `NN_AssetKey { accid, enCategory }`. FutuJP margin / derivative accounts
71/// therefore need independent position snapshots per asset category, just like
72/// funds. Category 0 keeps the legacy single-bucket behavior for non-JP and sim
73/// accounts.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub struct PositionsCacheKey {
76 pub acc_id: u64,
77 pub asset_category: i32,
78 /// Crypto CMD20631 stores one position list per returned currency. Ordinary,
79 /// sim, JP and combo snapshots preserve the legacy `None` dimension.
80 pub currency: Option<i32>,
81}
82
83impl PositionsCacheKey {
84 #[must_use]
85 pub const fn legacy(acc_id: u64) -> Self {
86 Self {
87 acc_id,
88 asset_category: 0,
89 currency: None,
90 }
91 }
92
93 #[must_use]
94 pub const fn scoped(acc_id: u64, asset_category: i32) -> Self {
95 Self {
96 acc_id,
97 asset_category,
98 currency: None,
99 }
100 }
101
102 #[must_use]
103 pub const fn full(acc_id: u64, asset_category: i32, currency: Option<i32>) -> Self {
104 Self {
105 acc_id,
106 asset_category,
107 currency,
108 }
109 }
110
111 #[must_use]
112 pub const fn asset_scope(self) -> Self {
113 Self::scoped(self.acc_id, self.asset_category)
114 }
115}