The process of synchronizing the data in the trading engine with what actually happens at the exchange is crucial to monitoring and controlling a live trading operation from within Superalgos. It is this process that allows users to access the information at the exchange throughout the life cycle of each order, on the fly.
This is when all calculations involving the actual size, actual rate, actual fees, and so on are made. We will closely look into each of the calculations.
Once the synchronization is done, the bot does the accounting (see Accounting), to keep the rest of the variables—like balances and the stage size—up to date with the reality of orders.
Actual Size Calculation
While users may define the target size of the stage in either the base asset or the quoted asset, the size of orders is always denominated in the base asset, as per the CCXT Library API, and—likely—most exchange’s APIs.
When the actual size of the order as reported by the exchange is different than the size defined by the user, the trading bot makes the adjustments described below.
Recalculate Actual Size
The actual size of the base asset is updated with the size reported by the exchange:
tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount
Then it’s time to calculate the actual size in the quoted asset. At this stage, the actual rate of the order hasn’t been established yet, so the existing rate is used temporarily.
tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value
Recalculate Size Placed
The open stage and close stage sections of the trading engine hierarchy keep track of the accumulated size of the orders that have been placed so that the stage size may be compared to the target size defined in the trading system.
If the size of the order as determined by the exchange is different from the original size of the order, the current stage size must be recalculated.
What the trading bot does in this case is subtracting the original size from the stage size placed, and then add the actual size, as obtained from the exchange. As usual, this is done both for the base asset and the quoted asset.
For clarity, each operation is done in two steps.
tradingEngineStage.stageBaseAsset.sizePlaced.value = tradingEngineStage.stageBaseAsset.sizePlaced.value - tradingEngineOrder.orderBaseAsset.size.value tradingEngineStage.stageBaseAsset.sizePlaced.value = tradingEngineStage.stageBaseAsset.sizePlaced.value + tradingEngineOrder.orderBaseAsset.actualSize.value tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value - tradingEngineOrder.orderQuotedAsset.size.value tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value + tradingEngineOrder.orderQuotedAsset.actualSize.value
Actual Rate Calculation
Market orders are placed without a definition in terms of the rate they will be executed at, as, by definition, a market rate is filled at whatever rate the order book allows.
In the case of limit orders—although the user defines a rate—the exchange may change it slightly, for multiple reasons. For example, due to issues related to decimals precision, or because the order may match existing orders in the order book with a better rate.
For those reasons, the trading bot gets the actual rate from the exchange and —in case it differs from the defined rate—makes the updates described below.
First of all, the actual rate under the order statistics node is updated with the actual rate provided by the exchange:
tradingEngineOrder.orderStatistics.actualRate.value = order.price
If the actual rate is the same as the rate of the order as originally defined, then that’s all. Otherwise, the bot goes back and recalculates the size of the order for the quoted asset and the size placed in the quoted asset for the corresponding stage—again.
Both calculations are virtually the same as described earlier, so we are not going to go over the details again. Suffice to say that the only difference is that the actual rate is used instead of the original rate as defined in the trading system.
And remember, the size in the base asset does not need to be recalculated, as it is not affected by the differences in rates.
Percentage Filled Calculation
This is a simple calculation to determine what percentage of the order has been filled, which is relevant later on to calculate fees and balances:
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / tradingEngineOrder.orderBaseAsset.actualSize.value
Fees Paid Calculation
Exchange fees are a crucial aspect of trading, as they may greatly affect the profitability of strategies.
There are a few fundamental workings that affect the calculations:
The fees are not explicitly reported by the exchange, hence the need to calculate them.
taker fee parameter is used to calculate the fees on market orders, while the
maker fee parameter is used to calculate fees for limit orders.
The exchange charges fees denominated in the asset that the user is acquiring in the transaction. For example, in the BTC/USDT market, a sell order exchanges BTC for USDT, and the exchange charges fees in USDT, because that is the asset you are acquiring. On the contrary, a buy order in the BTC/USDT market exchanges USDT for BTC, thus fees are charged in BTC.
Because orders (in particular limit orders) may be partially filled, the system uses the percentage filled calculated earlier to determine the fees to be paid for the portion filled, as the exchange charges fees for the amount filled only.
All of the above considerations are captured in the following calculations.
Market buy orders
Because you are acquiring the base asset, fees paid are calculated using the base asset actual size. Because it’s a market order, the taker fees parameter is used.
tradingEngineOrder.orderBaseAsset.feesPaid.value = tradingEngineOrder.orderBaseAsset.actualSize.value * sessionParameters.feeStructure.config.taker / 100 * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
Limit buy orders
Like with market buy orders, because you are acquiring the base asset, fees paid are calculated using the base asset actual size. Because it’s a limit order, the maker fees parameter is used.
tradingEngineOrder.orderBaseAsset.feesPaid.value = tradingEngineOrder.orderBaseAsset.actualSize.value * sessionParameters.feeStructure.config.maker / 100 * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
Market sell orders
Because you are acquiring the quoted asset, fees paid are calculated using the base asset actual size. Because it’s a market order, the taker fees parameter is used.
tradingEngineOrder.orderQuotedAsset.feesPaid.value = tradingEngineOrder.orderQuotedAsset.actualSize.value * sessionParameters.feeStructure.config.taker / 100 * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
Limit sell orders
Like with market sell orders, because you are acquiring the quoted asset, fees paid are calculated using the quoted asset actual size. Because it’s a limit order, the maker fees parameter is used.
tradingEngineOrder.orderQuotedAsset.feesPaid.value = tradingEngineOrder.orderQuotedAsset.actualSize.value * sessionParameters.feeStructure.config.maker / 100 * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
Size Filled Calculation
As mentioned earlier, and as may be inferred from the example orders presented earlier, the size filled is always reported by the exchange denominated in the base asset. That is:
tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled
Therefore, the trading bot needs to calculate the size filled in the quoted asset:
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = tradingEngineOrder.orderQuotedAsset.actualSize.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
Amount Received Calculation
The amount received is a simple calculation to facilitate the comprehension of the trade, given that the exchange does not provide that basic information. Remember that only the size filled is reported and that fees are subtracted from the size filled.
Market and limit buy orders
In these cases, the user receives the amount in the base asset.
tradingEngineOrder.orderBaseAsset.amountReceived.value = tradingEngineOrder.orderBaseAsset.sizeFilled.value - tradingEngineOrder.orderBaseAsset.feesPaid.value
Market and limit sell orders
In these cases, the user receives the amount in the quoted asset.
tradingEngineOrder.orderQuotedAsset.amountReceived.value = tradingEngineOrder.orderQuotedAsset.sizeFilled.value - tradingEngineOrder.orderQuotedAsset.feesPaid.value