GithubHelp home page GithubHelp logo

komnomnomnom / swigibpy Goto Github PK

View Code? Open in Web Editor NEW
154.0 45.0 36.0 1.27 MB

Third party Interactive Brokers Python API generated from TWS C++ API using SWIG.

Home Page: http://github.com/Komnomnomnom/swigibpy/

License: Other

C 0.20% C++ 93.15% Python 6.65%

swigibpy's Introduction

swigibpy

version

0.5.0

An Interactive Brokers Python API, auto-generated from the official C++ API using SWIG.

Installation

Use pip (recommended)

pip install swigibpy

Alternatively download a release, extract it and run

python setup.py install

Getting Started

TWS or IB Gateway must be running. To use swigibpy simply import the swigibpy module into your code, define an EWrapper sub-class and create a swigibpy.EPosixClientSocket instance.

Use the methods of your swigibpy.EPosixClientSocket instance to send requests to Interactive Brokers. All requests are asynchronous and any responses, notifications and warnings are handled by the methods of your EWrapper subclass.

In the following simple example a request is made to Interactive Brokers for some historical data for the GOOG ticker, and the response is printed to the console.

from datetime import datetime

import swigibpy


class MyEWrapper(swigibpy.EWrapperVerbose):

    def historicalData(self, reqId, date, open, high, low, close, volume,
           barCount, WAP, hasGaps):

      if date[:8] == 'finished':
          print("History request complete")
      else:
          date = datetime.strptime(date, "%Y%m%d").strftime("%d %b %Y")
          print(("History %s - Open: %s, High: %s, Low: %s, Close: "
                 "%s, Volume: %d") % (date, open, high, low, close, volume))

myWrapper = MyEWrapper()

tws = swigibpy.EPosixClientSocket(myWrapper, reconnect_auto=True)

tws.eConnect("", 7496, 42)

contract = swigibpy.Contract()
contract.exchange = "SMART"
contract.symbol = "GOOG"
contract.secType = "STK"
contract.currency = "USD"
today = datetime.today()

tws.reqHistoricalData(2, contract, today.strftime("%Y%m%d %H:%M:%S %Z"),
                      "1 W", "1 day", "TRADES", 0, 1, None)

See the examples for some more simple demos of using swigibpy. For a more in-depth introduction Rob Carver has written a nice series of blog posts on getting started with swigibpy and the Interative Brokers API.

For documentation on the methods of EPosixClientSocket, EWrapper and other API reference refer to the C++ API documentation.

Note that unlike the C++ API swigibpy will automatically poll TWS for messages, see Message Polling for more about this.

Error Handling

If TWS reports an error then the EWrapper methods error and winError will be called as described in the TWS C++ API documentation.

Additionally swigibpy augments EWrapper with an extra error handling method.

def pyError(self, type, value, traceback)

which will be called if an exception is raised during execution of one of your EWrapper Python methods. The default behaviour is to print the exception to standard error, but you can override the pyError method to implement your own handling. See the python docs for sys.exc_info() for details on the method's arguments.

EWrapper Utility Classes

Normally subclassing EWrapper means having to tiresomely provide an implementation for every method defined by EWrapper. Happily swigibpy adds two EWrapper subclasses which can help.

EWrapperVerbose implements every EWrapper method and by default just prints a message to standard out every time one of its methods is invoked. The message printed includes the arguments that were passed. Useful for development and debugging.

EWrapperQuiet implements every EWrapper method and silently ignores any calls that have not been implemented by you. Useful if you are not interested in defining every EWrapper method.

Auto-reconnect

swigibpy can automatically reconnect to TWS / IB Gateway in case of connection loss or restart. To enable this behaviour use the reconnect_auto argument added to EPosixClientSocket.

tws = EPosixClientSocket(mywrapper, reconnect_auto=True)

Auto-reconnect is disabled by default.

Notes

The yield parameter in CommissionReport clashes with a Python reserved keyword so it is renamed to _yield.

Advanced Usage

Message Polling

By default swigibpy will create a background thread (swigibpy.TWSPoller) to automatically poll TWS for messages. If you wish to disable this behaviour and handle polling yourself use the poll_auto argument added to EPosixClientSocket

tws = EPosixClientSocket(mywrapper, poll_auto=False)

or

tws = EPosixClientSocket(mywrapper)
...
tws.poll_auto = False

The TWS C++ API performs non-blocking socket I/O to communicate with TWS, swigibpy's background thread uses socket select to poll for incoming messages.

Patches

Apart from a few trivial patches to aid compilation and interoperability with Python swigibpy does not alter the TWS C++ API code in any way.

Contribute

swigibpy is open source so feel free to get involved. If something doesn't work, or you'd like to add a feature, example or some documentation please create a pull request, if you need help open an issue.

For development switch to the swigibpy code directory and build the extension in the current dir.

python setup.py build_ext --inplace

Apart from the patches all of swigibpy's code is defined in a SWIG interface file. The C++ and Python wrapper is then generated using SWIG.

The TWS API included in the repository has already been patched and the repository already includes the SWIG generated code but if you modify the interface file or need to rerun these steps the commands are

python setup.py swigify

to regenerate the SWIG wrappers (SWIG 3.0+ required), and

python setup.py patchify

to reapply the patches to the TWS API (specify the option -r if you want to un-apply the patches and get back to unaltered TWS code).

Windows Users

swigibpy provides a wrapper around the TWS C++ API so it must be compiled for your target platform during installation. While this should 'just work' for Linux and OSX, Windows users might need to do some extra work.

Only some basic tips are given here, for more see Installing Python Modules in the official documentation.

MinGW Compilation

Download and install MinGW and follow the steps to add MinGW to your path.

To get pip to use MinGW as the compiler edit or create a file named distutils.cfg in [PYTHON LOCATION]\Lib\distutils where [PYTHON LOCATION] is the path to your Python install, e.g. C:\Python27. Add the following to distutils.cfg.

[build]
compiler=mingw32

then use the pip command given above in Installation and with a bit of luck, you're done!

Alternatively you can download a release and build the package directly. To build and install manually use

python setup.py build -c mingw32
python setup.py install

This has been verified to work using MinGW and Python 2.7 on Windows 7, Vista, and XP.

Visual Studio Compilation

Several users have reported success building swigibpy with Visual Studio, with a few caveats:

  • Distutils has issues building with anything later than Visual Studio 2008 (version 9).
  • Visual Studio 11 doesn't like the /MD compile flag, which distutils adds. For a workaround see here.

License

swigibpy original code is free software under the New BSD license.

Interactive Brokers propriety C++ API is copyright Interactive Brokers LLC. swigibpy is in no way supported or endorsed by Interactive Brokers LLC.


swigibpy's People

Contributors

aidoom avatar komnomnomnom avatar olafsep01 avatar sandrinr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swigibpy's Issues

Undefined symbol error when calling swigibpy

I've tried to install the module in several ways on different versions of Python (2.7.10, 3.4 and 3.5) on Ubuntu 15.10 locally and in anaconda; I've tried pip install and a manual installation (setup install) but it makes no difference to the final result which is an import error. Incidentally, I'm able to use the IB API using ibpy and the Java interface but I'm really interested in getting your code to work. For completeness here is the full stack with the error message:

(py3k)bruno@bruno-VGN-SZ791N:~/IBJts$ python -c 'import swigibpy as ib; print(ib.version)'
Traceback (most recent call last):
File "", line 1, in
File "/home/bruno/anaconda/envs/py3k/lib/python3.5/site-packages/swigibpy-0.5.0-py3.5-linux-x86_64.egg/swigibpy.py", line 43, in
_swigibpy = swig_import_helper()
File "/home/bruno/anaconda/envs/py3k/lib/python3.5/site-packages/swigibpy-0.5.0-py3.5-linux-x86_64.egg/swigibpy.py", line 39, in swig_import_helper
_mod = imp.load_module('_swigibpy', fp, pathname, description)
File "/home/bruno/anaconda/envs/py3k/lib/python3.5/imp.py", line 242, in load_module
return load_dynamic(name, filename, file)
File "/home/bruno/anaconda/envs/py3k/lib/python3.5/imp.py", line 342, in load_dynamic
return _load(spec)
ImportError: /home/bruno/anaconda/envs/py3k/lib/python3.5/site-packages/swigibpy-0.5.0-py3.5-linux-x86_64.egg/_swigibpy.cpython-35m-x86_64-linux-gnu.so: undefined symbol: _ZTVNSt7__cxx1115basic_stringbufIcSt11char_traitsIcESaIcEEE

Better error handling in poll thread

Poll thread outer loop could catch and print all but fatal errors, making it more robust and encouraging client code to deal with errors is they need to.

fails with VS11

below is the error msg

"\afx.h(24) : fatal error C1189: #error : Building MFC application with /MD[d](CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]"

How to get it to work with Python 3?

Hi,

How do I run swigibpy with Python 3?

I tried to build as instructed in the earlier issue (#4):

swig -v -c++ -python -threads -o swig_wrap.cpp -outdir .. -modern -fastdispatch -nosafecstrings -noproxydel -fastproxy -fastinit -fastunpack -fastquery -modernargs -nobuildnone ../swigify_ib.i

python setup.py build_ext --build-lib .

Second step generates thousands of GCC errors.

I also tried the following:

Build using:
python setup.py build_ext --build-lib .

Then copy contractdetails.py to the root directory. After that I converted the contractdetails.py to Python 3 compatible code using "2to3 -w contractdetails.py". Now I can run that script with Python 2 but I get error when I try to run it with Python 3:

Traceback (most recent call last):
File "contractdetails.py", line 8, in
from swigibpy import EWrapper, EPosixClientSocket, Contract
File "/home/seb/sources/swigibpy/swigibpy.py", line 34, in
_swigibpy = swig_import_helper()
File "/home/seb/sources/swigibpy/swigibpy.py", line 30, in swig_import_helper
_mod = imp.load_module('_swigibpy', fp, pathname, description)
File "/usr/lib/python3.3/imp.py", line 188, in load_module
return load_dynamic(name, filename, file)
ImportError: /home/seb/sources/swigibpy/_swigibpy.so: undefined symbol: PyClass_Type

historicaldata.py missing 'error' method in 'HistoricalDataExample' callback object

class HistoricalDataExample(EWrapper) is missing an 'error' method

Without this, I get the following output with the 'TWS Warning' going to stderr instead of stdout. Python never goes into the 'historicalData' method.

Requesting historical data for GOOG
TWS Warning - 2106: HMDS data farm connection is OK:ushmds
 =====================================================================
History requested, waiting for TWS responses
=====================================================================

******************* Press ENTER to quit when done *******************

TWS Warning - 2106: HMDS data farm connection is OK:ushmds

adding an 'error' method seems to solve the issue.

My system info:

> yolk -l | grep swig
swigibpy        - 0.4          - active 
> uname -a
Linux hostname 3.5.0-27-generic #46~precise1-Ubuntu SMP Tue Mar 26 19:33:21 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
> cat API_VersionNum.txt 
API_Version=9.68

Historical data request failure causes subsequent requests to fail with same error

Request DELL, it fails.

Traceback (most recent call last):
  File "/Users/kieran/Work/git/swigibpy/swigibpy.py", line 1203, in run
    self._tws.checkMessages()
  File "/Users/kieran/Work/git/swigibpy/swigibpy.py", line 1467, in error
    raise TWSError(errorCode, errorString)
TWSError: 162: 'Historical Market Data Service error message:HMDS query returned no data: DELL@SMART Trades'

Now request GOOG, it fails with same error

Traceback (most recent call last):
  File "/Users/kieran/Work/git/swigibpy/swigibpy.py", line 1203, in run
    self._tws.checkMessages()
  File "/Users/kieran/Work/git/swigibpy/swigibpy.py", line 1467, in error
    raise TWSError(errorCode, errorString)
TWSError: 162: 'Historical Market Data Service error message:HMDS query returned no data: DELL@SMART Trades'

TWS Error when calling placeOrder from placeorder.py

When calling placeorder.py from the examples, I get this error

TWS Error - 321: Error validating request:-'le' : cause - You must specify an account.
Failed to fill order

I was wondering if anyone knew how to fix it. I think I need to specify the account somewhere. I am using a demo account and there is a list of 2 accounts in my TWS under accounts.

I can get historic data from the TWS but unable to place orders.

unable to add a comboLeg to contract

I am unable to add a comboLeg to a contract:

make comboLeg:
leg1 = ComboLeg()
leg1.conId = conId
leg1.ratio = 1
leg1.action = "BUY"
leg1.exchange = "FTA"
leg1.openClose = 0
leg1.shortSaleSlot = 0
leg1.designatedLocation = ""

contract = Contract()
contract.symbol = "EOE" # abitrary value only combo orders
contract.secType = "BAG" # BAG is the security type for COMBO order
contract.exchange = "FTA";
contract.currency = "EUR";
contract.comboLegs = ???? <- here I want to add leg1.

In the C++ manual of IB in "Placing a Combination Order":
contract.comboLegs = addAllLegs; //including combo order in contract object.

My C++ knowledge is very limited, but I understand comboLegs is a
Vector. But I do not know how to translate this to python.

AlgoParams not working

Received via Email.

I have been successfully using swigibpy for a while but I now I would
like to place an Algo order and I am not sure how to pass the TagValue
list to algoParams property of Order Class

I have tried the following:

contract = Contract()
order = Order()
contract.exchange ='SMART'
contract.symbol =  'MSFT'
contract.secType = 'STK'
contract.currency = 'USD'
order.action = 'BUY'
order.totalQuantity = 100
order.orderType = 'LMT'
order.algoStrategy = 'ArrivalPx'
order.lmtPrice =  0.14

Tried:

order.algoParams.append(TagValue('maxPctVol','0.01'))
---------------------------------------------------------------------------
SystemError                               Traceback (most recent call last)
/home/mauro/pytrader.dev/<ipython-input-15-2f2429c364fd> in <module>()
----> 1 order.algoParams.append(TagValue('maxPctVol','0.01'))

SystemError: error return without exception set

Tried:

order.algoParams.append(TagValue('maxPctVol','0.01').this)
order.algoParams.append(TagValue('riskAversion','Aggressive').this)
order.algoParams.append(TagValue('startTime','9:00:00 EST').this)
order.algoParams.append(TagValue('endTime','15:00:00 EST').this)
order.algoParams.append(TagValue('forceCompletion','1').this)
order.algoParams.append(TagValue('allowPastEndTime','1').this)

and seems to go ok, but when placing the order I get: "Algo attributes
validation failed: 'Max Percentage' is invalid: value is required"
So obviously, the attributes are not being set ..

Python 3.3 socket exception

From @azyr in #25

Whenever an exception gets raised (from my erroneous code) it somehow gets caught and I only get some useless output on EWrapper.Error. The output comes with code 509 and says: "Exception caught while reading socket - std::exception". This is very annoying because now I have no idea what I did wrong. Any ideas how can I raise the exceptions unhandled so I will know exactly what is going on?

Polling thread dies when IB sends unexpected message

Placing an order after trading hours causes the polling thread to abort with the following message:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Tools\Python27\lib\threading.py", line 551, in __bootstrap_inner
    self.run()
  File "C:\Tools\Python27\lib\site-packages\swigibpy.py", line 1224, in run
    self._tws.checkMessages()
  File "C:\Tools\Python27\lib\site-packages\swigibpy.py", line 1496, in error
    raise TWSError(errorCode, errorString)
TWSError: 399: 'Order Message:\nBUY 1 DELL NASDAQ.NMS\nWarning: your order will
not be placed at the exchange until 2012-11-23 09:30:00 US/Eastern'

Note that the IB message is not an error, just a benign warning, yet swigibpy throws an exception that cannot be caught in user code.
I tried to override error function in a subclass of EWrapper (class PlaceOrderExample) but for some reason that callback never gets called. The ability to override error and winError would be very useful to allow the program to handle such cases gracefully.

swigibpy: 0.3
python: 2.7.3
TWS: 931.3
OS: Windows 7 64-bit

Account values not updating

hello everyone,

I am having an issue with updating the account values using swigibpy and api access through the ib gateway. I am placing a call to return the cash balance each cycle of my program loop, however while it gets the initial balance fine it then will not update this value each cycle despite calling the same call. if the code is stopped and then started again it will again get the correct value the first time and then keep that value indefinitely until the program is restarted again.

any help is greatly appreciated.

Data for multiple tickers

I am trying to get data for multiple tickers using swigibpy. Following is my code.

from swigibpy import EWrapper, EPosixClientSocket, Contract, Order, TagValue, TagValueList, LAST, LAST_SIZE, CLOSE
from time import sleep
class getTickData(EWrapper):
    def openOrderEnd(self):
        '''Not relevant for our example'''
        pass

    def execDetails(self, id, contract, execution):
        '''Not relevant for our example'''
        pass

    def managedAccounts(self, openOrderEnd):
        '''Not relevant for our example'''
        pass


    def nextValidId(self, validOrderId):
        '''Capture the next order id'''
        global orderId
        orderId = validOrderId

    def orderStatus(self, id, status, filled, remaining, avgFillPrice, permId,
                    parentId, lastFilledPrice, clientId, whyHeld):

        print(("Order #%s - %s (filled %d, remaining %d, avgFillPrice %f,"
               "last fill price %f)") % (
                  id, status, filled, remaining, avgFillPrice, lastFilledPrice))

    def tickString(self, tickerId, tickType, value):
        pass

    def tickOptionComputation(self, tickerId, field, impliedVolatility, delta):
        pass

    def tickSize(self, tickerId, field, size):
        global lastSize, tickData
        tickData[field] = size
        if field == LAST_SIZE:
            lastSize = size

    def tickGeneric(self,tickerId,tickType,value):
        pass

    def tickPrice(self,tickerId,field, price, canAutoExecute):
        global lastPrice, tickData
        tickData[field] = price
        if field == CLOSE:
            lastPrice = price

    def tickSnapshotEnd(self, req_id):
        pass
def make_contract(symbol,sectype,exchange,currency,expiry,strike,right):
    cont = Contract()
    cont.symbol = symbol
    cont.secType = sectype
    cont.exchange = exchange
    cont.currency = currency
    cont.expiry = expiry
    cont.strike = strike
    cont.right = right
    return cont
symbols = ['MSFT','C']#,'INTC','GOOG']
if __name__ == '__main__':
    callback = getTickData()
    tws = EPosixClientSocket(callback)
    tws.eConnect("",7496,20)
    req_id=0
    for symbol in symbols:
        lastPrice = lastSize = None
        tickData = {}
        symbol_contract = make_contract(symbol, 'STK', 'SMART', 'USD', '', 0.0, '')
        tws.reqMktData(req_id,symbol_contract,'',True)
        while lastPrice is None:
            print 'For symbol %s lastPrice is still None' % symbol
            sleep(1)
        print tickData
        tws.cancelMktData(req_id)
        req_id += 1

tws.eDisconnect()

When I run the above code for some tickers I am not getting the tickPrice for CLOSE field hence. Same code works fine some times. Please look into this issue and let me know if I am doing some thing wrong.

pip install (of 0.4.1) fails in Windows under Python 3.5.1 using VS2015 compiler

When attempting to install swigibpy using pip and Python 3.5.1 in Windows, I received the following error when attempting to compile swigibpy/IB/Shared/EClientSocketBase.cpp:

Macro definition of snprintf conflicts with Standard Library function declaration

A quick search on Google yielded a very helpful result. It appears that until now, many libraries (including IB API) used snprintf by defining it as _snprintf, since _snprintf was supported.

As of VS2015 (e.g. VC14), snprintf is officially supported, and it should therefore never be #define'd.

The offending line is in swigibpy/IB/Shared/StdAfx.h (8):

#define snprintf _snprintf

It would appear that IB fixed this in a release following the version included with swigibpy 0.4.1. I believe that this is also specific to the Python 3.x releases because Python 2.x builds point to earlier version of the VC++ compiler.

Not sure if this is super urgent given the pending push of version 0.5, but wanted to raise it in case it is a forcing function to move things along.

Reconnection feature in TWSPoller

Hi

I was unable to find an email address of yours on your Guthub page so I am contacting you through opening an issue.

Recently you committed a change introducing a reconnection feature into the TWSPoller thread. Looking at this change I find it to be pretty complicated and I am not entirely convinced whether it does even belong there. You are diverging from the functionality offered by the original IB library quite a bit with this change. Wouldn't it be better to keep the TWSPoller as simple as possible and refrain from changing IB library functions and move such functionality into a separate IB utility library?

I do not want to tell you how you have to shape your library ;-), so I asking you what are your plans regarding the TWSPoller thread? Do you want to keep it that way or even extend it? If yes, then I will probably maintain a fork with the previous version of this TWSPoller thread.

BTW: From reading the code right you will end up in an endless loop when disabling the auto-connect feature and loosing the connection to TWS.

Regards,
Raffaele

Installation

I have tried to install swigibpy on Anaconda on both 32 and 64bit and can not build with MinGW on Windows 8.1 and 7.

I have tried to use Python 2.7.10, does not work either.

I have only been able to have it work on Python 32-bit 2.7.9 and it works great on both Windows 7 and 8.1.

poll interval does not work

Hi Kieran,

Yesterday I was not able to test TWS was down:
adding the poll interval (issue 5) does not work:
def eConnect(self, _args) should be def eConnect(self, *args, *_kwargs)

EWrapper::commissionReport not implemented

Hi,
I am getting this error after updating TWS to the latest version, build 944.3c (older version 939 not supported as of May 1). Looks like a new virtual method needs to be implemented? I am not familiar with C++ and where all the source/compiled files go, so can use some help/tips.
Thanks.

Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "/usr/local/lib/python2.7/dist-packages/swigibpy.py", line 1652, in run
ok = self._tws.checkMessages()
File ".py", line 232, in orderStatus
....
NotImplementedError: SWIG director pure virtual method called EWrapper::commissionReport

Allow custom poll interval

Email correspondance with @olafsep01

The sleep time in the Polling class I made variable:
swigify_ib.i:

%pythoncode %{
import threading
import time
class TWSPoller(threading.Thread):
    '''Polls TWS every second for any outstanding messages'''

    def __init__(self, tws):
        super(TWSPoller, self).__init__()
        self.daemon = True
        self._tws = tws
        self.stop_polling = False
        self.sleep_time = 1 # <-------

    def run(self):
        '''Continually poll TWS until the stop flag is set'''
        while not self.stop_polling:
            try:
                self._tws.checkMessages()
            except:
                if self.stop_polling:
                    break
                else:
                    raise
            time.sleep(self.sleep_time) # <-------
%}

I tend to use a smaller sleep time. So now I am able to change the
sleep_time after the connection with: tws.poller.sleep_time = 0.2

Combo Leg Order: Rejected - Invalid value in field # 6079

Hi,

I am experiencing problems sending comboLeg orders. Specifically, I get the following error message:
"Rejected - Invalid value in field # 6079"

This message is displayed in the TWS as a pop-up window (not in Python).

I tried to send the comboLeg order using the following code:

1. Get contract IDs

m_contract_object1 = IBcontract()
m_contract_object1.secType = 'STK'
m_contract_object1.symbol = 'EWA'
m_contract_object1.currency = 'USD'
m_contract_object1.exchange = 'SMART'

m_contract_object2 = IBcontract()
m_contract_object2.secType = 'STK'
m_contract_object2.symbol = 'EWC'
m_contract_object2.currency = 'USD'
m_contract_object2.exchange = 'SMART'

cdetails1 = client.get_contract_details(m_contract_object1)
cdetails2 = client.get_contract_details(m_contract_object2)

print cdetails1['conId'] # returns 2586606
print cdetails2['conId'] # returns 2586548

2. Create ComboLegs

leg1 = ComboLeg()
leg2 = ComboLeg()

leg1.conId = cdetails1['conId']
leg1.ratio = 1
leg1.action = 'BUY'
leg1.exchange = 'SMART'
leg1.openClose = 0
leg1.shortSaleSlot = 0
leg1.designatedLocation = ""

leg2.conId = cdetails2['conId']
leg2.ratio = 1
leg2.action = 'SELL'
leg2.exchange = 'SMART'
leg2.openClose = 0
leg2.shortSaleSlot = 0
leg2.designatedLocation = ""

3. Create new contract and order

contract = IBcontract()
contract.secType = 'BAG'
contract.symbol = 'EWA'
contract.currency = 'USD'
contract.exchange = 'SMART'
contract.comboLegs = ComboLegList([leg1, leg2])

order = IBOrder()
order.action = 'BUY'
order.totalQuantity = 1
order.orderType = 'MKT'
order.lmtPrice = 1

client.place_order(contract,order)

TWSPoller Thread - enhancement request - callback when a connection is reestablished.

Regarding the TWSPoller thread, I do like the idea and I have an enhancement request. I would like a callback to be called from the TWSPoller thread when the connection is lost and also when it's re-established. This is needed so that I can keep a flag when the connection is lost and then when its re-established I can clear the flag and use it to issue request executions, open orders, portfolio etc.

I believe there is a EWrapper callback connectionClosed() from IB API, but since TWSPoller thread automatically tries to reconnect, I would like a callback from the poller thread to notify me when it has successfully reestablished connection.

Exception in run - swigibpy.py (version: 0.5.0)

Hi

From the setup.py file

IB_DIR = 'IB'

VERSION = '0.5.0'

I am occasionally get an exception in run method. Here is the exception,

Exception in thread Thread-1 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
  File "/usr/local/lib/python2.7/dist-packages/swigibpy-0.5.0-py2.7-linux-x86_64.egg/swigibpy.py", line 2076, in run
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'error'

Here is the code

2072
2073             if fd > 0:
2074                 try:
2075                     evtin, evtout, evterr = select.select(pollfd, pollfd, pollfd, 1)
2076                 except select.error:
2077                     connected.clear()
2078                     continue

checkMessages str object not callable

After a tws.placeOrder(...) I get an error message:

Open Order status: Order Id: 10 Order State: Submitted
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "/usr/local/lib/python2.7/dist-packages/swigibpy.py", line 1224, in run
self._tws.checkMessages()
TypeError: 'str' object is not callable

extremely high cpu usage?

Great project!
I've noticed that when swigibpy connects to TWS the python process is taking 99.4% cpu, even if nothing happens just sitting there connected to TWS cause it to drain most of the CPU power.

I am using Mac though I am not sure this should make a difference

using the latest swigibpy (0.5.0 right from github)

seems like the auto_polling is the cause
setting poll_auto=False fixes the problem, probably a problem in the polling thread

Broken socket when using multiple EPosixClientSocket in different processes

We're getting an error when using multiple EPosixClientSocket in different processes:

509: Exception caught while reading socket - Connection reset by peer

And a similar error is written in log files:

[DH] INFO [JTS-usfutureListenerS18-29710] - Disconnecting cdc1.ibllc.com:4000 with status DISCONNECT_ON_BROKEN_SOCKET

Its occurs as soon as some data are received from TWS. Everyting works fine if we execute pieces of code so that only one socket is used. Using poll_auto=False removes the error but since no other Poller is used, TWS messages are not handled.

We use swigibpy 0.5.0 and TWS Build 956.2d

Docker

I've written a short docker file which gets swigibpy running in a Docker python3 container. I'll be writing more about this in my blog. Raising as an issue rather than a pull request as I don't know where you would want to put it.

FROM python
RUN apt-get update
RUN apt-get install -y g++
RUN apt-get install -y python3-dev
RUN pip3 install swigibpy

Update to TWS API v9.67

v9.67 brings a number of changes, most annoyingly is the expanded use of shared_ptr which has proven problematic for SWIG to deal with (#7).

shared_ptr is now used for ComboLeg, OrderComboLeg as well as TagValue

Missing method argument

On line 2017 in swigibpy.py, the eConnect method does not have an extraAuth argument yet it tries to use a variable with that name. The result is that it fails at run time:

def eConnect(self, host, port, clientId=0, poll_auto=True):
     val = _swigibpy.EPosixClientSocket_eConnect(self, host, port, clientId, extraAuth)
     if poll_auto and val:
         self.poller = TWSPoller(self)
         self.poller.start()
     return val

Exchange not set in EWrapper::position

I'm trying to get the exchange of a contract i have position on. From my understanding of the IB documentation, this info is not given in EWrapper::updatePortfolio and should be obtained in EWrapper::position instead, but in my case the exchange is not set in EWrapper::position.

My code looks like this:

def position(self, accountName_p, contract_p, position_p, avgCost_p):
    print("set exchange to '%s'" % contract_p.exchange)

And prints:

set exchange to ''

Maybe i'm missing something obvious but I can't tell what. Other information about the contract are correctly received

I'm using TWS build 952.2h, and swigibpy 0.5.0

fatal error C1189: #error: Macro definition of snprintf conflicts with Standard Library function declaration

I am getting an error when trying to install. from my research, it looks like 'snprintf' is declared incorrectly and should be called as a standard function instead of declaring it because it has apparently been added to the standard lib? not sure honestly, just what I have managed to gather from trying to fix it myself.

pip install swigibpy

Collecting swigibpy
Using cached swigibpy-0.4.1.tar.gz
Building wheels for collected packages: swigibpy
Running setup.py bdist_wheel for swigibpy ... error
Complete output from command C:\ProgramData\Anaconda3\python.exe -u -c "import setuptools, tokenize;file='C:\Users\me\AppData\Local\Temp\pip-build-b5wc6ast\swigibpy\setup.py';f=getattr(tokenize, 'open', open)(file);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, file, 'exec'))" bdist_wheel -d C:\Users\me\AppData\Local\Temp\tmp46pw1p5jpip-wheel- --python-tag cp36:
running bdist_wheel
running build
running build_py
creating build
creating build\lib.win-amd64-3.6
copying swigibpy.py -> build\lib.win-amd64-3.6
running build_ext
building '_swigibpy' extension
creating build\temp.win-amd64-3.6
creating build\temp.win-amd64-3.6\Release
creating build\temp.win-amd64-3.6\Release\IB
creating build\temp.win-amd64-3.6\Release\IB\PosixSocketClient
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -DIB_USE_STD_STRING=1 -IIB -IIB/PosixSocketClient -IIB/Shared -IC:\ProgramData\Anaconda3\include -IC:\ProgramData\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\winrt" /EHsc /TpIB/PosixSocketClient/EClientSocketBase.cpp /Fobuild\temp.win-amd64-3.6\Release\IB/PosixSocketClient/EClientSocketBase.obj /D_CRT_SECURE_NO_DEPRECATE /EHsc /wd4355 /wd4800
EClientSocketBase.cpp
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(1931): warning C4005: 'snprintf': macro redefinition
c:\users\me\appdata\local\temp\pip-build-b5wc6ast\swigibpy\ib\shared\StdAfx.h(8): note: see previous definition of 'snprintf'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(1933): fatal error C1189: #error: Macro definition of snprintf conflicts with Standard Library function declaration
error: command 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe' failed with exit status 2


Failed building wheel for swigibpy
Running setup.py clean for swigibpy
Failed to build swigibpy
Installing collected packages: swigibpy
Running setup.py install for swigibpy ... error
Complete output from command C:\ProgramData\Anaconda3\python.exe -u -c "import setuptools, tokenize;file='C:\Users\me\AppData\Local\Temp\pip-build-b5wc6ast\swigibpy\setup.py';f=getattr(tokenize, 'open', open)(file);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, file, 'exec'))" install --record C:\Users\me\AppData\Local\Temp\pip-n2axuo5h-record\install-record.txt --single-version-externally-managed --compile:
running install
running build
running build_py
creating build
creating build\lib.win-amd64-3.6
copying swigibpy.py -> build\lib.win-amd64-3.6
running build_ext
building '_swigibpy' extension
creating build\temp.win-amd64-3.6
creating build\temp.win-amd64-3.6\Release
creating build\temp.win-amd64-3.6\Release\IB
creating build\temp.win-amd64-3.6\Release\IB\PosixSocketClient
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -DIB_USE_STD_STRING=1 -IIB -IIB/PosixSocketClient -IIB/Shared -IC:\ProgramData\Anaconda3\include -IC:\ProgramData\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\winrt" /EHsc /TpIB/PosixSocketClient/EClientSocketBase.cpp /Fobuild\temp.win-amd64-3.6\Release\IB/PosixSocketClient/EClientSocketBase.obj /D_CRT_SECURE_NO_DEPRECATE /EHsc /wd4355 /wd4800
EClientSocketBase.cpp
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(1931): warning C4005: 'snprintf': macro redefinition
c:\users\me\appdata\local\temp\pip-build-b5wc6ast\swigibpy\ib\shared\StdAfx.h(8): note: see previous definition of 'snprintf'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(1933): fatal error C1189: #error: Macro definition of snprintf conflicts with Standard Library function declaration
error: command 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe' failed with exit status 2

----------------------------------------

Command "C:\ProgramData\Anaconda3\python.exe -u -c "import setuptools, tokenize;file='C:\Users\me\AppData\Local\Temp\pip-build-b5wc6ast\swigibpy\setup.py';f=getattr(tokenize, 'open', open)(file);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, file, 'exec'))" install --record C:\Users\me\AppData\Local\Temp\pip-n2axuo5h-record\install-record.txt --single-version-externally-managed --compile" failed with error code 1 in C:\Users\me\AppData\Local\Temp\pip-build-b5wc6ast\swigibpy\

Segfault when running examples/contractdetails.py

Getting segfault when running examples/contractdetails.py. The violating code is starting from line 66:

if contractDetails.secIdList is not None:
    for secId in contractDetails.secIdList:
        print("secIdList: %s" % secId)
else:
    print("secIdList: None")

Don't know why is it giving segfault though... Tested with Python 2.7.8, 3.3.4, 3.3.5 and 3.4.1.

python3.5 unable to get some contractDetails vars

Accessing attribute 'returnedContractDetail.liquidHours' - returned object from call to reqContractDetails doesn't work under python3.5.

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x98 in position 0: invalid start byte

tws.eDisconnect() no worky?

Hi,
Are you aware of lack of proper disconnect for a a clientId?
By which i mean that when calling tws.eDisconnect(), and then trying to reconnect i get a client connected error.
Seems only the python process ending will do, or did i miss something?

Why I cannot do reqPositions()?

when i do:
tws.reqPositions()

it gives me this error:
AttributeError: 'EPosixClientSocket' object has no attribute 'reqPositions'

When i checked the swigibpy.py
reqPositions is defined in EClient and EClientSocketBase

def reqPositions(self):
    """reqPositions(EClientSocketBase self)"""
    return _swigibpy.EClientSocketBase_reqPositions(self)

when I do dir(EPosixClientSocket) its not there. Can someone please help me understand why and how to load it?

Thanks.

Out[13]:
['TwsConnectionTime',
'class',
'delattr',
'dict',
'doc',
'format',
'getattribute',
'hash',
'init',
'module',
'new',
'reduce',
'reduce_ex',
'repr',
'setattr',
'sizeof',
'str',
'subclasshook',
'swig_destroy',
'weakref',
'calculateImpliedVolatility',
'calculateOptionPrice',
'cancelCalculateImpliedVolatility',
'cancelCalculateOptionPrice',
'cancelFundamentalData',
'cancelHistoricalData',
'cancelMktData',
'cancelMktDepth',
'cancelNewsBulletins',
'cancelOrder',
'cancelRealTimeBars',
'cancelScannerSubscription',
'checkMessages',
'clientId',
'eConnect',
'eDisconnect',
'exerciseOptions',
'fd',
'handleSocketError',
'isConnected',
'isInBufferEmpty',
'isOutBufferEmpty',
'isSocketOK',
'onError',
'onReceive',
'onSend',
'placeOrder',
'replaceFA',
'reqAccountUpdates',
'reqAllOpenOrders',
'reqAutoOpenOrders',
'reqContractDetails',
'reqCurrentTime',
'reqExecutions',
'reqFundamentalData',
'reqGlobalCancel',
'reqHistoricalData',
'reqIds',
'reqManagedAccts',
'reqMarketDataType',
'reqMktData',
'reqMktDepth',
'reqNewsBulletins',
'reqOpenOrders',
'reqRealTimeBars',
'reqScannerParameters',
'reqScannerSubscription',
'requestFA',
'serverVersion',
'setServerLogLevel',
'thisown']

MFC compilation error on Windows using Visual Studio compiler

This is actually a bug in the Interactive Brokers code. They have mixed up _MFC_VER with _MSC_VER. The default distutils setup uses the VS compiler and this does not compile the IB code (issue mentioned in README.1st) since the IB code insists on linking with MFC. I don't think that linking a Python package with MFC is a good idea.

The fix is actually easy (see patch below) but I don't know whether you want to apply it since it modifies 3rd party code. Recently, I have submitted the patch directly to IB, let's see whether they accept it.

For myself, I am keeping it in a Mercurial queue and applying it on every new API release. The problem dates from many API releases ago.

swigibpy: 0.3
python: 3.3.0
C/C++ compiler: VS 10.0 (aka Visual Studio 2010, aka _MSC_VER 1600)
TWS: 931.3
OS: Windows 7 64-bit

diff --git a/IB_966/Shared/EClientSocketBaseImpl.h b/IB_966/Shared/EClientSocketBaseImpl.h
--- a/IB_966/Shared/EClientSocketBaseImpl.h
+++ b/IB_966/Shared/EClientSocketBaseImpl.h
@@ -415,7 +415,7 @@
 }


-#ifdef _MSC_VER
+#ifdef _MFC_VER
 static IBString errMsg(CException *e) {
        // return the error associated with this exception
        char buf[1024];
@@ -2105,7 +2105,7 @@
                beginPtr = ptr;
                return processed;
        }
-#ifdef _MSC_VER
+#ifdef _MFC_VER
        catch( CException* e) {
                m_pEWrapper->error( NO_VALID_ID, SOCKET_EXCEPTION.code(),
                        SOCKET_EXCEPTION.msg() + errMsg(e));
@@ -3199,7 +3199,7 @@
                return processed;
        }

-#ifdef _MSC_VER
+#ifdef _MFC_VER
        catch( CException* e) {
                m_pEWrapper->error( NO_VALID_ID, SOCKET_EXCEPTION.code(),
                        SOCKET_EXCEPTION.msg() + errMsg(e));
diff --git a/IB_966/Shared/StdAfx.h b/IB_966/Shared/StdAfx.h
--- a/IB_966/Shared/StdAfx.h
+++ b/IB_966/Shared/StdAfx.h
@@ -7,7 +7,9 @@
 #define assert ASSERT
 #define snprintf _snprintf

+#ifdef _MFC_VER
 #include <afxwin.h>
+#endif

 #endif

reqMktData example (TypeError: in method 'EWrapper_tickString', argument 2 of type 'TickerId')

I don't think I'm calling reqMktData properly, is there an example to go by?

I'm passing in a genericTicks of "221" or "221,232" and not getting a good result.

Requesting market data for AAPL

=====================================================================
 Market data requested, waiting for TWS responses
=====================================================================

TWS Warning - 2104: Market data farm connection is OK:usfarm.us
TWS Warning - 2104: Market data farm connection is OK:usopt
TWS Warning - 2104: Market data farm connection is OK:usfarm
TWS Warning - 2106: HMDS data farm connection is OK:ushmds
Traceback (most recent call last):
  File "reqMktData_AAPL-tatra1.py", line 89, in <module>
    tws.checkMessages()
TypeError: in method 'EWrapper_tickString', argument 2 of type 'TickerId'

auto reconnect parameter seems to raise error

on macosx python 3.4 the parameter reconnect_auto=True seem to raise an error in the examples provided?

python historicaldata.py
Traceback (most recent call last):
File "historicaldata.py", line 72, in
tws = EPosixClientSocket(callback, reconnect_auto=True)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/swigibpy.py", line 1663, in init
_swigibpy.EPosixClientSocket_swiginit(self,_swigibpy.new_EPosixClientSocket(_args, *_kwargs))
TypeError: new_EPosixClientSocket() takes at most 1 argument (2 given)

Issues with Python 3.4

Swigibpy has some minor issues with Python 3.4. Fixing it was quite easy, just replacing all instances of dict.iteritems() with dict.items() in swigibpy.py solved the problem. Will keep an eye out for other incompatibilities and report them here...

Can this fix be merged in the repository?

no release / pip for v 0.5.0 ?

It seems only v 0.4.1 is available for official release. This means the examples on the front page do not work since auto_connect does not seem implemented yet? Thanks for your help in advance.

std::bad_alloc when trying to get open Contract properties?

First, let me just say thank you so much for developing swigibpy, it makes interfacing with the Interactive Brokers API using Python infinitely easier (and, combined with the pandas library, can make some pretty powerful trading algos).

I'm running into the C++ memory error std::bad_alloc when trying to access properties (such as symbol) for any open Contracts in my account.

The below is an example (based off of the provided placeorder_example.py): it successfully gets all open positions in my account (the freely accessible IB demo account number is used here), but for any position, when I try to access a property of the Contract (e.g. contract.symbol), it throws the std::bad_alloc error.

Do you know how I could go about resolving this issue? Thank you for your help in advance!

(Because this example uses the demo account, you might have to run this code during market hours and make some trades in the demo account first, because the demo account seems to reset itself nightly, and obviously the code doesn't do anything when there aren't any open positions. Demo account downloadable at https://www.interactivebrokers.com/en/?f=%2Fen%2Fpagemap%2Fpagemap_demo.php, button "Try Individual Demo")

import datetime as dt

import sys
from time import sleep

from swigibpy import EWrapper, EPosixClientSocket, Contract, Order, TagValue,\
        TagValueList

try:
    input = raw_input
except:
    pass

###

orderId = None

availableFunds = 0
netLiquidationValue = 0

currentPositions = []

class PlaceOrderExample(EWrapper):
    '''Callback object passed to TWS, these functions will be called directly
    by TWS.

    '''

    def openOrderEnd(self):
        '''Not relevant for our example'''
        pass

    def execDetails(self, id, contract, execution):
        '''Not relevant for our example'''
        pass

    def managedAccounts(self, openOrderEnd):
        '''Not relevant for our example'''
        pass

    ###############

    def nextValidId(self, validOrderId):
        '''Capture the next order id'''
        global orderId
        orderId = validOrderId

    def orderStatus(self, id, status, filled, remaining, avgFillPrice, permId,
            parentId, lastFilledPrice, clientId, whyHeld):

        print(("Order #%s - %s (filled %d, remaining %d, avgFillPrice %f,"
               "last fill price %f)") % (
                id, status, filled, remaining, avgFillPrice, lastFilledPrice))

    def openOrder(self, orderID, contract, order, orderState):

        print("Order opened for %s" % contract.symbol)

    ####account value
    def updateAccountValue(self, key, value, currency, accountName):
        global availableFunds
        global netLiquidationValue

        #get how much current available funds we have, also our net liquidation value
        if currency == 'USD':

            if key == 'AvailableFunds':
                availableFunds = float(value)
            elif key=='NetLiquidation':
                netLiquidationValue = float(value)

    def accountDownloadEnd(self,accountName):
        print 'account download ended for %s' % (accountName)
    def updateAccountTime(self,timestamp):
        print 'account information pulled at %s' % (timestamp)
    def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accoutName):
        #this only called when there are actually positions

        global currentPositions
        currentPositions.append((contract,position))


# Instantiate our callback object
callback = PlaceOrderExample()

# Instantiate a socket object, allowing us to call TWS directly. Pass our
# callback object so TWS can respond.
tws = EPosixClientSocket(callback)

# Connect to tws running on localhost
tws.eConnect("", 7496, 42)

#account updates
tws.reqAccountUpdates(True,'DU15068')

sleep(3)
print 'available funds: %s' % (availableFunds)
print 'net liquidation value: %s' % (netLiquidationValue)


for position in currentPositions:
    contract = position[0]
    quantity = position[1]

    print contract.symbol



print("******************* Press ENTER to quit when done *******************\n")
input()

print("\nDisconnecting...")
tws.eDisconnect()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.