Skip to content

Commit

Permalink
Merge pull request #11660 from vegaprotocol/release/v0.78.2
Browse files Browse the repository at this point in the history
Release/v0.78.2
  • Loading branch information
jeremyletang authored Sep 5, 2024
2 parents 7b08a4d + 271ebc7 commit 414ab7f
Show file tree
Hide file tree
Showing 38 changed files with 5,363 additions and 4,773 deletions.
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@
- [](https://github.com/vegaprotocol/vega/issues/xxx)


## 0.78.2

### 🛠 Improvements

- [11644](https://github.com/vegaprotocol/vega/issues/11644) - `liveOnly` flag has been added to the `AMM` API to show only active `AMMs`.

### 🐛 Fixes

- [11652](https://github.com/vegaprotocol/vega/issues/11652) - Apply fees and discounts on network disposal trades.
- [11645](https://github.com/vegaprotocol/vega/issues/11645) - Support party stats with no markets to retrieve for all markets.
- [11655](https://github.com/vegaprotocol/vega/issues/11655) - Liquidity fees paid does not like type derived party flag.
- [11650](https://github.com/vegaprotocol/vega/issues/11650) - Add include sub accounts flag to `listPositions`.
- [11641](https://github.com/vegaprotocol/vega/issues/11641) - Panic with pegged orders.
- [11646](https://github.com/vegaprotocol/vega/issues/11646) - Add tier numbers to API.


## 0.78.1

### 🐛 Fixes
Expand All @@ -28,7 +44,6 @@
- [11624](https://github.com/vegaprotocol/vega/issues/11624) - prevent creation of rewards with no payout, but with high computational cost.
- [11619](https://github.com/vegaprotocol/vega/issues/11619) - Fix `EstimatePositions` API for capped futures.


## 0.78.0

### 🛠 Improvements
Expand Down
53 changes: 12 additions & 41 deletions core/execution/amm/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,18 @@ func (e *Engine) GetVolumeAtPrice(price *num.Uint, side types.Side) uint64 {
vol := uint64(0)
for _, pool := range e.poolsCpy {
// get the pool's current price
fp := pool.BestPrice(nil)
volume := pool.TradableVolumeInRange(side, fp, price)
best := pool.BestPrice(&types.Order{Price: price, Side: side})

// make sure price is in tradable range
if side == types.SideBuy && best.GT(price) {
continue
}

if side == types.SideSell && best.LT(price) {
continue
}

volume := pool.TradableVolumeForPrice(side, price)
vol += volume
}
return vol
Expand Down Expand Up @@ -639,45 +649,15 @@ func (e *Engine) Create(
subAccount := DeriveAMMParty(submit.Party, submit.MarketID, V1, 0)
_, ok := e.pools[submit.Party]
if ok {
e.broker.Send(
events.NewAMMPoolEvent(
ctx, submit.Party, e.marketID, subAccount, poolID,
submit.CommitmentAmount, submit.Parameters,
types.AMMPoolStatusRejected, types.AMMStatusReasonPartyAlreadyOwnsAPool,
submit.ProposedFee, nil, nil,
),
)

return nil, ErrPartyAlreadyOwnAPool(e.marketID)
}

if err := e.ensureCommitmentAmount(ctx, submit.Party, subAccount, submit.CommitmentAmount); err != nil {
reason := types.AMMStatusReasonCannotFillCommitment
if err == ErrCommitmentTooLow {
reason = types.AMMStatusReasonCommitmentTooLow
}
e.broker.Send(
events.NewAMMPoolEvent(
ctx, submit.Party, e.marketID, subAccount, poolID,
submit.CommitmentAmount, submit.Parameters,
types.AMMPoolStatusRejected, reason,
submit.ProposedFee, nil, nil,
),
)
return nil, err
}

_, _, err := e.collateral.CreatePartyAMMsSubAccounts(ctx, submit.Party, subAccount, e.assetID, submit.MarketID)
if err != nil {
e.broker.Send(
events.NewAMMPoolEvent(
ctx, submit.Party, e.marketID, subAccount, poolID,
submit.CommitmentAmount, submit.Parameters,
types.AMMPoolStatusRejected, types.AMMStatusReasonUnspecified,
submit.ProposedFee, nil, nil,
),
)

return nil, err
}

Expand All @@ -698,15 +678,6 @@ func (e *Engine) Create(
e.maxCalculationLevels,
)
if err != nil {
e.broker.Send(
events.NewAMMPoolEvent(
ctx, submit.Party, e.marketID, subAccount, poolID,
submit.CommitmentAmount, submit.Parameters,
types.AMMPoolStatusRejected, types.AMMStatusReasonCommitmentTooLow,
submit.ProposedFee, nil, nil,
),
)

return nil, err
}

Expand Down
37 changes: 36 additions & 1 deletion core/execution/amm/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func TestAmendAMM(t *testing.T) {
t.Run("test amend AMM which doesn't exist", testAmendAMMWhichDoesntExist)
t.Run("test amend AMM with sparse amend", testAmendAMMSparse)
t.Run("test amend AMM insufficient commitment", testAmendInsufficientCommitment)
t.Run("test amend AMM when position to large", testAmendWhenPositionLarge)
}

func TestClosingAMM(t *testing.T) {
Expand Down Expand Up @@ -125,6 +126,7 @@ func testAmendAMMSparse(t *testing.T) {
amend.Parameters.UpperBound.AddSum(num.UintOne())
amend.Parameters.LowerBound.AddSum(num.UintOne())

ensurePosition(t, tst.pos, 0, nil)
updated, _, err := tst.engine.Amend(ctx, amend, riskFactors, scalingFactors, slippage)
require.NoError(t, err)

Expand Down Expand Up @@ -158,6 +160,39 @@ func testAmendInsufficientCommitment(t *testing.T) {
assert.Equal(t, poolID, tst.engine.poolsCpy[0].ID)
}

func testAmendWhenPositionLarge(t *testing.T) {
ctx := context.Background()
tst := getTestEngine(t)

party, subAccount := getParty(t, tst)
submit := getPoolSubmission(t, party, tst.marketID)
expectSubaccountCreation(t, tst, party, subAccount)
whenAMMIsSubmitted(t, tst, submit)

poolID := tst.engine.poolsCpy[0].ID

amend := getPoolAmendment(t, party, tst.marketID)

// lower commitment so that the AMM's position at the same price bounds will be less
amend.CommitmentAmount = num.NewUint(50000000000)

expectBalanceChecks(t, tst, party, subAccount, 100000000000)
ensurePosition(t, tst.pos, 20000000, nil)
_, _, err := tst.engine.Amend(ctx, amend, riskFactors, scalingFactors, slippage)
require.ErrorContains(t, err, "current position is outside of amended bounds")

// check that the original pool still exists
assert.Equal(t, poolID, tst.engine.poolsCpy[0].ID)

expectBalanceChecks(t, tst, party, subAccount, 100000000000)
ensurePosition(t, tst.pos, -20000000, nil)
_, _, err = tst.engine.Amend(ctx, amend, riskFactors, scalingFactors, slippage)
require.ErrorContains(t, err, "current position is outside of amended bounds")

// check that the original pool still exists
assert.Equal(t, poolID, tst.engine.poolsCpy[0].ID)
}

func testBasicSubmitOrder(t *testing.T) {
tst := getTestEngine(t)

Expand Down Expand Up @@ -559,7 +594,7 @@ func testAmendMakesClosingPoolActive(t *testing.T) {

amend := getPoolAmendment(t, party, tst.marketID)
expectBalanceChecks(t, tst, party, subAccount, amend.CommitmentAmount.Uint64())

ensurePosition(t, tst.pos, 0, num.UintZero())
updated, _, err := tst.engine.Amend(ctx, amend, riskFactors, scalingFactors, slippage)
require.NoError(t, err)
tst.engine.Confirm(ctx, updated)
Expand Down
31 changes: 31 additions & 0 deletions core/execution/amm/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,21 @@ func (p *Pool) IntoProto() *snapshotpb.PoolMapEntry_Pool {
}
}

// checkPosition will return false if its position exists outside of the curve boundaries and so the AMM
// is invalid.
func (p *Pool) checkPosition() bool {
pos := p.getPosition()
if pos > p.lower.pv.IntPart() {
return false
}

if -pos > p.upper.pv.IntPart() {
return false
}

return true
}

// Update returns a copy of the give pool but with its curves and parameters update as specified by `amend`.
func (p *Pool) Update(
amend *types.AmendAMM,
Expand Down Expand Up @@ -372,6 +387,11 @@ func (p *Pool) Update(
if err := updated.setCurves(rf, sf, linearSlippage); err != nil {
return nil, err
}

if !updated.checkPosition() {
return nil, errors.New("AMM's current position is outside of amended bounds - reduce position first")
}

return updated, nil
}

Expand Down Expand Up @@ -661,6 +681,17 @@ func (p *Pool) TradableVolumeInRange(side types.Side, price1 *num.Uint, price2 *
return num.MinV(uint64(stP-ndP), uint64(num.AbsV(pos)))
}

// TrableVolumeForPrice returns the volume available between the AMM's fair-price and the given
// price and side of an incoming order. It is a special case of TradableVolumeInRange with
// the benefit of accurately using the AMM's position instead of having to calculate the hop
// from fair-price -> position.
func (p *Pool) TradableVolumeForPrice(side types.Side, price *num.Uint) uint64 {
if side == types.SideSell {
return p.TradableVolumeInRange(side, price, nil)
}
return p.TradableVolumeInRange(side, nil, price)
}

// getBalance returns the total balance of the pool i.e it's general account + it's margin account.
func (p *Pool) getBalance() *num.Uint {
general, err := p.collateral.GetPartyGeneralAccount(p.AMMParty, p.asset)
Expand Down
64 changes: 64 additions & 0 deletions core/execution/amm/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (

func TestAMMPool(t *testing.T) {
t.Run("test volume between prices", testTradeableVolumeInRange)
t.Run("test volume between prices when AMM closing", testTradeableVolumeInRangeClosing)
t.Run("test tradable volume at price", testTradableVolumeAtPrice)
t.Run("test best price", testBestPrice)
t.Run("test pool logic with position factor", testPoolPositionFactor)
t.Run("test one sided pool", testOneSidedPool)
Expand Down Expand Up @@ -255,6 +257,64 @@ func testTradeableVolumeInRangeClosing(t *testing.T) {
}
}

func testTradableVolumeAtPrice(t *testing.T) {
p := newTestPool(t)
defer p.ctrl.Finish()

tests := []struct {
name string
price *num.Uint
position int64
side types.Side
expectedVolume uint64
}{
{
name: "full volume upper curve",
price: num.NewUint(2200),
side: types.SideBuy,
expectedVolume: 635,
},
{
name: "full volume lower curve",
price: num.NewUint(1800),
side: types.SideSell,
expectedVolume: 702,
},
{
name: "no volume upper, wrong side",
price: num.NewUint(2200),
side: types.SideSell,
expectedVolume: 0,
},
{
name: "no volume lower, wrong side",
price: num.NewUint(1800),
side: types.SideBuy,
expectedVolume: 0,
},
{
name: "no volume at fair-price buy",
price: num.NewUint(2000),
side: types.SideBuy,
expectedVolume: 0,
},
{
name: "no volume at fair-price sell",
price: num.NewUint(2000),
side: types.SideSell,
expectedVolume: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ensurePositionN(t, p.pos, tt.position, num.UintZero(), 1)
volume := p.pool.TradableVolumeForPrice(tt.side, tt.price)
assert.Equal(t, int(tt.expectedVolume), int(volume))
})
}
}

func TestTradeableVolumeWhenAtBoundary(t *testing.T) {
// from ticket 11389 this replicates a scenario found during fuzz testing
submit := &types.SubmitAMM{
Expand Down Expand Up @@ -488,6 +548,10 @@ func newBasicPoolWithSubmit(t *testing.T, submit *types.SubmitAMM) (*Pool, error
col := mocks.NewMockCollateral(ctrl)
pos := mocks.NewMockPosition(ctrl)

pos.EXPECT().GetPositionsByParty(gomock.Any()).AnyTimes().Return(
[]events.MarketPosition{&marketPosition{size: 0, averageEntry: nil}},
)

sqrter := &Sqrter{cache: map[string]num.Decimal{}}

return NewPool(
Expand Down
16 changes: 14 additions & 2 deletions core/execution/amm/shape.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,21 @@ func (sm *shapeMaker) makeBoundaryOrder(st, nd *num.Uint) *types.Order {
cu = sm.pool.upper
}

// if one of the boundaries it equal to the fair-price then the equivalent position
// if the AMM's current position and checking removes the risk of precision loss
stPosition := sm.pos
if st.NEQ(sm.fairPrice) {
stPosition = cu.positionAtPrice(sm.pool.sqrt, st)
}

ndPosition := sm.pos
if nd.NEQ(sm.fairPrice) {
ndPosition = cu.positionAtPrice(sm.pool.sqrt, nd)
}

volume := num.DeltaV(
cu.positionAtPrice(sm.pool.sqrt, st),
cu.positionAtPrice(sm.pool.sqrt, nd),
stPosition,
ndPosition,
)

if st.GTE(sm.fairPrice) {
Expand Down
2 changes: 1 addition & 1 deletion core/execution/future/liquidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error {
return nil
}
// transfer fees to the good party -> fees are now taken from the insurance pool
fees, _ := m.fee.GetFeeForPositionResolution(conf.Trades)
fees, _ := m.fee.GetFeeForPositionResolution(conf.Trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService)
tresps, err := m.collateral.TransferFees(ctx, m.GetID(), m.settlementAsset, fees)
if err != nil {
// we probably should reject the order, although if we end up here we have a massive problem.
Expand Down
16 changes: 15 additions & 1 deletion core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,20 @@ func (m *Market) getNewPeggedPrice(order *types.Order) (*num.Uint, error) {

// we're converting both offset and tick size to asset decimals so we can adjust the price (in asset) directly
priceInMarket, _ := num.UintFromDecimal(price.ToDecimal().Div(m.priceFactor))

// if the pegged offset is zero and the reference price is non-tick size (from an AMM) then we have to move it so it
// is otherwise the pegged will cross.
if order.PeggedOrder.Offset.IsZero() {
if mod := num.UintZero().Mod(priceInMarket, m.mkt.TickSize); !mod.IsZero() {
if order.Side == types.SideBuy {
priceInMarket.Sub(priceInMarket, mod)
} else {
d := num.UintOne().Sub(m.mkt.TickSize, mod)
priceInMarket.AddSum(d)
}
}
}

if order.Side == types.SideSell {
priceInMarket.AddSum(order.PeggedOrder.Offset)
// this can only happen when pegged to mid, in which case we want to round to the nearest *better* tick size
Expand Down Expand Up @@ -5362,7 +5376,7 @@ func (m *Market) getRebasingOrder(
Walk:
for {
// get the tradable volume necessary to move the AMM's position from fair-price -> price
required := pool.TradableVolumeInRange(types.OtherSide(side), fairPrice, price)
required := pool.TradableVolumeForPrice(types.OtherSide(side), price)

// AMM is close enough to the target that is has no volume between, so we do not need to rebase
if required == 0 {
Expand Down
Loading

0 comments on commit 414ab7f

Please sign in to comment.