"""
|
虚拟账户管理
|
"""
|
from log_module import async_log_util, log_export
|
from log_module.log import logger_virtual_account_money_records
|
from utils import tool, huaxin_util
|
|
EVENT_CHARGE = "charge" # 充值
|
|
EVENT_BUY_DEAL = "buy_deal" # 买成交
|
|
EVENT_SELL_DEAL = "sell_deal" # 卖成交
|
|
EVENT_BALANCE_ACCOUNT = "balance_account" # 平账
|
|
EVENT_FEE = "fee" # 手续费
|
|
|
@tool.singleton
|
class VirtualAccountMoneyManager:
|
"""
|
虚拟账户资金管理
|
"""
|
|
def __init__(self):
|
self.all_money = 0
|
self.available_money = 0
|
# 冻结金额记录
|
self.frozen_money_record_dict = {}
|
self.__load_data()
|
|
def __load_data(self):
|
"""
|
加载数据,从今天及以前的日志中读取数据
|
:return:
|
"""
|
all_money = None
|
available_money = None
|
now_day = tool.get_now_date_str()
|
# 加载今日之前的数据
|
for i in range(1, 60):
|
day = tool.date_sub(now_day, i)
|
results = log_export.load_virtual_trade_account(day)
|
if not results:
|
continue
|
|
for r in results:
|
if r[1]["type"] == "资金变化":
|
all_money = r[1]["data"]["all_money"]
|
available_money = r[1]["data"]["available_money"]
|
if all_money:
|
if day != now_day:
|
# 非今日
|
available_money = all_money
|
break
|
if all_money is not None:
|
self.all_money = all_money
|
self.available_money = available_money
|
|
# 读取今日日志
|
results = log_export.load_virtual_trade_account(now_day)
|
if results:
|
for r in results:
|
_type = r[1]["type"]
|
_data = r[1]["data"]
|
if _type == '资金变化':
|
self.add_money(_data["amount"], _data["event"], _data["unique_id"], enable_log=False)
|
elif _type == '冻结金额':
|
self.frozen_money(_data["amount"], _data["unique_id"], enable_log=False)
|
elif _type == '解冻金额':
|
self.un_frozen_money(_data["amount"], _data["unique_id"], enable_log=False)
|
elif _type == '消耗冻结金额':
|
self.consume_frozen_money(_data["money"], _data["unique_id"], enable_log=False)
|
|
def __format_log(self, type, data):
|
fdata = {"type": type, "data": data}
|
return f"{fdata}"
|
|
def add_money(self, money, event, unique_id, enable_log=True):
|
"""
|
添加资金
|
:param enable_log:
|
:param unique_id: 唯一ID
|
:param money: 金额
|
:param event: 事件
|
:return:
|
"""
|
self.all_money += money
|
if event != EVENT_BUY_DEAL:
|
# 买入成交不改变可用资金
|
self.available_money += money
|
if enable_log:
|
async_log_util.info(logger_virtual_account_money_records, self.__format_log("资金变化",
|
{"event": event,
|
'amount': money,
|
'all_money': self.all_money,
|
'available_money': self.available_money,
|
"unique_id": unique_id}))
|
|
def frozen_money(self, money, unique_id, enable_log=True):
|
"""
|
冻结金额:挂买要冻结
|
:param enable_log:
|
:param money: 金额
|
:param unique_id: 唯一索引
|
:return:
|
"""
|
if unique_id in self.frozen_money_record_dict:
|
# 已经冻结
|
return
|
self.available_money -= money
|
self.frozen_money_record_dict[unique_id] = money
|
if enable_log:
|
async_log_util.info(logger_virtual_account_money_records, self.__format_log("冻结金额",
|
{"unique_id": unique_id,
|
'amount': money,
|
'available_money': self.available_money}))
|
|
def un_frozen_money(self, money, unique_id, enable_log=True):
|
"""
|
解冻金额,买入撤单情况下可解冻
|
:param enable_log:
|
:param money: 金额
|
:param unique_id:
|
:return:
|
"""
|
self.available_money += money
|
if unique_id in self.frozen_money_record_dict:
|
self.frozen_money_record_dict.pop(unique_id)
|
if enable_log:
|
async_log_util.info(logger_virtual_account_money_records, self.__format_log("解冻金额",
|
{"unique_id": unique_id,
|
'amount': money,
|
'available_money': self.available_money}))
|
|
def consume_frozen_money(self, money, unique_id, enable_log=True):
|
"""
|
消耗冻结金额:买入成交会消耗
|
:param money: 消耗金额
|
:param enable_log:
|
:param unique_id:
|
:return:
|
"""
|
if unique_id in self.frozen_money_record_dict:
|
self.frozen_money_record_dict[unique_id] -= money
|
if self.frozen_money_record_dict[unique_id] == 0:
|
self.frozen_money_record_dict.pop(unique_id)
|
if enable_log:
|
async_log_util.info(logger_virtual_account_money_records, self.__format_log("消耗冻结金额",
|
{"unique_id": unique_id,
|
"money": money,
|
'available_money': self.available_money}))
|
|
def get_available_money(self):
|
"""
|
获取可用金额
|
:return:
|
"""
|
return self.available_money
|
|
def get_total_money(self):
|
"""
|
获取总共金额
|
:return:
|
"""
|
return self.all_money
|
|
|
class VirtualAccountOrderProcessUtil:
|
"""
|
虚拟账户订单处理帮助类
|
"""
|
|
@classmethod
|
def set_order_status(cls, order_info):
|
"""
|
设置订单状态
|
:param order_info:
|
:return:
|
"""
|
status = order_info["orderStatus"]
|
direction = int(order_info["direction"])
|
orderLocalID = order_info["orderLocalID"]
|
volume = order_info["volume"]
|
volumeTraded = order_info["volumeTraded"]
|
limitPrice = order_info["limitPrice"]
|
|
# 挂单/撤单 需要记录修改资金
|
if status == huaxin_util.TORA_TSTP_OST_Cached:
|
# 预埋
|
if direction == huaxin_util.TORA_TSTP_D_Buy:
|
VirtualAccountMoneyManager().frozen_money(round(round(float(limitPrice), 2) * volume, 2), orderLocalID)
|
elif status == huaxin_util.TORA_TSTP_OST_Unknown:
|
# 未知
|
if direction == huaxin_util.TORA_TSTP_D_Buy:
|
VirtualAccountMoneyManager().frozen_money(round(round(float(limitPrice), 2) * volume, 2), orderLocalID)
|
elif status == huaxin_util.TORA_TSTP_OST_Accepted:
|
# 交易所已接收
|
if direction == huaxin_util.TORA_TSTP_D_Buy:
|
VirtualAccountMoneyManager().frozen_money(round(round(float(limitPrice), 2) * volume, 2), orderLocalID)
|
elif status == huaxin_util.TORA_TSTP_OST_PartTradeCanceled:
|
# 部成部撤
|
VirtualAccountMoneyManager().un_frozen_money(
|
round(round(float(limitPrice), 2) * (volume - volumeTraded), 2), orderLocalID)
|
elif status == huaxin_util.TORA_TSTP_OST_AllCanceled:
|
# 全部撤单
|
VirtualAccountMoneyManager().un_frozen_money(round(round(float(limitPrice), 2) * volume, 2), orderLocalID)
|
|
elif status == huaxin_util.TORA_TSTP_OST_Rejected:
|
# 未知
|
pass
|
|
@classmethod
|
def set_order_deal(cls, deal_info):
|
"""
|
订单成交
|
:param deal_info:
|
:return:
|
"""
|
direction = deal_info["direction"]
|
tradeID = deal_info["tradeID"]
|
orderLocalID = deal_info["orderLocalID"]
|
volume = deal_info["volume"]
|
price = round(float(deal_info["price"]), 2)
|
money = round(volume * price, 2)
|
if int(direction) == huaxin_util.TORA_TSTP_D_Buy:
|
# 买入成交
|
VirtualAccountMoneyManager().consume_frozen_money(money, orderLocalID)
|
VirtualAccountMoneyManager().add_money(0 - money, event=EVENT_BUY_DEAL, unique_id=tradeID)
|
else:
|
# 卖出成交
|
VirtualAccountMoneyManager().add_money(money, event=EVENT_SELL_DEAL, unique_id=tradeID)
|
|
@classmethod
|
def set_order_detail_info(cls, order_info):
|
orderSysID, totalFee = order_info['orderSysID'], order_info['totalFee']
|
VirtualAccountMoneyManager().add_money(0 - totalFee, EVENT_FEE, orderSysID)
|
|
|
@tool.singleton
|
class VirtualAccountPositionManager:
|
"""
|
虚拟账户持仓查询
|
"""
|
|
def __init__(self):
|
# {代码:(总量, 历史量, 可用量, 成本金额)}
|
self.position_dict = {}
|
self.today_delegate_local_order_ids = set()
|
self.trade_id_set = set()
|
self.__load_data()
|
|
def __load_data(self):
|
# 查询近1个月的成交
|
now_day = tool.get_now_date_str()
|
days = []
|
for i in range(0, 30):
|
days.append(tool.date_sub(now_day, i))
|
days.sort()
|
# 统计持仓代码与数量
|
position_dict = {}
|
for day in days:
|
datas = log_export.load_deal_list(day)
|
if not datas:
|
continue
|
for d in datas:
|
code = d['securityID']
|
volume = d['volume']
|
tradeId = d['tradeID']
|
price = d['price']
|
if tradeId in self.trade_id_set:
|
continue
|
self.trade_id_set.add(tradeId)
|
if code not in position_dict:
|
position_dict[code] = [0, 0, 0, 0]
|
if d['direction'] == '0':
|
position_dict[code][0] += volume
|
if day != now_day:
|
position_dict[code][1] += volume
|
position_dict[code][2] += volume
|
position_dict[code][3] += round(volume * price, 2)
|
elif d['direction'] == '1':
|
position_dict[code][0] -= volume
|
if day != now_day:
|
position_dict[code][1] -= volume
|
position_dict[code][2] -= volume
|
position_dict[code][3] -= round(volume * price, 2)
|
self.position_dict = {c: v for c, v in position_dict.items() if v[0] > 0}
|
|
def add_deal_info(self, d):
|
"""
|
设置成交信息
|
:param d:
|
:return:
|
"""
|
code = d['securityID']
|
volume = d['volume']
|
tradeId = d['tradeID']
|
price = d['price']
|
if tradeId in self.trade_id_set:
|
return
|
self.trade_id_set.add(tradeId)
|
if code not in self.position_dict:
|
self.position_dict[code] = [0, 0, 0, 0]
|
# 当日可用量不在成交里面改变
|
if d['direction'] == '0':
|
self.position_dict[code][0] += volume
|
self.position_dict[code][3] += round(volume * price, 2)
|
elif d['direction'] == '1':
|
self.position_dict[code][0] -= volume
|
self.position_dict[code][3] -= round(volume * price, 2)
|
|
def set_order_status(self, order_info):
|
"""
|
设置订单状态,用于处理可用持仓
|
:param order_info:
|
:return:
|
"""
|
status = order_info["orderStatus"]
|
direction = int(order_info["direction"])
|
orderLocalID = order_info["orderLocalID"]
|
volume = order_info["volume"]
|
volumeTraded = order_info["volumeTraded"]
|
limitPrice = order_info["limitPrice"]
|
code = order_info['securityID']
|
if direction == huaxin_util.TORA_TSTP_D_Buy:
|
return
|
if code not in self.position_dict:
|
return
|
# 挂单/撤单 需要记录修改资金
|
if status == huaxin_util.TORA_TSTP_OST_Cached or status == huaxin_util.TORA_TSTP_OST_Unknown or status == huaxin_util.TORA_TSTP_OST_Accepted:
|
# 预埋/未知/挂单
|
if orderLocalID in self.today_delegate_local_order_ids:
|
return
|
self.today_delegate_local_order_ids.add(orderLocalID)
|
self.position_dict[code][2] -= volume
|
elif status == huaxin_util.TORA_TSTP_OST_PartTradeCanceled:
|
# 部成部撤
|
self.position_dict[code][2] += (volume - volumeTraded)
|
|
elif status == huaxin_util.TORA_TSTP_OST_AllCanceled:
|
# 全部撤单
|
self.position_dict[code][2] += volume
|
|
def get_position_dict(self):
|
"""
|
获取持仓
|
:return:
|
"""
|
return self.position_dict
|
|
def get_position_of_code(self, code):
|
"""
|
获取持仓
|
:return:(总持仓, 昨日持仓, 可用量, 持仓金额)
|
"""
|
return self.position_dict.get(code)
|
|
def get_position_codes(self):
|
"""
|
获取持仓的代码
|
:return: 代码集合
|
"""
|
if self.position_dict:
|
return set(self.position_dict.keys())
|
return set()
|
|
|
if __name__ == "__main__":
|
print(VirtualAccountPositionManager().get_position_dict())
|
print(VirtualAccountMoneyManager().get_total_money(),
|
VirtualAccountMoneyManager().get_available_money()) #.add_money(20000, EVENT_CHARGE, '')
|
async_log_util.run_sync()
|