381 lines
15 KiB
Python
381 lines
15 KiB
Python
import os
|
||
import json
|
||
import threading
|
||
from datetime import datetime
|
||
from logger_config import get_logger
|
||
from config import Config
|
||
from core.trade_constants import (
|
||
ORDER_DIRECTION_BUY,
|
||
ORDER_TYPE_LIMIT,
|
||
ORDER_TYPE_MARKET,
|
||
ORDER_STATUS_COMPLETED,
|
||
ORDER_STATUS_CANCELLED,
|
||
ORDER_STATUS_FAILED,
|
||
)
|
||
from core.local_position import LocalPosition
|
||
from core.local_order import LocalOrder
|
||
from utils.t0_stocks import is_t0
|
||
from typing import Dict
|
||
|
||
# 获取日志记录器
|
||
logger = get_logger("position_manager")
|
||
|
||
|
||
class PositionManager:
|
||
"""实盘策略持仓管理器,负责管理不同策略在实盘环境下的持仓情况"""
|
||
|
||
def __init__(self, strategy_name="default_strategy"):
|
||
"""初始化实盘持仓管理器"""
|
||
super().__init__()
|
||
self.strategy_name = strategy_name
|
||
# 创建线程锁
|
||
self._lock = threading.Lock()
|
||
# 策略持仓信息
|
||
self.positions: Dict[str, LocalPosition] = {} # {股票代码 -> LocalPosition}
|
||
# 待处理订单信息
|
||
self.pending_orders = {} # {order_id -> LocalOrder}
|
||
# 所有订单
|
||
self.all_orders = [] # [LocalOrder]
|
||
|
||
self.data_path = os.path.join(Config.DATA_DIR, self.strategy_name + "_positions.json")
|
||
# 确保数据目录存在
|
||
os.makedirs(os.path.dirname(self.data_path), exist_ok=True)
|
||
# 如果文件不存在,创建一个空文件
|
||
if not os.path.exists(self.data_path):
|
||
with open(self.data_path, 'w') as f:
|
||
f.write('{}')
|
||
else:
|
||
self.load_data()
|
||
|
||
def update_position(self, code, direction, amount):
|
||
with self._lock:
|
||
# 如果股票代码在持仓字典中不存在,初始化它
|
||
if code not in self.positions:
|
||
self.positions[code] = LocalPosition(code, 0, 0)
|
||
|
||
# 根据方向更新持仓
|
||
position = self.positions[code]
|
||
is_t0_stock = is_t0(code)
|
||
if direction == ORDER_DIRECTION_BUY:
|
||
position.total_amount += amount
|
||
if is_t0_stock:
|
||
position.closeable_amount += amount
|
||
else: # sell
|
||
position.total_amount -= amount
|
||
position.closeable_amount -= amount
|
||
|
||
logger.info(
|
||
f"更新策略持仓 - 策略: {self.strategy_name}, 代码: {code}, 方向: {direction}, 数量: {amount}, "
|
||
f"更新后总量: {position.total_amount}, "
|
||
f"可用: {position.closeable_amount}"
|
||
)
|
||
|
||
# 移除total_amount为0的持仓
|
||
if code in self.positions and self.positions[code].total_amount <= 0:
|
||
del self.positions[code]
|
||
logger.info(f"移除空持仓 - 策略: {self.strategy_name}, 代码: {code}")
|
||
|
||
# 在锁之外异步保存数据
|
||
threading.Thread(target=self.save_data).start()
|
||
|
||
def add_pending_order(
|
||
self, order_id, code, price, amount, direction, order_type=ORDER_TYPE_LIMIT
|
||
):
|
||
if not self.strategy_name:
|
||
return
|
||
|
||
with self._lock:
|
||
order = LocalOrder(order_id, code, price, amount, direction, order_type, created_time=datetime.now())
|
||
self.pending_orders[order_id] = order
|
||
self.all_orders.append(order)
|
||
|
||
logger.info(
|
||
f"添加订单 - ID: {order_id}, 策略: {self.strategy_name}, 代码: {code}, 方向: {direction}, "
|
||
f"数量: {amount}, 价格: {price}, 类型: {order_type}"
|
||
)
|
||
|
||
# 在锁之外异步保存数据
|
||
threading.Thread(target=self.save_data).start()
|
||
|
||
def update_order_status(self, order_id, filled, new_status):
|
||
with self._lock:
|
||
if order_id in self.pending_orders:
|
||
_order = self.pending_orders[order_id]
|
||
# 记录之前的状态用于日志
|
||
previous_status = _order.status
|
||
|
||
# 更新状态
|
||
_order.status = new_status
|
||
_order.filled = filled
|
||
|
||
# 记录状态变化日志
|
||
if previous_status != new_status:
|
||
code = self.pending_orders[order_id].code
|
||
logger.info(
|
||
f"订单状态变化: ID={order_id}, 代码={code}, 旧状态={previous_status}, 新状态={new_status}"
|
||
)
|
||
|
||
# 如果订单已完成,移除它
|
||
if new_status in [
|
||
ORDER_STATUS_COMPLETED,
|
||
ORDER_STATUS_CANCELLED,
|
||
ORDER_STATUS_FAILED,
|
||
]:
|
||
# 保留订单信息以供参考,但标记为已完成
|
||
del self.pending_orders[order_id]
|
||
logger.info(f"订单已删除 - ID: {order_id}, 状态: {new_status}")
|
||
|
||
has_changes = True
|
||
else:
|
||
has_changes = False
|
||
|
||
# 如果有修改,在锁外异步保存数据
|
||
if has_changes:
|
||
threading.Thread(target=self.save_data).start()
|
||
return True
|
||
|
||
return False
|
||
|
||
def get_pending_order(self, order_id) -> LocalOrder:
|
||
"""获取未完成委托信息
|
||
|
||
Args:
|
||
order_id: 订单ID
|
||
|
||
Returns:
|
||
dict: 委托信息,如果不存在返回None
|
||
"""
|
||
with self._lock:
|
||
order = self.pending_orders.get(order_id)
|
||
# 如果找到订单,返回它的副本而不是直接引用
|
||
return order
|
||
|
||
def get_pending_orders(self):
|
||
"""获取所有未完成委托
|
||
|
||
Returns:
|
||
dict: 订单ID到委托信息的映射
|
||
"""
|
||
# 创建临时变量存储锁内读取的数据
|
||
orders_copy = {}
|
||
with self._lock:
|
||
# 快速获取数据并立即释放锁
|
||
for order_id, order in self.pending_orders.items():
|
||
orders_copy[order_id] = order
|
||
|
||
# 锁外创建副本,避免外部修改影响内部数据
|
||
result = {}
|
||
for order_id, order in orders_copy.items():
|
||
result[order_id] = order
|
||
|
||
return result
|
||
|
||
def get_positions(self) -> Dict[str, LocalPosition]:
|
||
"""获取策略持仓
|
||
|
||
Returns:
|
||
Dict[str, LocalPosition]:
|
||
key为股票代码(str),value为LocalPosition对象,若无持仓则返回空字典。
|
||
"""
|
||
# 创建临时变量存储锁内读取的数据
|
||
positions_copy = {}
|
||
with self._lock:
|
||
# 快速获取数据并立即释放锁
|
||
for code, pos in self.positions.items():
|
||
positions_copy[code] = pos
|
||
|
||
# 锁外创建副本,避免外部修改影响内部数据
|
||
result = {}
|
||
for code, pos in positions_copy.items():
|
||
result[code] = LocalPosition(pos.code, pos.total_amount, pos.closeable_amount)
|
||
|
||
return result
|
||
|
||
def save_data(self):
|
||
"""保存策略数据"""
|
||
# 在锁内准备要保存的数据
|
||
with self._lock:
|
||
try:
|
||
# 将对象转换为可序列化的字典
|
||
positions_dict = {}
|
||
for code, pos in self.positions.items():
|
||
positions_dict[code] = {
|
||
"code": pos.code,
|
||
"total_amount": pos.total_amount,
|
||
"closeable_amount": pos.closeable_amount,
|
||
}
|
||
|
||
pending_orders_dict = {}
|
||
for order_id, order in self.pending_orders.items():
|
||
pending_orders_dict[order_id] = {
|
||
"order_id": order.order_id,
|
||
"code": order.code,
|
||
"price": order.price,
|
||
"amount": order.amount,
|
||
"filled": order.filled,
|
||
"direction": order.direction,
|
||
"order_type": order.order_type,
|
||
"status": order.status,
|
||
"created_time": (
|
||
order.created_time.isoformat()
|
||
if hasattr(order, "created_time")
|
||
else None
|
||
),
|
||
}
|
||
|
||
all_orders_array = []
|
||
for order in self.all_orders:
|
||
all_orders_array.append({
|
||
"order_id": order.order_id,
|
||
"code": order.code,
|
||
"price": order.price,
|
||
"amount": order.amount,
|
||
"direction": order.direction,
|
||
"order_type": order.order_type,
|
||
"created_time": (
|
||
order.created_time.isoformat()
|
||
if hasattr(order, "created_time")
|
||
else None
|
||
),
|
||
})
|
||
|
||
# 准备好要保存的数据
|
||
data_to_save = {
|
||
"positions": positions_dict,
|
||
"pending_orders": pending_orders_dict,
|
||
"all_orders": all_orders_array,
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"准备保存数据失败: {str(e)}")
|
||
return
|
||
|
||
# 锁外执行文件I/O操作
|
||
try:
|
||
with open(self.data_path, "w") as f:
|
||
json.dump(data_to_save, f)
|
||
logger.debug("成功保存实盘策略数据")
|
||
except Exception as e:
|
||
logger.error(f"保存实盘策略数据失败: {str(e)}")
|
||
|
||
def load_data(self):
|
||
"""加载策略数据"""
|
||
try:
|
||
# 文件I/O操作在锁外执行
|
||
if os.path.exists(self.data_path):
|
||
from datetime import datetime
|
||
with open(self.data_path, "r") as f:
|
||
data = json.load(f)
|
||
|
||
# 在锁内更新内存中的数据结构
|
||
with self._lock:
|
||
# 还原positions对象
|
||
self.positions = {}
|
||
positions_dict = data.get("positions", {})
|
||
for code, pos_data in positions_dict.items():
|
||
self.positions[code] = LocalPosition(
|
||
pos_data["code"],
|
||
int(pos_data["total_amount"]),
|
||
int(pos_data["closeable_amount"]),
|
||
)
|
||
|
||
# 还原pending_orders对象
|
||
self.pending_orders = {}
|
||
pending_orders_dict = data.get("pending_orders", {})
|
||
for order_id, order_data in pending_orders_dict.items():
|
||
order = LocalOrder(
|
||
order_data["order_id"],
|
||
order_data["code"],
|
||
float(order_data["price"]),
|
||
int(order_data["amount"]),
|
||
order_data["direction"],
|
||
order_data["order_type"],
|
||
int(order_data["filled"]),
|
||
order_data["status"],
|
||
)
|
||
if order_data.get("created_time"):
|
||
try:
|
||
order.created_time = datetime.fromisoformat(
|
||
order_data["created_time"]
|
||
)
|
||
except (ValueError, TypeError):
|
||
order.created_time = datetime.now()
|
||
|
||
self.pending_orders[order_id] = order
|
||
|
||
# 还原all_orders对象
|
||
self.all_orders = []
|
||
all_orders_array = data.get("all_orders", [])
|
||
for order_data in all_orders_array:
|
||
order = LocalOrder(
|
||
order_data["order_id"],
|
||
order_data["code"],
|
||
float(order_data["price"]),
|
||
int(order_data["amount"]),
|
||
order_data["direction"],
|
||
order_data["order_type"],
|
||
created_time=datetime.fromisoformat(
|
||
order_data["created_time"]
|
||
) if order_data.get("created_time") else datetime.now()
|
||
)
|
||
self.all_orders.append(order)
|
||
|
||
logger.info("已加载实盘策略数据")
|
||
logger.info(f"本策略: {self.strategy_name} 持仓股票个数: {len(self.positions)} 未完成委托数: {len(self.pending_orders)} 历史订单数: {len(self.all_orders)}")
|
||
else:
|
||
logger.info(f"实盘策略数据文件不存在: {self.data_path}")
|
||
with self._lock:
|
||
self.positions = {}
|
||
self.pending_orders = {}
|
||
self.all_orders = []
|
||
except Exception as e:
|
||
logger.error(f"加载实盘策略数据失败: {str(e)}")
|
||
# 初始化空数据结构
|
||
with self._lock:
|
||
self.positions = {}
|
||
self.pending_orders = {}
|
||
self.all_orders = []
|
||
|
||
def clear(self):
|
||
"""清除所有持仓管理数据"""
|
||
with self._lock:
|
||
self.positions = {}
|
||
self.pending_orders = {}
|
||
self.all_orders = []
|
||
|
||
# 在锁之外异步保存数据
|
||
threading.Thread(target=self.save_data).start()
|
||
|
||
def update_closeable_amount(self):
|
||
"""更新可卖持仓"""
|
||
need_save = False
|
||
with self._lock:
|
||
for _, position in self.positions.items():
|
||
if position.closeable_amount != position.total_amount:
|
||
position.closeable_amount = position.total_amount
|
||
need_save = True
|
||
|
||
# 只有在有更改时才保存
|
||
if need_save:
|
||
threading.Thread(target=self.save_data).start()
|
||
|
||
def clear_pending_orders(self):
|
||
"""清除所有未完成订单"""
|
||
with self._lock:
|
||
if self.pending_orders: # 只有在有挂单时才清除并保存
|
||
self.pending_orders = {}
|
||
need_save = True
|
||
else:
|
||
need_save = False
|
||
|
||
# 只有在有更改时才保存
|
||
if need_save:
|
||
threading.Thread(target=self.save_data).start()
|
||
|
||
def get_all_orders(self):
|
||
"""获取所有订单
|
||
|
||
Returns:
|
||
list: 所有订单列表的副本
|
||
"""
|
||
return self.all_orders
|