Skip to content

Commit

Permalink
Merge pull request #11463 from vegaprotocol/fix/11452
Browse files Browse the repository at this point in the history
feat: implement alternative estimations for fully collateralised capped future
  • Loading branch information
jeremyletang authored Jul 16, 2024
2 parents af87a90 + 0a7d2c1 commit 4096f41
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 13 deletions.
1 change: 1 addition & 0 deletions .spelling
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cleanup
Cleanup
clef
codegen
collateralised
cometbft
config
cyclomatic
Expand Down
21 changes: 11 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- [11026](https://github.com/vegaprotocol/vega/issues/11026) - Add API flag to get paid liquidity fees for a `vAMM` using the parent key.
- [11027](https://github.com/vegaprotocol/vega/issues/11027) - Add API filters to get fees and rewards by market, across epochs.
- [10360](https://github.com/vegaprotocol/vega/issues/10360) - Scale funding payment by fraction of period spent outside of auction.
- [11452](https://github.com/vegaprotocol/vega/issues/11452) - Implement alternative estimations for fully collateralised capped future.

### 🐛 Fixes

Expand All @@ -49,17 +50,17 @@
- [11319](https://github.com/vegaprotocol/vega/issues/11319) - Do not leak Ethereum client secrets in the logs.
- [11336](https://github.com/vegaprotocol/vega/issues/11336) - Add support for decay factor in governance recurring transfers and report the proposal amount rather than 0 when the proposal gets enacted.
- [11368](https://github.com/vegaprotocol/vega/issues/11368) - Add support for update vesting stats in REST API and fix summing the quantum balance for vesting stats.
- [11380](https://github.com/vegaprotocol/vega/issues/11380) - Handle broken stop orders in prepare proposal.
- [11380](https://github.com/vegaprotocol/vega/issues/11380) - Handle broken stop orders in prepare proposal.
- [11136](https://github.com/vegaprotocol/vega/issues/11136) - Fix premature invocation of post commit hooks in case of fee stats event.
- [11409](https://github.com/vegaprotocol/vega/issues/11409) - When updating a capped market - copy the cap from the existing market definition.
- [11415](https://github.com/vegaprotocol/vega/issues/11415) - End long block auction when expired.
- [11419](https://github.com/vegaprotocol/vega/issues/11419) - Fix long block auction extension to be calculated from current time.
- [11438](https://github.com/vegaprotocol/vega/issues/11438) - Add missing assignment to epoch to and from in `gameTeamScores` resolvers.
- [11442](https://github.com/vegaprotocol/vega/issues/11442) - Add validation to `submitAMM` to ensure the curves can be successfully generated.
- [11448](https://github.com/vegaprotocol/vega/issues/11448) - Fix team game score query with epoch filter.
- [11457](https://github.com/vegaprotocol/vega/issues/11457) - Fix cursor column ordering for game scores.
- [11454](https://github.com/vegaprotocol/vega/issues/11454) - Ensure ended transfers proper handling.
- [11409](https://github.com/vegaprotocol/vega/issues/11409) - When updating a capped market - copy the cap from the existing market definition.
- [11415](https://github.com/vegaprotocol/vega/issues/11415) - End long block auction when expired.
- [11419](https://github.com/vegaprotocol/vega/issues/11419) - Fix long block auction extension to be calculated from current time.
- [11438](https://github.com/vegaprotocol/vega/issues/11438) - Add missing assignment to epoch to and from in `gameTeamScores` resolvers.
- [11442](https://github.com/vegaprotocol/vega/issues/11442) - Add validation to `submitAMM` to ensure the curves can be successfully generated.
- [11448](https://github.com/vegaprotocol/vega/issues/11448) - Fix team game score query with epoch filter.
- [11457](https://github.com/vegaprotocol/vega/issues/11457) - Fix cursor column ordering for game scores.
- [11454](https://github.com/vegaprotocol/vega/issues/11454) - Ensure ended transfers proper handling.


## 0.76.1

Expand Down
10 changes: 10 additions & 0 deletions core/risk/liquidation_calculation.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ type OrderInfo struct {
IsMarketOrder bool
}

// Clone - Not really necessary, just added to avoid future pointers
// copy if some were added.
func (o *OrderInfo) Clone() *OrderInfo {
return &OrderInfo{
TrueRemaining: o.TrueRemaining,
Price: o.Price.Copy(),
IsMarketOrder: o.IsMarketOrder,
}
}

func CalculateLiquidationPriceWithSlippageFactors(sizePosition int64, buyOrders, sellOrders []*OrderInfo, currentPrice, collateralAvailable num.Decimal, positionFactor, linearSlippageFactor, quadraticSlippageFactor, riskFactorLong, riskFactorShort, fundingPaymentPerUnitPosition num.Decimal, isolatedMarginMode bool, marginFactor num.Decimal) (liquidationPriceForOpenVolume, liquidationPriceWithBuyOrders, liquidationPriceWithSellOrders num.Decimal, err error) {
openVolume := num.DecimalFromInt64(sizePosition).Div(positionFactor)

Expand Down
150 changes: 147 additions & 3 deletions datanode/api/trading_data_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -3526,6 +3526,9 @@ func (t *TradingDataServiceV2) EstimatePosition(ctx context.Context, req *v2.Est
if err != nil {
return nil, formatE(ErrMarketServiceGetByID, err)
}

cap, hasCap := mkt.HasCap()

collateralAvailable := marginAccountBalance
crossMarginMode := req.MarginMode == types.MarginModeCrossMargin
if crossMarginMode {
Expand Down Expand Up @@ -3666,6 +3669,8 @@ func (t *TradingDataServiceV2) EstimatePosition(ctx context.Context, req *v2.Est
req.MarginMode,
dMarginFactor,
auctionPrice,
cap,
avgEntryPrice,
)

marginEstimate := &v2.MarginEstimate{
Expand Down Expand Up @@ -3729,6 +3734,36 @@ func (t *TradingDataServiceV2) EstimatePosition(ctx context.Context, req *v2.Est
wWithSell = t.scaleDecimalFromAssetToMarketPrice(wWithSell, dPriceFactor)
}

f := func(volume int64) (worst, best decimal.Decimal) {
// if party is long, then liquidation is 0
if volume >= 0 {
return num.DecimalZero(), num.DecimalZero()
}

// if its short we use the size of capPrice
return num.MustDecimalFromString(cap.MaxPrice), num.MustDecimalFromString(cap.MaxPrice) // can't fail coming from the DB
}

// no worst or best case in this case, so just setting the same on boths
if hasCap && cap.FullyCollateralised != nil && *cap.FullyCollateralised {
// openVolume first
wPositionOnly, bPositionOnly = f(req.OpenVolume)

// then including buyOrders
incBuyOrders := req.OpenVolume
for _, v := range buyOrders {
incBuyOrders += int64(v.TrueRemaining)
}
wWithBuy, bWithBuy = f(incBuyOrders)

// then including sellOrders
incSellOrders := req.OpenVolume
for _, v := range sellOrders {
incSellOrders += int64(v.TrueRemaining)
}
wWithSell, bWithSell = f(incSellOrders)
}

liquidationEstimate := &v2.LiquidationEstimate{
WorstCase: &v2.LiquidationPrice{
OpenVolumeOnly: wPositionOnly.Round(0).String(),
Expand Down Expand Up @@ -3761,6 +3796,8 @@ func (t *TradingDataServiceV2) computeMarginRange(
auction bool,
marginMode vega.MarginMode,
marginFactor, auctionPrice num.Decimal,
cap *vega.FutureCap,
averageEntryPrice num.Decimal,
) (num.Decimal, num.Decimal, num.Decimal) {
bOrders, sOrders := buyOrders, sellOrders
orderMargin := num.DecimalZero()
Expand All @@ -3776,6 +3813,7 @@ func (t *TradingDataServiceV2) computeMarginRange(
}
}
sOrders = []*risk.OrderInfo{}

sNonMarketOrders := []*risk.OrderInfo{}
for _, o := range sellOrders {
if o.IsMarketOrder {
Expand All @@ -3784,15 +3822,121 @@ func (t *TradingDataServiceV2) computeMarginRange(
sNonMarketOrders = append(sNonMarketOrders, o)
}
}
orderMargin = risk.CalcOrderMarginIsolatedMode(openVolume, bNonMarketOrders, sNonMarketOrders, positionFactor, marginFactor, auctionPrice)

// this is a special case for fully collateralised capped future markets
if cap != nil && cap.FullyCollateralised != nil && *cap.FullyCollateralised {
orderMargin = calcOrderMarginIsolatedModeCappedAndFullyCollateralised(bNonMarketOrders, sNonMarketOrders, cap)
} else {
orderMargin = risk.CalcOrderMarginIsolatedMode(openVolume, bNonMarketOrders, sNonMarketOrders, positionFactor, marginFactor, auctionPrice)
}
}

worst := risk.CalculateMaintenanceMarginWithSlippageFactors(openVolume, bOrders, sOrders, marketObservable, positionFactor, linearSlippageFactor, quadraticSlippageFactor, riskFactors.Long, riskFactors.Short, fundingPaymentPerUnitPosition, auction, auctionPrice)
best := risk.CalculateMaintenanceMarginWithSlippageFactors(openVolume, bOrders, sOrders, marketObservable, positionFactor, num.DecimalZero(), num.DecimalZero(), riskFactors.Long, riskFactors.Short, fundingPaymentPerUnitPosition, auction, auctionPrice)
var worst, best num.Decimal
// this is a special case for fully collateralised capped future markets
if cap != nil && cap.FullyCollateralised != nil && *cap.FullyCollateralised {
worst = calcPositionMarginCappedAndFullyCollateralised(bOrders, sOrders, cap, openVolume, averageEntryPrice)
best = worst
} else {
worst = risk.CalculateMaintenanceMarginWithSlippageFactors(openVolume, bOrders, sOrders, marketObservable, positionFactor, linearSlippageFactor, quadraticSlippageFactor, riskFactors.Long, riskFactors.Short, fundingPaymentPerUnitPosition, auction, auctionPrice)
best = risk.CalculateMaintenanceMarginWithSlippageFactors(openVolume, bOrders, sOrders, marketObservable, positionFactor, num.DecimalZero(), num.DecimalZero(), riskFactors.Long, riskFactors.Short, fundingPaymentPerUnitPosition, auction, auctionPrice)
}

return worst, best, orderMargin
}

func calcPositionMarginCappedAndFullyCollateralised(
buyOrders []*risk.OrderInfo,
sellOrders []*risk.OrderInfo,
cap *vega.FutureCap,
openVolume int64,
openVolumeAverageEntryPrice decimal.Decimal,
) decimal.Decimal {
// get average entry price over all orders
// and the final volume.
// then if long:
// - averageEntryPrice * positionSize
// if short:
// - (priceCap - averageEntryPrice) * positionSize

priceCap := num.MustDecimalFromString(cap.MaxPrice)

positionSize := openVolume
totalVolume := openVolume
ongoing := openVolumeAverageEntryPrice.Mul(num.DecimalFromInt64(openVolume))
for _, v := range buyOrders {
price := v.Price
if price.GreaterThan(priceCap) {
price = priceCap
}

size := int64(v.TrueRemaining)
positionSize += size
totalVolume += size
ongoing = ongoing.Add(v.Price.Mul(num.DecimalFromInt64(size)))
}

for _, v := range sellOrders {
price := v.Price
if price.GreaterThan(priceCap) {
price = priceCap
}

size := int64(v.TrueRemaining)
positionSize -= size // only thing changing here really
totalVolume += size
ongoing = ongoing.Add(v.Price.Mul(num.DecimalFromInt64(size)))
}

averageEntryPrice := ongoing.Div(num.DecimalFromInt64(totalVolume))

if positionSize < 0 {
// short position
positionSize = -positionSize
return priceCap.Sub(averageEntryPrice).Mul(num.DecimalFromInt64(positionSize))
}

return priceCap.Mul(num.DecimalFromInt64(positionSize))
}

func calcOrderMarginIsolatedModeCappedAndFullyCollateralised(
buyOrders []*risk.OrderInfo,
sellOrders []*risk.OrderInfo,
cap *vega.FutureCap,
) decimal.Decimal {
// long order margin:
// - price * positionSize
// short order marign:
// - (cappedPrice - price) * positionSize

cappedPrice := num.MustDecimalFromString(cap.MaxPrice)
marginBuy, marginSell := num.DecimalZero(), num.DecimalZero()

for _, v := range buyOrders {
price := v.Price
if v.Price.GreaterThan(cappedPrice) {
price = cappedPrice
}

marginBuy.Add(price.Mul(num.DecimalFromInt64(int64(v.TrueRemaining))))
}

for _, v := range sellOrders {
price := v.Price
if v.Price.GreaterThan(cappedPrice) {
price = cappedPrice
}

price = cappedPrice.Sub(price)
marginSell.Add(price.Mul(num.DecimalFromInt64(int64(v.TrueRemaining))))
}

if marginBuy.GreaterThan(marginSell) {
return marginBuy
}

return marginSell
}

// ListNetworkParameters returns a list of network parameters.
func (t *TradingDataServiceV2) ListNetworkParameters(ctx context.Context, req *v2.ListNetworkParametersRequest) (*v2.ListNetworkParametersResponse, error) {
defer metrics.StartAPIRequestAndTimeGRPC("ListNetworkParametersV2")()
Expand Down
12 changes: 12 additions & 0 deletions datanode/entities/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ type Market struct {
EnableTXReordering bool
}

func (m *Market) HasCap() (cap *vega.FutureCap, hasCap bool) {
if inst := m.TradableInstrument.Instrument; inst != nil {
if fut := inst.GetFuture(); fut != nil {
if cap := fut.GetCap(); cap != nil {
return cap, true
}
}
}

return nil, false
}

type MarketCursor struct {
VegaTime time.Time `json:"vegaTime"`
ID MarketID `json:"id"`
Expand Down

0 comments on commit 4096f41

Please sign in to comment.