Optimize
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,6 +7,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
from datetime import datetime
|
||||
|
||||
import config
|
||||
@@ -25,9 +26,6 @@ from .positions import manage_positions
|
||||
|
||||
def Overview(assets, positions, account_cfg=None) -> None:
|
||||
"""打印策略启动时的账户、资金和持仓概览。
|
||||
|
||||
该函数对应 Go 客户端 ``logic.Overview``。为便于单独测试,可以
|
||||
显式传入账户配置;未传入时使用 ``config.account_config``。
|
||||
"""
|
||||
account_cfg = account_cfg or config.account_config
|
||||
|
||||
@@ -99,20 +97,24 @@ def StartTrend() -> None:
|
||||
open_watch=DipWatch(),
|
||||
add_watch=DipWatch(),
|
||||
profit_tracker=GridTrailingTracker(config.account_config.grid_step_pct),
|
||||
executor=ThreadPoolExecutor(max_workers=2, thread_name_prefix="trend"),
|
||||
)
|
||||
|
||||
Overview(assets, positions, config.account_config)
|
||||
|
||||
while True:
|
||||
started_at = time.monotonic()
|
||||
try:
|
||||
RunOnce(run, signals)
|
||||
except Exception:
|
||||
# 单轮错误只记录日志,下一轮仍继续运行。
|
||||
logging.exception("趋势策略本轮执行失败")
|
||||
try:
|
||||
while True:
|
||||
started_at = time.monotonic()
|
||||
try:
|
||||
RunOnce(run, signals)
|
||||
except Exception:
|
||||
# 单轮错误只记录日志,下一轮仍继续运行。
|
||||
logging.exception("趋势策略本轮执行失败")
|
||||
|
||||
elapsed = time.monotonic() - started_at
|
||||
time.sleep(max(0.0, 30.0 - elapsed))
|
||||
elapsed = time.monotonic() - started_at
|
||||
time.sleep(max(0.0, 30.0 - elapsed))
|
||||
finally:
|
||||
run.executor.shutdown(wait=True, cancel_futures=True)
|
||||
|
||||
|
||||
def RunOnce(run: Runtime, signals:list[SignalItem]) -> None:
|
||||
@@ -148,14 +150,7 @@ def RunOnce(run: Runtime, signals:list[SignalItem]) -> None:
|
||||
logging.exception("获取持仓失败")
|
||||
return
|
||||
|
||||
# 5. 更新状态机
|
||||
try:
|
||||
run.state.reconcile(positions, run.orders.data)
|
||||
except Exception:
|
||||
logging.exception("订单状态对账失败,本轮禁止自动交易")
|
||||
return
|
||||
|
||||
# 6. 验证有效开仓信号:排除已有持仓和未决订单。
|
||||
# 5. 验证有效开仓信号:排除已有持仓和未决订单。
|
||||
allow_open: list[SignalItem] = []
|
||||
allow_codes: list[str] = []
|
||||
for signal in signals:
|
||||
@@ -164,16 +159,48 @@ def RunOnce(run: Runtime, signals:list[SignalItem]) -> None:
|
||||
allow_codes.append(signal.code)
|
||||
|
||||
# 6. 获取持仓和待开仓证券的实时行情 tick。
|
||||
all_codes = allow_codes + position_codes
|
||||
all_codes = list(dict.fromkeys(position_codes + allow_codes))
|
||||
try:
|
||||
ticks = run.client.full_tick(all_codes)
|
||||
except Exception:
|
||||
logging.exception("获取行情失败")
|
||||
return
|
||||
|
||||
# 7. 执行开仓:必须同时存在有效信号且大盘允许开仓。
|
||||
if allow_open and market_ok and allow_open_by_cash:
|
||||
open_signal(run, ticks, allow_open)
|
||||
# 7. 更新状态机
|
||||
try:
|
||||
run.state.reconcile(positions, run.orders.data)
|
||||
except Exception:
|
||||
logging.exception("订单状态对账失败,本轮禁止自动交易")
|
||||
return
|
||||
|
||||
# 8. 持仓计算。当前 Go 版本的 managePositions 为空,保留扩展入口。
|
||||
manage_positions(run, ticks, positions, market_ok,assets.available)
|
||||
# 启动线程,开始计算
|
||||
# 9. 持仓计算。
|
||||
futures: list[tuple[str, Future]] = [
|
||||
(
|
||||
"持仓计算",
|
||||
run.executor.submit(
|
||||
manage_positions,
|
||||
run,
|
||||
ticks,
|
||||
positions,
|
||||
market_ok,
|
||||
assets.available,
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
# 10. 开仓计算:必须同时存在有效信号且大盘允许开仓。
|
||||
if allow_open and market_ok and allow_open_by_cash:
|
||||
futures.append(("开仓计算", run.executor.submit(open_signal, run, ticks, allow_open)))
|
||||
|
||||
# 11. 开始执行
|
||||
for name, future in futures:
|
||||
_wait_worker(name, future)
|
||||
|
||||
|
||||
def _wait_worker(name: str, future: Future) -> None:
|
||||
"""保留单轮继续运行的语义,分别记录工作线程异常。"""
|
||||
try:
|
||||
future.result()
|
||||
except Exception:
|
||||
logging.exception("趋势策略%s线程失败", name)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""趋势策略开仓逻辑,对应 Go 版本的 ``logic/open.go``。"""
|
||||
"""趋势策略开仓逻辑。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from config import AccountConfig, GlobalConfig
|
||||
@@ -42,3 +43,4 @@ class Runtime:
|
||||
open_watch: DipWatch
|
||||
add_watch: DipWatch
|
||||
profit_tracker: GridTrailingTracker
|
||||
executor: ThreadPoolExecutor
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
from datetime import datetime, time as clock_time
|
||||
|
||||
import config
|
||||
@@ -15,7 +16,7 @@ from sdk import Client
|
||||
from strategy.trend.order import OrderBook
|
||||
from strategy.trend.watch import DipWatch
|
||||
|
||||
from .open import open_base
|
||||
from .open import open_signal
|
||||
from .positions import manage_positions
|
||||
from .runtime import Runtime
|
||||
from .state import TState
|
||||
@@ -50,11 +51,10 @@ def RunOnce(run: Runtime) -> None:
|
||||
return
|
||||
today = datetime.now().date().isoformat()
|
||||
try:
|
||||
run.state.reconcile(positions, run.orders.data, today)
|
||||
signals = init_signals(run.global_cfg, run.account_cfg.signal_allow)
|
||||
except Exception:
|
||||
logging.exception("[ZT] 状态对账失败")
|
||||
logging.exception("[ZT] 获取 dcm 信号失败")
|
||||
return
|
||||
signals = init_signals(run.global_cfg, run.account_cfg.signal_allow)
|
||||
candidate_codes = [item.code for item in signals if item.code not in position_codes]
|
||||
codes = list(dict.fromkeys(position_codes + candidate_codes))
|
||||
try:
|
||||
@@ -63,13 +63,50 @@ def RunOnce(run: Runtime) -> None:
|
||||
logging.exception("[ZT] 获取行情失败")
|
||||
return
|
||||
market_ok = market_allow_open(run.global_cfg.api_host)
|
||||
if market_ok and assets.available >= assets.total * run.account_cfg.min_cash_ratio:
|
||||
open_base(run, ticks, signals)
|
||||
manage_positions(
|
||||
run,
|
||||
ticks,
|
||||
positions,
|
||||
assets.available,
|
||||
today,
|
||||
force_buy_back=datetime.now().time() >= clock_time(14, 50),
|
||||
)
|
||||
can_open = market_ok and assets.available >= assets.total * run.account_cfg.min_cash_ratio
|
||||
force_buy_back = datetime.now().time() >= clock_time(14, 50)
|
||||
|
||||
# 状态对账与开仓判断并行。持仓线程在自己的线程中等待对账完成,
|
||||
# 以保证它读取到最新的底仓和做 T 轮次状态,避免并发写 State。
|
||||
with ThreadPoolExecutor(max_workers=3, thread_name_prefix="zt") as executor:
|
||||
state_future = executor.submit(run.state.reconcile, positions, run.orders.data, today)
|
||||
open_future = executor.submit(_run_open_signal, state_future, run, ticks, signals, can_open)
|
||||
positions_future = executor.submit(
|
||||
_run_manage_positions,
|
||||
state_future,
|
||||
run,
|
||||
ticks,
|
||||
positions,
|
||||
assets.available,
|
||||
today,
|
||||
force_buy_back,
|
||||
)
|
||||
_wait_worker("状态对账", state_future)
|
||||
_wait_worker("开仓", open_future)
|
||||
_wait_worker("持仓管理", positions_future)
|
||||
|
||||
|
||||
def _run_open_signal(state_future: Future, run: Runtime, ticks, signals, can_open: bool) -> None:
|
||||
state_future.result()
|
||||
if can_open:
|
||||
open_signal(run, ticks, signals)
|
||||
|
||||
|
||||
def _run_manage_positions(
|
||||
state_future: Future,
|
||||
run: Runtime,
|
||||
ticks,
|
||||
positions,
|
||||
available: float,
|
||||
today: str,
|
||||
force_buy_back: bool,
|
||||
) -> None:
|
||||
state_future.result()
|
||||
manage_positions(run, ticks, positions, available, today, force_buy_back)
|
||||
|
||||
|
||||
def _wait_worker(name: str, future: Future) -> None:
|
||||
try:
|
||||
future.result()
|
||||
except Exception:
|
||||
logging.exception("[ZT] %s线程失败", name)
|
||||
|
||||
@@ -9,7 +9,7 @@ from sdk import OP_BUY
|
||||
from strategy.trend.order import PlaceOrderRequest
|
||||
|
||||
|
||||
def open_base(run, ticks, signals) -> None:
|
||||
def open_signal(run, ticks, signals) -> None:
|
||||
"""仅处理 dcm 信号,使用趋势策略同款反弹确认建立底仓。"""
|
||||
for signal in signals:
|
||||
if signal.signal_key != "dcm" or run.orders.busy(signal.code, "BUY"):
|
||||
@@ -25,3 +25,7 @@ def open_base(run, ticks, signals) -> None:
|
||||
if run.orders.place(request):
|
||||
run.buy_watch.forget(signal.code)
|
||||
logging.info("[ZT 建仓] %s 买入 %d 股", signal.code, volume)
|
||||
|
||||
|
||||
# 与 trend 策略的开仓函数命名保持一致。
|
||||
open_base = open_signal
|
||||
|
||||
Reference in New Issue
Block a user