futu_trd/types.rs
1// 交易域通用类型
2
3/// 交易环境
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[repr(i32)]
6#[non_exhaustive]
7pub enum TrdEnv {
8 Simulate = 0,
9 Real = 1,
10}
11
12impl TryFrom<i32> for TrdEnv {
13 type Error = ();
14
15 fn try_from(value: i32) -> Result<Self, Self::Error> {
16 match value {
17 0 => Ok(Self::Simulate),
18 1 => Ok(Self::Real),
19 _ => Err(()),
20 }
21 }
22}
23
24/// 交易市场
25///
26/// 对齐 `Trd_Common.proto::TrdMarket`:
27/// HK=1 / US=2 / CN=3 / HKCC=4 / Futures=5 / SG=6 / Crypto=7 / AU=8 /
28/// FuturesSimulateHK=10 / FuturesSimulateUS=11 / FuturesSimulateSG=12 /
29/// FuturesSimulateJP=13 / JP=15 / Prediction=17 / MY=111 / CA=112 /
30/// fund markets 113/123/124/125/126.
31///
32/// v1.4.93 BUG-001 fix (S level ship-blocker): v1.4.86-90 五版只列 4 variants
33/// (HK/US/CN/HKCC), 而 MCP / CLI schema 都已暴露 9. SG/AU/JP/MY/CA 5 国 user 用
34/// 导致 daemon 返 `unknown trd market SG (HK|US|CN|HKCC)`. 端到端不可下单.
35///
36/// 注: `Futures=5` 是不分国家的期货市场 (历史 backend 标识), 与具体 SG/AU/JP/MY/CA
37/// 国家 trd_market 不同. Futures 通常用 sec_market 派生 (例如 US futures 用
38/// sec_market=11 加 trd_market=5). 本枚举包含 Futures 让 frontend 也能直接传,
39/// 但典型用法仍然走国家 trd_market.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[repr(i32)]
42#[non_exhaustive]
43pub enum TrdMarket {
44 Unknown = 0,
45 HK = 1,
46 US = 2,
47 CN = 3,
48 HKCC = 4,
49 Futures = 5,
50 SG = 6,
51 Crypto = 7,
52 AU = 8,
53 FuturesSimulateHK = 10,
54 FuturesSimulateUS = 11,
55 FuturesSimulateSG = 12,
56 FuturesSimulateJP = 13,
57 JP = 15,
58 /// Event-contract / prediction market.
59 /// Ref: C++ `Trd_Common.proto:42` and `_APIServer_Trd_Comm.cpp:2575-2577`.
60 Prediction = 17,
61 MY = 111,
62 CA = 112,
63 /// HKFUND view-only 港币基金 (融资融券 / 基金账户) — v1.4.102 fund-market
64 /// handoff. C++ `NN_TrdMarket_HK_Fund=113` (NNBase_Define_Enum.h:113).
65 /// 注: cash-log backend `Market` enum 用 13 (MARKET_HKFUND), 翻译见
66 /// `cash_log_market_for_trd_market`.
67 HKFund = 113,
68 /// USFUND view-only 美元基金 — v1.4.102. C++ `NN_TrdMarket_US_Fund=123`.
69 /// cash-log Market enum 用 23 (MARKET_USFUND).
70 USFund = 123,
71 /// SGFUND view-only 新加坡基金 — C++ `NN_TrdMarket_SG_Fund=124`.
72 SGFund = 124,
73 /// MYFUND view-only 马来西亚基金 — C++ `NN_TrdMarket_MY_Fund=125`.
74 MYFund = 125,
75 /// JPFUND view-only 日本基金 — C++ `NN_TrdMarket_JP_Fund=126`.
76 JPFund = 126,
77}
78
79impl TryFrom<i32> for TrdMarket {
80 type Error = ();
81
82 fn try_from(value: i32) -> Result<Self, Self::Error> {
83 match value {
84 1 => Ok(Self::HK),
85 2 => Ok(Self::US),
86 3 => Ok(Self::CN),
87 4 => Ok(Self::HKCC),
88 5 => Ok(Self::Futures),
89 6 => Ok(Self::SG),
90 7 => Ok(Self::Crypto),
91 8 => Ok(Self::AU),
92 10 => Ok(Self::FuturesSimulateHK),
93 11 => Ok(Self::FuturesSimulateUS),
94 12 => Ok(Self::FuturesSimulateSG),
95 13 => Ok(Self::FuturesSimulateJP),
96 15 => Ok(Self::JP),
97 17 => Ok(Self::Prediction),
98 111 => Ok(Self::MY),
99 112 => Ok(Self::CA),
100 113 => Ok(Self::HKFund),
101 123 => Ok(Self::USFund),
102 124 => Ok(Self::SGFund),
103 125 => Ok(Self::MYFund),
104 126 => Ok(Self::JPFund),
105 _ => Err(()),
106 }
107 }
108}
109
110/// 交易方向
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112#[repr(i32)]
113#[non_exhaustive]
114pub enum TrdSide {
115 Unknown = 0,
116 Buy = 1,
117 Sell = 2,
118 SellShort = 3,
119 BuyBack = 4,
120}
121
122impl TryFrom<i32> for TrdSide {
123 type Error = ();
124
125 fn try_from(value: i32) -> Result<Self, Self::Error> {
126 match value {
127 1 => Ok(Self::Buy),
128 2 => Ok(Self::Sell),
129 3 => Ok(Self::SellShort),
130 4 => Ok(Self::BuyBack),
131 _ => Err(()),
132 }
133 }
134}
135
136/// 订单类型
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138#[repr(i32)]
139#[non_exhaustive]
140pub enum OrderType {
141 Unknown = 0,
142 Normal = 1,
143 Market = 2,
144 AbsoluteLimit = 5,
145 Auction = 6,
146 AuctionLimit = 7,
147 SpecialLimit = 8,
148 SpecialLimitAll = 9,
149 // v1.4.53 F1 条件单
150 Stop = 10, // 止损市价单
151 StopLimit = 11, // 止损限价单
152 MarketifTouched = 12, // 触及市价单(止盈)
153 LimitifTouched = 13, // 触及限价单(止盈)
154 TrailingStop = 14, // 跟踪止损市价单
155 TrailingStopLimit = 15, // 跟踪止损限价单
156 TwapMarket = 16,
157 TwapLimit = 17,
158 VwapMarket = 18,
159 VwapLimit = 19,
160}
161
162impl TryFrom<i32> for OrderType {
163 type Error = ();
164
165 fn try_from(value: i32) -> Result<Self, Self::Error> {
166 match value {
167 1 => Ok(Self::Normal),
168 2 => Ok(Self::Market),
169 5 => Ok(Self::AbsoluteLimit),
170 6 => Ok(Self::Auction),
171 7 => Ok(Self::AuctionLimit),
172 8 => Ok(Self::SpecialLimit),
173 9 => Ok(Self::SpecialLimitAll),
174 10 => Ok(Self::Stop),
175 11 => Ok(Self::StopLimit),
176 12 => Ok(Self::MarketifTouched),
177 13 => Ok(Self::LimitifTouched),
178 14 => Ok(Self::TrailingStop),
179 15 => Ok(Self::TrailingStopLimit),
180 16 => Ok(Self::TwapMarket),
181 17 => Ok(Self::TwapLimit),
182 18 => Ok(Self::VwapMarket),
183 19 => Ok(Self::VwapLimit),
184 _ => Err(()),
185 }
186 }
187}
188
189/// 修改订单操作类型
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191#[repr(i32)]
192#[non_exhaustive]
193pub enum ModifyOrderOp {
194 Unknown = 0,
195 Normal = 1,
196 Cancel = 2,
197 Disable = 3,
198 Enable = 4,
199 Delete = 5,
200}
201
202impl TryFrom<i32> for ModifyOrderOp {
203 type Error = ();
204
205 fn try_from(value: i32) -> Result<Self, Self::Error> {
206 match value {
207 1 => Ok(Self::Normal),
208 2 => Ok(Self::Cancel),
209 3 => Ok(Self::Disable),
210 4 => Ok(Self::Enable),
211 5 => Ok(Self::Delete),
212 _ => Err(()),
213 }
214 }
215}
216
217/// 交易请求头
218#[derive(Debug, Clone)]
219pub struct TrdHeader {
220 /// 交易环境(模拟 / 真实)
221 pub trd_env: TrdEnv,
222 /// 交易账户 ID
223 pub acc_id: u64,
224 /// 交易市场
225 pub trd_market: TrdMarket,
226 /// v1.4.106 codex F6 (P2): JP 子账户类型 (TrdSubAccType).
227 ///
228 /// 仅 JP broker (FutuJP) 在无 positionID 时**必填**, 否则 backend 拒
229 /// `MissNecessaryParameters`. 非 JP 场景为 `None`. C++ `Trd_Common.proto:320`
230 /// `TrdHeader.jpAccType` (field 4, optional).
231 pub jp_acc_type: Option<i32>,
232}
233
234impl TrdHeader {
235 pub fn to_proto(&self) -> futu_proto::trd_common::TrdHeader {
236 futu_proto::trd_common::TrdHeader {
237 trd_env: self.trd_env as i32,
238 acc_id: self.acc_id,
239 trd_market: self.trd_market as i32,
240 // v1.4.106 codex F6 (P2): SDK 现支持 jp_acc_type, 透传到 backend.
241 jp_acc_type: self.jp_acc_type,
242 }
243 }
244}
245
246/// 账户资金
247///
248/// v1.4.73 BUG-004 fix(external reviewer v1.4.71 AI tester P0 报告):之前只暴露 7 字段
249/// 给 MCP,而 C++ Python SDK `accinfo_query` 返 63+。MCP 客户端做多币种 /
250/// 多市场管理 / 风控监测都用不起来。本版补 18 个关键字段:
251///
252/// - **currency**:必备(之前 MCP 完全缺失,agent 无从判断币种)
253/// - **available_funds**:margin 账户可用资金(不同于 cash)
254/// - **unrealized_pl / realized_pl**:持仓盈亏
255/// - **risk_level / risk_status**:账户风控级别 / 状态
256/// - **initial_margin / maintenance_margin / margin_call_margin**:保证金
257/// - **max_power_short**:做空可用
258/// - **long_mv / short_mv**:多空持仓市值
259/// - **pending_asset**:挂单占用资产
260/// - **max_withdrawal**:可取现上限
261/// - **is_pdt / pdt_seq / remaining_dtbp / dt_call_amount / dt_status**:
262/// US 账户 Pattern Day Trader 相关(关键风控指标)
263/// - **securities_assets / fund_assets / bond_assets**:资产类别 breakdown
264///
265/// 保留 `cash_info_list` / `market_info_list` 为 raw proto,v1.4.74+ 按需解析。
266#[derive(Debug, Clone)]
267pub struct Funds {
268 // 旧 7 字段(保持二进制兼容,老 caller 不受影响)
269 /// 购买力
270 pub power: f64,
271 /// 资产净值(总资产)
272 pub total_assets: f64,
273 /// 现金 — top-level summary cash, in `currency` field's currency.
274 ///
275 /// **v1.4.106 codex 1612 Candidate A**: This is **NOT** a cross-currency sum
276 /// of `cash_info_list[].cash`. Different currencies cannot be summed without
277 /// FX conversion. Backend (`Ndt_Trd_AccFund.fTotalCash`) directly populates
278 /// this field, faithfully relayed via `pFunds->set_cash(nnFunds.fTotalCash)`
279 /// in C++ `APIServer_Trd_GetFunds.cpp::FillFunds`.
280 ///
281 /// **Semantics by account type**:
282 /// - **Futures / Universal**: cash in `union_currency` (request currency or
283 /// account base if not requested). v1.4.106 codex 1556 F1 fix: daemon now
284 /// passes user-requested currency to CMD3020 `union_currency`, ensuring
285 /// `cash` is denominated in the requested currency.
286 /// - **Legacy single-currency accounts**: cash in account's primary market
287 /// currency. Only one entry in `cash_info_list`; top-level `cash` equals
288 /// that entry's `cash`.
289 ///
290 /// To match Futu mobile app's '现金总值 in HKD' display for universal
291 /// accounts, client must compute `sum(cash_info_list[i].cash * fx_rate(...))`
292 /// — daemon does not perform FX aggregation. Per-currency breakdown is in
293 /// `cash_info_list`.
294 pub cash: f64,
295 /// 证券市值
296 pub market_val: f64,
297 /// 冻结金额(未成交委托锁住的资金)
298 pub frozen_cash: f64,
299 /// 欠款金额(融资或透支)
300 pub debt_cash: f64,
301 /// 可提金额
302 pub avl_withdrawal_cash: f64,
303
304 // v1.4.73 BUG-004:新补 18 字段
305 /// 账户主币种(HKD / USD / CNH / ...,对齐 proto `TrdCommon.Currency`)
306 pub currency: Option<i32>,
307 /// 可用资金
308 pub available_funds: Option<f64>,
309 /// 未实现盈亏
310 pub unrealized_pl: Option<f64>,
311 /// 已实现盈亏
312 pub realized_pl: Option<f64>,
313 /// 账户风险等级
314 pub risk_level: Option<i32>,
315 /// 账户风险状态(预警 / 追保 / 平仓等)
316 pub risk_status: Option<i32>,
317 /// 起始保证金
318 pub initial_margin: Option<f64>,
319 /// 维持保证金
320 pub maintenance_margin: Option<f64>,
321 /// Margin Call 保证金
322 pub margin_call_margin: Option<f64>,
323 /// 做空最大购买力
324 pub max_power_short: Option<f64>,
325 /// 净现金购买力(无杠杆)
326 pub net_cash_power: Option<f64>,
327 /// 多头市值
328 pub long_mv: Option<f64>,
329 /// 空头市值
330 pub short_mv: Option<f64>,
331 /// 在途资产(T+N 未结算)
332 pub pending_asset: Option<f64>,
333 /// 最大可提资金
334 pub max_withdrawal: Option<f64>,
335 /// 是否为 Pattern Day Trader(美股规则)
336 pub is_pdt: Option<bool>,
337 /// PDT 违规序号 (mobile UI: 剩余日内交易次数)
338 pub pdt_seq: Option<String>,
339 /// v1.4.98 T1-4: 初始日内交易购买力 (DTBP, US PDT 账户)
340 pub beginning_dtbp: Option<f64>,
341 /// 剩余日内交易购买力 (DTBP)
342 pub remaining_dtbp: Option<f64>,
343 /// 日内追保金额 (DT Call)
344 pub dt_call_amount: Option<f64>,
345 /// 日内保证金状态
346 pub dt_status: Option<i32>,
347 /// 证券资产
348 pub securities_assets: Option<f64>,
349 /// 基金资产
350 pub fund_assets: Option<f64>,
351 /// 债券资产
352 pub bond_assets: Option<f64>,
353 /// 数字货币市值
354 pub crypto_mv: Option<f64>,
355 /// 数字货币风险等级
356 pub exposure_level: Option<i32>,
357 /// 数字货币持仓限额
358 pub exposure_limit: Option<f64>,
359 /// 数字货币已用限额
360 pub used_limit: Option<f64>,
361 /// 数字货币剩余额度
362 pub remaining_limit: Option<f64>,
363
364 // v1.4.74 C1 BUG-004 Phase 2:cash_info_list + market_info_list 展开
365 /// 按币种细分的现金信息列表
366 pub cash_info_list: Vec<FundsCashInfo>,
367 /// 按市场细分的资产信息列表
368 pub market_info_list: Vec<FundsMarketInfo>,
369}
370
371/// v1.4.74 C1 BUG-004 Phase 2: 分币种现金信息(对齐 proto `AccCashInfo`)。
372///
373/// 多币种账户(如美股账户持 USD + JPY 债券)每币种一条。
374#[derive(Debug, Clone)]
375pub struct FundsCashInfo {
376 /// 币种(对齐 proto `TrdCommon.Currency`:HKD=1 / USD=2 / CNH=3 / ...)
377 pub currency: Option<i32>,
378 /// 该币种现金
379 pub cash: Option<f64>,
380 /// 该币种可用余额
381 pub available_balance: Option<f64>,
382 /// 该币种净购买力
383 pub net_cash_power: Option<f64>,
384}
385
386/// v1.4.74 C1 BUG-004 Phase 2: 分市场资产信息(对齐 proto `AccMarketInfo`)。
387///
388/// 综合账户 / 跨市场账户每市场一条。
389#[derive(Debug, Clone)]
390pub struct FundsMarketInfo {
391 /// 所属交易市场(对齐 proto `TrdCommon.TrdMarket`)
392 pub trd_market: Option<i32>,
393 /// 该市场资产总值
394 pub assets: Option<f64>,
395}
396
397impl Funds {
398 pub fn from_proto(f: &futu_proto::trd_common::Funds) -> Self {
399 Self {
400 power: f.power,
401 total_assets: f.total_assets,
402 cash: f.cash,
403 market_val: f.market_val,
404 frozen_cash: f.frozen_cash,
405 debt_cash: f.debt_cash,
406 avl_withdrawal_cash: f.avl_withdrawal_cash,
407 // v1.4.73 BUG-004
408 currency: f.currency,
409 available_funds: f.available_funds,
410 unrealized_pl: f.unrealized_pl,
411 realized_pl: f.realized_pl,
412 risk_level: f.risk_level,
413 risk_status: f.risk_status,
414 initial_margin: f.initial_margin,
415 maintenance_margin: f.maintenance_margin,
416 margin_call_margin: f.margin_call_margin,
417 max_power_short: f.max_power_short,
418 net_cash_power: f.net_cash_power,
419 long_mv: f.long_mv,
420 short_mv: f.short_mv,
421 pending_asset: f.pending_asset,
422 max_withdrawal: f.max_withdrawal,
423 is_pdt: f.is_pdt,
424 pdt_seq: f.pdt_seq.clone(),
425 beginning_dtbp: f.beginning_dtbp, // v1.4.98 T1-4
426 remaining_dtbp: f.remaining_dtbp,
427 dt_call_amount: f.dt_call_amount,
428 dt_status: f.dt_status,
429 securities_assets: f.securities_assets,
430 fund_assets: f.fund_assets,
431 bond_assets: f.bond_assets,
432 crypto_mv: f.crypto_mv,
433 exposure_level: f.exposure_level,
434 exposure_limit: f.exposure_limit,
435 used_limit: f.used_limit,
436 remaining_limit: f.remaining_limit,
437 // v1.4.74 C1 BUG-004 Phase 2
438 cash_info_list: f
439 .cash_info_list
440 .iter()
441 .map(|c| FundsCashInfo {
442 currency: c.currency,
443 cash: c.cash,
444 available_balance: c.available_balance,
445 net_cash_power: c.net_cash_power,
446 })
447 .collect(),
448 market_info_list: f
449 .market_info_list
450 .iter()
451 .map(|m| FundsMarketInfo {
452 trd_market: m.trd_market,
453 assets: m.assets,
454 })
455 .collect(),
456 }
457 }
458}
459
460/// 持仓信息
461///
462/// v1.4.94 Tier M2 (mobile-driven extension): 加 `diluted_cost_price` /
463/// `average_cost_price` / `average_pl_ratio` / `currency` / `trd_market` 字段,
464/// 对齐 OpenD `Trd_Common.proto Position` 字段 32-34 + 30-31 + mobile NN
465/// `aas_cmn.proto CostProfitCalcMethod` 用 case (JP 加权平均 / 美 开仓价).
466///
467/// **`cost_price` (字段 8) 已 deprecated** (proto 注释: "已废弃,请使用
468/// dilutedCostPrice 或 averageCostPrice"), 但保留向后兼容. 客户端推荐用新字段:
469/// - `diluted_cost_price`: 摊薄成本价 (HK/US/CN 默认显示)
470/// - `average_cost_price`: 平均成本价 (JP 信用 / 模拟交易证券默认)
471/// - `average_pl_ratio`: 基于 average_cost_price 的盈亏百分数值
472#[derive(Debug, Clone)]
473pub struct Position {
474 /// 服务端分配的持仓 ID
475 pub position_id: u64,
476 /// 持仓方向(0=多 / 1=空,对齐 proto `PositionSide`)
477 pub position_side: i32,
478 /// 证券代码(市场内 code,不含 `MKT.` 前缀)
479 pub code: String,
480 /// 证券名称(中文或本地化)
481 pub name: String,
482 /// 持仓数量
483 pub qty: f64,
484 /// 可卖数量(已扣除冻结 / 当日买入不可卖等)
485 pub can_sell_qty: f64,
486 /// 当前价
487 pub price: f64,
488 /// 持仓均价(**已废弃**,用 diluted_cost_price 或 average_cost_price)
489 pub cost_price: f64,
490 /// 持仓市值(`qty * price`)
491 pub val: f64,
492 /// 盈亏金额
493 pub pl_val: f64,
494 /// C++ APIServer 原样返回的持仓盈亏比例数值(基于 cost_price 旧字段)。
495 /// Rust gateway/API/JSON 保持该数值不变;CLI 展示层再格式化为带符号的
496 /// 百分比字符串,例如 `0.6078` 显示为 `+60.78%`。
497 pub pl_ratio: f64,
498 /// v1.4.94 Tier M2: 摊薄成本价 (proto 字段 32, 仅证券账户)
499 /// 对齐 C++ `Trd_Common.proto:411` "仅支持证券账户使用".
500 pub diluted_cost_price: Option<f64>,
501 /// v1.4.94 Tier M2: 平均成本价 (proto 字段 33, 模拟交易证券账户不适用)
502 pub average_cost_price: Option<f64>,
503 /// v1.4.94 Tier M2: 平均成本价的盈亏百分数值 (proto 字段 34)
504 pub average_pl_ratio: Option<f64>,
505 /// v1.4.94 Tier M2: 货币类型 (proto 字段 30, 取值 Currency enum)
506 pub currency: Option<i32>,
507 /// v1.4.94 Tier M2: 交易市场 (proto 字段 31, 取值 TrdMarket enum)
508 pub trd_market: Option<i32>,
509 /// C++ OpenD 10.6 `Position.comboID` (proto field 35).
510 pub combo_id: Option<u64>,
511 /// C++ OpenD 10.6 `Position.strategyType` (proto field 36).
512 pub strategy_type: Option<i32>,
513 /// C++ OpenD 10.6 `Position.positionType` (proto field 37).
514 pub position_type: Option<i32>,
515 /// C++ OpenD 10.6 `Position.accID` (proto field 38).
516 pub acc_id: Option<u64>,
517 /// C++ OpenD 10.6 `Position.jpAccType` (proto field 39).
518 pub jp_acc_type: Option<i32>,
519 /// Legacy Rust-only option DTE field. Authoritative C++ uses public proto
520 /// tag 40 for `payoutIfWin`, so this value is no longer transported on the
521 /// FTAPI wire and remains `None` for source compatibility.
522 #[deprecated(note = "not part of the C++ FTAPI Position wire contract")]
523 pub expiry_date_distance: Option<i32>,
524 /// Event-contract payout exposed by C++ as `Position.payoutIfWin` tag 40.
525 pub payout_if_win: Option<f64>,
526}
527
528impl Position {
529 #[allow(deprecated)]
530 pub fn from_proto(p: &futu_proto::trd_common::Position) -> Self {
531 Self {
532 position_id: p.position_id,
533 position_side: p.position_side,
534 code: p.code.clone(),
535 name: p.name.clone(),
536 qty: p.qty,
537 can_sell_qty: p.can_sell_qty,
538 price: p.price,
539 cost_price: p.cost_price.unwrap_or(0.0),
540 val: p.val,
541 pl_val: p.pl_val,
542 pl_ratio: p.pl_ratio.unwrap_or(0.0),
543 // v1.4.94 Tier M2: 抽 mobile-aligned 字段
544 diluted_cost_price: p.diluted_cost_price,
545 average_cost_price: p.average_cost_price,
546 average_pl_ratio: p.average_pl_ratio,
547 currency: p.currency,
548 trd_market: p.trd_market,
549 combo_id: p.combo_id,
550 strategy_type: p.strategy_type,
551 position_type: p.position_type,
552 acc_id: p.acc_id,
553 jp_acc_type: p.jp_acc_type,
554 expiry_date_distance: None,
555 payout_if_win: p.payout_if_win,
556 }
557 }
558}
559
560/// 下单参数
561#[derive(Debug, Clone)]
562pub struct PlaceOrderParams {
563 /// 交易头(env + acc_id + market)
564 pub header: TrdHeader,
565 /// 买卖方向
566 pub trd_side: TrdSide,
567 /// 订单类型(限价 / 市价 / 竞价 / 止损 / ...)
568 pub order_type: OrderType,
569 /// 证券代码
570 pub code: String,
571 /// 下单数量
572 pub qty: f64,
573 /// 下单价(限价单必填;市价单可空)
574 pub price: Option<f64>,
575 /// 价格调整开关(超出涨跌幅时是否自动调整到 limit 内)
576 pub adjust_price: Option<bool>,
577 /// 调整侧与幅度(配合 `adjust_price`,百分比范围内向内调整)
578 pub adjust_side_and_limit: Option<f64>,
579 /// v1.4.39: 可选幂等键。设置后,`place_order` 会根据此键派生 `Common.PacketID`
580 /// 的 `conn_id`(serial_no=0),使同一键的重试命中 daemon 端 90s TTL cache,
581 /// 返回缓存结果而不真实下单。external reviewer v1.4.38 报告发现 CLI/MCP 没接此机制 → 修。
582 pub idempotency_key: Option<String>,
583 // v1.4.53 F1 条件单:对齐 FTAPI `Trd_PlaceOrder.C2S.auxPrice` / `trailType`
584 // / `trailValue` / `trailSpread`。仅对 Stop / StopLimit / MIT / LIT /
585 // TrailingStop / TrailingStopLimit 等 order_type 生效。
586 /// 止损/止盈触发价(FTAPI `auxPrice`)。
587 pub aux_price: Option<f64>,
588 /// 跟踪类型 1=Ratio(比例)/ 2=Amount(金额),对 Trailing 变种有效。
589 pub trail_type: Option<i32>,
590 /// 跟踪金额 / 百分比(`trail_type=1` 时为百分比,`trail_type=2` 时为金额)。
591 pub trail_value: Option<f64>,
592 /// 指定价差(跟踪限价单 TrailingStopLimit 用)。
593 pub trail_spread: Option<f64>,
594}
595
596/// 下单高级选项。
597///
598/// 这些字段直接对应 `Trd_PlaceOrder.C2S` 的 optional 字段。保留在独立
599/// options 结构里,避免给现有 `PlaceOrderParams { ... }` 调用方制造源码级
600/// breaking change。
601#[derive(Debug, Clone, Default)]
602pub struct PlaceOrderOptions {
603 /// 订单有效期限:0=DAY, 1=GTC, 2=IOC, 3=GTD。
604 pub time_in_force: Option<i32>,
605 /// 是否允许美股盘前/盘后成交。C++ 会把缺省当 false。
606 pub fill_outside_rth: Option<bool>,
607 /// 美股订单时段:0=NONE, 1=RTH, 2=ETH, 3=ALL, 4=OVERNIGHT。
608 pub session: Option<i32>,
609 /// GTD 到期日期,格式 `YYYY-MM-DD`,仅在 `time_in_force=3` 时有效。
610 pub expire_time: Option<String>,
611 /// Event Contract cash amount. The daemon derives the two-decimal qty.
612 pub amount: Option<f64>,
613 /// Event Contract side: 1=Yes, 2=No.
614 pub pred_side: Option<i32>,
615}
616
617/// 下单结果
618#[derive(Debug, Clone)]
619pub struct PlaceOrderResult {
620 pub order_id: u64,
621}
622
623/// 下单结果,包含参照版本同时返回的无损 backend identity。
624///
625/// 这是 [`PlaceOrderResult`] 的 additive companion,避免给既有 public struct
626/// 直接增加字段而破坏外部调用者的结构体构造与解构源码兼容性。
627#[derive(Debug, Clone)]
628pub struct PlaceOrderResultWithIdentity {
629 pub order_id: u64,
630 /// Backend/server order identity returned by C++ as `orderIDEx`.
631 ///
632 /// This string is the lossless identity for automation and is accepted by
633 /// modify/cancel helpers. Keep it alongside the C++ numeric projection so
634 /// JSON consumers never have to round-trip a `u64` through IEEE-754.
635 pub order_id_ex: String,
636}
637
638/// 改单参数
639#[derive(Debug, Clone)]
640pub struct ModifyOrderParams {
641 pub header: TrdHeader,
642 pub order_id: u64,
643 /// v1.4.110: backend/server order id string (`orderIDEx`).
644 /// C++ accepts this as an alternative to `orderID` and hashes it back to
645 /// `orderID` at APIServer entry.
646 pub order_id_ex: Option<String>,
647 pub modify_order_op: ModifyOrderOp,
648 pub qty: Option<f64>,
649 pub price: Option<f64>,
650 pub for_all: Option<bool>,
651 /// v1.4.39: 可选幂等键。同 `PlaceOrderParams.idempotency_key`。
652 pub idempotency_key: Option<String>,
653}