refactor(qmt): 优化配置模型和百分比交易逻辑

- 统一使用配置模型属性访问替代字典下标
- 完善百分比模式买卖的日志记录和错误处理
- 代码格式化和死代码清理
- 更新 notebook 数据及测试脚本
This commit is contained in:
2026-03-03 15:16:37 +08:00
parent 7bb0a0537b
commit 4090b3c5be
5 changed files with 309 additions and 188 deletions

View File

@@ -21,12 +21,24 @@ from xtquant import xtconstant
try:
from .message_processor import StreamMessageProcessor
from .logger import QMTLogger
from .config_models import QMTConfig, QMTTerminalConfig, StrategyConfig, load_config, ConfigError
from .config_models import (
QMTConfig,
QMTTerminalConfig,
StrategyConfig,
load_config,
ConfigError,
)
except ImportError:
# 当作为脚本直接运行时
from message_processor import StreamMessageProcessor
from logger import QMTLogger
from config_models import QMTConfig, QMTTerminalConfig, StrategyConfig, load_config, ConfigError
from config_models import (
QMTConfig,
QMTTerminalConfig,
StrategyConfig,
load_config,
ConfigError,
)
import time
import datetime
import traceback
@@ -429,7 +441,6 @@ class TradingUnit:
self.account_id = t_cfg.account_id
self.account_type = t_cfg.account_type
self.xt_trader = None
self.acc_obj = None
self.callback = None
@@ -530,7 +541,7 @@ class MultiEngineManager:
def initialize(self, config_file="config.json"):
self._setup_logger() # 先初始化 logger
# 使用新的配置模型加载配置
try:
self.config = load_config(config_file)
@@ -567,7 +578,9 @@ class MultiEngineManager:
# 尝试多个路径
env_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), "config", ".env.local"),
os.path.join(
os.path.dirname(os.path.abspath(__file__)), "config", ".env.local"
),
os.path.join(os.path.dirname(__file__), "config", ".env.local"),
os.path.join(os.path.dirname(__file__), "..", "config", ".env.local"),
"/qmt/config/.env.local",
@@ -583,7 +596,9 @@ class MultiEngineManager:
break
if not loaded:
self.logger.warning("[Config] 警告: 未找到 .env.local 文件,使用默认配置")
self.logger.warning(
"[Config] 警告: 未找到 .env.local 文件,使用默认配置"
)
except ImportError:
self.logger.warning("[Config] 警告: 未安装 python-dotenv使用默认配置")
@@ -616,15 +631,11 @@ class MultiEngineManager:
sh.setFormatter(fmt)
self.logger.addHandler(fh)
self.logger.addHandler(sh)
def get_strategies_by_terminal(self, qmt_id):
if not self.config:
return []
return self.config.get_strategies_by_terminal(qmt_id)
return [
s
for s, cfg in self.config["strategies"].items()
if cfg.get("qmt_id") == qmt_id
]
def run_trading_loop(self):
self.logger = logging.getLogger("QMT_Engine")
@@ -752,7 +763,7 @@ class MultiEngineManager:
"""处理策略消息路由 - 使用 Redis Stream
从 Redis Stream 消费消息,处理成功后 ACK失败则进入失败队列。
Args:
strategy_name: 策略名称
is_trading_hours: 是否在交易时间内False 表示休盘后只消费消息不下单
@@ -815,14 +826,14 @@ class MultiEngineManager:
# 3. 执行交易动作(仅在交易时间内执行实际下单)
action = data.get("action")
# 获取策略配置,确定下单模式
strat_cfg = self.config.get_strategy(strategy_name)
if not strat_cfg:
self.logger.warning(f"[{strategy_name}] 策略配置不存在")
continue
order_mode = strat_cfg.order_mode
if not is_trading_hours:
# 休盘后:只记录日志,不下单
self.logger.info(
@@ -835,11 +846,11 @@ class MultiEngineManager:
validation_type="market_hours_check",
strategy_name=strategy_name,
details={
"action": action,
"action": action,
"code": data.get("stock_code"),
"order_mode": order_mode,
"msg_time": data.get("timestamp"),
"skip_reason": "休盘后只消费消息不下单"
"skip_reason": "休盘后只消费消息不下单",
},
result=True,
)
@@ -847,7 +858,11 @@ class MultiEngineManager:
self.qmt_logger.log_validation(
validation_type="action_check",
strategy_name=strategy_name,
details={"action": "BUY", "code": data.get("stock_code"), "order_mode": order_mode},
details={
"action": "BUY",
"code": data.get("stock_code"),
"order_mode": order_mode,
},
result=True,
)
# 根据下单模式执行相应逻辑
@@ -859,7 +874,11 @@ class MultiEngineManager:
self.qmt_logger.log_validation(
validation_type="action_check",
strategy_name=strategy_name,
details={"action": "SELL", "code": data.get("stock_code"), "order_mode": order_mode},
details={
"action": "SELL",
"code": data.get("stock_code"),
"order_mode": order_mode,
},
result=True,
)
# 根据下单模式执行相应逻辑
@@ -934,10 +953,7 @@ class MultiEngineManager:
return
# 2. 持仓数检查
if (
self.pos_manager.get_holding_count(strategy_name)
>= strat_cfg.total_slots
):
if self.pos_manager.get_holding_count(strategy_name) >= strat_cfg.total_slots:
return
try:
@@ -949,7 +965,8 @@ class MultiEngineManager:
# 权重默认为 1支持通过 weight 字段调整资金分配比例
# 示例strategies = {"strategy_a": {"total_slots": 5, "weight": 1}, "strategy_b": {"total_slots": 5, "weight": 2}}
total_weighted_slots = sum(
self.config.get_strategy(s).total_slots * self.config.get_strategy(s).weight
self.config.get_strategy(s).total_slots
* self.config.get_strategy(s).weight
for s in terminal_strategies
if self.config.get_strategy(s)
)
@@ -1127,54 +1144,72 @@ class MultiEngineManager:
if not strat_cfg:
self.logger.error(f"[{strategy_name}] 策略配置不存在")
return
# 获取目标持仓百分比
position_pct = float(data.get("position_pct", 0))
if position_pct <= 0:
self.logger.warning(f"[{strategy_name}] 百分比模式买入: position_pct 无效 ({position_pct})")
self.logger.warning(
f"[{strategy_name}] 百分比模式买入: position_pct 无效 ({position_pct})"
)
return
self.logger.info(f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}"
)
try:
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
if not asset:
self.logger.error(f"[{strategy_name}] API 错误: query_stock_asset 返回 None")
self.logger.error(
f"[{strategy_name}] API 错误: query_stock_asset 返回 None"
)
return
total_asset = asset.total_asset
available_cash = asset.cash
self.logger.info(f"[{strategy_name}] [百分比模式] 账户总资产: {total_asset:.2f}, 可用资金: {available_cash:.2f}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 账户总资产: {total_asset:.2f}, 可用资金: {available_cash:.2f}"
)
# 计算目标金额
target_amount = total_asset * position_pct
actual_amount = min(target_amount, available_cash * 0.98) # 预留手续费滑点
self.logger.info(f"[{strategy_name}] [百分比模式] 目标金额: {target_amount:.2f}, 实际可用: {actual_amount:.2f}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 目标金额: {target_amount:.2f}, 实际可用: {actual_amount:.2f}"
)
# 检查最小金额限制
if actual_amount < 2000:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 金额过小 ({actual_amount:.2f} < 2000)")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截买入: 金额过小 ({actual_amount:.2f} < 2000)"
)
return
# 价格校验
price = float(data.get("price", 0))
offset = strat_cfg.execution.buy_price_offset
price = round(price + offset, 3)
if price <= 0:
self.logger.warning(f"[{strategy_name}] [百分比模式] 价格异常: {price}强制设为1.0")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 价格异常: {price}强制设为1.0"
)
price = 1.0
# 计算股数
vol = int(actual_amount / price / 100) * 100
self.logger.info(f"[{strategy_name}] [百分比模式] 计算股数: 资金{actual_amount:.2f} / 价格{price} -> {vol}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 计算股数: 资金{actual_amount:.2f} / 价格{price} -> {vol}"
)
if vol < 100:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})"
)
return
# 记录订单执行请求
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1183,7 +1218,7 @@ class MultiEngineManager:
volume=vol,
price=price,
)
oid = unit.xt_trader.order_stock(
unit.acc_obj,
data["stock_code"],
@@ -1194,10 +1229,12 @@ class MultiEngineManager:
strategy_name,
"PyBuyPct",
)
if oid != -1:
unit.order_cache[oid] = (strategy_name, data["stock_code"], "BUY")
self.logger.info(f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单买入: {data['stock_code']} {vol}股 @ {price}")
self.logger.info(
f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单买入: {data['stock_code']} {vol}股 @ {price}"
)
# 记录订单执行成功
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1227,31 +1264,41 @@ class MultiEngineManager:
if not strat_cfg:
self.logger.error(f"[{strategy_name}] 策略配置不存在")
return
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
)
try:
# 查询实盘持仓
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
if real_pos is None:
self.logger.error(f"[{strategy_name}] [百分比模式] API 错误: query_stock_positions 返回 None")
self.logger.error(
f"[{strategy_name}] [百分比模式] API 错误: query_stock_positions 返回 None"
)
return
rp = next((p for p in real_pos if p.stock_code == data["stock_code"]), None)
can_use = rp.can_use_volume if rp else 0
self.logger.info(f"[{strategy_name}] [百分比模式] 股票 {data['stock_code']} 实盘可用持仓: {can_use}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 股票 {data['stock_code']} 实盘可用持仓: {can_use}"
)
if can_use <= 0:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓"
)
return
# 执行清仓
price = float(data.get("price", 0))
offset = strat_cfg.execution.sell_price_offset
price = round(price + offset, 3)
self.logger.info(f"[{strategy_name}] [百分比模式] 执行清仓: {data['stock_code']} @ {price}, 数量: {can_use}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 执行清仓: {data['stock_code']} @ {price}, 数量: {can_use}"
)
# 记录订单执行请求
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1260,7 +1307,7 @@ class MultiEngineManager:
volume=can_use,
price=price,
)
oid = unit.xt_trader.order_stock(
unit.acc_obj,
data["stock_code"],
@@ -1271,10 +1318,12 @@ class MultiEngineManager:
strategy_name,
"PySellPct",
)
if oid != -1:
unit.order_cache[oid] = (strategy_name, data["stock_code"], "SELL")
self.logger.info(f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单卖出: {data['stock_code']} {can_use}股 @ {price}")
self.logger.info(
f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单卖出: {data['stock_code']} {can_use}股 @ {price}"
)
# 记录订单执行成功
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1396,20 +1445,19 @@ class MultiEngineManager:
if not strat_cfg:
self.logger.error(f"[{strategy_name}] 策略配置不存在")
return
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
)
# 1. 槽位校验
if data["total_slots"] != strat_cfg["total_slots"]:
if data["total_slots"] != strat_cfg.total_slots:
self.logger.error(
f"[{strategy_name}] 信号槽位({data['total_slots']})与配置({strat_cfg['total_slots']})不符"
f"[{strategy_name}] 信号槽位({data['total_slots']})与配置({strat_cfg.total_slots})不符"
)
return
# 2. 持仓数检查
if (
self.pos_manager.get_holding_count(strategy_name)
>= strat_cfg["total_slots"]
):
if self.pos_manager.get_holding_count(strategy_name) >= strat_cfg.total_slots:
return
try:
@@ -1419,18 +1467,18 @@ class MultiEngineManager:
# 计算加权槽位总和(支持策略权重配置)
# 权重默认为 1支持通过 weight 字段调整资金分配比例
# 示例strategies = {"strategy_a": {"total_slots": 5, "weight": 1}, "strategy_b": {"total_slots": 5, "weight": 2}}
total_weighted_slots = sum(
self.config["strategies"][s].get("total_slots", 1)
* self.config["strategies"][s].get("weight", 1)
for s in terminal_strategies
)
# 计算加权槽位总和
total_weighted_slots = 0
for s in terminal_strategies:
s_cfg = self.config.strategies.get(s)
if s_cfg:
total_weighted_slots += s_cfg.total_slots * s_cfg.weight
if not asset or total_weighted_slots <= 0:
return
# 获取当前策略的权重
weight = strat_cfg.get("weight", 1)
weight = strat_cfg.weight
# 4. 资金加权分配 (基于该终端总资产)
total_equity = asset.cash + asset.market_value
@@ -1444,7 +1492,7 @@ class MultiEngineManager:
return
# 4. 价格与股数
offset = strat_cfg.get("execution", {}).get("buy_price_offset", 0.0)
offset = strat_cfg.execution.buy_price_offset
price = round(float(data["price"]) + offset, 3)
vol = int(actual_amt / (price if price > 0 else 1.0) / 100) * 100
@@ -1631,10 +1679,9 @@ class MultiEngineManager:
return
try:
strategy_config = self.config.strategies.get(strategy_name)
offset = (
self.config["strategies"][strategy_name]
.get("execution", {})
.get("sell_price_offset", 0.0)
strategy_config.execution.sell_price_offset if strategy_config else 0.0
)
price = round(float(data["price"]) + offset, 3)
@@ -1691,54 +1738,72 @@ class MultiEngineManager:
if not strat_cfg:
self.logger.error(f"[{strategy_name}] 策略配置不存在")
return
# 获取目标持仓百分比
position_pct = float(data.get("position_pct", 0))
if position_pct <= 0:
self.logger.warning(f"[{strategy_name}] 百分比模式买入: position_pct 无效 ({position_pct})")
self.logger.warning(
f"[{strategy_name}] 百分比模式买入: position_pct 无效 ({position_pct})"
)
return
self.logger.info(f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}"
)
try:
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
if not asset:
self.logger.error(f"[{strategy_name}] API 错误: query_stock_asset 返回 None")
self.logger.error(
f"[{strategy_name}] API 错误: query_stock_asset 返回 None"
)
return
total_asset = asset.total_asset
available_cash = asset.cash
self.logger.info(f"[{strategy_name}] [百分比模式] 账户总资产: {total_asset:.2f}, 可用资金: {available_cash:.2f}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 账户总资产: {total_asset:.2f}, 可用资金: {available_cash:.2f}"
)
# 计算目标金额
target_amount = total_asset * position_pct
actual_amount = min(target_amount, available_cash * 0.98) # 预留手续费滑点
self.logger.info(f"[{strategy_name}] [百分比模式] 目标金额: {target_amount:.2f}, 实际可用: {actual_amount:.2f}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 目标金额: {target_amount:.2f}, 实际可用: {actual_amount:.2f}"
)
# 检查最小金额限制
if actual_amount < 2000:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 金额过小 ({actual_amount:.2f} < 2000)")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截买入: 金额过小 ({actual_amount:.2f} < 2000)"
)
return
# 价格校验
price = float(data.get("price", 0))
offset = strat_cfg.execution.buy_price_offset
price = round(price + offset, 3)
if price <= 0:
self.logger.warning(f"[{strategy_name}] [百分比模式] 价格异常: {price}强制设为1.0")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 价格异常: {price}强制设为1.0"
)
price = 1.0
# 计算股数
vol = int(actual_amount / price / 100) * 100
self.logger.info(f"[{strategy_name}] [百分比模式] 计算股数: 资金{actual_amount:.2f} / 价格{price} -> {vol}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 计算股数: 资金{actual_amount:.2f} / 价格{price} -> {vol}"
)
if vol < 100:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})"
)
return
# 记录订单执行请求
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1747,7 +1812,7 @@ class MultiEngineManager:
volume=vol,
price=price,
)
oid = unit.xt_trader.order_stock(
unit.acc_obj,
data["stock_code"],
@@ -1758,10 +1823,12 @@ class MultiEngineManager:
strategy_name,
"PyBuyPct",
)
if oid != -1:
unit.order_cache[oid] = (strategy_name, data["stock_code"], "BUY")
self.logger.info(f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单买入: {data['stock_code']} {vol}股 @ {price}")
self.logger.info(
f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单买入: {data['stock_code']} {vol}股 @ {price}"
)
# 记录订单执行成功
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1791,31 +1858,41 @@ class MultiEngineManager:
if not strat_cfg:
self.logger.error(f"[{strategy_name}] 策略配置不存在")
return
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
)
try:
# 查询实盘持仓
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
if real_pos is None:
self.logger.error(f"[{strategy_name}] [百分比模式] API 错误: query_stock_positions 返回 None")
self.logger.error(
f"[{strategy_name}] [百分比模式] API 错误: query_stock_positions 返回 None"
)
return
rp = next((p for p in real_pos if p.stock_code == data["stock_code"]), None)
can_use = rp.can_use_volume if rp else 0
self.logger.info(f"[{strategy_name}] [百分比模式] 股票 {data['stock_code']} 实盘可用持仓: {can_use}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 股票 {data['stock_code']} 实盘可用持仓: {can_use}"
)
if can_use <= 0:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓"
)
return
# 执行清仓
price = float(data.get("price", 0))
offset = strat_cfg.execution.sell_price_offset
price = round(price + offset, 3)
self.logger.info(f"[{strategy_name}] [百分比模式] 执行清仓: {data['stock_code']} @ {price}, 数量: {can_use}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 执行清仓: {data['stock_code']} @ {price}, 数量: {can_use}"
)
# 记录订单执行请求
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1824,7 +1901,7 @@ class MultiEngineManager:
volume=can_use,
price=price,
)
oid = unit.xt_trader.order_stock(
unit.acc_obj,
data["stock_code"],
@@ -1835,10 +1912,12 @@ class MultiEngineManager:
strategy_name,
"PySellPct",
)
if oid != -1:
unit.order_cache[oid] = (strategy_name, data["stock_code"], "SELL")
self.logger.info(f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单卖出: {data['stock_code']} {can_use}股 @ {price}")
self.logger.info(
f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单卖出: {data['stock_code']} {can_use}股 @ {price}"
)
# 记录订单执行成功
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1862,55 +1941,73 @@ class MultiEngineManager:
except:
self.logger.error(traceback.format_exc())
"""处理百分比模式的买入逻辑"""
strat_cfg = self.config["strategies"][strategy_name]
strat_cfg = self.config.get_strategy(strategy_name)
# 获取目标持仓百分比
position_pct = float(data.get("position_pct", 0))
if position_pct <= 0:
self.logger.warning(f"[{strategy_name}] 百分比模式买入: position_pct 无效 ({position_pct})")
self.logger.warning(
f"[{strategy_name}] 百分比模式买入: position_pct 无效 ({position_pct})"
)
return
self.logger.info(f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}"
)
try:
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
if not asset:
self.logger.error(f"[{strategy_name}] API 错误: query_stock_asset 返回 None")
self.logger.error(
f"[{strategy_name}] API 错误: query_stock_asset 返回 None"
)
return
total_asset = asset.total_asset
available_cash = asset.cash
self.logger.info(f"[{strategy_name}] [百分比模式] 账户总资产: {total_asset:.2f}, 可用资金: {available_cash:.2f}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 账户总资产: {total_asset:.2f}, 可用资金: {available_cash:.2f}"
)
# 计算目标金额
target_amount = total_asset * position_pct
actual_amount = min(target_amount, available_cash * 0.98) # 预留手续费滑点
self.logger.info(f"[{strategy_name}] [百分比模式] 目标金额: {target_amount:.2f}, 实际可用: {actual_amount:.2f}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 目标金额: {target_amount:.2f}, 实际可用: {actual_amount:.2f}"
)
# 检查最小金额限制
if actual_amount < 2000:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 金额过小 ({actual_amount:.2f} < 2000)")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截买入: 金额过小 ({actual_amount:.2f} < 2000)"
)
return
# 价格校验
price = float(data.get("price", 0))
offset = strat_cfg.get("execution", {}).get("buy_price_offset", 0.0)
offset = strat_cfg.execution.buy_price_offset
price = round(price + offset, 3)
if price <= 0:
self.logger.warning(f"[{strategy_name}] [百分比模式] 价格异常: {price}强制设为1.0")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 价格异常: {price}强制设为1.0"
)
price = 1.0
# 计算股数
vol = int(actual_amount / price / 100) * 100
self.logger.info(f"[{strategy_name}] [百分比模式] 计算股数: 资金{actual_amount:.2f} / 价格{price} -> {vol}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 计算股数: 资金{actual_amount:.2f} / 价格{price} -> {vol}"
)
if vol < 100:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})"
)
return
# 记录订单执行请求
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1919,7 +2016,7 @@ class MultiEngineManager:
volume=vol,
price=price,
)
oid = unit.xt_trader.order_stock(
unit.acc_obj,
data["stock_code"],
@@ -1930,10 +2027,12 @@ class MultiEngineManager:
strategy_name,
"PyBuyPct",
)
if oid != -1:
unit.order_cache[oid] = (strategy_name, data["stock_code"], "BUY")
self.logger.info(f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单买入: {data['stock_code']} {vol}股 @ {price}")
self.logger.info(
f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单买入: {data['stock_code']} {vol}股 @ {price}"
)
# 记录订单执行成功
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1959,31 +2058,44 @@ class MultiEngineManager:
def _execute_percentage_sell(self, unit, strategy_name, data):
"""处理百分比模式的卖出逻辑(清仓)"""
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
self.logger.info(
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
)
try:
# 查询实盘持仓
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
if real_pos is None:
self.logger.error(f"[{strategy_name}] [百分比模式] API 错误: query_stock_positions 返回 None")
self.logger.error(
f"[{strategy_name}] [百分比模式] API 错误: query_stock_positions 返回 None"
)
return
rp = next((p for p in real_pos if p.stock_code == data["stock_code"]), None)
can_use = rp.can_use_volume if rp else 0
self.logger.info(f"[{strategy_name}] [百分比模式] 股票 {data['stock_code']} 实盘可用持仓: {can_use}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 股票 {data['stock_code']} 实盘可用持仓: {can_use}"
)
if can_use <= 0:
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓")
self.logger.warning(
f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓"
)
return
# 执行清仓
price = float(data.get("price", 0))
offset = self.config["strategies"][strategy_name].get("execution", {}).get("sell_price_offset", 0.0)
strategy_config = self.config.get_strategy(strategy_name)
offset = (
strategy_config.execution.sell_price_offset if strategy_config else 0.0
)
price = round(price + offset, 3)
self.logger.info(f"[{strategy_name}] [百分比模式] 执行清仓: {data['stock_code']} @ {price}, 数量: {can_use}")
self.logger.info(
f"[{strategy_name}] [百分比模式] 执行清仓: {data['stock_code']} @ {price}, 数量: {can_use}"
)
# 记录订单执行请求
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -1992,7 +2104,7 @@ class MultiEngineManager:
volume=can_use,
price=price,
)
oid = unit.xt_trader.order_stock(
unit.acc_obj,
data["stock_code"],
@@ -2003,10 +2115,12 @@ class MultiEngineManager:
strategy_name,
"PySellPct",
)
if oid != -1:
unit.order_cache[oid] = (strategy_name, data["stock_code"], "SELL")
self.logger.info(f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单卖出: {data['stock_code']} {can_use}股 @ {price}")
self.logger.info(
f"√√√ [{unit.alias}] [{strategy_name}] [百分比模式] 下单卖出: {data['stock_code']} {can_use}股 @ {price}"
)
# 记录订单执行成功
self.qmt_logger.log_order_execution(
strategy_name=strategy_name,
@@ -2029,6 +2143,7 @@ class MultiEngineManager:
)
except:
self.logger.error(traceback.format_exc())
def verify_connection(self, timeout=5):
"""验证物理连接是否有效"""
try: