[Add] Start ib gateway development

This commit is contained in:
vn.py 2019-01-09 16:41:51 +08:00
parent a899060360
commit be25b684a0
4 changed files with 355 additions and 62 deletions

View File

@ -0,0 +1,271 @@
"""
Please install ibapi from Interactive Brokers github page.
"""
from datetime import datetime
from copy import copy
from ibapi.wrapper import EWrapper
from ibapi.client import EClient
from vnpy.trader.gateway import Gateway
from vnpy.trader.object import (
TickData,
OrderData,
TradeData,
ContractData,
PositionData,
AccountData,
LogData,
SubscribeRequest,
OrderRequest,
CancelRequest
)
from vnpy.trader.constant import (
PRODUCT_EQUITY,
PRODUCT_FOREX,
PRODUCT_SPOT,
PRODUCT_FUTURES,
PRODUCT_OPTION,
PRICETYPE_LIMIT,
PRICETYPE_MARKET,
DIRECTION_LONG,
DIRECTION_SHORT,
EXCHANGE_SMART,
EXCHANGE_NYMEX,
EXCHANGE_GLOBEX,
EXCHANGE_IDEALPRO,
EXCHANGE_HKEX,
EXCHANGE_HKFE,
EXCHANGE_CME,
EXCHANGE_ICE,
CURRENCY_CNY,
CURRENCY_HKD,
CURRENCY_USD,
STATUS_SUBMITTING,
STATUS_NOTTRADED,
STATUS_PARTTRADED,
STATUS_ALLTRADED,
STATUS_CANCELLED,
STATUS_REJECTED,
OPTION_CALL,
OPTION_PUT
)
PRICETYPE_VT2IB = {PRICETYPE_LIMIT: "LMT", PRICETYPE_MARKET: "MKT"}
PRICETYPE_IB2VT = {v: k for k, v in PRICETYPE_VT2IB.items()}
DIRECTION_VT2IB = {DIRECTION_LONG: "BUY", DIRECTION_SHORT: "SELL"}
DIRECTION_IB2VT = {v: k for k, v in DIRECTION_IB2VT.items()}
EXCHANGE_VT2IB = {
EXCHANGE_SMART: "SMART",
EXCHANGE_NYMEX: "NYMEX",
EXCHANGE_GLOBEX: "GLOBEX",
EXCHANGE_IDEALPRO: "IDEALPRO",
EXCHANGE_CME: "CME",
EXCHANGE_ICE: "ICE",
EXCHANGE_HKEX: "HKEX",
EXCHANGE_HKFE: "HKFE"
}
EXCHANGE_IB2VT = {v: k for k, v in EXCHANGE_VT2IB.items()}
STATUS_IB2VT = {
"Submitted": STATUS_NOTTRADED,
"Filled": STATUS_ALLTRADED,
"Cancelled": STATUS_CANCELLED,
"PendingSubmit": STATUS_SUBMITTING,
"PreSubmitted": STATUS_NOTTRADED
}
PRODUCT_VT2IB = {
PRODUCT_EQUITY: "STK",
PRODUCT_FOREX: "CASH",
PRODUCT_SPOT: "CMDTY",
PRODUCT_OPTION: "OPT",
PRODUCT_FUTURES: "FUT"
}
OPTION_VT2IB = {OPTION_CALL: "CALL", OPTION_PUT: "PUT"}
CURRENCY_VT2IB = {CURRENCY_USD: "USD", CURRENCY_CNY: "CNY", CURRENCY_HKD: "HKD"}
TICKFIELD_IB2VT = {
0: "bid_volume_1",
1: "bid_price_1",
2: "ask_price_1",
3: "ask_volume_1",
4: "last_price",
5: "last_volume",
6: "high_price",
7: "low_price",
8: "volume",
9: "pre_close",
14: "open_price"
}
ACCOUNTFIELD_IB2VT = {
"NetLiquidationByCurrency": "balance",
"NetLiquidation": "balance",
"UnrealizedPnL": "positionProfit",
"AvailableFunds": "available",
"MaintMarginReq": "margin",
}
class IbGateway(Gateway):
""""""
def __init__(self, event_engine):
""""""
super(Gateway, self).__init__(event_engine, "IB")
def connect(self):
"""
Start gateway connection.
"""
pass
def close(self):
"""
Close gateway connection.
"""
pass
def subscribe(self, req: SubscribeRequest):
"""
Subscribe tick data update.
"""
pass
def send_order(self, req: OrderRequest):
"""
Send a new order.
"""
pass
def cancel_order(self, req: CancelRequest):
"""
Cancel an existing order.
"""
pass
def query_account(self):
"""
Query account balance.
"""
pass
def query_position(self):
"""
Query holding positions.
"""
pass
class IbApi(EWrapper):
""""""
def __init__(self, gateway: Gateway):
""""""
super(IbApi, self).__init__()
self.gateway = gateway
self.gateway_name = gateway.gateway_name
self.status = False
self.orderid = 0
self.ticks = {}
self.orders = {}
self.accounts = {}
self.contracts = {}
def nextValidId(self, orderId: int):
"""
Callback of next valid orderid.
"""
super(IbApi, self).nextValidId(orderId)
self.orderid = orderId
def currentTime(self, time: int):
"""
Callback of current server time of IB.
"""
super(IbApi, self).currentTime(time)
self.status = True
dt = datetime.fromtimestamp(time)
time_string = dt.strftime("%Y-%m-%d %H:%M:%S.%f")
log = LogData(
msg=f"IB TWS连接成功服务器时间: {time_string}",
gateway_name=self.gateway_name
)
self.gateway.on_log(log)
def error(self, reqId: TickerId, errorCode: int, errorString: str):
"""
Callback of error caused by specific request.
"""
super(IbApi, self).error(reqId, errorCode, errorString)
log = LogData(
msg=f"发生错误,错误代码:{errorCode},错误信息: {errorString}",
gateway_name=self.gateway_name
)
self.gateway.on_log(log)
def tickPrice(
self,
reqId: TickerId,
tickType: TickType,
price: float,
attrib: TickAttrib
):
"""
Callback of tick price update.
"""
super(IbApi, self).tickPrice(reqId, tickType, price, attrib)
if tickType not in TICKFIELD_IB2VT:
return
tick = self.ticks[reqId]
name = TICKFIELD_IB2VT[tickType]
setattr(tick, name, price)
# Forex and spot product of IB has no tick time and last price.
# We need to calculate locally.
contract = self.contracts[reqId]
if contract.product in (PRODUCT_FOREX, PRODUCT_SPOT):
tick.last_price = (tick.bid_price_1 + tick.ask_price_1) / 2
tick.datetime = datetime.now()
self.gateway.on_tick(copy(tick))
def tickSize(self, reqId: TickerId, tickType: TickType, size: int):
"""
Callback of tick volume update.
"""
super(IbApi, self).tickSize(reqId, tickType, size)
if tickType not in TICKFIELD_IB2VT:
return
tick = self.ticks[reqId]
name = TICKFIELD_IB2VT[tickType]
setattr(tick, name, size)
self.gateway.on_tick(copy(tick))
def tickString(self, reqId: TickerId, tickType: TickType, value: str):
"""
Callback of tick string update.
"""
super().tickString(reqId, tickType, value)
if tickType != "45":
return
tick = self.ticks[reqId]
tick.datetime = datetime.fromtimestamp(value)
self.gateway.on_tick(copy(tick))

View File

@ -2,54 +2,64 @@
General constant string used in VN Trader.
"""
DIRECTION_LONG = ''
DIRECTION_SHORT = ''
DIRECTION_NET = ''
DIRECTION_LONG = ""
DIRECTION_SHORT = ""
DIRECTION_NET = ""
OFFSET_OPEN = ''
OFFSET_CLOSE = ''
OFFSET_CLOSETODAY = '平今'
OFFSET_CLOSEYESTERDAY = '平昨'
OFFSET_OPEN = ""
OFFSET_CLOSE = ""
OFFSET_CLOSETODAY = "平今"
OFFSET_CLOSEYESTERDAY = "平昨"
STATUS_SUBMITTING = '提交中'
STATUS_NOTTRADED = '未成交'
STATUS_PARTTRADED = '部分成交'
STATUS_ALLTRADED = '全部成交'
STATUS_CANCELLED = '已撤销'
STATUS_REJECTED = '拒单'
STATUS_SUBMITTING = "提交中"
STATUS_NOTTRADED = "未成交"
STATUS_PARTTRADED = "部分成交"
STATUS_ALLTRADED = "全部成交"
STATUS_CANCELLED = "已撤销"
STATUS_REJECTED = "拒单"
PRODUCT_EQUITY = '股票'
PRODUCT_FUTURES = '期货'
PRODUCT_OPTION = '期权'
PRODUCT_INDEX = '指数'
PRODUCT_FOREX = '外汇'
PRODUCT_SPOT = '现货'
PRODUCT_EQUITY = "股票"
PRODUCT_FUTURES = "期货"
PRODUCT_OPTION = "期权"
PRODUCT_INDEX = "指数"
PRODUCT_FOREX = "外汇"
PRODUCT_SPOT = "现货"
PRICETYPE_LIMIT = '限价'
PRICETYPE_MARKET = '市价'
PRICETYPE_FAK = 'FAK'
PRICETYPE_FOK = 'FOK'
PRICETYPE_LIMIT = "限价"
PRICETYPE_MARKET = "市价"
PRICETYPE_FAK = "FAK"
PRICETYPE_FOK = "FOK"
OPTION_CALL = '看涨期权'
OPTION_PUT = '看跌期权'
OPTION_CALL = "看涨期权"
OPTION_PUT = "看跌期权"
EXCHANGE_SSE = 'SSE'
EXCHANGE_SZSE = 'SZSE'
EXCHANGE_CFFEX = 'CFFEX'
EXCHANGE_SHFE = 'SHFE'
EXCHANGE_CZCE = 'CZCE'
EXCHANGE_DCE = 'DCE'
EXCHANGE_INE = 'INE'
EXCHANGE_SGE = 'SGE'
EXCHANGE_SSE = "SSE"
EXCHANGE_SZSE = "SZSE"
EXCHANGE_CFFEX = "CFFEX"
EXCHANGE_SHFE = "SHFE"
EXCHANGE_CZCE = "CZCE"
EXCHANGE_DCE = "DCE"
EXCHANGE_INE = "INE"
EXCHANGE_SGE = "SGE"
CURRENCY_USD = 'USD'
CURRENCY_HKD = 'HKD'
EXCHANGE_SMART = "SMART"
EXCHANGE_NYMEX = "NYMEX"
EXCHANGE_GLOBEX = "GLOBEX"
EXCHANGE_IDEALPRO = "IDEALPRO"
EXCHANGE_CME = "CME"
EXCHANGE_ICE = "ICE"
EXCHANGE_HKEX = "HKEX"
EXCHANGE_HKFE = "HKFE"
INTERVAL_1M = '1分钟'
INTERVAL_5M = '5分钟'
INTERVAL_15M = '15分钟'
INTERVAL_30M = '30分钟'
INTERVAL_1H = '1小时'
INTERVAL_4H = '4小时'
INTERVAL_DAILY = '日线'
INTERVAL_WEEKLY = '周线'
CURRENCY_USD = "USD"
CURRENCY_HKD = "HKD"
CURRENCY_CNY = "CNY"
INTERVAL_1M = "1分钟"
INTERVAL_5M = "5分钟"
INTERVAL_15M = "15分钟"
INTERVAL_30M = "30分钟"
INTERVAL_1H = "1小时"
INTERVAL_4H = "4小时"
INTERVAL_DAILY = "日线"
INTERVAL_WEEKLY = "周线"

View File

@ -6,7 +6,15 @@ from datetime import datetime
from vnpy.event import EventEngine, Event
from .event import EVENT_LOG
from .event import (
EVENT_LOG,
EVENT_TICK,
EVENT_ORDER,
EVENT_TRADE,
EVENT_POSITION,
EVENT_ACCOUNT,
EVENT_CONTRACT
)
from .object import LogData, SubscribeRequest, OrderRequest, CancelRequest
from .utility import Singleton, get_temp_path, check_order_active
from .setting import SETTINGS
@ -31,7 +39,7 @@ class MainEngine:
self.init_engines()
def init_engines:
def init_engines(self):
"""
Init all engines.
"""
@ -40,20 +48,21 @@ class MainEngine:
# OMS Engine
self.engines["oms"] = OmsEngine(self, self.event_engine)
oms_engine = self.engines["oms"]
self.get_tick = oms_engine.get_tick
self.get_order = oms_engine.get_order
self.get_position = oms_engine.get_position
self.get_account = oms_engine.get_account
self.get_contract = oms_engine.get_contract
self.get_all_ticks = oms.get_all_ticks
self.get_all_orders = oms.get_all_orders
self.get_all_trades = oms.get_all_trades
self.get_all_positions = oms.get_all_positions
self.get_all_accounts = oms.get_all_accounts
self.get_all_active_orders = oms.get_all_active_orders
self.get_contract = oms_engine.get_contract
self.get_all_ticks = oms_engine.get_all_ticks
self.get_all_orders = oms_engine.get_all_orders
self.get_all_trades = oms_engine.get_all_trades
self.get_all_positions = oms_engine.get_all_positions
self.get_all_accounts = oms_engine.get_all_accounts
self.get_all_active_orders = oms_engine.get_all_active_orders
def write_log(self, msg: str):
"""
Put log event with specific message.
@ -172,7 +181,7 @@ class LogEngine:
file_handler = logging.FileHandler(file_path, mode='w', encoding='utf8')
file_handler.setLevel(self.level)
file_handler.setFormatter(self.formatter)
self.logger.StreamHandler(file_handler)
self.logger.addHandler(file_handler)
def register_event(self):
""""""
@ -232,7 +241,7 @@ class OmsEngine:
# Otherwise, pop inactive order from in dict
elif order.vt_orderid in self.active_orders:
self.active_orders.pop(order.vt_orderid)
def process_trade_event(self, event: Event):
""""""
trade = event.data
@ -251,8 +260,8 @@ class OmsEngine:
def process_contract_event(self, event: Event):
""""""
contract = event.data
self.contracts[contract.vt_symbol]] = contract
self.contracts[contract.vt_symbol] = contract
def get_tick(self, vt_symbol):
"""
Get latest market tick data by vt_symbol.
@ -264,7 +273,7 @@ class OmsEngine:
Get latest order data by vt_orderid.
"""
return self.orders.get(vt_orderid, None)
def get_trade(self, vt_tradeid):
"""
Get trade data by vt_tradeid.
@ -288,7 +297,7 @@ class OmsEngine:
Get contract data by vt_symbol.
"""
return self.contracts.get(vt_symbol, None)
def get_all_ticks(self):
"""
Get all tick data.
@ -324,8 +333,8 @@ class OmsEngine:
Get all contract data.
"""
return list(self.contracts.values())
def get_all_active_orders(self, vt_symbol: str=''):
def get_all_active_orders(self, vt_symbol: str = ''):
"""
Get all active orders by vt_symbol.
@ -334,5 +343,8 @@ class OmsEngine:
if not vt_symbol:
return list(self.active_orders.values())
else:
active_orders = [order for order in self.active_orders.values() if order.vt_symbol == vt_symbol]
active_orders = [
order for order in self.active_orders.values()
if order.vt_symbol == vt_symbol
]
return active_orders