diff --git a/examples/vn_trader/run.py b/examples/vn_trader/run.py index 04f5b760..5dcbe65d 100644 --- a/examples/vn_trader/run.py +++ b/examples/vn_trader/run.py @@ -7,11 +7,11 @@ from vnpy.trader.ui import MainWindow, create_qapp # from vnpy.gateway.binance import BinanceGateway # from vnpy.gateway.bitmex import BitmexGateway # 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.ctptest import CtptestGateway # from vnpy.gateway.femas import FemasGateway -from vnpy.gateway.tiger import TigerGateway +# from vnpy.gateway.tiger import TigerGateway # from vnpy.gateway.oes import OesGateway # from vnpy.gateway.okex import OkexGateway # from vnpy.gateway.huobi import HuobiGateway @@ -19,15 +19,15 @@ from vnpy.gateway.tiger import TigerGateway # from vnpy.gateway.onetoken import OnetokenGateway # from vnpy.gateway.okexf import OkexfGateway # from vnpy.gateway.xtp import XtpGateway -from vnpy.gateway.hbdm import HbdmGateway +# from vnpy.gateway.hbdm import HbdmGateway # from vnpy.gateway.tap import TapGateway -from vnpy.gateway.tora import ToraGateway -from vnpy.gateway.alpaca import AlpacaGateway +# from vnpy.gateway.tora import ToraGateway +# from vnpy.gateway.alpaca import AlpacaGateway -# from vnpy.app.cta_strategy import CtaStrategyApp +from vnpy.app.cta_strategy import CtaStrategyApp # from vnpy.app.csv_loader import CsvLoaderApp # from vnpy.app.algo_trading import AlgoTradingApp -# from vnpy.app.cta_backtester import CtaBacktesterApp +from vnpy.app.cta_backtester import CtaBacktesterApp # from vnpy.app.data_recorder import DataRecorderApp # from vnpy.app.risk_manager import RiskManagerApp @@ -44,7 +44,7 @@ def main(): # main_engine.add_gateway(CtpGateway) # main_engine.add_gateway(CtptestGateway) # main_engine.add_gateway(FemasGateway) - # main_engine.add_gateway(IbGateway) + main_engine.add_gateway(IbGateway) # main_engine.add_gateway(FutuGateway) # main_engine.add_gateway(BitmexGateway) # main_engine.add_gateway(TigerGateway) @@ -57,11 +57,11 @@ def main(): # main_engine.add_gateway(HbdmGateway) # main_engine.add_gateway(XtpGateway) # main_engine.add_gateway(TapGateway) - main_engine.add_gateway(ToraGateway) - main_engine.add_gateway(AlpacaGateway) + # main_engine.add_gateway(ToraGateway) + # main_engine.add_gateway(AlpacaGateway) - # main_engine.add_app(CtaStrategyApp) - # main_engine.add_app(CtaBacktesterApp) + main_engine.add_app(CtaStrategyApp) + main_engine.add_app(CtaBacktesterApp) # main_engine.add_app(CsvLoaderApp) # main_engine.add_app(AlgoTradingApp) # main_engine.add_app(DataRecorderApp) diff --git a/vnpy/gateway/ib/ib_gateway.py b/vnpy/gateway/ib/ib_gateway.py index 19a84774..930334a5 100644 --- a/vnpy/gateway/ib/ib_gateway.py +++ b/vnpy/gateway/ib/ib_gateway.py @@ -4,7 +4,7 @@ Please install ibapi from Interactive Brokers github page. from copy import copy from datetime import datetime from queue import Empty -from threading import Thread +from threading import Thread, Condition from ibapi import comm from ibapi.client import EClient @@ -16,6 +16,7 @@ from ibapi.order_state import OrderState from ibapi.ticktype import TickType from ibapi.wrapper import EWrapper from ibapi.errors import BAD_LENGTH +from ibapi.common import BarData as IbBarData from vnpy.trader.gateway import BaseGateway from vnpy.trader.object import ( @@ -25,9 +26,11 @@ from vnpy.trader.object import ( PositionData, AccountData, ContractData, + BarData, OrderRequest, CancelRequest, - SubscribeRequest + SubscribeRequest, + HistoryRequest ) from vnpy.trader.constant import ( Product, @@ -37,6 +40,7 @@ from vnpy.trader.constant import ( Currency, Status, OptionType, + Interval ) ORDERTYPE_VT2IB = {OrderType.LIMIT: "LMT", OrderType.MARKET: "MKT"} @@ -106,6 +110,12 @@ ACCOUNTFIELD_IB2VT = { "MaintMarginReq": "margin", } +INTERVAL_VT2IB = { + Interval.MINUTE: "1 min", + Interval.HOUR: "1 hour", + Interval.DAILY: "1 day", +} + class IbGateway(BaseGateway): """""" @@ -170,6 +180,10 @@ class IbGateway(BaseGateway): """ pass + def query_history(self, req: HistoryRequest): + """""" + return self.api.query_history(req) + class IbApi(EWrapper): """""" @@ -193,6 +207,10 @@ class IbApi(EWrapper): self.tick_exchange = {} + self.history_req = None + self.history_condition = Condition() + self.history_buf = [] + self.client = IbClient(self) self.thread = Thread(target=self.client.run) @@ -465,6 +483,7 @@ class IbApi(EWrapper): size=ib_size, pricetick=contractDetails.minTick, net_position=True, + history_data=True, gateway_name=self.gateway_name, ) @@ -504,6 +523,35 @@ class IbApi(EWrapper): for account_code in accountsList.split(","): self.client.reqAccountUpdates(True, account_code) + def historicalData(self, reqId: int, ib_bar: IbBarData): + """ + Callback of history data update. + """ + dt = datetime.strptime(ib_bar.date, "%Y%m%d %H:%M:%S") + + bar = BarData( + symbol=self.history_req.symbol, + exchange=self.history_req.exchange, + datetime=dt, + interval=self.history_req.interval, + volume=ib_bar.volume, + open_price=ib_bar.open, + high_price=ib_bar.high, + low_price=ib_bar.low, + close_price=ib_bar.close, + gateway_name=self.gateway_name + ) + + self.history_buf.append(bar) + + def historicalDataEnd(self, reqId: int, start: str, end: str): + """ + Callback of history data finished. + """ + self.history_condition.acquire() + self.history_condition.notify() + self.history_condition.release() + def connect(self, host: str, port: int, clientid: int): """ Connect to TWS. @@ -604,6 +652,56 @@ class IbApi(EWrapper): self.client.cancelOrder(int(req.orderid)) + def query_history(self, req: HistoryRequest): + """""" + self.history_req = req + + self.reqid += 1 + + ib_contract = Contract() + ib_contract.conId = str(req.symbol) + ib_contract.exchange = EXCHANGE_VT2IB[req.exchange] + + if req.end: + end = req.end + end_str = end.strftime("%Y%m%d %H:%M:%S") + else: + end = datetime.now() + end_str = "" + + delta = end - req.start + days = min(delta.days, 180) # IB only provides 6-month data + duration = f"{days} D" + bar_size = INTERVAL_VT2IB[req.interval] + + if req.exchange == Exchange.IDEALPRO: + bar_type = "MIDPOINT" + else: + bar_type = "TRADES" + + self.client.reqHistoricalData( + self.reqid, + ib_contract, + end_str, + duration, + bar_size, + bar_type, + 1, + 1, + False, + [] + ) + + self.history_condition.acquire() # Wait for async data return + self.history_condition.wait() + self.history_condition.release() + + history = self.history_buf + self.history_buf = [] # Create new buffer list + self.history_req = None + + return history + class IbClient(EClient): """"""