Order Matching Algorithm Description (Rough Draft – 2013/10/16)
Summary: A currency exchange is a system for buyers and sellers of different currencies to exchange different types of currency. The other matching module matches buy and sell orders, creates transactions to record the process, and updates the customers account balances.
Part One: Basic Order Processing
Action: A customer enters the quantity and prices of the orders and clicks “buy” or “sell”
1: Website creates record in OrderBook with Pending order status. The order is filed to be processed. [PlaceBuyBid
Then, the Order Matching Service iterates through the list of pending orders. [public int PlaceBuyBid(int customerId, decimal quantityOfBTC, decimal pricePerBTC, DateTime ?expirationDate = null) and public int PlaceSellOffer(int customerId, decimal quantityOfBTC, decimal pricePerBTC, DateTime ?expirationDate = null)] For each order:
2: Re-verify order status to ensure that it is still pending/active. If expired, the order is Cancelled
3: Validate the order funding. [public bool ValidateOrderFunding(Order order)] The customer must have sufficient assets to process the order. If not, the order is Suspended (it may be re-actived if funds become available later.) If order passes validation:
a: the order status is changed to Active
b: the assets needed to pay for the order are added to the Frozen balance. The prevents the customer from placing orders on more assets than he has. This feature may be removed later – we can allow spending greater than the available balance if we check the balance before processin the transaction.
4: The Order Matching Service tries to find a match to the buy or sell order. To find a match, we search all Active orders which match the specified price. [ISpecification IsMatchingOrderQuery(decimal price, int orderTypeId, int wantAssetTypeId,int offerAssetTypeId, bool? isMarketOrder)]
*If the order is a buy, we look for a price less than or equal.
*If the order is a sell, we look for a price greater than or equal.
*If it’s a market order, we find the highest (sell) or lowest (buy) price.
We sort the matches ascending for buy orders, and descending for sell orders. Then we sort by date if the price matches. [ISpecification IsMatchingOrderQuery(decimal price, int orderTypeId, int wantAssetTypeId,int offerAssetTypeId, bool? isMarketOrder)]
5: We load the top 3 matches into memory. The reason we take 3 matches is in case one of the orders fails validation, we can try with the order 2 orders.
6: We compare the order and the match. This is a double check with C# – the order should have been already matched by the database query above. The orders should match: assets ($/BTC, order types (buy/sell),non-two market orders, and have matching prices (as above). [OrderComparisonResult CompareOrders(Order firstOrder, Order secondOrder)]
7: If the orders comparison succeds, we generate a Transaction to record the match [Transaction GetTransactionForTwoOrders(OrderComparisonResult comparisonResult)].
*A_Order is the buy order
*B_Order is the sell order
(A and B are used because I’m still not sure whether ordering should be Buy/Sell, chronological, or something else)
8: If the order quantities do not match exactly, we must generate a split order with the remainder of the larger order.
9: We run [ActivateTakeProfitAndStopLossOrders(Order order)] – TODO – should not be done here, but scheduled. (See separate post for advanced orders – take proft, stop loss, trigger, stop order, etc.)
10: We process the transaction to record the result [public Order ProcessTransaction(Transaction transaction)] (note: this module is a database transaction)
a: add the transaction and the split orders to the DB
b: for both orders in the transaction:
- subtract the debit access (customers balance of $ or BTC)
- increment the credit asset (customers balance of $ or BTC)
- record the commision in the commision account
- unfreeze frozen balances
- save changes
11: Repeat the process on each split order, until there is either no remaining quantity (entire order is fullfilled) or we
run out of matching orders; the remaining split order stays open as an active order
foreach (OrderProcessResultModel n in ProcessOrder(splitOrder.OrderId))
yield return n;
12: If the remaining quantity is 0, set the status to Completed