335 lines
14 KiB
Python
335 lines
14 KiB
Python
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})" |