Friday 28 March 2014

LOBOs - The next banking scandal?

First we had PPI. Then LIBOR. Then swaps miss - selling. Now Ladies and Gentleman I give you LOBOgate. Or How The Banks Ripped Off Local Authorities and Housing Associations From Someone Who Was There.

(I am open to suggestions for a better name)

Cast your minds back over a decade to the early 2000's. Life was good. Both the public and private sector in the UK were doing pretty well. However as a treasurer for a local authority or housing association money was still tight on occasion. It was still necessary to borrow money.

You would have thought that a local authority could borrow cheaply, since in practise if not legally they are backed by the UK central government. Indeed they can borrow from central government via the PWLB at rates only slightly higher than the governments own debt issuance. But to a treasurer encouraged to make their department behave like a profit centre even these rates were too high.

It would of course made absolutely no sense for the local authority to approach a bank. Even in the halcyon days of the early 2000's banks generally had worse credit ratings than the countries that hosted them, and had to borrow money a little more expensively; and to make a profit lend out at a little more again.So borrowing from the bank was obviously going to be more costly than borrowing from the PWLB.

It would have made no sense at all..... but that is exactly what the local authorities did. And they were able to borrow more cheaply than from the PWLB. And the banks made huge profits, but not as much as some individuals working as money brokers did.

Welcome to the magic of the Local Authority Lender Option Borrowing Option - LOBO deal. Surprisingly what little fuss has been made about these deals has focused on their tenuous connection to LIBOR-Gate. In fact this is a completely different scandal.


A little introductory financial alchemy


Lets say I offer to lend you £40 and charge you 3% interest for 5 years. Some other guy comes along and offers you the same deal; but the twist is he will have the option to ask for his money back whenever he likes.


You wouldn't borrow money from him because its clearly a worse deal.

(By the way if you don't think its a worse deal then I have some double glazing and PPI insurance to sell you; plus did I mention that I am distantly related to a member of the royal family and there is $40 million in escrow with my name on it, 10% of which is yours if you send me your bank details...)

 Suppose he sticks to his guns but as a concession he will lend you the money at only 2.9% interest. Would you take that? What about 2.5%? 2%?

Essentially what I am asking you to do is to value the option of the lender wanting their money back. Why would the lender want their money back? There may be all kinds of reasons, but the most likely reason would be that interest rates have risen to say 4%; and they would rather lend the money to some other person than have your 3% coming in.

Of course having to suddenly repay your loan when interest rates have risen to 4% is the worst possible thing for you. I assume you haven't got the money to repay it. Your wealthy aunt hasn't suddenly died has she? You don't have a spare kidney, or spare first born son you could sell? No? Then you're going to have to borrow the money from someone else. At 4%. Ouch.


[To be pedantic LOBO deals allowed the lender the option to increase the interest rate. The borrower then had the option pay the new interest rate or repay the money. 

If interest rates fall: The lender wouldn't increase the interest rate if it was already high. If rates fall to 2% and the borrower is paying 3% the lender will stick.

If interest rates rise: The lender could increases the rate in line with market levels eg up to 4%, and the borrower decides to continue paying. 

The lender always has the option to increase it to 'silly' levels eg 10%, at which the borrower would have no choice but to repay the loan. To make things simpler I'll treat the loans as if the latter always happens when rates rise; but this is gives a conservative valuation of the lenders option]


There is nothing unethical or unusual about this (but don't worry, the unethical bit is coming....). If like me you have a fixed rate mortgage then you probably have an early repayment charge (ERC) on it. Again this is just the value of the option that I have to repay the mortgage early.

In the LOBO deals the lender has the right to ask for the loan to be repaid early. So they will charge less interest because they own a valuable option. The question still remains, how does the borrower value that option?

 

Enter the middleman


To value the mortgage option or the repayable loan option you just need a bermudan swaption pricer, the relevant volatility surface, some kind of interest rate model calibrated to the appropriate processes and the full forward and spot curve.

You probably don't have this kind of thing at home. I didn't when I took out my mortgage; I didn't really spend time pricing the various rates, upfront fees and ERC against each other; even though I could have done if I could be bothered - 99.9% of people can't do that. That is why a lot of people use mortgage brokers to get the best deal.

(Actually I didn't use a mortgage broker, but it kind of spoils the story if I mention that. And I used a calculator, a pencil and the back of the nearest envelope to do the calculations.)

Who should pay the mortgage brokerAround this question the UK regulator changed the entire way the financial system not so long ago. It is generally a good idea to align incentives. So when you sell a house you pay an estate agent to get the best price for you. Suppose that when you sold a house the estate agent rather than taking a commission from you, instead took one from the buyer. There would be a strong incentive for a dishonest agent to take the cheapest offer for the house, if its was coupled with a fatter commission for them personally.

(Okay this can still happen. Its not unusual apparently for estate agents to be offered bribes by desperate or unscrupulous buyers when the housing market is particularly hot. But we shouldn't make it easier for people to behave badly, should we?)


In the UK financial consumer world until relatively recently the incentives were distorted. Customers didn't want to pay large up front fees for financial advice, so instead advisers were paid through commission from the people supplying the financial products. If you don't know what happened as a result you can probably guess by now.

It will be no surprise if I tell you that the LOBO deals were not done directly by Local Authorities with Banks, but via middlemen known collectively as 'money brokers' (a rather old fashioned term, but I like the the Dickensian overtones, don't you?). Interestingly in my opinion the main reason this was being done was to protect the local authorities... although as we shall see it had the opposite effect.



A brief history of local authority mismanagement


Why keep the banks away from Local Authorities? Well there was a landmark legal case in the early 1990's when the borough of Hammersmith and Fulham had got themselves involved with trading interest rate swaps (a kind of interest rate derivative)... to reduce their funding costs (oh the irony!!!). It was decreed that they had exceeded their legal authority in signing these deals so therefore the deals were null and void. Local authorities were not allowed to trade these derivative products.

They were not allowed to trade swaps and certainly not bermudan swaptions, a more complex form of derivative. A bermudan swaption is a right to cancel a swap... you can guess where this is going. Yes the kind of option embedded in a LOBO deal is a bermudan swaption. So its okay to have a cancellable loan (or a cancellable fixed rate mortgage) that embeds a complex derivative but not to trade them directly. Interesting viewpoint, isn't it?



A not so brief history of an example trade


Lets give an example of a trade which may or may not have occurred which the author of this post may or may not have had intimate knowledge of whilst working on a bank trading desk in the early 2000's.

(This is a 'composite' story with elements of things which really happened; oh let's not be coy you can google me - at Barclays Capital; though they were no means the only bank doing this)

First the housing association B asked the broker to get them the best deal on a cancellable loan; say a 40 year loan first cancellable in 2 years and then every subsequent 6 months. There aren't many people who can do this kind of deal because the relevant derivatives market isn't very liquid (because loans over 30 years are quite rare, even to the UK government). They probably rang round 3 or 4 UK based banks and at least one German bank.

The German bank can't do this deal themselves so they called up one of the British banks to get a price for hedging the risk. Interestingly this resulted in a situation when the relevant trading desk had requests for pricing an identical hedge from two different parts of the bank. The request made directly would have made the bank more money so the trader told the German sales guy not to mess around with letting the Germans try and undercut them and steal their business. Probably the Germans were given a quote high enough to prevent them from making a competitive offer to the broker.

(Actually come to think of it this is probably bordering on cartel like behaviour but I am not a competition lawyer so I don't know.)

So the quotes come in from the banks. The broker chooses one. I don't know how he chooses the quote, I wasn't there (I never even met the guy, all deals being done via bank sales people). What I do know is that the quotes would have included a negotiated commission for the broker (remember the banks are paying him for arranging the loan). I know that the commissions on the deals that we won (and we seemed to win quite a bit) were often very high compared to commissions on other brokered products.

Would the broker have chosen the best deal for the local authority or the best for himself? It might be harder to make the right decision if the commission was very large...

So for example most products broked bank to bank had commissions of fractions of a basis point, or perhaps one basis point (1 basis point is 1/100 of a percent). Wheras on some LOBO deals the commissions could be approaching 1%. Although these deals are more unusual and complex than many other products, this is still a very large commission. In money terms we are talking £20 - £50 million dollar deals, so these six figure brokerage commissions were not uncommon.

On this particular deal the commission was so large in percentage terms that it exceeded internal limits. Even the most hard nosed traders on the trading desk were feeling pangs of.... well not guilt perhaps but fear that this kind of thing might one day be written on a blog. But the broker agreed to take half of the commission spread over subsequent deals, so that was okay.

As a point of fact the commission that the traders themselves in the bank would have personally earned on that would have been much lower than the brokers but these deals although not large in the grand scheme of things (billion dollar bond issues are not uncommon, though with much lower % profits) were certainly a good contribution to the desks profits.

(To be pedantic traders don't normally get paid a fixed commission of a deal but a percentage of the profits made by the trading desk over a whole year; the percentage isn't fixed of course and depends on many other factors. So its much harder to pin down exactly what a bank employee would have made from an individual deal like this)

 

How far does this go?


What I don't personally know is the extent of this. It could just be a few isolated trades with one UK bank that I personally know about over a couple of years. I have absolutely no proof that this is a major problem.

But I'd bet money that it goes much further than this.

 

No laws broken?


Of course none of this was illegal. That isn't a formal legal opinion and I may subsequently be proven wrong. But nobody comes out of this smelling particularly well. The money brokers for me are the worst offenders; their behaviour was downright immoral and they personally benefited the most. Its very easy to blame the bankers and they certainly should have behaved differently, but their incentives were to either pay the commissions or lose what was a very profitable business.

(What the bankers involved should have done was leave their jobs in disgust and go and work somewhere else more ethical. Then just when they had the moral high ground recaptured they should spoil it all by going to work for a hedge fund. It worked for me). 

But it isn't just them. Just as naughty I think were the people who put pressure on the treasurers to reduce their funding costs by a few fractions of a percent, at any cost. The treasurers themselves. The well meaning people who through the law of unintended consequences prevented the local authorities from getting quotes directly from banks which could have improved things a bit.

(This happens all the time. For example the use of financial advisors, investment and pension consultants to protect investors from being ripped off just adds layers of fees and in my opinion adds no value. But that's another story.)

It will be interesting to see how this story develops...








Wednesday 26 March 2014

Using swigibpy so that Python will play nicely with Interactive Brokers API



The interactive brokers API is as far I know the only way that a non institutional client can access financial markets in a way which makes properly programmatic, fully flexible, fully automated trading possible. 

(I am ignoring broker supplied 'front ends' that may allow you to automate trades, and some Janet and John environments that provide the ability to implement algorithms in weird proprietary languages  for a limited number of instruments)

This is a good thing.

Now for the not so good things. Firstly the API will only run in Excel, Visual Basic Active X, Java, C++, and C#. That is a bit like buying fuel for your car that will only work if you have a toy car, an electric toy car, a BMW or one of two kinds of Land Rover, one of which is only driven in Iceland. There is no support for the 'wimpy-quants' languages of choice, R, Matlab and Python.

NOTE:

There is now a native python API for interactive brokers. An updated version of this post using the native API can be found here.

(You can of course use something like Ninja Trader as a front end, but if you're reading this you're not that much of a wimp are you?)
 
The good news is there are things like IbPy and swigibpy out there to help us Python users, with my choice of swigibpy (see the December 2013 post for why). A quick Google reveals the existence of similar packages for R and Matlab. This mostly sit between your more high level language and the C++ API.

Secondly the documentation isn't great, either for the IB API itself or swigibpy. So it takes quite a lot of fiddling around and trial and error to get things working. There are some limited examples out there on the web for example here is one of the best for IBpy; however they mostly cover the absolute basics and focus on trading individual equities. They can be also very much of the 'like here is some code, and it seems to work' without any explanation that would allow you to understand and adopt the code for your own purposes.

Because I am a nice guy and a public servant to boot I intend in this and subsequent posts to provide enough working documentation to get you up and running with an interface to IB that you can use as a starting point to build an automated trading system in python. This may also be of use to those using IB API in other non swigibpy environments.

(Assumptions: I am using Python 2.7.4; older or newer versions may break. I will assume you know your way around Python to the extent of being able to create a simple package and modules and run them. My command line examples will be for Linux but similar things ought to be possible. Also that you understand the dangers and risks of trading futures; but you will need to sign a lot of disclaimers before IB let you do this for real so I will let them worry about that.)


Getting a test account, downloading the IB TWS


To play with IB without signing up for real you will need a test account. By the way if you are serious and you get a real IB account you can also request an additional account for simulated trading.

(The test account is not very realistic, eg prices can be total garbage. The simulated account is much better although for some reason you don't always get L1 and L2 data to all the data feeds your real account is signed up to. If you are going to do any systematic trading in the near future I highly recommend signing up to IB and using a proper simulated account. It doesn't cost anything if you don't trade or use any additional data feeds.)

We aren't going to bother downloading the TWS software, which is a rather heavy front end useful for trading yourself; but the much lighter and more stable 'Gateway'.

(I advise you to also download the TWS API at some point to have a play, but I don't recommend it for day to day running of a strategy since it seems to be very unstable due to the great lardy weight of fancy ultra bloated GUI that it has to support.)



  1.  Go to https://www.interactivebrokers.co.uk/en/main.php
  2. Click on trading menu, API solutions
  3. Under IB API click on more info
  4.  Click on IB gateway software
  5. Under UNIX clicks on IB gateway for Unix
  6. Follow the instructions.
The last section of the instructions asks you to type some gobbledygook to run 'TWS' (they mean gateway). Don't be a fool and create a shell script that does it for you. Then you just have to type something like . runGateway. I have a similar script for TWS.
  1. Select IB API radio button
  2.  Under username put 'edemo' and under password put 'demouser'.
 If all goes well you will eventually see a screen with a green bar showing a connected status at the top. This program acts as a server to pass on your instructions from the API connection to the great IB server, wherever that is. You now need to configure your API to accept connections.


  1. Click on the Configure menu. Go to API settings
  2. Socket port - should be 4001.
  3. Trusted IP addresses - should include 127.0.0.1. If it doesn't you will need to add it.
  4. Read only API - you can leave this checked for now but when you come to submit trades should be unchecked.
  5. Go to precautions. You might want to suppress market cap warnings here when you start trading in earnest.
  6. Go to presets. Again check you are happy with the limits shown.
Unlike TWS the gateway is automatically set up to accept connections so this is all you need to do.

(There is nothing special about 4001 so you can change it but be sure to remember the number and only use a radically different number if you realise you might break Linux in the process. Check http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers and make sure your game of Microsoft Ants isn't running. I run two or more Gateways in parallel each connecting to live and test accounts. I associate a different socket (I use 4001 up to about 4005) with each account and gateway session.

127.0.0.1 is just 'this machine'. If your code is running on the same machine as the Gateway you are fine. Otherwise eg if you are on a network you will have to include the IP address of any other machines that might connect to the Gateway.)

You might want to peruse the official IB API manual. The more interesting part is the C++ section. I find it very useful if I am having trouble sleeping.

 

Downloading required python libraries

 

swigibpy

Remember swigibpy is the way that python gets to talk to the c++ API for IB. Go to this page and follow the download instructions. Either method shown works for me.

(This might fail on a missing Python.h file if you have too minimal a Linux build. Just do sudo apt -get install python-dev if you have the same problem. )



Let's write some code - okay lets get some code I've already written


Pull the source from here. The two files of interest are test1_IB.py and wrapper.py.

 

Running the example


Once you have got the two files above into a package you can just run test1_IB. If it works properly you should see something like this:

Getting the time...
1395845174


So we now seem to have acquired a very complicated way of telling unix time!

Also I appear to have committed the deadly sin of 'like here is some code, and it seems to work' without any explanation. So lets dig into this a bit more.

 

All you wanted to know about IB connections but frankly couldn't care less

 

    callback = IBWrapper()
    client=IBclient(callback)


class IBclient(object):
    def __init__(self, callback):
        tws = EPosixClientSocket(callback)
        (host, port, clientid)=return_IB_connection_info()

    <snip - code missing here>


def return_IB_connection_info():
    """
    Returns the tuple host, port, clientID required by eConnect
   
    """
   
    host=""
   
    port=4001
    clientid=999
   
    return (host, port, clientid)
 


(Pedantic point - Although the code says tws here, it really means gateway. Actually the client doesn't know or care what is on the other end of the connection.)

As I said above you can have any number of IB servers (gateway or TWS session) running; so I use a function to tell me what host, port and clientid to use when connecting (here the function is rather dull). As briefly alluded to above each IB server will listen out for clients on a particular port (so these should be unique to the server). You can also have numerous clients connecting to the same server. For example I have one client picking up prices; another receiving accounting information, another managing orders, various one off clients getting diagnostic information and one doing the washing up.

If a clientid is already in use by a connected session then IB will cry. It doesn't seem to be possible to kill a connection without the python code terminating; there is a method tws.eDisconnect() but using it makes no difference, nor does setting tws=None or indeed anything else short of finding the thread running the python and killing it.

What this means if you have a series of functions you will call in a given python session, all of which connect to TWS, you can't do something like this:

    def main_function():
       
        functiona()
       
        functionb()
       
    def functiona():
        callback = IBWrapper()
        client=IBclient(callback)
       
        client.do_something()

       
    def functionb():

        callback = IBWrapper()
        client=IBclient(callback)
       
        client.do_something_else()


 ... as the call to functionb will fail probably with error 509. Instead you need to do something like this:

    def main_function():        callback = IBWrapper()
        client=IBclient(callback)
       
        functiona(client)
       
        functionb(client)
       

    def functiona(client):
        client.do_something()
       
    def functionb(client):
        client.do_something_else()
       

Back to the story.

    callback = IBWrapper()

    client=IBclient(callback) 

class IBclient(object):
    def __init__(self, callback):
        tws = EPosixClientSocket(callback)
        (host, port, clientid)=return_IB_connection_info()
        tws.eConnect(host, port, clientid)

        self.tws=tws
 

        self.cb=callback
    <snip - code missing here>

The callback instance of IBWrapper is the beast which we have to write to handle messages coming back from the server; more of that in a second.

We have to create the connection object as an instance of EPosixClientSocket initialising it with a callback function. We then call the eConnect function so that our tws object is a fully fledged connection. If that sounds like Outer Mongolian to you, don't worry. You shouldn't ever need to mess with this stuff. The point is once we have our client object (instance of IBclient) we can make it do cool things, like tell the time.

Telling the time - the hard way


We now call the speaking clock method of the IBclient:

    print client.speaking_clock()

### how many seconds before we give up
MAX_WAIT=30
 

    <snip - code missing here>


class IBclient(object): 
    <snip - code missing here>
 
    def speaking_clock(self):
        print "Getting the time... "
       
        self.tws.reqCurrentTime()
       
        start_time=time.time()
       
        self.cb.init_error()
        self.cb.init_time()

        iserror=False
        not_finished=True

        while not_finished and not iserror:
            not_finished=self.cb.data_the_time_now_is is None
            iserror=self.cb.flag_iserror

            if (time.time() - start_time) > MAX_WAIT:
                not_finished=False
               
            if iserror:
                not_finished=False
   
        if iserror:
            print "Error happened"
            print self.cb.error_msg
           
        return self.cb.data_the_time_now_is



tws.reqCurrentTime() is an example of a classy class EClientSocket functions, from the official IB API manual. These are the functions that ask the server to think about doing something.

 

Things that are interesting here #1: the wait loop 


 The issue we have here is that the IB API is very much set up as an event driven process. So not like normal sequential code like function A calling function B and function A returning the answer back. No instead we have function A just kind of hanging around waiting for function B and then somehow by magic function B just happens.

That is not the way I do stuff. I don't have to - I am running fairly slow trading systems not intraday high frequency stuff that needs to react to every tick for which an event driven system makes sense. Also it goes against my ethical beliefs. Why should function A have to wait for function B to run? What makes function B so special? Its just rudeness.

(Also I've never found debugging event driven code to be particularly easy .... )

So what we have to do is make the client-server relationship appear sequential, at least to anything sitting outside the wrapper module. That also means we need to handle the conditions of the thing not finishing in a reasonable time and finishing with an error.




Things that are interesting here #2: The contents of self.cb


All the things we are pulling out of self.cb (which is the callback we passed in when we initialised the IBclient object) set somewhere else as if by magic. Actually they get set when the IB server summons the callback, calling the appropriate method. In the official IB API manual these are the very classy Class EWrapper Functions.

There are three kinds of classy ClassE Wrapper functions / methods:

 

Pointless methods that don't do anything


class IBWrapper(EWrapper):
    """

        Callback object passed to TWS, these functions will be called directly
    by TWS.

    """

    <snip - code missing here>
       
    def nextValidId(self, orderId):
        pass
   
    def managedAccounts(self, openOrderEnd):
        pass



These methods are there because the IB server isn't very gracious about not being able to call certain methods, and they don't get inherited from EWrapper (why not?!?! yes why bloody not?!). When you start doing more interesting stuff you will find you might need more of these; you will start seeing errors like this (which you can reproduce by removing the managedAccounts method):


NotImplementedError: SWIG director pure virtual method called EWrapper::managedAccounts

 

Method that handle errors


class IBWrapper(EWrapper):
    """

        Callback object passed to TWS, these functions will be called directly
    by TWS.

    """

    def init_error(self):
        setattr(self, "flag_iserror", False)
        setattr(self, "error_msg", "")

    def error(self, id, errorCode, errorString):
        """
        error handling, simple for now
      
        Here are some typical IB errors
        INFO: 2107, 2106
        WARNING 326 - can't connect as already connected
        CRITICAL: 502, 504 can't connect to TWS.
            200 no security definition found
            162 no trades

        """

        ## Any errors not on this list we just treat as information
        ERRORS_TO_TRIGGER=[201, 103, 502, 504, 509, 200, 162, 420, 2105, 1100, 478, 201, 399]
      
        if errorCode in ERRORS_TO_TRIGGER:
            errormsg="IB error id %d errorcode %d string %s" %(id, errorCode, errorString)
            print errormsg
            setattr(self, "flag_iserror", True)
            setattr(self, "error_msg", True)
          
        ## Wrapper functions don't have to return anything

    <snip - code missing here>

Note the two different methods; init_error is just my own method to ensure the callback has somewhere to store the error. Wheras error() is mandated by the IB API - if you took it out or renamed it the thing would break.

You need to have at least the two levels of error handling as shown here since a lot of the IB "errors" are really just IB telling you its alive; kind of like a child whining you need to be able to know when to ignore them. Once you get serious you may even want to have a third level of errors for when things really get nasty.

The way we handle errors is to make our waiting process finish before its proper finishing time with an error flag set. Subtle point; if an error appears when no process is waiting does anyone hear it? If a tree falls in an empty forest... does a bear crap on it? Well we'll still print the error.... arguably if there is no process to fail I wouldn't care.... for example if we temporarily lost the internet connection because, just suppose as a completely arbitrary example, you had builders in the house and they tripped the fusebox for the 5th time that morning... but if we weren't waiting for a price at the time I wouldn't be that bothered. Well I'd be livid with the builders but I wouldn't want the python client-server code to throw its toys out of the pram so I had to restart it.

That is one reason why I don't raise an exception here but let the calling process do it.


Methods that actually do something vaguely useful

Of which we only have two here; again one which is my init_ method, and the other is the IB mandated version:

    def init_time(self):
        setattr(self, "data_the_time_now_is", None)


     def currentTime(self, time_from_server):

        setattr(self, "data_the_time_now_is", time_from_server)


So lets reiterate what happens here.

    <In the client function>
    def speaking_clock(self):
        print "Getting the time... "
       
        self.tws.reqCurrentTime()
       
        start_time=time.time()
       
        self.cb.init_error()
        self.cb.init_time()





    <in the IBWrapper function, of which IBclient self.cb is an instance>
    def init_time(self):
        setattr(self, "data_the_time_now_is", None)


    <back in the client function speaking_clock>

        iserror=False
        not_finished=True





        while not_finished and not iserror:
   

    <at some point in this while loop, in the IBWrapper function, of which IBclient self.cb is an instance this will get called at some point ... hopefully.... >
     def currentTime(self, time_from_server):

        setattr(self, "data_the_time_now_is", time_from_server)



    <and back in the client function speaking_clock, in the while loop>

            not_finished=self.cb.data_the_time_now_is is None
            iserror=self.cb.flag_iserror

            if (time.time() - start_time) > MAX_WAIT:
                not_finished=False
               
            if iserror:
                not_finished=False
         if iserror:
            print "Error happened"
            print self.cb.error_msg
           
        return self.cb.data_the_time_now_is


We set data_the_time_now in the callback to accept a value and then ask the server tws.reqCurrentTime(); then somewhere in the ether the IBWrapper instance method currentTime gets called by the server with the parameter time_from_server; we change the value of data_the_time_now in the callback instance, and this terminates the loop. All this assumes we don't get an error condition, or the server falls asleep and the process hits its MAX_WAIT. Pretty much everything else we do with the IB API is a variation on this particular theme so if that makes sense, you are now an expert.

(Note that the parameter names in the EWrapper method function definitions don't need to match those in the manual; in the manual it uses time which is already the name of an imported module.)

 

And We Are Done


Although this example is very simple, like the author, it does illustrate most of the 'gotchas' from working with swigibpy / Python / IB API. Subsequent posts will expand on this example to cover the full lifecycle of getting a price, generating an order, getting a fill, finding out what positions we have and working out whether we have made enough money to buy a decent laptop.




This is the first in a series of posts. The next post is:
http://qoppac.blogspot.co.uk/2014/04/getting-prices-out-of-ib-api-with.html