fix trend,zt

This commit is contained in:
2026-09-06 13:12:48 +08:00
parent bcc6f02398
commit 2eafbb8303
15 changed files with 502 additions and 480 deletions

View File

@@ -16,9 +16,9 @@ from libs.signal import init_signals
from libs.collector import collector_push
from libs.grid_take_profit import GridTrailingTracker
from sdk import Client
from .order import OrderBook
from .watch import DipWatch
from .runtime import Runtime
from libs.order import OrderBook
from libs.watch import DipWatch
from libs.runtime import Runtime
from .state import TState, SOLD
from .open import open_signal
from .positions import manage_positions
@@ -26,20 +26,39 @@ from .positions import manage_positions
def StartZT() -> None:
"""初始化做 T 策略,并以 30 秒间隔持续执行。"""
with Client(config.global_config.qmt_base_url, config.global_config.qmt_token,
config.HTTP_TIMEOUT) as client:
state = TState.for_strategy(config.global_config.qmt_data_dir, "zt", config.account_config.account_id)
run = Runtime(client, config.global_config, config.account_config, state,
OrderBook(), DipWatch(), DipWatch(),
GridTrailingTracker(config.account_config.grid_step_pct))
log.info("[ZT 启动] 账户=%s,底仓信号=dcm状态文件=%s", run.account_cfg.account_id, state.path)
with Client(
config.global_config.qmt_base_url,
config.global_config.qmt_token,
config.HTTP_TIMEOUT,
) as client:
state = TState.for_strategy(
config.global_config.qmt_data_dir, "zt", config.account_config.account_id
)
run = Runtime(
client=client,
global_cfg=config.global_config,
account_cfg=config.account_config,
orders=OrderBook("zt"),
open_watch=DipWatch(),
add_watch=DipWatch(),
profit_tracker=GridTrailingTracker(config.account_config.grid_step_pct),
)
log.info(
"[ZT 启动] 账户=%s,底仓信号=dcm状态文件=%s",
run.account_cfg.account_id,
state.path,
)
while True:
now = datetime.now()
if now.time() >= clock_time(15):
# 收盘前最后一次只读对账,不发新单;未完成买回继续持久保存。
try:
portfolio = client.portfolio()
state.reconcile(list(portfolio.positions.values()), portfolio.orders, now.date().isoformat())
state.reconcile(
list(portfolio.positions.values()),
portfolio.orders,
now.date().isoformat(),
)
except Exception:
log.exception("[ZT] 收盘对账失败,保留本地待确认记录")
for item in state.items.values():
@@ -48,14 +67,14 @@ def StartZT() -> None:
return
# 单轮失败不能杀死唯一的交易定时线程。
try:
RunOnce(run)
RunOnce(run, state)
except Exception:
log.exception("[ZT] 本 tick 执行失败,下一个 tick 继续")
# 计算距离下一个目标时间点0秒或30秒的等待时间。
time.sleep(30 - datetime.now().second % 30)
def RunOnce(run: Runtime) -> None:
def RunOnce(run: Runtime, state: TState) -> None:
"""账户快照 → 成交对账 → 做 T 管理 → dcm 建仓,共用一份资金预算。"""
now = datetime.now()
if not trading_time(now) or now.time() >= clock_time(15):
@@ -68,7 +87,7 @@ def RunOnce(run: Runtime) -> None:
positions = list(portfolio.positions.values())
run.orders.refresh(run.client, portfolio.orders)
# 对账使用完整原始订单列表,不能丢弃撤单和废单的部分成交。
run.state.reconcile(positions, portfolio.orders, today)
state.reconcile(positions, portfolio.orders, today)
# 2. 获取本策略的信号开仓数据;信号失败不阻断已有做 T 买回。
try:
@@ -76,13 +95,23 @@ def RunOnce(run: Runtime) -> None:
except Exception:
log.exception("[ZT] 获取 dcm 信号失败,本轮只管理已有底仓")
signals = []
position_codes = {position.stock_code for position in positions if position.volume > 0}
candidates = [signal for signal in signals
if signal.signal_key == "dcm" and signal.code not in position_codes]
position_codes = {
position.stock_code for position in positions if position.volume > 0
}
candidates = [
signal
for signal in signals
if signal.signal_key == "dcm" and signal.code not in position_codes
]
# 3. 获取持仓和待开仓证券的实时行情 tick零持仓的待买回证券也包含在内。
codes = list(dict.fromkeys(list(position_codes) + list(run.state.items)
+ [signal.code for signal in candidates]))
codes = list(
dict.fromkeys(
list(position_codes)
+ list(state.items)
+ [signal.code for signal in candidates]
)
)
ticks = run.client.full_tick(codes) if codes else {}
now = datetime.now() # 网络请求可能跨过尾盘边界,提交前重新判断。
if not trading_time(now) or now.time() >= clock_time(15):
@@ -91,7 +120,7 @@ def RunOnce(run: Runtime) -> None:
# 4. 先完成买回,避免开底仓抢占资金;交易逻辑串行,状态无需多线程写入。
available = max(0.0, portfolio.assets.available)
# 未确认买单可能尚未反映在资金快照中,保守预留,宁可少买也不重复使用。
for pending in run.state.pending.values():
for pending in state.pending.values():
if pending.kind != "sell":
tick = ticks.get(pending.code)
if tick is None or tick.last_price <= 0:
@@ -99,16 +128,22 @@ def RunOnce(run: Runtime) -> None:
break
available = max(0.0, available - pending.qty * tick.last_price * 1.01)
force = now.time() >= clock_time(14, 50)
available = manage_positions(run, ticks, positions, available, today, force)
available = manage_positions(run, state, ticks, positions, available, today, force)
# 5. 验证可用资金;低于资金安全线时禁止开新仓,尾盘只完成做 T 买回。
reserve = max(0.0, portfolio.assets.total * run.account_cfg.min_cash_ratio)
# 未完成的卖出/买回可能继续占用资金,不再额外开底仓。
outstanding = bool(run.state.pending) or any(item.phase == SOLD for item in run.state.items.values())
outstanding = bool(state.pending) or any(
item.phase == SOLD for item in state.items.values()
)
if not force and not outstanding and market_allow_open() and available > reserve:
open_signal(run, ticks, candidates, available - reserve)
open_signal(run, state, ticks, candidates, available - reserve)
# 6. 数据采集不与交易逻辑争用状态;采集函数自身隔离传输异常。
collector_push(run.account_cfg.account_id, portfolio.assets, positions)
log.info("[ZT] 本轮完成,底仓=%d,待确认=%d,耗时=%d毫秒",
len(run.state.items), len(run.state.pending), int((time.monotonic() - started_at) * 1000))
log.info(
"[ZT] 本轮完成,底仓=%d,待确认=%d,耗时=%d毫秒",
len(state.items),
len(state.pending),
int((time.monotonic() - started_at) * 1000),
)