Skip to content

Commit

Permalink
[Perpetual]: Fix estimatedPnL (#882)
Browse files Browse the repository at this point in the history
* fix

* clean up

* fix

* show liability

* remove unecessary field

* fix

* fix

* fix

* return coin

* fix test

* fix

* fix all tests
  • Loading branch information
amityadav0 authored Oct 25, 2024
1 parent 28bb08f commit 0f23bc1
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 567 deletions.
665 changes: 305 additions & 360 deletions docs/static/openapi.yml

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions proto/elys/perpetual/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,7 @@ message MtpAndPrice {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string pnl = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
cosmos.base.v1beta1.Coin pnl = 3 [(gogoproto.nullable) = false];
string effective_leverage = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
Expand Down
7 changes: 4 additions & 3 deletions x/perpetual/client/cli/query_mtp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"bytes"
"context"
"fmt"
"io"
"testing"

abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/client"
Expand All @@ -19,8 +22,6 @@ import (
"github.com/elys-network/elys/x/perpetual/client/cli"
"github.com/elys-network/elys/x/perpetual/types"
"github.com/stretchr/testify/require"
"io"
"testing"
)

func networkWithMTPObjects(t *testing.T, n int) (*network.Network, []*types.MtpAndPrice) {
Expand Down Expand Up @@ -57,7 +58,7 @@ func networkWithMTPObjects(t *testing.T, n int) (*network.Network, []*types.MtpA
StopLossPrice: sdk.NewDec(0),
},
TradingAssetPrice: sdk.ZeroDec(),
Pnl: sdk.ZeroInt(),
Pnl: sdk.NewCoin("USDC", sdk.NewInt(0)),
LiquidationPrice: sdk.ZeroDec(),
Fees: &types.Fees{
TotalFeesBaseCurrency: sdk.NewInt(0),
Expand Down
71 changes: 27 additions & 44 deletions x/perpetual/keeper/mtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
ammtypes "github.com/elys-network/elys/x/amm/types"
ptypes "github.com/elys-network/elys/x/parameter/types"
"github.com/elys-network/elys/x/perpetual/types"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -118,9 +117,8 @@ func (k Keeper) GetMTPData(ctx sdk.Context, pagination *query.PageRequest, addre
}

entry, found := k.assetProfileKeeper.GetEntry(ctx, ptypes.BaseCurrency)
realTime := true
if !found {
realTime = false
return nil, nil, status.Error(codes.NotFound, "base currency not found")
}
baseCurrency := entry.Denom

Expand All @@ -132,7 +130,7 @@ func (k Keeper) GetMTPData(ctx sdk.Context, pagination *query.PageRequest, addre
return nil
}

mtpAndPrice, err := k.fillMTPData(ctx, mtp, ammPoolId, realTime, baseCurrency)
mtpAndPrice, err := k.fillMTPData(ctx, mtp, baseCurrency)
if err != nil {
return err
}
Expand All @@ -148,33 +146,30 @@ func (k Keeper) GetMTPData(ctx sdk.Context, pagination *query.PageRequest, addre
return mtps, pageRes, nil
}

func (k Keeper) fillMTPData(ctx sdk.Context, mtp types.MTP, ammPoolId *uint64, realTime bool, baseCurrency string) (*types.MtpAndPrice, error) {
var ammPool ammtypes.Pool
var poolFound bool
if ammPoolId != nil {
ammPool, poolFound = k.amm.GetPool(ctx, *ammPoolId)
} else {
ammPool, poolFound = k.amm.GetPool(ctx, mtp.AmmPoolId)
func (k Keeper) fillMTPData(ctx sdk.Context, mtp types.MTP, baseCurrency string) (*types.MtpAndPrice, error) {
ammPool, found := k.amm.GetPool(ctx, mtp.AmmPoolId)
if !found {
return &types.MtpAndPrice{}, fmt.Errorf("amm pool %d not found", mtp.AmmPoolId)
}
if !poolFound {
realTime = false

pool, found := k.GetPool(ctx, mtp.AmmPoolId)
if !found {
return &types.MtpAndPrice{}, fmt.Errorf("perpetual pool %d not found", mtp.AmmPoolId)
}

pnl := math.ZeroInt()
liquidationPrice := sdk.ZeroDec()
if realTime {
mtpHealth, err := k.GetMTPHealth(ctx, mtp, ammPool, baseCurrency)
if err == nil {
mtp.MtpHealth = mtpHealth
}
// Update interest first and then calculate health
k.UpdateMTPBorrowInterestUnpaidLiability(ctx, &mtp)
k.UpdateFundingFee(ctx, &mtp, &pool, ammPool)

k.UpdateMTPBorrowInterestUnpaidLiability(ctx, &mtp)
pnl, err = k.GetEstimatedPnL(ctx, mtp, baseCurrency, false)
if err != nil {
return nil, err
}
liquidationPrice = k.GetLiquidationPrice(ctx, mtp)
mtpHealth, err := k.GetMTPHealth(ctx, mtp, ammPool, baseCurrency)
if err == nil {
mtp.MtpHealth = mtpHealth
}
pnl, err := k.GetEstimatedPnL(ctx, mtp, baseCurrency, false)
if err != nil {
return nil, err
}
liquidationPrice := k.GetLiquidationPrice(ctx, mtp)

tradingAssetPrice, err := k.GetAssetPrice(ctx, mtp.TradingAsset)
if err != nil {
Expand All @@ -198,10 +193,13 @@ func (k Keeper) fillMTPData(ctx sdk.Context, mtp types.MTP, ammPoolId *uint64, r
return nil, err
}

// Show updated liability
mtp.Liabilities = mtp.Liabilities.Add(mtp.BorrowInterestUnpaidLiability)

return &types.MtpAndPrice{
Mtp: &mtp,
TradingAssetPrice: tradingAssetPrice,
Pnl: pnl,
Pnl: sdk.NewCoin(baseCurrency, pnl),
LiquidationPrice: liquidationPrice,
EffectiveLeverage: effectiveLeverage,
Fees: &types.Fees{
Expand Down Expand Up @@ -313,21 +311,6 @@ func (k Keeper) DeleteToPay(ctx sdk.Context, address sdk.AccAddress, id uint64)

func (k Keeper) GetEstimatedPnL(ctx sdk.Context, mtp types.MTP, baseCurrency string, useTakeProfitPrice bool) (math.Int, error) {
// P&L = Custody (in USD) - Total Liability ( in USD) - Collateral ( in USD)

// Funding rate payment consideration
// get funding rate
fundingRate, _ := k.GetFundingRate(ctx, mtp.LastFundingCalcBlock, mtp.LastFundingCalcTime, mtp.AmmPoolId)
var fundingAmount sdk.Int
// if funding rate is zero, return
if fundingRate.IsZero() {
fundingAmount = sdk.ZeroInt()
} else if (fundingRate.IsNegative() && mtp.Position == types.Position_LONG) || (fundingRate.IsPositive() && mtp.Position == types.Position_SHORT) {
fundingAmount = sdk.ZeroInt()
} else {
// Calculate the take amount in custody asset
fundingAmount = types.CalcTakeAmount(mtp.Custody, fundingRate)
}

// Liability should include margin interest and funding fee accrued.
collateralAmt := mtp.Collateral

Expand All @@ -343,12 +326,12 @@ func (k Keeper) GetEstimatedPnL(ctx sdk.Context, mtp types.MTP, baseCurrency str
}

// in long it's in trading asset ,if short position, custody asset is already in base currency
custodyAmtAfterFunding := mtp.Custody.Sub(fundingAmount)
custodyAmtAfterFunding := mtp.Custody

totalLiabilities := mtp.Liabilities.Add(mtp.BorrowInterestUnpaidLiability)

// Calculate estimated PnL
estimatedPnL := sdk.ZeroInt()
var estimatedPnL sdk.Int

if mtp.Position == types.Position_SHORT {
// Estimated PnL for short position:
Expand Down
1 change: 0 additions & 1 deletion x/perpetual/keeper/mtp_borrow_interest.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ func (k Keeper) UpdateMTPBorrowInterestUnpaidLiability(ctx sdk.Context, mtp *typ
mtp.BorrowInterestUnpaidLiability = mtp.BorrowInterestUnpaidLiability.Add(borrowInterestPaymentInt)
mtp.LastInterestCalcBlock = uint64(ctx.BlockHeight())
mtp.LastInterestCalcTime = uint64(ctx.BlockTime().Unix())
return
}

// SettleMTPBorrowInterestUnpaidLiability This does not update BorrowInterestUnpaidLiability, it should be done through UpdateMTPBorrowInterestUnpaidLiability beforehand
Expand Down
10 changes: 8 additions & 2 deletions x/perpetual/keeper/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ func (k Keeper) GetBorrowInterestRate(ctx sdk.Context, startBlock, startTime uin
Quo(sdk.NewDec(numberOfBlocks)).
Quo(blocksPerYear)

return sdk.MaxDec(finalInterestRate.Mul(takeProfitBorrowFactor), k.GetParams(ctx).BorrowInterestRateMin)
return sdk.MaxDec(
finalInterestRate.Mul(takeProfitBorrowFactor),
k.GetParams(ctx).BorrowInterestRateMin.Mul(sdk.NewDec(ctx.BlockTime().Unix()-int64(startTime))).Quo(blocksPerYear),
)
}

if !store.Has(startBlockKey) && store.Has(currentBlockKey) {
Expand All @@ -157,7 +160,10 @@ func (k Keeper) GetBorrowInterestRate(ctx sdk.Context, startBlock, startTime uin
Quo(sdk.NewDec(numberOfBlocks)).
Quo(blocksPerYear)

return sdk.MaxDec(finalInterestRate.Mul(takeProfitBorrowFactor), k.GetParams(ctx).BorrowInterestRateMin)
return sdk.MaxDec(
finalInterestRate.Mul(takeProfitBorrowFactor),
k.GetParams(ctx).BorrowInterestRateMin.Mul(sdk.NewDec(ctx.BlockTime().Unix()-int64(startTime))).Quo(blocksPerYear),
)
}
}
pool, found := k.GetPool(ctx, poolId)
Expand Down
5 changes: 2 additions & 3 deletions x/perpetual/keeper/query_mtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ func (k Keeper) MTP(goCtx context.Context, req *types.MTPRequest) (*types.MTPRes
}

entry, found := k.assetProfileKeeper.GetEntry(ctx, ptypes.BaseCurrency)
realTime := true
if !found {
realTime = false
return &types.MTPResponse{}, status.Error(codes.NotFound, "base currency not found")
}
baseCurrency := entry.Denom

mtpAndPrice, err := k.fillMTPData(ctx, mtp, nil, realTime, baseCurrency)
mtpAndPrice, err := k.fillMTPData(ctx, mtp, baseCurrency)
if err != nil {
return &types.MTPResponse{}, err
}
Expand Down
3 changes: 2 additions & 1 deletion x/perpetual/keeper/query_open_estimation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -182,7 +183,7 @@ func (k Keeper) HandleOpenEstimation(ctx sdk.Context, req *types.QueryOpenEstima
OpenPrice: mtp.OpenPrice,
TakeProfitPrice: req.TakeProfitPrice,
LiquidationPrice: liquidationPrice,
EstimatedPnl: sdk.Coin{mtp.CustodyAsset, estimatedPnLAmount},
EstimatedPnl: sdk.NewCoin(baseCurrency, estimatedPnLAmount),
AvailableLiquidity: availableLiquidity,
Slippage: slippage,
PriceImpact: priceImpact,
Expand Down
9 changes: 5 additions & 4 deletions x/perpetual/keeper/query_open_estimation_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package keeper_test

import (
"testing"

"cosmossdk.io/math"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
leveragelpmodulekeeper "github.com/elys-network/elys/x/leveragelp/keeper"
leveragelpmoduletypes "github.com/elys-network/elys/x/leveragelp/types"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
ammtypes "github.com/elys-network/elys/x/amm/types"
Expand Down Expand Up @@ -125,7 +126,7 @@ func TestOpenEstimation_Long5XAtom100Usdc(t *testing.T) {
OpenPrice: sdk.MustNewDecFromStr("5.017911485225627730"),
TakeProfitPrice: tradingAssetPrice.MulInt64(3),
LiquidationPrice: sdk.MustNewDecFromStr("3.823170655410002080"),
EstimatedPnl: sdk.NewCoin(ptypes.ATOM, sdk.NewInt(994_645_735)),
EstimatedPnl: sdk.NewCoin(ptypes.BaseCurrency, sdk.NewInt(994_645_735)),
AvailableLiquidity: sdk.NewCoin(ptypes.ATOM, sdk.NewInt(600000000000)),
Slippage: sdk.MustNewDecFromStr("0.002572074655759760"),
BorrowInterestRate: sdk.MustNewDecFromStr("0.000000000000000000"),
Expand Down Expand Up @@ -247,7 +248,7 @@ func TestOpenEstimation_Long5XAtom10Atom(t *testing.T) {
OpenPrice: sdk.MustNewDecFromStr("5.014308340000000000"),
TakeProfitPrice: tradingAssetPrice.MulInt64(3),
LiquidationPrice: sdk.MustNewDecFromStr("3.824815447619047619"),
EstimatedPnl: sdk.NewCoin(ptypes.ATOM, sdk.NewInt(399_197_189)),
EstimatedPnl: sdk.NewCoin(ptypes.BaseCurrency, sdk.NewInt(399_197_189)),
InterestAmount: sdk.NewCoin(ptypes.BaseCurrency, math.NewInt(0)), // Need to increase block height to have non zero value
AvailableLiquidity: sdk.NewCoin(ptypes.ATOM, sdk.NewInt(10000000000)),
Slippage: sdk.MustNewDecFromStr("0.003001007041215876"),
Expand Down Expand Up @@ -375,7 +376,7 @@ func TestOpenEstimation_Long10XAtom1000Usdc(t *testing.T) {
TakeProfitPrice: tradingAssetPrice.MulInt64(3),
LiquidationPrice: sdk.MustNewDecFromStr("3.958928731787454040"),
InterestAmount: sdk.NewCoin(ptypes.BaseCurrency, math.NewInt(0)), // Need to increase block height to have non zero value
EstimatedPnl: sdk.NewCoin(ptypes.ATOM, sdk.NewInt(18_514_207_234)),
EstimatedPnl: sdk.NewCoin(ptypes.BaseCurrency, sdk.NewInt(18_514_207_234)),
AvailableLiquidity: sdk.NewCoin(ptypes.ATOM, sdk.NewInt(600_000_000000)),
Slippage: sdk.MustNewDecFromStr("0.048575000399137173"),
BorrowInterestRate: sdk.MustNewDecFromStr("0.000000000000000000"),
Expand Down
Loading

0 comments on commit 0f23bc1

Please sign in to comment.