fix: CTP 连接捕获 SimNow 真实报错并增加诊断脚本
显式设置柜台环境=实盘;连接失败时解析 4097/登录拒单;scripts/test_simnow.py 供服务器排查。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+58
-15
@@ -22,7 +22,7 @@ def _env(key: str, default: str = "") -> str:
|
||||
|
||||
|
||||
def _simnow_setting() -> dict[str, str]:
|
||||
"""SimNow 7×24 仿真默认前置(可在 .env 覆盖)。"""
|
||||
"""SimNow 仿真前置(可在 .env 覆盖)。看穿式前置需「柜台环境=实盘」。"""
|
||||
return {
|
||||
"用户名": _env("SIMNOW_USER"),
|
||||
"密码": _env("SIMNOW_PASSWORD"),
|
||||
@@ -31,7 +31,7 @@ def _simnow_setting() -> dict[str, str]:
|
||||
"行情服务器": _env("SIMNOW_MD_ADDRESS", "tcp://180.168.146.187:10211"),
|
||||
"产品名称": _env("SIMNOW_APP_ID", "simnow_client_test"),
|
||||
"授权编码": _env("SIMNOW_AUTH_CODE", "0000000000000000"),
|
||||
"产品信息": _env("SIMNOW_PRODUCT_INFO", "simnow_client_test"),
|
||||
"柜台环境": _env("SIMNOW_ENV", "实盘"),
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ def _live_setting() -> dict[str, str]:
|
||||
"行情服务器": _env("CTP_LIVE_MD_ADDRESS"),
|
||||
"产品名称": _env("CTP_LIVE_APP_ID"),
|
||||
"授权编码": _env("CTP_LIVE_AUTH_CODE"),
|
||||
"产品信息": _env("CTP_LIVE_PRODUCT_INFO"),
|
||||
"柜台环境": _env("CTP_LIVE_ENV", "实盘"),
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,25 @@ def _mode_label(mode: str) -> str:
|
||||
return "SimNow 模拟" if mode == "simulation" else "期货公司实盘"
|
||||
|
||||
|
||||
def _format_ctp_failure(ctp_logs: list[str]) -> str:
|
||||
"""根据 CTP 网关日志拼出可读错误。"""
|
||||
text = "\n".join(ctp_logs)
|
||||
if "4097" in text or "Decrypt handshake" in text or "shake hand" in text.lower():
|
||||
return (
|
||||
"CTP 握手失败(4097):vnpy_ctp 与 SimNow 前置加密不匹配。"
|
||||
"请执行 pip install -U vnpy vnpy_ctp 后重启,并确认 .env 中 SIMNOW_ENV=实盘"
|
||||
)
|
||||
if "不合法的登录" in text or "密码" in text or "账号" in text:
|
||||
tail = ctp_logs[-1] if ctp_logs else ""
|
||||
return f"CTP 登录被拒:{tail or '请检查投资者代码与密码(快期能否登录)'}"
|
||||
if "连接断开" in text or "disconnect" in text.lower():
|
||||
tail = ctp_logs[-1] if ctp_logs else ""
|
||||
return f"CTP 连接断开:{tail or '请检查前置地址与网络'}"
|
||||
if ctp_logs:
|
||||
return f"CTP 连接失败:{ctp_logs[-1]}"
|
||||
return "CTP 连接超时:未收到柜台回报。请检查 SimNow 账号、前置地址、网络(nc 测端口),并用快期验证账号"
|
||||
|
||||
|
||||
class CtpBridge:
|
||||
def __init__(self) -> None:
|
||||
self._engine = None
|
||||
@@ -127,18 +146,42 @@ class CtpBridge:
|
||||
self._connected_mode = None
|
||||
time.sleep(1)
|
||||
|
||||
self._engine.connect(setting, GATEWAY_NAME)
|
||||
# 等待登录与结算信息
|
||||
for _ in range(30):
|
||||
accounts = self._engine.get_all_accounts()
|
||||
if accounts:
|
||||
self._connected_mode = mode
|
||||
self._last_error = ""
|
||||
logger.info("CTP 已连接 [%s] account=%s", mode, len(accounts))
|
||||
return
|
||||
time.sleep(0.5)
|
||||
self._last_error = "CTP 连接超时,请检查 SimNow 账号、前置地址与交易时段"
|
||||
raise RuntimeError(self._last_error)
|
||||
ctp_logs: list[str] = []
|
||||
from vnpy.trader.event import EVENT_LOG
|
||||
|
||||
def _on_log(event) -> None:
|
||||
msg = getattr(event.data, "msg", "") or str(event.data)
|
||||
if msg:
|
||||
ctp_logs.append(str(msg))
|
||||
if len(ctp_logs) > 20:
|
||||
ctp_logs.pop(0)
|
||||
logger.info("CTP | %s", msg)
|
||||
|
||||
self._ee.register(EVENT_LOG, _on_log)
|
||||
try:
|
||||
logger.info(
|
||||
"CTP 连接 [%s] user=%s td=%s env=%s",
|
||||
mode,
|
||||
setting.get("用户名"),
|
||||
setting.get("交易服务器"),
|
||||
setting.get("柜台环境", "实盘"),
|
||||
)
|
||||
self._engine.connect(setting, GATEWAY_NAME)
|
||||
# 等待登录与结算信息(最多约 30 秒)
|
||||
for _ in range(60):
|
||||
accounts = self._engine.get_all_accounts()
|
||||
if accounts:
|
||||
self._connected_mode = mode
|
||||
self._last_error = ""
|
||||
logger.info("CTP 已连接 [%s] account=%s", mode, len(accounts))
|
||||
return
|
||||
time.sleep(0.5)
|
||||
finally:
|
||||
self._ee.unregister(EVENT_LOG, _on_log)
|
||||
|
||||
hint = _format_ctp_failure(ctp_logs)
|
||||
self._last_error = hint
|
||||
raise RuntimeError(hint)
|
||||
|
||||
def ensure_connected(self, mode: str) -> None:
|
||||
if self._connected_mode != mode:
|
||||
|
||||
Reference in New Issue
Block a user