diff --git a/ctp_premarket_connect.py b/ctp_premarket_connect.py
index 1f015de..c56b644 100644
--- a/ctp_premarket_connect.py
+++ b/ctp_premarket_connect.py
@@ -12,7 +12,6 @@ import threading
import time
from typing import Callable
-from ctp_settings import is_ctp_auto_connect_enabled
from market_sessions import in_premarket_connect_window, is_trading_session
from vnpy_bridge import ctp_start_connect, ctp_status
@@ -60,10 +59,7 @@ def start_ctp_premarket_connect_worker(
while True:
sleep_sec = max(30, interval)
try:
- gs = get_setting_fn
- if gs is None:
- from fee_specs import get_setting as gs
- if is_ctp_auto_connect_enabled(gs) and should_auto_connect_now():
+ if should_auto_connect_now():
mode = get_mode_fn()
st = ctp_status(mode)
if (
@@ -71,7 +67,7 @@ def start_ctp_premarket_connect_worker(
and not st.get("connecting")
and int(st.get("login_cooldown_sec") or 0) <= 0
):
- info = ctp_start_connect(mode, force=False)
+ info = ctp_start_connect(mode, force=False, scheduled=True)
if info.get("started"):
if is_trading_session():
logger.info("交易时段内自动连接 CTP [%s]", mode)
diff --git a/ctp_reconnect.py b/ctp_reconnect.py
index 7f8683d..47581f5 100644
--- a/ctp_reconnect.py
+++ b/ctp_reconnect.py
@@ -13,7 +13,6 @@ import time
from typing import Callable
from ctp_premarket_connect import should_auto_connect_now
-from ctp_settings import is_ctp_auto_connect_enabled
from vnpy_bridge import ctp_try_auto_reconnect
logger = logging.getLogger(__name__)
@@ -40,14 +39,7 @@ def start_ctp_reconnect_worker(
def _loop() -> None:
while True:
try:
- gs = get_setting_fn
- if gs is None:
- from fee_specs import get_setting as gs
- if (
- is_ctp_auto_connect_enabled(gs)
- and _auto_reconnect_enabled()
- and should_auto_connect_now()
- ):
+ if _auto_reconnect_enabled() and should_auto_connect_now():
mode = get_mode_fn()
if ctp_try_auto_reconnect(mode):
logger.debug("CTP 连接正常 [%s]", mode)
diff --git a/ctp_settings.py b/ctp_settings.py
index e660bc4..cdb236b 100644
--- a/ctp_settings.py
+++ b/ctp_settings.py
@@ -35,11 +35,11 @@ LIVE_FIELDS: tuple[tuple[str, str, str, str], ...] = (
PASSWORD_DB_KEYS = frozenset({"simnow_password", "ctp_live_password"})
CTP_AUTO_CONNECT_KEY = "ctp_auto_connect"
-CTP_DISABLED_HINT = "CTP 自动连接已关闭(非交易时段建议关闭,避免反复连接失败)"
+CTP_DISABLED_HINT = "CTP 自动连接已关闭(非交易时段不重连;开盘前 30 分钟仍会按计划连接)"
def is_ctp_auto_connect_enabled(get_setting=None) -> bool:
- """系统设置:是否允许 CTP 连接(含自动重连、盘前连接、手动连接)。"""
+ """系统设置:是否允许手动连接及非交易时段自动重连(盘前/交易时段计划连接不受此限制)。"""
if get_setting is None:
from fee_specs import get_setting as _gs
diff --git a/install_trading.py b/install_trading.py
index 5fee70c..ba68a8c 100644
--- a/install_trading.py
+++ b/install_trading.py
@@ -1667,10 +1667,12 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
threading.Thread(target=_warm, daemon=True, name="position-bootstrap").start()
try:
- if is_ctp_auto_connect_enabled(get_setting):
- from vnpy_bridge import ctp_start_connect
+ from ctp_premarket_connect import should_auto_connect_now
+ from vnpy_bridge import ctp_start_connect
+
+ if should_auto_connect_now():
mode = get_trading_mode(get_setting)
- ctp_start_connect(mode, force=False)
+ ctp_start_connect(mode, force=False, scheduled=True)
except Exception as exc:
logger.debug("bootstrap ctp connect: %s", exc)
diff --git a/static/js/trade.js b/static/js/trade.js
index 2bff0ad..a7f856f 100644
--- a/static/js/trade.js
+++ b/static/js/trade.js
@@ -484,7 +484,7 @@
if (hint) {
hint.textContent = ctpAutoConnectEnabled
? '断线自动重连 · 开盘前 30 分钟自动连接'
- : 'CTP 自动连接已关闭(系统设置可开启)';
+ : '自动连接已关闭 · 开盘前 30 分钟仍会按计划连接';
}
if (btnConnect && !ctpAutoConnectEnabled) {
btnConnect.disabled = true;
diff --git a/templates/settings.html b/templates/settings.html
index 777af31..bf35c46 100644
--- a/templates/settings.html
+++ b/templates/settings.html
@@ -271,7 +271,7 @@
CTP 自动连接
- 开启:盘前自动连接、断线重连、持仓页可连 CTP。关闭:立即断开所有 CTP 连接,不再尝试重连。
+ 开启:盘前自动连接、断线重连、持仓页可连 CTP。关闭:立即断开,非交易时段不再重连;开盘前 30 分钟及交易时段仍会自动连接。
SimNow 非交易时段前置常不可用(与快期相同),建议收盘后关闭。
diff --git a/templates/trade.html b/templates/trade.html
index 0cf014f..f746d2d 100644
--- a/templates/trade.html
+++ b/templates/trade.html
@@ -40,7 +40,7 @@
{% if not ctp_auto_connect %}disabled title="请先在系统设置 → CTP 连接 中开启自动连接"{% endif %}>
{% if ctp_status.connected %}重连 CTP{% else %}连接 CTP{% endif %}
- {% if ctp_auto_connect %}断线自动重连 · 开盘前 30 分钟自动连接{% else %}CTP 自动连接已关闭{% endif %}
+ {% if ctp_auto_connect %}断线自动重连 · 开盘前 30 分钟自动连接{% else %}自动连接已关闭 · 开盘前 30 分钟仍会按计划连接{% endif %}
diff --git a/vnpy_bridge.py b/vnpy_bridge.py
index 95667eb..5c8173e 100644
--- a/vnpy_bridge.py
+++ b/vnpy_bridge.py
@@ -575,10 +575,10 @@ class CtpBridge:
"td_address": st.get("交易服务器", ""),
}
- def connect(self, mode: str, *, force: bool = False) -> None:
- from ctp_settings import CTP_DISABLED_HINT, is_ctp_auto_connect_enabled
+ def connect(self, mode: str, *, force: bool = False, scheduled: bool = False) -> None:
+ from ctp_settings import CTP_DISABLED_HINT
- if not is_ctp_auto_connect_enabled():
+ if not _ctp_connect_permitted(scheduled=scheduled):
self._last_error = CTP_DISABLED_HINT
_persist_last_error(CTP_DISABLED_HINT)
raise RuntimeError(CTP_DISABLED_HINT)
@@ -680,11 +680,13 @@ class CtpBridge:
finally:
self._connect_in_progress = False
- def start_connect_async(self, mode: str, *, force: bool = False) -> dict[str, Any]:
+ def start_connect_async(
+ self, mode: str, *, force: bool = False, scheduled: bool = False,
+ ) -> dict[str, Any]:
"""后台连接,不阻塞 HTTP 请求。"""
- from ctp_settings import CTP_DISABLED_HINT, is_ctp_auto_connect_enabled
+ from ctp_settings import CTP_DISABLED_HINT
- if not is_ctp_auto_connect_enabled():
+ if not _ctp_connect_permitted(scheduled=scheduled):
self._last_error = CTP_DISABLED_HINT
_persist_last_error(CTP_DISABLED_HINT)
return {
@@ -709,7 +711,7 @@ class CtpBridge:
def _run() -> None:
try:
- self.connect(mode, force=force)
+ self.connect(mode, force=force, scheduled=scheduled)
except Exception as exc:
logger.warning("CTP 后台连接失败: %s", exc)
@@ -1915,6 +1917,19 @@ def vnpy_available() -> bool:
return get_bridge().available()
+def _ctp_connect_permitted(*, scheduled: bool = False) -> bool:
+ """scheduled=True:盘前/交易时段计划连接,不受「自动连接」开关限制。"""
+ from ctp_settings import is_ctp_auto_connect_enabled
+
+ if is_ctp_auto_connect_enabled():
+ return True
+ if not scheduled:
+ return False
+ from ctp_premarket_connect import should_auto_connect_now
+
+ return should_auto_connect_now()
+
+
def ctp_disconnect(*, set_disabled_hint: bool = False) -> None:
"""主动断开 CTP 并清理内存状态。"""
from ctp_settings import CTP_DISABLED_HINT
@@ -1935,19 +1950,17 @@ def ctp_connect(mode: str, *, force: bool = False) -> dict[str, Any]:
return b.status(mode)
-def ctp_start_connect(mode: str, *, force: bool = False) -> dict[str, Any]:
+def ctp_start_connect(mode: str, *, force: bool = False, scheduled: bool = False) -> dict[str, Any]:
"""非阻塞发起连接,供 Web API 使用。"""
b = get_bridge()
- info = b.start_connect_async(mode, force=force)
+ info = b.start_connect_async(mode, force=force, scheduled=scheduled)
st = b.status(mode)
return {**info, "status": st}
def ctp_try_auto_reconnect(mode: str) -> bool:
"""断线时静默异步重连;已连接且交易通道正常则不再重复 connect。"""
- from ctp_settings import is_ctp_auto_connect_enabled
-
- if not is_ctp_auto_connect_enabled():
+ if not _ctp_connect_permitted(scheduled=True):
return False
b = get_bridge()
if not b.available():
@@ -1974,7 +1987,7 @@ def ctp_try_auto_reconnect(mode: str) -> bool:
"请更新 SIMNOW_TD_ADDRESS 并确认服务器出网。"
)
return False
- info = b.start_connect_async(mode, force=False)
+ info = b.start_connect_async(mode, force=False, scheduled=True)
return bool(
info.get("connected")
or info.get("connecting")