From 5883104a768f7c10682677f4b97ec5de1e40d0de Mon Sep 17 00:00:00 2001 From: Tian Date: Tue, 24 Sep 2024 09:54:08 -0400 Subject: [PATCH] cap total size of vault orders in close only mode (#2329) --- protocol/x/vault/keeper/orders.go | 16 ++++++++---- protocol/x/vault/keeper/orders_test.go | 34 +++++++++++++------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/protocol/x/vault/keeper/orders.go b/protocol/x/vault/keeper/orders.go index 3e59423f19..311bbbbe38 100644 --- a/protocol/x/vault/keeper/orders.go +++ b/protocol/x/vault/keeper/orders.go @@ -384,11 +384,11 @@ func (k Keeper) GetVaultClobOrders( if vaultParams.Status == types.VaultStatus_VAULT_STATUS_CLOSE_ONLY { // In close-only mode with non-zero leverage. - reduceOnlyMaxOrderSize := k.GetVaultInventoryInPerpetual(ctx, vaultId, perpetual.Params.Id) + reduceOnlyTotalOrderSize := k.GetVaultInventoryInPerpetual(ctx, vaultId, perpetual.Params.Id) stepSize := lib.BigU(clobPair.StepBaseQuantums) - reduceOnlyMaxOrderSize.Quo(reduceOnlyMaxOrderSize, stepSize) - reduceOnlyMaxOrderSize.Mul(reduceOnlyMaxOrderSize, stepSize) - if reduceOnlyMaxOrderSize.Sign() == 0 { + reduceOnlyTotalOrderSize.Quo(reduceOnlyTotalOrderSize, stepSize) + reduceOnlyTotalOrderSize.Mul(reduceOnlyTotalOrderSize, stepSize) + if reduceOnlyTotalOrderSize.Sign() == 0 { return []*clobtypes.Order{}, nil } @@ -399,9 +399,15 @@ func (k Keeper) GetVaultClobOrders( reduceOnlySide = clobtypes.Order_SIDE_BUY } reduceOnlyOrders := make([]*clobtypes.Order, 0, len(orders)) + totalOrderSize := reduceOnlyTotalOrderSize.Uint64() for _, order := range orders { if order.Side == reduceOnlySide { - order.Quantums = lib.Min(order.Quantums, reduceOnlyMaxOrderSize.Uint64()) + if totalOrderSize == 0 { + break + } + + order.Quantums = lib.Min(order.Quantums, totalOrderSize) + totalOrderSize -= order.Quantums reduceOnlyOrders = append(reduceOnlyOrders, order) } } diff --git a/protocol/x/vault/keeper/orders_test.go b/protocol/x/vault/keeper/orders_test.go index 5e631e0b20..04df0a376d 100644 --- a/protocol/x/vault/keeper/orders_test.go +++ b/protocol/x/vault/keeper/orders_test.go @@ -1508,20 +1508,20 @@ func TestGetVaultClobOrders(t *testing.T) { expectedOrderQuantums: []uint64{}, }, "Success - Vault Clob 1, close-only status, 2 layers, leverage 0.6, sell orders only, " + - "order size capped to position size": { + "total size of orders capped to position size": { vaultStatus: vaulttypes.VaultStatus_VAULT_STATUS_CLOSE_ONLY, vaultQuotingParams: vaulttypes.QuotingParams{ Layers: 2, // 2 layers SpreadMinPpm: 7_654, // 76.54 bps SpreadBufferPpm: 2_900, // 29 bps SkewFactorPpm: 1_234_000, // 1.234 - OrderSizePctPpm: 1_000_000, // 100% + OrderSizePctPpm: 500_000, // 50% OrderExpirationSeconds: 4, // 4 seconds ActivationThresholdQuoteQuantums: dtypes.NewInt(1_000_000_000), }, vaultId: constants.Vault_Clob1, - vaultAssetQuoteQuantums: big.NewInt(500_000_000), // 500 USDC - vaultInventoryBaseQuantums: big.NewInt(250_000_000), // 0.25 ETH + vaultAssetQuoteQuantums: big.NewInt(1_000_000_000), // 1000 USDC + vaultInventoryBaseQuantums: big.NewInt(500_000_000), // 0.5 ETH clobPair: constants.ClobPair_Eth, marketParam: constants.TestMarketParams[1], marketPrice: constants.TestMarketPrices[1], @@ -1549,28 +1549,28 @@ func TestGetVaultClobOrders(t *testing.T) { expectedOrderSubticks: []uint64{ // spreadPpm = max(7_654, 2_900 + 50) = 7_654 // spread = 0.007654 - // open_notional = 250_000_000 * 10^-9 * 3_000 * 10^6 = 750_000_000 - // leverage = 750_000_000 / (500_000_000 + 750_000_000) = 0.6 + // open_notional = 500_000_000 * 10^-9 * 3_000 * 10^6 = 1_500_000_000 + // leverage = 1_500_000_000 / (1_000_000_000 + 1_500_000_000) = 0.6 // oracleSubticks = 3_000_000_000 * 10^(-6 - (-9) + (-9) - (-6)) = 3e9 - // leverage_0 = leverage - 0 * 1 = 0.6 + // leverage_0 = leverage - 0 * 0.5 = 0.6 // skew_ask_0 = -1.234 * 0.6 = -0.7404 // ask_spread_0 = (1 - 0.7404) * 0.007654 = 0.0019869784 // a_0 = 3e9 * (1 + 0.0019869784) = 3_005_960_935.2 ~= 3_005_961_000 (round up to 1000) 3_005_961_000, - // leverage_1 = leverage - 1 * 1 = -0.4 - // skew_ask_0 = -1.234 * -0.4 = 0.4936 - // ask_spread_0 = (1 + 0.4936) * 0.007654 = 0.0114320144 - // a_0 = 3e9 * (1 + 0.0114320144) = 3_034_296_043.2 ~= 3_034_297_000 (round up to 1000) - 3_034_297_000, + // leverage_1 = leverage - 1 * 0.5 = 0.1 + // skew_ask_0 = -1.234 * 0.1 = -0.1234 + // ask_spread_0 = (1 - 0.1234) * 0.007654 = 0.0067094964 + // a_0 = 3e9 * (1 + 0.0067094964) = 3020128489.2 ~= 3_020_129_000 (round up to 1000) + 3_020_129_000, }, - // order_size = 100% * 1250 / 3000 ~= 0.4166666667 + // order_size = 50% * 2500 / 3000 ~= 0.4166666667 // order_size_base_quantums = 0.4166666667e9 ~= 416_666_667 - // order_size_base_quantums = min(416_666_667, inventory) - // = min(416_666_667, 250_000_000) = 250_000_000 // round down to nearest multiple of step_base_quantums=1_000. expectedOrderQuantums: []uint64{ - 250_000_000, - 250_000_000, + 416_666_000, + // this order size is capped at `position size - sum of previous order sizes` + // = 500_000_000 - 416_666_000 = 83_334_000 + 83_334_000, }, expectedTimeInForce: []clobtypes.Order_TimeInForce{ clobtypes.Order_TIME_IN_FORCE_UNSPECIFIED,