[Add] Start ib gateway development
This commit is contained in:
parent
a899060360
commit
be25b684a0
271
vnpy/gateway/ib/ib_gateway.py
Normal file
271
vnpy/gateway/ib/ib_gateway.py
Normal 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))
|
@ -2,54 +2,64 @@
|
|||||||
General constant string used in VN Trader.
|
General constant string used in VN Trader.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DIRECTION_LONG = '多'
|
DIRECTION_LONG = "多"
|
||||||
DIRECTION_SHORT = '空'
|
DIRECTION_SHORT = "空"
|
||||||
DIRECTION_NET = '净'
|
DIRECTION_NET = "净"
|
||||||
|
|
||||||
OFFSET_OPEN = '开'
|
OFFSET_OPEN = "开"
|
||||||
OFFSET_CLOSE = '平'
|
OFFSET_CLOSE = "平"
|
||||||
OFFSET_CLOSETODAY = '平今'
|
OFFSET_CLOSETODAY = "平今"
|
||||||
OFFSET_CLOSEYESTERDAY = '平昨'
|
OFFSET_CLOSEYESTERDAY = "平昨"
|
||||||
|
|
||||||
STATUS_SUBMITTING = '提交中'
|
STATUS_SUBMITTING = "提交中"
|
||||||
STATUS_NOTTRADED = '未成交'
|
STATUS_NOTTRADED = "未成交"
|
||||||
STATUS_PARTTRADED = '部分成交'
|
STATUS_PARTTRADED = "部分成交"
|
||||||
STATUS_ALLTRADED = '全部成交'
|
STATUS_ALLTRADED = "全部成交"
|
||||||
STATUS_CANCELLED = '已撤销'
|
STATUS_CANCELLED = "已撤销"
|
||||||
STATUS_REJECTED = '拒单'
|
STATUS_REJECTED = "拒单"
|
||||||
|
|
||||||
PRODUCT_EQUITY = '股票'
|
PRODUCT_EQUITY = "股票"
|
||||||
PRODUCT_FUTURES = '期货'
|
PRODUCT_FUTURES = "期货"
|
||||||
PRODUCT_OPTION = '期权'
|
PRODUCT_OPTION = "期权"
|
||||||
PRODUCT_INDEX = '指数'
|
PRODUCT_INDEX = "指数"
|
||||||
PRODUCT_FOREX = '外汇'
|
PRODUCT_FOREX = "外汇"
|
||||||
PRODUCT_SPOT = '现货'
|
PRODUCT_SPOT = "现货"
|
||||||
|
|
||||||
PRICETYPE_LIMIT = '限价'
|
PRICETYPE_LIMIT = "限价"
|
||||||
PRICETYPE_MARKET = '市价'
|
PRICETYPE_MARKET = "市价"
|
||||||
PRICETYPE_FAK = 'FAK'
|
PRICETYPE_FAK = "FAK"
|
||||||
PRICETYPE_FOK = 'FOK'
|
PRICETYPE_FOK = "FOK"
|
||||||
|
|
||||||
OPTION_CALL = '看涨期权'
|
OPTION_CALL = "看涨期权"
|
||||||
OPTION_PUT = '看跌期权'
|
OPTION_PUT = "看跌期权"
|
||||||
|
|
||||||
EXCHANGE_SSE = 'SSE'
|
EXCHANGE_SSE = "SSE"
|
||||||
EXCHANGE_SZSE = 'SZSE'
|
EXCHANGE_SZSE = "SZSE"
|
||||||
EXCHANGE_CFFEX = 'CFFEX'
|
EXCHANGE_CFFEX = "CFFEX"
|
||||||
EXCHANGE_SHFE = 'SHFE'
|
EXCHANGE_SHFE = "SHFE"
|
||||||
EXCHANGE_CZCE = 'CZCE'
|
EXCHANGE_CZCE = "CZCE"
|
||||||
EXCHANGE_DCE = 'DCE'
|
EXCHANGE_DCE = "DCE"
|
||||||
EXCHANGE_INE = 'INE'
|
EXCHANGE_INE = "INE"
|
||||||
EXCHANGE_SGE = 'SGE'
|
EXCHANGE_SGE = "SGE"
|
||||||
|
|
||||||
CURRENCY_USD = 'USD'
|
EXCHANGE_SMART = "SMART"
|
||||||
CURRENCY_HKD = 'HKD'
|
EXCHANGE_NYMEX = "NYMEX"
|
||||||
|
EXCHANGE_GLOBEX = "GLOBEX"
|
||||||
|
EXCHANGE_IDEALPRO = "IDEALPRO"
|
||||||
|
EXCHANGE_CME = "CME"
|
||||||
|
EXCHANGE_ICE = "ICE"
|
||||||
|
EXCHANGE_HKEX = "HKEX"
|
||||||
|
EXCHANGE_HKFE = "HKFE"
|
||||||
|
|
||||||
INTERVAL_1M = '1分钟'
|
CURRENCY_USD = "USD"
|
||||||
INTERVAL_5M = '5分钟'
|
CURRENCY_HKD = "HKD"
|
||||||
INTERVAL_15M = '15分钟'
|
CURRENCY_CNY = "CNY"
|
||||||
INTERVAL_30M = '30分钟'
|
|
||||||
INTERVAL_1H = '1小时'
|
INTERVAL_1M = "1分钟"
|
||||||
INTERVAL_4H = '4小时'
|
INTERVAL_5M = "5分钟"
|
||||||
INTERVAL_DAILY = '日线'
|
INTERVAL_15M = "15分钟"
|
||||||
INTERVAL_WEEKLY = '周线'
|
INTERVAL_30M = "30分钟"
|
||||||
|
INTERVAL_1H = "1小时"
|
||||||
|
INTERVAL_4H = "4小时"
|
||||||
|
INTERVAL_DAILY = "日线"
|
||||||
|
INTERVAL_WEEKLY = "周线"
|
@ -6,7 +6,15 @@ from datetime import datetime
|
|||||||
|
|
||||||
from vnpy.event import EventEngine, Event
|
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 .object import LogData, SubscribeRequest, OrderRequest, CancelRequest
|
||||||
from .utility import Singleton, get_temp_path, check_order_active
|
from .utility import Singleton, get_temp_path, check_order_active
|
||||||
from .setting import SETTINGS
|
from .setting import SETTINGS
|
||||||
@ -31,7 +39,7 @@ class MainEngine:
|
|||||||
|
|
||||||
self.init_engines()
|
self.init_engines()
|
||||||
|
|
||||||
def init_engines:
|
def init_engines(self):
|
||||||
"""
|
"""
|
||||||
Init all engines.
|
Init all engines.
|
||||||
"""
|
"""
|
||||||
@ -47,12 +55,13 @@ class MainEngine:
|
|||||||
self.get_position = oms_engine.get_position
|
self.get_position = oms_engine.get_position
|
||||||
self.get_account = oms_engine.get_account
|
self.get_account = oms_engine.get_account
|
||||||
self.get_contract = oms_engine.get_contract
|
self.get_contract = oms_engine.get_contract
|
||||||
self.get_all_ticks = oms.get_all_ticks
|
self.get_contract = oms_engine.get_contract
|
||||||
self.get_all_orders = oms.get_all_orders
|
self.get_all_ticks = oms_engine.get_all_ticks
|
||||||
self.get_all_trades = oms.get_all_trades
|
self.get_all_orders = oms_engine.get_all_orders
|
||||||
self.get_all_positions = oms.get_all_positions
|
self.get_all_trades = oms_engine.get_all_trades
|
||||||
self.get_all_accounts = oms.get_all_accounts
|
self.get_all_positions = oms_engine.get_all_positions
|
||||||
self.get_all_active_orders = oms.get_all_active_orders
|
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):
|
def write_log(self, msg: str):
|
||||||
"""
|
"""
|
||||||
@ -172,7 +181,7 @@ class LogEngine:
|
|||||||
file_handler = logging.FileHandler(file_path, mode='w', encoding='utf8')
|
file_handler = logging.FileHandler(file_path, mode='w', encoding='utf8')
|
||||||
file_handler.setLevel(self.level)
|
file_handler.setLevel(self.level)
|
||||||
file_handler.setFormatter(self.formatter)
|
file_handler.setFormatter(self.formatter)
|
||||||
self.logger.StreamHandler(file_handler)
|
self.logger.addHandler(file_handler)
|
||||||
|
|
||||||
def register_event(self):
|
def register_event(self):
|
||||||
""""""
|
""""""
|
||||||
@ -251,7 +260,7 @@ class OmsEngine:
|
|||||||
def process_contract_event(self, event: Event):
|
def process_contract_event(self, event: Event):
|
||||||
""""""
|
""""""
|
||||||
contract = event.data
|
contract = event.data
|
||||||
self.contracts[contract.vt_symbol]] = contract
|
self.contracts[contract.vt_symbol] = contract
|
||||||
|
|
||||||
def get_tick(self, vt_symbol):
|
def get_tick(self, vt_symbol):
|
||||||
"""
|
"""
|
||||||
@ -334,5 +343,8 @@ class OmsEngine:
|
|||||||
if not vt_symbol:
|
if not vt_symbol:
|
||||||
return list(self.active_orders.values())
|
return list(self.active_orders.values())
|
||||||
else:
|
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
|
return active_orders
|
Loading…
Reference in New Issue
Block a user