有很多例子显示如何从盈透证券获得特定资产的价格。然而,当我想获得一项资产的整个期权链时,我不知道列出了哪些特定的罢工。期货也是一样,我不知道目前有哪些到期。因此,也就是说,对于选项,我只是循环遍历所有可能的罢工和reqMktData
,并且每100条消息生成一个sleep(1)
,以避免达到每秒请求数的限制。显然,许多这样的消息返回错误"没有为请求找到安全定义"。
这看起来是错误的方法,因为它在不存在的资产上浪费了大量时间。是否有更干净的方法来做到这一点,或为此目的的特殊功能?
实现Donn Lee建议的contractDetailsEnd处理程序。感谢shashkello和Donn Lee。
from ib.ext.Contract import Contract
from ib.ext.ContractDetails import ContractDetails
from ib.opt import ibConnection, message
import time
def watcher(msg):
print msg
def contractDetailsHandler(msg):
contracts.append(msg.contractDetails.m_summary)
def contractDetailsEndHandler(msg):
global DataWait
DataWait = False
con = ibConnection()
con.registerAll(watcher)
con.register(contractDetailsHandler, 'ContractDetails')
con.register(contractDetailsEndHandler, 'ContractDetailsEnd')
con.connect()
contract = Contract()
contract.m_exchange = "SMART"
contract.m_secType = "OPT"
contract.m_symbol = "VTR"
#contract.m_multiplier = "100"
contract.m_currency = "USD"
con.reqContractDetails(1, contract)
contracts = [] # to store all the contracts
DataWait = True ; i = 0
while DataWait and i < 90:
i += 1 ; print i,
time.sleep(1)
con.disconnect()
con.close()
print contracts
我在不久之前开始使用IbPy,并且在示例和上面的答案中也看到了time.sleep
习语。因为ibpy
有一个线程在后台运行,并且消息接收方法/函数因此在该线程中被调用,因此转向基于queue
的方法似乎很自然。
这里是上面两个合同规范的代码和下面的输出。
from __future__ import (absolute_import, division, print_function,)
# unicode_literals)
import sys
if sys.version_info.major == 2:
import Queue as queue
else: # >= 3
import queue
import ib.opt
import ib.ext.Contract
class IbManager(object):
def __init__(self, timeout=20, **kwargs):
self.q = queue.Queue()
self.timeout = 20
self.con = ib.opt.ibConnection(**kwargs)
self.con.registerAll(self.watcher)
self.msgs = {
ib.opt.message.error: self.errors,
ib.opt.message.contractDetails: self.contractDetailsHandler,
ib.opt.message.contractDetailsEnd: self.contractDetailsHandler
}
self.skipmsgs = tuple(self.msgs.keys())
for msgtype, handler in self.msgs.items():
self.con.register(handler, msgtype)
self.con.connect()
def watcher(self, msg):
if isinstance(msg, ib.opt.message.error):
if msg.errorCode > 2000: # informative message
print('-' * 10, msg)
elif not isinstance(msg, self.skipmsgs):
print('-' * 10, msg)
def errors(self, msg):
if msg.id is None: # something is very wrong in the connection to tws
self.q.put((True, -1, 'Lost Connection to TWS'))
elif msg.errorCode < 1000:
self.q.put((True, msg.errorCode, msg.errorMsg))
def contractDetailsHandler(self, msg):
if isinstance(msg, ib.opt.message.contractDetailsEnd):
self.q.put((False, msg.reqId, msg))
else:
self.q.put((False, msg.reqId, msg.contractDetails))
def get_contract_details(self, symbol, sectype, exch='SMART', curr='USD'):
contract = ib.ext.Contract.Contract()
contract.m_symbol = symbol
contract.m_exchange = exch
contract.m_currency = curr
contract.m_secType = sectype
self.con.reqContractDetails(1, contract)
cdetails = list()
while True:
try:
err, mid, msg = self.q.get(block=True, timeout=self.timeout)
except queue.Empty:
err, mid, msg = True, -1, "Timeout receiving information"
break
if isinstance(msg, ib.opt.message.contractDetailsEnd):
mid, msg = None, None
break
cdetails.append(msg) # must be contractDetails
# return list of contract details, followed by:
# last return code (False means no error / True Error)
# last error code or None if no error
# last error message or None if no error
# last error message
return cdetails, err, mid, msg
ibm = IbManager(clientId=5001)
cs = (
('VTR', 'OPT', 'SMART'),
('ES', 'FUT', 'GLOBEX'),
)
for c in cs:
cdetails, err, errid, errmsg = ibm.get_contract_details(*c)
if err:
print('Last Error %d: %s' % (errid, errmsg))
print('-' * 50)
print('-- ', c)
for cdetail in cdetails:
# m_summary is the contract in details
print('Expiry:', cdetail.m_summary.m_expiry)
sys.exit(0) # Ensure ib thread is terminated
输出:Server Version: 76
TWS Time at connection:20160112 23:17:15 CET
---------- <managedAccounts accountsList=D999999>
---------- <nextValidId orderId=1>
---------- <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:usfuture>
---------- <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:eufarm>
---------- <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:cashfarm>
---------- <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:usfarm.us>
---------- <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:ushmds.us>
---------- <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:ilhmds>
---------- <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:cashhmds>
---------- <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:ethmds>
--------------------------------------------------
-- ('VTR', 'OPT', 'SMART')
Expiry: 20160219
Expiry: 20160219
...
...
...
Expiry: 20160819
Expiry: 20160819
--------------------------------------------------
-- ('ES', 'FUT', 'GLOBEX')
Expiry: 20160318
Expiry: 20160617
Expiry: 20160916
Expiry: 20161216
Expiry: 20170317
我自己想出来的。
有一个函数可以请求上市证券的详细信息,reqContractDetails
。下面显示了一些请求E-mini SPX期货(代码ES)的示例代码。
from ib.ext.Contract import Contract
from ib.ext.ContractDetails import ContractDetails
from ib.opt import ibConnection, message
import time
def watcher(msg):
print msg
contracts = [] # to store all the contracts
def contractDetailsHandler(msg):
contracts.append(msg.contractDetails.m_summary)
con = ibConnection()
con.registerAll(watcher)
con.register(contractDetailsHandler, 'ContractDetails')
con.connect()
contract = Contract()
contract.m_symbol = "ES"
contract.m_exchange = "GLOBEX"
contract.m_currency = "USD"
contract.m_secType = "FUT"
con.reqContractDetails(1, contract)
time.sleep(2)
con.disconnect()
现在合同保存在contracts
列表中,我们可以通过以下命令获得所有可用的到期日期:
for c in contracts:
print c.m_expiry
输出:20140919
20141219
20150320
20150619
20150918
很明显,这也可以扩展到选项。