Skip to content

Commit

Permalink
adding more details for perpetual query (#890)
Browse files Browse the repository at this point in the history
* adding more details for perpetual query

* code review changes
  • Loading branch information
avkr003 authored Oct 28, 2024
1 parent a793d42 commit 886da31
Show file tree
Hide file tree
Showing 20 changed files with 668 additions and 325 deletions.
12 changes: 12 additions & 0 deletions proto/elys/perpetual/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,15 @@ message PoolResponse {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
cosmos.base.v1beta1.Coin total_liabilities = 11 [ (gogoproto.nullable) = false ];
string total_long_open_interest = 12 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string total_short_open_interest = 13 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}

message QueryCloseEstimationRequest {
Expand All @@ -313,4 +322,7 @@ message QueryCloseEstimationResponse {
cosmos.base.v1beta1.Coin returning_amount = 8 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin paying_liabilities = 9 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin return_amount_at_closing_price = 10 [(gogoproto.nullable) = false];
string closing_price = 11 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin custody = 12 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin collateral = 13 [(gogoproto.nullable) = false];
}
44 changes: 10 additions & 34 deletions x/perpetual/keeper/begin_blocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,12 @@ func (k Keeper) BeginBlocker(ctx sdk.Context) {
pool.FundingRate = fundingRateShort.Neg()
}

// account custody from long position
totalCustodyLong := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsLong {
totalCustodyLong = totalCustodyLong.Add(asset.Custody)
}

// account custody from short position
totalCustodyShort := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsShort {
totalCustodyShort = totalCustodyShort.Add(asset.Custody)
}
totalLongOpenInterest := pool.GetTotalLongOpenInterest()
totalShortOpenInterest := pool.GetTotalShortOpenInterest()

blocksPerYear := k.parameterKeeper.GetParams(ctx).TotalBlocksPerYear
fundingAmountLong := types.CalcTakeAmount(totalCustodyLong, fundingRateLong).ToLegacyDec().Quo(sdk.NewDec(blocksPerYear))
fundingAmountShort := types.CalcTakeAmount(totalCustodyShort, fundingRateShort).ToLegacyDec().Quo(sdk.NewDec(blocksPerYear))
fundingAmountLong := types.CalcTakeAmount(totalLongOpenInterest, fundingRateLong).ToLegacyDec().Quo(sdk.NewDec(blocksPerYear))
fundingAmountShort := types.CalcTakeAmount(totalShortOpenInterest, fundingRateShort).ToLegacyDec().Quo(sdk.NewDec(blocksPerYear))

k.SetFundingRate(ctx, uint64(ctx.BlockHeight()), pool.AmmPoolId, types.FundingRateBlock{
BlockHeight: ctx.BlockHeight(),
Expand All @@ -68,38 +59,23 @@ func (k Keeper) ComputeFundingRate(ctx sdk.Context, pool types.Pool) (sdk.Dec, s
// Custody amount for long is trading asset -
// Liability amount for short is trading asset
// popular_rate = fixed_rate * abs(Custody-Liability) / (Custody+Liability)
totalCustodyLong := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsLong {
// We subtract asset.Collateral from totalCustodyLong because for long with collateral same as trading asset and user will
// be charged for that the collateral as well even though they have already given that amount to the pool.
// For LONG, asset.Custody will be 0 only for base currency but asset.Collateral won't be zero for base currency and trading asset
// We subtract asset.Collateral only when asset is trading asset and in that case asset.Custody won't be zero
// For base currency, asset.Collateral might not be 0 but asset.Custody will be 0 in LONG
// !asset.Custody.IsZero() ensures that asset is trading asset for LONG
if !asset.Custody.IsZero() {
totalCustodyLong = totalCustodyLong.Add(asset.Custody).Sub(asset.Collateral)
}
}

totalLiabilitiesShort := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsShort {
totalLiabilitiesShort = totalLiabilitiesShort.Add(asset.Liabilities)
}
totalLongOpenInterest := pool.GetTotalLongOpenInterest()
totalShortOpenInterest := pool.GetTotalShortOpenInterest()

if totalCustodyLong.IsZero() || totalLiabilitiesShort.IsZero() {
if totalLongOpenInterest.IsZero() || totalShortOpenInterest.IsZero() {
return sdk.ZeroDec(), sdk.ZeroDec()
}

fixedRate := k.GetParams(ctx).FixedFundingRate
if totalCustodyLong.GT(totalLiabilitiesShort) {
if totalLongOpenInterest.GT(totalShortOpenInterest) {
// long is popular
// long pays short
netLongRatio := (totalCustodyLong.Sub(totalLiabilitiesShort)).ToLegacyDec().Quo((totalCustodyLong.Add(totalLiabilitiesShort)).ToLegacyDec())
netLongRatio := (totalLongOpenInterest.Sub(totalShortOpenInterest)).ToLegacyDec().Quo((totalLongOpenInterest.Add(totalShortOpenInterest)).ToLegacyDec())
return netLongRatio.Mul(fixedRate), sdk.ZeroDec()
} else {
// short is popular
// short pays long
netShortRatio := (totalLiabilitiesShort.Sub(totalCustodyLong)).ToLegacyDec().Quo((totalCustodyLong.Add(totalLiabilitiesShort)).ToLegacyDec())
netShortRatio := (totalShortOpenInterest.Sub(totalLongOpenInterest)).ToLegacyDec().Quo((totalLongOpenInterest.Add(totalShortOpenInterest)).ToLegacyDec())
return sdk.ZeroDec(), netShortRatio.Mul(fixedRate)
}
}
9 changes: 0 additions & 9 deletions x/perpetual/keeper/close_position.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,6 @@ func (k Keeper) ClosePosition(ctx sdk.Context, msg *types.MsgClose, baseCurrency
closingRatio = math.LegacyOneDec()
}

// closingAmount is what user is trying to close
closingAmount := mtp.Custody.ToLegacyDec().Mul(closingRatio).TruncateInt()

// Take out custody
err = k.TakeOutCustody(ctx, mtp, &pool, closingAmount)
if err != nil {
return nil, math.ZeroInt(), err
}

// Estimate swap and repay
repayAmt, err := k.EstimateAndRepay(ctx, &mtp, &pool, &ammPool, baseCurrency, closingRatio)
if err != nil {
Expand Down
10 changes: 1 addition & 9 deletions x/perpetual/keeper/estimate_and_repay.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,8 @@ func (k Keeper) EstimateAndRepay(ctx sdk.Context, mtp *types.MTP, pool *types.Po
return sdk.ZeroInt(), err
}

// update mtp health
mtp.MtpHealth, err = k.GetMTPHealth(ctx, *mtp, *ammPool, baseCurrency)
if err != nil {
return math.ZeroInt(), err
}

mtp.Liabilities = mtp.Liabilities.Sub(payingLiabilities)

// Note: Long settlement is done in trading asset. And short settlement in usdc in Repay function
if err = k.Repay(ctx, mtp, pool, ammPool, returnAmount, payingLiabilities, closingRatio); err != nil {
if err = k.Repay(ctx, mtp, pool, ammPool, returnAmount, payingLiabilities, closingRatio, baseCurrency); err != nil {
return sdk.ZeroInt(), err
}

Expand Down
5 changes: 0 additions & 5 deletions x/perpetual/keeper/force_close_long.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ func (k Keeper) ForceCloseLong(ctx sdk.Context, mtp *types.MTP, pool *types.Pool
return math.ZeroInt(), err
}

err = k.TakeOutCustody(ctx, *mtp, pool, mtp.Custody)
if err != nil {
return math.ZeroInt(), err
}

// Estimate swap and repay
repayAmt, err := k.EstimateAndRepay(ctx, mtp, pool, &ammPool, baseCurrency, math.LegacyOneDec())
if err != nil {
Expand Down
5 changes: 0 additions & 5 deletions x/perpetual/keeper/force_close_short.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ func (k Keeper) ForceCloseShort(ctx sdk.Context, mtp *types.MTP, pool *types.Poo
return math.ZeroInt(), err
}

err = k.TakeOutCustody(ctx, *mtp, pool, mtp.Custody)
if err != nil {
return math.ZeroInt(), err
}

// Estimate swap and repay
repayAmt, err := k.EstimateAndRepay(ctx, mtp, pool, &ammPool, baseCurrency, sdk.OneDec())
if err != nil {
Expand Down
43 changes: 6 additions & 37 deletions x/perpetual/keeper/get_net_open_interest.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,30 @@
package keeper

import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/perpetual/types"
)

// GetNetOpenInterest calculates the net open interest for a given pool.
// Note: Net open interest should always be in terms of trading asset
func (k Keeper) GetNetOpenInterest(ctx sdk.Context, pool types.Pool) math.Int {
// account custody from long position
totalCustodyLong := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsLong {
totalCustodyLong = totalCustodyLong.Add(asset.Custody)
}

// account liabilities from short position
totalLiabilityShort := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsShort {
totalLiabilityShort = totalLiabilityShort.Add(asset.Liabilities)
}

// Net Open Interest = Long custody - Short Liabilities
netOpenInterest := totalCustodyLong.Sub(totalLiabilityShort)

return netOpenInterest
}

func (k Keeper) GetFundingPaymentRates(ctx sdk.Context, pool types.Pool) (long sdk.Dec, short sdk.Dec) {
fundingRateLong, fundingRateShort := k.ComputeFundingRate(ctx, pool)

// account custody from long position
totalCustodyLong := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsLong {
totalCustodyLong = totalCustodyLong.Add(asset.Custody)
}

// account custody from short position
totalLiabilitiesShort := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsShort {
totalLiabilitiesShort = totalLiabilitiesShort.Add(asset.Liabilities)
}
totalLongOpenInterest := pool.GetTotalLongOpenInterest()
totalShortOpenInterest := pool.GetTotalShortOpenInterest()

if fundingRateLong.IsZero() {
// short will pay
// long will receive
unpopular_rate := sdk.ZeroDec()
if !totalCustodyLong.IsZero() {
unpopular_rate = fundingRateShort.Mul(totalLiabilitiesShort.ToLegacyDec()).Quo(totalCustodyLong.ToLegacyDec())
if !totalLongOpenInterest.IsZero() {
unpopular_rate = fundingRateShort.Mul(totalShortOpenInterest.ToLegacyDec()).Quo(totalLongOpenInterest.ToLegacyDec())
}
return unpopular_rate.Neg(), fundingRateShort
} else {
// long will pay
// short will receive
unpopular_rate := sdk.ZeroDec()
if !totalLiabilitiesShort.IsZero() {
unpopular_rate = fundingRateLong.Mul(totalCustodyLong.ToLegacyDec()).Quo(totalLiabilitiesShort.ToLegacyDec())
if !totalShortOpenInterest.IsZero() {
unpopular_rate = fundingRateLong.Mul(totalLongOpenInterest.ToLegacyDec()).Quo(totalShortOpenInterest.ToLegacyDec())
}
return fundingRateLong, unpopular_rate.Neg()
}
Expand Down
20 changes: 7 additions & 13 deletions x/perpetual/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ func (k Keeper) Borrow(ctx sdk.Context, collateralAmount math.Int, custodyAmount
return err
}

err = pool.UpdateCollateral(mtp.CollateralAsset, mtp.Collateral, true, mtp.Position)
err = pool.UpdateCustody(mtp.CustodyAsset, mtp.Custody, true, mtp.Position)
if err != nil {
return err
return nil
}

// All liability has to be in liabilities asset
Expand All @@ -150,6 +150,11 @@ func (k Keeper) Borrow(ctx sdk.Context, collateralAmount math.Int, custodyAmount
return err
}

err = pool.UpdateCollateral(mtp.CollateralAsset, mtp.Collateral, true, mtp.Position)
if err != nil {
return err
}

// All take profit liability has to be in liabilities asset
err = pool.UpdateTakeProfitLiabilities(mtp.LiabilitiesAsset, mtp.TakeProfitLiabilities, true, mtp.Position)
if err != nil {
Expand Down Expand Up @@ -203,17 +208,6 @@ func (k Keeper) SendFromAmmPool(ctx sdk.Context, ammPool *ammtypes.Pool, receive
return nil
}

func (k Keeper) TakeInCustody(ctx sdk.Context, mtp types.MTP, pool *types.Pool) error {
err := pool.UpdateCustody(mtp.CustodyAsset, mtp.Custody, true, mtp.Position)
if err != nil {
return nil
}

k.SetPool(ctx, *pool)

return nil
}

func (k Keeper) BorrowInterestRateComputationByPosition(pool types.Pool, ammPool ammtypes.Pool, position types.Position) (sdk.Dec, error) {
poolAssets := pool.GetPoolAssets(position)
targetBorrowInterestRate := sdk.OneDec()
Expand Down
10 changes: 6 additions & 4 deletions x/perpetual/keeper/open_consolidate.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ func (k Keeper) OpenConsolidate(ctx sdk.Context, existingMtp *types.MTP, newMtp
return nil, err
}

consolidatedOpenPrice := (existingMtp.Custody.ToLegacyDec().Mul(existingMtp.OpenPrice).Add(newMtp.Custody.ToLegacyDec().Mul(newMtp.OpenPrice))).Quo(existingMtp.Custody.ToLegacyDec().Add(newMtp.Custody.ToLegacyDec()))
existingMtp.OpenPrice = consolidatedOpenPrice
if !newMtp.Liabilities.IsZero() {
consolidatedOpenPrice := (existingMtp.Custody.ToLegacyDec().Mul(existingMtp.OpenPrice).Add(newMtp.Custody.ToLegacyDec().Mul(newMtp.OpenPrice))).Quo(existingMtp.Custody.ToLegacyDec().Add(newMtp.Custody.ToLegacyDec()))
existingMtp.OpenPrice = consolidatedOpenPrice

consolidatedTakeProfitPrice := existingMtp.Custody.ToLegacyDec().Mul(existingMtp.TakeProfitPrice).Add(newMtp.Custody.ToLegacyDec().Mul(newMtp.TakeProfitPrice)).Quo(existingMtp.Custody.ToLegacyDec().Add(newMtp.Custody.ToLegacyDec()))
existingMtp.TakeProfitPrice = consolidatedTakeProfitPrice
consolidatedTakeProfitPrice := existingMtp.Custody.ToLegacyDec().Mul(existingMtp.TakeProfitPrice).Add(newMtp.Custody.ToLegacyDec().Mul(newMtp.TakeProfitPrice)).Quo(existingMtp.Custody.ToLegacyDec().Add(newMtp.Custody.ToLegacyDec()))
existingMtp.TakeProfitPrice = consolidatedTakeProfitPrice
}

existingMtp.TakeProfitCustody = existingMtp.TakeProfitCustody.Add(newMtp.TakeProfitCustody)
existingMtp.TakeProfitLiabilities = existingMtp.TakeProfitLiabilities.Add(newMtp.TakeProfitLiabilities)
Expand Down
38 changes: 38 additions & 0 deletions x/perpetual/keeper/pool_health.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package keeper

import (
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
ammtypes "github.com/elys-network/elys/x/amm/types"
assetprofiletypes "github.com/elys-network/elys/x/assetprofile/types"
ptypes "github.com/elys-network/elys/x/parameter/types"
"github.com/elys-network/elys/x/perpetual/types"
)

Expand Down Expand Up @@ -85,3 +88,38 @@ func (k Keeper) CheckMinimumCustodyAmt(ctx sdk.Context, poolId uint64) error {
}
return nil
}

func (k Keeper) GetPoolTotalBaseCurrencyLiabilities(ctx sdk.Context, pool types.Pool) (sdk.Coin, error) {
// retrieve base currency denom
entry, found := k.assetProfileKeeper.GetEntry(ctx, ptypes.BaseCurrency)
if !found {
return sdk.Coin{}, errorsmod.Wrapf(assetprofiletypes.ErrAssetProfileNotFound, "asset %s not found", ptypes.BaseCurrency)
}
baseCurrency := entry.Denom

totalLiabilities := math.LegacyZeroDec()
for _, poolAsset := range pool.PoolAssetsLong {
// for long, liabilities will always be in base currency
totalLiabilities = totalLiabilities.Add(poolAsset.Liabilities.ToLegacyDec())
}

tradingAsset := ""
for _, poolAsset := range pool.PoolAssetsLong {
if poolAsset.AssetDenom != baseCurrency {
tradingAsset = poolAsset.AssetDenom
break
}
}

tradingAssetPrice, err := k.GetAssetPrice(ctx, tradingAsset)
if err != nil {
return sdk.Coin{}, err
}

for _, poolAsset := range pool.PoolAssetsShort {
// For short liabilities will be in trading asset
baseCurrencyAmt := poolAsset.Liabilities.ToLegacyDec().Mul(tradingAssetPrice)
totalLiabilities = totalLiabilities.Add(baseCurrencyAmt)
}
return sdk.NewCoin(baseCurrency, totalLiabilities.TruncateInt()), nil
}
5 changes: 0 additions & 5 deletions x/perpetual/keeper/process_open.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,6 @@ func (k Keeper) ProcessOpen(ctx sdk.Context, mtp *types.MTP, leverage sdk.Dec, e
return nil, err
}

// Take custody from the pool balance.
if err = k.TakeInCustody(ctx, *mtp, &pool); err != nil {
return nil, err
}

// Update the MTP health.
mtp.MtpHealth, err = k.GetMTPHealth(ctx, *mtp, ammPool, baseCurrency)
if err != nil {
Expand Down
9 changes: 8 additions & 1 deletion x/perpetual/keeper/query_close_estimation.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ func (k Keeper) HandleCloseEstimation(ctx sdk.Context, req *types.QueryCloseEsti
}

// need to make sure mtp.Custody has been used to unpaid liability
returnAmount, err := k.CalcReturnAmount(mtp, repayAmount, sdk.OneDec())
returnAmount, err := k.CalcReturnAmount(mtp, repayAmount, closingRatio)
if err != nil {
return &types.QueryCloseEstimationResponse{}, err
}

mtp.Liabilities = mtp.Liabilities.Sub(payingLiabilities)
mtp.Custody = mtp.Custody.ToLegacyDec().Mul(math.LegacyOneDec().Sub(closingRatio)).TruncateInt()
mtp.Collateral = mtp.Collateral.ToLegacyDec().Mul(math.LegacyOneDec().Sub(closingRatio)).TruncateInt()

liquidationPrice := k.GetLiquidationPrice(ctx, mtp)
executionPrice := math.LegacyZeroDec()
// calculate liquidation price
Expand Down Expand Up @@ -132,9 +136,12 @@ func (k Keeper) HandleCloseEstimation(ctx sdk.Context, req *types.QueryCloseEsti
Position: mtp.Position,
PositionSize: sdk.NewCoin(positionAsset, positionSize),
Liabilities: sdk.NewCoin(mtp.LiabilitiesAsset, mtp.Liabilities),
Custody: sdk.NewCoin(mtp.CustodyAsset, mtp.Custody),
Collateral: sdk.NewCoin(mtp.CollateralAsset, mtp.Collateral),
PriceImpact: priceImpact,
LiquidationPrice: liquidationPrice,
MaxCloseAmount: maxCloseAmount,
ClosingPrice: executionPrice,
BorrowInterestUnpaidLiability: sdk.NewCoin(mtp.LiabilitiesAsset, unpaidInterestLiability),
ReturningAmount: sdk.NewCoin(mtp.CustodyAsset, returnAmount),
PayingLiabilities: sdk.NewCoin(mtp.LiabilitiesAsset, payingLiabilities),
Expand Down
Loading

0 comments on commit 886da31

Please sign in to comment.