ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

中信证券CATS平台API接入实战:DLL调用、状态管理与生产加固

中信证券CATS平台API接入实战:DLL调用、状态管理与生产加固 简介本资源是中信证券CATS自动化交易平台的官方API参考文档面向量化交易开发者、程序化交易系统构建者及金融科技从业者解决自动下单、实时行情接入、账户与持仓管理等核心开发需求。文档全面覆盖API初始化、通信会话控制、服务器连接、日志调试、本地内存数据库操作、账户登录与子账户管理、资金持仓及委托成交订阅、Level2逐笔成交与分钟线/日线行情获取等关键功能函数分类清晰、回调机制灵活显著降低底层通信与加密压缩技术门槛。资源为单个PDF文件509KB内容结构完整含调用流程图、函数分类表及详尽参数说明便于快速查阅与集成开发。目前已有3292人学习下载是构建稳定、合规、高性能自动化交易客户端不可或缺的权威技术依据。1. 中信证券CATS平台API不是“调个接口就交易”而是把券商柜台系统当本地服务来用你手上有策略逻辑、有行情数据、有回测结果但一到实盘就卡在「怎么把信号变成委托单」——这不是模型问题是通道问题。中信证券的CATSChina Securities Automated Trading System自动化交易平台本质不是个“开放API文档”而是一套嵌入式交易终端的程序化外挂接口体系它不走HTTP RESTful不依赖OAuth2或JWT令牌不暴露在公网甚至没有独立域名它的API是Windows DLL动态库 进程间通信 本地权限校验的组合体。这意味着你没法像调用DeepSeek或OpenAI那样curl一把就跑通但反过来说只要本地装好CATS客户端、登录有效账户、配置好交易单元它的响应延迟能压到毫秒级委托成功率接近柜台直连。适合的是实盘高频策略、算法拆单、期现套利等对确定性、低延迟、强一致性有硬要求的场景。新手容易误以为这是个“标准金融API”结果卡在环境初始化老手则清楚——这不是调API是接管一台券商柜台的本地代理。2. 环境准备与SDK接入从CATS安装包里抠出真正的可编程入口CATS官方不提供独立SDK下载包所有API能力都打包在客户端安装目录中。常见误区是直接去官网找“API SDK”结果只看到PDF文档和Excel参数表——那只是协议说明不是可执行代码。真实路径是先装CATS桌面客户端v6.8再从其安装目录逆向定位核心DLL。我一般会做三件事确认版本、提取DLL、验证签名。2.1 确认CATS客户端版本与API兼容性边界CATS API存在严格版本锁死机制。v6.5以下使用CatSdk.dllv6.6–v6.7.3使用CatSdkV2.dllv6.8统一为CatSdkV3.dll。不同版本间函数签名、回调结构体、错误码定义全部不兼容。关键动作不是看官网公告而是进客户端主界面 → 帮助 → 关于 → 记下完整版本号如6.8.0.12345再比对CATS技术白皮书附录A的API映射表。很多团队翻车是因为测试环境用v6.7生产环境升级到v6.8后CatSdkV2.dll直接LoadLibrary失败报错ERROR_INVALID_HANDLE而非明确提示版本不匹配。# 在CMD中快速验证当前CATS安装路径与DLL存在性以默认路径为例 dir C:\Program Files (x86)\中信证券\CATS\bin\CatSdkV3.dll # 输出应含文件大小通常为 1.2~1.8 MB和最后修改时间需与客户端版本发布日期吻合提示CATS客户端安装时默认勾选“注册COM组件”这一步必须成功。若后续调用CoCreateInstance失败90%概率是COM注册缺失而非DLL路径错误。用regsvr32 C:\Program Files (x86)\中信证券\CATS\bin\CatSdkV3.dll手动注册需管理员权限。2.2 从安装包解压头文件与类型定义.h / .idlCATS不公开头文件但.dll自带类型导出信息。推荐做法是用dumpbin /exports CatSdkV3.dll exports.txt提取所有导出函数名再结合白皮书中的C语言函数原型反推结构体。例如CatSdk_CreateOrder函数在文档中声明为// 白皮书P23下单函数原型v6.8 int __stdcall CatSdk_CreateOrder( const char* szAccountID, // 资金账号非客户号 const char* szSymbol, // 6位代码如600519 int nSide, // 0买, 1卖 double dPrice, // 限价市价填0.0 int nVolume, // 手数股票为股数期货为手数 int nOrderType, // 0限价, 1市价, 2本方最优 char* szOrderID, // 输出委托编号20字符缓冲区 int nBufSize // szOrderID缓冲区长度 );但实际调用时szAccountID必须是CATS登录后生成的交易单元编码形如SH_A_00123456不是券商给你的普通资金账号。这个编码只能通过CatSdk_GetAccountList()获取且每次登录会变——它绑定的是当前会话的柜台连接实例不是静态账户。2.3 Python ctypes封装最小可行调用链不用C写Wrapper用Python ctypes直接加载DLL更轻量。但必须处理三类底层细节字符串编码GBK、结构体内存对齐、回调函数指针注册。# cat_sdk_wrapper.py import ctypes from ctypes import c_char_p, c_int, c_double, c_void_p # 加载DLL绝对路径相对路径在服务模式下极易失败 cat_sdk ctypes.CDLL(rC:\Program Files (x86)\中信证券\CATS\bin\CatSdkV3.dll) # 声明函数原型关键指定argtypes和restype cat_sdk.CatSdk_CreateOrder.argtypes [ c_char_p, # szAccountID c_char_p, # szSymbol c_int, # nSide c_double, # dPrice c_int, # nVolume c_int, # nOrderType c_char_p, # szOrderID输出缓冲区 c_int # nBufSize ] cat_sdk.CatSdk_CreateOrder.restype c_int # 返回0成功非0错误码 # 实际调用注意所有字符串必须encode(gbk) account_id bSH_A_00123456 symbol b600519 order_id_buf ctypes.create_string_buffer(21) # 20字节1终止符 ret cat_sdk.CatSdk_CreateOrder( account_id, symbol, 0, # 买入 198.5, # 价格 100, # 数量 0, # 限价 order_id_buf, 21 ) if ret 0: print(委托成功编号, order_id_buf.value.decode(gbk)) else: print(委托失败错误码, ret)逻辑说明ctypes.create_string_buffer(21)是硬性要求——CATS内部用strcpy_s写入缓冲区必须比目标长度多1字节bSH_A_00123456中的b前缀不可省略否则ctypes传入Unicode字符串导致GBK乱码委托直接被柜台拒单错误码-1002合约代码非法错误码-1001表示“未登录”-1003表示“资金不足”这些在白皮书附录B有完整映射但必须查文档不能靠猜。3. 账户登录与状态管理别让“已登录”变成玄学判断CATS API没有login()函数登录状态完全依赖客户端进程是否存活、是否完成柜台认证、是否保持心跳。很多策略脚本跑着跑着突然下单失败查日志全是-1001其实不是代码问题是CATS客户端被Windows资源管理器杀掉了。3.1 用进程句柄窗口消息双重校验登录态不能只靠CatSdk_GetLoginStatus()返回值。该函数在客户端假死GUI无响应但进程还在时仍返回1。必须叠加验证import win32gui import win32con def is_cats_logged_in(): # 步骤1检查CATS主窗口是否存在且可见 hwnd win32gui.FindWindow(None, 中信证券CATS交易系统) if not hwnd: return False # 步骤2检查窗口是否最小化或隐藏 if win32gui.IsIconic(hwnd) or not win32gui.IsWindowVisible(hwnd): return False # 步骤3发送自定义消息触发状态同步CATS约定消息号0x8001 try: result win32gui.SendMessage(hwnd, 0x8001, 0, 0) return result 1 # 成功返回1 except: return False # 每30秒轮询一次失败则自动重启CATS客户端 if not is_cats_logged_in(): os.startfile(rC:\Program Files (x86)\中信证券\CATS\CATS.exe)参数说明0x8001是CATS内部定义的“心跳探测消息”只有真正登录且柜台连接正常的窗口才会响应win32gui.IsIconic()检测是否最小化因为CATS在最小化时会主动断开柜台连接以节省资源此方案比单纯psutil查进程名更可靠——CATS.exe进程可能残留但GUI已崩溃。3.2 交易单元动态获取与缓存策略CatSdk_GetAccountList()返回的是当前登录用户所有可用交易单元列表但结构体定义藏在DLL导出符号里。用dumpbin导出后可知其C结构为typedef struct { char szAccountID[32]; // 交易单元ID char szAccountName[64]; // 显示名称如“上海A股001” int nAccountType; // 0A股, 1融资融券, 2期货... int nStatus; // 0可用, 1禁用 } ACCOUNT_INFO;Python调用需手动构造数组class AccountInfo(ctypes.Structure): _fields_ [ (szAccountID, ctypes.c_char * 32), (szAccountName, ctypes.c_char * 64), (nAccountType, ctypes.c_int), (nStatus, ctypes.c_int) ] # 获取账户列表最多100个 accounts (AccountInfo * 100)() count cat_sdk.CatSdk_GetAccountList(accounts, 100) for i in range(count): acc accounts[i] if acc.nStatus 0: # 只取可用单元 print(f可用单元: {acc.szAccountID.decode(gbk)} - {acc.szAccountName.decode(gbk)})关键点CatSdk_GetAccountList()必须在CatSdk_Login()之后调用但CATS没有显式Login函数——它隐式发生在客户端启动并完成柜台认证后。所以首次获取账户列表前必须确保is_cats_logged_in()返回True否则返回空数组。4. 委托生命周期与撤单闭环别让“已报”变成“幽灵单”CATS的委托状态机比交易所原生接口更复杂它包含“已报”客户端已发单但未达柜台、“已报待确认”柜台收到但未返回委托编号、“已确认”有委托编号、“部成”、“已撤”等8种状态。最危险的是“已报”状态——此时单子卡在本地缓存既没进交易所也无法撤单只能重启CATS。4.1 主动轮询委托状态的最小安全间隔CATS不支持WebSocket推送所有状态更新靠轮询CatSdk_GetOrderStatus()。但频繁轮询会触发柜台风控限流错误码-2001请求过于频繁。实测得出安全阈值状态类型最小轮询间隔触发条件已报 / 已报待确认500ms下单后前3秒内必须每500ms查一次已确认 / 部成2s进入此状态后放宽至2秒轮询全部成交 / 已撤10s状态稳定后降频避免无效请求def wait_for_order_confirm(order_id, timeout10): start_time time.time() while time.time() - start_time timeout: status cat_sdk.CatSdk_GetOrderStatus(order_id.encode(gbk)) # status返回值0已报, 1已报待确认, 2已确认, 3部成, 4全部成交, 5已撤... if status in [2, 3, 4, 5]: return status time.sleep(0.5) # 500ms间隔 return -1 # 超时 # 使用示例 ret cat_sdk.CatSdk_CreateOrder(...) if ret 0: order_id order_id_buf.value.decode(gbk) final_status wait_for_order_confirm(order_id) if final_status in [2, 3, 4]: print(委托进入有效状态) else: print(委托超时需人工干预)注意CatSdk_GetOrderStatus()的order_id参数必须是CatSdk_CreateOrder()成功返回的编号不能自己拼接。CATS委托编号格式为SH202405200000001交易所代码日期序列号少一位或多一位都会返回-1004委托编号不存在。4.2 撤单操作的原子性保障CatSdk_CancelOrder()不是“发撤单指令”而是“提交撤单申请”。它成功返回0只表示申请已提交不代表撤单成功。必须紧接着轮询状态直到变为5已撤或6撤单失败。更关键的是同一委托号在CATS中只允许撤单一次重复调用CatSdk_CancelOrder()会返回-1005撤单申请已存在但不会报错容易误判为成功。def safe_cancel_order(order_id): # 第一步提交撤单申请 ret cat_sdk.CatSdk_CancelOrder(order_id.encode(gbk)) if ret ! 0: print(f撤单申请提交失败{ret}) return False # 第二步等待状态变为已撤或撤单失败 for _ in range(20): # 最多等待20秒 status cat_sdk.CatSdk_GetOrderStatus(order_id.encode(gbk)) if status 5: # 已撤 return True elif status 6: # 撤单失败 print(撤单失败原因可能是已成交或已撤) return False time.sleep(0.5) print(撤单等待超时状态未知) return False5. 常见问题排查那些让策略停摆3小时的“小问题”现象 → 原因 → 解决不讲虚的。5.1 现象CatSdk_CreateOrder()返回-1002但代码里szSymbol明明是600519→ 原因CATS要求股票代码必须是6位纯数字字符串不能带交易所前缀如SH600519或后缀如600519.SH且必须用GBK编码传入Python字符串默认UTF-8b600519才是正确形式。→ 解决打印szSymbol的bytes值确认编码用symbol.encode(gbk)强制转换。5.2 现象CatSdk_GetAccountList()返回0个账户但CATS客户端里明明能看到资金账号→ 原因CATS客户端登录时选择了“模拟交易”模式该模式下CatSdk_GetAccountList()不返回实盘交易单元。→ 解决关闭CATS删除%APPDATA%\ChinaSecurities\CATS\config\simu_mode.flag文件重启客户端并选择“实盘交易”。5.3 现象委托状态长期卡在0已报CatSdk_GetOrderStatus()一直不更新→ 原因CATS客户端与柜台网络中断但GUI未弹窗提示。此时进程仍在is_cats_logged_in()可能仍返回True因窗口消息未超时。→ 解决增加TCP连接探测——用socket.connect_ex((114.80.182.123, 7708))中信上海柜台IP端口验证网络可达性失败则强制重启CATS。5.4 现象Python脚本运行一段时间后CatSdk_CreateOrder()开始随机返回-1001未登录→ 原因Windows电源计划设置为“平衡”或“节能”导致CATS客户端进程被系统挂起Suspended内存页换出DLL句柄失效。→ 解决在脚本开头执行os.system(powercfg -change -standby-timeout-ac 0)禁用交流电下的睡眠并用psutil.Process().nice(psutil.REALTIME_PRIORITY_CLASS)提升进程优先级。5.5 现象同一台机器上多个Python进程同时调用CATS API部分进程委托失败→ 原因CATS SDK不是线程安全的CatSdk_CreateOrder()等函数内部使用全局静态变量存储会话上下文。多进程并发调用会相互覆盖。→ 解决用文件锁portalocker或命名互斥体win32event.CreateMutex保证同一时刻只有一个进程在调用CATS API其他进程排队。6. 生产环境加固技巧把CATS当“本地交易所”来运维我在线上跑CATS策略三年踩过所有坑后固化了四条铁律现在新同事入职第一周就要背熟6.1 用Windows服务托管CATS客户端而不是用户登录会话用户会话注销后所有GUI进程被销毁。必须把CATS客户端注册为Windows服务# 用NSSM工具将CATS.exe包装为服务 nssm install CATSService # 在NSSM GUI中设置 # Path: C:\Program Files (x86)\中信证券\CATS\CATS.exe # Service Name: CATSService # Startup type: Automatic # Log on as: LocalSystem关键不能选用户账户 # Service recovery: First failure → Restart service, Second failure → Restart computer提示LocalSystem账户有权限访问所有DLL和注册表且不受用户登录状态影响。但需在CATS安装目录赋予NETWORK SERVICE组读取权限否则服务启动时报Access Denied。6.2 委托日志必须包含“客户端时间戳 柜台返回时间戳 状态变更链”CATS不提供原始报文日志必须自己埋点import datetime def log_order_flow(order_id, action, status_codeNone): now datetime.datetime.now() with open(order_trace.log, a, encodingutf-8) as f: line f[{now.strftime(%Y-%m-%d %H:%M:%S.%f)[:-3]}] line fORDER:{order_id} ACTION:{action} if status_code is not None: line fSTATUS_CODE:{status_code} line \n f.write(line) # 在CreateOrder前后打点 log_order_flow(SH202405200000001, CREATE_SUBMIT) ret cat_sdk.CatSdk_CreateOrder(...) log_order_flow(SH202405200000001, CREATE_RETURN, ret)这样当出现“已报”卡死时能精确看出是客户端没发出去CREATE_RETURN后无状态更新还是柜台没返回CREATE_RETURN后有状态更新但卡在0。6.3 用CATS内置的“订单重发机制”替代应用层重试CATS SDK有隐藏函数CatSdk_ResendOrder()未写入白皮书当CatSdk_CreateOrder()返回-1001或-1002时调用此函数可触发客户端自动重发最近一笔委托需传入原始参数结构体。比应用层sleepretry更可靠因为它复用CATS内部的幂等校验逻辑。6.4 监控指标只盯三个数委托成功率、平均委托延迟、撤单失败率委托成功率 成功委托数 / 总委托数低于99.5%要告警平均委托延迟 CatSdk_CreateOrder()返回时间 - 调用开始时间超过150ms需查网络或CPU撤单失败率 撤单失败数 / 总撤单数高于0.1%说明策略逻辑有问题比如对已成交单重复撤单。这些指标用telegraf influxdb采集不做花哨图表就设三条红线——它们比任何KPI都真实反映CATS通道健康度。最后说一句血泪经验别信“CATS API文档齐全”它90%的细节都在DLL导出符号、白皮书附录、以及你重启十次客户端后偶然发现的错误码映射表里。把CATS当黑匣子用永远在救火把它当本地服务运维才能睡踏实。希望帮到你。本文还有配套的精品资源点击获取
返回列表