From d76e842a735ef378cbb93f113aa5565f9b9ca6a3 Mon Sep 17 00:00:00 2001 From: 1122455801 Date: Thu, 30 May 2019 15:09:45 +0800 Subject: [PATCH 1/5] Create __init__.py --- vnpy/app/risk_manager/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 vnpy/app/risk_manager/__init__.py diff --git a/vnpy/app/risk_manager/__init__.py b/vnpy/app/risk_manager/__init__.py new file mode 100644 index 00000000..3c51a1e7 --- /dev/null +++ b/vnpy/app/risk_manager/__init__.py @@ -0,0 +1,14 @@ +from pathlib import Path +from vnpy.trader.app import BaseApp +from .engine import RiskManagerEngine, APP_NAME + + +class RiskManagerApp(BaseApp): + """""" + app_name = APP_NAME + app_module = __module__ + app_path = Path(__file__).parent + display_name = "风险控制" + engine_class = RiskManagerEngine + widget_name = "RiskManager" + icon_name = "risk_manager.ico" From a5dff8e948a4e744dd50be12b36ffcaf952d28f7 Mon Sep 17 00:00:00 2001 From: 1122455801 Date: Thu, 30 May 2019 15:11:17 +0800 Subject: [PATCH 2/5] Update engine.py --- vnpy/app/risk_manager/engine.py | 55 ++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/vnpy/app/risk_manager/engine.py b/vnpy/app/risk_manager/engine.py index 981baf12..84fa1c9c 100644 --- a/vnpy/app/risk_manager/engine.py +++ b/vnpy/app/risk_manager/engine.py @@ -1,6 +1,7 @@ """""" + from collections import defaultdict -from vnpy.trader.object import OrderRequest +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 @@ -12,15 +13,16 @@ APP_NAME = "RiskManager" class RiskManagerEngine(BaseEngine): - """风控引擎""" + """""" setting_filename = "risk_manager_setting.json" def __init__(self, main_engine: MainEngine, event_engine: EventEngine): """""" + super().__init__(main_engine, event_engine, APP_NAME) + self.main_engine = main_engine self.event_engine = event_engine - main_engine.rmEngine = self - + self.active = False self.order_flow_count = 0 self.order_flow_limit = 50 @@ -31,22 +33,34 @@ class RiskManagerEngine(BaseEngine): self.trade_limit = 1000 self.order_cancel_limit = 10 self.order_cancel_counts = defaultdict(int) - self.active_order_limit = 20 + self.active_order_limit = 20 + # Patch send order function of MainEngine + self._send_order = self.main_engine.send_order + self.main_engine.send_order = self.send_order + self.load_setting() - self.registerEvent() + self.register_event() + + def send_order(self, req: OrderRequest, gateway_name: str): + """""" + result = self.check_risk(req, gateway_name) + if not result: + return "" + + return self._send_order(req, gateway_name) def load_setting(self): """""" setting = load_json(self.setting_filename) - self.active = setting["active"] - self.order_flow_limit = setting["order_flow_limit"] - self.order_flow_clear = setting["order_flow_clear"] - self.order_size_limit = setting["order_size_limit"] - self.trade_limit = setting["trade_limit"] - self.active_order_limit = setting["active_order_limit"] - self.order_cancel_limit = setting["order_cancel_limit"] + self.active = setting.get("active", self.active) + self.order_flow_limit = setting.get("order_flow_limit", self.order_flow_count) + self.order_flow_clear = setting.get("order_flow_clear", self.order_flow_clear) + self.order_size_limit = setting.get("order_size_limit", self.order_size_limit) + self.trade_limit = setting.get("trade_limit", self.trade_limit) + self.active_order_limit = setting.get("active_order_limit", self.active_order_limit) + self.order_cancel_limit = setting.get("order_cancel_limit", self.order_cancel_limit) def save_setting(self): """""" @@ -89,11 +103,9 @@ class RiskManagerEngine(BaseEngine): self.order_flow_timer = 0 def write_risk_log(self, msg: str): - """""" - event = Event( - EVENT_LOG, - msg - ) + """""" + log = LogData(msg=msg, gateway_name="RiskManager") + event = Event(type=EVENT_LOG, data=log) self.event_engine.put(event) def check_risk(self, req: OrderRequest, gateway_name: str): @@ -117,17 +129,17 @@ class RiskManagerEngine(BaseEngine): # Check flow count if self.order_flow_count >= self.order_flow_limit: - self.write_risk_log(f"委托流数量{self.order_flow_count},超过限制每{self.order_flow_clear}秒{self.order_flow_limit}") + self.write_risk_log(f"委托流数量{self.order_flow_count},超过限制每{self.order_flow_clear}秒{self.order_flow_limit}次") return False # Check all active orders active_order_count = len(self.main_engine.get_all_active_orders()) if active_order_count >= self.active_order_limit: - self.write_risk_log(f"当前活动委托数量{active_order_count},超过限制{self.active_order_limit}") + self.write_risk_log(f"当前活动委托次数{active_order_count},超过限制{self.active_order_limit}") return False # Check order cancel counts - if req.symbol in self.order_cancel_counts and self.order_cancel_counts[req.symbol] >= self.order_cancel_limit: + if req.symbol in self.order_cancel_counts and self.order_cancel_counts[req.symbol] >= self.order_cancel_limit: self.write_risk_log(f"当日{req.symbol}撤单次数{self.order_cancel_counts[req.symbol]},超过限制{self.order_cancel_limit}") return False @@ -181,4 +193,3 @@ class RiskManagerEngine(BaseEngine): def stop(self): """""" self.save_setting() - From b219f44c754aafb959217ecf4199c8dc23a41ab1 Mon Sep 17 00:00:00 2001 From: 1122455801 Date: Thu, 30 May 2019 15:11:22 +0800 Subject: [PATCH 3/5] Create __init__.py --- vnpy/app/risk_manager/ui/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 vnpy/app/risk_manager/ui/__init__.py diff --git a/vnpy/app/risk_manager/ui/__init__.py b/vnpy/app/risk_manager/ui/__init__.py new file mode 100644 index 00000000..358dbf33 --- /dev/null +++ b/vnpy/app/risk_manager/ui/__init__.py @@ -0,0 +1 @@ +from .widget import RiskManager From 3730bb95ad0f9b0acaa7960fd6b6ab28e2b64afa Mon Sep 17 00:00:00 2001 From: 1122455801 Date: Thu, 30 May 2019 15:11:26 +0800 Subject: [PATCH 4/5] Create risk_manager.ico --- vnpy/app/risk_manager/ui/risk_manager.ico | Bin 0 -> 67646 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 vnpy/app/risk_manager/ui/risk_manager.ico diff --git a/vnpy/app/risk_manager/ui/risk_manager.ico b/vnpy/app/risk_manager/ui/risk_manager.ico new file mode 100644 index 0000000000000000000000000000000000000000..4b376934c3f7d350ca3577567c6e2a39ef2812ed GIT binary patch literal 67646 zcmeI5eT-d28OCodhGL-#LJ^xCmk*Yw}RDR6W9-;06|fj58#U&xu3N>rESkn$|B1pE~!p10Da z)5k}!X%PGbydN9^f*-W$tr^ayP(BBIA3O&-t?5MD+m5Xdfh)mLAoxKGKge=K`5oXk zun|mDhi-rGb!=Y=E&~T$BebBT5{KcXVQf%79xMeujrUf5rfbIg!G%CEE$wq9L>{A) zHV@v3Hxeh0h_RN4pZ{*%`n%k4ntO<%Qb$8Py}AE;a`pwFq_C;1PM zJ`UR9uMD5n+)_W#{cOs~7@@KhZ`=JOulpn2&%FxDpyx#TpnJyO0$uBt+6V0Zk)H|f z2AY$d2ybQkj^?T7gHrl{-7oT|gGWHwG@NXIUWAitL8-0@=uhg?UMS_w*OR~92lD5GHK458<<&3EZGR1D{W|smAN4o3C+Qs0r$AmDmAxL#8)^RM*L}dU zRr4CZ0#(tfsDA3cKyzfy`+#Mm&Re&DqByBqy_z3L=ZIX40hadi)5YL5P&J*3?C%5c z`4$kz0oQ!M*hQ*6vl~E>Tui#|z9+Xu3oI;j++O(9TyZ~0=Z9SJ0c?tWl2rcb{M_CZ zPY*>)*6oOv{;Q6U{(U&|zz4Kuw7~G~g8s&qB)x(30GRanEUJz^Z2RQe;b_yI1JT}* zC?bi|JO8&Ujt51xn`6CNJ5KixblV58#f*K${TFiVXCXc${Xd2NTlXQp_4@Ji-smr0 zw1=eY=K*Xm+G{QMK2V5edDdGJ{k4xsd&y${yA=aW`=lQPF7C-b$RCaVuMqeZ0(1^g zdY510U;Wa4!fBu@K497E#xsmA_afUzf}o#jI7gjax$ z#sJeEsn#aK9uaw3ubIDe4?x>L)!JtOH@}yTO6hhOPsJo&!w%q}PC= zpR*Bst3BV+GbrNupn}K0&NKG_ul4b8{kIk0o6zKJP)H0g^^%?lypH`^|NSa!znTO5 zI#BvC|K|Ui`GcNERJ8t!Xk#~M*1q%anf2e7ng7??@9qO5q$80>>%Zf1d@s68l@H|o zQk@BIA|1~Lr;nNSpKX4x|Bl7+vuLAdqU9~W(O%aVzb}LSTea|Ky*yk0A4`8dXQWs9 z3wgehI(tZ;17o%C{QGA7msEf8|G7k~+~ha)kKZ`LpzwCx z&-g^pf5Q6D5BfVF`*m$rRQ$h)x_6jy=AfwYe+Js?SyVxP zw>cQFt!Aoc~8?-O<7~fD`3BExZ^mm(szoF4_(kri5qP=|JD&Y0seO2K6 z{~#L0za3D3{^wHnX5eiMsDS?8L!UTa=cT{uN$Nb7^y#3#v;MExe08E#p7I<0^|znD z4Ej5(|0`(ot^)O!A6y^wcUJ$^Xfs3l=lxQh3C<_o3cS1qDEayS2S%s7^;dmK-SRK_ zSo*~eHrg1iibzcxq_e;V;FS-QRR6CSU5e1$ zuG6$bs`bDhf}nqQIneVsG#2ByY-?3i-n2z}0eBU7;{zoe|F>Ft6eW2>y;ggNk^gzn zzia-#5$+3_?`;sgNx|}g3(0#E14=gjZ-aZChdUPgZC6Oz)tb;9z>9N0iS*ZbKi=mf z9lG&Sy9v%D-2j~TffDJz)bQU8?M<7;22$OFi~y&7pd|Y1e*bvTmAG%TS88k^)&B5D zLD0Wg4z|PbWuWWY8$U3%kj@28UkjFC{NE18y6#yH>+`N~Ee_O8~x&Qm0+S_%p(O#)*FUvOF4`@Bwxfo!6|8vdueX06- z2E2X)K)(s$qRrT(zv){7;&HFk#g}a#`AdL&);Uh)154KJh@KqY7yW0~{%FOn^??1E&M0<9;(Av}~S6Uh~w>-VShgQYemMv} zaA>}!zp>IjdvCRu57_pT*PcP`Pj~WMfL*S{fG4nJ4oLYk4=AcqirMpbY1Pee`1$_z=+jZ`#+A%i4DLY;MSFU$OR< zuK+&!f#$hZgO7uTYkSIVb5o@+h#w1@FSr0`-;=JFyBWg|rETrc({E&73yuMHU#cYI zr23lWjk5NZod&)D{s@M_q3F?x&nU+0xAE=(x{jR=g6@;@fYC2!c}&V$OSlN=IV6g) zTRt^zlLUjRL~T+gl_1TO-`i=E(gum?!nA)wzk z)Nfq=6RZNi1~-FGgSnswG+e_|PPH$t$D!u&7_c>Hl2=UDv+Ly-iqU5Pt*^cp%m><2 zD{XbJq50@j!3p3)7xwFL zt=3~c5P#ijVIV$SuVA!Tt0kXLs!J`NROL92+oL|9o5?sF!1dta40YfBkq|t`|ocvCGS%$TBxJ zxS`w|MV9$-2O7#f4c&2Mf*B8pU z^O5Cy+;S5=2W@>gK5n^*|788Gp-fE5mb3ocP{#kW;~_hK8p@2f$hJRQ=5PbynQ}ay z2kI5XWs3mlXdr~{H*_ctxmE|_vekheecm#-H&b4wvekjOST{H}$1)%`$1)%`$BG!q z86Y*FceK8=fn`a#f3(L*^=Si1^=XAkb*Ut#I%9EC8_9qwOuwE~&{IDicgZS7aAqI2 I4|O^Je^F4ot^fc4 literal 0 HcmV?d00001 From 1f7a112302bfa460ed9470574e35743e21fbb2bc Mon Sep 17 00:00:00 2001 From: 1122455801 Date: Thu, 30 May 2019 15:11:32 +0800 Subject: [PATCH 5/5] Create widget.py --- vnpy/app/risk_manager/ui/widget.py | 116 +++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 vnpy/app/risk_manager/ui/widget.py diff --git a/vnpy/app/risk_manager/ui/widget.py b/vnpy/app/risk_manager/ui/widget.py new file mode 100644 index 00000000..d4baaa18 --- /dev/null +++ b/vnpy/app/risk_manager/ui/widget.py @@ -0,0 +1,116 @@ +from vnpy.event import EventEngine +from vnpy.trader.engine import MainEngine +from vnpy.trader.ui import QtWidgets +from ..engine import APP_NAME + + +class RiskManager(QtWidgets.QWidget): + """""" + + def __init__(self, main_engine: MainEngine, event_engine: EventEngine): + """""" + super().__init__() + + self.main_engine = main_engine + self.event_engine = event_engine + self.risk_manager_engine = main_engine.get_engine(APP_NAME) + + self.init_ui() + self.update_engine_status() + + def init_ui(self): + """""" + self.setWindowTitle("风险控制") + + # SpinBox + self.order_flow_limit = RiskManagerSpinBox(0) + self.order_flow_clear = RiskManagerSpinBox(0) + self.order_size_limit = RiskManagerSpinBox(0) + self.trade_limit = RiskManagerSpinBox(0) + self.active_order_limit = RiskManagerSpinBox(0) + self.order_cancel_limit = RiskManagerSpinBox(0) + + # Button + self.switch_button = QtWidgets.QPushButton("风控模块未启动") + clear_order_count_button = QtWidgets.QPushButton("清空流控计数") + clear_trade_count_button = QtWidgets.QPushButton("清空总成交计数") + save_setting_button = QtWidgets.QPushButton("保存设置") + + # Grid layout + Label = QtWidgets.QLabel + grid = QtWidgets.QGridLayout() + grid.addWidget(Label("工作状态"), 0, 0) + grid.addWidget(self.switch_button, 0, 1) + grid.addWidget(Label("流控上限"), 1, 0) + grid.addWidget(self.order_flow_limit, 1, 1) + grid.addWidget(Label("流控清空(秒)"), 2, 0) + grid.addWidget(self.order_flow_clear, 2, 1) + grid.addWidget(Label("单笔委托上限"), 3, 0) + grid.addWidget(self.order_size_limit, 3, 1) + grid.addWidget(Label("总成交上限"), 4, 0) + grid.addWidget(self.trade_limit, 4, 1) + grid.addWidget(Label("活动订单上限"), 5, 0) + grid.addWidget(self.active_order_limit, 5, 1) + grid.addWidget(Label("单合约撤单上限"), 6, 0) + grid.addWidget(self.order_cancel_limit, 6, 1) + + # Horizontal box layout + hbox = QtWidgets.QHBoxLayout() + hbox.addWidget(clear_order_count_button) + hbox.addWidget(clear_trade_count_button) + hbox.addWidget(save_setting_button) + + # Vertical box layout + vbox = QtWidgets.QVBoxLayout() + vbox.addLayout(grid) + vbox.addLayout(hbox) + self.setLayout(vbox) + + # Connect signal to SpinBox + self.order_flow_limit.valueChanged.connect(self.risk_manager_engine.set_order_flow_limit) + self.order_flow_clear.valueChanged.connect(self.risk_manager_engine.set_order_flow_clear) + self.order_size_limit.valueChanged.connect(self.risk_manager_engine.set_order_size_limit) + self.trade_limit.valueChanged.connect(self.risk_manager_engine.set_trade_limit) + self.active_order_limit.valueChanged.connect(self.risk_manager_engine.set_active_order_limit) + self.order_cancel_limit.valueChanged.connect(self.risk_manager_engine.set_order_cancel_limit) + + # Connect signal to button + self.switch_button.clicked.connect(self.switch_engine_status) + clear_order_count_button.clicked.connect(self.risk_manager_engine.clear_order_flow_count) + clear_trade_count_button.clicked.connect(self.risk_manager_engine.clear_trade_count) + save_setting_button.clicked.connect(self.risk_manager_engine.save_setting) + + # Set Fix Size + self.setFixedSize(self.sizeHint()) + + def switch_engine_status(self): + """""" + self.risk_manager_engine.switch_engine_status() + self.update_engine_status() + + def update_engine_status(self): + """""" + if self.risk_manager_engine.active: + self.switch_button.setText("风控模块运行中") + self.order_flow_limit.setValue(self.risk_manager_engine.order_flow_limit) + self.order_flow_clear.setValue(self.risk_manager_engine.order_flow_clear) + self.order_size_limit.setValue(self.risk_manager_engine.order_size_limit) + self.trade_limit.setValue(self.risk_manager_engine.trade_limit) + self.active_order_limit.setValue(self.risk_manager_engine.active_order_limit) + self.order_cancel_limit.setValue(self.risk_manager_engine.order_cancel_limit) + else: + self.switch_button.setText("风控模块未启动") + + self.risk_manager_engine.save_setting() + + +class RiskManagerSpinBox(QtWidgets.QSpinBox): + """""" + + def __init__(self, value): + """""" + super(RiskManagerSpinBox, self).__init__() + + self.setMinimum(0) + self.setMaximum(1000000) + self.setValue(value)