refactor(qmt): 优化配置模型和百分比交易逻辑
- 统一使用配置模型属性访问替代字典下标 - 完善百分比模式买卖的日志记录和错误处理 - 代码格式化和死代码清理 - 更新 notebook 数据及测试脚本
This commit is contained in:
@@ -137,7 +137,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.12.11"
|
"version": "3.13.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
"Please upgrade to Gymnasium, the maintained drop-in replacement of Gym, or contact the authors of your software and request that they upgrade.\n",
|
"Please upgrade to Gymnasium, the maintained drop-in replacement of Gym, or contact the authors of your software and request that they upgrade.\n",
|
||||||
"Users of this version of Gym should be able to simply replace 'import gym' with 'import gymnasium as gym' in the vast majority of cases.\n",
|
"Users of this version of Gym should be able to simply replace 'import gym' with 'import gymnasium as gym' in the vast majority of cases.\n",
|
||||||
"See the migration guide at https://gymnasium.farama.org/introduction/migration_guide/ for additional information.\n",
|
"See the migration guide at https://gymnasium.farama.org/introduction/migration_guide/ for additional information.\n",
|
||||||
"[177513:MainThread](2026-02-25 22:33:43,460) INFO - qlib.Initialization - [config.py:452] - default_conf: client.\n",
|
"[65879:MainThread](2026-03-01 22:39:22,291) INFO - qlib.Initialization - [config.py:452] - default_conf: client.\n",
|
||||||
"[177513:MainThread](2026-02-25 22:33:43,461) WARNING - qlib.Initialization - [config.py:459] - Unrecognized config freq\n",
|
"[65879:MainThread](2026-03-01 22:39:22,291) WARNING - qlib.Initialization - [config.py:459] - Unrecognized config freq\n",
|
||||||
"[177513:MainThread](2026-02-25 22:33:43,467) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings.\n",
|
"[65879:MainThread](2026-03-01 22:39:22,297) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings.\n",
|
||||||
"[177513:MainThread](2026-02-25 22:33:43,469) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': PosixPath('/mnt/d/PyProject/NewStock/data/qlib')}\n"
|
"[65879:MainThread](2026-03-01 22:39:22,298) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': PosixPath('/mnt/d/PyProject/NewStock/data/qlib')}\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"cyq perf\n",
|
"cyq perf\n",
|
||||||
"left merge on ['ts_code', 'trade_date']\n",
|
"left merge on ['ts_code', 'trade_date']\n",
|
||||||
"<class 'pandas.core.frame.DataFrame'>\n",
|
"<class 'pandas.core.frame.DataFrame'>\n",
|
||||||
"RangeIndex: 9436343 entries, 0 to 9436342\n",
|
"RangeIndex: 9456764 entries, 0 to 9456763\n",
|
||||||
"Data columns (total 33 columns):\n",
|
"Data columns (total 33 columns):\n",
|
||||||
" # Column Dtype \n",
|
" # Column Dtype \n",
|
||||||
"--- ------ ----- \n",
|
"--- ------ ----- \n",
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 10,
|
"execution_count": 7,
|
||||||
"id": "5f3847ec",
|
"id": "5f3847ec",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -182,15 +182,17 @@
|
|||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
" trade_date return_5_rank\n",
|
" trade_date ma5 ma10 return_5 close\n",
|
||||||
"6527870 2024-06-03 0.523969\n",
|
"6527953 2024-01-23 7.358 7.500 -0.043364 7.28\n",
|
||||||
" trade_date ma5 ma10 close\n",
|
"6527952 2024-01-24 7.412 7.507 0.036339 7.70\n",
|
||||||
"6527870 2024-06-03 10.526 10.216 10.37\n"
|
"6527951 2024-01-25 7.552 7.556 0.094213 8.13\n",
|
||||||
|
"6527950 2024-01-26 7.692 7.609 0.093583 8.18\n",
|
||||||
|
"6527949 2024-01-29 7.910 7.667 0.152022 8.26\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"\n",
|
"df = df.sort_values(by=['trade_date'])\n",
|
||||||
"df['return_5'] = df.groupby('ts_code')['close'].pct_change(periods=5)\n",
|
"df['return_5'] = df.groupby('ts_code')['close'].pct_change(periods=5)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"df['return_5_rank'] = df.groupby('trade_date')['return_5'].transform(\n",
|
"df['return_5_rank'] = df.groupby('trade_date')['return_5'].transform(\n",
|
||||||
@@ -198,10 +200,19 @@
|
|||||||
" )\n",
|
" )\n",
|
||||||
"\n",
|
"\n",
|
||||||
"df['ma5'] = df.groupby('ts_code')['close'].transform(lambda x: x.rolling(window=5, min_periods=1).mean())\n",
|
"df['ma5'] = df.groupby('ts_code')['close'].transform(lambda x: x.rolling(window=5, min_periods=1).mean())\n",
|
||||||
"df['ma10'] = df.groupby('ts_code')['close'].transform(lambda x: x.rolling(window=10, min_periods=1).mean())\n",
|
"df['ma10'] = df.groupby('ts_code')['close'].transform(lambda x: x.rolling(window=10, min_periods=1).mean())\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "e6de3c12",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
"\n",
|
"\n",
|
||||||
"print(df[(df['ts_code'] == '601117.SH') & (df['trade_date'] == '2024-06-03')][['trade_date', 'return_5_rank']])\n",
|
"# print(df[(df['ts_code'] == '601117.SH') & (df['trade_date'] >= '2024-01-23')][['trade_date', 'return_5_rank']])\n",
|
||||||
"print(df[(df['ts_code'] == '601117.SH') & (df['trade_date'] == '2024-06-03')][['trade_date', 'ma5', 'ma10', 'close']])"
|
"print(df[(df['ts_code'] == '601117.SH') & (df['trade_date'] >= '2024-01-23') & (df['trade_date'] <= '2024-01-29')][['trade_date', 'ma5', 'ma10', 'return_5', 'close']])"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -9370,7 +9381,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.13.2"
|
"version": "3.12.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|||||||
@@ -2508,7 +2508,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.13.2"
|
"version": "3.12.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|||||||
@@ -2,14 +2,9 @@ from operator import index
|
|||||||
|
|
||||||
import tushare as ts
|
import tushare as ts
|
||||||
|
|
||||||
import sys
|
|
||||||
print(sys.path)
|
|
||||||
|
|
||||||
from main.factor.factor import calculate_arbr
|
|
||||||
|
|
||||||
ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')
|
ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')
|
||||||
pro = ts.pro_api()
|
pro = ts.pro_api()
|
||||||
|
|
||||||
df = pro.dc_member(trade_date='20190105')
|
df = ts.pro_bar(ts_code='601117.SH', adj='qfq', start_date='20240129', end_date='20240129', fields='trade_date,ts_code,close,tor')
|
||||||
|
|
||||||
print(df)
|
print(df)
|
||||||
@@ -21,12 +21,24 @@ from xtquant import xtconstant
|
|||||||
try:
|
try:
|
||||||
from .message_processor import StreamMessageProcessor
|
from .message_processor import StreamMessageProcessor
|
||||||
from .logger import QMTLogger
|
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:
|
except ImportError:
|
||||||
# 当作为脚本直接运行时
|
# 当作为脚本直接运行时
|
||||||
from message_processor import StreamMessageProcessor
|
from message_processor import StreamMessageProcessor
|
||||||
from logger import QMTLogger
|
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 time
|
||||||
import datetime
|
import datetime
|
||||||
import traceback
|
import traceback
|
||||||
@@ -429,7 +441,6 @@ class TradingUnit:
|
|||||||
self.account_id = t_cfg.account_id
|
self.account_id = t_cfg.account_id
|
||||||
self.account_type = t_cfg.account_type
|
self.account_type = t_cfg.account_type
|
||||||
|
|
||||||
|
|
||||||
self.xt_trader = None
|
self.xt_trader = None
|
||||||
self.acc_obj = None
|
self.acc_obj = None
|
||||||
self.callback = None
|
self.callback = None
|
||||||
@@ -567,7 +578,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
# 尝试多个路径
|
# 尝试多个路径
|
||||||
env_paths = [
|
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"),
|
||||||
os.path.join(os.path.dirname(__file__), "..", "config", ".env.local"),
|
os.path.join(os.path.dirname(__file__), "..", "config", ".env.local"),
|
||||||
"/qmt/config/.env.local",
|
"/qmt/config/.env.local",
|
||||||
@@ -583,7 +596,9 @@ class MultiEngineManager:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not loaded:
|
if not loaded:
|
||||||
self.logger.warning("[Config] 警告: 未找到 .env.local 文件,使用默认配置")
|
self.logger.warning(
|
||||||
|
"[Config] 警告: 未找到 .env.local 文件,使用默认配置"
|
||||||
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
self.logger.warning("[Config] 警告: 未安装 python-dotenv,使用默认配置")
|
self.logger.warning("[Config] 警告: 未安装 python-dotenv,使用默认配置")
|
||||||
|
|
||||||
@@ -616,15 +631,11 @@ class MultiEngineManager:
|
|||||||
sh.setFormatter(fmt)
|
sh.setFormatter(fmt)
|
||||||
self.logger.addHandler(fh)
|
self.logger.addHandler(fh)
|
||||||
self.logger.addHandler(sh)
|
self.logger.addHandler(sh)
|
||||||
|
|
||||||
def get_strategies_by_terminal(self, qmt_id):
|
def get_strategies_by_terminal(self, qmt_id):
|
||||||
if not self.config:
|
if not self.config:
|
||||||
return []
|
return []
|
||||||
return self.config.get_strategies_by_terminal(qmt_id)
|
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):
|
def run_trading_loop(self):
|
||||||
self.logger = logging.getLogger("QMT_Engine")
|
self.logger = logging.getLogger("QMT_Engine")
|
||||||
@@ -839,7 +850,7 @@ class MultiEngineManager:
|
|||||||
"code": data.get("stock_code"),
|
"code": data.get("stock_code"),
|
||||||
"order_mode": order_mode,
|
"order_mode": order_mode,
|
||||||
"msg_time": data.get("timestamp"),
|
"msg_time": data.get("timestamp"),
|
||||||
"skip_reason": "休盘后只消费消息不下单"
|
"skip_reason": "休盘后只消费消息不下单",
|
||||||
},
|
},
|
||||||
result=True,
|
result=True,
|
||||||
)
|
)
|
||||||
@@ -847,7 +858,11 @@ class MultiEngineManager:
|
|||||||
self.qmt_logger.log_validation(
|
self.qmt_logger.log_validation(
|
||||||
validation_type="action_check",
|
validation_type="action_check",
|
||||||
strategy_name=strategy_name,
|
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,
|
result=True,
|
||||||
)
|
)
|
||||||
# 根据下单模式执行相应逻辑
|
# 根据下单模式执行相应逻辑
|
||||||
@@ -859,7 +874,11 @@ class MultiEngineManager:
|
|||||||
self.qmt_logger.log_validation(
|
self.qmt_logger.log_validation(
|
||||||
validation_type="action_check",
|
validation_type="action_check",
|
||||||
strategy_name=strategy_name,
|
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,
|
result=True,
|
||||||
)
|
)
|
||||||
# 根据下单模式执行相应逻辑
|
# 根据下单模式执行相应逻辑
|
||||||
@@ -934,10 +953,7 @@ class MultiEngineManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 2. 持仓数检查
|
# 2. 持仓数检查
|
||||||
if (
|
if self.pos_manager.get_holding_count(strategy_name) >= strat_cfg.total_slots:
|
||||||
self.pos_manager.get_holding_count(strategy_name)
|
|
||||||
>= strat_cfg.total_slots
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -949,7 +965,8 @@ class MultiEngineManager:
|
|||||||
# 权重默认为 1,支持通过 weight 字段调整资金分配比例
|
# 权重默认为 1,支持通过 weight 字段调整资金分配比例
|
||||||
# 示例:strategies = {"strategy_a": {"total_slots": 5, "weight": 1}, "strategy_b": {"total_slots": 5, "weight": 2}}
|
# 示例:strategies = {"strategy_a": {"total_slots": 5, "weight": 1}, "strategy_b": {"total_slots": 5, "weight": 2}}
|
||||||
total_weighted_slots = sum(
|
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
|
for s in terminal_strategies
|
||||||
if self.config.get_strategy(s)
|
if self.config.get_strategy(s)
|
||||||
)
|
)
|
||||||
@@ -1131,31 +1148,43 @@ class MultiEngineManager:
|
|||||||
# 获取目标持仓百分比
|
# 获取目标持仓百分比
|
||||||
position_pct = float(data.get("position_pct", 0))
|
position_pct = float(data.get("position_pct", 0))
|
||||||
if 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
|
return
|
||||||
|
|
||||||
self.logger.info(f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}")
|
self.logger.info(
|
||||||
|
f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
|
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
|
||||||
if not asset:
|
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
|
return
|
||||||
|
|
||||||
total_asset = asset.total_asset
|
total_asset = asset.total_asset
|
||||||
available_cash = asset.cash
|
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
|
target_amount = total_asset * position_pct
|
||||||
actual_amount = min(target_amount, available_cash * 0.98) # 预留手续费滑点
|
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:
|
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
|
return
|
||||||
|
|
||||||
# 价格校验
|
# 价格校验
|
||||||
@@ -1164,15 +1193,21 @@ class MultiEngineManager:
|
|||||||
price = round(price + offset, 3)
|
price = round(price + offset, 3)
|
||||||
|
|
||||||
if price <= 0:
|
if price <= 0:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 价格异常: {price},强制设为1.0")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 价格异常: {price},强制设为1.0"
|
||||||
|
)
|
||||||
price = 1.0
|
price = 1.0
|
||||||
|
|
||||||
# 计算股数
|
# 计算股数
|
||||||
vol = int(actual_amount / price / 100) * 100
|
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:
|
if vol < 100:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 记录订单执行请求
|
# 记录订单执行请求
|
||||||
@@ -1197,7 +1232,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
if oid != -1:
|
if oid != -1:
|
||||||
unit.order_cache[oid] = (strategy_name, data["stock_code"], "BUY")
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
strategy_name=strategy_name,
|
strategy_name=strategy_name,
|
||||||
@@ -1227,22 +1264,30 @@ class MultiEngineManager:
|
|||||||
if not strat_cfg:
|
if not strat_cfg:
|
||||||
self.logger.error(f"[{strategy_name}] 策略配置不存在")
|
self.logger.error(f"[{strategy_name}] 策略配置不存在")
|
||||||
return
|
return
|
||||||
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
|
self.logger.info(
|
||||||
|
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 查询实盘持仓
|
# 查询实盘持仓
|
||||||
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
||||||
if real_pos is None:
|
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
|
return
|
||||||
|
|
||||||
rp = next((p for p in real_pos if p.stock_code == data["stock_code"]), None)
|
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
|
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:
|
if can_use <= 0:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 执行清仓
|
# 执行清仓
|
||||||
@@ -1250,7 +1295,9 @@ class MultiEngineManager:
|
|||||||
offset = strat_cfg.execution.sell_price_offset
|
offset = strat_cfg.execution.sell_price_offset
|
||||||
price = round(price + offset, 3)
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
@@ -1274,7 +1321,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
if oid != -1:
|
if oid != -1:
|
||||||
unit.order_cache[oid] = (strategy_name, data["stock_code"], "SELL")
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
strategy_name=strategy_name,
|
strategy_name=strategy_name,
|
||||||
@@ -1396,20 +1445,19 @@ class MultiEngineManager:
|
|||||||
if not strat_cfg:
|
if not strat_cfg:
|
||||||
self.logger.error(f"[{strategy_name}] 策略配置不存在")
|
self.logger.error(f"[{strategy_name}] 策略配置不存在")
|
||||||
return
|
return
|
||||||
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
|
self.logger.info(
|
||||||
|
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
|
||||||
|
)
|
||||||
|
|
||||||
# 1. 槽位校验
|
# 1. 槽位校验
|
||||||
if data["total_slots"] != strat_cfg["total_slots"]:
|
if data["total_slots"] != strat_cfg.total_slots:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"[{strategy_name}] 信号槽位({data['total_slots']})与配置({strat_cfg['total_slots']})不符"
|
f"[{strategy_name}] 信号槽位({data['total_slots']})与配置({strat_cfg.total_slots})不符"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 2. 持仓数检查
|
# 2. 持仓数检查
|
||||||
if (
|
if self.pos_manager.get_holding_count(strategy_name) >= strat_cfg.total_slots:
|
||||||
self.pos_manager.get_holding_count(strategy_name)
|
|
||||||
>= strat_cfg["total_slots"]
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1419,18 +1467,18 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
# 计算加权槽位总和(支持策略权重配置)
|
# 计算加权槽位总和(支持策略权重配置)
|
||||||
# 权重默认为 1,支持通过 weight 字段调整资金分配比例
|
# 权重默认为 1,支持通过 weight 字段调整资金分配比例
|
||||||
# 示例:strategies = {"strategy_a": {"total_slots": 5, "weight": 1}, "strategy_b": {"total_slots": 5, "weight": 2}}
|
# 计算加权槽位总和
|
||||||
total_weighted_slots = sum(
|
total_weighted_slots = 0
|
||||||
self.config["strategies"][s].get("total_slots", 1)
|
for s in terminal_strategies:
|
||||||
* self.config["strategies"][s].get("weight", 1)
|
s_cfg = self.config.strategies.get(s)
|
||||||
for s in terminal_strategies
|
if s_cfg:
|
||||||
)
|
total_weighted_slots += s_cfg.total_slots * s_cfg.weight
|
||||||
|
|
||||||
if not asset or total_weighted_slots <= 0:
|
if not asset or total_weighted_slots <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
# 获取当前策略的权重
|
# 获取当前策略的权重
|
||||||
weight = strat_cfg.get("weight", 1)
|
weight = strat_cfg.weight
|
||||||
|
|
||||||
# 4. 资金加权分配 (基于该终端总资产)
|
# 4. 资金加权分配 (基于该终端总资产)
|
||||||
total_equity = asset.cash + asset.market_value
|
total_equity = asset.cash + asset.market_value
|
||||||
@@ -1444,7 +1492,7 @@ class MultiEngineManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 4. 价格与股数
|
# 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)
|
price = round(float(data["price"]) + offset, 3)
|
||||||
vol = int(actual_amt / (price if price > 0 else 1.0) / 100) * 100
|
vol = int(actual_amt / (price if price > 0 else 1.0) / 100) * 100
|
||||||
|
|
||||||
@@ -1631,10 +1679,9 @@ class MultiEngineManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
strategy_config = self.config.strategies.get(strategy_name)
|
||||||
offset = (
|
offset = (
|
||||||
self.config["strategies"][strategy_name]
|
strategy_config.execution.sell_price_offset if strategy_config else 0.0
|
||||||
.get("execution", {})
|
|
||||||
.get("sell_price_offset", 0.0)
|
|
||||||
)
|
)
|
||||||
price = round(float(data["price"]) + offset, 3)
|
price = round(float(data["price"]) + offset, 3)
|
||||||
|
|
||||||
@@ -1695,31 +1742,43 @@ class MultiEngineManager:
|
|||||||
# 获取目标持仓百分比
|
# 获取目标持仓百分比
|
||||||
position_pct = float(data.get("position_pct", 0))
|
position_pct = float(data.get("position_pct", 0))
|
||||||
if 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
|
return
|
||||||
|
|
||||||
self.logger.info(f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}")
|
self.logger.info(
|
||||||
|
f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
|
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
|
||||||
if not asset:
|
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
|
return
|
||||||
|
|
||||||
total_asset = asset.total_asset
|
total_asset = asset.total_asset
|
||||||
available_cash = asset.cash
|
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
|
target_amount = total_asset * position_pct
|
||||||
actual_amount = min(target_amount, available_cash * 0.98) # 预留手续费滑点
|
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:
|
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
|
return
|
||||||
|
|
||||||
# 价格校验
|
# 价格校验
|
||||||
@@ -1728,15 +1787,21 @@ class MultiEngineManager:
|
|||||||
price = round(price + offset, 3)
|
price = round(price + offset, 3)
|
||||||
|
|
||||||
if price <= 0:
|
if price <= 0:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 价格异常: {price},强制设为1.0")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 价格异常: {price},强制设为1.0"
|
||||||
|
)
|
||||||
price = 1.0
|
price = 1.0
|
||||||
|
|
||||||
# 计算股数
|
# 计算股数
|
||||||
vol = int(actual_amount / price / 100) * 100
|
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:
|
if vol < 100:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 记录订单执行请求
|
# 记录订单执行请求
|
||||||
@@ -1761,7 +1826,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
if oid != -1:
|
if oid != -1:
|
||||||
unit.order_cache[oid] = (strategy_name, data["stock_code"], "BUY")
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
strategy_name=strategy_name,
|
strategy_name=strategy_name,
|
||||||
@@ -1791,22 +1858,30 @@ class MultiEngineManager:
|
|||||||
if not strat_cfg:
|
if not strat_cfg:
|
||||||
self.logger.error(f"[{strategy_name}] 策略配置不存在")
|
self.logger.error(f"[{strategy_name}] 策略配置不存在")
|
||||||
return
|
return
|
||||||
self.logger.info(f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)")
|
self.logger.info(
|
||||||
|
f"[{strategy_name}] [百分比模式] 处理卖出: {data['stock_code']} (清仓)"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 查询实盘持仓
|
# 查询实盘持仓
|
||||||
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
||||||
if real_pos is None:
|
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
|
return
|
||||||
|
|
||||||
rp = next((p for p in real_pos if p.stock_code == data["stock_code"]), None)
|
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
|
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:
|
if can_use <= 0:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 执行清仓
|
# 执行清仓
|
||||||
@@ -1814,7 +1889,9 @@ class MultiEngineManager:
|
|||||||
offset = strat_cfg.execution.sell_price_offset
|
offset = strat_cfg.execution.sell_price_offset
|
||||||
price = round(price + offset, 3)
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
@@ -1838,7 +1915,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
if oid != -1:
|
if oid != -1:
|
||||||
unit.order_cache[oid] = (strategy_name, data["stock_code"], "SELL")
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
strategy_name=strategy_name,
|
strategy_name=strategy_name,
|
||||||
@@ -1862,53 +1941,71 @@ class MultiEngineManager:
|
|||||||
except:
|
except:
|
||||||
self.logger.error(traceback.format_exc())
|
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))
|
position_pct = float(data.get("position_pct", 0))
|
||||||
if 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
|
return
|
||||||
|
|
||||||
self.logger.info(f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}")
|
self.logger.info(
|
||||||
|
f"[{strategy_name}] [百分比模式] 处理买入: {data['stock_code']}, 目标占比: {position_pct}"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
|
asset = unit.xt_trader.query_stock_asset(unit.acc_obj)
|
||||||
if not asset:
|
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
|
return
|
||||||
|
|
||||||
total_asset = asset.total_asset
|
total_asset = asset.total_asset
|
||||||
available_cash = asset.cash
|
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
|
target_amount = total_asset * position_pct
|
||||||
actual_amount = min(target_amount, available_cash * 0.98) # 预留手续费滑点
|
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:
|
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
|
return
|
||||||
|
|
||||||
# 价格校验
|
# 价格校验
|
||||||
price = float(data.get("price", 0))
|
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)
|
price = round(price + offset, 3)
|
||||||
|
|
||||||
if price <= 0:
|
if price <= 0:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 价格异常: {price},强制设为1.0")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 价格异常: {price},强制设为1.0"
|
||||||
|
)
|
||||||
price = 1.0
|
price = 1.0
|
||||||
|
|
||||||
# 计算股数
|
# 计算股数
|
||||||
vol = int(actual_amount / price / 100) * 100
|
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:
|
if vol < 100:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 拦截买入: 股数不足 100 ({vol})"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 记录订单执行请求
|
# 记录订单执行请求
|
||||||
@@ -1933,7 +2030,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
if oid != -1:
|
if oid != -1:
|
||||||
unit.order_cache[oid] = (strategy_name, data["stock_code"], "BUY")
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
strategy_name=strategy_name,
|
strategy_name=strategy_name,
|
||||||
@@ -1959,30 +2058,43 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
def _execute_percentage_sell(self, unit, strategy_name, data):
|
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:
|
try:
|
||||||
# 查询实盘持仓
|
# 查询实盘持仓
|
||||||
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
real_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
||||||
if real_pos is None:
|
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
|
return
|
||||||
|
|
||||||
rp = next((p for p in real_pos if p.stock_code == data["stock_code"]), None)
|
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
|
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:
|
if can_use <= 0:
|
||||||
self.logger.warning(f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓")
|
self.logger.warning(
|
||||||
|
f"[{strategy_name}] [百分比模式] 拦截卖出: 无可用持仓"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 执行清仓
|
# 执行清仓
|
||||||
price = float(data.get("price", 0))
|
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)
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
@@ -2006,7 +2118,9 @@ class MultiEngineManager:
|
|||||||
|
|
||||||
if oid != -1:
|
if oid != -1:
|
||||||
unit.order_cache[oid] = (strategy_name, data["stock_code"], "SELL")
|
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(
|
self.qmt_logger.log_order_execution(
|
||||||
strategy_name=strategy_name,
|
strategy_name=strategy_name,
|
||||||
@@ -2029,6 +2143,7 @@ class MultiEngineManager:
|
|||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
self.logger.error(traceback.format_exc())
|
self.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
def verify_connection(self, timeout=5):
|
def verify_connection(self, timeout=5):
|
||||||
"""验证物理连接是否有效"""
|
"""验证物理连接是否有效"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user