[Add] query_history function for BinanceGateway
This commit is contained in:
parent
28b9ce6d8d
commit
4d36b5198b
@ -4,11 +4,11 @@ from vnpy.event import EventEngine
|
|||||||
from vnpy.trader.engine import MainEngine
|
from vnpy.trader.engine import MainEngine
|
||||||
from vnpy.trader.ui import MainWindow, create_qapp
|
from vnpy.trader.ui import MainWindow, create_qapp
|
||||||
|
|
||||||
# from vnpy.gateway.binance import BinanceGateway
|
from vnpy.gateway.binance import BinanceGateway
|
||||||
# from vnpy.gateway.bitmex import BitmexGateway
|
# from vnpy.gateway.bitmex import BitmexGateway
|
||||||
# from vnpy.gateway.futu import FutuGateway
|
# from vnpy.gateway.futu import FutuGateway
|
||||||
# from vnpy.gateway.ib import IbGateway
|
# from vnpy.gateway.ib import IbGateway
|
||||||
from vnpy.gateway.ctp import CtpGateway
|
# from vnpy.gateway.ctp import CtpGateway
|
||||||
# from vnpy.gateway.ctptest import CtptestGateway
|
# from vnpy.gateway.ctptest import CtptestGateway
|
||||||
# from vnpy.gateway.femas import FemasGateway
|
# from vnpy.gateway.femas import FemasGateway
|
||||||
# from vnpy.gateway.tiger import TigerGateway
|
# from vnpy.gateway.tiger import TigerGateway
|
||||||
@ -42,8 +42,8 @@ def main():
|
|||||||
|
|
||||||
main_engine = MainEngine(event_engine)
|
main_engine = MainEngine(event_engine)
|
||||||
|
|
||||||
# main_engine.add_gateway(BinanceGateway)
|
main_engine.add_gateway(BinanceGateway)
|
||||||
main_engine.add_gateway(CtpGateway)
|
# main_engine.add_gateway(CtpGateway)
|
||||||
# main_engine.add_gateway(CtptestGateway)
|
# main_engine.add_gateway(CtptestGateway)
|
||||||
# main_engine.add_gateway(FemasGateway)
|
# main_engine.add_gateway(FemasGateway)
|
||||||
# main_engine.add_gateway(IbGateway)
|
# main_engine.add_gateway(IbGateway)
|
||||||
|
@ -315,8 +315,11 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
""""""
|
""""""
|
||||||
vt_symbol = self.symbol_line.text()
|
vt_symbol = self.symbol_line.text()
|
||||||
interval = self.interval_combo.currentText()
|
interval = self.interval_combo.currentText()
|
||||||
start = self.start_date_edit.date().toPyDate()
|
start_date = self.start_date_edit.date()
|
||||||
end = self.end_date_edit.date().toPyDate()
|
end_date = self.end_date_edit.date()
|
||||||
|
|
||||||
|
start = datetime(start_date.year(), start_date.month(), start_date.day())
|
||||||
|
end = datetime(end_date.year(), end_date.month(), end_date.day())
|
||||||
|
|
||||||
self.backtester_engine.start_downloading(
|
self.backtester_engine.start_downloading(
|
||||||
vt_symbol,
|
vt_symbol,
|
||||||
|
@ -7,7 +7,7 @@ import hashlib
|
|||||||
import hmac
|
import hmac
|
||||||
import time
|
import time
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
@ -18,7 +18,8 @@ from vnpy.trader.constant import (
|
|||||||
Exchange,
|
Exchange,
|
||||||
Product,
|
Product,
|
||||||
Status,
|
Status,
|
||||||
OrderType
|
OrderType,
|
||||||
|
Interval
|
||||||
)
|
)
|
||||||
from vnpy.trader.gateway import BaseGateway
|
from vnpy.trader.gateway import BaseGateway
|
||||||
from vnpy.trader.object import (
|
from vnpy.trader.object import (
|
||||||
@ -27,9 +28,11 @@ from vnpy.trader.object import (
|
|||||||
TradeData,
|
TradeData,
|
||||||
AccountData,
|
AccountData,
|
||||||
ContractData,
|
ContractData,
|
||||||
|
BarData,
|
||||||
OrderRequest,
|
OrderRequest,
|
||||||
CancelRequest,
|
CancelRequest,
|
||||||
SubscribeRequest
|
SubscribeRequest,
|
||||||
|
HistoryRequest
|
||||||
)
|
)
|
||||||
from vnpy.trader.event import EVENT_TIMER
|
from vnpy.trader.event import EVENT_TIMER
|
||||||
from vnpy.event import Event
|
from vnpy.event import Event
|
||||||
@ -59,6 +62,18 @@ DIRECTION_VT2BINANCE = {
|
|||||||
}
|
}
|
||||||
DIRECTION_BINANCE2VT = {v: k for k, v in DIRECTION_VT2BINANCE.items()}
|
DIRECTION_BINANCE2VT = {v: k for k, v in DIRECTION_VT2BINANCE.items()}
|
||||||
|
|
||||||
|
INTERVAL_VT2BINANCE = {
|
||||||
|
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 Security(Enum):
|
class Security(Enum):
|
||||||
NONE = 0
|
NONE = 0
|
||||||
@ -126,6 +141,10 @@ class BinanceGateway(BaseGateway):
|
|||||||
""""""
|
""""""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def query_history(self, req: HistoryRequest):
|
||||||
|
""""""
|
||||||
|
return self.rest_api.query_history(req)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
""""""
|
""""""
|
||||||
self.rest_api.stop()
|
self.rest_api.stop()
|
||||||
@ -167,14 +186,17 @@ class BinanceRestApi(RestClient):
|
|||||||
"""
|
"""
|
||||||
Generate BINANCE signature.
|
Generate BINANCE signature.
|
||||||
"""
|
"""
|
||||||
|
security = request.data["security"]
|
||||||
|
if security == Security.NONE:
|
||||||
|
request.data = None
|
||||||
|
return request
|
||||||
|
|
||||||
if request.params:
|
if request.params:
|
||||||
path = request.path + "?" + urllib.parse.urlencode(request.params)
|
path = request.path + "?" + urllib.parse.urlencode(request.params)
|
||||||
else:
|
else:
|
||||||
request.params = dict()
|
request.params = dict()
|
||||||
path = request.path
|
path = request.path
|
||||||
|
|
||||||
security = request.data["security"]
|
|
||||||
|
|
||||||
if security == Security.SIGNED:
|
if security == Security.SIGNED:
|
||||||
timestamp = int(time.time() * 1000)
|
timestamp = int(time.time() * 1000)
|
||||||
|
|
||||||
@ -184,7 +206,6 @@ class BinanceRestApi(RestClient):
|
|||||||
timestamp += abs(self.time_offset)
|
timestamp += abs(self.time_offset)
|
||||||
|
|
||||||
request.params["timestamp"] = timestamp
|
request.params["timestamp"] = timestamp
|
||||||
# request.params["recv_window"] = self.recv_window
|
|
||||||
|
|
||||||
query = urllib.parse.urlencode(sorted(request.params.items()))
|
query = urllib.parse.urlencode(sorted(request.params.items()))
|
||||||
signature = hmac.new(self.secret, query.encode(
|
signature = hmac.new(self.secret, query.encode(
|
||||||
@ -204,7 +225,7 @@ class BinanceRestApi(RestClient):
|
|||||||
"X-MBX-APIKEY": self.key
|
"X-MBX-APIKEY": self.key
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == Security.SIGNED or security == Security.API_KEY:
|
if security in [Security.SIGNED, Security.API_KEY]:
|
||||||
request.headers = headers
|
request.headers = headers
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -454,6 +475,7 @@ class BinanceRestApi(RestClient):
|
|||||||
size=1,
|
size=1,
|
||||||
min_volume=min_volume,
|
min_volume=min_volume,
|
||||||
product=Product.SPOT,
|
product=Product.SPOT,
|
||||||
|
history_data=True,
|
||||||
gateway_name=self.gateway_name,
|
gateway_name=self.gateway_name,
|
||||||
)
|
)
|
||||||
self.gateway.on_contract(contract)
|
self.gateway.on_contract(contract)
|
||||||
@ -507,6 +529,82 @@ class BinanceRestApi(RestClient):
|
|||||||
""""""
|
""""""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def query_history(self, req: HistoryRequest):
|
||||||
|
""""""
|
||||||
|
history = []
|
||||||
|
limit = 1000
|
||||||
|
start_time = int(datetime.timestamp(req.start))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Create query params
|
||||||
|
params = {
|
||||||
|
"symbol": req.symbol,
|
||||||
|
"interval": INTERVAL_VT2BINANCE[req.interval],
|
||||||
|
"limit": limit,
|
||||||
|
"startTime": start_time * 1000, # convert to millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add end time if specified
|
||||||
|
if req.end:
|
||||||
|
end_time = int(datetime.timestamp(req.end))
|
||||||
|
params["endTime"] = end_time * 1000 # convert to millisecond
|
||||||
|
|
||||||
|
# Get response from server
|
||||||
|
resp = self.request(
|
||||||
|
"GET",
|
||||||
|
"/api/v1/klines",
|
||||||
|
data={"security": Security.NONE},
|
||||||
|
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()
|
||||||
|
if not data:
|
||||||
|
msg = f"获取历史数据为空,开始时间:{start_time}"
|
||||||
|
self.gateway.write_log(msg)
|
||||||
|
break
|
||||||
|
|
||||||
|
buf = []
|
||||||
|
|
||||||
|
for l in data:
|
||||||
|
dt = datetime.fromtimestamp(l[0] / 1000) # convert to second
|
||||||
|
|
||||||
|
bar = BarData(
|
||||||
|
symbol=req.symbol,
|
||||||
|
exchange=req.exchange,
|
||||||
|
datetime=dt,
|
||||||
|
interval=req.interval,
|
||||||
|
volume=float(l[5]),
|
||||||
|
open_price=float(l[1]),
|
||||||
|
high_price=float(l[2]),
|
||||||
|
low_price=float(l[3]),
|
||||||
|
close_price=float(l[4]),
|
||||||
|
gateway_name=self.gateway_name
|
||||||
|
)
|
||||||
|
buf.append(bar)
|
||||||
|
|
||||||
|
history.extend(buf)
|
||||||
|
|
||||||
|
begin = buf[0].datetime
|
||||||
|
end = buf[-1].datetime
|
||||||
|
msg = f"获取历史数据成功,{req.symbol} - {req.interval.value},{begin} - {end}"
|
||||||
|
self.gateway.write_log(msg)
|
||||||
|
|
||||||
|
# Break if total data count less than limit (latest date collected)
|
||||||
|
if len(data) < limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Update start time
|
||||||
|
start_dt = bar.datetime + TIMEDELTA_MAP[req.interval]
|
||||||
|
start_time = int(datetime.timestamp(start_dt))
|
||||||
|
|
||||||
|
return history
|
||||||
|
|
||||||
|
|
||||||
class BinanceTradeWebsocketApi(WebsocketClient):
|
class BinanceTradeWebsocketApi(WebsocketClient):
|
||||||
""""""
|
""""""
|
||||||
|
Loading…
Reference in New Issue
Block a user