Skip to content

Commit

Permalink
fix: estimate AMM bounds now reports commitment issues and respects m…
Browse files Browse the repository at this point in the history
…arket DPs
  • Loading branch information
wwestgarth committed Aug 7, 2024
1 parent 54d7749 commit e3c30e0
Show file tree
Hide file tree
Showing 6 changed files with 2,688 additions and 2,415 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
- [11428](https://github.com/vegaprotocol/vega/issues/11428) - Add buy back and treasury fee and separate discount/reward factors.
- [11468](https://github.com/vegaprotocol/vega/issues/11468) - Added support for volume rebate program.
- [11523](https://github.com/vegaprotocol/vega/issues/11523) - Change method of caching to improve `AMM` snapshot performance.
- [11426](https://github.com/vegaprotocol/vega/issues/11426) - `EstimateAMMBounds` now reports issues with committment.
- [11459](https://github.com/vegaprotocol/vega/issues/11459) - Deprecate time weight position reward metric and replace it with time weighted notional.
- [11372](https://github.com/vegaprotocol/vega/issues/11372) - Support combined filters for the `AMM` API.

### 🐛 Fixes

- [11521](https://github.com/vegaprotocol/vega/issues/11521) - Restore `AMM` position factor when loading from a snapshot.
- [11526](https://github.com/vegaprotocol/vega/issues/11526) - `EstimateAMMBounds` now respects the market's decimal places.


## 0.77.5
Expand Down
77 changes: 56 additions & 21 deletions core/execution/amm/estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ import (
)

type EstimatedBounds struct {
PositionSizeAtUpper num.Decimal
PositionSizeAtLower num.Decimal
LossOnCommitmentAtUpper num.Decimal
LossOnCommitmentAtLower num.Decimal
LiquidationPriceAtUpper num.Decimal
LiquidationPriceAtLower num.Decimal
TooWideLower bool

PositionSizeAtUpper num.Decimal
LossOnCommitmentAtUpper num.Decimal
LiquidationPriceAtUpper num.Decimal
TooWideUpper bool
}

func EstimateBounds(
Expand All @@ -34,37 +37,62 @@ func EstimateBounds(
leverageLower, leverageUpper num.Decimal,
balance *num.Uint,
linearSlippageFactor, initialMargin,
riskFactorShort, riskFactorLong num.Decimal,
riskFactorShort, riskFactorLong,
priceFactor, positionFactor num.Decimal,
) EstimatedBounds {
r := EstimatedBounds{}

balanceD := balance.ToDecimal()

oneTick, _ := num.UintFromDecimal(priceFactor)
oneTick = num.Max(num.UintOne(), oneTick)

if lowerPrice != nil {
unitLower := LiquidityUnit(sqrter, basePrice, lowerPrice)

avgEntryLower := AverageEntryPrice(sqrter, unitLower, basePrice)
riskFactorLower := RiskFactor(leverageLower, riskFactorLong, linearSlippageFactor, initialMargin)
lowerPriceD := lowerPrice.ToDecimal()
boundPosLower := PositionAtLowerBound(riskFactorLower, balanceD, lowerPriceD, avgEntryLower)
boundPosLower := PositionAtLowerBound(riskFactorLower, balanceD, lowerPriceD, avgEntryLower, positionFactor)

lossLower := LossOnCommitment(avgEntryLower, lowerPriceD, boundPosLower)

liquidationPriceAtLower := LiquidationPrice(balanceD, lossLower, boundPosLower, lowerPriceD, linearSlippageFactor, riskFactorLong)

r.PositionSizeAtLower = boundPosLower.Truncate(5)
r.LiquidationPriceAtLower = liquidationPriceAtLower.Truncate(5)
r.LossOnCommitmentAtLower = lossLower.Truncate(5)
r.PositionSizeAtLower = boundPosLower
r.LiquidationPriceAtLower = liquidationPriceAtLower
r.LossOnCommitmentAtLower = lossLower

// now lets check that the lower bound is not too wide that the volume is spread too thin
l := unitLower.Mul(boundPosLower).Abs()

pos := impliedPosition(sqrter.sqrt(num.UintZero().Sub(basePrice, oneTick)), sqrter.sqrt(basePrice), l)
if pos.LessThan(num.DecimalOne()) {
r.TooWideLower = true
}
}

if upperPrice != nil {
unitUpper := LiquidityUnit(sqrter, upperPrice, basePrice)

avgEntryUpper := AverageEntryPrice(sqrter, unitUpper, upperPrice)
riskFactorUpper := RiskFactor(leverageUpper, riskFactorShort, linearSlippageFactor, initialMargin)
upperPriceD := upperPrice.ToDecimal()
boundPosUpper := PositionAtUpperBound(riskFactorUpper, balanceD, upperPriceD, avgEntryUpper)
boundPosUpper := PositionAtUpperBound(riskFactorUpper, balanceD, upperPriceD, avgEntryUpper, positionFactor)
lossUpper := LossOnCommitment(avgEntryUpper, upperPriceD, boundPosUpper)
liquidationPriceAtUpper := LiquidationPrice(balanceD, lossUpper, boundPosUpper, upperPriceD, linearSlippageFactor, riskFactorShort)

r.PositionSizeAtUpper = boundPosUpper.Truncate(5)
r.LiquidationPriceAtUpper = liquidationPriceAtUpper.Truncate(5)
r.LossOnCommitmentAtUpper = lossUpper.Truncate(5)
r.PositionSizeAtUpper = boundPosUpper
r.LiquidationPriceAtUpper = liquidationPriceAtUpper
r.LossOnCommitmentAtUpper = lossUpper

// now lets check that the lower bound is not too wide that the volume is spread too thin
l := unitUpper.Mul(boundPosUpper).Abs()

pos := impliedPosition(sqrter.sqrt(num.UintZero().Sub(upperPrice, oneTick)), sqrter.sqrt(upperPrice), l)
if pos.LessThan(num.DecimalOne()) {
r.TooWideUpper = true
}
}

return r
Expand Down Expand Up @@ -93,33 +121,40 @@ func AverageEntryPrice(sqrter *Sqrter, lu num.Decimal, pu *num.Uint) num.Decimal
}

// Pvl = rf * b / (pl * (1 - rf) + rf * pa).
func PositionAtLowerBound(rf, b, pl, pa num.Decimal) num.Decimal {
func PositionAtLowerBound(rf, b, pl, pa, positionFactor num.Decimal) num.Decimal {
oneSubRf := num.DecimalOne().Sub(rf)
rfMulPa := rf.Mul(pa)

return rf.Mul(b).Div(
pv := rf.Mul(b).Div(
pl.Mul(oneSubRf).Add(rfMulPa),
)
return pv.Mul(positionFactor)
}

// Pvl = -rf * b / (pl * (1 + rf) - rf * pa).
func PositionAtUpperBound(rf, b, pl, pa num.Decimal) num.Decimal {
func PositionAtUpperBound(rf, b, pl, pa, positionFactor num.Decimal) num.Decimal {
onePlusRf := num.DecimalOne().Add(rf)
rfMulPa := rf.Mul(pa)

return rf.Neg().Mul(b).Div(
pv := rf.Neg().Mul(b).Div(
pl.Mul(onePlusRf).Sub(rfMulPa),
)
return pv.Mul(positionFactor)
}

// lc = |pa - pb * pB|.
// lc = |(pa - pb) * pB|.
func LossOnCommitment(pa, pb, pB num.Decimal) num.Decimal {
return pa.Sub(pb).Mul(pB).Abs()
res := pa.Sub(pb).Mul(pB).Abs()
return res
}

// Pliq = (b - lc - Pb * pb) / (|Pb| * (fl + mr) - Pb).
func LiquidationPrice(b, lc, pB, pb, fl, mr num.Decimal) num.Decimal {
return b.Sub(lc).Sub(pB.Mul(pb)).Div(
pB.Abs().Mul(fl.Add(mr)).Sub(pB),
)
// (b - lc - Pb * pb)
numer := b.Sub(lc).Sub(pB.Mul(pb))

// (|Pb| * (fl + mr) - Pb)
denom := pB.Abs().Mul(fl.Add(mr)).Sub(pB)

return num.MaxD(num.DecimalZero(), numer.Div(denom))
}
51 changes: 49 additions & 2 deletions core/execution/amm/estimator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func TestEstimateSeparateFunctions(t *testing.T) {
upperPriceD := upperPrice.ToDecimal()

// test position at bounds
lowerBoundPos := PositionAtLowerBound(riskFactorLower, balance.ToDecimal(), lowerPriceD, avgEntryLower)
upperBoundPos := PositionAtUpperBound(riskFactorUpper, balance.ToDecimal(), upperPriceD, avgEntryUpper)
lowerBoundPos := PositionAtLowerBound(riskFactorLower, balance.ToDecimal(), lowerPriceD, avgEntryLower, num.DecimalOne())
upperBoundPos := PositionAtUpperBound(riskFactorUpper, balance.ToDecimal(), upperPriceD, avgEntryUpper, num.DecimalOne())
assert.Equal(t, num.DecimalFromFloat(0.437).String(), lowerBoundPos.Round(3).String())
assert.Equal(t, num.DecimalFromFloat(-0.069).String(), upperBoundPos.Round(3).String())

Expand Down Expand Up @@ -116,6 +116,8 @@ func TestEstimate(t *testing.T) {
initialMargin,
riskFactorShort,
riskFactorLong,
num.DecimalOne(),
num.DecimalOne(),
)

assert.Equal(t, expectedMetrics.PositionSizeAtUpper.String(), metrics.PositionSizeAtUpper.Round(3).String())
Expand All @@ -124,6 +126,8 @@ func TestEstimate(t *testing.T) {
assert.Equal(t, expectedMetrics.LossOnCommitmentAtLower.String(), metrics.LossOnCommitmentAtLower.Round(3).String())
assert.Equal(t, expectedMetrics.LiquidationPriceAtUpper.String(), metrics.LiquidationPriceAtUpper.Round(3).String())
assert.Equal(t, expectedMetrics.LiquidationPriceAtLower.String(), metrics.LiquidationPriceAtLower.Round(3).String())
assert.True(t, metrics.TooWideLower)
assert.True(t, metrics.TooWideUpper)
})

t.Run("test 0014-NP-VAMM-004", func(t *testing.T) {
Expand Down Expand Up @@ -155,6 +159,8 @@ func TestEstimate(t *testing.T) {
initialMargin,
riskFactorShort,
riskFactorLong,
num.DecimalOne(),
num.DecimalOne(),
)

assert.Equal(t, expectedMetrics.PositionSizeAtUpper.String(), metrics.PositionSizeAtUpper.Round(3).String())
Expand All @@ -165,3 +171,44 @@ func TestEstimate(t *testing.T) {
assert.Equal(t, expectedMetrics.LiquidationPriceAtLower.String(), metrics.LiquidationPriceAtLower.Round(3).String())
})
}

func TestEstimatePositionFactor(t *testing.T) {
initialMargin := num.DecimalFromFloat(1.2)
riskFactorShort := num.DecimalFromFloat(0.05529953589167391)
riskFactorLong := num.DecimalFromFloat(0.05529953589167391)
linearSlippageFactor := num.DecimalFromFloat(0.01)
sqrter := NewSqrter()

lowerPrice := num.MustUintFromString("80000000000000000000", 10)
basePrice := num.MustUintFromString("100000000000000000000", 10)
upperPrice := num.MustUintFromString("120000000000000000000", 10)
leverageUpper := num.DecimalFromFloat(0.5)
leverageLower := num.DecimalFromFloat(0.5)
balance := num.MustUintFromString("390500000000000000000000000", 10)

expectedMetrics := EstimatedBounds{
PositionSizeAtUpper: num.DecimalFromFloat(-1559159.284),
PositionSizeAtLower: num.DecimalFromFloat(2304613.63),
}

metrics := EstimateBounds(
sqrter,
lowerPrice,
basePrice,
upperPrice,
leverageLower,
leverageUpper,
balance,
linearSlippageFactor,
initialMargin,
riskFactorShort,
riskFactorLong,
num.DecimalFromInt64(1000000000000000000),
num.DecimalOne(),
)

assert.Equal(t, expectedMetrics.PositionSizeAtUpper.String(), metrics.PositionSizeAtUpper.Round(3).String())
assert.Equal(t, expectedMetrics.PositionSizeAtLower.String(), metrics.PositionSizeAtLower.Round(3).String())
assert.False(t, metrics.TooWideLower)
assert.False(t, metrics.TooWideUpper)
}
Loading

0 comments on commit e3c30e0

Please sign in to comment.