What-if on combo order

Ultimate goal: Be able to submit both WhatIf:True & WhatIf:False on a combo order with SMART routing.

I read this here: The IBKR API requires setting a "non-guaranteed" flag on SMART-routed combo orders to acknowledge the risk of partial execution. QuantRocket sets this flag for you on SMART-routed combo orders.
For orders '6001:1', '6001:2' I submitted as WhatIf:True.
For order '6001:3' I submitted commented out (below). Either way I end up with the non-guaranteed error, but my interpretation is QR would do this automatically given the sentence above. The margin fields are NaN for all 3 orders. I'm submitting to a paper-account right now. This is a test on index options, wondering if Exchange: SMART must be different?

order_id = place_orders(orders=[{
        'Sid': 'IC3',                
        'Exchange': 'SMART',                    
        'OrderType': 'LMT',                     
        'LmtPrice': -2.50,                      
        'TotalQuantity': 1,                     
        'Action': 'BUY',                        
        'Tif': 'Day',
        'Account': 'xxxxx',                 
        'OrderRef': 'xxxxxx',    
        # 'WhatIf': True,                         
        }    
    ])

download_order_statuses(f, 
                    order_ids=['6001:1', '6001:2', '6001:3'], 
                    fields=['InitMarginChange', 'MaintMarginChange', 'EquityWithLoanChange', 'MinCommission', 'MaxCommission']
                    )
statuses = pd.read_csv(f)
display(statuses[['OrderId', 'Status']])
print(statuses['Errors'].values)
OrderId	Status
0	6001:1	Inactive
1	6001:2	Inactive
2	6001:3	Error

['[{"ErrorCode": 10043, "ErrorMsg": "Missing or invalid NonGuaranteed value. REL+MKT, LMT+MKT, and REL+LMT order of two legs can only be set as non-guaranteed."}]'
 '[{"ErrorCode": 10043, "ErrorMsg": "Missing or invalid NonGuaranteed value. REL+MKT, LMT+MKT, and REL+LMT order of two legs can only be set as non-guaranteed."}]'
 '[{"ErrorCode": 10043, "ErrorMsg": "Missing or invalid NonGuaranteed value. REL+MKT, LMT+MKT, and REL+LMT order of two legs can only be set as non-guaranteed."}]']

Side question: On the IBKR App & TWS if I submit a manual mid-price LMT credit combo order like the above it seems to always fill on all N legs or not at all; however, this NonGuaranteed section makes me question this. For example, I've never experienced submitting a bull-put credit spread selecting 2 legs with a combo bid-ask of 1.50-1.70, submit at -1.60, and only have either the long or short leg execute, they always have in tandem.

QuantRocket does set NonGuaranteed to 1 in smartComboRoutingParams, but there are plenty of ways to cause errors when submitting combo orders.

First, I would try other order types (e.g. all MKT), other contracts (e.g. stock options instead of index options), and perhaps other exchange values to isolate what part of the order is triggering the error. Can you also provide code to create the specific combo you're testing?

Combos are unfortunately a finicky corner of the IBKR API. The exact combination of parameters needed to avoid errors often changes from one version of IB Gateway to another. Combining combo orders with WhatIf may be even more challenging, although from your error messages it doesn't seem like WhatIf is the problem here.

Setting 'Exchange': 'CBOE' worked but ran into another issue.
I get values for InitMarginChange, MaintMarginChange, EquityWithLoanChange but I still get NaN for MinCommission, MaxCommission which was a primary piece I was hoping to get. I am manually calculating from IBKR $0.65/contract + CBOE take/add liquidity. Executions show ~$1.65045 so I think it's close enough for now.

Here's something I ran into.

# 1. Placed IC10 order, it worked ✅
place_orders('IC10') 

# 2. Checked list_positions() to confirm it was there ✅
# 3. Checked close_positions() ✅
# 4. Actually closed the position ✅
quantrocket blotter close --order-refs 'xxxx' --params 'OrderType:MKT' 'Tif:Day' 'Exchange:CBOE' | quantrocket blotter order -f '-'

But when I tried to re-run the custom script again I'm getting this failure whereas create_ibkr_combo() used to just return "IC10" if it was already created in the past. These legs are different, but seems like it's trying to give out IC10 again.
I did submit one with What-if:True and also got assigned IC10. This has me wondering if that's causing the backend to think IC10 is still available to assign when it shouldn't be?

1. Original IC10: Contracts 60, 70, 90, 100. ✅
2. A What-if:True was submitted, given IC10 too. ✅
3. This IC10: Contracts 70, 80, 95, 105. ❌

The error is also showing SMART but I submitted with CBOE.

# SPXW  250624P06070000, SPXW  250624P06080000, SPXW  250624C06095000, SPXW  250624C06105000. 
legs = [['BUY', 1, 'IB785127924'], ['SELL', 1, 'IB790920043'], ['SELL', 1, 'IB790919943'], ['BUY', 1, 'IB790919944']]
combo_sid = create_ibkr_combo(legs)

>> msg: 'error running function: (''500 Server Error: Internal Server Error for url:
  http://houston/master/combos/ibkr'', {''status'': ''error'', ''msg'': ''an unhandled
  exception occurred, please check flightlog for the traceback'', ''error'': ''(sqlite3.IntegrityError)
  UNIQUE constraint failed: SecurityIBKR.Sid\n[SQL: \n        INSERT INTO SecurityIBKR
  (\n            Sid,\n            ConId,\n            Symbol,\n            SecType,\n            Etf,\n            PrimaryExchange,\n            Currency,\n            LocalSymbol,\n            Timezone,\n            PriceMagnifier,\n            Multiplier,\n            ValidExchanges,\n            MinTick,\n            ComboLegs)\n        VALUES
  (\n            ?,?,?,?,0,?,?,?,?,?,?,?,?,?\n        )\n        ]\n[parameters: (\''IC10\'',
  -1110, \''SPX\'', \''BAG\'', \''SMART\'', \''USD\'', \''SPXW  250624P06070000,SPXW  250624P06080000,SPXW  250624C06095000,SPXW  250624C06105000\'',
  \''America/Chicago\'', 1, 100, \''SMART,IBUSOPT,CBOE\'', 0.05, \''[["BUY", 1, "IB789304577",
  789304577, "SMART", "OPT"], ["SELL", 1, "IB789304590", 789304590, "SMART", "OPT"],
  ["SELL", 1, "IB790919932", 790919932, "SMART", "OPT"], ["BUY", 1, "IB790919943",
  790919943, "SMART", "OPT"]]\'')]\n(Background on this error at: https://sqlalche.me/e/14/gkpj)''})'

The original IC10 that executed in paper & was closed.

On the 500 error, can you please edit your docker-compose.yml to reference this patch image:

master:
  image: 'quantrocket/master:2.11.0.patch20250624'

Then

docker compose pull master 
docker compose up -d master

Then try creating the combo again.

Ok it correctly moved onto IC11, IC12, IC13 after a few runs. I ran script 3 times: what-if:false, what-if:true, what-if:false and create_ibkr_combo(legs) didn't throw error.

2025-06-24 21:17:13 INFO Created IBKR combo with SID: IC11
# Submitted after hours & expiration (error is no surprise).
2025-06-24 21:17:14 quantrocket.blotter: WARNING ibg1 client 6001 got IBKR message code 399: Order Message: BUY 1 SPX Combo Warning: Your order will not be placed at the exchange until 2025-06-25 08:30:00 US/Central.
2025-06-24 21:17:14 quantrocket.blotter: WARNING ibg1 client 6001 got IBKR message code 201: Order rejected - reason:Order is already expired

Submitted during next trading day, worked :white_check_mark: :clap:

2025-06-25 11:32:38: INFO Created IBKR combo with SID: IC12
2025-06-25 11:32:39: INFO Placed order with ID: ['6001:33']

Additional info:
Issue that showed up inside quantrocket flightlog stream but went away with docker compose restart blotter (wasn't happening before patch, seems like non-issue now, just stating for transparency).

2025-06-24 21:14:25 quantrocket.blotter: ERROR Traceback (most recent call last):
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "sym://qrocket_log_py", line 34, in wrapped
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "sym://qrocket_spooler_py", line 36, in spooler_start_order_monitor
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "sym://qrocket_blotter_ibkr_sid_py", line 76, in refresh_cache
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "/opt/conda/lib/python3.11/site-packages/pandas/io/parsers/readers.py", line 948, in read_csv
2025-06-24 21:14:25 quantrocket.blotter: ERROR     return _read(filepath_or_buffer, kwds)
2025-06-24 21:14:25 quantrocket.blotter: ERROR            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "/opt/conda/lib/python3.11/site-packages/pandas/io/parsers/readers.py", line 611, in _read
2025-06-24 21:14:25 quantrocket.blotter: ERROR     parser = TextFileReader(filepath_or_buffer, **kwds)
2025-06-24 21:14:25 quantrocket.blotter: ERROR              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "/opt/conda/lib/python3.11/site-packages/pandas/io/parsers/readers.py", line 1448, in __init__
2025-06-24 21:14:25 quantrocket.blotter: ERROR     self._engine = self._make_engine(f, self.engine)
2025-06-24 21:14:25 quantrocket.blotter: ERROR                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "/opt/conda/lib/python3.11/site-packages/pandas/io/parsers/readers.py", line 1723, in _make_engine
2025-06-24 21:14:25 quantrocket.blotter: ERROR     return mapping[engine](f, **self.options)
2025-06-24 21:14:25 quantrocket.blotter: ERROR            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "/opt/conda/lib/python3.11/site-packages/pandas/io/parsers/c_parser_wrapper.py", line 93, in __init__
2025-06-24 21:14:25 quantrocket.blotter: ERROR     self._reader = parsers.TextReader(src, **kwds)
2025-06-24 21:14:25 quantrocket.blotter: ERROR                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-24 21:14:25 quantrocket.blotter: ERROR   File "parsers.pyx", line 586, in pandas._libs.parsers.TextReader.__cinit__
2025-06-24 21:14:25 quantrocket.blotter: ERROR pandas.errors.EmptyDataError: No columns to parse from file
2025-06-24 21:14:25 quantrocket.blotter: ERROR

Odd 503 ibg1 ERROR that showed up following morning (but connected fine prior night). Solved with docker compose up -d --force-recreate ibg1. This is just the satellite service starting ibg1 is if it's not running yet.

^C(base) root@5c5297fa24ad:/codeload# quantrocket flightlog stream
2025-06-25 11:23:13 quantrocket.ibg1: ERROR Traceback (most recent call last):
2025-06-25 11:23:13 quantrocket.ibg1: ERROR   File "sym://qrocket_log_py", line 34, in wrapped
2025-06-25 11:23:13 quantrocket.ibg1: ERROR   File "sym://qrocket_spooler_py", line 73, in spooler_monitor_gateway_connectivity
2025-06-25 11:23:13 quantrocket.ibg1: ERROR AttributeError: 'NoneType' object has no attribute 'decode'
2025-06-25 11:23:13 quantrocket.ibg1: ERROR 
2025-06-25 11:24:13 quantrocket.ibg1: ERROR Traceback (most recent call last):
2025-06-25 11:24:13 quantrocket.ibg1: ERROR   File "sym://qrocket_log_py", line 34, in wrapped
2025-06-25 11:24:13 quantrocket.ibg1: ERROR   File "sym://qrocket_spooler_py", line 73, in spooler_monitor_gateway_connectivity
2025-06-25 11:24:13 quantrocket.ibg1: ERROR AttributeError: 'NoneType' object has no attribute 'decode'
2025-06-25 11:24:13 quantrocket.ibg1: ERROR 
2025-06-25 11:24:30 quantrocket.satellite: ERROR Traceback (most recent call last):
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "sym://qrocket_app_py", line 112, in post
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/codeload/.py", line 67, in wrapper
2025-06-25 11:24:30 quantrocket.satellite: ERROR     result = func(*args, **kwargs)
2025-06-25 11:24:30 quantrocket.satellite: ERROR              ^^^^^^^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   , line 875, in run_main_sync
2025-06-25 11:24:30 quantrocket.satellite: ERROR     result = asyncio.run(main(output_file, cache_path))
2025-06-25 11:24:30 quantrocket.satellite: ERROR              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/asyncio/runners.py", line 190, in run
2025-06-25 11:24:30 quantrocket.satellite: ERROR     return runner.run(main)
2025-06-25 11:24:30 quantrocket.satellite: ERROR            ^^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/asyncio/runners.py", line 118, in run
2025-06-25 11:24:30 quantrocket.satellite: ERROR     return self._loop.run_until_complete(task)
2025-06-25 11:24:30 quantrocket.satellite: ERROR            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/asyncio/base_events.py", line 650, in run_until_complete
2025-06-25 11:24:30 quantrocket.satellite: ERROR     return future.result()
2025-06-25 11:24:30 quantrocket.satellite: ERROR            ^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/codeload/.py", line 767, in main
2025-06-25 11:24:30 quantrocket.satellite: ERROR     initial_data_collection()
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/codeload/.py", line 67, in wrapper
2025-06-25 11:24:30 quantrocket.satellite: ERROR     result = func(*args, **kwargs)
2025-06-25 11:24:30 quantrocket.satellite: ERROR              ^^^^^^^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/codeload/.py", line 134, in initial_data_collection
2025-06-25 11:24:30 quantrocket.satellite: ERROR     ibg1_status = start_gateways(wait=True, gateways=['ibg1'])['ibg1'] #gateways="ibg1"
2025-06-25 11:24:30 quantrocket.satellite: ERROR                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/site-packages/quantrocket/ibg.py", line 222, in start_gateways
2025-06-25 11:24:30 quantrocket.satellite: ERROR     houston.raise_for_status_with_json(response)
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/site-packages/quantrocket/houston.py", line 225, in raise_for_status_with_json
2025-06-25 11:24:30 quantrocket.satellite: ERROR     raise e
2025-06-25 11:24:30 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/site-packages/quantrocket/houston.py", line 217, in raise_for_status_with_json
2025-06-25 11:24:31 quantrocket.satellite: ERROR     response.raise_for_status()
2025-06-25 11:24:31 quantrocket.satellite: ERROR   File "/opt/conda/lib/python3.11/site-packages/requests/models.py", line 1021, in raise_for_status
2025-06-25 11:24:31 quantrocket.satellite: ERROR     raise HTTPError(http_error_msg, response=self)
2025-06-25 11:24:31 quantrocket.satellite: ERROR requests.exceptions.HTTPError: ('503 Server Error: SERVICE UNAVAILABLE for url: http://houston/ibgrouter/gateways?wait=True&gateways=ibg1', {'ibg1': {'status': 'error', 'msg': "502 Server Error: Bad Gateway for url: http://houston/ibg1/gateway?wait=True: b''"}})```