Skip to main content

Order Management

Workflows

In this section, we shall present a series of order management workflows and concepts at a high level, with references to the relevant FIX messages in subsequent sub-sections. The platform supports three common order types, controlled by OrdType (40) which are entered using the NewOrderSingle [D] message.

Limit Order (OrdType = 2)

A Limit order is a priced order which is considered for either immediate execution (if a suitable counter order already exists in the market) or can remain in the order book until expiry.

Limit orders can be specified as having one of three possible TimeInForce (59) values:

  • Good Till Cancel (GTC). The order will remain in the order book until either fully filled or canceled.

  • Fill Or Kill (FOK). The order may either be either immediately and fully executed (possibly against multiple orders) or the full quantity will expire immediately. It is not possible to partially fill an FOK order.

  • Immediate Or Cancel (IOC). The order will be immediately filled as much as possible against existing orders, and any remainder will be expired immediately.

Successfully-entered limit orders will be acknowledged by an ExecutionReport [8] with OrdStatus (39) = 0 (new). If the order receives one or more (immediate) fills, then additional ExecutionReport [8] messages will be sent with OrdStatus (39) = 1 (partially filled) or 2 (fully filled). If the order carries an FOK or IOC TimeInForce (59) condition, then the remaining balance will be immediately expired with a final ExecutionReport [8] indicating OrdStatus (39) = C (expired).

Figure 6: Successful entry and acknowledgment of GTC limit order (without immediate fill)

Market Order (OrdType = 1)

A Market order is an unpriced order type which is considered for immediate execution against other (limit) orders already in the market, with any remainder immediately expiring.

Figure 7: Successful entry of market order which receives a partial fill and the remainder expires

Stop Limit Order (OrdType = 4)

A Stop Limit order is a long-lived order which remains hidden within the platform until the point at which a stop price is triggered. Once a trade occurs in the relevant instrument at a price above the indicated StopPx (99) for buy orders, or below the StopPx (99) for sell orders, then a limit order is placed into the order book at the indicated Price (44).

The conversion of the Stop Order into a Limit Order is notified to the order originator using an ExecutionReport [8] with ExecType (150) = 7 (stopped). Once conversion has happened, the order behaves as a normal limit order and can be executed or canceled as normal.

Since Stop Orders are long-lived orders, the only suitable TimeInForce (59) value for them is 1 (GTC).

Figure 8: Successful entry and triggering of a buy Stop Limit order

Post Only Order (OrdType = p)

A postOnly order never takes liquidity and assures that the user's order will always be a market maker.

Post-Only orders are applicable to only Limit Orders. By selecting this option, the system will automatically cancel the limit order, if it detects that it will be executed immediately upon the order placement.

Only applicable for GTC orders, all other values in tag59 (TimeInForce) will be rejected.

Margin Trading

The Bullish platform allows institutional users with prior approval to trade on margin, using funds borrowed from other institutional users (peer-to-peer). The system manages the automatic borrowing of assets required to satisfy orders placed, the repayment of those borrowed assets, as well as monitoring the leverage of borrowed assets.

Funds placed in an account can be used as collateral against loans to fulfill margin orders (requiring borrowing) entered into the platform. Each account will be allocated a "Maximum Initial Leverage," governing the maximum amount that can be initially borrowed against collateral (calculated in USD). The system monitors leverage as trading continues and asset prices change. Should leverage become too high, then the system may automatically restrict further borrowing, cancel orders and/or liquidate positions in order to reduce leverage and protect lenders. This liquidation process is described later in this section.

The platform manages both the allocation of loaned assets from other participants, the repayment of those margin loans, as well as the collection and payment of margin interest between the parties. For information on how to loan assets to the margin facility (and therefore earn interest on your assets), please contact Bullish. Note that the FIX API does not currently support the workflows to query margin or loan status or to create loan offers. Any trades arising as a result of the automatic creation of liquidation orders within the Bullish platform will, however, be communicated via FIX.

In case the client has multiple active FIX sessions, the liquidation trades will be sent to all these sessions that are tied to the specific trading account.

Orders which require a margin loan to complete them (i.e. the entering user does not have sufficient funds to complete the trade) are entered by setting CashMargin (544) = 2 (Margin Open) in the New Order Single [D] message.

Such orders are validated upon entry to confirm that both:

  1. sufficient collateral has been posted (up to Maximum Initial Leverage) and,
  2. that the pool of assets available to lend can accommodate the request.

If either of these two conditions is not met, then the order will be immediately rejected using an ExecutionReport [8] message with OrdRejectReason (103) equal to 2015 (Exceeded max open margin orders) or 3003 (Borrowing is unavailable) respectively.

When a margin order is accepted, the platform will automatically calculate the additional amount required to borrow as a result of accepting the order. This incremental borrow quantity is then reserved for the order and the user begins paying interest on the loan immediately. The amount borrowed is communicated via BorrowedQty (8004) in the ExecutionReport [8].

Figure 9: Unsuccessful entry of a margin order to buy BTCUSD due to a lack of USD assets

Figure 10: Successful margin order to buy BTCUSD which borrows USD 10,000

Once margin orders have been accepted by the platform, the leverage will be monitored as market conditions change to yield an overall health status. A change in leverage health can impact the range of actions that can be performed by the user as indicated in the table below.

Figure 11: Impact of leverage health on order management

Action / MetricHealthy (1x)Healthy (>1x)MonitorCautionDangerCriticalSuspended
Borrow?
Leverage rangeNo leverage (1x)> 1x and < 3x≥ 3x and < 5x≥ 5x and < 6x≥ 6x and < 12x≥ 12x and < 30x≥ 30x
Trade to increase leverage?
Trade to decrease leverage?
Cancel orders?
Withdraw?
Create AMM Instruction?
Transfer out?

IMPORTANT: this table may change from time to time, please check this knowledge article for the latest description of how leverage impacts the set of actions that can be performed, and how leverage is reduced through liquidation.

Leverage health status can be queried through the Bullish user interface or private WebSocket connection.

From a FIX order-management perspective, the key leverage health transitions are:

  • Healthy => Monitor. Any order which would further increase leverage is rejected at the time of order entry with an ExecutionReport [8] indicating OrderRejStatus (103) = 3042 (Insufficient loaned balance).

  • Caution => Danger. The system will automatically expire all open margin orders by sending an ExecutionReport [8] with OrderStatus (39) = C (Expired), and OrderRejReason (103) = 3020 (Unsolicited cancel).

Users with poor levels of leverage health can improve their standing by depositing more collateral or trading out of the loans. Should the user fail to take action to improve leverage health, then the Bullish Liquidation Engine will step in to automatically reduce leverage either partially (Danger health level) or fully (Critical health level).

Where the Liquidation Engine submits instructions to reduce leverage, users will receive one or more unsolicited ExecutionReport [8] messages, each indicating ExecType (150) = F (Trade) and CashMargin (544) = 3 (Margin close) and the details of the trade. These messages will be sent to all active FIX sessions of the users. Note that since the trade relates to an internal order that was automatically generated by the Bullish Liquidation Engine (as opposed to submitted by the user), the ClOrdID (11) value will NOT appear in these messages as it would not match any reference known by the user's software.

Figure 12: A leverage health transition from Caution to Danger triggers unsolicited order expiry

New Order Single (D)

The New Order Single [D] message is used by the client to send orders to the Bullish platform.

Table 11: New Order Single [D] message

TagField NameReq'dTypeComments
< Standard Header >Y35 = D
11ClOrdIDYStringUnique identifier of the order as assigned by the Bullish customer. Firms submitting multi-day (Good Till Cancel) orders should ensure uniqueness across days. The value provided must be a String representation of a positive integer (e.g., "12345"). ClOrdIDs containing non-digit characters or representing non-integer numeric values will be rejected
1AccountYStringAccount on which the order is being placed.
544CashMarginNcharWhether the order involves a margin transaction. Where not specified, orders are cash (no margin). 1 = Cash (no margin) 2 = Margin open (borrow to complete the transaction if necessary)
55SymbolYStringTicker symbol of the instrument to be traded. See symbology.
54SideYint1 = Buy 2 = Sell
60TransactTimeYUTCTimestampThe time that this order request was initiated/released in UTC
38OrderQtyYQtyOrder quantity expressed as a decimal and with a precision acceptable to the instrument's quantity precision and within the minimum / maximum quantity range for the instrument.
40OrdTypeYcharOrder type. NOTE: not all order types are accepted for all instruments. 1 = Market 2 = Limit 4 = Stop Limit p = Post Only
44PriceNPricePrice of the limit order, or resulting limit order in the case of a triggered Stop Limit order. Required for Order Types of 2 (limit) or 4 (stop limit), and should not be present for 1 (market). NOTE: Price will be validated against the instrument's tick size, and must be within any minimum/maximum price range indicated for the instrument.
99StopPxNPriceRequired for Order Type of 4 (stop limit), and should be absent in all other cases. NOTE: This price will be validated against the instrument's tick size, and must be within any minimum/maximum price range indicated for the instrument.
59TimeInForceNcharThe time validity for the order. Defaults to 1 (GTC) if not present. Ignored for Market Orders. 1 = Good-Till-Cancel (GTC) 3 = Immediate Or Cancel (IOC) 4 = Fill Or Kill (FOK)
115OnBehalfOfCompIDNStringAssigned value used to identify firm originating message if the message was delivered by a third party
20002MmpEnabledNBooleanMark order eligible for MMP checks. Important: manual admin action is necessary to activate MMP for an account. Once enabled on the account, please set this to "Y" on your new Order Request for the orders to be applicable for MMP checks
< Standard Trailer >Y

Execution Report (8)

The Bullish platform shall use this message to communicate various order state transitions to Bullish customers, as well as details of completed trades.

Table 12: ExecutionReport [8] message

TagField NameReq'dTypeComments
< Standard Header >35 = 8
37OrderIDYStringUnique order reference as assigned by Bullish.
11ClOrdIDNStringThe last-known OrderID as assigned by the originator. Not present for trades arising from orders generated by automated position liquidation. Not present for execution reports from orders entered via REST excluding the handle field which were canceled via FIX.
1AccountYStringAccount echoed from order
544CashMarginNcharWhether the order involves a margin transaction. Where not specified, orders are cash (no margin). 1 = Cash (no margin) 2 = Margin open 3 = Margin close
17ExecIDNStringUnique reference for the trade (in the case of fills)
1369MassActionReportIDNStringSent in the case of order cancellation as a result of an OrderMassCancelRequest. A unique reference for the action as allocated by Bullish and contained in the OrderMassCancelReport.
150ExecTypeYcharThe purpose of this execution report 0 = New 4 = Canceled 5 = Replaced 6 = Pending Cancel 7 = Stopped 8 = Rejected C = Expired D = Restated F = Trade
378ExecRestatementReasonNcharThe purpose of this execution report 5 = Partial decline of OrderQty<38>
39OrdStatusYcharThe status of the order after the action reported by this ExecutionReport has completed 0 = New 1 = Partially filled 2 = Fully filled 4 = Canceled 6 = Pending Cancel 7 = Stopped 8 = Rejected C = Expired
103OrdRejReasonNintReason for order rejection where ExecType (150) = 8 (rejected). See Error & Rejection Codes. 1 = Unknown symbol 5 = Unknown order 6 = Duplicate order 13 = Incorrect quantity 15 = Invalid account 18 = Invalid price increment 99 = Other 2013 = Invalid order type 2015 = Exceeded max open margin orders 2021 = Exceeded max open spot orders 3001 = Insufficient account balance 3003 = Borrowing is unavailable 3020 = Unsolicited cancel 3021 = Forced cancel 3031 = Price is out of range 3032 = Order is either closed or rejected 3033 = Leverage increase not permitted 3034 = Order entry throttle reached
58TextNStringDescribes why the order is in a specific state
55SymbolYStringSymbol for order
54SideYintEchoed from the order 1 = Buy 2 = Sell
44PriceNPriceEchoed from the order (if present)
99StopPxNPriceEchoed from the order (if present)
32LastQtyNQtyLast quantity traded (present for fills only)
31LastPxNPriceTrade price (present for fills only)
6AvgPxNPriceVolume-weighted average of the volume executed so far against this order.
14CumQtyYQtyThe cumulative quantity executed against this order so far (all fills). Value may be zero.
60TransactTimeYUTCTimestampTime of this Execution Report message.
38OrderQtyYQtyTotal order quantity echoed from Order Single.
151LeavesQtyYQtyThe remaining quantity available for further execution. Value may be zero for full fills.
381GrossTradeAmtNAmtQuote quantity deducted from asset account excluding fees
8004BorrowedQtyNQtyMargin orders only. The total amount of currency that has been borrowed in order to accommodate this order. BorrowedQtyCurr (8006) indicates the currency that was borrowed.
8006BorrowedQtyCurrNCurrencyMargin orders only. The currency borrowed in order to accommodate this order, with the quantity borrowed indicated by BorrowedQty (8004). This will be the quote currency for sell orders, and the base currency for buy orders.
40OrdTypeYcharEchoed from the order 1 = Market 2 = Limit 4 = Stop Limit
59TimeInForceNintEchoed from the order or defaulted to GTC for limit orders. Not present for Market Orders. 1 = Good-Till-Cancel (GTC) 3 = Immediate Or Cancel (IOC) 4 = Fill Or Kill (FOK)
136NoMiscFeesNNumInGroupPresent for fills. Always 1.
→ 137MiscFeeAmtNAmtThe fee amount.
→ 138MiscFeeCurrNCurrencyThe currency of the fee amount. This will be the quote currency for sell orders, and the base currency for buy orders.
→ 139MiscFeeTypeNintIndicates the type of fee described in this repeating group. 4 = Exchange Fee
797CopyMsgIndicatorNBooleanIndicates whether or not this message is a drop copy of another message
1057AggressorIndicatorNBooleanDenotes whether this trade was generated by the order taking liquidity (i.e. it was the aggressor).
20000TradeFeeRebateAmtNAmtAmount of rebate that is credited to the user as part of the trade, if the trade receives a rebate
20001TradeFeeRebateAssetNStringAsset of rebate that is credited to the user as part of the trade, if the trade receives a rebate
< Standard Trailer >

Order Cancellation

The Bullish platform supports order cancellation.

A request to cancel an existing order is made using the OrderCancelRequest [F] message, specifying the (Bullish-allocated) OrderID (37) reference for the order. A range of order attributes are requested on this cancellation request and are used to validate that the requestor has the same image of the outstanding order as the Bullish system at the time of the cancellation.

Table 13: Order Cancel Request [F] message

TagField NameReq'dTypeComments
< Standard Header >35 = F
37OrderIDNStringThe unique OrderID assigned by Bullish.
41OrigClOrdIDYStringThe last-known ClOrdID value for this order
11ClOrdIDYStringThe unique reference for this order cancellation request
1AccountYStringAccount on which the order is being placed.
55SymbolYStringSame as in the original order
60TransactTimeYUTCTimestampThe time of this order cancellation request
< Standard Trailer >

Since order cancellation requires a check within the matching engine to ensure that the order has not been executed whilst the cancellation request was in flight. For this reason, the expected response to a valid Order Cancel Request [F] is an ExecutionReport [8] indicating OrderStatus (39) = 6 (Pending Cancel), immediately followed by a second ExecutionReport [8] indicating OrderStatus (39) = 4 (Canceled).

Figure 13: Successful order entry and subsequent cancellation

In the case that the original order was a margin order, the cancellation of an order may release some borrowed quantity that had been earmarked for that order at the time of order entry (with a corresponding reduction in the user's interest payments). The ExecutionReport [8] message will therefore contain an updated BorrowedQty (8004) value, indicating the amount actually borrowed for this order after the cancellation has been completed.

If the cancellation attempt is unsuccessful for some reason, they will receive an OrderCancelReject [9] message from the Bullish platform indicating the reason for the rejection.

Table 14: Order Cancel Reject [9] message

TagField NameReq'dTypeComments
< Standard Header >35 = 9
37OrderIDNStringThe unique OrderID assigned by Bullish.
41OrigClOrdIDYStringThe last-known ClOrdID value for this order
11ClOrdIDYStringThe unique reference for the (rejected) order cancellation request.
39OrdStatusYCharStatus of the order after the cancellation rejection. 0 = New 1 = Partially filled 2 = Fully filled C = Expired
434CxlRejResponseToYcharThe type of cancellation that is being rejected 1 = Order cancel request
102CxlRejReasonYintThe reason for the rejection. See Error & Rejection Codes. 0 = Too late to cancel 1 = Unknown order 6 = Duplicate ClOrdID (11) received 99 = Other
1328RejectTextYStringText description of the rejection reason
60TransactTimeYUTCTimestampThe time of this order cancellation rejection
< Standard Trailer >

Figure 14: Successful order entry but unsuccessful cancellation

Mass Order Cancellation

The Bullish platform also supports mass order cancellation in the event that a participant wishes to quickly cancel all orders. This has the same functional outcome as Cancel on Disconnect, except that the FIX session (and network connection) remain connected.

A mass cancellation request is triggered by submitting an OrderMassCancelRequest [q] message. The symbol is optional.

Table 15: Order Mass Cancel Request [q] message

TagField NameReq'dTypeComments
< Standard Header >35 = q
11ClOrdIDYStringThe unique reference for this mass cancellation request
530MassCancelRequestTypeYcharThe type of cancellation request. 7 = Cancel all orders
1AccountYStringAccount on which the order is being placed.
55SymbolNStringSymbol for orders to cancel. Either Symbol or UnderlyingBaseSymbol only 1 can be present in the message
167SecurityTypeNcharThe market for which you want to cancel by UnderlyingBaseSymbol 1 = PERPETUAL 2 = DATED_FUTURE 3 = OPTION
20003UnderlyingBaseSymbolNStringUnderlying base asset symbol (only applies to derivatives markets) Valid values for UnderlyingBaseSymbol can be found here: https://api.exchange.bullish.com/trading-api/v1/markets Field: underlyingBaseSymbol
60TransactTimeYUTCTimestampThe time of this order cancellation request
< Standard Trailer >

The initial response to the mass order cancellation request is an OrderMassCancelReport [r] message.

In the case that the mass cancellation request is rejected, this message will indicate MassCancelResponse (531) = 0 (Cancel Request Rejected), with details of the rejection provided in MassCancelRejectReason (532) and Text (58).

Table 16: Order Mass Cancel Report [r] message

TagField NameReq'dTypeComments
< Standard Header >35 = r
11ClOrdIDYStringThe unique reference echoed from the OrderMassCancelRequest message.
1369MassActionReportIDYStringUnique reference for the mass cancel action, as allocated by the Bullish platform.
530MassCancelRequestTypeYcharThe type of cancellation request. 7 = Cancel all orders
531MassCancelResponseYcharThe actions taken as a result of the mass cancellation request. 0 = Cancel Request rejected 7 = Cancel All Orders
532MassCancelRejectReasonNintSent in the case of mass order cancel rejection. 99 = Other
58TextNStringSent in the case of mass order cancel rejection to provide a reason for the rejection.
60TransactTimeYUTCTimestampThe time of this order cancellation report
< Standard Trailer >

Figure 15: Unsuccessful attempt to mass cancel orders

If the mass order cancel is successful, then the platform will initially respond with an OrderMassCancelReport [r] indicating MassCancelResponse (531) = 7 (Cancel All Orders), and then a series of unsolicited ExecutionReport [8] messages - one for each order canceled - which will indicate OrderStatus (39) = 4 (Canceled), and which carry the unique MassActionReportID (1369) identifier to link the action to the mass order cancellation.

Figure 16: Successful mass order cancellation

Order Amendment

The Bullish platform supports order amendment.

A request to amend an existing order is made using the Order Cancel/Replace Request [G] (a.k.a. Order Modification Request), specifying the (Bullish-allocated) OrderID (37) reference for the order and last-known ClOrdID value for this order (OrigClOrdID). A range of order attributes are requested on this amendment request and are used to validate that the requestor has the same image of the outstanding order as the Bullish system at the time of the amendment.

Current supported amendment features:

  • Only GTC orders (Limit or Post-only) are eligible for amendment.
  • Only the following 3 fields are amendable: price, quantity, order type
  • QuantityFilled of the original order = 0 (Partially filled order amendment not supported)
  • There is no limit on how many times an order can be amended
  • At least one of the fields to be amended (price, quantity, order type) needs to be present in amend request

Table 17: Order Cancel/Replace Request [G] message

TagField NameReq'dTypeComments
< Standard Header >35 = G
37OrderIDNStringThe unique OrderID assigned by Bullish.
41OrigClOrdIDYStringThe last-known ClOrdID value for this order
11ClOrdIDYStringThe unique reference for this order amendment request
1AccountYStringAccount on which the order is being placed.
44PriceNPriceNew Price of the limit order. Mandatory because only limit orders are currently supported for order amendment. NOTE: Price will be validated against the instrument's tick size, and must be within any minimum/maximum price range indicated for the instrument.
40OrdTypeNcharOrder type. NOTE: not all order types are accepted for order amendment 2 = Limit p = Post Only
38OrderQtyNQtyNew Order quantity expressed as a decimal and with a precision acceptable to the instrument's quantity precision and within the minimum / maximum quantity range for the instrument.
55SymbolYStringSame as in the original order
60TransactTimeYUTCTimestampThe time of this order amendment request
< Standard Trailer >

Figure 17: Successful order entry and subsequent amendment

If the amendment attempt is unsuccessful for some reason, they will receive an OrderCancelReject [9] message from the Bullish platform indicating the reason for the rejection.

Table 18: Order Cancel Reject [9] message (Amendment)

TagField NameReq'dTypeComments
< Standard Header >35 = 9
37OrderIDNStringThe unique OrderID assigned by Bullish.
41OrigClOrdIDYStringThe last-known ClOrdID value for this order
11ClOrdIDYStringThe unique reference for the (rejected) order cancellation request.
39OrdStatusYCharStatus of the order after the amendment rejection. 0 = New 1 = Partially filled 2 = Fully filled 8 = Rejected C = Expired In scenarios where an amend request is rejected preliminarily due to message validation failure or rate limit threshold, this value will be 8 (Rejected), but this will not impact the status of the original order. Some preliminary rejection text messages: Unknown symbol Order disabled Invalid amend request Invalid UserID Incorrect quantity Price is out of range Invalid price increment Invalid order type Possible duplicate Rate limit error
434CxlRejResponseToYcharThe type of request that is being rejected 2 = Order Cancel/Replace Request (G)
102CxlRejReasonYintThe reason for the rejection. See Error & Rejection Codes. 0 = Too late to cancel 1 = Unknown order 6 = Duplicate ClOrdID (11) received 99 = Other
1328RejectTextYStringText description of the rejection reason
60TransactTimeYUTCTimestampThe time of this order amendment rejection
< Standard Trailer >

Figure 18: Successful order entry but subsequent unsuccessful amendment