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