[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.
|
||||
"""
|
||||
|
||||
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 = "周线"
|
@ -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
|
Loading…
Reference in New Issue
Block a user