Skip to content

Commit

Permalink
[CT-1327] place post only orders first in prepare check state (#2618)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayy04 authored Dec 2, 2024
1 parent 369a0c1 commit b062a1c
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 23 deletions.
10 changes: 5 additions & 5 deletions protocol/mocks/MemClob.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions protocol/testutil/constants/stateful_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,19 @@ var (
}

// Long-Term post-only orders.
LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT5_PO = clobtypes.Order{
OrderId: clobtypes.OrderId{
SubaccountId: Alice_Num0,
ClientId: 0,
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
ClobPairId: 0,
},
Side: clobtypes.Order_SIDE_BUY,
Quantums: 5,
Subticks: 10,
GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{GoodTilBlockTime: 5},
TimeInForce: clobtypes.Order_TIME_IN_FORCE_POST_ONLY,
}
LongTermOrder_Alice_Num0_Id0_Clob0_Buy100_Price10_GTBT15_PO = clobtypes.Order{
OrderId: clobtypes.OrderId{
SubaccountId: Alice_Num0,
Expand Down Expand Up @@ -1360,6 +1373,19 @@ var (
GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{GoodTilBlockTime: 10},
TimeInForce: clobtypes.Order_TIME_IN_FORCE_POST_ONLY,
}
LongTermOrder_Dave_Num0_Id0_Clob0_Sell025BTC_Price50000_GTBT10_PO = clobtypes.Order{
OrderId: clobtypes.OrderId{
SubaccountId: Dave_Num0,
ClientId: 0,
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
ClobPairId: 0,
},
Side: clobtypes.Order_SIDE_SELL,
Quantums: 25_000_000,
Subticks: 50_000_000_000,
GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{GoodTilBlockTime: 10},
TimeInForce: clobtypes.Order_TIME_IN_FORCE_POST_ONLY,
}

// Long-Term reduce-only orders.
LongTermOrder_Bob_Num0_Id2_Clob0_Sell10_Price35_GTB20_RO = clobtypes.Order{
Expand Down
47 changes: 37 additions & 10 deletions protocol/x/clob/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,42 @@ func PrepareCheckState(
offchainUpdates,
)

// 3. Place all stateful order placements included in the last block on the memclob.
// 3. Go through the orders two times and only place the post only orders during the first pass.
longTermOrderIds := keeper.GetDeliveredLongTermOrderIds(ctx)
offchainUpdates = keeper.PlaceStatefulOrdersFromLastBlock(
ctx,
longTermOrderIds,
offchainUpdates,
true, // post only
)

offchainUpdates = keeper.PlaceConditionalOrdersTriggeredInLastBlock(
ctx,
processProposerMatchesEvents.ConditionalOrderIdsTriggeredInLastBlock,
offchainUpdates,
true, // post only
)

replayUpdates := keeper.MemClob.ReplayOperations(
ctx,
localValidatorOperationsQueue,
shortTermOrderTxBytes,
offchainUpdates,
true, // post only
)
if replayUpdates != nil {
offchainUpdates = replayUpdates
}

// 4. Place all stateful order placements included in the last block on the memclob.
// Note telemetry is measured outside of the function call because `PlaceStatefulOrdersFromLastBlock`
// is called within `PlaceConditionalOrdersTriggeredInLastBlock`.
startPlaceLongTermOrders := time.Now()
longTermOrderIds := keeper.GetDeliveredLongTermOrderIds(ctx)
offchainUpdates = keeper.PlaceStatefulOrdersFromLastBlock(
ctx,
longTermOrderIds,
offchainUpdates,
false, // post only
)
telemetry.MeasureSince(
startPlaceLongTermOrders,
Expand All @@ -208,27 +235,27 @@ func PrepareCheckState(
metrics.Count,
)

// 4. Place all conditional orders triggered in EndBlocker of last block on the memclob.
// 5. Place all conditional orders triggered in EndBlocker of last block on the memclob.
offchainUpdates = keeper.PlaceConditionalOrdersTriggeredInLastBlock(
ctx,
processProposerMatchesEvents.ConditionalOrderIdsTriggeredInLastBlock,
offchainUpdates,
false, // post only
)

// 5. Replay the local validator’s operations onto the book.
replayUpdates := keeper.MemClob.ReplayOperations(
// 6. Replay the local validator’s operations onto the book.
replayUpdates = keeper.MemClob.ReplayOperations(
ctx,
localValidatorOperationsQueue,
shortTermOrderTxBytes,
offchainUpdates,
false, // post only
)

// TODO(CLOB-275): Do not gracefully handle panics in `PrepareCheckState`.
if replayUpdates != nil {
offchainUpdates = replayUpdates
}

// 6. Get all potentially liquidatable subaccount IDs and attempt to liquidate them.
// 7. Get all potentially liquidatable subaccount IDs and attempt to liquidate them.
liquidatableSubaccountIds := keeper.DaemonLiquidationInfo.GetLiquidatableSubaccountIds()
subaccountsToDeleverage, err := keeper.LiquidateSubaccountsAgainstOrderbook(ctx, liquidatableSubaccountIds)
if err != nil {
Expand All @@ -241,14 +268,14 @@ func PrepareCheckState(
keeper.GetSubaccountsWithPositionsInFinalSettlementMarkets(ctx)...,
)

// 7. Deleverage subaccounts.
// 8. Deleverage subaccounts.
// TODO(CLOB-1052) - decouple steps 6 and 7 by using DaemonLiquidationInfo.NegativeTncSubaccounts
// as the input for this function.
if err := keeper.DeleverageSubaccounts(ctx, subaccountsToDeleverage); err != nil {
panic(err)
}

// 8. Gate withdrawals by inserting a zero-fill deleveraging operation into the operations queue if any
// 9. Gate withdrawals by inserting a zero-fill deleveraging operation into the operations queue if any
// of the negative TNC subaccounts still have negative TNC after liquidations and deleveraging steps.
negativeTncSubaccountIds := keeper.DaemonLiquidationInfo.GetNegativeTncSubaccountIds()
if err := keeper.GateWithdrawalsIfNegativeTncSubaccountSeen(ctx, negativeTncSubaccountIds); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions protocol/x/clob/e2e/conditional_orders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ func TestConditionalOrder(t *testing.T) {
constants.Dave_Num0_500000USD,
},
orders: []clobtypes.Order{
constants.LongTermOrder_Dave_Num0_Id0_Clob0_Sell025BTC_Price50000_GTBT10,
constants.LongTermOrder_Dave_Num0_Id0_Clob0_Sell025BTC_Price50000_GTBT10_PO,
constants.ConditionalOrder_Carl_Num0_Id0_Clob0_Buy05BTC_Price50000_GTBT10_TP_49999_PO,
},
priceUpdateForFirstBlock: &prices.MsgUpdateMarketPrices{
Expand Down Expand Up @@ -1869,7 +1869,7 @@ func TestConditionalOrder_TriggeringUsingMatchedPrice(t *testing.T) {
constants.Order_Dave_Num1_Id0_Clob0_Sell1BTC_Price49997_GTB10,
constants.Order_Carl_Num1_Id0_Clob0_Buy1BTC_Price50003_GTB10,
// Place the conditional order.
constants.LongTermOrder_Dave_Num0_Id0_Clob0_Sell025BTC_Price50000_GTBT10,
constants.LongTermOrder_Dave_Num0_Id0_Clob0_Sell025BTC_Price50000_GTBT10_PO,
constants.ConditionalOrder_Carl_Num0_Id0_Clob0_Buy05BTC_Price50000_GTBT10_TP_49999_PO,
},
expectedInTriggeredStateAfterBlock: map[uint32]map[clobtypes.OrderId]bool{
Expand Down
4 changes: 2 additions & 2 deletions protocol/x/clob/e2e/order_removal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestConditionalOrderRemoval(t *testing.T) {
constants.Bob_Num0_10_000USD,
},
orders: []clobtypes.Order{
constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT5,
constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT5_PO,
constants.ConditionalOrder_Bob_Num0_Id0_Clob0_Sell10_Price10_GTBT10_PO_SL_15,
},

Expand Down Expand Up @@ -629,7 +629,7 @@ func TestOrderRemoval(t *testing.T) {
constants.Alice_Num0_10_000USD,
constants.Bob_Num0_10_000USD,
},
firstOrder: constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT5,
firstOrder: constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT5_PO,
secondOrder: constants.LongTermOrder_Bob_Num0_Id0_Clob0_Sell10_Price10_GTBT10_PO,

expectedFirstOrderRemoved: false,
Expand Down
21 changes: 20 additions & 1 deletion protocol/x/clob/keeper/orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@ func (k Keeper) AddPreexistingStatefulOrder(
// PlaceStatefulOrdersFromLastBlock validates and places stateful orders from the last block onto the memclob.
// Note that stateful orders could fail to be placed due to various reasons such as collateralization
// check failures, self-trade errors, etc. In these cases the `checkState` will not be written to.
// Note that this function also takes in a postOnlyFilter variable and only places post-only orders if
// postOnlyFilter is true and non-post-only orders if postOnlyFilter is false.
//
// This function is used in:
// 1. `PrepareCheckState` to place newly placed long term orders from the last
// block from ProcessProposerMatchesEvents.PlacedStatefulOrderIds. This is step 3 in PrepareCheckState.
Expand All @@ -496,6 +499,7 @@ func (k Keeper) PlaceStatefulOrdersFromLastBlock(
ctx sdk.Context,
placedStatefulOrderIds []types.OrderId,
existingOffchainUpdates *types.OffchainUpdates,
postOnlyFilter bool,
) (
offchainUpdates *types.OffchainUpdates,
) {
Expand All @@ -521,6 +525,12 @@ func (k Keeper) PlaceStatefulOrdersFromLastBlock(
}

order := orderPlacement.GetOrder()

// Skip post-only orders if postOnlyFilter is false or non-post-only orders if postOnlyFilter is true.
if postOnlyFilter != order.IsPostOnlyOrder() {
continue
}

// Validate and place order.
_, orderStatus, placeOrderOffchainUpdates, err := k.AddPreexistingStatefulOrder(
ctx,
Expand Down Expand Up @@ -575,10 +585,14 @@ func (k Keeper) PlaceStatefulOrdersFromLastBlock(
// PlaceConditionalOrdersTriggeredInLastBlock takes in a list of conditional order ids that were triggered
// in the last block, verifies they are conditional orders, verifies they are in triggered state, and places
// the orders on the memclob.
//
// Note that this function also takes in a postOnlyFilter variable and only places post-only orders if
// postOnlyFilter is true and non-post-only orders if postOnlyFilter is false.
func (k Keeper) PlaceConditionalOrdersTriggeredInLastBlock(
ctx sdk.Context,
conditionalOrderIdsTriggeredInLastBlock []types.OrderId,
existingOffchainUpdates *types.OffchainUpdates,
postOnlyFilter bool,
) (
offchainUpdates *types.OffchainUpdates,
) {
Expand Down Expand Up @@ -608,7 +622,12 @@ func (k Keeper) PlaceConditionalOrdersTriggeredInLastBlock(
}
}

return k.PlaceStatefulOrdersFromLastBlock(ctx, conditionalOrderIdsTriggeredInLastBlock, existingOffchainUpdates)
return k.PlaceStatefulOrdersFromLastBlock(
ctx,
conditionalOrderIdsTriggeredInLastBlock,
existingOffchainUpdates,
postOnlyFilter,
)
}

// PerformOrderCancellationStatefulValidation performs stateful validation on an order cancellation.
Expand Down
Loading

0 comments on commit b062a1c

Please sign in to comment.