From 8c707435e88b81d27ea7e5ed660f4fbcd6e0ff58 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Wed, 8 May 2019 15:39:15 +0800 Subject: [PATCH] [Add] download bar data from gateway in CtaBacktester --- vnpy/api/rest/rest_client.py | 2 +- vnpy/app/cta_backtester/engine.py | 24 ++++++-- vnpy/app/cta_backtester/ui/widget.py | 4 +- vnpy/gateway/bitmex/bitmex_gateway.py | 89 +++++++++++++++++++++++++-- vnpy/trader/object.py | 3 +- 5 files changed, 108 insertions(+), 14 deletions(-) diff --git a/vnpy/api/rest/rest_client.py b/vnpy/api/rest/rest_client.py index 255ba35f..7bd4568e 100644 --- a/vnpy/api/rest/rest_client.py +++ b/vnpy/api/rest/rest_client.py @@ -258,7 +258,7 @@ class RestClient(object): request.response = response status_code = response.status_code - if status_code / 100 == 2: # 2xx都算成功,尽管交易所都用200 + if status_code // 100 == 2: # 2xx都算成功,尽管交易所都用200 jsonBody = response.json() request.callback(jsonBody, request) request.status = RequestStatus.success diff --git a/vnpy/app/cta_backtester/engine.py b/vnpy/app/cta_backtester/engine.py index 573feefe..40b3fb03 100644 --- a/vnpy/app/cta_backtester/engine.py +++ b/vnpy/app/cta_backtester/engine.py @@ -9,6 +9,7 @@ from vnpy.event import Event, EventEngine from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.constant import Interval from vnpy.trader.utility import extract_vt_symbol +from vnpy.trader.object import HistoryRequest from vnpy.trader.rqdata import rqdata_client from vnpy.trader.database import database_manager from vnpy.app.cta_strategy import ( @@ -337,10 +338,25 @@ class BacktesterEngine(BaseEngine): """ self.write_log(f"{vt_symbol}-{interval}开始下载历史数据") - symbol, exchange = extract_vt_symbol(vt_symbol) - data = rqdata_client.query_bar( - symbol, exchange, Interval(interval), start, end - ) + contract = self.main_engine.get_contract(vt_symbol) + + # If history data provided in gateway, then query + if contract and contract.history_data: + req = HistoryRequest( + symbol=contract.symbol, + exchange=contract.exchange, + interval=Interval(interval), + start=start, + end=end + ) + data = self.main_engine.query_history(req, contract.gateway_name) + # Otherwise use RQData to query data + else: + symbol, exchange = extract_vt_symbol(vt_symbol) + + data = rqdata_client.query_bar( + symbol, exchange, Interval(interval), start, end + ) if data: database_manager.save_bar_data(data) diff --git a/vnpy/app/cta_backtester/ui/widget.py b/vnpy/app/cta_backtester/ui/widget.py index 2198c11d..6be8f167 100644 --- a/vnpy/app/cta_backtester/ui/widget.py +++ b/vnpy/app/cta_backtester/ui/widget.py @@ -56,14 +56,14 @@ class BacktesterManager(QtWidgets.QWidget): self.class_combo = QtWidgets.QComboBox() self.class_combo.addItems(self.class_names) - self.symbol_line = QtWidgets.QLineEdit("IF88.CFFEX") + self.symbol_line = QtWidgets.QLineEdit("XBTUSD.BITMEX") self.interval_combo = QtWidgets.QComboBox() for inteval in Interval: self.interval_combo.addItem(inteval.value) end_dt = datetime.now() - start_dt = end_dt - timedelta(days=3 * 365) + start_dt = end_dt - timedelta(days=30)# * 365) self.start_date_edit = QtWidgets.QDateEdit( QtCore.QDate( diff --git a/vnpy/gateway/bitmex/bitmex_gateway.py b/vnpy/gateway/bitmex/bitmex_gateway.py index 9127c64a..338ebfad 100644 --- a/vnpy/gateway/bitmex/bitmex_gateway.py +++ b/vnpy/gateway/bitmex/bitmex_gateway.py @@ -7,7 +7,7 @@ import hmac import sys import time from copy import copy -from datetime import datetime +from datetime import datetime, timedelta from threading import Lock from urllib.parse import urlencode @@ -21,7 +21,8 @@ from vnpy.trader.constant import ( OrderType, Product, Status, - Offset + Offset, + Interval ) from vnpy.trader.gateway import BaseGateway from vnpy.trader.object import ( @@ -31,6 +32,7 @@ from vnpy.trader.object import ( PositionData, AccountData, ContractData, + BarData, OrderRequest, CancelRequest, SubscribeRequest, @@ -61,6 +63,18 @@ ORDERTYPE_VT2BITMEX = { } ORDERTYPE_BITMEX2VT = {v: k for k, v in ORDERTYPE_VT2BITMEX.items()} +INTERVAL_VT2BITMEX = { + Interval.MINUTE: "1m", + Interval.HOUR: "1h", + Interval.DAILY: "1d", +} + +TIMEDELTA_MAP = { + Interval.MINUTE: timedelta(minutes=1), + Interval.HOUR: timedelta(hours=1), + Interval.DAILY: timedelta(days=1), +} + class BitmexGateway(BaseGateway): """ @@ -160,7 +174,7 @@ class BitmexRestApi(RestClient): Generate BitMEX signature. """ # Sign - expires = int(time.time() + 5) + expires = int(time.time() + 30) if request.params: query = urlencode(request.params) @@ -286,7 +300,72 @@ class BitmexRestApi(RestClient): def query_history(self, req: HistoryRequest): """""" - pass + history = [] + count = 750 + start_time = req.start.isoformat() + + while True: + # Create query params + params = { + "binSize": INTERVAL_VT2BITMEX[req.interval], + "symbol": req.symbol, + "count": count, + "startTime": start_time + } + + # Add end time if specified + if req.end: + params["endTime"] = req.end.isoformat() + + # Get response from server + resp = self.request( + "GET", + "/trade/bucketed", + params=params + ) + + # Break if request failed with other status code + if resp.status_code // 100 != 2: + msg = f"获取历史数据失败,状态码:{resp.status_code},信息:{resp.text}" + self.gateway.write_log(msg) + break + else: + data = resp.json() + + for d in data: + dt = datetime.strptime(d["timestamp"], "%Y-%m-%dT%H:%M:%S.%fZ") + bar = BarData( + symbol=req.symbol, + exchange=req.exchange, + datetime=dt, + interval=req.interval, + volume=d["volume"], + open_price=d["open"], + high_price=d["high"], + low_price=d["low"], + close_price=d["close"], + gateway_name=self.gateway_name + ) + history.append(bar) + + begin = data[0]["timestamp"] + end = data[-1]["timestamp"] + msg = f"获取历史数据成功,{req.symbol} - {req.interval.value},{begin} - {end}" + self.gateway.write_log(msg) + + # Break if total data count less than 750 (latest date collected) + if len(data) < 750: + break + + # Update start time + start_time = bar.datetime + TIMEDELTA_MAP[req.interval] + + return history + + + + + def on_send_order_failed(self, status_code: str, request: Request): """ @@ -624,7 +703,7 @@ class BitmexWebsocketApi(WebsocketClient): size=d["lotSize"], stop_supported=True, net_position=True, - bar_history=True, + history_data=True, gateway_name=self.gateway_name, ) diff --git a/vnpy/trader/object.py b/vnpy/trader/object.py index 7a551a85..952c0360 100644 --- a/vnpy/trader/object.py +++ b/vnpy/trader/object.py @@ -236,8 +236,7 @@ class ContractData(BaseData): min_volume: float = 1 # minimum trading volume of the contract stop_supported: bool = False # whether server supports stop order net_position: bool = False # whether gateway uses net position volume - bar_history: bool = False # whether gateway provides bar history data - tick_history: bool = False # whether gateway provides tick history data + history_data: bool = False # whether gateway provides bar history data option_strike: float = 0 option_underlying: str = "" # vt_symbol of underlying contract