Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(perp): settle markets #1573

Merged
merged 27 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c23b000
feat(perp): impl AMM.SettlementPrice func #wip
Unique-Divine Sep 13, 2023
27d1821
refactor(perp): rename amm.MarkPrice -> amm.InstMarkPrice to reflect …
Unique-Divine Sep 13, 2023
d4f0234
Merge branch 'master' into realu/uniform-settlement
jgimeno Sep 18, 2023
5978e68
remove unused param
jgimeno Sep 25, 2023
0413191
Merge remote-tracking branch 'origin/master' into realu/uniform-settl…
jgimeno Sep 25, 2023
9050403
add settlement
jgimeno Sep 25, 2023
be275b4
open position on disabled market
jgimeno Sep 25, 2023
9445c25
close market and don't allow to close positions on close market
jgimeno Sep 26, 2023
90f726e
add settlement tests part 1
jgimeno Sep 27, 2023
c31b35f
add get amm
jgimeno Sep 28, 2023
42b2771
temp commit for refactoring position to include versioning
jgimeno Sep 28, 2023
8dde9f2
Merge branch 'master' into realu/uniform-settlement
jgimeno Sep 28, 2023
7ef4de7
make versioned positions
jgimeno Sep 29, 2023
80b1382
Merge remote-tracking branch 'origin/master' into realu/uniform-settl…
jgimeno Sep 29, 2023
a5a4de5
Merge remote-tracking branch 'origin/master' into realu/uniform-settl…
jgimeno Oct 3, 2023
8a529a7
Merge branch 'master' into realu/uniform-settlement
jgimeno Oct 3, 2023
083db93
Merge branch 'realu/uniform-settlement' of github.com:NibiruChain/nib…
jgimeno Oct 3, 2023
b3d7181
Merge branch 'master' into realu/uniform-settlement
jgimeno Oct 3, 2023
f5701bb
Merge branch 'realu/uniform-settlement' of github.com:NibiruChain/nib…
jgimeno Oct 3, 2023
f271a00
merge conflicts
jgimeno Oct 5, 2023
8c77486
add check when position does not exist
jgimeno Oct 5, 2023
975b69a
Merge branch 'master' into realu/uniform-settlement
jgimeno Oct 8, 2023
e810f72
feat: implement setllement price calculation on close market
matthiasmatt Oct 9, 2023
8b1a342
fix: changelog
matthiasmatt Oct 9, 2023
20217e0
fix: lint
matthiasmatt Oct 9, 2023
a9db538
Merge remote-tracking branch 'origin/master' into realu/uniform-settl…
matthiasmatt Oct 10, 2023
812fc4b
fix: lint
matthiasmatt Oct 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion x/perp/v2/integration/action/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/NibiruChain/nibiru/x/perp/v2/types"
)

// Logger
type logger struct {
log string
}
Expand Down
23 changes: 0 additions & 23 deletions x/perp/v2/integration/action/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/testutil/action"
)

type changeLiquidationFeeRatio struct {
LiquidationFeeRatio sdk.Dec
}

type setMarketEnabled struct {
Enable bool
Pair asset.Pair
}

func (c changeLiquidationFeeRatio) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
// TODO(k-yang): implement

Expand All @@ -28,20 +22,3 @@ func ChangeLiquidationFeeRatio(liquidationFeeRatio sdk.Dec) action.Action {
LiquidationFeeRatio: liquidationFeeRatio,
}
}

// Enable market
func SetMarketEnabled(pair asset.Pair, enable bool) action.Action {
return setMarketEnabled{
Enable: enable,
Pair: pair,
}
}

func (c setMarketEnabled) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.ChangeMarketEnabledParameter(ctx, c.Pair, c.Enable)
if err != nil {
return ctx, err, true
}

return ctx, nil, true
}
26 changes: 25 additions & 1 deletion x/perp/v2/integration/action/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,32 @@ func ClosePosition(account sdk.AccAddress, pair asset.Pair) action.Action {
}
}

// Manually insert position, skipping open position logic
type closePositionFailsAction struct {
Account sdk.AccAddress
Pair asset.Pair

expectedErr error
}

func (c closePositionFailsAction) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
_, err := app.PerpKeeperV2.ClosePosition(ctx, c.Pair, c.Account)

if !errors.Is(err, c.expectedErr) {
return ctx, fmt.Errorf("expected error %s, got %s", c.expectedErr, err), true
}

return ctx, nil, true
}

func ClosePositionFails(account sdk.AccAddress, pair asset.Pair, expectedErr error) action.Action {
return &closePositionFailsAction{
Account: account,
Pair: pair,
expectedErr: expectedErr,
}
}

// Manually insert position, skipping open position logic
type insertPosition struct {
position types.Position
}
Expand Down
69 changes: 69 additions & 0 deletions x/perp/v2/integration/action/settlement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package action

import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/testutil/action"
)

type closeMarket struct {
pair asset.Pair
}

func (c closeMarket) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.CloseMarket(ctx, c.pair)
if err != nil {
return ctx, err, false
}

return ctx, nil, true
}

func CloseMarket(pair asset.Pair) action.Action {
return closeMarket{pair: pair}
}

type settlePosition struct {
account sdk.AccAddress
pair asset.Pair
version uint64
}

func (s settlePosition) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.SettlePosition(ctx, s.account, s.pair, s.version)
if err != nil {
return ctx, err, false
}

return ctx, nil, true
}

func SettlePosition(account sdk.AccAddress, pair asset.Pair, version uint64) action.Action {
return settlePosition{account: account, pair: pair, version: version}
}

type settlePositionShouldFail struct {
account sdk.AccAddress
pair asset.Pair
version uint64

expectedErr error
}

func (s settlePositionShouldFail) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.SettlePosition(ctx, s.account, s.pair, s.version)

if !errors.Is(err, s.expectedErr) {
return ctx, fmt.Errorf("expected error: %v, got: %v", s.expectedErr, err), false
}

return ctx, nil, true
}

func SettlePositionShouldFail(account sdk.AccAddress, pair asset.Pair, version uint64, expectedErr error) action.Action {
return settlePositionShouldFail{account: account, pair: pair, version: version, expectedErr: expectedErr}
}
31 changes: 3 additions & 28 deletions x/perp/v2/keeper/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ import (
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/denoms"
"github.com/NibiruChain/nibiru/x/common/testutil"
. "github.com/NibiruChain/nibiru/x/common/testutil/action"
"github.com/NibiruChain/nibiru/x/common/testutil/mock"
"github.com/NibiruChain/nibiru/x/common/testutil/testapp"
. "github.com/NibiruChain/nibiru/x/perp/v2/integration/action"
. "github.com/NibiruChain/nibiru/x/perp/v2/integration/assertion"
"github.com/NibiruChain/nibiru/x/perp/v2/keeper"
types "github.com/NibiruChain/nibiru/x/perp/v2/types"
"github.com/NibiruChain/nibiru/x/perp/v2/types"
)

func TestAdmin_WithdrawFromInsuranceFund(t *testing.T) {
Expand Down Expand Up @@ -85,28 +82,6 @@ func TestAdmin_WithdrawFromInsuranceFund(t *testing.T) {
testutil.RunFunctionTests(t, testCases)
}

func TestEnableMarket(t *testing.T) {
pair := asset.Registry.Pair(denoms.BTC, denoms.NUSD)

tests := TestCases{
TC("true -> false").
Given(
CreateCustomMarket(pair),
MarketShouldBeEqual(pair, Market_EnableShouldBeEqualTo(true)),
).
When(
SetMarketEnabled(pair, false),
MarketShouldBeEqual(pair, Market_EnableShouldBeEqualTo(false)),
SetMarketEnabled(pair, true),
).
Then(
MarketShouldBeEqual(pair, Market_EnableShouldBeEqualTo(true)),
),
}

NewTestSuite(t).WithTestCases(tests...).Run()
}

func TestCreateMarket(t *testing.T) {
pair := asset.Registry.Pair(denoms.BTC, denoms.NUSD)
amm := *mock.TestAMMDefault()
Expand Down Expand Up @@ -159,8 +134,8 @@ func TestCreateMarket(t *testing.T) {
})
require.ErrorContains(t, err, "already exists")

// Disable the market to test that we can create it again but with an increased version
err = app.PerpKeeperV2.ChangeMarketEnabledParameter(ctx, pair, false)
// Close the market to test that we can create it again but with an increased version
err = app.PerpKeeperV2.CloseMarket(ctx, pair)
require.NoError(t, err)

err = app.PerpKeeperV2.Admin().CreateMarket(ctx, keeper.ArgsCreateMarket{
Expand Down
12 changes: 11 additions & 1 deletion x/perp/v2/keeper/amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (k Keeper) handleMarketUpdateCost(ctx sdk.Context, pair asset.Pair, costAmt
return nil
}

// GetMarket returns the market with last version.
// GetMarket returns the market that is enabled. It is the last version of the market.
func (k Keeper) GetMarket(ctx sdk.Context, pair asset.Pair) (types.Market, error) {
lastVersion, err := k.MarketLastVersion.Get(ctx, pair)
if err != nil {
Expand All @@ -132,6 +132,16 @@ func (k Keeper) GetMarket(ctx sdk.Context, pair asset.Pair) (types.Market, error
return market, nil
}

// GetMarketByPairAndVersion this function returns the market by pair and version. It can be enabled or disabled.
func (k Keeper) GetMarketByPairAndVersion(ctx sdk.Context, pair asset.Pair, version uint64) (types.Market, error) {
market, err := k.Markets.Get(ctx, collections.Join(pair, version))
if err != nil {
return types.Market{}, fmt.Errorf("market with pair %s and version %d not found", pair, version)
}

return market, nil
}

// SaveMarket saves the market by pair and version.
func (k Keeper) SaveMarket(ctx sdk.Context, market types.Market) {
k.Markets.Insert(ctx, collections.Join(market.Pair, market.Version), market)
Expand Down
25 changes: 25 additions & 0 deletions x/perp/v2/keeper/amm_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"fmt"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -416,3 +417,27 @@ func TestEditSwapInvariant(t *testing.T) {

NewTestSuite(t).WithTestCases(tests...).Run()
}

func TestKeeper_GetMarketByPairAndVersion(t *testing.T) {
app, ctx := testapp.NewNibiruTestAppAndContext()

pair := asset.Registry.Pair(denoms.BTC, denoms.NUSD)

err := app.PerpKeeperV2.Admin().CreateMarket(
ctx,
keeper.ArgsCreateMarket{
Pair: pair,
PriceMultiplier: sdk.NewDec(2),
SqrtDepth: sdk.NewDec(1_000_000),
},
)
require.NoError(t, err)

market, err := app.PerpKeeperV2.Admin().GetMarketByPairAndVersion(ctx, pair, 1)
require.NoError(t, err)
require.Equal(t, market.Version, uint64(1))
require.Equal(t, market.Pair, pair)

market, err = app.PerpKeeperV2.Admin().GetMarketByPairAndVersion(ctx, pair, 2)
require.ErrorContains(t, err, fmt.Sprintf("market with pair %s and version 2 not found", pair.String()))
}
2 changes: 1 addition & 1 deletion x/perp/v2/keeper/calc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func PositionNotionalSpot(amm types.AMM, position types.Position) (positionNotio
if err != nil {
return sdk.Dec{}, err
}
return amm.FromQuoteReserveToAsset(quoteReserve), nil
return amm.QuoteReserveToAsset(quoteReserve), nil
}

// PositionNotionalTWAP returns the position's notional value based on the TWAP price.
Expand Down
14 changes: 10 additions & 4 deletions x/perp/v2/keeper/clearing_house.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ func (k Keeper) increasePosition(

updatedAMM, baseAssetDeltaAbs, err := k.SwapQuoteAsset(
ctx,
market,
amm,
dir,
increasedNotional,
Expand Down Expand Up @@ -314,7 +313,6 @@ func (k Keeper) decreasePosition(

updatedAMM, baseAssetDeltaAbs, err := k.SwapQuoteAsset(
ctx,
market,
amm,
dir,
decreasedNotional,
Expand Down Expand Up @@ -446,7 +444,7 @@ func (k Keeper) closeAndOpenReversePosition(
}

// check if it's worth continuing with the increase position
quoteReserveAmt := updatedAMM.FromQuoteAssetToReserve(remainingReverseNotionalValue)
quoteReserveAmt := updatedAMM.QuoteAssetToReserve(remainingReverseNotionalValue)
possibleNextSize, err := updatedAMM.GetBaseReserveAmt(quoteReserveAmt, dir)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -703,6 +701,10 @@ func (k Keeper) ClosePosition(ctx sdk.Context, pair asset.Pair, traderAddr sdk.A
return nil, fmt.Errorf("%w: %s", types.ErrPairNotFound, pair)
}

if !market.Enabled {
return nil, fmt.Errorf("%w: this position can be only closed by Settlement", types.ErrMarketNotEnabled)
}

amm, err := k.GetAMM(ctx, pair)
if err != nil {
return nil, fmt.Errorf("%w: %s", types.ErrPairNotFound, pair)
Expand Down Expand Up @@ -844,6 +846,10 @@ func (k Keeper) PartialClose(
return nil, types.ErrPairNotFound.Wrapf("pair: %s", pair)
}

if !market.Enabled {
matthiasmatt marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("%w: this position can be only closed by Settlement", types.ErrMarketNotEnabled)
}

amm, err := k.GetAMM(ctx, pair)
if err != nil {
return nil, types.ErrPairNotFound.Wrapf("pair: %s", pair)
Expand Down Expand Up @@ -873,7 +879,7 @@ func (k Keeper) PartialClose(
if err != nil {
return nil, err
}
reverseNotionalAmt = amm.FromQuoteReserveToAsset(reverseNotionalAmt)
reverseNotionalAmt = amm.QuoteReserveToAsset(reverseNotionalAmt)

updatedAMM, positionResp, err := k.decreasePosition(ctx, market, amm, position, reverseNotionalAmt, sdk.ZeroDec())
if err != nil {
Expand Down
36 changes: 1 addition & 35 deletions x/perp/v2/keeper/clearing_house_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,51 +873,17 @@ func TestMarketOrder(t *testing.T) {
PositionShouldNotExist(alice, pairBtcNusd),
),

TC("new long position, can close position after market is not enabled").
Given(
CreateCustomMarket(pairBtcNusd),
SetBlockTime(startBlockTime),
SetBlockNumber(1),
FundAccount(alice, sdk.NewCoins(sdk.NewCoin(denoms.NUSD, sdk.NewInt(47_714_285_715)))),
MarketOrder(alice, pairBtcNusd, types.Direction_SHORT, sdk.NewInt(47_619_047_619), sdk.OneDec(), sdk.ZeroDec()),
SetMarketEnabled(pairBtcNusd, false),
).
When(
ClosePosition(alice, pairBtcNusd),
).
Then(
PositionShouldNotExist(alice, pairBtcNusd),
),

TC("new long position, can not open new position after market is not enabled").
Given(
CreateCustomMarket(pairBtcNusd),
SetBlockTime(startBlockTime),
SetBlockNumber(1),
FundAccount(alice, sdk.NewCoins(sdk.NewCoin(denoms.NUSD, sdk.NewInt(47_714_285_715)))),
SetMarketEnabled(pairBtcNusd, false),
).
When(
MarketOrderFails(alice, pairBtcNusd, types.Direction_SHORT, sdk.NewInt(47_619_047_619), sdk.OneDec(), sdk.ZeroDec(),
types.ErrMarketNotEnabled),
).
Then(
PositionShouldNotExist(alice, pairBtcNusd),
),

TC("existing long position, can not open new one but can close").
Given(
CreateCustomMarket(pairBtcNusd),
SetBlockTime(startBlockTime),
SetBlockNumber(1),
FundAccount(alice, sdk.NewCoins(sdk.NewCoin(denoms.NUSD, sdk.NewInt(47_714_285_715)))),
MarketOrder(alice, pairBtcNusd, types.Direction_SHORT, sdk.NewInt(50_000), sdk.OneDec(), sdk.ZeroDec()),
SetMarketEnabled(pairBtcNusd, false),
CloseMarket(pairBtcNusd),
).
When(
MarketOrderFails(alice, pairBtcNusd, types.Direction_SHORT, sdk.NewInt(47_619_047_619), sdk.OneDec(), sdk.ZeroDec(),
types.ErrMarketNotEnabled),
ClosePosition(alice, pairBtcNusd),
).
Then(
PositionShouldNotExist(alice, pairBtcNusd),
Expand Down
Loading