Skip to main content

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}