Получение параметров перечисленных опционов и фьючерсов в Interactive Brokers API

Существует множество примеров, показывающих, как получить цену конкретного актива от Interactive Brokers. Однако когда я хочу получить всю цепочку опционов на один актив, я не знаю, какие именно страйки перечислены. То же самое и с фьючерсами, я не знаю, какие экспирации доступны в данный момент. Итак, то есть для вариантов, я просто перебираю все возможные страйки и reqMktData для каждого, а также делаю sleep(1) каждые 100 сообщений, чтобы не достичь предела количества запросов в секунду. Очевидно, что многие из этих сообщений возвращаются с ошибкой «Для запроса не найдено определение безопасности».

Это выглядит как неправильный подход, поскольку он тратит много времени на несуществующие активы. Есть ли более чистый способ сделать это или специальная функция для этой цели?


person sashkello    schedule 14.08.2014    source источник


Ответы (3)


Реализация обработчика для contractDetailsEnd, предложенного Донном Ли. Спасибо и шашкелло, и Донну Ли.

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
person Michael Gruen    schedule 02.09.2014

Я начал работать с 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
person mementum    schedule 12.01.2016
comment
Интересная идея, но разве вы не должны использовать очередь для вывода сообщений вместо ввода? Ограничение на количество сообщений, отправляемых на сервер в секунду, не иначе - person Roman Rdgz; 29.02.2016
comment
Queue предназначен для приема входящих (TWS -> Клиент) сообщений, включая ситуации с ошибками, и в то же время обеспечивает традиционный блокирующий интерфейс для вызывающей стороны. Дросселированием при движении в противоположном направлении, конечно же, также можно управлять с помощью Queue (я сделал это для игрушечного приложения, которое я разработал много лет назад для другой формы торговли, называемой ставками с github.com/mementum/bfplusplus) - person mementum; 29.02.2016
comment
@mementum, отличный фрагмент. Часть приемника ясна как день, но не могли бы вы подробнее рассказать о предоставлении вызывающей стороне традиционного интерфейса блокировки? - person dave; 26.07.2016

Разобрался с этим сам.

Существует функция, которая может запросить информацию о листинговых ценных бумагах, 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

Очевидно, это можно распространить и на опции.

person sashkello    schedule 18.08.2014
comment
Да, это сработало для меня. Но вместо «time.sleep(n)» и затем разъединения() мой код ожидает contractDetailsEnd и использует обработчик, зарегистрированный с conn.register(contractDetailsEndHandler, 'ContractDetailsEnd'). Таким образом, он отключается после получения contractDetailsEnd, что должно быть более надежным (в случае задержки интернета и тому подобного). - person Donn Lee; 31.08.2014