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

Fix/issue 412 #445

Merged
merged 2 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion x/derivatives/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,18 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) {
func CheckPosition(ctx sdk.Context, k keeper.Keeper) {
positions := k.GetAllPositions(ctx)
params := k.GetParams(ctx)
quoteTicker := k.GetPoolQuoteTicker(ctx)
for _, position := range positions {
currentBaseUsdRate, currentQuoteUsdRate, err := k.GetPairUsdPriceFromMarket(ctx, position.Market)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, currentBaseUsdRate)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, currentQuoteUsdRate)
if err != nil {
// todo: user logger
fmt.Println("failed to get pair usd price from market")
fmt.Println(err)
continue
}
if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, currentBaseUsdRate, currentQuoteUsdRate) {
if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, baseMetricsRate, quoteMetricsRate) {
msg := types.MsgReportLiquidation{
Sender: position.Address,
PositionId: position.Id,
Expand Down
28 changes: 18 additions & 10 deletions x/derivatives/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ func (k Keeper) PerpetualFutures(c context.Context, req *types.QueryPerpetualFut
}
}

longUUsd := positions.EvaluateLongPositions(getPriceFunc(ctx))
shortUUsd := positions.EvaluateShortPositions(getPriceFunc(ctx))
quoteTicker := k.GetPoolQuoteTicker(ctx)
longUUsd := positions.EvaluateLongPositions(quoteTicker, getPriceFunc(ctx))
shortUUsd := positions.EvaluateShortPositions(quoteTicker, getPriceFunc(ctx))
// TODO: implement the handler logic
ctx.BlockHeight()
metricsQuoteTicker := "USD"
Expand Down Expand Up @@ -209,20 +210,23 @@ func (k Keeper) MakeQueriedPositions(ctx sdk.Context, positions types.Positions)
return nil, status.Error(codes.Internal, err.Error())
}

profit := perpetualFuturesPosition.ProfitAndLossInMetrics(currentBaseUsdRate, currentQuoteUsdRate)
quoteTicker := k.GetPoolQuoteTicker(ctx)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, currentBaseUsdRate)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, currentQuoteUsdRate)
profit := perpetualFuturesPosition.ProfitAndLossInMetrics(baseMetricsRate, quoteMetricsRate)
// fixme do not use sdk.Coin directly
positiveOrNegativeProfitCoin := sdk.Coin{
Denom: "uusd",
Amount: types.MicroToNormalDenom(profit),
}
positiveOrNegativeEffectiveMargin := sdk.Coin{
Denom: "uusd",
Amount: types.MicroToNormalDenom(perpetualFuturesPosition.EffectiveMarginInMetrics(currentBaseUsdRate, currentQuoteUsdRate)),
Amount: types.MicroToNormalDenom(perpetualFuturesPosition.EffectiveMarginInMetrics(baseMetricsRate, quoteMetricsRate)),
}
queriedPosition := types.QueriedPosition{
Position: position,
ValuationProfit: positiveOrNegativeProfitCoin,
MarginMaintenanceRate: perpetualFuturesPosition.MarginMaintenanceRate(currentBaseUsdRate, currentQuoteUsdRate),
MarginMaintenanceRate: perpetualFuturesPosition.MarginMaintenanceRate(baseMetricsRate, quoteMetricsRate),
EffectiveMargin: positiveOrNegativeEffectiveMargin,
}
queriedPositions = append(queriedPositions, queriedPosition)
Expand Down Expand Up @@ -253,13 +257,16 @@ func (k Keeper) Position(c context.Context, req *types.QueryPositionRequest) (*t
if err != nil {
panic(err)
}
quoteTicker := k.GetPoolQuoteTicker(ctx)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, currentBaseUsdRate)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, currentQuoteUsdRate)

profit := perpetualFuturesPosition.ProfitAndLossInMetrics(currentBaseUsdRate, currentQuoteUsdRate)
profit := perpetualFuturesPosition.ProfitAndLossInMetrics(baseMetricsRate, quoteMetricsRate)
return &types.QueryPositionResponse{
Position: position,
ValuationProfit: sdk.NewCoin("uusd", types.MicroToNormalDenom(profit)),
MarginMaintenanceRate: perpetualFuturesPosition.MarginMaintenanceRate(currentBaseUsdRate, currentQuoteUsdRate),
EffectiveMargin: sdk.NewCoin("uusd", types.MicroToNormalDenom(perpetualFuturesPosition.EffectiveMarginInMetrics(currentBaseUsdRate, currentQuoteUsdRate))),
MarginMaintenanceRate: perpetualFuturesPosition.MarginMaintenanceRate(baseMetricsRate, quoteMetricsRate),
EffectiveMargin: sdk.NewCoin("uusd", types.MicroToNormalDenom(perpetualFuturesPosition.EffectiveMarginInMetrics(baseMetricsRate, quoteMetricsRate))),
}, nil
}

Expand All @@ -280,10 +287,11 @@ func (k Keeper) PerpetualFuturesPositionSize(c context.Context, req *types.Query
}
}
var result sdk.Dec
quoteTicker := k.GetPoolQuoteTicker(ctx)
if req.PositionType == types.PositionType_LONG {
result = positions.EvaluateLongPositions(getPriceFunc(ctx))
result = positions.EvaluateLongPositions(quoteTicker, getPriceFunc(ctx))
} else if req.PositionType == types.PositionType_SHORT {
result = positions.EvaluateShortPositions(getPriceFunc(ctx))
result = positions.EvaluateShortPositions(quoteTicker, getPriceFunc(ctx))
} else {
return nil, status.Error(codes.InvalidArgument, "invalid position type")
}
Expand Down
15 changes: 12 additions & 3 deletions x/derivatives/keeper/perpetual_futures.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ func (k Keeper) OpenPerpetualFuturesPosition(ctx sdk.Context, positionId string,
}

params := k.GetParams(ctx)
if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, position.OpenedBaseRate, position.OpenedQuoteRate) {
quoteTicker := k.GetPoolQuoteTicker(ctx)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, position.OpenedBaseRate)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, position.OpenedQuoteRate)
if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, baseMetricsRate, quoteMetricsRate) {
return nil, types.ErrorInvalidPositionParams
}

Expand Down Expand Up @@ -121,7 +124,10 @@ func (k Keeper) ClosePerpetualFuturesPosition(ctx sdk.Context, position types.Pe
}
}

returningAmount, lossToLP := position.CalcReturningAmountAtClose(baseUsdPrice, quoteUsdPrice)
quoteTicker := k.GetPoolQuoteTicker(ctx)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, baseUsdPrice)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, quoteUsdPrice)
returningAmount, lossToLP := position.CalcReturningAmountAtClose(baseMetricsRate, quoteMetricsRate)

if !(lossToLP.IsNil()) {
// TODO: emit event to tell how much loss is taken by liquidity provider.
Expand Down Expand Up @@ -152,7 +158,10 @@ func (k Keeper) ReportLiquidationNeededPerpetualFuturesPosition(ctx sdk.Context,
panic(err)
}

if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, currentBaseUsdRate, currentQuoteUsdRate) {
quoteTicker := k.GetPoolQuoteTicker(ctx)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, currentBaseUsdRate)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, currentQuoteUsdRate)
if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, baseMetricsRate, quoteMetricsRate) {
k.ClosePerpetualFuturesPosition(ctx, position)

rewardAmount := sdk.NewDecFromInt(position.RemainingMargin.Amount).Mul(params.PoolParams.ReportLiquidationRewardRate).RoundInt()
Expand Down
83 changes: 49 additions & 34 deletions x/derivatives/types/positions.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func MustUnpackPositionInstance(positionAny types.Any) PositionInstance {
return position
}

func (m Position) NeedLiquidation(MarginMaintenanceRate, currentBaseUsdRate, currentQuoteUsdRate sdk.Dec) bool {
func (m Position) NeedLiquidation(MarginMaintenanceRate sdk.Dec, currentBaseMetricsRate, currentQuoteMetricsRate MetricsRateType) bool {
ins, err := UnpackPositionInstance(m.PositionInstance)
if err != nil {
return false
Expand All @@ -56,7 +56,7 @@ func (m Position) NeedLiquidation(MarginMaintenanceRate, currentBaseUsdRate, cur
switch positionInstance := ins.(type) {
case *PerpetualFuturesPositionInstance:
perpetualFuturesPosition := NewPerpetualFuturesPosition(m, *positionInstance)
return perpetualFuturesPosition.NeedLiquidation(MarginMaintenanceRate, currentBaseUsdRate, currentQuoteUsdRate)
return perpetualFuturesPosition.NeedLiquidation(MarginMaintenanceRate, currentBaseMetricsRate, currentQuoteMetricsRate)
break
case *PerpetualOptionsPositionInstance:
panic("not implemented")
Expand Down Expand Up @@ -108,8 +108,8 @@ func NewPerpetualFuturesPositionFromPosition(position Position) (PerpetualFuture
return PerpetualFuturesPosition{}, fmt.Errorf("this Any doesn't have PerpetualFuturesPositionInstance value")
}

func (m PerpetualFuturesPosition) NeedLiquidation(minMarginMaintenanceRate, currentBaseUsdRate, currentQuoteUsdRate sdk.Dec) bool {
marginMaintenanceRate := m.MarginMaintenanceRate(currentBaseUsdRate, currentQuoteUsdRate)
func (m PerpetualFuturesPosition) NeedLiquidation(minMarginMaintenanceRate sdk.Dec, currentBaseMetricsRate, currentQuoteMetricsRate MetricsRateType) bool {
marginMaintenanceRate := m.MarginMaintenanceRate(currentBaseMetricsRate, currentQuoteMetricsRate)
if marginMaintenanceRate.LT(minMarginMaintenanceRate) {
return true
} else {
Expand All @@ -122,8 +122,8 @@ func (m PerpetualFuturesPosition) OpenedPairRate() sdk.Dec {
}

// todo make test
func (m PerpetualFuturesPosition) EvaluatePosition(currentBaseUsdRate sdk.Dec) sdk.Dec {
return currentBaseUsdRate.Mul(m.PositionInstance.Size_)
func (m PerpetualFuturesPosition) EvaluatePosition(currentBaseMetricsRate MetricsRateType) sdk.Dec {
return currentBaseMetricsRate.Amount.Amount.Mul(m.PositionInstance.Size_)
}

// TODO: consider to use sdk.DecCoin
Expand All @@ -135,9 +135,9 @@ func MicroToNormalDec(amount sdk.Dec) sdk.Dec {
return amount.Mul(sdk.MustNewDecFromStr("1000000"))
}

func (m PerpetualFuturesPosition) CalcReturningAmountAtClose(baseUSDRate, quoteUSDRate sdk.Dec) (returningAmount math.Int, lossToLP math.Int) {
func (m PerpetualFuturesPosition) CalcReturningAmountAtClose(baseMetricsRate, quoteMetricsRate MetricsRateType) (returningAmount math.Int, lossToLP math.Int) {
principal := m.RemainingMargin.Amount
pnlAmount := m.ProfitAndLossInMetrics(baseUSDRate, quoteUSDRate)
pnlAmount := m.ProfitAndLossInMetrics(baseMetricsRate, quoteMetricsRate)

returningAmount = principal.Add(pnlAmount.TruncateInt())

Expand All @@ -151,7 +151,7 @@ func (m PerpetualFuturesPosition) CalcReturningAmountAtClose(baseUSDRate, quoteU
}

// todo make test
func (m Positions) EvaluatePositions(posType PositionType, getCurrentPriceF func(denom string) (sdk.Dec, error)) sdk.Dec {
func (m Positions) EvaluatePositions(posType PositionType, quoteTicker string, getCurrentPriceF func(denom string) (sdk.Dec, error)) sdk.Dec {
usdMap := map[string]sdk.Dec{}
result := sdk.ZeroDec()
for _, position := range m {
Expand All @@ -174,7 +174,10 @@ func (m Positions) EvaluatePositions(posType PositionType, getCurrentPriceF func
if perpetualFuturesPosition.PositionInstance.PositionType != posType {
continue
}
result = result.Add(perpetualFuturesPosition.EvaluatePosition(usdMap[position.Market.BaseDenom]))

metricsRate := NewMetricsRateType(quoteTicker, position.Market.BaseDenom, usdMap[position.Market.BaseDenom])

result = result.Add(perpetualFuturesPosition.EvaluatePosition(metricsRate))
break
case *PerpetualOptionsPositionInstance:
panic("not implemented")
Expand All @@ -185,12 +188,12 @@ func (m Positions) EvaluatePositions(posType PositionType, getCurrentPriceF func
return result
}

func (m Positions) EvaluateLongPositions(getCurrentPriceF func(denom string) (sdk.Dec, error)) sdk.Dec {
return m.EvaluatePositions(PositionType_LONG, getCurrentPriceF)
func (m Positions) EvaluateLongPositions(quoteTicker string, getCurrentPriceF func(denom string) (sdk.Dec, error)) sdk.Dec {
return m.EvaluatePositions(PositionType_LONG, quoteTicker, getCurrentPriceF)
}

func (m Positions) EvaluateShortPositions(getCurrentPriceF func(denom string) (sdk.Dec, error)) sdk.Dec {
return m.EvaluatePositions(PositionType_SHORT, getCurrentPriceF)
func (m Positions) EvaluateShortPositions(quoteTicker string, getCurrentPriceF func(denom string) (sdk.Dec, error)) sdk.Dec {
return m.EvaluatePositions(PositionType_SHORT, quoteTicker, getCurrentPriceF)
}

func (m PerpetualFuturesPosition) RequiredMarginInQuote(baseQuoteRate sdk.Dec) sdk.Dec {
Expand All @@ -202,22 +205,22 @@ func (m PerpetualFuturesPosition) RequiredMarginInBase() sdk.Dec {
return m.PositionInstance.MarginRequirement(sdk.MustNewDecFromStr("1"))
}

// func (m PerpetualFuturesPosition) RequiredMarginInMetrics(requiredMarginInQuote, quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) RequiredMarginInMetrics(baseUSDRate, quoteUSDRate sdk.Dec) sdk.Dec {
// func (m PerpetualFuturesPosition) RequiredMarginInMetrics(requiredMarginInQuote, quoteMetricsRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) RequiredMarginInMetrics(baseMetricsRate, quoteMetricsRate MetricsRateType) sdk.Dec {
// 必要証拠金(USD単位) = 必要証拠金(quote単位) * 現在のquote/USDレート
// = 必要証拠金(base単位) * 現在のbase/USDレート
if m.RemainingMargin.Denom == m.Market.QuoteDenom {
baseQuoteRate := baseUSDRate.Quo(quoteUSDRate)
return m.RequiredMarginInQuote(baseQuoteRate).Mul(quoteUSDRate)
baseQuoteRate := baseMetricsRate.Amount.Amount.Quo(quoteMetricsRate.Amount.Amount)
return m.RequiredMarginInQuote(baseQuoteRate).Mul(quoteMetricsRate.Amount.Amount)
} else if m.RemainingMargin.Denom == m.Market.BaseDenom {
return m.RequiredMarginInBase().Mul(baseUSDRate)
return m.RequiredMarginInBase().Mul(baseMetricsRate.Amount.Amount)
} else {
panic("not supported denom")
}
}
func (m PerpetualFuturesPosition) ProfitAndLossInQuote(baseUSDRate, quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) ProfitAndLossInQuote(baseMetricsRate, quoteMetricsRate MetricsRateType) sdk.Dec {
// 損益(quote単位) = (longなら1,shortなら-1) * (現在のbase/quoteレート - ポジション開設時base/quoteレート) * ポジションサイズ(base単位)
baseQuoteRate := baseUSDRate.Quo(quoteUSDRate)
baseQuoteRate := baseMetricsRate.Amount.Amount.Quo(quoteMetricsRate.Amount.Amount)
profitOrLoss := baseQuoteRate.Sub(m.OpenedPairRate()).Mul(m.PositionInstance.Size_)
if m.PositionInstance.PositionType == PositionType_LONG {
return profitOrLoss
Expand All @@ -226,35 +229,47 @@ func (m PerpetualFuturesPosition) ProfitAndLossInQuote(baseUSDRate, quoteUSDRate
}
}

func (m PerpetualFuturesPosition) ProfitAndLossInMetrics(baseUSDRate, quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) ProfitAndLossInMetrics(baseMetricsRate, quoteMetricsRate MetricsRateType) sdk.Dec {
// 損益(USD単位) = 損益(quote単位) * 現在のquote/USDレート
return m.ProfitAndLossInQuote(baseUSDRate, quoteUSDRate).Mul(quoteUSDRate)
return m.ProfitAndLossInQuote(baseMetricsRate, quoteMetricsRate).Mul(quoteMetricsRate.Amount.Amount)
}
func (m PerpetualFuturesPosition) MarginMaintenanceRate(baseUSDRate, quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) MarginMaintenanceRate(baseMetricsRate, quoteMetricsRate MetricsRateType) sdk.Dec {
// 証拠金維持率 = 有効証拠金(USD単位) ÷ 必要証拠金(USD単位)
return m.EffectiveMarginInMetrics(baseUSDRate, quoteUSDRate).Quo(m.RequiredMarginInMetrics(baseUSDRate, quoteUSDRate))
return m.EffectiveMarginInMetrics(baseMetricsRate, quoteMetricsRate).Quo(m.RequiredMarginInMetrics(baseMetricsRate, quoteMetricsRate))
}
func (m PerpetualFuturesPosition) RemainingMarginInBase(baseUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) RemainingMarginInBase(baseMetricsRate MetricsRateType) sdk.Dec {
// 残存証拠金(USD単位) = 残存証拠金(base単位) * 現在のbase/USDレート
return sdk.NewDecFromInt(m.RemainingMargin.Amount).Mul(baseUSDRate)
return sdk.NewDecFromInt(m.RemainingMargin.Amount).Mul(baseMetricsRate.Amount.Amount)
}
func (m PerpetualFuturesPosition) RemainingMarginInQuote(quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) RemainingMarginInQuote(quoteMetricsRate MetricsRateType) sdk.Dec {
// 残存証拠金(USD単位) = 残存証拠金(quote単位) * 現在のquote/USDレート
return sdk.NewDecFromInt(m.RemainingMargin.Amount).Mul(quoteUSDRate)
return sdk.NewDecFromInt(m.RemainingMargin.Amount).Mul(quoteMetricsRate.Amount.Amount)
}
func (m PerpetualFuturesPosition) RemainingMarginInMetrics(baseUSDRate, quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) RemainingMarginInMetrics(baseMetricsRate, quoteMetricsRate MetricsRateType) sdk.Dec {
// 残存証拠金(USD単位) = 残存証拠金(base単位) * 現在のbase/USDレート
// = 残存証拠金(quote単位) * 現在のquote/USDレート
if m.RemainingMargin.Denom == m.Market.BaseDenom {
return m.RemainingMarginInBase(baseUSDRate)
return m.RemainingMarginInBase(baseMetricsRate)
} else if m.RemainingMargin.Denom == m.Market.QuoteDenom {
return m.RemainingMarginInQuote(quoteUSDRate)
return m.RemainingMarginInQuote(quoteMetricsRate)
} else {
panic("not supported denom")
}
}

func (m PerpetualFuturesPosition) EffectiveMarginInMetrics(baseUSDRate, quoteUSDRate sdk.Dec) sdk.Dec {
func (m PerpetualFuturesPosition) EffectiveMarginInMetrics(baseMetricsRate, quoteMetricsRate MetricsRateType) sdk.Dec {
// 有効証拠金(USD単位) = 残存証拠金(USD単位) + 損益(USD単位)
return m.RemainingMarginInMetrics(baseUSDRate, quoteUSDRate).Add(m.ProfitAndLossInMetrics(baseUSDRate, quoteUSDRate))
return m.RemainingMarginInMetrics(baseMetricsRate, quoteMetricsRate).Add(m.ProfitAndLossInMetrics(baseMetricsRate, quoteMetricsRate))
}

func NewMetricsRateType(unit string, denom string, amount sdk.Dec) MetricsRateType {
return MetricsRateType{
MetricsUnit: unit,
Amount: sdk.NewDecCoinFromDec(denom, amount),
}
}

type MetricsRateType struct {
MetricsUnit string
Amount sdk.DecCoin
}