Match price increment in TWS AIP and IB Controller

When you submit a bunch of orders via IB Controller TWS API rejects those orders if the price is wrongly incremented.

For example if you want to buy BMW @IBIS stock and the required price variation is 0,01 and the buy price is 50,015, the API will reject the order. Different exchanges have different price increments which also depend on the price range.
The price increment of the contract has been set by the exchange. Exchange rejects the order which do not follow it. You may retrieve this information via API by calling reqMarketRule method. Below is the documentation.
https://interactivebrokers.github.io/tws-api/minimum_increment.html

How could I implement this reqMarketRule to check the price increment so that IB Controller could submit the correct price?

you need to set the ticksize in your order, see

https://www.amibroker.com/at/

so for oil futures it is 100, for ES-mini futures it is 2500 for most stocks with price increments of 0.01 it is also 100 like oil

I set the Ticksize inside the information window of Amibroker and then calculate the tz you need to send to IB

so I use this formula

    if( TickSize != 0 AND TickSize >= 0.0001 )
    {
        tz = round( TickSize * 10000 );
    }
    else
        if( TickSize != 0 AND TickSize == 0.00001 )
        {
            tz = 0.5;
        }
        else
            if( TickSize == 0 )
            {
                tz = 100;
            }

But if your trade currencies that have increments smaller than 0.00001 there is a problem. That will not work correctly when using the IBc. But I do not trade them so for me there is no problem

3 Likes

Thank you. The problem is that different stocks have different Ticksizes so I can't make a rule for hundreds of stocks in advance. To get the correct ticksize reqMarketRule method would be needed to fetch the right ticksize for a particular stock but how do I implement it?

there probably is a function to get the ticksize but as far as I know it is not implemented in the IB controller. So I set it manually. Maybe you can ask @alligator I think he was using the open source IB controller code.

Is there anybody who understood what I asked? There is no such a function in IB Controller to check the correct ticksize before submitting the order as far as I know. Has anybody managed to implement the reqMarketRule method?

I would probably just use AFL Function Reference - PREC to send orders with 2 decimals or I would probably handle the IB controller error returned.

So no miracle solutions...

You have to pass correct TickSize argument in the bo.PlaceOrder call. That parameter is different and unrelated to TickSize variable in AFL. Don't mix them.

They use different scale and serve different purposes. The TickSize argument in the bo.PlaceOrder call is used to ROUND prices internally in IBController with greater precision (64-bit) before sending to TWS. It is crucial to use that when placing the order and NOT use prec() in AFL.
The rounding procedure is clearly shown in the source, it uses double (64-bit precision) i.e. 15 significant digits:

Thank you, Tomasz. I already use TickSize argument in the bo.PlaceOrder call. My question is how to get the 'correct TickSize argument' because it is sometimes rounded to 1 digit, sometimes 2 digits and sometimes 3 digits. It changes stock by stock. IB support mentioned that I should use the reqMarketRule method API to get that info. But how do I implement it?

IB staff has no clue about third party apps.
You don't need to use any APIs to use proper arguments. Besides their APIs are super slow (it takes seconds for them to respond to calls as it goes thru Internet). It is better and faster to just use code provided by @empottasch already in this thread. By storing required ticksize in AmIBroker native database, you have immediate access (NO delay at all).

since we now have access to Python via Amibroker we can now also use the Python IBAPI.

I am no specialist but there is a lot to be found on the net. So I made a small code that gives you an idea how to access this information you need via the use of the Python plugin. I assume you have Python installed and know how it works. You need to install the "ibapi" (pip install ibapi in the command window). I also use another library i found on the net called ib_insync. So if you have it all installed and press the Param window button "Get Smallest Increment". The output information is printed in the Title of your Amibroker window. So I did not work it out but I just show that it is possible to get this information.

In the example code I retrieve the increment for the June contract of ES futures

so then the AFL test file looks like:

PyLoadFromFile( "IBSmallestIncrement", "C:\\Users\\win 10\\AppData\\Local\\Programs\\Python\\Python38\\mypython\\ib\\ibtest1.py" );

trig1 = ParamTrigger( "Trig1", "Get Smallest Increment" );
trig2 = ParamTrigger( "Trig2", "Clear" );

if( trig2 )
{
	StaticVarSetText( "output", "" );
}

if( trig1 )
{
	StaticVarSetText( "output", "" );
    outputArray = PyEvalFunction( "IBSmallestIncrement", "getSmallestIncrement" );
    StaticVarSetText( "output", outputArray );
}

Plot( C, "", colorWhite, styleCandle );

output = StaticVarGetText( "output" );

Title = "Output: " + output;

and the Python file looks like

'''
pip install ibapi
pip install ib_insync

for ib_insync see: https://docplayer.net/178553718-Ib_insync-release-ewald-de-wit-feb-18-2020.html

examples
Contract(conId=270639)
Stock('AMD', 'SMART', 'USD')
Stock('INTC', 'SMART', 'USD', primaryExchange='NASDAQ')
Forex('EURUSD')
CFD('IBUS30')
Future('ES', '20180921', 'GLOBEX')
Option('SPY', '20170721', 240, 'C', 'SMART')
Bond(secIdType='ISIN', secId='US03076KAA60')
'''

import ibapi
from ib_insync import *
import asyncio

#def getSmallestIncrement( contract, exchange, type, currency, date ):
def getSmallestIncrement():
    # connect to TWS

    asyncio.set_event_loop(asyncio.new_event_loop())
    ib = IB()
    ib.connect('127.0.0.1', 7496, clientId=1)

    # con = Forex('EURUSD')
    # con = Stock('AMD', 'SMART', 'USD')
    con = Future('ES', '20210618', 'GLOBEX')

    cd = ib.reqContractDetails(con)[0]

    # print(cd.marketRuleIds)
    rules = [ib.reqMarketRule(ruleId)
    for ruleId in cd.marketRuleIds.split(',')]

    ib.disconnect()
    listToStr = ' '.join(map(str, rules))

    return listToStr
3 Likes

the Python code actually doesn't need ibapi because I think this is integrated in ib_insync. I installed it anyway but to run the code it is not needed. Also the asyncio part is handled by ib_insinc. In the documentation (page 3) he mentions to use util.StartLoop().

so the code would then become

'''
pip install ibapi
pip install ib_insync

for ib_insync see: https://docplayer.net/178553718-Ib_insync-release-ewald-de-wit-feb-18-2020.html
https://github.com/erdewit/ib_insync

examples
Contract(conId=270639)
Stock('AMD', 'SMART', 'USD')
Stock('INTC', 'SMART', 'USD', primaryExchange='NASDAQ')
Forex('EURUSD')
CFD('IBUS30')
Future('ES', '20180921', 'GLOBEX')
Option('SPY', '20170721', 240, 'C', 'SMART')
Bond(secIdType='ISIN', secId='US03076KAA60')
'''

# import ibapi
from ib_insync import *
# import asyncio

#def getSmallestIncrement( contract, exchange, type, currency, date ):
def getSmallestIncrement():
    # connect to TWS

    # asyncio.set_event_loop(asyncio.new_event_loop())
    util.Startloop()
    ib = IB()
    ib.connect('127.0.0.1', 7496, clientId=1)

    # con = Forex('EURUSD')
    # con = Stock('AMD', 'SMART', 'USD')
    con = Future('ES', '20210618', 'GLOBEX')

    cd = ib.reqContractDetails(con)[0]

    # print(cd.marketRuleIds)
    rules = [ib.reqMarketRule(ruleId)
    for ruleId in cd.marketRuleIds.split(',')]

    ib.disconnect()
    listToStr = ' '.join(map(str, rules))

    return listToStr

1 Like

little typo error, because I tested code on other computer, so I just made the edit here

until.Startloop()

should be

util.startLoop()