Problem:
Currently when PlaceAskOrder is called, the server checks if the user has enough cash/stocks to fulfill that order, leaving that money/stocks still in the user's ownership. This is problematic because if my balance is Rs. 1000, then I can make several orders at the same time whose total value is Rs. 1000. For example one order for buying 10 stocks at 100 each, and another order for buying 5 stocks at 200 each.
While the PerformOrderFillTransaction checks if there is sufficient balance to execute the order, that's wasteful. And it also gives an incentive to the user to make random orders without thinking.
Change to be made:
I'd like to reserve the relevant money/stocks that the user has once he makes an order. So for example if he makes an order of buying 10 stocks at 100 each, the cash he has is 1000 lesser than what he had. Instead, this 1000 gets paid to the exchange as a deposit. Similarly if he wants to sell 10 stocks at 100 each, he will have 10 lesser stocks than what he had before making order.
How to go about changing:
On a high level we'll need to introduce two new types of transactions - PlaceOrderTransaction
and CancelOrderTransaction
.
PlaceOrderTransaction
will have only (Price
, StockQuantity
) or (Total
) fields non-zero. If it is an Ask
order, (Price
, StockQuantity
) will be set to the respective values, and Total
will be zero. If it is a Bid
order only the Total
field will be set.
CancelOrderTransaction
transaction will also have only one of (Price
, StockQuantity
) or (Total
) fields non-zero. However, the signs will differ. In PlaceOrderTransaction
the signs will be negative (StockQuantity
, or Total
- one of them. Price
is never negative). In CancelOrderTransaction
, since the stocks/money is going back to the user, the signs will be positive.
We've to take care that the following properties are maintained:
Sum(Total)
of a user's transactions should equal the cash he has in hand (not deposited for buying).
Sum(StockQuantity)
of a user's transactions for a given stock should equal the stocks he has in his own control (not deposited for selling or mortgaged)
Since we also want to be able to link the PlaceOrderTransaction
and CancelOrderTransaction
transactions to the relevant orders, we'll have to have a mapping between Orders
and Transactions
table. The best way I thought was to introduce a new table called Order_DepositWithdrawalTransactions
(or some better name). There will be three columns - OrderId
, TransactionId
and TransactionType
(PlaceOrderTransaction
/CancelOrderTransaction
). All three together uniquely determine a row.
What things to update:
This change will affect the following:
-
migrations:
a. Update Users
table to add a new column - cash_in_orders
.
b. Update Transactions
table to have two new types of transactions (alter the enum values) - PlaceOrderTransaction
and CancelOrderTransaction
.
c. Create a new table called Order_DepositWithdrawalTransactions
(or some better name) with fields OrderId
, TransactionId
and TransactionType
(PlaceOrderTransaction
/CancelOrderTransaction
)
-
models.User: Introduce a new field: CashInOrders
(take care of casing convention - pascal case in Go, snake case in database table)
-
proto/models.User: Make the same change in the proto/models/User.proto
. Make sure the ToProto
method in Users
model is in sync, including the test.
-
models.TransactionType: Make the transaction type include more values. Update the proto as well.
-
models.Order_DepositWithdrawalTransaction: Make a new model for this table. Give a better name to it I think. It's super lengthy, though conveys the point. This will be used while making the transactions. Also make sure you have the correct foreign key integrity checks. It's super important!
-
models.PlaceAskOrder: Update the logic to make a PlaceOrderTransaction
transaction to the database. Also update user's cash
and cash_in_orders
. You'll need to insert a row in the Order_DepositWithdrawalTransactions
table.
-
models.PlaceBidOrder: Update the logic to make a PlaceOrderTransaction
transaction to the database. You'll need to insert a row in the Order_DepositWithdrawalTransactions
table.
-
models.CancelOrder: Update the logic to refund the cash or stocks to the user. You'll need to insert a row in the Order_DepositWithdrawalTransactions
table.
-
models.OrderFillTransaction: This function currently checks if the user has enough balance or stocks. We don't need to do that anymore and can be dropped (however let the code for checking if order is closed or not be there). Also, the OrderFillTransaction
it creates in the Transactions table will have to have only one of the fields set (StockQuantity,Price) or (Total). If it's an Ask order, Total will be set to a positive value. Vice versa.
Remember the convention regarding transactions table - StockQuantity positive means stocks go to the user. Same way, Total positive means cash goes to the user. Negative is reverse.