SOR
The Smart Order Router (SOR) algorithm is designed to intelligently route orders across multiple exchanges for optimal execution. It supports both Market and Limit order types.
Key Factors for Execution Plan
The SOR considers the following factors when determining the best execution strategy for an incoming order:
- Available liquidity: Depth of the order book on each exchange, starting from the top.
- Commissions: Maker/taker fees and trade volume discounts offered by each exchange.
- Account balances: Ensures sufficient funds on each exchange to execute the order.
- Reject statistics: Tracks past rejections to avoid exchanges with recent issues.
- Order parameters: Price, quantity, precision, and minimum order size.
Algorithm Logic
The SOR algorithm follows a set of rules when evaluating liquidity across different venues:
Order Validation: Verifies the incoming order (Market or Limit). Fails the order if invalid.
Execution Plan: Builds a plan defining quantity and price for orders sent to each eligible exchange. The eligible exchanges list can be overridden in the order.
Aggressive Execution: Re-sorts the source liquidity by applying exchange-specific fees. Quotes offering equal prices after adjustment are further sorted by available liquidity.
Passive Execution: Allocates the residual order quantity across eligible venues based on a proportion of passive fills observed on each exchange.
Execution Plan
The SOR builds its execution plan based on the current market conditions across eligible exchanges.
Aggressive Execution: Executes orders based on whether the price is immediately executable on each price level on the opposite side of the order book.
Passive Execution: Allocates the residual amount not filled aggressively, based on historical passive turnover statistics per exchange (e.g., an exchange with higher passive turnover receives a higher allocation).
Child Order Rejection Handling
If a child order is rejected, the SOR marks the exchange as ineligible, rebuilds the execution plan, and sends new orders accordingly.
Exchange Eligibility
Default: All exchanges are eligible by default.
TIF Support: Exchanges that don't support the order's Time In Force are ineligible.
Rejection statistics: Exchanges with recent order rejections are temporarily ineligible.
If all exchanges are ineligible, the SOR cancels the order.
Order Constraints
The SOR takes into account the following constraints:
Minimal order increment and size: Defined by the exchange's security metadata.
Available balance: Sourced from Trading Connectors that publish real-time balance information. SOR verifies orders don't exceed the available balance on a given exchange.
Working with Balances
Balance Checks: The SOR always checks balances, but this can be disabled in the configuration file (
balances
stream parameter set tonull
).API Rate Limits: Exchanges often limit how frequently account balances can be requested. Consider this when determining the balance polling frequency.
Prioritizing Exchanges
The prioritizedExchanges
configuration parameter defines an order of preference. If multiple exchanges offer the same price, those on the prioritized list take precedence in the execution plan.
import { SORConfig } from '@botmain/execution';
const sorOrderConfig: SORConfig = {
prioritizedExchanges: ["COINBASE", "BINANCE", "KRAKEN"],
};
this.tradingApi.setSorConfig(sorOrderConfig)
Advanced Configuration
- Exchange Constraints: Granular control over supported order types, time-in-force conditions, and balance checks per exchange.
const exchangeConstraints: ExchangeConstraints[] = [
{
exchangeName: "COINBASE",
orderTypeDefinitions: [
{ orderType: "MARKET", timeInForces: ["GOOD_TILL_CANCEL", "IMMEDIATE_OR_CANCEL"] },
{ orderType: "LIMIT", timeInForces: ["GOOD_TILL_DATE", "IMMEDIATE_OR_CANCEL"] }
]
},
{
exchangeName: "GEMINI",
orderTypeDefinitions: [
{ orderType: "LIMIT", timeInForces: ["DAY", "GOOD_TILL_CANCEL"] }
],
aggressiveOrdersOnly: true // Only route aggressive orders to Gemini
},
];
this.tradingApi.setSorConfig({ exchangeConstraints })
- Example of sending order
// Example: Create a BUY Market order for 1 BTC/USD, eligible on COINBASE and GEMINI
const btcSorOrder = new SOROrder(
'BTCUSD',
1,
'buy',
undefined, // Market order, so price isn't needed
TimeInForce .GOOD_TILL_CANCEL,
['COINBASE', 'GEMINI']
);
// Example: Create a SELL Limit order for 0.5 ETH/USD at 1750, aggressive execution only
const ethSorOrder = new SOROrder(
'ETHUSD',
0.5,
'sell',
1750,
TimeInForce .IMMEDIATE_OR_CANCEL,
undefined, // Use default exchange list
true // Aggressive only flag
);
// Now these orders would be passed to the SOR algorithm.
// The SOR would split them into child orders and send them, applying exchange constraints.