From be9142e878ca4328a14917ebb9aa619adb32310b Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 16 Sep 2019 14:51:57 +0800 Subject: [PATCH] [Mod] add send/cancel order function of algo engine --- vnpy/app/spread_trading/algo.py | 5 +- vnpy/app/spread_trading/engine.py | 93 ++++++++++++++++++++++++++-- vnpy/app/spread_trading/template.py | 5 +- vnpy/app/spread_trading/ui/widget.py | 14 ++++- 4 files changed, 108 insertions(+), 9 deletions(-) diff --git a/vnpy/app/spread_trading/algo.py b/vnpy/app/spread_trading/algo.py index 9e0066ed..13b3e968 100644 --- a/vnpy/app/spread_trading/algo.py +++ b/vnpy/app/spread_trading/algo.py @@ -21,12 +21,13 @@ class SpreadTakerAlgo(SpreadAlgoTemplate): price: float, volume: float, payup: int, - interval: int + interval: int, + lock: bool ): """""" super().__init__( algo_engine, algoid, spread, direction, - price, volume, payup, interval + price, volume, payup, interval, lock ) self.cancel_interval: int = 2 diff --git a/vnpy/app/spread_trading/engine.py b/vnpy/app/spread_trading/engine.py index 42690280..e12afeb2 100644 --- a/vnpy/app/spread_trading/engine.py +++ b/vnpy/app/spread_trading/engine.py @@ -1,4 +1,4 @@ -from typing import List, Dict +from typing import List, Dict, Set from collections import defaultdict from copy import copy @@ -13,7 +13,8 @@ from vnpy.trader.object import ( TickData, ContractData, LogData, SubscribeRequest, OrderRequest, CancelRequest ) -from vnpy.trader.constant import Direction +from vnpy.trader.constant import Direction, Offset, OrderType +from vnpy.trader.converter import OffsetConverter, PositionHolding from .base import ( LegData, SpreadData, @@ -242,6 +243,11 @@ class SpreadAlgoEngine: self.symbol_algo_map: dict[str: SpreadAlgoTemplate] = defaultdict(list) self.algo_count: int = 0 + self.vt_tradeids: Set = set() + + self.offset_converter: OffsetConverter = OffsetConverter( + self.main_engine + ) def start(self): """""" @@ -254,9 +260,11 @@ class SpreadAlgoEngine: self.event_engine.register(EVENT_TICK, self.process_tick_event) self.event_engine.register(EVENT_ORDER, self.process_order_event) self.event_engine.register(EVENT_TRADE, self.process_trade_event) + self.event_engine.register(EVENT_POSITION, self.process_position_event) self.event_engine.register(EVENT_TIMER, self.process_timer_event) self.event_engine.register( - EVENT_SPREAD_DATA, self.process_spread_event) + EVENT_SPREAD_DATA, self.process_spread_event + ) def process_spread_event(self, event: Event): """""" @@ -280,6 +288,9 @@ class SpreadAlgoEngine: def process_order_event(self, event: Event): """""" order = event.data + + self.offset_converter.update_order(order) + algo = self.order_algo_map.get(order.vt_orderid, None) if algo and algo.is_active(): algo.update_order(order) @@ -287,10 +298,24 @@ class SpreadAlgoEngine: def process_trade_event(self, event: Event): """""" trade = event.data + + # Filter duplicate trade push + if trade.vt_tradeid in self.vt_tradeids: + return + self.vt_tradeids.add(trade.vt_tradeid) + + self.offset_converter.update_trade(trade) + algo = self.order_algo_map.get(trade.vt_orderid, None) if algo and algo.is_active(): algo.update_trade(trade) + def process_position_event(self, event: Event): + """""" + position = event.data + + self.offset_converter.update_position(position) + def process_timer_event(self, event: Event): """""" buf = list(self.algos.values()) @@ -372,13 +397,71 @@ class SpreadAlgoEngine: price: float, volume: float, direction: Direction, + lock: bool ) -> List[str]: """""" - pass + holding = self.offset_converter.get_position_holding(vt_symbol) + contract = self.main_engine.get_contract(vt_symbol) + + if direction == Direction.LONG: + available = holding.short_pos - holding.short_pos_frozen + else: + available = holding.long_pos - holding.long_pos_frozen + + # If no position to close, just open new + if not available: + offset = Offset.OPEN + # If enougth position to close, just close old + elif volume < available: + offset = Offset.CLOSE + # Otherwise, just close existing position + else: + volume = available + offset = Offset.CLOSE + + original_req = OrderRequest( + contract.symbol, + contract.exchange, + direction, + offset, + OrderType.LIMIT, + price, + volume + ) + + # Convert with offset converter + req_list = self.offset_converter.convert_order_request( + original_req, lock) + + # Send Orders + vt_orderids = [] + + for req in req_list: + vt_orderid = self.main_engine.send_order( + req, contract.gateway_name) + + # Check if sending order successful + if not vt_orderid: + continue + + vt_orderids.append(vt_orderid) + + self.offset_converter.update_order_request(req, vt_orderid) + + # Save relationship between orderid and algo. + self.order_algo_map[vt_orderid] = algo + + return vt_orderids def cancel_order(self, algo: SpreadAlgoTemplate, vt_orderid: str) -> None: """""" - pass + order = self.main_engine.get_order(vt_orderid) + if not order: + self.write_algo_log(algo, "撤单失败,找不到委托{}".format(vt_orderid)) + return + + req = order.create_cancel_request() + self.main_engine.cancel_order(req, order.gateway_name) def get_tick(self, vt_symbol: str) -> TickData: """""" diff --git a/vnpy/app/spread_trading/template.py b/vnpy/app/spread_trading/template.py index 6c967d91..0a0767ec 100644 --- a/vnpy/app/spread_trading/template.py +++ b/vnpy/app/spread_trading/template.py @@ -25,7 +25,8 @@ class SpreadAlgoTemplate: price: float, volume: float, payup: int, - interval: int + interval: int, + lock: bool ): """""" self.algo_engine = algo_engine @@ -39,6 +40,7 @@ class SpreadAlgoTemplate: self.volume: float = volume self.payup: int = payup self.interval = interval + self.lock = lock if direction == Direction.LONG: self.target = volume @@ -166,6 +168,7 @@ class SpreadAlgoTemplate: price, volume, direction, + self.lock ) self.leg_orders[vt_symbol].extend(vt_orderids) diff --git a/vnpy/app/spread_trading/ui/widget.py b/vnpy/app/spread_trading/ui/widget.py index 062e6221..644bcef7 100644 --- a/vnpy/app/spread_trading/ui/widget.py +++ b/vnpy/app/spread_trading/ui/widget.py @@ -227,6 +227,11 @@ class SpreadAlgoDialog(QtWidgets.QDialog): button_start = QtWidgets.QPushButton("启动") button_start.clicked.connect(self.start_algo) + self.lock_combo = QtWidgets.QComboBox() + self.lock_combo.addItems( + ["否", "是"] + ) + form = QtWidgets.QFormLayout() form.addRow("价差", self.name_line) form.addRow("方向", self.direction_combo) @@ -234,6 +239,7 @@ class SpreadAlgoDialog(QtWidgets.QDialog): form.addRow("数量", self.volume_line) form.addRow("超价", self.payup_line) form.addRow("间隔", self.interval_line) + form.addRow("锁仓", self.lock_line) form.addRow(button_start) self.setLayout(form) @@ -247,8 +253,14 @@ class SpreadAlgoDialog(QtWidgets.QDialog): payup = int(self.payup_line.text()) interval = int(self.interval_line.text()) + lock_str = self.lock_combo.currentText() + if lock_str == "是": + lock = True + else: + lock = False + self.spread_engine.start_algo( - name, direction, price, volume, payup, interval + name, direction, price, volume, payup, interval, lock )