ek-ctp
CTP 期货行情/交易网关(Rust 移植版,enkeep_trade 生态)。
对 enkeep_ctp(vnpy 的 ctp_gateway 移植层)行为逐字移植。
0.1.0(已发布)为纯逻辑层;0.2.0(开发中)分批交付 native 桥——
SDK 已随 crates.io 包发布(下游安装即得、无需自备),特性与 build.rs 见下节。
结构
| 模块 | 职责 |
|---|---|
wire |
CTP 官方字段的最小子集(仅 DepthMarketData/InstrumentField/OrderField/TradeField/仓位/资金/插撤单输入) |
map |
CTP 码值 ↔ ek-data 类型(状态/方向/开平/单型/交易所/时间戳/价格修整) |
driver |
行情/交易两条纯状态机(MdDriver::step/TdDriver::step),零线程零时钟 |
plugin |
CtpPlugin:ek-core 宿主插件。出门 9 线值:eTick/eContract/eOrder/eTrade/ePosition/eAccount/ePositionDone/eContractFinish/eLog;进门 eTimer 与 eReq.<gw> 三服务 |
native |
(仅 native 特性)官方 C++ SDK 的 C ABI 桥;M2 = MD + TD 全面(会话、12 个交易回调、字段映射、传输层实现) |
快速用法
纯逻辑驱动层(不带任何网络库,可直接驱动 mock 做测试):
use ;
let mut td = new;
for out in td.step
总线级(同 ek-oms):
let cfg = CtpConfig ;
host.add_plugin?; // 默认构建无传输;动作被吸收
native(0.2.0)
M2 已落地(MD + TD 全面),用 --features native 启用(默认关闭,纯逻辑消费方与 docs.rs
不触碰 C++ 工具链)。SDK 已随包发布(D-SDK-SHIP):四份 ThostFtdc*.h + win64 导入库/DLL
- linux SO 都在
native/sdk/(来源enkeep_ctp自带的一套,头与二进制同源,API 版本 6.7.7、 DLL 内嵌构建时间戳20240607),压缩后约 5.9MB、在 crates.io 单包 10MB 上限内 —— 下游cargo add ek-ctp即得,不需要自备 SDK。许可边界见NOTICE: 本包的 MIT 不覆盖这些上期技术二进制。
cargo test --features native # 46 用例:逻辑 21 + native 侧 25(POD 自检 14 / 传输层 3 / 离线冒烟 8)
powershell -ExecutionPolicy Bypass -File scripts/gate.ps1 -WithNative # 门禁含 native
- 形态:
src/native/(common共用工具 +md/td两面的extern "C"声明与 RAII 句柄pod的 CTP↔POD 转换 +transport的CtpTransport官方实现)- 两个 C++ 单元(
native/shim/ek_ctp_shim.{h,cpp}会话/回调面、ek_ctp_pod.{h,cpp}字段面, 唯一说 C++ 的地方) - 手写
build.rs(仅 std,零第三方 crate;特性关闭时完全 no-op)。
- 平台:只支持 x86_64-pc-windows-msvc;其他目标在
build.rs里给出明确报错, 而不是让用户在链接期面对读不懂的符号错误。Linux/GCC 路径尚未实现(SDK 的.so已随包备好,待补;见 DESIGN D-NATIVE-TARGET)。 - 工具链:MSVC 环境优先用现成的
cl.exe/INCLUDE/LIB;缺失时经vswhere自建, 不要求"VS 开发者命令行"(EK_CTP_FORCE_VSWHERE=1可强制走 vswhere 路径以便自验)。 - 运行期 DLL:
build.rs把thost*.dll投送到target/<profile>/与deps/—— Windows 只在 exe 所在目录、系统目录、cwd、PATH 里找 DLL,两处都要有。 - ABI 自检三层:① 回调表 —— shim 导出
sizeof,测试断言等于 Rust 侧size_of(MD/TD 各一张表);② 每个事件 POD —— 同样的sizeof断言;③ 字段映射 ——ek_pod_selftest_*在 C++ 里构造带唯一值的 CTP 报文、经真实转换函数后由 Rust 逐字段断言(顺序/类型/映射错位都会被抓住)。C++ 侧每处 SPI 覆盖都带override, SDK 换了虚函数签名会编译失败,而不是留到运行期崩。 - 线程模型:SPI 回调跑在 CTP 自己的线程上,不引入 ingress 线程 —— 回调直接步进
插件(驱动状态自锁在
Mutex里,PluginContext可跨线程put),与原稿"回调里直接 上抛"同构(见 DESIGN D-NATIVE-THREAD)。 - 连接触发在插件内:宿主
start()→on_start→CtpPlugin::connect()(先 TD 后 MD); 也可显式调用。CtpPlugin::with_native(cfg, flow_root)一次完成"传输层 + 回调接线"装配 (回调入口用Weak回指插件,避免循环引用)。 - 离线可验:native 用例完全不触网(编译 → 链接 → DLL 加载 → 创建/装配/析构 + 版本串
- POD 逐字段映射);真连网登录(SimNow)属 M3,且需本机能直达前置地址。
shim/build.rs/.ps1正文一律 ASCII:CTP 头是 GBK,ASCII 源码 + 不给 MSVC 传/utf-8,它才按系统码页正确解码头文件(见 DESIGN D-NATIVE-ENC)。- 发布前跑
scripts/check-package.ps1:双向断言包内必须含 SDK、不得含私密配置。
本地测试配置(SimNow)
账户与前置地址放在本机私密文件里(已被 .gitignore 忽略,永不提交/推送):
Copy-Item config/simnow.example.env config/simnow.local.env # 然后填入自己的账号
SimNow 现行两套前置(2026-10 实测口径,来源:openctp 模拟环境监控 http://www.openctp.cn/simenv.html):
| 套 | 交易前置 | 行情前置 | 服务时间 |
|---|---|---|---|
| 第一套(仿真,与实盘时段一致,优先) | tcp://182.254.243.31:30001(备用 :30002 / :30003) |
tcp://182.254.243.31:30011(备用 :30012 / :30013) |
与实盘生产时段一致(日盘 / 夜盘) |
| 第二套(7x24) | tcp://182.254.243.31:40001 |
tcp://182.254.243.31:40011 |
全天候(周末与节假日另有维护窗口) |
2026-10 地址变更:SimNow 前置已从
180.168.146.187(电信)/218.202.237.33(移动) 迁到182.254.243.31。老地址的失效形态很好认 —— 电信线路 TCP 超时(包被丢), 移动线路主动拒绝(RST,端口已关)。地址会随 SimNow 运维变动:连不上时先查 openctp 监控页,再改config/simnow.local.env(本仓库已按 2026-10 口径更新)。
选择规则(脚本自动裁决):交易时段(北京时区,日盘 08:55–15:15 / 夜盘 20:55–02:35)→ 第一套;其余时间 → 第二套。两套都通时优先第一套。
powershell -ExecutionPolicy Bypass -File scripts/simnow-fronts.ps1 # 看当前该用哪套(含理由)
powershell -ExecutionPolicy Bypass -File scripts/simnow-fronts.ps1 -Probe # 先实测 TCP 连通性再选
powershell -ExecutionPolicy Bypass -File scripts/simnow-fronts.ps1 -Env # 输出 EK_CTP_TD_ADDR / EK_CTP_MD_ADDR 赋值行
powershell -ExecutionPolicy Bypass -File scripts/simnow-fronts.ps1 -Force 724
脚本正文只含 ASCII(输出为英文):Windows PowerShell 5.1 对无 BOM 的 .ps1 按 ANSI 解码,
中文串会乱码并炸解析;配置文件是 KEY=VALUE,读取时显式按 UTF-8,故其中文注释安全。
排查:-Probe 前置全部 down 时先分清两种形态 —— 超时(包被丢:本机/网络策略或对端下线)
与主动拒绝 RST(对端可达但端口已关:地址过时)。SimNow 前置是裸 TCP(不是 80/443),
可用 Test-NetConnection 182.254.243.31 -Port 40001 或 telnet 182.254.243.31 40001 复核;
本机网络本身是否通,用 Test-NetConnection www.baidu.com -Port 443 对照。
登录字段:BrokerID 9999、AppID simnow_client_test、AuthCode 0000000000000000(16 个 0)。
CtpConfig.td_auth_code 非空即走认证分支(与 enkeep_ctp 原稿 448-451 的分支一致)。
SimNow 仅提供快照行情(无分笔),周末与节假日环境关闭。
真连端到端联调
$env:EK_CTP_FRONT_SET = 'live' # live = 仿真那套 / 724 = 7x24
cargo run --features native --example live_simnow # 真 SDK、真前置,非 mock
examples/live_simnow.rs 按原稿启动顺序跑完整业务面(启动链 → 结算确认 → 合约查询 →
订阅行情并挑"当前真在交易"的标的 → 可成交价下单/成交 → 挂单/撤单 → 拒单探针 ×2 →
资金/持仓),逐步判定 + 完整时间线 + Markdown 报告落盘 target/ctp-live-report.md。
2026-10-15 实测:7 步全 PASS(真实成交 / 撤单 / 拒单 / 资金 / 持仓),详见
LIVE-TEST.md —— 该轮还抓出并修掉两个时间戳真 bug(决策见 DESIGN D-TZ)。
生态依赖(expected)
ek-core0.3.0(宿主/服务)ek-data0.2.2(数据类型)ek-event0.1.0(总线形态)