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

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 datetime import datetime
from vnpy.trader.constant import Offset
from vnpy.trader.object import OrderRequest, LogData
from vnpy.event import Event, EventEngine, EVENT_TIMER
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.utility import load_json, save_json
@ -23,20 +26,28 @@ class RiskManagerEngine(BaseEngine):
self.active = False
self.order_flow_count = 0
self.order_flow_limit = 50
self.order_flow_limit = 500
self.order_flow_clear = 1
self.order_flow_timer = 0
self.order_size_limit = 100
self.order_size_limit = 1000
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.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.register_event()
@ -66,7 +77,7 @@ class RiskManagerEngine(BaseEngine):
self.trade_limit = setting["trade_limit"]
self.active_order_limit = setting["active_order_limit"]
self.order_cancel_limit = setting["order_cancel_limit"]
self.percent_limit = setting.get('percent_limit', 100)
if self.active:
self.write_log("交易风控功能启动")
else:
@ -82,6 +93,7 @@ class RiskManagerEngine(BaseEngine):
"trade_limit": self.trade_limit,
"active_order_limit": self.active_order_limit,
"order_cancel_limit": self.order_cancel_limit,
"percent_limit": self.percent_limit
}
return setting
@ -103,6 +115,7 @@ class RiskManagerEngine(BaseEngine):
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_ORDER, self.process_order_event)
self.event_engine.register(EVENT_ACCOUNT, self.process_account_event)
def process_order_event(self, event: Event):
""""""
@ -124,6 +137,47 @@ class RiskManagerEngine(BaseEngine):
self.order_flow_count = 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):
""""""
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}")
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
self.order_flow_count += 1
return True

View File

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

View File

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

View File

@ -772,11 +772,19 @@ class CtpTdApi(TdApi):
account = AccountData(
accountid=data["AccountID"],
pre_balance=data['PreBalance'],
balance=data["Balance"],
frozen=data["FrozenMargin"] + data["FrozenCash"] + data["FrozenCommission"],
gateway_name=self.gateway_name
)
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)

View File

@ -220,9 +220,15 @@ class AccountData(BaseData):
"""
accountid: str
balance: float = 0
frozen: float = 0
pre_balance: float = 0 # 昨净值
balance: 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):
""""""

View File

@ -480,9 +480,14 @@ class AccountMonitor(BaseMonitor):
headers = {
"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},
"margin": {"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},
}