real_trader/src/strategy_position_manager.py
2025-04-30 22:16:48 +08:00

335 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
import os
import json
from simulation_trader import SimulationTrader
from xtquant import xtconstant
from logger_config import get_logger
# 获取日志记录器
logger = get_logger('strategy')
# 策略仓位管理
strategy_positions = {
'real': {}, # 存储实盘策略持仓
'simulation': {} # 存储模拟交易策略持仓
}
strategy_trades = {
'real': {}, # 存储实盘策略交易记录
'simulation': {} # 存储模拟交易策略交易记录
}
pending_orders = {
'real': {}, # 存储实盘未完成委托
'simulation': {} # 存储模拟交易未完成委托
}
class StrategyPositionManager:
"""策略持仓管理器,负责管理不同策略的持仓情况"""
@staticmethod
def get_trader_type(trader):
"""根据交易实例确定交易类型
Args:
trader: 交易实例
Returns:
str: 'simulation''real'
"""
return 'simulation' if isinstance(trader, SimulationTrader) else 'real'
@staticmethod
def update_strategy_position(trader, strategy_name, code, direction, amount):
"""更新策略持仓
Args:
trader: 交易实例
strategy_name: 策略名称
code: 股票代码
direction: 'buy''sell'
amount: 交易数量
"""
if not strategy_name:
return
# 判断交易类型
trader_type = StrategyPositionManager.get_trader_type(trader)
# 确保策略在字典中
if strategy_name not in strategy_positions[trader_type]:
strategy_positions[trader_type][strategy_name] = {}
try:
# 获取交易实例持仓情况
actual_positions = trader.get_positions()
code_position = next((pos for pos in actual_positions if pos.get('stock_code') == code), None)
# 记录实际持仓总量
actual_total = code_position.get('volume', 0) if code_position else 0
actual_can_use = code_position.get('can_use_volume', 0) if code_position else 0
logger.info(f"实际持仓 - 代码: {code}, 总量: {actual_total}, 可用: {actual_can_use}")
# 如果股票代码在持仓字典中不存在,初始化它
if code not in strategy_positions[trader_type][strategy_name]:
strategy_positions[trader_type][strategy_name][code] = {
'total_amount': 0,
'closeable_amount': 0
}
# 直接使用实际持仓数据更新策略持仓
strategy_positions[trader_type][strategy_name][code]['total_amount'] = actual_total
strategy_positions[trader_type][strategy_name][code]['closeable_amount'] = actual_can_use
logger.info(f"更新策略持仓 - 交易类型: {trader_type}, 策略: {strategy_name}, 代码: {code}, 方向: {direction}, 数量: {amount}, 总量: {strategy_positions[trader_type][strategy_name][code]['total_amount']}, 可用: {strategy_positions[trader_type][strategy_name][code]['closeable_amount']}")
except Exception as e:
logger.error(f"获取实际持仓失败: {str(e)}")
# 异常情况下只记录错误,不尝试更新持仓
# 移除total_amount为0的持仓
if code in strategy_positions[trader_type][strategy_name] and strategy_positions[trader_type][strategy_name][code]['total_amount'] <= 0:
del strategy_positions[trader_type][strategy_name][code]
@staticmethod
def update_pending_orders(trader):
"""更新未完成委托状态
Args:
trader: 交易实例
"""
try:
# 判断当前交易类型
trader_type = StrategyPositionManager.get_trader_type(trader)
# 获取今日委托
today_entrusts = trader.get_today_entrust()
# 更新委托状态
for order_id, order_info in list(pending_orders[trader_type].items()):
entrust = next((e for e in today_entrusts if e.get('order_id') == order_id), None)
if entrust:
if entrust.get('order_status') in [xtconstant.ORDER_SUCCEEDED, xtconstant.ORDER_PART_SUCC]:
# 成交量计算
traded_amount = int(entrust.get('traded_volume', 0))
# 更新策略持仓
StrategyPositionManager.update_strategy_position(
trader,
order_info['strategy_name'],
order_info['code'],
order_info['direction'],
traded_amount
)
# 如果完全成交,从待处理列表中移除
if entrust.get('order_status') == xtconstant.ORDER_SUCCEEDED:
del pending_orders[trader_type][order_id]
# 如果已撤单、废单等终态,也从待处理列表中移除
elif entrust.get('order_status') in [xtconstant.ORDER_CANCELED, xtconstant.ORDER_JUNK]:
del pending_orders[trader_type][order_id]
except Exception as e:
logger.error(f"更新未完成委托状态失败: {str(e)}")
@staticmethod
def add_pending_order(trader, order_id, strategy_name, code, price, amount, direction):
"""添加未完成委托
Args:
trader: 交易实例
order_id: 委托编号
strategy_name: 策略名称
code: 股票代码
price: 委托价格
amount: 委托数量
direction: 交易方向,'buy''sell'
"""
if not order_id or order_id == 'simulation':
return
# 判断当前交易类型
trader_type = StrategyPositionManager.get_trader_type(trader)
# 添加到未完成委托列表
pending_orders[trader_type][order_id] = {
'strategy_name': strategy_name,
'code': code,
'price': price,
'amount': amount,
'direction': direction,
'created_time': time.time()
}
# 同时记录到交易历史
if strategy_name:
if strategy_name not in strategy_trades[trader_type]:
strategy_trades[trader_type][strategy_name] = []
strategy_trades[trader_type][strategy_name].append({
'time': time.strftime('%Y-%m-%d %H:%M:%S'),
'type': direction,
'code': code,
'price': price,
'amount': amount,
'order_id': order_id,
'status': 'pending'
})
logger.info(f"添加未完成委托: {order_id}, 交易类型: {trader_type}, 策略: {strategy_name}, 代码: {code}, 方向: {direction}")
@staticmethod
def clean_timeout_orders():
"""清理超时委托"""
current_time = time.time()
# 遍历实盘和模拟两种类型的委托
for trader_type in ['real', 'simulation']:
for order_id, order_info in list(pending_orders[trader_type].items()):
# 超过24小时的委托视为超时
if current_time - order_info['created_time'] > 24 * 60 * 60:
del pending_orders[trader_type][order_id]
@staticmethod
def load_strategy_data():
"""加载策略数据"""
global strategy_positions, strategy_trades, pending_orders
try:
if os.path.exists('strategy_data.json'):
with open('strategy_data.json', 'r') as f:
data = json.load(f)
# 直接使用新版数据结构,不再兼容旧版格式
strategy_positions = data.get('positions', {'real': {}, 'simulation': {}})
strategy_trades = data.get('trades', {'real': {}, 'simulation': {}})
pending_orders = data.get('pending_orders', {'real': {}, 'simulation': {}})
# 确保数据结构完整
if 'real' not in strategy_positions:
strategy_positions['real'] = {}
if 'simulation' not in strategy_positions:
strategy_positions['simulation'] = {}
if 'real' not in strategy_trades:
strategy_trades['real'] = {}
if 'simulation' not in strategy_trades:
strategy_trades['simulation'] = {}
if 'real' not in pending_orders:
pending_orders['real'] = {}
if 'simulation' not in pending_orders:
pending_orders['simulation'] = {}
logger.info("已加载策略数据")
logger.info(f"实盘策略数: {len(strategy_positions['real'])}, 模拟策略数: {len(strategy_positions['simulation'])}")
except Exception as e:
logger.error(f"加载策略数据失败: {str(e)}")
# 初始化空数据结构
strategy_positions = {'real': {}, 'simulation': {}}
strategy_trades = {'real': {}, 'simulation': {}}
pending_orders = {'real': {}, 'simulation': {}}
@staticmethod
def save_strategy_data():
"""保存策略数据"""
try:
with open('strategy_data.json', 'w') as f:
json.dump({
'positions': strategy_positions,
'trades': strategy_trades,
'pending_orders': pending_orders
}, f)
except Exception as e:
logger.error(f"保存策略数据失败: {str(e)}")
@staticmethod
def get_strategy_positions(trader, strategy_name=None):
"""获取策略持仓
Args:
trader: 交易实例
strategy_name: 策略名称如果为None返回所有持仓
Returns:
如果strategy_name为None返回交易实例的所有持仓
否则返回指定策略的持仓
"""
# 判断当前交易类型
trader_type = StrategyPositionManager.get_trader_type(trader)
# 如果指定了策略名称,返回该策略的持仓
if strategy_name:
# 获取真实账户持仓,用于计算可交易量
real_positions = trader.get_positions()
real_positions_map = {}
for pos in real_positions:
# 使用xt_trader返回的字段名
if 'stock_code' in pos and 'can_use_volume' in pos:
real_positions_map[pos['stock_code']] = pos
# 如果该策略没有记录,返回空列表
if strategy_name not in strategy_positions[trader_type]:
logger.info(f"Strategy {strategy_name} has no positions in {trader_type} mode")
return []
# 合并策略持仓和真实持仓的可交易量
result = []
for code, pos_info in strategy_positions[trader_type][strategy_name].items():
# 忽略total_amount为0的持仓
if pos_info['total_amount'] <= 0:
continue
# 使用真实账户的可交易量作为策略的可交易量上限
real_pos = real_positions_map.get(code, {})
closeable = min(pos_info['total_amount'], real_pos.get('can_use_volume', 0))
result.append({
code: {
'total_amount': pos_info['total_amount'],
'closeable_amount': closeable
}
})
logger.info(f"Strategy {strategy_name} positions in {trader_type} mode: {result}")
return result
# 否则返回原始持仓
positions = trader.get_positions()
logger.info(f"Positions in {trader_type} mode: {positions}")
return positions
@staticmethod
def clear_strategy(trader, strategy_name):
"""清除指定策略的持仓管理数据
Args:
trader: 交易实例
strategy_name: 策略名称
Returns:
tuple: (success, message)
success: 是否成功清除
message: 提示信息
"""
if not strategy_name:
return False, "缺少策略名称参数"
# 判断当前交易类型
trader_type = StrategyPositionManager.get_trader_type(trader)
# 检查策略是否存在于当前交易类型中
if strategy_name in strategy_positions[trader_type]:
# 从策略持仓字典中删除该策略
del strategy_positions[trader_type][strategy_name]
# 清除该策略的交易记录
if strategy_name in strategy_trades[trader_type]:
del strategy_trades[trader_type][strategy_name]
# 清除与该策略相关的未完成委托
for order_id, order_info in list(pending_orders[trader_type].items()):
if order_info.get('strategy_name') == strategy_name:
del pending_orders[trader_type][order_id]
# 保存更新后的策略数据
StrategyPositionManager.save_strategy_data()
logger.info(f"成功清除策略持仓数据: {strategy_name} (交易类型: {trader_type})")
return True, f"成功清除策略 '{strategy_name}' 的持仓数据 (交易类型: {trader_type})"
else:
logger.info(f"策略不存在或没有持仓数据: {strategy_name} (交易类型: {trader_type})")
return True, f"策略 '{strategy_name}' 不存在或没有持仓数据 (交易类型: {trader_type})"