├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── changelog.md ├── demo ├── api_coverage.py ├── example_opt.py ├── fancy_marketdata.py ├── filters.py ├── log_filter.py ├── option_order_data.py ├── reference_jython.py └── reference_python.py ├── ib ├── __init__.py ├── ext │ ├── AnyWrapper.py │ ├── AnyWrapperMsgGenerator.py │ ├── ComboLeg.py │ ├── CommissionReport.py │ ├── Contract.py │ ├── ContractDetails.py │ ├── EClientErrors.py │ ├── EClientSocket.py │ ├── EReader.py │ ├── EWrapper.py │ ├── EWrapperMsgGenerator.py │ ├── Execution.py │ ├── ExecutionFilter.py │ ├── Makefile │ ├── MarketDataType.py │ ├── Order.py │ ├── OrderComboLeg.py │ ├── OrderState.py │ ├── ScannerSubscription.py │ ├── TagValue.py │ ├── TickType.py │ ├── UnderComp.py │ ├── Util.py │ ├── __init__.py │ ├── cfg │ │ ├── AnyWrapper.py │ │ ├── AnyWrapperMsgGenerator.py │ │ ├── ComboLeg.py │ │ ├── CommissionReport.py │ │ ├── Contract.py │ │ ├── ContractDetails.py │ │ ├── EClientErrors.py │ │ ├── EClientSocket.py │ │ ├── EReader.py │ │ ├── EWrapper.py │ │ ├── EWrapperMsgGenerator.py │ │ ├── Execution.py │ │ ├── ExecutionFilter.py │ │ ├── Makefile │ │ ├── MarketDataType.py │ │ ├── Order.py │ │ ├── OrderComboLeg.py │ │ ├── OrderState.py │ │ ├── ScannerSubscription.py │ │ ├── TagValue.py │ │ ├── TickType.py │ │ ├── UnderComp.py │ │ ├── Util.py │ │ └── __init__.py │ └── src │ │ ├── .gitignore │ │ └── Makefile ├── lib │ ├── __init__.py │ ├── logger.py │ └── overloading.py ├── opt │ ├── __init__.py │ ├── connection.py │ ├── dispatcher.py │ ├── message.py │ ├── messagetools.py │ ├── receiver.py │ └── sender.py └── sym │ └── __init__.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | release-* 3 | *.py[oc] 4 | *.sw[nop] 5 | *~ 6 | ._* 7 | .DS_Store 8 | *.bak 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Troy Melhase 2 | All Rights Reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | The name of Troy Melhase may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | recursive-include demo * 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IbPy - Interactive Brokers Python API 2 | 3 | ## NOTE 4 | 5 | Beginning with release 9.73, InteractiveBrokers is now officially supporting a new Python API client (Python 3 only). 6 | This should make this repo superfluous except for Python 2. 7 | 8 | For more info: https://interactivebrokers.github.io/tws-api/#gsc.tab=0 9 | 10 | ## What is IbPy? 11 | 12 | IbPy is a third-party implementation of the API used for accessing the 13 | Interactive Brokers online trading system. IbPy implements functionality that 14 | the Python programmer can use to connect to IB, request stock ticker data, 15 | submit orders for stocks and futures, and more. 16 | 17 | ## Installation 18 | 19 | There is a package maintained on PyPI under the name IbPy2, it's version is in sync 20 | with the tags on GitHub. 21 | 22 | ``` 23 | pip install IbPy2 24 | ``` 25 | 26 | Alternatively, it can be installed from source. From within the IbPy directory, execute: 27 | 28 | ``` 29 | python setup.py install 30 | ``` 31 | 32 | Pip also supports installing directly from GitHub, e.g. if you want commit `83b9d08ed9c850d840a6700d0fb9c3ca164f9bff`, use 33 | 34 | ``` 35 | pip install git+https://github.com/blampe/IbPy@83b9d08ed9c850d840a6700d0fb9c3ca164f9bff 36 | ``` 37 | 38 | ## How do I use IbPy? 39 | 40 | See the IbPy wiki page https://github.com/blampe/IbPy/wiki/Getting-Started 41 | 42 | ## What are the requirements? 43 | 44 | * Python >2.5 or >3.3. Previous versions are not supported. 45 | * Either a running instance of Trader Workstation (TWS) or IB Gateway. 46 | 47 | ## License 48 | 49 | IbPy is distributed under the New BSD License. See the LICENSE file in the 50 | release for details. 51 | 52 | ## Note 53 | 54 | IbPy is not a product of Interactive Brokers, nor is this project affiliated 55 | with IB. 56 | 57 | ## Source code 58 | 59 | https://github.com/blampe/IbPy -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # v0.8.0 2 | 3 | * Some internal reorganization 4 | * Made available as IbPy2 in PyPI 5 | 6 | # Pre v0.8.0 7 | 8 | * TWS API version 9.70 now supported 9 | * Over 60% test coverage and growing 10 | * Fixed outstanding bugs in EReader generated source 11 | * Module ib.opt.logger moved to ib.lib.logger 12 | * Class ib.opt.Connection moved to new ib.opt.connection module 13 | * Added script to filter TWS log files; see `demo/log_filter` 14 | * Added ib.sym package to hold various symbolic constants 15 | * Many small enhancements to ib.opt package -------------------------------------------------------------------------------- /demo/api_coverage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ## 5 | # This script attempts to determine how much of the TWS API is 6 | # available from IbPy. 7 | # 8 | # It's not meant as an example of correct use of the package, nor is 9 | # it an example of correct use of a programming language. 10 | # 11 | ## 12 | import logging 13 | import sys 14 | 15 | from functools import partial 16 | from logging import DEBUG, INFO, WARN, ERROR 17 | from optparse import OptionParser 18 | from random import randint 19 | from time import sleep, strftime, time 20 | 21 | from ib.ext.ComboLeg import ComboLeg 22 | from ib.ext.Contract import Contract 23 | from ib.ext.ExecutionFilter import ExecutionFilter 24 | from ib.ext.Order import Order 25 | from ib.ext.ScannerSubscription import ScannerSubscription 26 | from ib.lib.logger import logger as basicConfig 27 | from ib.opt import ibConnection, message 28 | 29 | 30 | error_msgs = {} 31 | order_ids = [0] 32 | tick_msgs = [] 33 | short_sleep = partial(sleep, 1) 34 | long_sleep = partial(sleep, 10) 35 | generic_tick_keys = '100,101,104,106,165,221,225,236' 36 | 37 | unseen_hints = { 38 | 'OpenOrder' : 'only works with existing order(s) before connecting', 39 | 'RealtimeBar' : 'only works during trading hours', 40 | 'ReceiveFA' : 'does not work with edemo account', 41 | 'UpdateNewsBulletin' : 'news bulletins may not be available', 42 | 'ConnectionClosed' : 'may work if TWS is closed during script', 43 | } 44 | 45 | verbose_levels = { 46 | 3 : DEBUG, 47 | 2 : INFO, 48 | 1 : WARN, 49 | 0 : ERROR, 50 | } 51 | 52 | 53 | def format_error(msg): 54 | which = ('(in %s)' % error_msgs.get(msg)) if error_msgs.get(msg) else '' 55 | return '%8s: %s %s' % (msg.errorCode, msg.errorMsg, which) 56 | 57 | 58 | def format_default(msg): 59 | return ' %s' % msg 60 | 61 | 62 | msg_formatters = { 63 | 'default':format_default, 64 | 'Error':format_error, 65 | } 66 | 67 | 68 | def next_order_id(): 69 | return order_ids[-1] 70 | 71 | 72 | def save_order_id(msg): 73 | order_ids.append(msg.orderId) 74 | 75 | 76 | def save_tick(msg): 77 | tick_msgs.append(msg) 78 | 79 | 80 | def gen_tick_id(): 81 | i = randint(100, 10000) 82 | while True: 83 | yield i 84 | i += 1 85 | if sys.version_info[0] < 3: 86 | gen_tick_id = gen_tick_id().next 87 | else: 88 | gen_tick_id = gen_tick_id().__next__ 89 | 90 | 91 | def make_contract(symbol): 92 | contract = Contract() 93 | contract.m_symbol = symbol 94 | contract.m_secType = 'STK' 95 | contract.m_exchange = 'SMART' 96 | contract.m_primaryExch = 'SMART' 97 | contract.m_currency = 'USD' 98 | contract.m_localSymbol = symbol 99 | return contract 100 | 101 | 102 | def make_order(limit_price): 103 | order = Order() 104 | order.m_minQty = 100 105 | order.m_lmtPrice = limit_price 106 | order.m_orderType = 'MKT' 107 | order.m_totalQuantity = 100 108 | order.m_action = 'BUY' 109 | return order 110 | 111 | 112 | def exec_filter(client_id): 113 | contract = make_contract('NVDA') 114 | filt = ExecutionFilter() 115 | filt.m_clientId = client_id 116 | filt.m_symbol = contract.m_symbol 117 | filt.m_secType = contract.m_secType 118 | filt.m_exchange = contract.m_exchange 119 | return filt 120 | 121 | 122 | def make_msg_counter(rec_map, unrec_map): 123 | for classes in list(message.registry.values()): 124 | for cls in [c for c in classes if True]: 125 | if not cls.__name__.endswith('Pre') and not cls.__name__.endswith('Post'): 126 | rec_map[cls] = [] 127 | def counter(msg): 128 | cls = msg.__class__ 129 | try: 130 | rec_map[cls].append(msg) 131 | except (KeyError, ): 132 | unrec_map.setdefault(cls, []).append(msg) 133 | return counter 134 | 135 | 136 | def make_error_catcher(seq): 137 | def catcher(msg): 138 | seq.append(msg) 139 | return catcher 140 | 141 | 142 | def maybe_verbose(call): 143 | name = call.__name__ 144 | def inner(connection, options): 145 | logging.info('Start %s', name) 146 | start = time() 147 | v = call(connection, options) 148 | finish = time() 149 | logging.info('Finish %s in %0.3f sec', name, finish-start) 150 | return v 151 | inner.__name__ = name 152 | return inner 153 | 154 | 155 | def catch_errors(call): 156 | def inner(connection, options): 157 | errors = [] 158 | catcher = make_error_catcher(errors) 159 | connection.register(catcher, 'Error') 160 | call(connection, options) 161 | connection.unregister(catcher, 'Error') 162 | return errors 163 | inner.__name__ = call.__name__ 164 | return inner 165 | 166 | 167 | def test_000(connection, options): 168 | connection.setServerLogLevel(5) 169 | connection.reqCurrentTime() 170 | connection.reqAccountUpdates(1, 'DF16165') 171 | connection.reqManagedAccts() 172 | connection.requestFA(connection.GROUPS) 173 | connection.replaceFA(connection.GROUPS, '') 174 | connection.reqIds(10) 175 | 176 | 177 | def test_001(connection, options): 178 | ticker_id = gen_tick_id() 179 | subscript = ScannerSubscription() 180 | subscript.numberOfRows(3) 181 | subscript.locationCode('STK.NYSE') 182 | connection.reqScannerSubscription(ticker_id, subscript) 183 | connection.reqScannerParameters() 184 | short_sleep() 185 | connection.cancelScannerSubscription(ticker_id) 186 | 187 | 188 | def test_002(connection, options): 189 | ticker_id = gen_tick_id() 190 | contract = make_contract('NVDA') 191 | connection.reqMktData(ticker_id, contract, generic_tick_keys, False) 192 | short_sleep() 193 | connection.cancelMktData(ticker_id) 194 | 195 | 196 | def test_003(connection, options): 197 | ticker_id = gen_tick_id() 198 | contract = make_contract('GOOG') 199 | connection.reqMktDepth(ticker_id, contract, 10) 200 | short_sleep() 201 | connection.cancelMktDepth(ticker_id) 202 | 203 | 204 | def test_004(connection, options): 205 | connection.reqAllOpenOrders() 206 | connection.reqAutoOpenOrders(True) 207 | connection.reqOpenOrders() 208 | connection.reqExecutions(0, exec_filter(options.clientid)) 209 | 210 | 211 | def test_005(connection, options): 212 | connection.reqNewsBulletins(True) 213 | short_sleep() 214 | connection.cancelNewsBulletins() 215 | 216 | 217 | def test_006(connection, options): 218 | try: 219 | askprice = [m.price for m in tick_msgs 220 | if (getattr(m, 'price', None) is not None) and m.field==2][0] 221 | except (IndexError, ): 222 | askprice = 100.0 223 | order = make_order(askprice) 224 | if options.demo: 225 | connection.placeOrder(id=next_order_id(), 226 | contract=make_contract('NVDA'), 227 | order=order) 228 | #connection.exerciseOptions() 229 | contract = make_contract('NVDA') 230 | connection.reqContractDetails(3, contract) 231 | 232 | 233 | def test_007(connection, options): 234 | endtime = strftime('%Y%m%d %H:%M:%S') 235 | ticker_id = gen_tick_id() 236 | connection.reqHistoricalData( 237 | tickerId=ticker_id, 238 | contract=make_contract('INTC'), 239 | endDateTime=endtime, 240 | durationStr='2 D', 241 | barSizeSetting='30 mins', 242 | whatToShow='TRADES', 243 | useRTH=0, 244 | formatDate=1) 245 | short_sleep() 246 | connection.cancelHistoricalData(ticker_id) 247 | 248 | 249 | def test_008a(connection, options): 250 | c = Contract() 251 | c.m_exchange = 'IDEALPRO' 252 | c.m_symbol = 'MO' 253 | c.m_localSymbol = 'MO1C' 254 | c.m_secType = 'BAG' 255 | c.m_expiry = '200806' 256 | leg1 = ComboLeg() 257 | leg1.m_conId = 123 258 | leg1.m_ratio = 1 259 | leg1.m_exchange = 'ONE' 260 | leg1.m_action = 'SELL' 261 | leg2 = ComboLeg() 262 | leg2.m_conId = 125 263 | leg2.m_ratio = 100 264 | leg2.m_exchange = 'NYSE' 265 | leg2.m_action = 'BUY' 266 | c.m_comboLegs = [leg1, leg2] 267 | connection.reqMktData(1, c, generic_tick_keys, False) 268 | 269 | 270 | def test_008b(connection, options): 271 | def cb(*a, **b): 272 | pass 273 | connection.register(cb, 'ExecDetails') 274 | filtr = exec_filter(options.clientid) 275 | connection.reqExecutions(1, filtr) 276 | c = Contract() 277 | c.m_symbol = 'GOOG' 278 | c.m_secType = 'OPT' 279 | c.m_exchange = 'SMART' 280 | c.m_right = 'CALL' 281 | c.m_strike = 360.0 282 | c.m_expiry = '200806' 283 | connection.reqMktData(2, c, '', False) 284 | long_sleep() 285 | 286 | 287 | def test_009(connection, options): 288 | ticker_id = gen_tick_id() 289 | connection.reqRealTimeBars(ticker_id, make_contract('AAPL'), 5, 'TRADES', 0) 290 | short_sleep() 291 | 292 | 293 | def test_010(connection, options): 294 | connection.reqPositions() 295 | short_sleep() 296 | connection.cancelPositions() 297 | 298 | 299 | def test_011(connection, options): 300 | reqId = gen_tick_id() 301 | connection.reqAccountSummary(reqId, 'All', 'AccountType,NetLiquidation') 302 | short_sleep() 303 | connection.cancelAccountSummary(reqId) 304 | 305 | 306 | def test_999(connection, options): 307 | short_sleep() 308 | connection.eDisconnect() 309 | 310 | def last_wait(connection, options): 311 | pass 312 | 313 | 314 | def name_count(value): 315 | if value.count(':') == 1: 316 | name, count = value.split(':') 317 | try: 318 | count = int(count) 319 | except (TypeError, ValueError, ): 320 | count = 0 321 | else: 322 | name, count = value, 0 323 | return name, count 324 | 325 | 326 | def get_options(): 327 | version = '%prog 0.1' 328 | parser = OptionParser(version=version) 329 | add = parser.add_option 330 | add('-d', '--demo', dest='demo', action='store_true', 331 | help='Server using demo account, safe for placing orders') 332 | add('-m', '--messages', dest='printmsgs', action='store_true', 333 | help='Print message type names and exit') 334 | add('-s', '--show', dest='showmsgs', metavar='MSG[:MAX]', action='append', 335 | help=('Print no more than MAX messages of type MSG, may use ALL to ' 336 | 'print all messages, may be repeated'), default=[]) 337 | add('-n', '--host', dest='host', default='localhost', 338 | help='Name or address of remote server (default: %default)') 339 | add('-p', '--port', dest='port', default=7496, type='int', 340 | help='Port number for remote connection (default: %default)') 341 | add('-c', '--client', dest='clientid', metavar='ID', default=0, type='int', 342 | help='Client id for remote connection (default: %default)') 343 | add('-v', '--verbose', default=0, action='count', 344 | help='Verbose output, may be repeated') 345 | opts, args = parser.parse_args() 346 | return opts 347 | 348 | 349 | def main(options): 350 | basicConfig() 351 | logging.root.setLevel(verbose_levels.get(options.verbose, ERROR)) 352 | 353 | rec_msgs = {} 354 | unrec_msgs = {} 355 | 356 | handler = make_msg_counter(rec_msgs, unrec_msgs) 357 | 358 | ## make_msg_counter fills in the defaults for the rec_msgs dict; now we can 359 | ## print those values and exit if the option is given 360 | if options.printmsgs: 361 | for name in sorted(k[0].typeName for k in list(rec_msgs.keys())): 362 | print(name) 363 | return 364 | 365 | ## if we're still here, we should connect 366 | con = ibConnection(options.host, options.port, options.clientid) 367 | con.registerAll(handler) 368 | con.register(save_order_id, 'NextValidId') 369 | con.register(save_tick, 'TickSize', 'TickPrice') 370 | con.connect() 371 | short_sleep() 372 | 373 | ## and if we've connected, we shoud execute all of the test functions in 374 | ## the module namespace. 375 | calls = [v for k, v in list(globals().items()) if k.startswith('test_')] 376 | for call in sorted(calls, key=lambda f: f.__name__): 377 | call = maybe_verbose(catch_errors(call)) 378 | errors = call(con, options) 379 | for err in errors: 380 | error_msgs[err] = call.__name__ 381 | 382 | type_count = len(rec_msgs) 383 | seen_items = list(rec_msgs.items()) 384 | seen = [(k, v) for k, v in seen_items if v] 385 | unseen = [(k, v) for k, v in seen_items if not v] 386 | 387 | ## adjust the showmsgs option if given --show=all 388 | alls = [v for v in options.showmsgs if 'all' in v.lower()] 389 | if any(alls): 390 | all, count = name_count(alls[0]) 391 | options.showmsgs = ['%s:%s' % (k.typeName, count) for k in list(rec_msgs.keys())] 392 | 393 | ## ready, set, print! 394 | for msg_typename in options.showmsgs: 395 | msg_typename, msg_showmax = name_count(msg_typename) 396 | formatter = msg_formatters.get(msg_typename, msg_formatters['default']) 397 | msgs = [v for k, v in seen_items if k.typeName==msg_typename] 398 | if msgs: 399 | msgs = msgs[0] 400 | if not msg_showmax or msg_showmax > len(msgs): 401 | msg_showmax = len(msgs) 402 | print('\n%s (%s of %s):' % (msg_typename, msg_showmax, len(msgs), )) 403 | for msg in msgs[0:msg_showmax]: 404 | print(formatter(msg)) 405 | else: 406 | if msg_typename in [k.typeName for k in list(rec_msgs.keys())]: 407 | print('\n%s (%s):' % (msg_typename, 0, )) 408 | else: 409 | print('\nMessage type %s not recognized' % (msg_typename, )) 410 | 411 | ## but wait, there's more! here we print a summary of seen message 412 | ## types and associated counts. 413 | if seen: 414 | print('\nSeen Message Types (count):') 415 | for cls, seq in sorted(seen, key=lambda t: t[0].typeName): 416 | print(' %s (%s)' % (cls.__name__, len(seq), )) 417 | else: 418 | print('\nTotal failure; no messages received.') 419 | ## but wait, there's more! here we print a summary of unseen message 420 | ## types and associated counts. 421 | if unseen: 422 | print('\nUnseen Message Types (help):') 423 | for cls, zero in sorted(unseen, key=lambda t: t[0].typeName): 424 | name = cls.__name__ 425 | help = unseen_hints.get(name, '') 426 | print(' %s%s' % (name, ' (%s)' % help if help else '', )) 427 | else: 428 | print('\nAll Message types received.') 429 | ## last but not least we print the seen and unseen totals, and their ratio 430 | print('\nSummary:') 431 | args = (type_count, len(seen), len(unseen), 100*len(seen)/float(type_count)) 432 | print(' total:%s seen:%s unseen:%s coverage:%2.2f%%' % args) 433 | 434 | 435 | if __name__ == '__main__': 436 | try: 437 | main(get_options()) 438 | except (KeyboardInterrupt, ): 439 | print('\nKeyboard interrupt.\n') 440 | -------------------------------------------------------------------------------- /demo/example_opt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ## 5 | # This script is an exmple of using the (optional) ib.opt package 6 | # instead of the regular API. 7 | ## 8 | 9 | from time import sleep 10 | from ib.ext.Contract import Contract 11 | from ib.opt import ibConnection, message 12 | 13 | 14 | def my_account_handler(msg): 15 | print(msg) 16 | 17 | 18 | def my_tick_handler(msg): 19 | print(msg) 20 | 21 | 22 | if __name__ == '__main__': 23 | con = ibConnection() 24 | con.register(my_account_handler, 'UpdateAccountValue') 25 | con.register(my_tick_handler, message.tickSize, message.tickPrice) 26 | con.connect() 27 | 28 | def inner(): 29 | 30 | con.reqAccountUpdates(1, '') 31 | qqqq = Contract() 32 | qqqq.m_symbol = 'QQQQ' 33 | qqqq.m_secType = 'STK' 34 | qqqq.m_exchange = 'SMART' 35 | con.reqMktData(1, qqqq, '', False) 36 | 37 | inner() 38 | sleep(5) 39 | print('disconnected', con.disconnect()) 40 | sleep(3) 41 | print('reconnected', con.reconnect()) 42 | inner() 43 | sleep(3) 44 | 45 | print('again disconnected', con.disconnect()) 46 | -------------------------------------------------------------------------------- /demo/fancy_marketdata.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from ib.ext.Contract import Contract 5 | from ib.opt import ibConnection, message 6 | from time import sleep 7 | 8 | # print all messages from TWS 9 | def watcher(msg): 10 | print msg 11 | 12 | # show Bid and Ask quotes 13 | def my_BidAsk(msg): 14 | if msg.field == 1: 15 | print ('%s:%s: bid: %s' % (contractTuple[0], 16 | contractTuple[6], msg.price)) 17 | elif msg.field == 2: 18 | print ('%s:%s: ask: %s' % (contractTuple[0], contractTuple[6], msg.price)) 19 | 20 | def makeStkContract(contractTuple): 21 | newContract = Contract() 22 | newContract.m_symbol = contractTuple[0] 23 | newContract.m_secType = contractTuple[1] 24 | newContract.m_exchange = contractTuple[2] 25 | newContract.m_currency = contractTuple[3] 26 | newContract.m_expiry = contractTuple[4] 27 | newContract.m_strike = contractTuple[5] 28 | newContract.m_right = contractTuple[6] 29 | print ('Contract Values:%s,%s,%s,%s,%s,%s,%s:' % contractTuple) 30 | return newContract 31 | 32 | if __name__ == '__main__': 33 | con = ibConnection() 34 | con.registerAll(watcher) 35 | showBidAskOnly = True # set False to see the raw messages 36 | if showBidAskOnly: 37 | con.unregister(watcher, message.tickSize, message.tickPrice, 38 | message.tickString, message.tickOptionComputation) 39 | con.register(my_BidAsk, message.tickPrice) 40 | con.connect() 41 | sleep(1) 42 | tickId = 1 43 | 44 | # Note: Option quotes will give an error if they aren't shown in TWS 45 | contractTuple = ('QQQQ', 'STK', 'SMART', 'USD', '', 0.0, '') 46 | #contractTuple = ('QQQQ', 'OPT', 'SMART', 'USD', '20070921', 47.0, 'CALL') 47 | #contractTuple = ('ES', 'FUT', 'GLOBEX', 'USD', '200709', 0.0, '') 48 | #contractTuple = ('ES', 'FOP', 'GLOBEX', 'USD', '20070920', 1460.0, 'CALL') 49 | #contractTuple = ('EUR', 'CASH', 'IDEALPRO', 'USD', '', 0.0, '') 50 | stkContract = makeStkContract(contractTuple) 51 | print ('* * * * REQUESTING MARKET DATA * * * *') 52 | con.reqMktData(tickId, stkContract, '', False) 53 | sleep(15) 54 | print ('* * * * CANCELING MARKET DATA * * * *') 55 | con.cancelMktData(tickId) 56 | sleep(1) 57 | con.disconnect() 58 | sleep(1) 59 | -------------------------------------------------------------------------------- /demo/filters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ## 5 | # This script is an exmple of using the ib.opt.messagetools module. 6 | ## 7 | 8 | from time import sleep 9 | from ib.ext.Contract import Contract 10 | from ib.ext.TickType import TickType 11 | from ib.opt import ibConnection, message 12 | from ib.opt import messagetools 13 | 14 | all_messages = [] 15 | 16 | def my_account_handler(msg): 17 | all_messages.append(msg) 18 | print msg 19 | 20 | 21 | 22 | def my_tick_handler(msg): 23 | all_messages.append(msg) 24 | print msg 25 | 26 | 27 | # wrap our tick handler to get only bid size 28 | tick_handler = messagetools.messageFilter(my_tick_handler, lambda m:m.field==TickType.BID_SIZE) 29 | 30 | 31 | # another way to do it 32 | tick_handler = messagetools.bidSizeFilter(my_tick_handler) 33 | 34 | 35 | # wrap our account handler to get only cash values 36 | cash_handler = messagetools.messageFilter(my_account_handler, lambda m:m.key.lower().count('cash')) 37 | 38 | 39 | # try out the new before and after send messages 40 | def pre_req_account_updates(msg): 41 | all_messages.append(msg) 42 | print 'pre account updates: ', msg 43 | return True 44 | 45 | def post_req_account_updates(msg): 46 | all_messages.append(msg) 47 | print 'post account updates: ', msg 48 | 49 | 50 | if __name__ == '__main__': 51 | con = ibConnection() 52 | #con.enableLogging() 53 | con.register(cash_handler, 'UpdateAccountValue') 54 | con.register(tick_handler, message.tickSize, message.tickPrice) 55 | con.register(pre_req_account_updates, 'ReqAccountUpdatesPre') 56 | con.register(post_req_account_updates, 'PostReqAccountUpdatesPost') 57 | con.connect() 58 | 59 | def inner(): 60 | con.reqAccountUpdates(1, '') 61 | qqqq = Contract() 62 | qqqq.m_symbol = 'QQQQ' 63 | qqqq.m_secType = 'STK' 64 | qqqq.m_exchange = 'SMART' 65 | con.reqMktData(1, qqqq, '', False) 66 | 67 | inner() 68 | sleep(5) 69 | -------------------------------------------------------------------------------- /demo/log_filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import logging 3 | import optparse 4 | import re 5 | import sys 6 | 7 | 8 | usage = 'usage: %prog filename [options]' 9 | version = '%prog 0.1' 10 | log_format_default = '%(asctime)s ' + logging.BASIC_FORMAT 11 | log_level_names = logging._levelNames 12 | log_level_default = log_level_names[logging.CRITICAL] 13 | 14 | 15 | log_level_map = { 16 | 'SYS' : logging.critical, 17 | 'ERR' : logging.error, 18 | 'WARN' : logging.warning, 19 | 'INFO' : logging.info, 20 | 'DET' : logging.debug, 21 | } 22 | 23 | 24 | log_pat = (r'(?P\w+)\s+(?P