Bracket Orders stay ApiPending / PreSubmitted

I am trying to submit Bracket Orders following this example: IBController 1.3.8 READ ME but I must be doing something wrong as they stay ApiPending (the BUY) and PreSubmitted (the STP), observed in the IBController application window.

The code:

_TRACE("Live Cache: " + NumToStr(av_funds, 1.2));
			
	for (sig=bo.GetFirstSignal(bar); sig; sig=bo.GetNextSignal(bar)) {				
				
		if (sig.isLong) {	
		
			if (sig.isEntry) {				
				
				// Check if already exists
				for( i = 0; ( symbol = StrExtract( order_symbols, i ) ) != ""; i++ ) {
					if (symbol == sig.Symbol) {
						block_buy = True;
						break;
					}
				} if (block_buy) {
					continue;
				}
				
				// Check if already long
				for( i = 0; ( symbol = StrExtract( positions, i ) ) != ""; i++ ) {
					if (symbol == sig.Symbol) {
						block_buy = True;
						break;
					}
				} if (block_buy) {
					continue;
				}				
				
				//msg = msg + " " + sig.Symbol + ":" + NumToStr(sig.PosSize);
								
				// Bracket order: 2% stop loss
				if (ibc.IsConnected()) {
					qty = floor(limit / sig.Price);					
										
					_TRACE("Transmitting live order: " + sig.Symbol + " BUY: " + NumToStr(qty, 1.0) + " " + NumToStr(sig.Price, 1.2) + " STP: " + NumToStr(sig.Price*0.98, 1.2));
					
					// does not work :(
					parent_id = ibc.PlaceOrder( sig.Symbol, "BUY", qty, "MKT", 0, 0, "Day", False ); // todo: limit price
					ibc.PlaceOrder( sig.Symbol, "Sell", qty, "STP", sig.Price*0.98, sig.Price*0.98, "GTC", True, 100, "", 0, parent_id );
					
					// works as expected
					//ibc.PlaceOrder( sig.Symbol, "Buy", qty, "MKT", 0, 0, "Day", True ); // todo: limit price
					
				}				
				
				//_TRACE(msg);				
			
			}			
			
			if (sig.IsExit()) {		
				if ( ibc.GetPositionSize( sig.Symbol ) > 0 ) {
					ibc.PlaceOrder( sig.Symbol, "Sell", sig.PosSize, "MKT", 0, 0, "Day", False ); // todo: limit price
				}
			}								
		}		
	}	
	
	bo.PostProcess();

Output:

Live Cache: 101,805.65
Transmitting live order: AZO BUY: 11 1,620.00 STP: 1,587.60

Tried "GTC" for both orders, did not help.

I might be blind but I really cannot see the substantial difference with the example provided in the guide. Please help.

All additional questions welcome, thanks in advance, etc.

Typically orders are not accepted by TWS if prices passed are incorrect. For example when you specify price that does not meet minimum price fluctuation requirements.

The STP order is the one that fails even with a low price such as sig.Price*0.90. This non-bracketed STP order fails as well:

stop_price = sig.Price*0.90;										
ibc.PlaceOrder( sig.Symbol, "Buy", qty, "MKT", 0, 0, "Day", True ); // todo: limit price
ibc.PlaceOrder( sig.Symbol, "Sell", qty, "STP", stop_price, stop_price, "GTC", True);

Buy orders are accpted but the Stop orders stay PreSubmitted.

How to submit a stop order correctly?

sig.Price * 0.90 may NOT be correct price. TWS puts strict limits on prices that you place in orders. If sig.Price is say 1.01 then 1.01 * 0.90 = 0.909 is incorrect because it violates 0.01 minimum fluctuation requirement (if that is a stock with ticksize = 0.01)

Note that if TickSize is set to the right value, you can align price to the tick using:

its = round(1/TickSize);	// 1/TickSize is ALWAYS an integer, rounded for precision
aligned_price = round(price*its)*TickSize; // closest price accepted by TWS

Regards

Tomasz, that was obviously not the case as the TRACE above indicates:

In that case ask Interactive Brokers why they don't accept the order that has proper price.
It is TWS that send status codes, not the client application.

Maybe you are not populating the stop prices correctly by using stop_price in both fields when the order type is STP:

When the order type is STP, it should be like this:

ibc.PlaceOrder( sig.Symbol, "Sell", qty, "STP", 0, stop_price, "GTC", True);

When the order type is LMT, it should be like this:

ibc.PlaceOrder( sig.Symbol, "Sell", qty, "LMT", stop_price, 0, "GTC", True);

When the order type is TRAIL, it should be like this:

trail_points = sig.Price - stop_price;
ibc.PlaceOrder( sig.Symbol, "Sell", qty, "LMT", 0, trail_points, "GTC", True);

Give that a try and see if it works.

Tried it both ways. That does not work (just chekcked):

stop_price = round(sig.Price*0.90*100)/100;
parent_id = ibc.PlaceOrder( sig.Symbol, "BUY", qty, "MKT", 0, 0, "Day", False ); // todo: limit price
ibc.PlaceOrder( sig.Symbol, "Sell", qty, "STP", 0, stop_price, "GTC", True, 100, "", 0, parent_id );

That means, it makes no difference in the outcome for both bracketed and non-bracketed orders.

Tried again with rounding the price to 2 decimal places with no progress: stop_price = round(sig.Price*0.90*100)/100;

This page Understanding all 13 OrderStatus states in TWS-Link - Software discussion board says that all STP orders are PreSubmitted until the Stop price is met and the order triggered. That would explain why the non-bracketed STP order remains PreSubmitted. The TWS API page is not very informative TWS API v9.72+: Placing Orders beyond stating that PreSubmitted means acceptance. However, all this does not explain the problem with the Bracket Order.

Tomasz, I understand that you cannot be held responsible for the features of the IB TWS API. However, I have been able to submit Bracket Orders with Python in the past (see TWS API v9.72+: Bracket Orders ) but I consistently fail to do it with Amibroker, although I followed the instructions in the guide.


I would be most grateful if it turned out that I had made some stupid mistake and actually not followed the guide...

For what it is worth, at the time of writing the manual for IBController IBController 1.3.8 READ ME
all examples worked perfectly fine and that included bracket orders. I don't remember if PreSubmitted status existed back then.
IB is free to change their status codes as they wish.

Having said that, the page you have quoted simply says that PreSubmitted is actually correct status now and there is nothing to worry about.

As I wrote, you should contact IB directly asking about their statuses. It is their software.
IBController is open source and does not do anything with your calls just passes them to TWS. You can check all sources and verify if you don't believe me.

For what it is worth, since 1/100 is not finite binary fraction it may still have some edge cases. For high-precision rounding it is better to use TickSize parameter of PlaceOrder/ModifyOrder call because it uses double precision internally:

As far as rounding prices goes, this is what I use. I have had no problems submitting bracket orders.

function RoundToTick(price)
{
	return round(price / TickSize) * TickSize;
}
1 Like

Most fortunately, somebody did just that in a private message.

This code results in BUY orders Filled and STP orders PreSubmitted that is probably normal (see above).

parent_id = ibc.PlaceOrder( sig.Symbol, "BUY", qty, "MKT", 0, 0, "Day", False ); // todo: limit price
ibc.PlaceOrder( sig.Symbol, "Sell", qty, "STP", 0, stop_price, "GTC", True, 100, "", parent_id );

Many thanks!

Isn't that the same that you already had in first place? Update: OK, I see you have incorrectly placed 0, before parent_id, in your original

There is also the ibc.GetLastError() function.
I use it like this:

parentId = ibc.PlaceOrder( symbol, openAction, quantity, mainOrderType, entryLimitPrice, entryPrice,
                           openOrderTime, false, 100, attributes );
if( parentId != "0" )
{
    childId1 = ibc.PlaceOrder( symbol, closeLimitAction, limitQuantity, "LMT", exitLimitPrice, 0,
            limitOrderTime, false, 100, attributes, parentId, ocaGroup ); 
    childId2 = ibc.PlaceOrder( symbol, closeStopAction, quantity, gStopType, 0, exitStopPrice,
            stopOrderTime, isTransmit, 100, attributes, parentId, ocaGroup );

    stat = "ApiPending";
    pollTime = 50;
    currTime = 0;
    while( (stat == "" OR stat == "ApiPending" OR stat == "PendingSubmit") AND stat != "Cancelled" AND currTime < totalWaitTime )
    {
        ThreadSleep( pollTime );
        currTime += pollTime;
        errMessage = ibc.GetLastError(parentId);
        stat = ibc.GetStatus(parentId, true);
    }
}

First: busy-waiting is highly discouraged. Secondly: the formula posted is incomplete (syntax errors). Please follow this advice: How to ask a good question

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.