ek_ctp/wire.rs
1//! CTP 数据行的内部规范形态(wire)。
2//!
3//! 来源:原稿 `enkeep_ctp/enkeep_ctp/gateway/ctp_gateway.py` 各回调**实际取用的
4//! 字段子集**(TThostFtdc*Field 的窄面)。原生 FFI 镜像(native 特性,0.2.0)将
5//! C++ 结构体逐字段搬进这些结构;mock 测试直接构造 wire 行。
6//!
7//! - 数值统一 `f64`/整数用 `i64`:ek-data 对象面如此,零损耗复制。
8//! - 码值字符串(状态/方向/开平/条件)保持 CTP 原值(如 `'3'`),翻译发生在
9//! [`crate::map`]——wire 永远是"未解释"面。
10
11/// CTP 响应错误行(`CThostFtdcRspInfoField` 窄面:ErrorID/ErrorMsg)。
12#[derive(Debug, Clone, PartialEq, Eq, Default)]
13pub struct ErrorField {
14 /// 错误代码(0 = 成功)
15 pub error_id: i32,
16 /// 错误信息
17 pub error_msg: String,
18}
19
20impl ErrorField {
21 /// 空错误(成功语义,原稿 `if not error["ErrorID"]` 的对偶面)。
22 pub fn ok() -> Self {
23 Self {
24 error_id: 0,
25 error_msg: String::new(),
26 }
27 }
28}
29
30/// 深度行情行(`CThostFtdcDepthMarketDataField` 窄面;md `onRtnDepthMarketData`
31/// 336 处实际取用字段)。
32///
33/// 原稿取用:InstrumentID/Volume/Turnover/OpenInterest/LastPrice/UpperLimitPrice/
34/// LowerLimitPrice/OpenPrice/HighestPrice/LowestPrice/PreClosePrice/BidPrice1..5/
35/// AskPrice1..5/BidVolume1..5/AskVolume1..5/UpdateTime/UpdateMillisec/ActionDay。
36#[derive(Debug, Clone, PartialEq, Default)]
37pub struct DepthMarketData {
38 /// 合约代码(CTP InstrumentID,不带交易所)
39 pub instrument_id: String,
40 /// 交易所代码(0.12.0 `D-XFALLBACK` 新增的镜像字段)。
41 ///
42 /// 原稿**不读**这个字段(它用合约表查交易所),所以这是本数据面比原稿多的
43 /// 一个字段 —— 记档偏差见 DESIGN `D-XFALLBACK`。
44 ///
45 /// 存在的唯一理由:合约不在册时,没有它就判不出交易所,只能把整条行情丢掉。
46 /// 柜台的 `CThostFtdcDepthMarketDataField` 本来就带 `ExchangeID`,读它是
47 /// **取用既有事实**,不是猜。
48 pub exchange_id: String,
49 /// 最新价(MAX_FLOAT 表全停板,原稿 331 行 `adjust_price` 归零处理)
50 pub last_price: f64,
51 /// 当日累计成交量
52 pub volume: f64,
53 /// 当日累计成交额
54 pub turnover: f64,
55 /// 持仓量
56 pub open_interest: f64,
57 /// 涨停价
58 pub upper_limit_price: f64,
59 /// 跌停价
60 pub lower_limit_price: f64,
61 /// 今开
62 pub open_price: f64,
63 /// 最高
64 pub highest_price: f64,
65 /// 最低
66 pub lowest_price: f64,
67 /// 昨收
68 pub pre_close_price: f64,
69 /// 买一价..买五价(MAX_FLOAT 时 `adjust_price` 归零)
70 pub bid_price1: f64,
71 /// 买二价
72 pub bid_price2: f64,
73 /// 买三价
74 pub bid_price3: f64,
75 /// 买四价
76 pub bid_price4: f64,
77 /// 买五价
78 pub bid_price5: f64,
79 /// 卖一价
80 pub ask_price1: f64,
81 /// 卖二价
82 pub ask_price2: f64,
83 /// 卖三价
84 pub ask_price3: f64,
85 /// 卖四价
86 pub ask_price4: f64,
87 /// 卖五价
88 pub ask_price5: f64,
89 /// 买一量
90 pub bid_volume1: f64,
91 /// 买二量
92 pub bid_volume2: f64,
93 /// 买三量
94 pub bid_volume3: f64,
95 /// 买四量
96 pub bid_volume4: f64,
97 /// 买五量
98 pub bid_volume5: f64,
99 /// 卖一量
100 pub ask_volume1: f64,
101 /// 卖二量
102 pub ask_volume2: f64,
103 /// 卖三量
104 pub ask_volume3: f64,
105 /// 卖四量
106 pub ask_volume4: f64,
107 /// 卖五量
108 pub ask_volume5: f64,
109 /// 更新时间("HH:MM:SS")
110 pub update_time: String,
111 /// 更新毫秒段
112 pub update_millisec: i64,
113 /// 交易日("YYYYMMDD";报文 `TradingDay` 原值,业务日的权威来源。记档偏差:
114 /// 原稿不读该字段,这是从 DepthMarketData 上岸的唯一新字段,见 DESIGN D-TZ)
115 pub trading_day: String,
116 /// 行动日("YYYYMMDD")
117 pub action_day: String,
118}
119
120/// 合约行(`CThostFtdcInstrumentField` 窄面;td `onRspQryInstrument` 632-654 行
121/// 实际取用字段)。
122///
123/// 原稿注记(619-631 行样例):合约行的 `ExchangeID` 是交易所真源;`ProductClass`
124/// 决定资产大类映射;`ProductID` 为品种代码。
125#[derive(Debug, Clone, PartialEq, Default)]
126pub struct InstrumentField {
127 /// 合约代码
128 pub instrument_id: String,
129 /// 交易所代码("CFFEX"/"SHFE"/"CZCE"/"DCE"/"INE"/"GFEX")
130 pub exchange_id: String,
131 /// 合约名称
132 pub instrument_name: String,
133 /// 产品类码(`THOST_FTDC_PC_*`,map::asset_from 翻译)
134 pub product_class: String,
135 /// 合约乘数(VolumeMultiple)
136 pub volume_multiple: f64,
137 /// 最小价格变动(PriceTick)
138 pub price_tick: f64,
139 /// 限价单最大上量
140 pub max_limit_order_volume: f64,
141 /// 限价单最小上量
142 pub min_limit_order_volume: f64,
143 /// 市价单最大上量
144 pub max_market_order_volume: f64,
145 /// 市价单最小上量
146 pub min_market_order_volume: f64,
147 /// 交割年
148 pub delivery_year: i64,
149 /// 交割月
150 pub delivery_month: i64,
151 /// 品种代码
152 pub product_id: String,
153 /// 是否可交易(1/0)
154 pub is_trading: i64,
155 /// 上市日(`YYYYMMDD`,空 = 柜台未给)。
156 ///
157 /// 合约表**不提供**涨跌停价 —— 那两个只在行情 `DepthMarketData` 里,
158 /// 已由 `TickData.limit_up/limit_down` 承担。
159 pub open_date: String,
160}
161
162/// 委托/订单行(`CThostFtdcOrderField` 窄面;td `onRspOrderInsert` 494-511 与
163/// `onRtnOrder` 669-718 行实际取用字段)。
164#[derive(Debug, Clone, PartialEq, Default)]
165pub struct OrderField {
166 /// 合约代码
167 pub instrument_id: String,
168 /// 交易所代码(0.7.0 起回报自带;合约表查 exchange 的时序依赖退役 ——
169 /// 委托回报不再要求合约在册,「按勾选丢弃」在回报侧也随之下线)
170 pub exchange_id: String,
171 /// 本地报单号(OrderRef,零起递增)
172 pub order_ref: String,
173 /// 前置编号
174 pub front_id: i64,
175 /// 会话编号
176 pub session_id: i64,
177 /// 交易所报单编号(OrderSysID)
178 pub order_sys_id: String,
179 /// 报单方向码(`'0'`买/`'1'`卖)
180 pub direction: String,
181 /// 组合开平标志码(OFEN_*;原稿 708 行读 `CombOffsetFlag`)
182 pub comb_offset_flag: String,
183 /// 报单价格类型码(OPT_*)
184 pub order_price_type: String,
185 /// 有效期类型码(TC_*)
186 pub time_condition: String,
187 /// 成交量类型码(VC_*)
188 pub volume_condition: String,
189 /// 委托价
190 pub limit_price: f64,
191 /// 报单中的委托总量
192 pub volume_total_original: f64,
193 /// 尚未成交量
194 pub volume_total: f64,
195 /// 已成交数量
196 pub volume_traded: f64,
197 /// 报单状态码(OST_*;map::status_of 翻译)
198 pub order_status: String,
199 /// 报单日期("YYYYMMDD")
200 pub insert_date: String,
201 /// 委托时间("HH:MM:SS")
202 pub insert_time: String,
203}
204
205/// 成交行(`CThostFtdcTradeField` 窄面;td `onRtnTrade` 720-747 行实际取用字段)。
206///
207/// 注意(原稿 741 与 708 的差异):成交行读 `OffsetFlag`(单偏移),委托行读
208/// `CombOffsetFlag`(组合偏移);wire 分列如实保留。
209#[derive(Debug, Clone, PartialEq, Default)]
210pub struct TradeField {
211 /// 合约代码
212 pub instrument_id: String,
213 /// 交易所代码(0.7.0:同 OrderField)
214 pub exchange_id: String,
215 /// 交易所报单编号(与 OrderData 的映射键)
216 pub order_sys_id: String,
217 /// 成交编号(TradeID)
218 pub trade_id: String,
219 /// 报单方向码('0'/'1')
220 pub direction: String,
221 /// 单偏移开平标志码(OF_*;原稿 741 行)
222 pub offset_flag: String,
223 /// 成交价格
224 pub price: f64,
225 /// 成交数量
226 pub volume: f64,
227 /// 成交日期("YYYYMMDD")
228 pub trade_date: String,
229 /// 成交时间("HH:MM:SS")
230 pub trade_time: String,
231}
232
233/// 持仓明细行(`CThostFtdcInvestorPositionField` 窄面;td `onRspQryInvestorPosition`
234/// 543-586 行实际取用字段)。
235///
236/// CTP 语义候选源:同一 (InstrumentID, PosiDirection) 键可能多行(上期 Today/
237/// Yesterday 分行),合并算法在驱动层(仓库 driver.rs 持仓合并块,546-595 行)。
238#[derive(Debug, Clone, PartialEq, Default)]
239pub struct InvestorPositionField {
240 /// 合约代码
241 pub instrument_id: String,
242 /// 交易所代码(0.7.0:同 OrderField)
243 pub exchange_id: String,
244 /// 持仓方向码(PD_*:'1'净/'2'多/'3'空;map::direction_from 同表)
245 pub posi_direction: String,
246 /// 上日持仓(YdPosition)
247 pub yd_position: f64,
248 /// 总持仓(Position)
249 pub position: f64,
250 /// 今日持仓(TodayPosition)
251 pub today_position: f64,
252 /// 持仓成本(PositionCost)
253 pub position_cost: f64,
254 /// 持仓盈亏(PositionProfit)
255 pub position_profit: f64,
256 /// 空头冻结量(多向仓记其值)
257 pub short_frozen: f64,
258 /// 多头冻结量(空向仓记其值)
259 pub long_frozen: f64,
260}
261
262/// 资金行(`CThostFtdcTradingAccountField` 窄面;td `onRspQryTradingAccount`
263/// 604-610 行实际取用字段)。
264#[derive(Debug, Clone, PartialEq, Default)]
265pub struct TradingAccountField {
266 /// 账户号
267 pub account_id: String,
268 /// 当前权益(Balance)
269 pub balance: f64,
270 /// 冻结的保证金(并进 frozen;原稿 607 三项求和)
271 pub frozen_margin: f64,
272 /// 冻结的现金
273 pub frozen_cash: f64,
274 /// 冻结的手续费
275 pub frozen_commission: f64,
276 /// 可用资金(⚠ 见 DESIGN.md D-AVAIL)
277 pub available: f64,
278}
279
280/// 下单输入行(原稿 `send_order` 808-842 行 ctp_req 的字段面)。
281#[derive(Debug, Clone, PartialEq, Default)]
282pub struct InputOrder {
283 /// 合约代码
284 pub instrument_id: String,
285 /// 交易所代码
286 pub exchange_id: String,
287 /// 委托价
288 pub limit_price: f64,
289 /// 委托总量(int(req.volume) 截断)
290 pub volume_total_original: i64,
291 /// 报单价格类型码
292 pub order_price_type: String,
293 /// 报单方向码
294 pub direction: String,
295 /// 组合开平标志码
296 pub comb_offset_flag: String,
297 /// 本地报单号(str(order_ref))
298 pub order_ref: String,
299 /// 投资者代码
300 pub investor_id: String,
301 /// 用户代码
302 pub user_id: String,
303 /// 经纪公司代码
304 pub broker_id: String,
305 /// 组合投机套保标志('1'=投机)
306 pub comb_hedge_flag: String,
307 /// 触发条件('1'=立即)
308 pub contingent_condition: String,
309 /// 强平原因('0'=非强平)
310 pub force_close_reason: String,
311 /// 自动挂起标志(0)
312 pub is_auto_suspend: i32,
313 /// 有效期类型码
314 pub time_condition: String,
315 /// 成交量类型码
316 pub volume_condition: String,
317 /// 最小成交量(原稿固定 1)
318 pub min_volume: i64,
319}
320
321/// 撤单输入行(原稿 `cancel_order` 856-872 行 ctp_req 的字段面)。
322#[derive(Debug, Clone, PartialEq, Default)]
323pub struct InputOrderAction {
324 /// 合约代码
325 pub instrument_id: String,
326 /// 交易所代码
327 pub exchange_id: String,
328 /// 本地报单号(req.orderid 三段中的第三段)
329 pub order_ref: String,
330 /// 前置编号
331 pub front_id: i64,
332 /// 会话编号
333 pub session_id: i64,
334 /// 报单操作标志('0'=删除,AF_Delete)
335 pub action_flag: String,
336 /// 经纪公司代码
337 pub broker_id: String,
338 /// 投资者代码
339 pub investor_id: String,
340}
341
342// ---------------------------------------------------------------------------
343// 附加接口面(超出原稿,2026-10-16 定):保证金率/手续费率/交易状态三件。
344// 镜像策略与 InstrumentField 一致:保留字段 reserve1/2 不进窄面,字段顺序与
345// CTP struct 声明序一致。输入 POD 面与 InputOrderAction 同规(写方向)。
346// ---------------------------------------------------------------------------
347
348/// 合约保证金率行(`CThostFtdcInstrumentMarginRateField` 窄面,reserve1 省略)。
349///
350/// SDK 源(6.7.7,ThostFtdcUserApiStruct.h:861-885):InvestorRange/BrokerID/
351/// InvestorID/HedgeFlag/四比率/IsRelative/ExchangeID/InvestUnitID/InstrumentID。
352/// 投机/套保档由 `hedge_flag` 区分('1'投机/'2'套利/'3'套保)。
353#[derive(Debug, Clone, PartialEq, Default)]
354pub struct InstrumentMarginRateField {
355 /// 投资者范围码(IR_*:'0'所有/'1'投资者组/'2'投资者/'3'合约)
356 pub investor_range: String,
357 /// 经纪公司代码
358 pub broker_id: String,
359 /// 投资者代码
360 pub investor_id: String,
361 /// 投机套保标志码
362 pub hedge_flag: String,
363 /// 多头保证金率(按金额)
364 pub long_margin_ratio_by_money: f64,
365 /// 多头保证金费(按量)
366 pub long_margin_ratio_by_volume: f64,
367 /// 空头保证金率(按金额)
368 pub short_margin_ratio_by_money: f64,
369 /// 空头保证金费(按量)
370 pub short_margin_ratio_by_volume: f64,
371 /// 是否相对交易所收取(1/0)
372 pub is_relative: i32,
373 /// 交易所代码
374 pub exchange_id: String,
375 /// 投资单元代码
376 pub invest_unit_id: String,
377 /// 合约代码
378 pub instrument_id: String,
379}
380
381/// 合约手续费率行(`CThostFtdcInstrumentCommissionRateField` 窄面,reserve1 省略)。
382///
383/// SDK 源(6.7.7,ThostFtdcUserApiStruct.h:892-920):六比率/ExchangeID/
384/// BizType/InvestUnitID/InstrumentID。BizType '1'=期货(THOST_FTDC_BZTYP_All?)。
385#[derive(Debug, Clone, PartialEq, Default)]
386pub struct InstrumentCommissionRateField {
387 /// 投资者范围码(IR_*)
388 pub investor_range: String,
389 /// 经纪公司代码
390 pub broker_id: String,
391 /// 投资者代码
392 pub investor_id: String,
393 /// 开仓手续费率(按金额)
394 pub open_ratio_by_money: f64,
395 /// 开仓手续费(按量,f64 承 RatioType)
396 pub open_ratio_by_volume: f64,
397 /// 平仓手续费率(按金额)
398 pub close_ratio_by_money: f64,
399 /// 平仓手续费(按量)
400 pub close_ratio_by_volume: f64,
401 /// 平今手续费率(按金额)
402 pub close_today_ratio_by_money: f64,
403 /// 平今手续费(按量)
404 pub close_today_ratio_by_volume: f64,
405 /// 交易所代码
406 pub exchange_id: String,
407 /// 业务类型码(BizType)
408 pub biz_type: String,
409 /// 投资单元代码
410 pub invest_unit_id: String,
411 /// 合约代码
412 pub instrument_id: String,
413}
414
415/// 合约交易状态行(`CThostFtdcInstrumentStatusField` 窄面,reserve1/2 省略)。
416///
417/// SDK 源(6.7.7,ThostFtdcUserApiStruct.h:5666-5683)。**纯被动**:OnRtnInstrumentStatus
418/// 推送(SDK 无 ReqQryInstrumentStatus,2026-10-16 源头核实);SimNow 实测 0 帧。
419#[derive(Debug, Clone, PartialEq, Default)]
420pub struct InstrumentStatusField {
421 /// 交易所代码
422 pub exchange_id: String,
423 /// 结算组代码
424 pub settlement_group_id: String,
425 /// 合约交易状态码(IS_*:'0'开盘前/'1'非交易/'2'连续交易/'3'集合竞价报单/
426 /// '4'集合竞价平衡/'5'集合竞价撮合/'6'收盘/'7'业务处理)
427 pub instrument_status: String,
428 /// 交易阶段编号
429 pub trading_segment_sn: i32,
430 /// 进入本状态时间("HH:MM:SS")
431 pub enter_time: String,
432 /// 进入本状态原因码(IER_*:'1'自动切换/'2'手动切换/'3'熔断)
433 pub enter_reason: String,
434 /// 合约在交易所的代码
435 pub exchange_inst_id: String,
436 /// 合约代码
437 pub instrument_id: String,
438}
439
440/// 查询合约保证金率的输入(`CThostFtdcQryInstrumentMarginRateField` 窄面,
441/// reserve1 省略;SDK ThostFtdcUserApiStruct.h:2594-2611)。
442///
443/// 有 HedgeFlag 过滤(投机档 '1' 常用;空则前行为不限?)——Live 验收取 '1'。
444#[derive(Debug, Clone, PartialEq, Default)]
445pub struct QryInstrumentMarginRate {
446 /// 经纪公司代码
447 pub broker_id: String,
448 /// 投资者代码
449 pub investor_id: String,
450 /// 投机套保标志码(过滤档;'1'投机)
451 pub hedge_flag: String,
452 /// 交易所代码(过滤)
453 pub exchange_id: String,
454 /// 投资单元代码(过滤)
455 pub invest_unit_id: String,
456 /// 合约代码(过滤)
457 pub instrument_id: String,
458}
459
460/// 查询合约手续费率的输入(`CThostFtdcQryInstrumentCommissionRateField` 窄面,
461/// reserve1 省略;SDK ThostFtdcUserApiStruct.h:2613-2630)。
462///
463/// **没有 HedgeFlag**(与保证金率查询不同;省略保留字段)。
464#[derive(Debug, Clone, PartialEq, Default)]
465pub struct QryInstrumentCommissionRate {
466 /// 经纪公司代码
467 pub broker_id: String,
468 /// 投资者代码
469 pub investor_id: String,
470 /// 交易所代码(过滤)
471 pub exchange_id: String,
472 /// 投资单元代码(过滤)
473 pub invest_unit_id: String,
474 /// 合约代码(过滤)
475 pub instrument_id: String,
476}