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 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}