Exchange Iceberg Orders
Iceberg orders are supported in New Order Single (35=D) FIX 4.2 and FIX 5.0 with the Create a new order REST API.
Iceberg orders allow you to disclose fills to the market in parts, by sending a New Order Single (D) with MaxFloor (111):
- A quantity equal to the MaxFloor is displayed on the order book, with the remainder added to the order book at the same price level as the hidden quantity at the end of the queue.
- When the displayed quantity is fully filled, matching logic continues to execute and fill the hidden quantity.
- Once the matching event is completed, if there is still a hidden quantity left, a new displayed order is added to the end of the display order queue, for a size up to MaxFloor.
Changes to the displayed portion of the order, such as replenishes, fills, STPs, or user cancels, are supplied via ExecutionReport with SecondaryOrderID (198).
Supported Pairs
See Iceberg Orders Supported Trading Pairs.
All trading pairs have Iceberg supported in sandbox.
Sample Message Flow
In this example message flow, a client submitted a buy order of quantity = 1 and while displaying only 0.3 at a time. (Non-relevant FIX fields and IDs are not shown for brevity.)
Event 1 - Passive New Order
A passive Iceberg order is placed with OrderQty=1 (38=1
) and MaxFloor=0.3 (111=0.3
):
OrderID123
is not in the Full channel, nor in the Level2 FIX Market Data. Instead, only the displayed portion is shown as Order456
.
- FIX
- WebSocket Full Channel
- WebSocket Level2 Channel
-> 35=D|55=BTC-USD|40=2|38=1|44=25000|111=0.3
<- 35=8|37=OrderID123|39=New|38=1
// Event generated when the first displayed portion of the order is added to the orderbook
<- 35=8|37=OrderID123|198=OrderID456|39=Restated|378=Broker_Option|38=0.3
{"order_id":"OrderID456","order_type":"limit","size":"0.3","price":"25000","type":"received","side":"buy","product_id":"BTC-USD","time":...}
{"order_id":"OrderID456","order_type":"limit","remaining_size":"0.3","price":"25000","type":"open","side":"buy","product_id":"BTC-USD","time":...}
{"type":"l2update","product_id":"BTC-USD","changes":[["buy","25000.0000","0.3000"]]...}
Event 2 - MaxFloor Match
A match takes place with quantity = 0.3.
Once the displayed order is fully filled, a Done_For_Day
with SecondaryOrderID is sent. This does not mean the Iceberg order is complete. Rather, a replenish event occurs with a different SecondaryOrderID. The new displayed order is added to the end of the queue of the displayed book at the price of 25000.
- FIX
- WebSocket Full Channel
- WebSocket Level2 Channel
<- 35=8|37=OrderID123|198=OrderID456|39=Partially_Filled|32=0.3|151=0.7|14=0.3|137=0.0004
<- 35=8|37=OrderID123|198=OrderID456|39=Done_For_Day|32=0|151=0.7|14=0.3
// Displayed portion replenish event
<- 35=8|37=OrderID123|198=OrderID789|39=Restated|378=Broker_Option|38=0.3
{"trade_id":1761595,"maker_order_id":"OrderID456","taker_order_id":"...","size":"0.3","price":"25000","type":"match","side":"buy",...}
{"order_id":"OrderID456","reason":"filled","price":"25000","remaining_size":"0","type":"done","side":"buy","product_id":"BTC-USD",...}
{"order_id":"Order789","order_type":"limit","size":"0.3","price":"25000","type":"received","side":"buy","product_id":"BTC-USD","time":...}
{"order_id":"Order789","order_type":"limit","remaining_size":"0.3","price":"25000","type":"open","side":"buy","product_id":"BTC-USD","time":...}
{"type":"l2update","product_id":"BTC-USD","changes":[["buy","25000.0000","0.0000"]]...} {"type":"l2update","product_id":"BTC-USD","changes":[["buy","25000.0000","0.3000"]]...}
Event 3 - Aggressive Order
An aggressive order takes place with quantity = 0.7.
In this event, a large aggressive order came in. First, the displayed OrderID789
was filled for 0.3. Then, because the displayed book was empty at the price level of 25000, the matching logic continued to the hidden book at the price level 25000. The hidden OrderID123
was filled for 0.4. Note that the Done_For_Day
ExecutionReport does not have a SecondaryOrderID and the the Iceberg order is complete.
- FIX
- WebSocket Full Channel
- WebSocket Level2 Channel
<- 35=8|37=OrderID123|198=OrderID789|39=Partially_Filled|32=0.3|151=0.4|14=0.6|137=0.0004
<- 35=8|37=OrderID123|198=OrderID789|39=Done_For_Day|32=0|151=0.4|14=0.6
<- 35=8|37=OrderID123|39=Partially_Filled|32=0.4|151=0|14=1.0|137=0.00045
<- 35=8|37=OrderID123|39=Done_For_Day|32=0|151=0|14=1.0
{"trade_id":1761596,"maker_order_id":"OrderID456","taker_order_id":"...","size":"0.3","price":"25000","type":"match","side":"buy",...}
{"order_id":"OrderID456","reason":"filled","price":"25000","remaining_size":"0","type":"done","side":"buy","product_id":"BTC-USD",...}
{"trade_id":1761596,"maker_order_id":"OrderID123","taker_order_id":"...","size":"0.4","price":"25000","type":"match","side":"buy",...}
{"type":"l2update","product_id":"BTC-USD","changes":[["buy","25000.0000","0.0000"]]...}
Trading Rules
To date, the Coinbase Exchange matching engine has followed price/time priority. With the introduction of Iceberg orders, our matching engine is changing to price/display/time priority. Regardless of whether they are displayed or hidden, better price orders will have higher priority. At a given price level, all displayed orders will have a higher priority than the hidden portion of the Iceberg orders. Replenish events will add a displayed order to the end of the displayed book at that price level.
Fees for Non-displayed Fills
Fees are subject to change, but in general:
- Fills for displayed orders are charged with the standard taker/maker fee rate.
- Fills for non-displayed orders are charged with the standard taker/maker fee rate + a fixed 0.00005 (half a bps).
Fills can occur as non-displayed when an Iceberg order:
- Is executed as a taker.
- Is resting in the non-displayed book, and a significant taker order clears all displayed orders, eventually executing against this non-displayed order.
Self Trade Prevention
Displayed and hidden orders will be treated separately. For instance, if you have an Iceberg buy order of OrderQty=1 and MaxFloor=0.3, and you place a sell order with OrderQty=0.2 with SelfTradePrevention=O (Cancel Old), the displayed order is canceled but the hidden order is not. Instead, a replenish event will occur.
- Cancel Old: Once the displayed order is touched, the displayed order is canceled and matching continues. If the aggressive order hits the hidden order, the hidden order will then be canceled.
- Decrement Cancel: Size matters. The displayed order is first decremented and the matching logic continues. If the hidden order has quantity remaining, a replenish event occurs. If the hidden order is crossed, it will then be decremented.
- Cancel Both: This is similar to the approach for Cancel Old. Only the displayed portion gets canceled with the aggressive order. A replenish event occurs, if there is hidden size left.
Allowed / Not Allowed
Allowed
- Post Only order with MaxFloor.
- Stop order with MaxFloor.
- MaxFloor must be >= 10% of the OrderQty .
Not Yet Allowed
- Batch order with MaxFloor.
- Modifying an Iceberg order.
- Iceberg order in auction mode (existing Iceberg orders will be canceled if a product is transitioned to auction mode).