[增强] 恢复账号资金属性,增加风控(仓位百分比)

This commit is contained in:
msincenselee 2020-01-14 13:43:52 +08:00
parent 4f071444f5
commit df102d5237
7 changed files with 124 additions and 1319 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,13 @@
"""""" """"""
from copy import copy
from collections import defaultdict from collections import defaultdict
from datetime import datetime
from vnpy.trader.constant import Offset
from vnpy.trader.object import OrderRequest, LogData from vnpy.trader.object import OrderRequest, LogData
from vnpy.event import Event, EventEngine, EVENT_TIMER from vnpy.event import Event, EventEngine, EVENT_TIMER
from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_LOG from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_LOG, EVENT_ACCOUNT
from vnpy.trader.constant import Status from vnpy.trader.constant import Status
from vnpy.trader.utility import load_json, save_json from vnpy.trader.utility import load_json, save_json
@ -23,20 +26,28 @@ class RiskManagerEngine(BaseEngine):
self.active = False self.active = False
self.order_flow_count = 0 self.order_flow_count = 0
self.order_flow_limit = 50 self.order_flow_limit = 500
self.order_flow_clear = 1 self.order_flow_clear = 1
self.order_flow_timer = 0 self.order_flow_timer = 0
self.order_size_limit = 100 self.order_size_limit = 1000
self.trade_count = 0 self.trade_count = 0
self.trade_limit = 1000 self.trade_limit = 10000
self.order_cancel_limit = 500 self.order_cancel_limit = 5000
self.order_cancel_counts = defaultdict(int) self.order_cancel_counts = defaultdict(int)
self.active_order_limit = 50 self.active_order_limit = 500
# 总仓位相关(0~100+)
self.percent_limit = 100 # 仓位比例限制
self.last_over_time = None # 启动风控后,最后一次超过仓位限制的时间
self.account_dict = {} # 资金账号信息
self.gateway_dict = {} # 记录gateway对应的仓位比例
self.currency_list = [] # 资金账号风控管理得币种
self.load_setting() self.load_setting()
self.register_event() self.register_event()
@ -66,7 +77,7 @@ class RiskManagerEngine(BaseEngine):
self.trade_limit = setting["trade_limit"] self.trade_limit = setting["trade_limit"]
self.active_order_limit = setting["active_order_limit"] self.active_order_limit = setting["active_order_limit"]
self.order_cancel_limit = setting["order_cancel_limit"] self.order_cancel_limit = setting["order_cancel_limit"]
self.percent_limit = setting.get('percent_limit', 100)
if self.active: if self.active:
self.write_log("交易风控功能启动") self.write_log("交易风控功能启动")
else: else:
@ -82,6 +93,7 @@ class RiskManagerEngine(BaseEngine):
"trade_limit": self.trade_limit, "trade_limit": self.trade_limit,
"active_order_limit": self.active_order_limit, "active_order_limit": self.active_order_limit,
"order_cancel_limit": self.order_cancel_limit, "order_cancel_limit": self.order_cancel_limit,
"percent_limit": self.percent_limit
} }
return setting return setting
@ -103,6 +115,7 @@ class RiskManagerEngine(BaseEngine):
self.event_engine.register(EVENT_TRADE, self.process_trade_event) self.event_engine.register(EVENT_TRADE, self.process_trade_event)
self.event_engine.register(EVENT_TIMER, self.process_timer_event) self.event_engine.register(EVENT_TIMER, self.process_timer_event)
self.event_engine.register(EVENT_ORDER, self.process_order_event) self.event_engine.register(EVENT_ORDER, self.process_order_event)
self.event_engine.register(EVENT_ACCOUNT, self.process_account_event)
def process_order_event(self, event: Event): def process_order_event(self, event: Event):
"""""" """"""
@ -124,6 +137,47 @@ class RiskManagerEngine(BaseEngine):
self.order_flow_count = 0 self.order_flow_count = 0
self.order_flow_timer = 0 self.order_flow_timer = 0
def process_account_event(self, event):
"""更新账号资金
add by Incense
"""
account = event.data
# 如果有币种过滤,就进行过滤动作
if len(self.currency_list) > 0:
if account.currency not in self.currency_list:
return
# 净值为0得不做处理
if account.balance == 0:
return
# 保存在dict中
k = u'{}_{}'.format(account.vt_accountid, account.currency)
self.account_dict.update({k: copy(account)})
# 计算当前资金仓位
if account.balance == 0:
account_percent = 0
else:
account_percent = round((account.balance - account.available) * 100 / account.balance, 2)
if not self.active:
return
# 更新gateway对应的当前资金仓位
self.gateway_dict.update({account.gateway_name: account_percent})
# 判断资金仓位超出
if account_percent > self.percent_limit:
if self.last_over_time is None or (
datetime.now() - self.last_over_time).total_seconds() > 60 * 10:
self.last_over_time = datetime.now()
msg = u'账号:{} 今净值:{},保证金占用{} 超过设定:{}' \
.format(account.vt_accountid,
account.balance, account_percent, self.percent_limit)
self.write_log(msg)
def write_log(self, msg: str): def write_log(self, msg: str):
"""""" """"""
log = LogData(msg=msg, gateway_name="RiskManager") log = LogData(msg=msg, gateway_name="RiskManager")
@ -170,6 +224,11 @@ class RiskManagerEngine(BaseEngine):
f"当日{req.symbol}撤单次数{self.order_cancel_counts[req.symbol]},超过限制{self.order_cancel_limit}") f"当日{req.symbol}撤单次数{self.order_cancel_counts[req.symbol]},超过限制{self.order_cancel_limit}")
return False return False
# 开仓时,检查是否超过保证金比率
if req.offset == Offset.OPEN and self.gateway_dict.get(gateway_name, 0) > self.percent_limit:
self.write_log(f'当前资金仓位{self.gateway_dict[gateway_name]}超过仓位限制:{self.percent_limit}')
return False
# Add flow count if pass all checks # Add flow count if pass all checks
self.order_flow_count += 1 self.order_flow_count += 1
return True return True

View File

@ -31,6 +31,7 @@ class RiskManager(QtWidgets.QDialog):
self.trade_limit_spin = RiskManagerSpinBox() self.trade_limit_spin = RiskManagerSpinBox()
self.active_limit_spin = RiskManagerSpinBox() self.active_limit_spin = RiskManagerSpinBox()
self.cancel_limit_spin = RiskManagerSpinBox() self.cancel_limit_spin = RiskManagerSpinBox()
self.percent_limit_spin = RiskManagerSpinBox()
save_button = QtWidgets.QPushButton("保存") save_button = QtWidgets.QPushButton("保存")
save_button.clicked.connect(self.save_setting) save_button.clicked.connect(self.save_setting)
@ -44,6 +45,7 @@ class RiskManager(QtWidgets.QDialog):
form.addRow("总成交上限(笔)", self.trade_limit_spin) form.addRow("总成交上限(笔)", self.trade_limit_spin)
form.addRow("活动委托上限(笔)", self.active_limit_spin) form.addRow("活动委托上限(笔)", self.active_limit_spin)
form.addRow("合约撤单上限(笔)", self.cancel_limit_spin) form.addRow("合约撤单上限(笔)", self.cancel_limit_spin)
form.addRow("资金仓位上限(%)", self.percent_limit_spin)
form.addRow(save_button) form.addRow(save_button)
self.setLayout(form) self.setLayout(form)
@ -68,6 +70,7 @@ class RiskManager(QtWidgets.QDialog):
"trade_limit": self.trade_limit_spin.value(), "trade_limit": self.trade_limit_spin.value(),
"active_order_limit": self.active_limit_spin.value(), "active_order_limit": self.active_limit_spin.value(),
"order_cancel_limit": self.cancel_limit_spin.value(), "order_cancel_limit": self.cancel_limit_spin.value(),
"percent_limit": self.percent_limit_spin.value(),
} }
self.rm_engine.update_setting(setting) self.rm_engine.update_setting(setting)
@ -89,6 +92,7 @@ class RiskManager(QtWidgets.QDialog):
self.trade_limit_spin.setValue(setting["trade_limit"]) self.trade_limit_spin.setValue(setting["trade_limit"])
self.active_limit_spin.setValue(setting["active_order_limit"]) self.active_limit_spin.setValue(setting["active_order_limit"])
self.cancel_limit_spin.setValue(setting["order_cancel_limit"]) self.cancel_limit_spin.setValue(setting["order_cancel_limit"])
self.percent_limit_spin.setValue(setting.get('percent_limit', 100))
def exec_(self): def exec_(self):
"""""" """"""

View File

@ -13,7 +13,7 @@
"mi_symbol": "ag2007", "mi_symbol": "ag2007",
"full_symbol": "AG2007", "full_symbol": "AG2007",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.07, "margin_rate": 0.08,
"symbol_size": 15, "symbol_size": 15,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -22,7 +22,7 @@
"mi_symbol": "al2003", "mi_symbol": "al2003",
"full_symbol": "AL2003", "full_symbol": "AL2003",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.07, "margin_rate": 0.1,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 5.0 "price_tick": 5.0
}, },
@ -31,7 +31,7 @@
"mi_symbol": "AP005", "mi_symbol": "AP005",
"full_symbol": "AP2005", "full_symbol": "AP2005",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.08, "margin_rate": 0.07,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -40,7 +40,7 @@
"mi_symbol": "au2006", "mi_symbol": "au2006",
"full_symbol": "AU2006", "full_symbol": "AU2006",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.06, "margin_rate": 0.08,
"symbol_size": 1000, "symbol_size": 1000,
"price_tick": 0.02 "price_tick": 0.02
}, },
@ -67,7 +67,7 @@
"mi_symbol": "bu2006", "mi_symbol": "bu2006",
"full_symbol": "BU2006", "full_symbol": "BU2006",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.09, "margin_rate": 0.1,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 2.0 "price_tick": 2.0
}, },
@ -112,7 +112,7 @@
"mi_symbol": "cu2003", "mi_symbol": "cu2003",
"full_symbol": "CU2003", "full_symbol": "CU2003",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.07, "margin_rate": 0.09,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 10.0 "price_tick": 10.0
}, },
@ -139,7 +139,7 @@
"mi_symbol": "eg2005", "mi_symbol": "eg2005",
"full_symbol": "EG2005", "full_symbol": "EG2005",
"exchange": "DCE", "exchange": "DCE",
"margin_rate": 0.06, "margin_rate": 0.05,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -148,9 +148,9 @@
"mi_symbol": "fb2005", "mi_symbol": "fb2005",
"full_symbol": "FB2005", "full_symbol": "FB2005",
"exchange": "DCE", "exchange": "DCE",
"margin_rate": 0.1, "margin_rate": 0.2,
"symbol_size": 10, "symbol_size": 500,
"price_tick": 0.5 "price_tick": 0.05
}, },
"FG": { "FG": {
"underlying_symbol": "FG", "underlying_symbol": "FG",
@ -166,7 +166,7 @@
"mi_symbol": "fu2005", "mi_symbol": "fu2005",
"full_symbol": "FU2005", "full_symbol": "FU2005",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.1, "margin_rate": 0.2,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -175,7 +175,7 @@
"mi_symbol": "hc2005", "mi_symbol": "hc2005",
"full_symbol": "HC2005", "full_symbol": "HC2005",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.08, "margin_rate": 0.1,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -184,7 +184,7 @@
"mi_symbol": "i2005", "mi_symbol": "i2005",
"full_symbol": "I2005", "full_symbol": "I2005",
"exchange": "DCE", "exchange": "DCE",
"margin_rate": 0.08, "margin_rate": 0.05,
"symbol_size": 100, "symbol_size": 100,
"price_tick": 0.5 "price_tick": 0.5
}, },
@ -193,7 +193,7 @@
"mi_symbol": "IC2003", "mi_symbol": "IC2003",
"full_symbol": "IC2003", "full_symbol": "IC2003",
"exchange": "CFFEX", "exchange": "CFFEX",
"margin_rate": 0.12, "margin_rate": 0.1,
"symbol_size": 200, "symbol_size": 200,
"price_tick": 0.2 "price_tick": 0.2
}, },
@ -220,7 +220,7 @@
"mi_symbol": "j2005", "mi_symbol": "j2005",
"full_symbol": "J2005", "full_symbol": "J2005",
"exchange": "DCE", "exchange": "DCE",
"margin_rate": 0.08, "margin_rate": 0.05,
"symbol_size": 100, "symbol_size": 100,
"price_tick": 0.5 "price_tick": 0.5
}, },
@ -229,7 +229,7 @@
"mi_symbol": "jd2005", "mi_symbol": "jd2005",
"full_symbol": "JD2005", "full_symbol": "JD2005",
"exchange": "DCE", "exchange": "DCE",
"margin_rate": 0.07, "margin_rate": 0.08,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -238,7 +238,7 @@
"mi_symbol": "jm2005", "mi_symbol": "jm2005",
"full_symbol": "JM2005", "full_symbol": "JM2005",
"exchange": "DCE", "exchange": "DCE",
"margin_rate": 0.08, "margin_rate": 0.05,
"symbol_size": 60, "symbol_size": 60,
"price_tick": 0.5 "price_tick": 0.5
}, },
@ -328,7 +328,7 @@
"mi_symbol": "pb2003", "mi_symbol": "pb2003",
"full_symbol": "PB2003", "full_symbol": "PB2003",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.07, "margin_rate": 0.1,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 5.0 "price_tick": 5.0
}, },
@ -355,7 +355,7 @@
"mi_symbol": "rb2005", "mi_symbol": "rb2005",
"full_symbol": "RB2005", "full_symbol": "RB2005",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.08, "margin_rate": 0.1,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -373,7 +373,7 @@
"mi_symbol": "RM005", "mi_symbol": "RM005",
"full_symbol": "RM2005", "full_symbol": "RM2005",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.06, "margin_rate": 0.05,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -391,7 +391,7 @@
"mi_symbol": "RS011", "mi_symbol": "RS011",
"full_symbol": "RS2011", "full_symbol": "RS2011",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.2, "margin_rate": 0.05,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -400,7 +400,7 @@
"mi_symbol": "ru2005", "mi_symbol": "ru2005",
"full_symbol": "RU2005", "full_symbol": "RU2005",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.09, "margin_rate": 0.1,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 5.0 "price_tick": 5.0
}, },
@ -418,7 +418,7 @@
"mi_symbol": "sc2003", "mi_symbol": "sc2003",
"full_symbol": "SC2003", "full_symbol": "SC2003",
"exchange": "INE", "exchange": "INE",
"margin_rate": 0.07, "margin_rate": 0.05,
"symbol_size": 1000, "symbol_size": 1000,
"price_tick": 0.1 "price_tick": 0.1
}, },
@ -427,7 +427,7 @@
"mi_symbol": "SF005", "mi_symbol": "SF005",
"full_symbol": "SF2005", "full_symbol": "SF2005",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.07, "margin_rate": 0.05,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 2.0 "price_tick": 2.0
}, },
@ -436,7 +436,7 @@
"mi_symbol": "SM005", "mi_symbol": "SM005",
"full_symbol": "SM2005", "full_symbol": "SM2005",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.07, "margin_rate": 0.05,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 2.0 "price_tick": 2.0
}, },
@ -445,7 +445,7 @@
"mi_symbol": "sn2006", "mi_symbol": "sn2006",
"full_symbol": "SN2006", "full_symbol": "SN2006",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.08, "margin_rate": 0.09,
"symbol_size": 1, "symbol_size": 1,
"price_tick": 10.0 "price_tick": 10.0
}, },
@ -490,7 +490,7 @@
"mi_symbol": "TA005", "mi_symbol": "TA005",
"full_symbol": "TA2005", "full_symbol": "TA2005",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.06, "margin_rate": 0.05,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 2.0 "price_tick": 2.0
}, },
@ -535,7 +535,7 @@
"mi_symbol": "WH011", "mi_symbol": "WH011",
"full_symbol": "WH2011", "full_symbol": "WH2011",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.07, "margin_rate": 0.05,
"symbol_size": 20, "symbol_size": 20,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -544,7 +544,7 @@
"mi_symbol": "wr2012", "mi_symbol": "wr2012",
"full_symbol": "WR2012", "full_symbol": "WR2012",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.08, "margin_rate": 0.2,
"symbol_size": 10, "symbol_size": 10,
"price_tick": 1.0 "price_tick": 1.0
}, },
@ -562,7 +562,7 @@
"mi_symbol": "ZC005", "mi_symbol": "ZC005",
"full_symbol": "ZC2005", "full_symbol": "ZC2005",
"exchange": "CZCE", "exchange": "CZCE",
"margin_rate": 0.06, "margin_rate": 0.05,
"symbol_size": 100, "symbol_size": 100,
"price_tick": 0.2 "price_tick": 0.2
}, },
@ -571,7 +571,7 @@
"mi_symbol": "zn2003", "mi_symbol": "zn2003",
"full_symbol": "ZN2003", "full_symbol": "ZN2003",
"exchange": "SHFE", "exchange": "SHFE",
"margin_rate": 0.07, "margin_rate": 0.1,
"symbol_size": 5, "symbol_size": 5,
"price_tick": 5.0 "price_tick": 5.0
} }

View File

@ -772,11 +772,19 @@ class CtpTdApi(TdApi):
account = AccountData( account = AccountData(
accountid=data["AccountID"], accountid=data["AccountID"],
pre_balance=data['PreBalance'],
balance=data["Balance"], balance=data["Balance"],
frozen=data["FrozenMargin"] + data["FrozenCash"] + data["FrozenCommission"], frozen=data["FrozenMargin"] + data["FrozenCash"] + data["FrozenCommission"],
gateway_name=self.gateway_name gateway_name=self.gateway_name
) )
account.available = data["Available"] account.available = data["Available"]
account.commission = data['Commission']
account.margin = data['CurrMargin']
account.close_profit = data['CloseProfit']
account.holding_profit = data['PositionProfit']
account.trading_day = str(data['TradingDay'])
if '-' not in account.trading_day and len(account.trading_day)== 8:
account.trading_day = account.trading_day[0:4] + '-' + account.trading_day[4:6] + '-' + account.trading_day[6:8]
self.gateway.on_account(account) self.gateway.on_account(account)

View File

@ -220,9 +220,15 @@ class AccountData(BaseData):
""" """
accountid: str accountid: str
pre_balance: float = 0 # 昨净值
balance: float = 0 balance: float = 0 # 当前净值
frozen: float = 0 frozen: float = 0 # 冻结资金
currency: str = "" # 币种
commission: float = 0 # 手续费
margin: float = 0 # 使用保证金
close_profit: float = 0 # 平仓盈亏
holding_profit: float = 0 # 持仓盈亏
trading_day: str = "" # 当前交易日
def __post_init__(self): def __post_init__(self):
"""""" """"""

View File

@ -480,9 +480,14 @@ class AccountMonitor(BaseMonitor):
headers = { headers = {
"accountid": {"display": "账号", "cell": BaseCell, "update": False}, "accountid": {"display": "账号", "cell": BaseCell, "update": False},
"balance": {"display": "余额", "cell": BaseCell, "update": True}, "pre_balance": {"display": "昨净值", "cell": BaseCell, "update": False},
"balance": {"display": "净值", "cell": BaseCell, "update": True},
"frozen": {"display": "冻结", "cell": BaseCell, "update": True}, "frozen": {"display": "冻结", "cell": BaseCell, "update": True},
"margin": {"display": "保证金", "cell": BaseCell, "update": True},
"available": {"display": "可用", "cell": BaseCell, "update": True}, "available": {"display": "可用", "cell": BaseCell, "update": True},
"commission": {"display": "手续费", "cell": BaseCell, "update": True},
"close_profit": {"display": "平仓收益", "cell": BaseCell, "update": True},
"holding_profit": {"display": "持仓收益", "cell": BaseCell, "update": True},
"gateway_name": {"display": "接口", "cell": BaseCell, "update": False}, "gateway_name": {"display": "接口", "cell": BaseCell, "update": False},
} }