Skip to main content

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}