[Add]ib api source code into vnpy.api
This commit is contained in:
parent
da53a5fc0b
commit
adb884e19f
18
vnpy/api/ib/__init__.py
Normal file
18
vnpy/api/ib/__init__.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
""" Package implementing the Python API for the TWS/IB Gateway """
|
||||||
|
|
||||||
|
VERSION = {
|
||||||
|
'major': 9,
|
||||||
|
'minor': 73,
|
||||||
|
'micro': 7}
|
||||||
|
|
||||||
|
|
||||||
|
def get_version_string():
|
||||||
|
version = '{major}.{minor}.{micro}'.format(**VERSION)
|
||||||
|
return version
|
||||||
|
|
||||||
|
__version__ = get_version_string()
|
47
vnpy/api/ib/account_summary_tags.py
Normal file
47
vnpy/api/ib/account_summary_tags.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class AccountSummaryTags:
|
||||||
|
AccountType = "AccountType"
|
||||||
|
NetLiquidation = "NetLiquidation"
|
||||||
|
TotalCashValue = "TotalCashValue"
|
||||||
|
SettledCash = "SettledCash"
|
||||||
|
AccruedCash = "AccruedCash"
|
||||||
|
BuyingPower = "BuyingPower"
|
||||||
|
EquityWithLoanValue = "EquityWithLoanValue"
|
||||||
|
PreviousEquityWithLoanValue = "PreviousEquityWithLoanValue"
|
||||||
|
GrossPositionValue = "GrossPositionValue"
|
||||||
|
ReqTEquity = "ReqTEquity"
|
||||||
|
ReqTMargin = "ReqTMargin"
|
||||||
|
SMA = "SMA"
|
||||||
|
InitMarginReq = "InitMarginReq"
|
||||||
|
MaintMarginReq = "MaintMarginReq"
|
||||||
|
AvailableFunds = "AvailableFunds"
|
||||||
|
ExcessLiquidity = "ExcessLiquidity"
|
||||||
|
Cushion = "Cushion"
|
||||||
|
FullInitMarginReq = "FullInitMarginReq"
|
||||||
|
FullMaintMarginReq = "FullMaintMarginReq"
|
||||||
|
FullAvailableFunds = "FullAvailableFunds"
|
||||||
|
FullExcessLiquidity = "FullExcessLiquidity"
|
||||||
|
LookAheadNextChange = "LookAheadNextChange"
|
||||||
|
LookAheadInitMarginReq = "LookAheadInitMarginReq"
|
||||||
|
LookAheadMaintMarginReq = "LookAheadMaintMarginReq"
|
||||||
|
LookAheadAvailableFunds = "LookAheadAvailableFunds"
|
||||||
|
LookAheadExcessLiquidity = "LookAheadExcessLiquidity"
|
||||||
|
HighestSeverity = "HighestSeverity"
|
||||||
|
DayTradesRemaining = "DayTradesRemaining"
|
||||||
|
Leverage = "Leverage"
|
||||||
|
|
||||||
|
AllTags = ",".join((AccountType, NetLiquidation, TotalCashValue,
|
||||||
|
SettledCash, AccruedCash, BuyingPower, EquityWithLoanValue,
|
||||||
|
PreviousEquityWithLoanValue, GrossPositionValue, ReqTEquity,
|
||||||
|
ReqTMargin, SMA, InitMarginReq, MaintMarginReq, AvailableFunds,
|
||||||
|
ExcessLiquidity , Cushion, FullInitMarginReq, FullMaintMarginReq,
|
||||||
|
FullAvailableFunds, FullExcessLiquidity,
|
||||||
|
LookAheadNextChange, LookAheadInitMarginReq, LookAheadMaintMarginReq,
|
||||||
|
LookAheadAvailableFunds, LookAheadExcessLiquidity, HighestSeverity,
|
||||||
|
DayTradesRemaining, Leverage))
|
||||||
|
|
||||||
|
|
3269
vnpy/api/ib/client.py
Normal file
3269
vnpy/api/ib/client.py
Normal file
File diff suppressed because it is too large
Load Diff
75
vnpy/api/ib/comm.py
Normal file
75
vnpy/api/ib/comm.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module has tools for implementing the IB low level messaging.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ibapi.common import UNSET_INTEGER, UNSET_DOUBLE
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def make_msg(text) -> bytes:
|
||||||
|
""" adds the length prefix """
|
||||||
|
msg = struct.pack("!I%ds" % len(text), len(text), str.encode(text))
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def make_field(val) -> str:
|
||||||
|
""" adds the NULL string terminator """
|
||||||
|
|
||||||
|
if val is None:
|
||||||
|
raise ValueError("Cannot send None to TWS")
|
||||||
|
|
||||||
|
# bool type is encoded as int
|
||||||
|
if type(val) is bool:
|
||||||
|
val = int(val)
|
||||||
|
|
||||||
|
field = str(val) + '\0'
|
||||||
|
return field
|
||||||
|
|
||||||
|
|
||||||
|
def make_field_handle_empty(val) -> str:
|
||||||
|
|
||||||
|
if val is None:
|
||||||
|
raise ValueError("Cannot send None to TWS")
|
||||||
|
|
||||||
|
if UNSET_INTEGER == val or UNSET_DOUBLE == val:
|
||||||
|
val = ""
|
||||||
|
|
||||||
|
return make_field(val)
|
||||||
|
|
||||||
|
|
||||||
|
def read_msg(buf:bytes) -> tuple:
|
||||||
|
""" first the size prefix and then the corresponding msg payload """
|
||||||
|
if len(buf) < 4:
|
||||||
|
return (0, "", buf)
|
||||||
|
size = struct.unpack("!I", buf[0:4])[0]
|
||||||
|
logger.debug("read_msg: size: %d", size)
|
||||||
|
if len(buf) - 4 >= size:
|
||||||
|
text = struct.unpack("!%ds" % size, buf[4:4+size])[0]
|
||||||
|
return (size, text, buf[4+size:])
|
||||||
|
else:
|
||||||
|
return (size, "", buf)
|
||||||
|
|
||||||
|
|
||||||
|
def read_fields(buf:bytes) -> tuple:
|
||||||
|
|
||||||
|
if isinstance(buf, str):
|
||||||
|
buf = buf.encode()
|
||||||
|
|
||||||
|
""" msg payload is made of fields terminated/separated by NULL chars """
|
||||||
|
fields = buf.split(b"\0")
|
||||||
|
|
||||||
|
return tuple(fields[0:-1]) #last one is empty; this may slow dow things though, TODO
|
||||||
|
|
||||||
|
|
||||||
|
|
21
vnpy/api/ib/commission_report.py
Normal file
21
vnpy/api/ib/commission_report.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
from ibapi import utils
|
||||||
|
|
||||||
|
class CommissionReport(Object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.execId = ""
|
||||||
|
self.commission = 0.
|
||||||
|
self.currency = ""
|
||||||
|
self.realizedPNL = 0.
|
||||||
|
self.yield_ = 0.
|
||||||
|
self.yieldRedemptionDate = 0 # YYYYMMDD format
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "ExecId: %s, Commission: %f, Currency: %s, RealizedPnL: %s, Yield: %s, YieldRedemptionDate: %d" % (self.execId, self.commission,
|
||||||
|
self.currency, utils.floatToStr(self.realizedPNL), utils.floatToStr(self.yield_), self.yieldRedemptionDate)
|
190
vnpy/api/ib/common.py
Normal file
190
vnpy/api/ib/common.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ibapi.enum_implem import Enum
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
|
||||||
|
|
||||||
|
NO_VALID_ID = -1
|
||||||
|
MAX_MSG_LEN = 0xFFFFFF # 16Mb - 1byte
|
||||||
|
|
||||||
|
UNSET_INTEGER = 2 ** 31 - 1
|
||||||
|
UNSET_DOUBLE = sys.float_info.max
|
||||||
|
|
||||||
|
TickerId = int
|
||||||
|
OrderId = int
|
||||||
|
TagValueList = list
|
||||||
|
|
||||||
|
FaDataType = int
|
||||||
|
FaDataTypeEnum = Enum("N/A", "GROUPS", "PROFILES", "ALIASES")
|
||||||
|
|
||||||
|
MarketDataType = int
|
||||||
|
MarketDataTypeEnum = Enum("N/A", "REALTIME", "FROZEN", "DELAYED", "DELAYED_FROZEN")
|
||||||
|
|
||||||
|
Liquidities = int
|
||||||
|
LiquiditiesEnum = Enum("None", "Added", "Remove", "RoudedOut")
|
||||||
|
|
||||||
|
SetOfString = set
|
||||||
|
SetOfFloat = set
|
||||||
|
ListOfOrder = list
|
||||||
|
ListOfFamilyCode = list
|
||||||
|
ListOfContractDescription = list
|
||||||
|
ListOfDepthExchanges = list
|
||||||
|
ListOfNewsProviders = list
|
||||||
|
SmartComponentMap = dict
|
||||||
|
HistogramDataList = list
|
||||||
|
ListOfPriceIncrements = list
|
||||||
|
ListOfHistoricalTick = list
|
||||||
|
ListOfHistoricalTickBidAsk = list
|
||||||
|
ListOfHistoricalTickLast = list
|
||||||
|
|
||||||
|
class BarData(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.date = ""
|
||||||
|
self.open = 0.
|
||||||
|
self.high = 0.
|
||||||
|
self.low = 0.
|
||||||
|
self.close = 0.
|
||||||
|
self.volume = 0
|
||||||
|
self.barCount = 0
|
||||||
|
self.average = 0.
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Date: %s, Open: %f, High: %f, Low: %f, Close: %f, Volume: %d, Average: %f, BarCount: %d" % (self.date, self.open, self.high,
|
||||||
|
self.low, self.close, self.volume, self.average, self.barCount)
|
||||||
|
|
||||||
|
class RealTimeBar(Object):
|
||||||
|
def __init__(self, time = 0, endTime = -1, open_ = 0., high = 0., low = 0., close = 0., volume = 0., wap = 0., count = 0):
|
||||||
|
self.time = time
|
||||||
|
self.endTime = endTime
|
||||||
|
self.open_ = open_
|
||||||
|
self.high = high
|
||||||
|
self.low = low
|
||||||
|
self.close = close
|
||||||
|
self.volume = volume
|
||||||
|
self.wap = wap
|
||||||
|
self.count = count
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Time: %d, Open: %f, High: %f, Low: %f, Close: %f, Volume: %d, WAP: %f, Count: %d" % (self.time, self.open_, self.high,
|
||||||
|
self.low, self.close, self.volume, self.wap, self.count)
|
||||||
|
|
||||||
|
class HistogramData(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.price = 0.
|
||||||
|
self.count = 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Price: %f, Count: %d" % (self.price, self.count)
|
||||||
|
|
||||||
|
class NewsProvider(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.code = ""
|
||||||
|
self.name = ""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Code: %s, Name: %s" % (self.code, self.name)
|
||||||
|
|
||||||
|
class DepthMktDataDescription(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.exchange = ""
|
||||||
|
self.secType = ""
|
||||||
|
self.listingExch = ""
|
||||||
|
self.serviceDataType = ""
|
||||||
|
self.aggGroup = UNSET_INTEGER
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if (self.aggGroup!= UNSET_INTEGER):
|
||||||
|
aggGroup = self.aggGroup
|
||||||
|
else:
|
||||||
|
aggGroup = ""
|
||||||
|
return "Exchange: %s, SecType: %s, ListingExchange: %s, ServiceDataType: %s, AggGroup: %s, " % (self.exchange, self.secType, self.listingExch,self.serviceDataType, aggGroup)
|
||||||
|
|
||||||
|
class SmartComponent(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.bitNumber = 0
|
||||||
|
self.exchange = ""
|
||||||
|
self.exchangeLetter = ""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "BitNumber: %d, Exchange: %s, ExchangeLetter: %s" % (self.bitNumber, self.exchange, self.exchangeLetter)
|
||||||
|
|
||||||
|
class TickAttrib(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.canAutoExecute = False
|
||||||
|
self.pastLimit = False
|
||||||
|
self.preOpen = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "CanAutoExecute: %d, PastLimit: %d, PreOpen: %d" % (self.canAutoExecute, self.pastLimit, self.preOpen)
|
||||||
|
|
||||||
|
class TickAttribBidAsk(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.bidPastLow = False
|
||||||
|
self.askPastHigh = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "BidPastLow: %d, AskPastHigh: %d" % (self.bidPastLow, self.askPastHigh)
|
||||||
|
|
||||||
|
class TickAttribLast(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.pastLimit = False
|
||||||
|
self.unreported = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "PastLimit: %d, Unreported: %d" % (self.pastLimit, self.unreported)
|
||||||
|
|
||||||
|
class FamilyCode(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.accountID = ""
|
||||||
|
self.familyCodeStr = ""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "AccountId: %s, FamilyCodeStr: %s" % (self.accountID, self.familyCodeStr)
|
||||||
|
|
||||||
|
class PriceIncrement(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.lowEdge = 0.
|
||||||
|
self.increment = 0.
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "LowEdge: %f, Increment: %f" % (self.lowEdge, self.increment)
|
||||||
|
|
||||||
|
class HistoricalTick(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.time = 0
|
||||||
|
self.price = 0.
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Time: %d, Price: %f, Size: %d" % (self.time, self.price, self.size)
|
||||||
|
|
||||||
|
class HistoricalTickBidAsk(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.time = 0
|
||||||
|
self.tickAttribBidAsk = TickAttribBidAsk()
|
||||||
|
self.priceBid = 0.
|
||||||
|
self.priceAsk = 0.
|
||||||
|
self.sizeBid = 0
|
||||||
|
self.sizeAsk = 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Time: %d, TickAttriBidAsk: %s, PriceBid: %f, PriceAsk: %f, SizeBid: %d, SizeAsk: %d" % (self.time, self.tickAttribBidAsk, self.priceBid, self.priceAsk, self.sizeBid, self.sizeAsk)
|
||||||
|
|
||||||
|
class HistoricalTickLast(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.time = 0
|
||||||
|
self.tickAttribLast = TickAttribLast()
|
||||||
|
self.price = 0.
|
||||||
|
self.size = 0
|
||||||
|
self.exchange = ""
|
||||||
|
self.specialConditions = ""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Time: %d, TickAttribLast: %s, Price: %f, Size: %d, Exchange: %s, SpecialConditions: %s" % (self.time, self.tickAttribLast, self.price, self.size, self.exchange, self.specialConditions)
|
||||||
|
|
||||||
|
|
122
vnpy/api/ib/connection.py
Normal file
122
vnpy/api/ib/connection.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Just a thin wrapper around a socket.
|
||||||
|
It allows us to keep some other info along with it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ibapi.common import * # @UnusedWildImport
|
||||||
|
from ibapi.errors import * # @UnusedWildImport
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: support SSL !!
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def __init__(self, host, port):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.socket = None
|
||||||
|
self.wrapper = None
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
try:
|
||||||
|
self.socket = socket.socket()
|
||||||
|
#TODO: list the exceptions you want to catch
|
||||||
|
except socket.error:
|
||||||
|
if self.wrapper:
|
||||||
|
self.wrapper.error(NO_VALID_ID, FAIL_CREATE_SOCK.code(), FAIL_CREATE_SOCK.msg())
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.socket.connect((self.host, self.port))
|
||||||
|
except socket.error:
|
||||||
|
if self.wrapper:
|
||||||
|
self.wrapper.error(NO_VALID_ID, CONNECT_FAIL.code(), CONNECT_FAIL.msg())
|
||||||
|
|
||||||
|
self.socket.settimeout(1) #non-blocking
|
||||||
|
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
self.lock.acquire()
|
||||||
|
try:
|
||||||
|
logger.debug("disconnecting")
|
||||||
|
self.socket.close()
|
||||||
|
self.socket = None
|
||||||
|
logger.debug("disconnected")
|
||||||
|
if self.wrapper:
|
||||||
|
self.wrapper.connectionClosed()
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
def isConnected(self):
|
||||||
|
#TODO: also handle when socket gets interrupted/error
|
||||||
|
return self.socket is not None
|
||||||
|
|
||||||
|
|
||||||
|
def sendMsg(self, msg):
|
||||||
|
|
||||||
|
logger.debug("acquiring lock")
|
||||||
|
self.lock.acquire()
|
||||||
|
logger.debug("acquired lock")
|
||||||
|
if not self.isConnected():
|
||||||
|
logger.debug("sendMsg attempted while not connected, releasing lock")
|
||||||
|
self.lock.release()
|
||||||
|
return 0
|
||||||
|
try:
|
||||||
|
nSent = self.socket.send(msg)
|
||||||
|
except socket.error:
|
||||||
|
logger.debug("exception from sendMsg %s", sys.exc_info())
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
logger.debug("releasing lock")
|
||||||
|
self.lock.release()
|
||||||
|
logger.debug("release lock")
|
||||||
|
|
||||||
|
logger.debug("sendMsg: sent: %d", nSent)
|
||||||
|
|
||||||
|
return nSent
|
||||||
|
|
||||||
|
|
||||||
|
def recvMsg(self):
|
||||||
|
if not self.isConnected():
|
||||||
|
logger.debug("recvMsg attempted while not connected, releasing lock")
|
||||||
|
return b""
|
||||||
|
try:
|
||||||
|
buf = self._recvAllMsg()
|
||||||
|
except socket.error:
|
||||||
|
logger.debug("exception from recvMsg %s", sys.exc_info())
|
||||||
|
buf = b""
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return buf
|
||||||
|
|
||||||
|
|
||||||
|
def _recvAllMsg(self):
|
||||||
|
cont = True
|
||||||
|
allbuf = b""
|
||||||
|
|
||||||
|
while cont and self.socket is not None:
|
||||||
|
buf = self.socket.recv(4096)
|
||||||
|
allbuf += buf
|
||||||
|
logger.debug("len %d raw:%s|", len(buf), buf)
|
||||||
|
|
||||||
|
if len(buf) < 4096:
|
||||||
|
cont = False
|
||||||
|
|
||||||
|
return allbuf
|
||||||
|
|
205
vnpy/api/ib/contract.py
Normal file
205
vnpy/api/ib/contract.py
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
SAME_POS = open/close leg value is same as combo
|
||||||
|
OPEN_POS = open
|
||||||
|
CLOSE_POS = close
|
||||||
|
UNKNOWN_POS = unknown
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
|
||||||
|
|
||||||
|
(SAME_POS, OPEN_POS, CLOSE_POS, UNKNOWN_POS) = range(4)
|
||||||
|
|
||||||
|
|
||||||
|
class ComboLeg(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.conId = 0 # type: int
|
||||||
|
self.ratio = 0 # type: int
|
||||||
|
self.action = "" # BUY/SELL/SSHORT
|
||||||
|
self.exchange = ""
|
||||||
|
self.openClose = 0 # type: int; LegOpenClose enum values
|
||||||
|
# for stock legs when doing short sale
|
||||||
|
self.shortSaleSlot = 0
|
||||||
|
self.designatedLocation = ""
|
||||||
|
self.exemptCode = -1
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ",".join((
|
||||||
|
str(self.conId),
|
||||||
|
str(self.ratio),
|
||||||
|
str(self.action),
|
||||||
|
str(self.exchange),
|
||||||
|
str(self.openClose),
|
||||||
|
str(self.shortSaleSlot),
|
||||||
|
str(self.designatedLocation),
|
||||||
|
str(self.exemptCode)))
|
||||||
|
|
||||||
|
|
||||||
|
class DeltaNeutralContract(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.conId = 0 # type: int
|
||||||
|
self.delta = 0. # type: float
|
||||||
|
self.price = 0. # type: float
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ",".join((
|
||||||
|
str(self.conId),
|
||||||
|
str(self.delta),
|
||||||
|
str(self.price)))
|
||||||
|
|
||||||
|
|
||||||
|
class Contract(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.conId = 0
|
||||||
|
self.symbol = ""
|
||||||
|
self.secType = ""
|
||||||
|
self.lastTradeDateOrContractMonth = ""
|
||||||
|
self.strike = 0. # float !!
|
||||||
|
self.right = ""
|
||||||
|
self.multiplier = ""
|
||||||
|
self.exchange = ""
|
||||||
|
self.primaryExchange = "" # pick an actual (ie non-aggregate) exchange that the contract trades on. DO NOT SET TO SMART.
|
||||||
|
self.currency = ""
|
||||||
|
self.localSymbol = ""
|
||||||
|
self.tradingClass = ""
|
||||||
|
self.includeExpired = False
|
||||||
|
self.secIdType = "" # CUSIP;SEDOL;ISIN;RIC
|
||||||
|
self.secId = ""
|
||||||
|
|
||||||
|
#combos
|
||||||
|
self.comboLegsDescrip = "" # type: str; received in open order 14 and up for all combos
|
||||||
|
self.comboLegs = None # type: list<ComboLeg>
|
||||||
|
self.deltaNeutralContract = None
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = ",".join((
|
||||||
|
str(self.conId),
|
||||||
|
str(self.symbol),
|
||||||
|
str(self.secType),
|
||||||
|
str(self.lastTradeDateOrContractMonth),
|
||||||
|
str(self.strike),
|
||||||
|
str(self.right),
|
||||||
|
str(self.multiplier),
|
||||||
|
str(self.exchange),
|
||||||
|
str(self.primaryExchange),
|
||||||
|
str(self.currency),
|
||||||
|
str(self.localSymbol),
|
||||||
|
str(self.tradingClass),
|
||||||
|
str(self.includeExpired),
|
||||||
|
str(self.secIdType),
|
||||||
|
str(self.secId)))
|
||||||
|
s += "combo:" + self.comboLegsDescrip
|
||||||
|
|
||||||
|
if self.comboLegs:
|
||||||
|
for leg in self.comboLegs:
|
||||||
|
s += ";" + str(leg)
|
||||||
|
|
||||||
|
if self.deltaNeutralContract:
|
||||||
|
s += ";" + str(self.deltaNeutralContract)
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
class ContractDetails(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.contract = Contract()
|
||||||
|
self.marketName = ""
|
||||||
|
self.minTick = 0.
|
||||||
|
self.orderTypes = ""
|
||||||
|
self.validExchanges = ""
|
||||||
|
self.priceMagnifier = 0
|
||||||
|
self.underConId = 0
|
||||||
|
self.longName = ""
|
||||||
|
self.contractMonth = ""
|
||||||
|
self.industry = ""
|
||||||
|
self.category = ""
|
||||||
|
self.subcategory = ""
|
||||||
|
self.timeZoneId = ""
|
||||||
|
self.tradingHours = ""
|
||||||
|
self.liquidHours = ""
|
||||||
|
self.evRule = ""
|
||||||
|
self.evMultiplier = 0
|
||||||
|
self.mdSizeMultiplier = 0
|
||||||
|
self.aggGroup = 0
|
||||||
|
self.underSymbol = ""
|
||||||
|
self.underSecType = ""
|
||||||
|
self.marketRuleIds = ""
|
||||||
|
self.secIdList = None
|
||||||
|
self.realExpirationDate = ""
|
||||||
|
self.lastTradeTime = ""
|
||||||
|
# BOND values
|
||||||
|
self.cusip = ""
|
||||||
|
self.ratings = ""
|
||||||
|
self.descAppend = ""
|
||||||
|
self.bondType = ""
|
||||||
|
self.couponType = ""
|
||||||
|
self.callable = False
|
||||||
|
self.putable = False
|
||||||
|
self.coupon = 0
|
||||||
|
self.convertible = False
|
||||||
|
self.maturity = ""
|
||||||
|
self.issueDate = ""
|
||||||
|
self.nextOptionDate = ""
|
||||||
|
self.nextOptionType = ""
|
||||||
|
self.nextOptionPartial = False
|
||||||
|
self.notes = ""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = ",".join((
|
||||||
|
str(self.contract),
|
||||||
|
str(self.marketName),
|
||||||
|
str(self.minTick),
|
||||||
|
str(self.orderTypes),
|
||||||
|
str(self.validExchanges),
|
||||||
|
str(self.priceMagnifier),
|
||||||
|
str(self.underConId),
|
||||||
|
str(self.longName),
|
||||||
|
str(self.contractMonth),
|
||||||
|
str(self.industry),
|
||||||
|
str(self.category),
|
||||||
|
str(self.subcategory),
|
||||||
|
str(self.timeZoneId),
|
||||||
|
str(self.tradingHours),
|
||||||
|
str(self.liquidHours),
|
||||||
|
str(self.evRule),
|
||||||
|
str(self.evMultiplier),
|
||||||
|
str(self.mdSizeMultiplier),
|
||||||
|
str(self.underSymbol),
|
||||||
|
str(self.underSecType),
|
||||||
|
str(self.marketRuleIds),
|
||||||
|
str(self.aggGroup),
|
||||||
|
str(self.secIdList),
|
||||||
|
str(self.realExpirationDate),
|
||||||
|
str(self.cusip),
|
||||||
|
str(self.ratings),
|
||||||
|
str(self.descAppend),
|
||||||
|
str(self.bondType),
|
||||||
|
str(self.couponType),
|
||||||
|
str(self.callable),
|
||||||
|
str(self.putable),
|
||||||
|
str(self.coupon),
|
||||||
|
str(self.convertible),
|
||||||
|
str(self.maturity),
|
||||||
|
str(self.issueDate),
|
||||||
|
str(self.nextOptionDate),
|
||||||
|
str(self.nextOptionType),
|
||||||
|
str(self.nextOptionPartial),
|
||||||
|
str(self.notes)))
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
class ContractDescription(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.contract = Contract()
|
||||||
|
self.derivativeSecTypes = None # type: list of strings
|
||||||
|
|
||||||
|
|
1512
vnpy/api/ib/decoder.py
Normal file
1512
vnpy/api/ib/decoder.py
Normal file
File diff suppressed because it is too large
Load Diff
22
vnpy/api/ib/enum_implem.py
Normal file
22
vnpy/api/ib/enum_implem.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Simple enum implementation
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Enum:
|
||||||
|
def __init__(self, *args):
|
||||||
|
self.idx2name = {}
|
||||||
|
for (idx, name) in enumerate(args):
|
||||||
|
setattr(self, name, idx)
|
||||||
|
self.idx2name[idx] = name
|
||||||
|
|
||||||
|
def to_str(self, idx):
|
||||||
|
return self.idx2name.get(idx, "NOTFOUND")
|
||||||
|
|
||||||
|
|
41
vnpy/api/ib/errors.py
Normal file
41
vnpy/api/ib/errors.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is the interface that will need to be overloaded by the customer so
|
||||||
|
that his/her code can receive info from the TWS/IBGW.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CodeMsgPair:
|
||||||
|
def __init__(self, code, msg):
|
||||||
|
self.errorCode = code
|
||||||
|
self.errorMsg = msg
|
||||||
|
|
||||||
|
def code(self):
|
||||||
|
return self.errorCode
|
||||||
|
|
||||||
|
def msg(self):
|
||||||
|
return self.errorMsg
|
||||||
|
|
||||||
|
|
||||||
|
ALREADY_CONNECTED = CodeMsgPair(501, "Already connected.")
|
||||||
|
CONNECT_FAIL = CodeMsgPair(502,
|
||||||
|
"""Couldn't connect to TWS. Confirm that \"Enable ActiveX and Socket EClients\"
|
||||||
|
is enabled and connection port is the same as \"Socket Port\" on the
|
||||||
|
TWS \"Edit->Global Configuration...->API->Settings\" menu. Live Trading ports:
|
||||||
|
TWS: 7496; IB Gateway: 4001. Simulated Trading ports for new installations
|
||||||
|
of version 954.1 or newer: TWS: 7497; IB Gateway: 4002""")
|
||||||
|
UPDATE_TWS = CodeMsgPair(503, "The TWS is out of date and must be upgraded.")
|
||||||
|
NOT_CONNECTED = CodeMsgPair(504, "Not connected")
|
||||||
|
UNKNOWN_ID = CodeMsgPair(505, "Fatal Error: Unknown message id.")
|
||||||
|
UNSUPPORTED_VERSION = CodeMsgPair(506, "Unsupported version")
|
||||||
|
BAD_LENGTH = CodeMsgPair(507, "Bad message length")
|
||||||
|
BAD_MESSAGE = CodeMsgPair(508, "Bad message")
|
||||||
|
SOCKET_EXCEPTION = CodeMsgPair(509, "Exception caught while reading socket - ")
|
||||||
|
FAIL_CREATE_SOCK = CodeMsgPair(520, "Failed to create socket")
|
||||||
|
SSL_FAIL = CodeMsgPair(530, "SSL specific error: ")
|
||||||
|
|
50
vnpy/api/ib/execution.py
Normal file
50
vnpy/api/ib/execution.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
|
||||||
|
class Execution(Object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.execId = ""
|
||||||
|
self.time = ""
|
||||||
|
self.acctNumber = ""
|
||||||
|
self.exchange = ""
|
||||||
|
self.side = ""
|
||||||
|
self.shares = 0.
|
||||||
|
self.price = 0.
|
||||||
|
self.permId = 0
|
||||||
|
self.clientId = 0
|
||||||
|
self.orderId = 0
|
||||||
|
self.liquidation = 0
|
||||||
|
self.cumQty = 0.
|
||||||
|
self.avgPrice = 0.
|
||||||
|
self.orderRef = ""
|
||||||
|
self.evRule = ""
|
||||||
|
self.evMultiplier = 0.
|
||||||
|
self.modelCode = ""
|
||||||
|
self.lastLiquidity = 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "ExecId: %s, Time: %s, Account: %s, Exchange: %s, Side: %s, Shares: %f, Price: %f, PermId: %d, " \
|
||||||
|
"ClientId: %d, OrderId: %d, Liquidation: %d, CumQty: %f, AvgPrice: %f, OrderRef: %s, EvRule: %s, " \
|
||||||
|
"EvMultiplier: %f, ModelCode: %s, LastLiquidity: %d" % (self.execId, self.time, self.acctNumber,
|
||||||
|
self.exchange, self.side, self.shares, self.price, self.permId, self.clientId, self.orderId, self.liquidation,
|
||||||
|
self.cumQty, self.avgPrice, self.orderRef, self.evRule, self.evMultiplier, self.modelCode, self.lastLiquidity)
|
||||||
|
|
||||||
|
class ExecutionFilter(Object):
|
||||||
|
|
||||||
|
# Filter fields
|
||||||
|
def __init__(self):
|
||||||
|
self.clientId = 0
|
||||||
|
self.acctCode = ""
|
||||||
|
self.time = ""
|
||||||
|
self.symbol = ""
|
||||||
|
self.secType = ""
|
||||||
|
self.exchange = ""
|
||||||
|
self.side = ""
|
||||||
|
|
171
vnpy/api/ib/message.py
Normal file
171
vnpy/api/ib/message.py
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
High level IB message info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# field types
|
||||||
|
INT = 1
|
||||||
|
STR = 2
|
||||||
|
FLT = 3
|
||||||
|
|
||||||
|
|
||||||
|
# incoming msg id's
|
||||||
|
class IN:
|
||||||
|
TICK_PRICE = 1
|
||||||
|
TICK_SIZE = 2
|
||||||
|
ORDER_STATUS = 3
|
||||||
|
ERR_MSG = 4
|
||||||
|
OPEN_ORDER = 5
|
||||||
|
ACCT_VALUE = 6
|
||||||
|
PORTFOLIO_VALUE = 7
|
||||||
|
ACCT_UPDATE_TIME = 8
|
||||||
|
NEXT_VALID_ID = 9
|
||||||
|
CONTRACT_DATA = 10
|
||||||
|
EXECUTION_DATA = 11
|
||||||
|
MARKET_DEPTH = 12
|
||||||
|
MARKET_DEPTH_L2 = 13
|
||||||
|
NEWS_BULLETINS = 14
|
||||||
|
MANAGED_ACCTS = 15
|
||||||
|
RECEIVE_FA = 16
|
||||||
|
HISTORICAL_DATA = 17
|
||||||
|
BOND_CONTRACT_DATA = 18
|
||||||
|
SCANNER_PARAMETERS = 19
|
||||||
|
SCANNER_DATA = 20
|
||||||
|
TICK_OPTION_COMPUTATION = 21
|
||||||
|
TICK_GENERIC = 45
|
||||||
|
TICK_STRING = 46
|
||||||
|
TICK_EFP = 47
|
||||||
|
CURRENT_TIME = 49
|
||||||
|
REAL_TIME_BARS = 50
|
||||||
|
FUNDAMENTAL_DATA = 51
|
||||||
|
CONTRACT_DATA_END = 52
|
||||||
|
OPEN_ORDER_END = 53
|
||||||
|
ACCT_DOWNLOAD_END = 54
|
||||||
|
EXECUTION_DATA_END = 55
|
||||||
|
DELTA_NEUTRAL_VALIDATION = 56
|
||||||
|
TICK_SNAPSHOT_END = 57
|
||||||
|
MARKET_DATA_TYPE = 58
|
||||||
|
COMMISSION_REPORT = 59
|
||||||
|
POSITION_DATA = 61
|
||||||
|
POSITION_END = 62
|
||||||
|
ACCOUNT_SUMMARY = 63
|
||||||
|
ACCOUNT_SUMMARY_END = 64
|
||||||
|
VERIFY_MESSAGE_API = 65
|
||||||
|
VERIFY_COMPLETED = 66
|
||||||
|
DISPLAY_GROUP_LIST = 67
|
||||||
|
DISPLAY_GROUP_UPDATED = 68
|
||||||
|
VERIFY_AND_AUTH_MESSAGE_API = 69
|
||||||
|
VERIFY_AND_AUTH_COMPLETED = 70
|
||||||
|
POSITION_MULTI = 71
|
||||||
|
POSITION_MULTI_END = 72
|
||||||
|
ACCOUNT_UPDATE_MULTI = 73
|
||||||
|
ACCOUNT_UPDATE_MULTI_END = 74
|
||||||
|
SECURITY_DEFINITION_OPTION_PARAMETER = 75
|
||||||
|
SECURITY_DEFINITION_OPTION_PARAMETER_END = 76
|
||||||
|
SOFT_DOLLAR_TIERS = 77
|
||||||
|
FAMILY_CODES = 78
|
||||||
|
SYMBOL_SAMPLES = 79
|
||||||
|
MKT_DEPTH_EXCHANGES = 80
|
||||||
|
TICK_REQ_PARAMS = 81
|
||||||
|
SMART_COMPONENTS = 82
|
||||||
|
NEWS_ARTICLE = 83
|
||||||
|
TICK_NEWS = 84
|
||||||
|
NEWS_PROVIDERS = 85
|
||||||
|
HISTORICAL_NEWS = 86
|
||||||
|
HISTORICAL_NEWS_END = 87
|
||||||
|
HEAD_TIMESTAMP = 88
|
||||||
|
HISTOGRAM_DATA = 89
|
||||||
|
HISTORICAL_DATA_UPDATE = 90
|
||||||
|
REROUTE_MKT_DATA_REQ = 91
|
||||||
|
REROUTE_MKT_DEPTH_REQ = 92
|
||||||
|
MARKET_RULE = 93
|
||||||
|
PNL = 94
|
||||||
|
PNL_SINGLE = 95
|
||||||
|
HISTORICAL_TICKS = 96
|
||||||
|
HISTORICAL_TICKS_BID_ASK = 97
|
||||||
|
HISTORICAL_TICKS_LAST = 98
|
||||||
|
TICK_BY_TICK = 99
|
||||||
|
ORDER_BOUND = 100
|
||||||
|
|
||||||
|
# outgoing msg id's
|
||||||
|
class OUT:
|
||||||
|
REQ_MKT_DATA = 1
|
||||||
|
CANCEL_MKT_DATA = 2
|
||||||
|
PLACE_ORDER = 3
|
||||||
|
CANCEL_ORDER = 4
|
||||||
|
REQ_OPEN_ORDERS = 5
|
||||||
|
REQ_ACCT_DATA = 6
|
||||||
|
REQ_EXECUTIONS = 7
|
||||||
|
REQ_IDS = 8
|
||||||
|
REQ_CONTRACT_DATA = 9
|
||||||
|
REQ_MKT_DEPTH = 10
|
||||||
|
CANCEL_MKT_DEPTH = 11
|
||||||
|
REQ_NEWS_BULLETINS = 12
|
||||||
|
CANCEL_NEWS_BULLETINS = 13
|
||||||
|
SET_SERVER_LOGLEVEL = 14
|
||||||
|
REQ_AUTO_OPEN_ORDERS = 15
|
||||||
|
REQ_ALL_OPEN_ORDERS = 16
|
||||||
|
REQ_MANAGED_ACCTS = 17
|
||||||
|
REQ_FA = 18
|
||||||
|
REPLACE_FA = 19
|
||||||
|
REQ_HISTORICAL_DATA = 20
|
||||||
|
EXERCISE_OPTIONS = 21
|
||||||
|
REQ_SCANNER_SUBSCRIPTION = 22
|
||||||
|
CANCEL_SCANNER_SUBSCRIPTION = 23
|
||||||
|
REQ_SCANNER_PARAMETERS = 24
|
||||||
|
CANCEL_HISTORICAL_DATA = 25
|
||||||
|
REQ_CURRENT_TIME = 49
|
||||||
|
REQ_REAL_TIME_BARS = 50
|
||||||
|
CANCEL_REAL_TIME_BARS = 51
|
||||||
|
REQ_FUNDAMENTAL_DATA = 52
|
||||||
|
CANCEL_FUNDAMENTAL_DATA = 53
|
||||||
|
REQ_CALC_IMPLIED_VOLAT = 54
|
||||||
|
REQ_CALC_OPTION_PRICE = 55
|
||||||
|
CANCEL_CALC_IMPLIED_VOLAT = 56
|
||||||
|
CANCEL_CALC_OPTION_PRICE = 57
|
||||||
|
REQ_GLOBAL_CANCEL = 58
|
||||||
|
REQ_MARKET_DATA_TYPE = 59
|
||||||
|
REQ_POSITIONS = 61
|
||||||
|
REQ_ACCOUNT_SUMMARY = 62
|
||||||
|
CANCEL_ACCOUNT_SUMMARY = 63
|
||||||
|
CANCEL_POSITIONS = 64
|
||||||
|
VERIFY_REQUEST = 65
|
||||||
|
VERIFY_MESSAGE = 66
|
||||||
|
QUERY_DISPLAY_GROUPS = 67
|
||||||
|
SUBSCRIBE_TO_GROUP_EVENTS = 68
|
||||||
|
UPDATE_DISPLAY_GROUP = 69
|
||||||
|
UNSUBSCRIBE_FROM_GROUP_EVENTS = 70
|
||||||
|
START_API = 71
|
||||||
|
VERIFY_AND_AUTH_REQUEST = 72
|
||||||
|
VERIFY_AND_AUTH_MESSAGE = 73
|
||||||
|
REQ_POSITIONS_MULTI = 74
|
||||||
|
CANCEL_POSITIONS_MULTI = 75
|
||||||
|
REQ_ACCOUNT_UPDATES_MULTI = 76
|
||||||
|
CANCEL_ACCOUNT_UPDATES_MULTI = 77
|
||||||
|
REQ_SEC_DEF_OPT_PARAMS = 78
|
||||||
|
REQ_SOFT_DOLLAR_TIERS = 79
|
||||||
|
REQ_FAMILY_CODES = 80
|
||||||
|
REQ_MATCHING_SYMBOLS = 81
|
||||||
|
REQ_MKT_DEPTH_EXCHANGES = 82
|
||||||
|
REQ_SMART_COMPONENTS = 83
|
||||||
|
REQ_NEWS_ARTICLE = 84
|
||||||
|
REQ_NEWS_PROVIDERS = 85
|
||||||
|
REQ_HISTORICAL_NEWS = 86
|
||||||
|
REQ_HEAD_TIMESTAMP = 87
|
||||||
|
REQ_HISTOGRAM_DATA = 88
|
||||||
|
CANCEL_HISTOGRAM_DATA = 89
|
||||||
|
CANCEL_HEAD_TIMESTAMP = 90
|
||||||
|
REQ_MARKET_RULE = 91
|
||||||
|
REQ_PNL = 92
|
||||||
|
CANCEL_PNL = 93
|
||||||
|
REQ_PNL_SINGLE = 94
|
||||||
|
CANCEL_PNL_SINGLE = 95
|
||||||
|
REQ_HISTORICAL_TICKS = 96
|
||||||
|
REQ_TICK_BY_TICK_DATA = 97
|
||||||
|
CANCEL_TICK_BY_TICK_DATA = 98
|
10
vnpy/api/ib/news.py
Normal file
10
vnpy/api/ib/news.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TWS New Bulletins constants
|
||||||
|
NEWS_MSG = 1 # standard IB news bulleting message
|
||||||
|
EXCHANGE_AVAIL_MSG = 2 # control message specifing that an exchange is available for trading
|
||||||
|
EXCHANGE_UNAVAIL_MSG = 3 # control message specifing that an exchange is unavailable for trading
|
||||||
|
|
14
vnpy/api/ib/object_implem.py
Normal file
14
vnpy/api/ib/object_implem.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Object(object):
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Object"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(id(self)) + ": " + self.__str__()
|
||||||
|
|
||||||
|
|
226
vnpy/api/ib/order.py
Normal file
226
vnpy/api/ib/order.py
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from ibapi.common import UNSET_INTEGER, UNSET_DOUBLE
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
from ibapi.softdollartier import SoftDollarTier
|
||||||
|
|
||||||
|
# enum Origin
|
||||||
|
(CUSTOMER, FIRM, UNKNOWN) = range(3)
|
||||||
|
|
||||||
|
# enum AuctionStrategy
|
||||||
|
(AUCTION_UNSET, AUCTION_MATCH,
|
||||||
|
AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT) = range(4)
|
||||||
|
|
||||||
|
|
||||||
|
class OrderComboLeg(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.price = UNSET_DOUBLE # type: float
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%f" % self.price
|
||||||
|
|
||||||
|
|
||||||
|
class Order(Object):
|
||||||
|
def __init__(self):
|
||||||
|
self.softDollarTier = SoftDollarTier("", "", "")
|
||||||
|
# order identifier
|
||||||
|
self.orderId = 0
|
||||||
|
self.clientId = 0
|
||||||
|
self.permId = 0
|
||||||
|
|
||||||
|
# main order fields
|
||||||
|
self.action = ""
|
||||||
|
self.totalQuantity = 0
|
||||||
|
self.orderType = ""
|
||||||
|
self.lmtPrice = UNSET_DOUBLE
|
||||||
|
self.auxPrice = UNSET_DOUBLE
|
||||||
|
|
||||||
|
# extended order fields
|
||||||
|
self.tif = "" # "Time in Force" - DAY, GTC, etc.
|
||||||
|
self.activeStartTime = "" # for GTC orders
|
||||||
|
self.activeStopTime = "" # for GTC orders
|
||||||
|
self.ocaGroup = "" # one cancels all group name
|
||||||
|
self.ocaType = 0 # 1 = CANCEL_WITH_BLOCK, 2 = REDUCE_WITH_BLOCK, 3 = REDUCE_NON_BLOCK
|
||||||
|
self.orderRef = ""
|
||||||
|
self.transmit = True # if false, order will be created but not transmited
|
||||||
|
self.parentId = 0 # Parent order Id, to associate Auto STP or TRAIL orders with the original order.
|
||||||
|
self.blockOrder = False
|
||||||
|
self.sweepToFill = False
|
||||||
|
self.displaySize = 0
|
||||||
|
self.triggerMethod = 0 # 0=Default, 1=Double_Bid_Ask, 2=Last, 3=Double_Last, 4=Bid_Ask, 7=Last_or_Bid_Ask, 8=Mid-point
|
||||||
|
self.outsideRth = False
|
||||||
|
self.hidden = False
|
||||||
|
self.goodAfterTime = "" # Format: 20060505 08:00:00 {time zone}
|
||||||
|
self.goodTillDate = "" # Format: 20060505 08:00:00 {time zone}
|
||||||
|
self.rule80A = "" # Individual = 'I', Agency = 'A', AgentOtherMember = 'W', IndividualPTIA = 'J', AgencyPTIA = 'U', AgentOtherMemberPTIA = 'M', IndividualPT = 'K', AgencyPT = 'Y', AgentOtherMemberPT = 'N'
|
||||||
|
self.allOrNone = False
|
||||||
|
self.minQty = UNSET_INTEGER #type: int
|
||||||
|
self.percentOffset = UNSET_DOUBLE # type: float; REL orders only
|
||||||
|
self.overridePercentageConstraints = False
|
||||||
|
self.trailStopPrice = UNSET_DOUBLE # type: float
|
||||||
|
self.trailingPercent = UNSET_DOUBLE # type: float; TRAILLIMIT orders only
|
||||||
|
|
||||||
|
# financial advisors only
|
||||||
|
self.faGroup = ""
|
||||||
|
self.faProfile = ""
|
||||||
|
self.faMethod = ""
|
||||||
|
self.faPercentage = ""
|
||||||
|
|
||||||
|
# institutional (ie non-cleared) only
|
||||||
|
self.designatedLocation = "" #used only when shortSaleSlot=2
|
||||||
|
self.openClose = "O" # O=Open, C=Close
|
||||||
|
self.origin = CUSTOMER # 0=Customer, 1=Firm
|
||||||
|
self.shortSaleSlot = 0 # type: int; 1 if you hold the shares, 2 if they will be delivered from elsewhere. Only for Action=SSHORT
|
||||||
|
self.exemptCode = -1
|
||||||
|
|
||||||
|
# SMART routing only
|
||||||
|
self.discretionaryAmt = 0
|
||||||
|
self.eTradeOnly = True
|
||||||
|
self.firmQuoteOnly = True
|
||||||
|
self.nbboPriceCap = UNSET_DOUBLE # type: float
|
||||||
|
self.optOutSmartRouting = False
|
||||||
|
|
||||||
|
# BOX exchange orders only
|
||||||
|
self.auctionStrategy = AUCTION_UNSET # type: int; AUCTION_MATCH, AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT
|
||||||
|
self.startingPrice = UNSET_DOUBLE # type: float
|
||||||
|
self.stockRefPrice = UNSET_DOUBLE # type: float
|
||||||
|
self.delta = UNSET_DOUBLE # type: float
|
||||||
|
|
||||||
|
# pegged to stock and VOL orders only
|
||||||
|
self.stockRangeLower = UNSET_DOUBLE # type: float
|
||||||
|
self.stockRangeUpper = UNSET_DOUBLE # type: float
|
||||||
|
|
||||||
|
self.randomizePrice = False
|
||||||
|
self.randomizeSize = False
|
||||||
|
|
||||||
|
# VOLATILITY ORDERS ONLY
|
||||||
|
self.volatility = UNSET_DOUBLE # type: float
|
||||||
|
self.volatilityType = UNSET_INTEGER # type: int # 1=daily, 2=annual
|
||||||
|
self.deltaNeutralOrderType = ""
|
||||||
|
self.deltaNeutralAuxPrice = UNSET_DOUBLE # type: float
|
||||||
|
self.deltaNeutralConId = 0
|
||||||
|
self.deltaNeutralSettlingFirm = ""
|
||||||
|
self.deltaNeutralClearingAccount = ""
|
||||||
|
self.deltaNeutralClearingIntent = ""
|
||||||
|
self.deltaNeutralOpenClose = ""
|
||||||
|
self.deltaNeutralShortSale = False
|
||||||
|
self.deltaNeutralShortSaleSlot = 0
|
||||||
|
self.deltaNeutralDesignatedLocation = ""
|
||||||
|
self.continuousUpdate = False
|
||||||
|
self.referencePriceType = UNSET_INTEGER # type: int; 1=Average, 2 = BidOrAsk
|
||||||
|
|
||||||
|
# COMBO ORDERS ONLY
|
||||||
|
self.basisPoints = UNSET_DOUBLE # type: float; EFP orders only
|
||||||
|
self.basisPointsType = UNSET_INTEGER # type: int; EFP orders only
|
||||||
|
|
||||||
|
# SCALE ORDERS ONLY
|
||||||
|
self.scaleInitLevelSize = UNSET_INTEGER # type: int
|
||||||
|
self.scaleSubsLevelSize = UNSET_INTEGER # type: int
|
||||||
|
self.scalePriceIncrement = UNSET_DOUBLE # type: float
|
||||||
|
self.scalePriceAdjustValue = UNSET_DOUBLE # type: float
|
||||||
|
self.scalePriceAdjustInterval = UNSET_INTEGER # type: int
|
||||||
|
self.scaleProfitOffset = UNSET_DOUBLE # type: float
|
||||||
|
self.scaleAutoReset = False
|
||||||
|
self.scaleInitPosition = UNSET_INTEGER # type: int
|
||||||
|
self.scaleInitFillQty = UNSET_INTEGER # type: int
|
||||||
|
self.scaleRandomPercent = False
|
||||||
|
self.scaleTable = ""
|
||||||
|
|
||||||
|
# HEDGE ORDERS
|
||||||
|
self.hedgeType = "" # 'D' - delta, 'B' - beta, 'F' - FX, 'P' - pair
|
||||||
|
self.hedgeParam = "" # 'beta=X' value for beta hedge, 'ratio=Y' for pair hedge
|
||||||
|
|
||||||
|
# Clearing info
|
||||||
|
self.account = "" # IB account
|
||||||
|
self.settlingFirm = ""
|
||||||
|
self.clearingAccount = "" #True beneficiary of the order
|
||||||
|
self.clearingIntent = "" # "" (Default), "IB", "Away", "PTA" (PostTrade)
|
||||||
|
|
||||||
|
# ALGO ORDERS ONLY
|
||||||
|
self.algoStrategy = ""
|
||||||
|
|
||||||
|
self.algoParams = None #TagValueList
|
||||||
|
self.smartComboRoutingParams = None #TagValueList
|
||||||
|
|
||||||
|
self.algoId = ""
|
||||||
|
|
||||||
|
# What-if
|
||||||
|
self.whatIf = False
|
||||||
|
|
||||||
|
# Not Held
|
||||||
|
self.notHeld = False
|
||||||
|
self.solicited = False
|
||||||
|
|
||||||
|
# models
|
||||||
|
self.modelCode = ""
|
||||||
|
|
||||||
|
# order combo legs
|
||||||
|
|
||||||
|
self.orderComboLegs = None # OrderComboLegListSPtr
|
||||||
|
|
||||||
|
self.orderMiscOptions = None # TagValueList
|
||||||
|
|
||||||
|
# VER PEG2BENCH fields:
|
||||||
|
self.referenceContractId = 0
|
||||||
|
self.peggedChangeAmount = 0.
|
||||||
|
self.isPeggedChangeAmountDecrease = False
|
||||||
|
self.referenceChangeAmount = 0.
|
||||||
|
self.referenceExchangeId = ""
|
||||||
|
self.adjustedOrderType = ""
|
||||||
|
|
||||||
|
self.triggerPrice = UNSET_DOUBLE
|
||||||
|
self.adjustedStopPrice = UNSET_DOUBLE
|
||||||
|
self.adjustedStopLimitPrice = UNSET_DOUBLE
|
||||||
|
self.adjustedTrailingAmount = UNSET_DOUBLE
|
||||||
|
self.adjustableTrailingUnit = 0
|
||||||
|
self.lmtPriceOffset = UNSET_DOUBLE
|
||||||
|
|
||||||
|
self.conditions = [] # std::vector<std::shared_ptr<OrderCondition>>
|
||||||
|
self.conditionsCancelOrder = False
|
||||||
|
self.conditionsIgnoreRth = False
|
||||||
|
|
||||||
|
# ext operator
|
||||||
|
self.extOperator = ""
|
||||||
|
|
||||||
|
# native cash quantity
|
||||||
|
self.cashQty = UNSET_DOUBLE
|
||||||
|
|
||||||
|
self.mifid2DecisionMaker = ""
|
||||||
|
self.mifid2DecisionAlgo = ""
|
||||||
|
self.mifid2ExecutionTrader = ""
|
||||||
|
self.mifid2ExecutionAlgo = ""
|
||||||
|
|
||||||
|
self.dontUseAutoPriceForHedge = False
|
||||||
|
|
||||||
|
self.isOmsContainer = False
|
||||||
|
|
||||||
|
self.discretionaryUpToLimitPrice = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = "%s,%d,%s:" % (self.orderId, self.clientId, self.permId)
|
||||||
|
|
||||||
|
s += " %s %s %d@%f" % (
|
||||||
|
self.orderType,
|
||||||
|
self.action,
|
||||||
|
self.totalQuantity,
|
||||||
|
self.lmtPrice)
|
||||||
|
|
||||||
|
s += " %s" % self.tif
|
||||||
|
|
||||||
|
if self.orderComboLegs:
|
||||||
|
s += " CMB("
|
||||||
|
for leg in self.orderComboLegs:
|
||||||
|
s += str(leg) + ","
|
||||||
|
s += ")"
|
||||||
|
|
||||||
|
if self.conditions:
|
||||||
|
s += " COND("
|
||||||
|
for cond in self.conditions:
|
||||||
|
s += str(cond) + ","
|
||||||
|
s += ")"
|
||||||
|
|
||||||
|
return s
|
282
vnpy/api/ib/order_condition.py
Normal file
282
vnpy/api/ib/order_condition.py
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from ibapi import comm
|
||||||
|
from ibapi.common import UNSET_DOUBLE
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
from ibapi.enum_implem import Enum
|
||||||
|
from ibapi.utils import decode
|
||||||
|
|
||||||
|
#TODO: add support for Rebate, P/L, ShortableShares conditions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OrderCondition(Object):
|
||||||
|
Price = 1
|
||||||
|
Time = 3
|
||||||
|
Margin = 4
|
||||||
|
Execution = 5
|
||||||
|
Volume = 6
|
||||||
|
PercentChange = 7
|
||||||
|
|
||||||
|
def __init__(self, condType):
|
||||||
|
self.condType = condType
|
||||||
|
self.isConjunctionConnection = True
|
||||||
|
|
||||||
|
def type(self):
|
||||||
|
return self.condType
|
||||||
|
|
||||||
|
def And(self):
|
||||||
|
self.isConjunctionConnection = True
|
||||||
|
return self
|
||||||
|
|
||||||
|
def Or(self):
|
||||||
|
self.isConjunctionConnection = False
|
||||||
|
return self
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
connector = decode(str, fields)
|
||||||
|
self.isConjunctionConnection = connector == "a"
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = []
|
||||||
|
flds.append(comm.make_field("a" if self.isConjunctionConnection else "o"))
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<AND>" if self.isConjunctionConnection else "<OR>"
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutionCondition(OrderCondition):
|
||||||
|
|
||||||
|
def __init__(self, secType=None, exch=None, symbol=None):
|
||||||
|
OrderCondition.__init__(self, OrderCondition.Execution)
|
||||||
|
self.secType = secType
|
||||||
|
self.exchange = exch
|
||||||
|
self.symbol = symbol
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
OrderCondition.decode(self, fields)
|
||||||
|
self.secType = decode(str, fields)
|
||||||
|
self.exchange = decode(str, fields)
|
||||||
|
self.symbol = decode(str, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = OrderCondition.make_fields(self) + \
|
||||||
|
[comm.make_field(self.secType),
|
||||||
|
comm.make_field(self.exchange),
|
||||||
|
comm.make_field(self.symbol)]
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "trade occurs for " + self.symbol + " symbol on " + \
|
||||||
|
self.exchange + " exchange for " + self.secType + " security type"
|
||||||
|
|
||||||
|
|
||||||
|
class OperatorCondition(OrderCondition):
|
||||||
|
def __init__(self, condType=None, isMore=None):
|
||||||
|
OrderCondition.__init__(self, condType)
|
||||||
|
self.isMore = isMore
|
||||||
|
|
||||||
|
def valueToString(self) -> str:
|
||||||
|
raise NotImplementedError("abstractmethod!")
|
||||||
|
|
||||||
|
def setValueFromString(self, text: str) -> None:
|
||||||
|
raise NotImplementedError("abstractmethod!")
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
OrderCondition.decode(self, fields)
|
||||||
|
self.isMore = decode(bool, fields)
|
||||||
|
text = decode(str, fields)
|
||||||
|
self.setValueFromString(text)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = OrderCondition.make_fields(self) + \
|
||||||
|
[comm.make_field(self.isMore),
|
||||||
|
comm.make_field(self.valueToString()), ]
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
sb = ">= " if self.isMore else "<= "
|
||||||
|
return " %s %s" % (sb, self.valueToString())
|
||||||
|
|
||||||
|
|
||||||
|
class MarginCondition(OperatorCondition):
|
||||||
|
def __init__(self, isMore=None, percent=None):
|
||||||
|
OperatorCondition.__init__(self, OrderCondition.Margin, isMore)
|
||||||
|
self.percent = percent
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
OperatorCondition.decode(self, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = OperatorCondition.make_fields(self)
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def valueToString(self) -> str:
|
||||||
|
return str(self.percent)
|
||||||
|
|
||||||
|
def setValueFromString(self, text: str) -> None:
|
||||||
|
self.percent = float(text)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "the margin cushion percent %s " % (
|
||||||
|
OperatorCondition.__str__(self))
|
||||||
|
|
||||||
|
|
||||||
|
class ContractCondition(OperatorCondition):
|
||||||
|
def __init__(self, condType=None, conId=None, exch=None, isMore=None):
|
||||||
|
OperatorCondition.__init__(self, condType, isMore)
|
||||||
|
self.conId = conId
|
||||||
|
self.exchange = exch
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
OperatorCondition.decode(self, fields)
|
||||||
|
self.conId = decode(int, fields)
|
||||||
|
self.exchange = decode(str, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = OperatorCondition.make_fields(self) + \
|
||||||
|
[comm.make_field(self.conId),
|
||||||
|
comm.make_field(self.exchange), ]
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s on %s is %s " % (self.conId, self.exchange,
|
||||||
|
OperatorCondition.__str__(self))
|
||||||
|
|
||||||
|
|
||||||
|
class TimeCondition(OperatorCondition):
|
||||||
|
def __init__(self, isMore=None, time=None):
|
||||||
|
OperatorCondition.__init__(self, OrderCondition.Time, isMore)
|
||||||
|
self.time = time
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
OperatorCondition.decode(self, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = OperatorCondition.make_fields(self)
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def valueToString(self) -> str:
|
||||||
|
return self.time
|
||||||
|
|
||||||
|
def setValueFromString(self, text: str) -> None:
|
||||||
|
self.time = text
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "time is %s " % (OperatorCondition.__str__(self))
|
||||||
|
|
||||||
|
|
||||||
|
class PriceCondition(ContractCondition):
|
||||||
|
TriggerMethodEnum = Enum(
|
||||||
|
"Default", # = 0,
|
||||||
|
"DoubleBidAsk", # = 1,
|
||||||
|
"Last", # = 2,
|
||||||
|
"DoubleLast", # = 3,
|
||||||
|
"BidAsk", # = 4,
|
||||||
|
"N/A1",
|
||||||
|
"N/A2",
|
||||||
|
"LastBidAsk", #= 7,
|
||||||
|
"MidPoint") #= 8
|
||||||
|
|
||||||
|
def __init__(self, triggerMethod=None, conId=None, exch=None, isMore=None,
|
||||||
|
price=None):
|
||||||
|
ContractCondition.__init__(self, OrderCondition.Price, conId, exch,
|
||||||
|
isMore)
|
||||||
|
self.price = price
|
||||||
|
self.triggerMethod = triggerMethod
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
ContractCondition.decode(self, fields)
|
||||||
|
self.triggerMethod = decode(int, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = ContractCondition.make_fields(self) + \
|
||||||
|
[comm.make_field(self.triggerMethod), ]
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def valueToString(self) -> str:
|
||||||
|
return str(self.price)
|
||||||
|
|
||||||
|
def setValueFromString(self, text: str) -> None:
|
||||||
|
self.price = float(text)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s price of %s " % (
|
||||||
|
PriceCondition.TriggerMethodEnum.to_str(self.triggerMethod),
|
||||||
|
ContractCondition.__str__(self))
|
||||||
|
|
||||||
|
|
||||||
|
class PercentChangeCondition(ContractCondition):
|
||||||
|
def __init__(self, conId=None, exch=None, isMore=None,
|
||||||
|
changePercent=UNSET_DOUBLE):
|
||||||
|
ContractCondition.__init__(self, OrderCondition.PercentChange, conId,
|
||||||
|
exch, isMore)
|
||||||
|
self.changePercent = changePercent
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
ContractCondition.decode(self, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = ContractCondition.make_fields(self)
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def valueToString(self) -> str:
|
||||||
|
return str(self.changePercent)
|
||||||
|
|
||||||
|
def setValueFromString(self, text: str) -> None:
|
||||||
|
self.changePercent = float(text)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "percent change of %s " % (
|
||||||
|
ContractCondition.__str__(self))
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeCondition(ContractCondition):
|
||||||
|
def __init__(self, conId=None, exch=None, isMore=None, volume=None):
|
||||||
|
ContractCondition.__init__(self, OrderCondition.Volume, conId, exch,
|
||||||
|
isMore)
|
||||||
|
self.volume = volume
|
||||||
|
|
||||||
|
def decode(self, fields):
|
||||||
|
ContractCondition.decode(self, fields)
|
||||||
|
|
||||||
|
def make_fields(self):
|
||||||
|
flds = ContractCondition.make_fields(self)
|
||||||
|
return flds
|
||||||
|
|
||||||
|
def valueToString(self) -> str:
|
||||||
|
return str(self.volume)
|
||||||
|
|
||||||
|
def setValueFromString(self, text: str) -> None:
|
||||||
|
self.volume = int(text)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "volume of %s " % (
|
||||||
|
ContractCondition.__str__(self))
|
||||||
|
|
||||||
|
|
||||||
|
def Create(condType):
|
||||||
|
cond = None
|
||||||
|
|
||||||
|
if OrderCondition.Execution == condType:
|
||||||
|
cond = ExecutionCondition()
|
||||||
|
elif OrderCondition.Margin == condType:
|
||||||
|
cond = MarginCondition()
|
||||||
|
elif OrderCondition.PercentChange == condType:
|
||||||
|
cond = PercentChangeCondition()
|
||||||
|
elif OrderCondition.Price == condType:
|
||||||
|
cond = PriceCondition()
|
||||||
|
elif OrderCondition.Time == condType:
|
||||||
|
cond = TimeCondition()
|
||||||
|
elif OrderCondition.Volume == condType:
|
||||||
|
cond = VolumeCondition()
|
||||||
|
|
||||||
|
return cond
|
||||||
|
|
||||||
|
|
||||||
|
|
28
vnpy/api/ib/order_state.py
Normal file
28
vnpy/api/ib/order_state.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ibapi.common import UNSET_DOUBLE
|
||||||
|
|
||||||
|
|
||||||
|
class OrderState:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.status= ""
|
||||||
|
|
||||||
|
self.initMarginBefore= ""
|
||||||
|
self.maintMarginBefore= ""
|
||||||
|
self.equityWithLoanBefore= ""
|
||||||
|
self.initMarginChange= ""
|
||||||
|
self.maintMarginChange= ""
|
||||||
|
self.equityWithLoanChange= ""
|
||||||
|
self.initMarginAfter= ""
|
||||||
|
self.maintMarginAfter= ""
|
||||||
|
self.equityWithLoanAfter= ""
|
||||||
|
|
||||||
|
self.commission = UNSET_DOUBLE # type: float
|
||||||
|
self.minCommission = UNSET_DOUBLE # type: float
|
||||||
|
self.maxCommission = UNSET_DOUBLE # type: float
|
||||||
|
self.commissionCurrency = ""
|
||||||
|
self.warningText = ""
|
51
vnpy/api/ib/reader.py
Normal file
51
vnpy/api/ib/reader.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
The EReader runs in a separate threads and is responsible for receiving the
|
||||||
|
incoming messages.
|
||||||
|
It will read the packets from the wire, use the low level IB messaging to
|
||||||
|
remove the size prefix and put the rest in a Queue.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
from ibapi import comm
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EReader(Thread):
|
||||||
|
def __init__(self, conn, msg_queue):
|
||||||
|
super().__init__()
|
||||||
|
self.conn = conn
|
||||||
|
self.msg_queue = msg_queue
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
buf = b""
|
||||||
|
while self.conn.isConnected():
|
||||||
|
|
||||||
|
data = self.conn.recvMsg()
|
||||||
|
logger.debug("reader loop, recvd size %d", len(data))
|
||||||
|
buf += data
|
||||||
|
|
||||||
|
while len(buf) > 0:
|
||||||
|
(size, msg, buf) = comm.read_msg(buf)
|
||||||
|
#logger.debug("resp %s", buf.decode('ascii'))
|
||||||
|
logger.debug("size:%d msg.size:%d msg:|%s| buf:%s|", size,
|
||||||
|
len(msg), buf, "|")
|
||||||
|
|
||||||
|
if msg:
|
||||||
|
self.msg_queue.put(msg)
|
||||||
|
else:
|
||||||
|
logger.debug("more incoming packet(s) are needed ")
|
||||||
|
break
|
||||||
|
|
||||||
|
logger.debug("EReader thread finished")
|
||||||
|
|
||||||
|
|
57
vnpy/api/ib/scanner.py
Normal file
57
vnpy/api/ib/scanner.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
from ibapi.common import UNSET_INTEGER, UNSET_DOUBLE
|
||||||
|
|
||||||
|
|
||||||
|
class ScanData(Object):
|
||||||
|
def __init__(self, contract = None, rank = 0, distance = "", benchmark = "", projection = "", legsStr = ""):
|
||||||
|
self.contract = contract
|
||||||
|
self.rank = rank
|
||||||
|
self.distance = distance
|
||||||
|
self.benchmark = benchmark
|
||||||
|
self.projection = projection
|
||||||
|
self.legsStr = legsStr
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Rank: %d, Symbol: %s, SecType: %s, Currency: %s, Distance: %s, Benchmark: %s, Projection: %s, Legs String: %s" % (self.rank,
|
||||||
|
self.contract.symbol, self.contract.secType, self.contract.currency, self.distance,
|
||||||
|
self.benchmark, self.projection, self.legsStr)
|
||||||
|
|
||||||
|
NO_ROW_NUMBER_SPECIFIED = -1
|
||||||
|
|
||||||
|
class ScannerSubscription(Object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.numberOfRows = NO_ROW_NUMBER_SPECIFIED
|
||||||
|
self.instrument = ""
|
||||||
|
self.locationCode = ""
|
||||||
|
self.scanCode = ""
|
||||||
|
self.abovePrice = UNSET_DOUBLE
|
||||||
|
self.belowPrice = UNSET_DOUBLE
|
||||||
|
self.aboveVolume = UNSET_INTEGER
|
||||||
|
self.marketCapAbove = UNSET_DOUBLE
|
||||||
|
self.marketCapBelow = UNSET_DOUBLE
|
||||||
|
self.moodyRatingAbove = ""
|
||||||
|
self.moodyRatingBelow = ""
|
||||||
|
self.spRatingAbove = ""
|
||||||
|
self.spRatingBelow = ""
|
||||||
|
self.maturityDateAbove = ""
|
||||||
|
self.maturityDateBelow = ""
|
||||||
|
self.couponRateAbove = UNSET_DOUBLE
|
||||||
|
self.couponRateBelow = UNSET_DOUBLE
|
||||||
|
self.excludeConvertible = False
|
||||||
|
self.averageOptionVolumeAbove = UNSET_INTEGER
|
||||||
|
self.scannerSettingPairs = ""
|
||||||
|
self.stockTypeFilter = ""
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = "Instrument: %s, LocationCode: %s, ScanCode: %s" % (self.instrument, self.locationCode, self.scanCode)
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
104
vnpy/api/ib/server_versions.py
Normal file
104
vnpy/api/ib/server_versions.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The known server versions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#MIN_SERVER_VER_REAL_TIME_BARS = 34
|
||||||
|
#MIN_SERVER_VER_SCALE_ORDERS = 35
|
||||||
|
#MIN_SERVER_VER_SNAPSHOT_MKT_DATA = 35
|
||||||
|
#MIN_SERVER_VER_SSHORT_COMBO_LEGS = 35
|
||||||
|
#MIN_SERVER_VER_WHAT_IF_ORDERS = 36
|
||||||
|
#MIN_SERVER_VER_CONTRACT_CONID = 37
|
||||||
|
MIN_SERVER_VER_PTA_ORDERS = 39
|
||||||
|
MIN_SERVER_VER_FUNDAMENTAL_DATA = 40
|
||||||
|
MIN_SERVER_VER_DELTA_NEUTRAL = 40
|
||||||
|
MIN_SERVER_VER_CONTRACT_DATA_CHAIN = 40
|
||||||
|
MIN_SERVER_VER_SCALE_ORDERS2 = 40
|
||||||
|
MIN_SERVER_VER_ALGO_ORDERS = 41
|
||||||
|
MIN_SERVER_VER_EXECUTION_DATA_CHAIN = 42
|
||||||
|
MIN_SERVER_VER_NOT_HELD = 44
|
||||||
|
MIN_SERVER_VER_SEC_ID_TYPE = 45
|
||||||
|
MIN_SERVER_VER_PLACE_ORDER_CONID = 46
|
||||||
|
MIN_SERVER_VER_REQ_MKT_DATA_CONID = 47
|
||||||
|
MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT = 49
|
||||||
|
MIN_SERVER_VER_REQ_CALC_OPTION_PRICE = 50
|
||||||
|
MIN_SERVER_VER_SSHORTX_OLD = 51
|
||||||
|
MIN_SERVER_VER_SSHORTX = 52
|
||||||
|
MIN_SERVER_VER_REQ_GLOBAL_CANCEL = 53
|
||||||
|
MIN_SERVER_VER_HEDGE_ORDERS = 54
|
||||||
|
MIN_SERVER_VER_REQ_MARKET_DATA_TYPE = 55
|
||||||
|
MIN_SERVER_VER_OPT_OUT_SMART_ROUTING = 56
|
||||||
|
MIN_SERVER_VER_SMART_COMBO_ROUTING_PARAMS = 57
|
||||||
|
MIN_SERVER_VER_DELTA_NEUTRAL_CONID = 58
|
||||||
|
MIN_SERVER_VER_SCALE_ORDERS3 = 60
|
||||||
|
MIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE = 61
|
||||||
|
MIN_SERVER_VER_TRAILING_PERCENT = 62
|
||||||
|
MIN_SERVER_VER_DELTA_NEUTRAL_OPEN_CLOSE = 66
|
||||||
|
MIN_SERVER_VER_POSITIONS = 67
|
||||||
|
MIN_SERVER_VER_ACCOUNT_SUMMARY = 67
|
||||||
|
MIN_SERVER_VER_TRADING_CLASS = 68
|
||||||
|
MIN_SERVER_VER_SCALE_TABLE = 69
|
||||||
|
MIN_SERVER_VER_LINKING = 70
|
||||||
|
MIN_SERVER_VER_ALGO_ID = 71
|
||||||
|
MIN_SERVER_VER_OPTIONAL_CAPABILITIES = 72
|
||||||
|
MIN_SERVER_VER_ORDER_SOLICITED = 73
|
||||||
|
MIN_SERVER_VER_LINKING_AUTH = 74
|
||||||
|
MIN_SERVER_VER_PRIMARYEXCH = 75
|
||||||
|
MIN_SERVER_VER_RANDOMIZE_SIZE_AND_PRICE = 76
|
||||||
|
MIN_SERVER_VER_FRACTIONAL_POSITIONS = 101
|
||||||
|
MIN_SERVER_VER_PEGGED_TO_BENCHMARK = 102
|
||||||
|
MIN_SERVER_VER_MODELS_SUPPORT = 103
|
||||||
|
MIN_SERVER_VER_SEC_DEF_OPT_PARAMS_REQ = 104
|
||||||
|
MIN_SERVER_VER_EXT_OPERATOR = 105
|
||||||
|
MIN_SERVER_VER_SOFT_DOLLAR_TIER = 106
|
||||||
|
MIN_SERVER_VER_REQ_FAMILY_CODES = 107
|
||||||
|
MIN_SERVER_VER_REQ_MATCHING_SYMBOLS = 108
|
||||||
|
MIN_SERVER_VER_PAST_LIMIT = 109
|
||||||
|
MIN_SERVER_VER_MD_SIZE_MULTIPLIER = 110
|
||||||
|
MIN_SERVER_VER_CASH_QTY = 111
|
||||||
|
MIN_SERVER_VER_REQ_MKT_DEPTH_EXCHANGES = 112
|
||||||
|
MIN_SERVER_VER_TICK_NEWS = 113
|
||||||
|
MIN_SERVER_VER_REQ_SMART_COMPONENTS = 114
|
||||||
|
MIN_SERVER_VER_REQ_NEWS_PROVIDERS = 115
|
||||||
|
MIN_SERVER_VER_REQ_NEWS_ARTICLE = 116
|
||||||
|
MIN_SERVER_VER_REQ_HISTORICAL_NEWS = 117
|
||||||
|
MIN_SERVER_VER_REQ_HEAD_TIMESTAMP = 118
|
||||||
|
MIN_SERVER_VER_REQ_HISTOGRAM = 119
|
||||||
|
MIN_SERVER_VER_SERVICE_DATA_TYPE = 120
|
||||||
|
MIN_SERVER_VER_AGG_GROUP = 121
|
||||||
|
MIN_SERVER_VER_UNDERLYING_INFO = 122
|
||||||
|
MIN_SERVER_VER_CANCEL_HEADTIMESTAMP = 123
|
||||||
|
MIN_SERVER_VER_SYNT_REALTIME_BARS = 124
|
||||||
|
MIN_SERVER_VER_CFD_REROUTE = 125
|
||||||
|
MIN_SERVER_VER_MARKET_RULES = 126
|
||||||
|
MIN_SERVER_VER_PNL = 127
|
||||||
|
MIN_SERVER_VER_NEWS_QUERY_ORIGINS = 128
|
||||||
|
MIN_SERVER_VER_UNREALIZED_PNL = 129
|
||||||
|
MIN_SERVER_VER_HISTORICAL_TICKS = 130
|
||||||
|
MIN_SERVER_VER_MARKET_CAP_PRICE = 131
|
||||||
|
MIN_SERVER_VER_PRE_OPEN_BID_ASK = 132
|
||||||
|
MIN_SERVER_VER_REAL_EXPIRATION_DATE = 134
|
||||||
|
MIN_SERVER_VER_REALIZED_PNL = 135
|
||||||
|
MIN_SERVER_VER_LAST_LIQUIDITY = 136
|
||||||
|
MIN_SERVER_VER_TICK_BY_TICK = 137
|
||||||
|
MIN_SERVER_VER_DECISION_MAKER = 138
|
||||||
|
MIN_SERVER_VER_MIFID_EXECUTION = 139
|
||||||
|
MIN_SERVER_VER_TICK_BY_TICK_IGNORE_SIZE = 140
|
||||||
|
MIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE = 141
|
||||||
|
MIN_SERVER_VER_WHAT_IF_EXT_FIELDS = 142
|
||||||
|
MIN_SERVER_VER_SCANNER_GENERIC_OPTS = 143
|
||||||
|
MIN_SERVER_VER_API_BIND_ORDER = 144
|
||||||
|
MIN_SERVER_VER_ORDER_CONTAINER = 145
|
||||||
|
MIN_SERVER_VER_SMART_DEPTH = 146
|
||||||
|
MIN_SERVER_VER_REMOVE_NULL_ALL_CASTING = 147
|
||||||
|
MIN_SERVER_VER_D_PEG_ORDERS = 148
|
||||||
|
|
||||||
|
# 100+ messaging */
|
||||||
|
# 100 = enhanced handshake, msg length prefixes
|
||||||
|
|
||||||
|
MIN_CLIENT_VER = 100
|
||||||
|
MAX_CLIENT_VER = MIN_SERVER_VER_D_PEG_ORDERS
|
17
vnpy/api/ib/softdollartier.py
Normal file
17
vnpy/api/ib/softdollartier.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
|
||||||
|
|
||||||
|
class SoftDollarTier(Object):
|
||||||
|
def __init__(self, name = "", val = "", displayName = ""):
|
||||||
|
self.name = name
|
||||||
|
self.val = val
|
||||||
|
self.displayName = displayName
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Name: %s, Value: %s, DisplayName: %s" % (self.name, self.val, self.displayName)
|
24
vnpy/api/ib/tag_value.py
Normal file
24
vnpy/api/ib/tag_value.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
Simple class mapping a tag to a value. Both of them are strings.
|
||||||
|
They are used in a list to convey extra info with the requests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ibapi.object_implem import Object
|
||||||
|
|
||||||
|
|
||||||
|
class TagValue(Object):
|
||||||
|
def __init__(self, tag:str=None, value:str=None):
|
||||||
|
self.tag = str(tag)
|
||||||
|
self.value = str(value)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# this is not only used for Python dump but when encoding to send
|
||||||
|
# so don't change it lightly !
|
||||||
|
return "%s=%s;" % (self.tag, self.value)
|
||||||
|
|
||||||
|
|
109
vnpy/api/ib/ticktype.py
Normal file
109
vnpy/api/ib/ticktype.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
TickType type
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ibapi.enum_implem import Enum
|
||||||
|
|
||||||
|
|
||||||
|
# TickType
|
||||||
|
TickType = int
|
||||||
|
TickTypeEnum = Enum("BID_SIZE",
|
||||||
|
"BID",
|
||||||
|
"ASK",
|
||||||
|
"ASK_SIZE",
|
||||||
|
"LAST",
|
||||||
|
"LAST_SIZE",
|
||||||
|
"HIGH",
|
||||||
|
"LOW",
|
||||||
|
"VOLUME",
|
||||||
|
"CLOSE",
|
||||||
|
"BID_OPTION_COMPUTATION",
|
||||||
|
"ASK_OPTION_COMPUTATION",
|
||||||
|
"LAST_OPTION_COMPUTATION",
|
||||||
|
"MODEL_OPTION",
|
||||||
|
"OPEN",
|
||||||
|
"LOW_13_WEEK",
|
||||||
|
"HIGH_13_WEEK",
|
||||||
|
"LOW_26_WEEK",
|
||||||
|
"HIGH_26_WEEK",
|
||||||
|
"LOW_52_WEEK",
|
||||||
|
"HIGH_52_WEEK",
|
||||||
|
"AVG_VOLUME",
|
||||||
|
"OPEN_INTEREST",
|
||||||
|
"OPTION_HISTORICAL_VOL",
|
||||||
|
"OPTION_IMPLIED_VOL",
|
||||||
|
"OPTION_BID_EXCH",
|
||||||
|
"OPTION_ASK_EXCH",
|
||||||
|
"OPTION_CALL_OPEN_INTEREST",
|
||||||
|
"OPTION_PUT_OPEN_INTEREST",
|
||||||
|
"OPTION_CALL_VOLUME",
|
||||||
|
"OPTION_PUT_VOLUME",
|
||||||
|
"INDEX_FUTURE_PREMIUM",
|
||||||
|
"BID_EXCH",
|
||||||
|
"ASK_EXCH",
|
||||||
|
"AUCTION_VOLUME",
|
||||||
|
"AUCTION_PRICE",
|
||||||
|
"AUCTION_IMBALANCE",
|
||||||
|
"MARK_PRICE",
|
||||||
|
"BID_EFP_COMPUTATION",
|
||||||
|
"ASK_EFP_COMPUTATION",
|
||||||
|
"LAST_EFP_COMPUTATION",
|
||||||
|
"OPEN_EFP_COMPUTATION",
|
||||||
|
"HIGH_EFP_COMPUTATION",
|
||||||
|
"LOW_EFP_COMPUTATION",
|
||||||
|
"CLOSE_EFP_COMPUTATION",
|
||||||
|
"LAST_TIMESTAMP",
|
||||||
|
"SHORTABLE",
|
||||||
|
"FUNDAMENTAL_RATIOS",
|
||||||
|
"RT_VOLUME",
|
||||||
|
"HALTED",
|
||||||
|
"BID_YIELD",
|
||||||
|
"ASK_YIELD",
|
||||||
|
"LAST_YIELD",
|
||||||
|
"CUST_OPTION_COMPUTATION",
|
||||||
|
"TRADE_COUNT",
|
||||||
|
"TRADE_RATE",
|
||||||
|
"VOLUME_RATE",
|
||||||
|
"LAST_RTH_TRADE",
|
||||||
|
"RT_HISTORICAL_VOL",
|
||||||
|
"IB_DIVIDENDS",
|
||||||
|
"BOND_FACTOR_MULTIPLIER",
|
||||||
|
"REGULATORY_IMBALANCE",
|
||||||
|
"NEWS_TICK",
|
||||||
|
"SHORT_TERM_VOLUME_3_MIN",
|
||||||
|
"SHORT_TERM_VOLUME_5_MIN",
|
||||||
|
"SHORT_TERM_VOLUME_10_MIN",
|
||||||
|
"DELAYED_BID",
|
||||||
|
"DELAYED_ASK",
|
||||||
|
"DELAYED_LAST",
|
||||||
|
"DELAYED_BID_SIZE",
|
||||||
|
"DELAYED_ASK_SIZE",
|
||||||
|
"DELAYED_LAST_SIZE",
|
||||||
|
"DELAYED_HIGH",
|
||||||
|
"DELAYED_LOW",
|
||||||
|
"DELAYED_VOLUME",
|
||||||
|
"DELAYED_CLOSE",
|
||||||
|
"DELAYED_OPEN",
|
||||||
|
"RT_TRD_VOLUME",
|
||||||
|
"CREDITMAN_MARK_PRICE",
|
||||||
|
"CREDITMAN_SLOW_MARK_PRICE",
|
||||||
|
"DELAYED_BID_OPTION",
|
||||||
|
"DELAYED_ASK_OPTION",
|
||||||
|
"DELAYED_LAST_OPTION",
|
||||||
|
"DELAYED_MODEL_OPTION",
|
||||||
|
"LAST_EXCH",
|
||||||
|
"LAST_REG_TIME",
|
||||||
|
"FUTURES_OPEN_INTEREST",
|
||||||
|
"AVG_OPT_VOLUME",
|
||||||
|
"DELAYED_LAST_TIMESTAMP",
|
||||||
|
"SHORTABLE_SHARES",
|
||||||
|
"NOT_SET")
|
||||||
|
|
||||||
|
|
||||||
|
|
115
vnpy/api/ib/utils.py
Normal file
115
vnpy/api/ib/utils.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Collection of misc tools
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from ibapi.common import UNSET_INTEGER, UNSET_DOUBLE
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# I use this just to visually emphasize it's a wrapper overriden method
|
||||||
|
def iswrapper(fn):
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
class BadMessage(Exception):
|
||||||
|
def __init__(self, text):
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
|
||||||
|
class LogFunction(object):
|
||||||
|
def __init__(self, text, logLevel):
|
||||||
|
self.text = text
|
||||||
|
self.logLevel = logLevel
|
||||||
|
|
||||||
|
def __call__(self, fn):
|
||||||
|
def newFn(origSelf, *args, **kwargs):
|
||||||
|
if logger.getLogger().isEnabledFor(self.logLevel):
|
||||||
|
argNames = [argName for argName in inspect.getfullargspec(fn)[0] if argName != 'self']
|
||||||
|
logger.log(self.logLevel,
|
||||||
|
"{} {} {} kw:{}".format(self.text, fn.__name__,
|
||||||
|
[nameNarg for nameNarg in zip(argNames, args) if nameNarg[1] is not origSelf], kwargs))
|
||||||
|
fn(origSelf, *args)
|
||||||
|
return newFn
|
||||||
|
|
||||||
|
|
||||||
|
def current_fn_name(parent_idx = 0):
|
||||||
|
#depth is 1 bc this is already a fn, so we need the caller
|
||||||
|
return sys._getframe(1 + parent_idx).f_code.co_name
|
||||||
|
|
||||||
|
|
||||||
|
def setattr_log(self, var_name, var_value):
|
||||||
|
#import code; code.interact(local=locals())
|
||||||
|
logger.debug("%s %s %s=|%s|", self.__class__, id(self), var_name, var_value)
|
||||||
|
super(self.__class__, self).__setattr__(var_name, var_value)
|
||||||
|
|
||||||
|
|
||||||
|
SHOW_UNSET = True
|
||||||
|
def decode(the_type, fields, show_unset = False):
|
||||||
|
try:
|
||||||
|
s = next(fields)
|
||||||
|
except StopIteration:
|
||||||
|
raise BadMessage("no more fields")
|
||||||
|
|
||||||
|
logger.debug("decode %s %s", the_type, s)
|
||||||
|
|
||||||
|
if the_type is str:
|
||||||
|
if type(s) is str:
|
||||||
|
return s
|
||||||
|
elif type(s) is bytes:
|
||||||
|
return s.decode()
|
||||||
|
else:
|
||||||
|
raise TypeError("unsupported incoming type " + type(s) + " for desired type 'str")
|
||||||
|
|
||||||
|
orig_type = the_type
|
||||||
|
if the_type is bool:
|
||||||
|
the_type = int
|
||||||
|
|
||||||
|
if show_unset:
|
||||||
|
if s is None or len(s) == 0:
|
||||||
|
if the_type is float:
|
||||||
|
n = UNSET_DOUBLE
|
||||||
|
elif the_type is int:
|
||||||
|
n = UNSET_INTEGER
|
||||||
|
else:
|
||||||
|
raise TypeError("unsupported desired type for empty value" + the_type)
|
||||||
|
else:
|
||||||
|
n = the_type(s)
|
||||||
|
else:
|
||||||
|
n = the_type(s or 0)
|
||||||
|
|
||||||
|
if orig_type is bool:
|
||||||
|
n = False if n == 0 else True
|
||||||
|
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ExerciseStaticMethods(klass):
|
||||||
|
|
||||||
|
import types
|
||||||
|
#import code; code.interact(local=dict(globals(), **locals()))
|
||||||
|
for (_, var) in inspect.getmembers(klass):
|
||||||
|
#print(name, var, type(var))
|
||||||
|
if type(var) == types.FunctionType:
|
||||||
|
print("Exercising: %s:" % var)
|
||||||
|
print(var())
|
||||||
|
print()
|
||||||
|
|
||||||
|
def floatToStr(val):
|
||||||
|
return str(val) if val != UNSET_DOUBLE else "";
|
||||||
|
|
||||||
|
|
||||||
|
|
701
vnpy/api/ib/wrapper.py
Normal file
701
vnpy/api/ib/wrapper.py
Normal file
@ -0,0 +1,701 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2018 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
||||||
|
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is the interface that will need to be overloaded by the customer so
|
||||||
|
that his/her code can receive info from the TWS/IBGW.
|
||||||
|
|
||||||
|
NOTE: the methods use type annotations to describe the types of the arguments.
|
||||||
|
This is used by the Decoder to dynamically and automatically decode the
|
||||||
|
received message into the given EWrapper method. This method can only be
|
||||||
|
used for the most simple messages, but it's still huge helper.
|
||||||
|
Also this method currently automatically decode a 'version' field in the
|
||||||
|
message. However having a 'version' field is a legacy thing, newer
|
||||||
|
message use the 'unified version': the agreed up min version of both
|
||||||
|
server and client.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ibapi.common import * # @UnusedWildImport
|
||||||
|
from ibapi.utils import * # @UnusedWildImport
|
||||||
|
from ibapi.contract import (Contract, ContractDetails, DeltaNeutralContract)
|
||||||
|
from ibapi.order import Order
|
||||||
|
from ibapi.order_state import OrderState
|
||||||
|
from ibapi.execution import Execution
|
||||||
|
from ibapi.ticktype import * # @UnusedWildImport
|
||||||
|
from ibapi.commission_report import CommissionReport
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EWrapper:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def logAnswer(self, fnName, fnParams):
|
||||||
|
if logger.isEnabledFor(logging.INFO):
|
||||||
|
if 'self' in fnParams:
|
||||||
|
prms = dict(fnParams)
|
||||||
|
del prms['self']
|
||||||
|
else:
|
||||||
|
prms = fnParams
|
||||||
|
logger.info("ANSWER %s %s", fnName, prms)
|
||||||
|
|
||||||
|
|
||||||
|
def error(self, reqId:TickerId, errorCode:int, errorString:str):
|
||||||
|
"""This event is called when there is an error with the
|
||||||
|
communication or when TWS wants to send a message to the client."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
logger.error("ERROR %s %s %s", reqId, errorCode, errorString)
|
||||||
|
|
||||||
|
|
||||||
|
def winError(self, text:str, lastError:int):
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def connectAck(self):
|
||||||
|
""" callback signifying completion of successful connection """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def marketDataType(self, reqId:TickerId, marketDataType:int):
|
||||||
|
"""TWS sends a marketDataType(type) callback to the API, where
|
||||||
|
type is set to Frozen or RealTime, to announce that market data has been
|
||||||
|
switched between frozen and real-time. This notification occurs only
|
||||||
|
when market data switches between real-time and frozen. The
|
||||||
|
marketDataType( ) callback accepts a reqId parameter and is sent per
|
||||||
|
every subscription because different contracts can generally trade on a
|
||||||
|
different schedule."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickPrice(self, reqId:TickerId , tickType:TickType, price:float,
|
||||||
|
attrib:TickAttrib):
|
||||||
|
"""Market data tick price callback. Handles all price related ticks."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickSize(self, reqId:TickerId, tickType:TickType, size:int):
|
||||||
|
"""Market data tick size callback. Handles all size-related ticks."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickSnapshotEnd(self, reqId:int):
|
||||||
|
"""When requesting market data snapshots, this market will indicate the
|
||||||
|
snapshot reception is finished. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickGeneric(self, reqId:TickerId, tickType:TickType, value:float):
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickString(self, reqId:TickerId, tickType:TickType, value:str):
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickEFP(self, reqId:TickerId, tickType:TickType, basisPoints:float,
|
||||||
|
formattedBasisPoints:str, totalDividends:float,
|
||||||
|
holdDays:int, futureLastTradeDate:str, dividendImpact:float,
|
||||||
|
dividendsToLastTradeDate:float):
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
""" market data call back for Exchange for Physical
|
||||||
|
tickerId - The request's identifier.
|
||||||
|
tickType - The type of tick being received.
|
||||||
|
basisPoints - Annualized basis points, which is representative of
|
||||||
|
the financing rate that can be directly compared to broker rates.
|
||||||
|
formattedBasisPoints - Annualized basis points as a formatted string
|
||||||
|
that depicts them in percentage form.
|
||||||
|
impliedFuture - The implied Futures price.
|
||||||
|
holdDays - The number of hold days until the lastTradeDate of the EFP.
|
||||||
|
futureLastTradeDate - The expiration date of the single stock future.
|
||||||
|
dividendImpact - The dividend impact upon the annualized basis points
|
||||||
|
interest rate.
|
||||||
|
dividendsToLastTradeDate - The dividends expected until the expiration
|
||||||
|
of the single stock future."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def orderStatus(self, orderId:OrderId , status:str, filled:float,
|
||||||
|
remaining:float, avgFillPrice:float, permId:int,
|
||||||
|
parentId:int, lastFillPrice:float, clientId:int,
|
||||||
|
whyHeld:str, mktCapPrice: float):
|
||||||
|
"""This event is called whenever the status of an order changes. It is
|
||||||
|
also fired after reconnecting to TWS if the client has any open orders.
|
||||||
|
|
||||||
|
orderId: OrderId - The order ID that was specified previously in the
|
||||||
|
call to placeOrder()
|
||||||
|
status:str - The order status. Possible values include:
|
||||||
|
PendingSubmit - indicates that you have transmitted the order, but have not yet received confirmation that it has been accepted by the order destination. NOTE: This order status is not sent by TWS and should be explicitly set by the API developer when an order is submitted.
|
||||||
|
PendingCancel - indicates that you have sent a request to cancel the order but have not yet received cancel confirmation from the order destination. At this point, your order is not confirmed canceled. You may still receive an execution while your cancellation request is pending. NOTE: This order status is not sent by TWS and should be explicitly set by the API developer when an order is canceled.
|
||||||
|
PreSubmitted - indicates that a simulated order type has been accepted by the IB system and that this order has yet to be elected. The order is held in the IB system until the election criteria are met. At that time the order is transmitted to the order destination as specified.
|
||||||
|
Submitted - indicates that your order has been accepted at the order destination and is working.
|
||||||
|
Cancelled - indicates that the balance of your order has been confirmed canceled by the IB system. This could occur unexpectedly when IB or the destination has rejected your order.
|
||||||
|
Filled - indicates that the order has been completely filled.
|
||||||
|
Inactive - indicates that the order has been accepted by the system (simulated orders) or an exchange (native orders) but that currently the order is inactive due to system, exchange or other issues.
|
||||||
|
filled:int - Specifies the number of shares that have been executed.
|
||||||
|
For more information about partial fills, see Order Status for Partial Fills.
|
||||||
|
remaining:int - Specifies the number of shares still outstanding.
|
||||||
|
avgFillPrice:float - The average price of the shares that have been executed. This parameter is valid only if the filled parameter value is greater than zero. Otherwise, the price parameter will be zero.
|
||||||
|
permId:int - The TWS id used to identify orders. Remains the same over TWS sessions.
|
||||||
|
parentId:int - The order ID of the parent order, used for bracket and auto trailing stop orders.
|
||||||
|
lastFilledPrice:float - The last price of the shares that have been executed. This parameter is valid only if the filled parameter value is greater than zero. Otherwise, the price parameter will be zero.
|
||||||
|
clientId:int - The ID of the client (or TWS) that placed the order. Note that TWS orders have a fixed clientId and orderId of 0 that distinguishes them from API orders.
|
||||||
|
whyHeld:str - This field is used to identify an order held when TWS is trying to locate shares for a short sell. The value used to indicate this is 'locate'.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def openOrder(self, orderId:OrderId, contract:Contract, order:Order,
|
||||||
|
orderState:OrderState):
|
||||||
|
"""This function is called to feed in open orders.
|
||||||
|
|
||||||
|
orderID: OrderId - The order ID assigned by TWS. Use to cancel or
|
||||||
|
update TWS order.
|
||||||
|
contract: Contract - The Contract class attributes describe the contract.
|
||||||
|
order: Order - The Order class gives the details of the open order.
|
||||||
|
orderState: OrderState - The orderState class includes attributes Used
|
||||||
|
for both pre and post trade margin and commission data."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def openOrderEnd(self):
|
||||||
|
"""This is called at the end of a given request for open orders."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def connectionClosed(self):
|
||||||
|
"""This function is called when TWS closes the sockets
|
||||||
|
connection with the ActiveX control, or when TWS is shut down."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def updateAccountValue(self, key:str, val:str, currency:str,
|
||||||
|
accountName:str):
|
||||||
|
""" This function is called only when ReqAccountUpdates on
|
||||||
|
EEClientSocket object has been called. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def updatePortfolio(self, contract:Contract, position:float,
|
||||||
|
marketPrice:float, marketValue:float,
|
||||||
|
averageCost:float, unrealizedPNL:float,
|
||||||
|
realizedPNL:float, accountName:str):
|
||||||
|
"""This function is called only when reqAccountUpdates on
|
||||||
|
EEClientSocket object has been called."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def updateAccountTime(self, timeStamp:str):
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def accountDownloadEnd(self, accountName:str):
|
||||||
|
"""This is called after a batch updateAccountValue() and
|
||||||
|
updatePortfolio() is sent."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def nextValidId(self, orderId:int):
|
||||||
|
""" Receives next valid order id."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def contractDetails(self, reqId:int, contractDetails:ContractDetails):
|
||||||
|
"""Receives the full contract's definitons. This method will return all
|
||||||
|
contracts matching the requested via EEClientSocket::reqContractDetails.
|
||||||
|
For example, one can obtain the whole option chain with it."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def bondContractDetails(self, reqId:int, contractDetails:ContractDetails):
|
||||||
|
"""This function is called when reqContractDetails function
|
||||||
|
has been called for bonds."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def contractDetailsEnd(self, reqId:int):
|
||||||
|
"""This function is called once all contract details for a given
|
||||||
|
request are received. This helps to define the end of an option
|
||||||
|
chain."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def execDetails(self, reqId:int, contract:Contract, execution:Execution):
|
||||||
|
"""This event is fired when the reqExecutions() functions is
|
||||||
|
invoked, or when an order is filled. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def execDetailsEnd(self, reqId:int):
|
||||||
|
"""This function is called once all executions have been sent to
|
||||||
|
a client in response to reqExecutions()."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def updateMktDepth(self, reqId:TickerId , position:int, operation:int,
|
||||||
|
side:int, price:float, size:int):
|
||||||
|
"""Returns the order book.
|
||||||
|
|
||||||
|
tickerId - the request's identifier
|
||||||
|
position - the order book's row being updated
|
||||||
|
operation - how to refresh the row:
|
||||||
|
0 = insert (insert this new order into the row identified by 'position')
|
||||||
|
1 = update (update the existing order in the row identified by 'position')
|
||||||
|
2 = delete (delete the existing order at the row identified by 'position').
|
||||||
|
side - 0 for ask, 1 for bid
|
||||||
|
price - the order's price
|
||||||
|
size - the order's size"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def updateMktDepthL2(self, reqId:TickerId , position:int, marketMaker:str,
|
||||||
|
operation:int, side:int, price:float, size:int, isSmartDepth:bool):
|
||||||
|
"""Returns the order book.
|
||||||
|
|
||||||
|
tickerId - the request's identifier
|
||||||
|
position - the order book's row being updated
|
||||||
|
marketMaker - the exchange holding the order
|
||||||
|
operation - how to refresh the row:
|
||||||
|
0 = insert (insert this new order into the row identified by 'position')
|
||||||
|
1 = update (update the existing order in the row identified by 'position')
|
||||||
|
2 = delete (delete the existing order at the row identified by 'position').
|
||||||
|
side - 0 for ask, 1 for bid
|
||||||
|
price - the order's price
|
||||||
|
size - the order's size
|
||||||
|
isSmartDepth - is SMART Depth request"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def updateNewsBulletin(self, msgId:int, msgType:int, newsMessage:str,
|
||||||
|
originExch:str):
|
||||||
|
""" provides IB's bulletins
|
||||||
|
msgId - the bulletin's identifier
|
||||||
|
msgType - one of: 1 - Regular news bulletin 2 - Exchange no longer
|
||||||
|
available for trading 3 - Exchange is available for trading
|
||||||
|
message - the message
|
||||||
|
origExchange - the exchange where the message comes from. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def managedAccounts(self, accountsList:str):
|
||||||
|
"""Receives a comma-separated string with the managed account ids."""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def receiveFA(self, faData:FaDataType , cxml:str):
|
||||||
|
""" receives the Financial Advisor's configuration available in the TWS
|
||||||
|
|
||||||
|
faDataType - one of:
|
||||||
|
Groups: offer traders a way to create a group of accounts and apply
|
||||||
|
a single allocation method to all accounts in the group.
|
||||||
|
Profiles: let you allocate shares on an account-by-account basis
|
||||||
|
using a predefined calculation value.
|
||||||
|
Account Aliases: let you easily identify the accounts by meaningful
|
||||||
|
names rather than account numbers.
|
||||||
|
faXmlData - the xml-formatted configuration """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalData(self, reqId: int, bar: BarData):
|
||||||
|
""" returns the requested historical data bars
|
||||||
|
|
||||||
|
reqId - the request's identifier
|
||||||
|
date - the bar's date and time (either as a yyyymmss hh:mm:ssformatted
|
||||||
|
string or as system time according to the request)
|
||||||
|
open - the bar's open point
|
||||||
|
high - the bar's high point
|
||||||
|
low - the bar's low point
|
||||||
|
close - the bar's closing point
|
||||||
|
volume - the bar's traded volume if available
|
||||||
|
count - the number of trades during the bar's timespan (only available
|
||||||
|
for TRADES).
|
||||||
|
WAP - the bar's Weighted Average Price
|
||||||
|
hasGaps -indicates if the data has gaps or not. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def historicalDataEnd(self, reqId:int, start:str, end:str):
|
||||||
|
""" Marks the ending of the historical bars reception. """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def scannerParameters(self, xml:str):
|
||||||
|
""" Provides the xml-formatted parameters available to create a market
|
||||||
|
scanner.
|
||||||
|
|
||||||
|
xml - the xml-formatted string with the available parameters."""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def scannerData(self, reqId:int, rank:int, contractDetails:ContractDetails,
|
||||||
|
distance:str, benchmark:str, projection:str, legsStr:str):
|
||||||
|
""" Provides the data resulting from the market scanner request.
|
||||||
|
|
||||||
|
reqid - the request's identifier.
|
||||||
|
rank - the ranking within the response of this bar.
|
||||||
|
contractDetails - the data's ContractDetails
|
||||||
|
distance - according to query.
|
||||||
|
benchmark - according to query.
|
||||||
|
projection - according to query.
|
||||||
|
legStr - describes the combo legs when the scanner is returning EFP"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def scannerDataEnd(self, reqId:int):
|
||||||
|
""" Indicates the scanner data reception has terminated.
|
||||||
|
|
||||||
|
reqId - the request's identifier"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def realtimeBar(self, reqId: TickerId, time:int, open_: float, high: float, low: float, close: float,
|
||||||
|
volume: int, wap: float, count: int):
|
||||||
|
|
||||||
|
""" Updates the real time 5 seconds bars
|
||||||
|
|
||||||
|
reqId - the request's identifier
|
||||||
|
bar.time - start of bar in unix (or 'epoch') time
|
||||||
|
bar.endTime - for synthetic bars, the end time (requires TWS v964). Otherwise -1.
|
||||||
|
bar.open_ - the bar's open value
|
||||||
|
bar.high - the bar's high value
|
||||||
|
bar.low - the bar's low value
|
||||||
|
bar.close - the bar's closing value
|
||||||
|
bar.volume - the bar's traded volume if available
|
||||||
|
bar.WAP - the bar's Weighted Average Price
|
||||||
|
bar.count - the number of trades during the bar's timespan (only available
|
||||||
|
for TRADES)."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def currentTime(self, time:int):
|
||||||
|
""" Server's current time. This method will receive IB server's system
|
||||||
|
time resulting after the invokation of reqCurrentTime. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def fundamentalData(self, reqId:TickerId , data:str):
|
||||||
|
"""This function is called to receive fundamental
|
||||||
|
market data. The appropriate market data subscription must be set
|
||||||
|
up in Account Management before you can receive this data."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def deltaNeutralValidation(self, reqId:int, deltaNeutralContract:DeltaNeutralContract):
|
||||||
|
"""Upon accepting a Delta-Neutral RFQ(request for quote), the
|
||||||
|
server sends a deltaNeutralValidation() message with the DeltaNeutralContract
|
||||||
|
structure. If the delta and price fields are empty in the original
|
||||||
|
request, the confirmation will contain the current values from the
|
||||||
|
server. These values are locked when the RFQ is processed and remain
|
||||||
|
locked until the RFQ is canceled."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def commissionReport(self, commissionReport:CommissionReport):
|
||||||
|
"""The commissionReport() callback is triggered as follows:
|
||||||
|
- immediately after a trade execution
|
||||||
|
- by calling reqExecutions()."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def position(self, account:str, contract:Contract, position:float,
|
||||||
|
avgCost:float):
|
||||||
|
"""This event returns real-time positions for all accounts in
|
||||||
|
response to the reqPositions() method."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def positionEnd(self):
|
||||||
|
"""This is called once all position data for a given request are
|
||||||
|
received and functions as an end marker for the position() data. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def accountSummary(self, reqId:int, account:str, tag:str, value:str,
|
||||||
|
currency:str):
|
||||||
|
"""Returns the data from the TWS Account Window Summary tab in
|
||||||
|
response to reqAccountSummary()."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def accountSummaryEnd(self, reqId:int):
|
||||||
|
"""This method is called once all account summary data for a
|
||||||
|
given request are received."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def verifyMessageAPI(self, apiData:str):
|
||||||
|
""" Deprecated Function """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def verifyCompleted(self, isSuccessful:bool, errorText:str):
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def verifyAndAuthMessageAPI(self, apiData:str, xyzChallange:str):
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def verifyAndAuthCompleted(self, isSuccessful:bool, errorText:str):
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def displayGroupList(self, reqId:int, groups:str):
|
||||||
|
"""This callback is a one-time response to queryDisplayGroups().
|
||||||
|
|
||||||
|
reqId - The requestId specified in queryDisplayGroups().
|
||||||
|
groups - A list of integers representing visible group ID separated by
|
||||||
|
the | character, and sorted by most used group first. This list will
|
||||||
|
not change during TWS session (in other words, user cannot add a
|
||||||
|
new group; sorting can change though)."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def displayGroupUpdated(self, reqId:int, contractInfo:str):
|
||||||
|
"""This is sent by TWS to the API client once after receiving
|
||||||
|
the subscription request subscribeToGroupEvents(), and will be sent
|
||||||
|
again if the selected contract in the subscribed display group has
|
||||||
|
changed.
|
||||||
|
|
||||||
|
requestId - The requestId specified in subscribeToGroupEvents().
|
||||||
|
contractInfo - The encoded value that uniquely represents the contract
|
||||||
|
in IB. Possible values include:
|
||||||
|
none = empty selection
|
||||||
|
contractID@exchange = any non-combination contract.
|
||||||
|
Examples: 8314@SMART for IBM SMART; 8314@ARCA for IBM @ARCA.
|
||||||
|
combo = if any combo is selected. """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def positionMulti(self, reqId:int, account:str, modelCode:str,
|
||||||
|
contract:Contract, pos:float, avgCost:float):
|
||||||
|
"""same as position() except it can be for a certain
|
||||||
|
account/model"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def positionMultiEnd(self, reqId:int):
|
||||||
|
"""same as positionEnd() except it can be for a certain
|
||||||
|
account/model"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def accountUpdateMulti(self, reqId:int, account:str, modelCode:str,
|
||||||
|
key:str, value:str, currency:str):
|
||||||
|
"""same as updateAccountValue() except it can be for a certain
|
||||||
|
account/model"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def accountUpdateMultiEnd(self, reqId:int):
|
||||||
|
"""same as accountDownloadEnd() except it can be for a certain
|
||||||
|
account/model"""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def tickOptionComputation(self, reqId:TickerId, tickType:TickType ,
|
||||||
|
impliedVol:float, delta:float, optPrice:float, pvDividend:float,
|
||||||
|
gamma:float, vega:float, theta:float, undPrice:float):
|
||||||
|
"""This function is called when the market in an option or its
|
||||||
|
underlier moves. TWS's option model volatilities, prices, and
|
||||||
|
deltas, along with the present value of dividends expected on that
|
||||||
|
options underlier are received."""
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def securityDefinitionOptionParameter(self, reqId:int, exchange:str,
|
||||||
|
underlyingConId:int, tradingClass:str, multiplier:str,
|
||||||
|
expirations:SetOfString, strikes:SetOfFloat):
|
||||||
|
""" Returns the option chain for an underlying on an exchange
|
||||||
|
specified in reqSecDefOptParams There will be multiple callbacks to
|
||||||
|
securityDefinitionOptionParameter if multiple exchanges are specified
|
||||||
|
in reqSecDefOptParams
|
||||||
|
|
||||||
|
reqId - ID of the request initiating the callback
|
||||||
|
underlyingConId - The conID of the underlying security
|
||||||
|
tradingClass - the option trading class
|
||||||
|
multiplier - the option multiplier
|
||||||
|
expirations - a list of the expiries for the options of this underlying
|
||||||
|
on this exchange
|
||||||
|
strikes - a list of the possible strikes for options of this underlying
|
||||||
|
on this exchange """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def securityDefinitionOptionParameterEnd(self, reqId:int):
|
||||||
|
""" Called when all callbacks to securityDefinitionOptionParameter are
|
||||||
|
complete
|
||||||
|
|
||||||
|
reqId - the ID used in the call to securityDefinitionOptionParameter """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def softDollarTiers(self, reqId:int, tiers:list):
|
||||||
|
""" Called when receives Soft Dollar Tier configuration information
|
||||||
|
|
||||||
|
reqId - The request ID used in the call to EEClient::reqSoftDollarTiers
|
||||||
|
tiers - Stores a list of SoftDollarTier that contains all Soft Dollar
|
||||||
|
Tiers information """
|
||||||
|
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def familyCodes(self, familyCodes:ListOfFamilyCode):
|
||||||
|
""" returns array of family codes """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
|
||||||
|
def symbolSamples(self, reqId:int,
|
||||||
|
contractDescriptions:ListOfContractDescription):
|
||||||
|
""" returns array of sample contract descriptions """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def mktDepthExchanges(self, depthMktDataDescriptions:ListOfDepthExchanges):
|
||||||
|
""" returns array of exchanges which return depth to UpdateMktDepthL2"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def tickNews(self, tickerId: int, timeStamp:int, providerCode:str, articleId:str, headline:str, extraData:str):
|
||||||
|
""" returns news headlines"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def smartComponents(self, reqId:int, smartComponentMap:SmartComponentMap):
|
||||||
|
"""returns exchange component mapping"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def tickReqParams(self, tickerId:int, minTick:float, bboExchange:str, snapshotPermissions:int):
|
||||||
|
"""returns exchange map of a particular contract"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def newsProviders(self, newsProviders:ListOfNewsProviders):
|
||||||
|
"""returns available, subscribed API news providers"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def newsArticle(self, requestId:int, articleType:int, articleText:str):
|
||||||
|
"""returns body of news article"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalNews(self, requestId:int, time:str, providerCode:str, articleId:str, headline:str):
|
||||||
|
"""returns historical news headlines"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalNewsEnd(self, requestId:int, hasMore:bool):
|
||||||
|
"""signals end of historical news"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def headTimestamp(self, reqId:int, headTimestamp:str):
|
||||||
|
"""returns earliest available data of a type of data for a particular contract"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def histogramData(self, reqId:int, items:HistogramData):
|
||||||
|
"""returns histogram data for a contract"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalDataUpdate(self, reqId: int, bar: BarData):
|
||||||
|
"""returns updates in real time when keepUpToDate is set to True"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def rerouteMktDataReq(self, reqId: int, conId: int, exchange: str):
|
||||||
|
"""returns reroute CFD contract information for market data request"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def rerouteMktDepthReq(self, reqId: int, conId: int, exchange: str):
|
||||||
|
"""returns reroute CFD contract information for market depth request"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def marketRule(self, marketRuleId: int, priceIncrements: ListOfPriceIncrements):
|
||||||
|
"""returns minimum price increment structure for a particular market rule ID"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def pnl(self, reqId: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float):
|
||||||
|
"""returns the daily PnL for the account"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def pnlSingle(self, reqId: int, pos: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float, value: float):
|
||||||
|
"""returns the daily PnL for a single position in the account"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalTicks(self, reqId: int, ticks: ListOfHistoricalTick, done: bool):
|
||||||
|
"""returns historical tick data when whatToShow=MIDPOINT"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalTicksBidAsk(self, reqId: int, ticks: ListOfHistoricalTickBidAsk, done: bool):
|
||||||
|
"""returns historical tick data when whatToShow=BID_ASK"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def historicalTicksLast(self, reqId: int, ticks: ListOfHistoricalTickLast, done: bool):
|
||||||
|
"""returns historical tick data when whatToShow=TRADES"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def tickByTickAllLast(self, reqId: int, tickType: int, time: int, price: float,
|
||||||
|
size: int, tickAttribLast: TickAttribLast, exchange: str,
|
||||||
|
specialConditions: str):
|
||||||
|
"""returns tick-by-tick data for tickType = "Last" or "AllLast" """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def tickByTickBidAsk(self, reqId: int, time: int, bidPrice: float, askPrice: float,
|
||||||
|
bidSize: int, askSize: int, tickAttribBidAsk: TickAttribBidAsk):
|
||||||
|
"""returns tick-by-tick data for tickType = "BidAsk" """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def tickByTickMidPoint(self, reqId: int, time: int, midPoint: float):
|
||||||
|
"""returns tick-by-tick data for tickType = "MidPoint" """
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
||||||
|
|
||||||
|
def orderBound(self, reqId: int, apiClientId: int, apiOrderId: int):
|
||||||
|
"""returns orderBound notification"""
|
||||||
|
self.logAnswer(current_fn_name(), vars())
|
@ -6,16 +6,16 @@ from datetime import datetime
|
|||||||
from queue import Empty
|
from queue import Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from ibapi import comm
|
from vnpy.api.ib import comm
|
||||||
from ibapi.client import EClient
|
from vnpy.api.ib.client import EClient
|
||||||
from ibapi.common import MAX_MSG_LEN, NO_VALID_ID, OrderId, TickAttrib, TickerId
|
from vnpy.api.ib.common import MAX_MSG_LEN, NO_VALID_ID, OrderId, TickAttrib, TickerId
|
||||||
from ibapi.contract import Contract, ContractDetails
|
from vnpy.api.ib.contract import Contract, ContractDetails
|
||||||
from ibapi.execution import Execution
|
from vnpy.api.ib.execution import Execution
|
||||||
from ibapi.order import Order
|
from vnpy.api.ib.order import Order
|
||||||
from ibapi.order_state import OrderState
|
from vnpy.api.ib.order_state import OrderState
|
||||||
from ibapi.ticktype import TickType
|
from vnpy.api.ib.ticktype import TickType
|
||||||
from ibapi.wrapper import EWrapper
|
from vnpy.api.ib.wrapper import EWrapper
|
||||||
from ibapi.errors import BAD_LENGTH
|
from vnpy.api.ib.errors import BAD_LENGTH
|
||||||
|
|
||||||
from vnpy.trader.gateway import BaseGateway
|
from vnpy.trader.gateway import BaseGateway
|
||||||
from vnpy.trader.object import (
|
from vnpy.trader.object import (
|
||||||
|
Loading…
Reference in New Issue
Block a user