Closed thraneh closed 10 months ago
Using the FIX Bridge, it was noticed that it was not possible to properly issue an order cancel request. This would only happen after gateway restart.
These are the gateway event following restart:
The order was downloaded (update_type=SNAPSHOT)
update_type=SNAPSHOT
order_update={stream_id=0, account="T1", order_id=1327747935573, exchange="binance-futures-fapi", symbol="ETHUSDT", side=BUY, position_effect=UNDEFINED, max_show_quantity=nan, order_type=LIMIT, time_in_force=GTC, execution_instructions=, create_time_utc=1704560847804000000ns, update_time_utc=1704560847804000000ns, external_account="", external_order_id="1257003957", client_order_id="", status=WORKING, quantity=0.010000000000000002, price=2000, stop_price=0, risk_exposure=nan, risk_exposure_change=nan, remaining_quantity=nan, traded_quantity=0, average_traded_price=0, last_traded_quantity=0, last_traded_price=0, last_liquidity=UNDEFINED, routing_id="clordi100867", max_request_version=1, max_response_version=1, max_accepted_version=1, update_type=SNAPSHOT, sending_time_utc=0ns, user="fix-bridge", strategy_id=0}
In particular, notice
external_order_id="1257003957"
client_order_id=""
status=WORKING
routing_id="clordi100867"
The next update for this order comes from the drop-copy stream
order_trade_update={event_type=ORDER_TRADE_UPDATE, event_time=1704560892025ms, transaction_time=1704560892015ms, account_alias="", execution_report={symbol="ETHUSDT", client_order_id="WwIBVa3xIzUBAQAAAAAA", side=BUY, order_type=LIMIT, time_in_force=GTC, original_quantity=0.010000000000000002, original_price=2000, average_price=0, stop_price=0, execution_type=CANCELED, order_status=CANCELED, order_id=1257003957, last_filled_quantity=0, order_filled_accumulated_quantity=0, last_filled_price=0, margin_asset="", commission_asset="USDT", commission=0, order_trade_time=1704560892015ms, trade_id=0, bids_notional=0, asks_notional=0, is_trade_maker=false, is_reduce_only=false, stop_price_working_type=CONTRACT_PRICE, original_order_type=LIMIT, position_side=BOTH, if_close_all=false, activation_price=nan, callback_rate=nan, realized_profit=0, unknown_1=false, unknown_2=0, unknown_3=0, unknown_4="", self_trade_prevention_mode="NONE", price_match_mode="NONE", good_till_date=0ms}}
Notice
execution_type=CANCELED
order_status=CANCELED
Which results in this order update
order_update={stream_id=3, account="T1", order_id=1327747935573, exchange="binance-futures-fapi", symbol="ETHUSDT", side=BUY, position_effect=UNDEFINED, max_show_quantity=nan, order_type=LIMIT, time_in_force=GTC, execution_instructions=, create_time_utc=1704560847804000000ns, update_time_utc=1704560892015000000ns, external_account="", external_order_id="1257003957", client_order_id="", status=CANCELED, quantity=0.010000000000000002, price=2000, stop_price=0, risk_exposure=0, risk_exposure_change=nan, remaining_quantity=nan, traded_quantity=0, average_traded_price=0, last_traded_quantity=0, last_traded_price=0, last_liquidity=TAKER, routing_id="clordi100867", max_request_version=1, max_response_version=1, max_accepted_version=1, update_type=INCREMENTAL, sending_time_utc=1704560892025000000ns, user="fix-bridge", strategy_id=0}
status=CANCELED
This is a secondary bug, we will get back to this towards the end
Then we get the request (from the FIX Bridge) to cancel the order
cancel_order={account="T1", order_id=1327747935573, request_template="", routing_id="clordi100868", version=2, conditional_on_version=1}
This looks correct.
However, this is the REST request that is being forwarded to Binance
symbol=ETHUSDT&origClientOrderId=&recvWindow=5000
origClientOrderId=
We then get an exchange reject
{"code":-1102,"msg":"Param 'orderid' or 'origclientorderid' must be sent, but both were empty/null!"}
This is the bug that was observed.
There are therefore 2 issues
client_order_id
external_order_id
orderId
origClientOrderId
The latter must be fixed for both order modify and cancel requests.
This leaves the question why the order got canceled.
Looking at the FIX Bridge log
It is unclear how the order got canceled. Two options exist:
The user has confirmed that the order was not manually canceled.
The gateway has the option to instruct the exchange to auto-cancel orders following disconnect
--rest_cancel_on_disconnect
false
--rest_order_countdown
30s
The default should therefore be to not auto-cancel.
However, we find this in the gateway log (signature intentionally removed)
POST /fapi/v1/countdownCancelAll?timestamp=1704560894174&signature=
Inspecting the code, it appears that only --rest_order_countdown is being tested against non-empty.
This is a secondary bug: the --rest_cancel_on_disconnect flag should guide whether auto-cancel is enabled or not.
This has also been fixed.
Using the FIX Bridge, it was noticed that it was not possible to properly issue an order cancel request. This would only happen after gateway restart.
These are the gateway event following restart:
The order was downloaded (
update_type=SNAPSHOT
)In particular, notice
external_order_id="1257003957"
,client_order_id=""
(empty),status=WORKING
, androuting_id="clordi100867"
(only relevant because the FIX Bridge is involved).The next update for this order comes from the drop-copy stream
Notice
execution_type=CANCELED
andorder_status=CANCELED
Which results in this order update
Notice
status=CANCELED
Then we get the request (from the FIX Bridge) to cancel the order
This looks correct.
However, this is the REST request that is being forwarded to Binance
Notice
origClientOrderId=
(empty)We then get an exchange reject
This is the bug that was observed.
There are therefore 2 issues
client_order_id
was not populated during downloadexternal_order_id
(asorderId
) whenclient_order_id
(asorigClientOrderId
) is unavailable.This leaves the question why the order got canceled.
Looking at the FIX Bridge log
status=CANCELED
It is unclear how the order got canceled. Two options exist:
The gateway has the option to instruct the exchange to auto-cancel orders following disconnect
--rest_cancel_on_disconnect
(default isfalse
)--rest_order_countdown
(default30s
)The default should therefore be to not auto-cancel.
However, we find this in the gateway log (signature intentionally removed)
Inspecting the code, it appears that only
--rest_order_countdown
is being tested against non-empty.This is a secondary bug: the
--rest_cancel_on_disconnect
flag should guide whether auto-cancel is enabled or not.This has also been fixed.