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

Release version 0.78.1 #11640

Merged
merged 14 commits into from
Sep 3, 2024
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
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### 🚨 Breaking changes

- [8777](https://github.com/vegaprotocol/vega/issues/8777) - Stop orders can now be used to create or increase a position.
- [](https://github.com/vegaprotocol/vega/issues/xxx)

### 🗑️ Deprecation

Expand All @@ -19,6 +19,16 @@
- [](https://github.com/vegaprotocol/vega/issues/xxx)


## 0.78.1

### 🐛 Fixes

- [11633](https://github.com/vegaprotocol/vega/issues/11633) - Use bridge deployment heights from network parameter when starting network from genesis.)
- [11583](https://github.com/vegaprotocol/vega/issues/11583) - Rough bound on price interval when matching with `AMMs` is now looser and calculated in the `AMM` engine.
- [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 Expand Up @@ -47,13 +57,16 @@
- [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.
- [11486](https://github.com/vegaprotocol/vega/issues/11486) - `AMMs` can now be submitted on markets with more decimal places than asset decimal places.
- [11635](https://github.com/vegaprotocol/vega/issues/11635) - Handle expansion of one sided `AMMs` that reduce to point expansion when calculating order book shape.
- [11561](https://github.com/vegaprotocol/vega/issues/11561) - Failing amends on `AMMs` now restore original properly.
- [11583](https://github.com/vegaprotocol/vega/issues/11583) - Remove `AMMs` entirely from engine when market closes.
- [11568](https://github.com/vegaprotocol/vega/issues/11568) - order book shape on closing `AMM` no longer panics.
- [11540](https://github.com/vegaprotocol/vega/issues/11540) - Fix spam check for spots to use not double count quantum.
- [11542](https://github.com/vegaprotocol/vega/issues/11542) - Fix non determinism in lottery ranking.
- [11616](https://github.com/vegaprotocol/vega/issues/11616) - `AMM` tradable volume now calculated purely in positions to prevent loss of precision.
- [11544](https://github.com/vegaprotocol/vega/issues/11544) - Fix empty candles stream.
- [11583](https://github.com/vegaprotocol/vega/issues/11583) - Rough bound on price interval when matching with `AMMs` is now looser and calculated in the `AMM` engine.
- [11633](https://github.com/vegaprotocol/vega/issues/11633) - Use bridge deployment heights from network parameter when starting network from genesis.
- [11619](https://github.com/vegaprotocol/vega/issues/11619) - Fix `EstimatePositions` API for capped futures.
- [11579](https://github.com/vegaprotocol/vega/issues/11579) - Spot calculate fee on amend, use order price if no amended price is provided.
- [11585](https://github.com/vegaprotocol/vega/issues/11585) - Initialise rebate stats service in API.
Expand Down
8 changes: 4 additions & 4 deletions core/evtforward/ethereum/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ func NewEngine(
poller: newPoller(cfg.PollEventRetryDuration.Get()),
filterer: filterer,
forwarder: fwdWrapper{forwarder, chainID},
stakingDeployment: &Contract{stakingDeployment, 0, 0},
vestingDeployment: &Contract{vestingDeployment, 0, 0},
multisigDeployment: &Contract{multiSigDeployment, 0, 0},
collateralDeployment: &Contract{collateralDeployment, 0, 0},
stakingDeployment: &Contract{stakingDeployment, stakingDeployment.DeploymentBlockHeight(), stakingDeployment.DeploymentBlockHeight()},
vestingDeployment: &Contract{vestingDeployment, vestingDeployment.DeploymentBlockHeight(), vestingDeployment.DeploymentBlockHeight()},
multisigDeployment: &Contract{multiSigDeployment, multiSigDeployment.DeploymentBlockHeight(), multiSigDeployment.DeploymentBlockHeight()},
collateralDeployment: &Contract{collateralDeployment, collateralDeployment.DeploymentBlockHeight(), collateralDeployment.DeploymentBlockHeight()},
chainID: chainID,
heartbeatInterval: uint64(heartbeatInterval),
}
Expand Down
17 changes: 17 additions & 0 deletions core/execution/amm/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,23 @@ func (e *Engine) partition(agg *types.Order, inner, outer *num.Uint) ([]*Pool, [
}
}

if inner == nil {
// if inner is given as nil it means the matching engine is trading up to its first price level
// and so has no lower bound on the range. So we'll calculate one using best price of all pools
// note that if the incoming order is a buy the price range we need to evaluate is from
// fair-price -> best-ask -> outer, so we need to step one back. But then if we use fair-price exactly we
// risk hitting numerical problems and given this is just to exclude AMM's completely out of range we
// can be a bit looser and so step back again so that we evaluate from best-buy -> best-ask -> outer.
buy, _, ask, _ := e.BestPricesAndVolumes()
two := num.UintZero().AddSum(e.oneTick, e.oneTick)
if agg.Side == types.SideBuy && ask != nil {
inner = num.UintZero().Sub(ask, two)
}
if agg.Side == types.SideSell && buy != nil {
inner = num.UintZero().Add(buy, two)
}
}

// switch so that inner < outer to make it easier to reason with
if agg.Side == types.SideSell {
inner, outer = outer, inner
Expand Down
6 changes: 6 additions & 0 deletions core/execution/amm/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,11 +629,17 @@ func (p *Pool) TradableVolumeInRange(side types.Side, price1 *num.Uint, price2 *

if side == types.SideSell {
// want all buy volume so everything below fair price, where the AMM is long
if pos > stP {
return 0
}
ndP = num.MaxV(pos, ndP)
}

if side == types.SideBuy {
// want all sell volume so everything above fair price, where the AMM is short
if pos < ndP {
return 0
}
stP = num.MinV(pos, stP)
}

Expand Down
16 changes: 16 additions & 0 deletions core/execution/amm/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ func testTradeableVolumeInRange(t *testing.T) {
expectedVolume: 1052,
position: -350,
},
{
name: "AMM is long and price range is fully in short section",
price1: num.NewUint(1900),
price2: num.NewUint(2200),
side: types.SideSell,
expectedVolume: 0,
position: 700,
},
{
name: "AMM is short and price range is fully in long section",
price1: num.NewUint(1900),
price2: num.NewUint(2100),
side: types.SideBuy,
expectedVolume: 0,
position: -700,
},
}

for _, tt := range tests {
Expand Down
10 changes: 6 additions & 4 deletions core/execution/amm/shape.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,15 @@ func (sm *shapeMaker) adjustRegion() bool {
return false
}

if sm.from.EQ(sm.to) && sm.from.EQ(sm.fairPrice) {
return false
}

// cap the range to the pool's bounds, there will be no orders outside of this
from := num.Max(sm.from, lower)
to := num.Min(sm.to, upper)

// expansion is a point region *at* fair-price, there are no orders
if from.EQ(to) && from.EQ(sm.fairPrice) {
return false
}

switch {
case sm.from.GT(sm.fairPrice):
// if we are expanding entirely in the sell range to calculate the order at price `from`
Expand Down
34 changes: 34 additions & 0 deletions core/execution/amm/shape_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func TestOrderbookShape(t *testing.T) {
t.Run("test orderbook shape boundary order when approx", testOrderbookShapeBoundaryOrder)
t.Run("test orderbook shape region not divisible by tick", testOrderbookSubTick)
t.Run("test orderbook shape closing pool close to base", testClosingCloseToBase)
t.Run("test orderbook shape point expansion at fair price", testPointExpansionAtFairPrice)
}

func testOrderbookShapeZeroPosition(t *testing.T) {
Expand Down Expand Up @@ -416,3 +417,36 @@ func testClosingCloseToBase(t *testing.T) {
assert.Equal(t, 0, len(buys))
assert.Equal(t, 0, len(sells))
}

func testPointExpansionAtFairPrice(t *testing.T) {
p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
defer p.ctrl.Finish()

base := p.submission.Parameters.Base

// range [10, 10] fair price is 10, no orders
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
buys, sells := p.pool.OrderbookShape(base, base, nil)
assert.Equal(t, 0, len(buys))
assert.Equal(t, 0, len(sells))

// now try with a one sided curve where the input range shrinks to a point-expansion
p = newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), nil)
defer p.ctrl.Finish()

// range [10, 1000] but sell curve is empty so effective range is [10, 10] at fair-price
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
buys, sells = p.pool.OrderbookShape(base, num.NewUint(1000), nil)
assert.Equal(t, 0, len(buys))
assert.Equal(t, 0, len(sells))

// now try with a one sided curve where the input range shrinks to a point-expansion
p = newTestPoolWithRanges(t, nil, num.NewUint(10), num.NewUint(13))
defer p.ctrl.Finish()

// range [1, 10] but buy curve is empty so effective range is [10, 10] at fair-price
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
buys, sells = p.pool.OrderbookShape(num.NewUint(1), base, nil)
assert.Equal(t, 0, len(buys))
assert.Equal(t, 0, len(sells))
}
46 changes: 45 additions & 1 deletion core/integration/features/amm/0090-VAMM-auction.feature
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,48 @@ Feature: vAMM rebasing when created or amended
When the opening auction period ends for market "ETH/MAR22"
And the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | best bid price | best offer price |
| 93 | TRADING_MODE_CONTINUOUS | 92 | 94 |
| 93 | TRADING_MODE_CONTINUOUS | 92 | 94 |


@VAMM
Scenario: AMM crossed with limit order, AMM pushed to boundary


And the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | reference |
| lp1 | ETH/MAR22 | buy | 423 | 200 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b |

Then the parties submit the following AMM:
| party | market id | amount | slippage | base | lower bound | upper bound | proposed fee |
| vamm1 | ETH/MAR22 | 100000 | 0.05 | 100 | 90 | 110 | 0.03 |
Then the AMM pool status should be:
| party | market id | amount | status | base | lower bound | upper bound |
| vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | 110 |


# now place some pegged orders which will cause a panic if the uncrossing is crossed
When the parties place the following pegged orders:
| party | market id | side | volume | pegged reference | offset |
| lp3 | ETH/MAR22 | buy | 100 | BID | 1 |
| lp3 | ETH/MAR22 | sell | 100 | ASK | 1 |

And set the following AMM sub account aliases:
| party | market id | alias |
| vamm1 | ETH/MAR22 | vamm1-id |


And the market data for the market "ETH/MAR22" should be:
| trading mode | indicative price | indicative volume |
| TRADING_MODE_OPENING_AUCTION | 155 | 423 |


When the opening auction period ends for market "ETH/MAR22"

# the volume of this trade should be the entire volume of the AMM's sell curve
Then the following trades should be executed:
| buyer | price | size | seller | is amm |
| lp1 | 155 | 423 | vamm1-id | true |

And the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | best bid price | best offer price |
| 155 | TRADING_MODE_CONTINUOUS | 109 | 0 |
46 changes: 7 additions & 39 deletions core/matching/orderbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,16 +491,12 @@ func (b *OrderBook) GetIndicativeTrades() ([]*types.Trade, error) {
var (
uncrossOrders []*types.Order
uncrossingSide *OrderBookSide
uncrossBound *num.Uint
)

min, max := b.indicativePriceAndVolume.GetCrossedRegion()
if uncrossSide == types.SideBuy {
uncrossingSide = b.buy
uncrossBound = min
} else {
uncrossingSide = b.sell
uncrossBound = max
}

// extract uncrossing orders from all AMMs
Expand All @@ -513,7 +509,7 @@ func (b *OrderBook) GetIndicativeTrades() ([]*types.Trade, error) {
uncrossOrders = append(uncrossOrders, uncrossingSide.ExtractOrders(price, volume, false)...)
opSide := b.getOppositeSide(uncrossSide)
output := make([]*types.Trade, 0, len(uncrossOrders))
trades, err := opSide.fakeUncrossAuction(uncrossOrders, uncrossBound)
trades, err := opSide.fakeUncrossAuction(uncrossOrders)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -558,18 +554,12 @@ func (b *OrderBook) uncrossBook() ([]*types.OrderConfirmation, error) {
return nil, nil
}

var (
uncrossingSide *OrderBookSide
uncrossBound *num.Uint
)
var uncrossingSide *OrderBookSide

min, max := b.indicativePriceAndVolume.GetCrossedRegion()
if uncrossSide == types.SideBuy {
uncrossingSide = b.buy
uncrossBound = min
} else {
uncrossingSide = b.sell
uncrossBound = max
}

// extract uncrossing orders from all AMMs
Expand All @@ -580,15 +570,15 @@ func (b *OrderBook) uncrossBook() ([]*types.OrderConfirmation, error) {

// Remove all the orders from that side of the book up to the given volume
uncrossOrders = append(uncrossOrders, uncrossingSide.ExtractOrders(price, volume, true)...)
return b.uncrossBookSide(uncrossOrders, b.getOppositeSide(uncrossSide), price.Clone(), uncrossBound)
return b.uncrossBookSide(uncrossOrders, b.getOppositeSide(uncrossSide), price.Clone())
}

// Takes extracted order from a side of the book, and uncross them
// with the opposite side.
func (b *OrderBook) uncrossBookSide(
uncrossOrders []*types.Order,
opSide *OrderBookSide,
price, uncrossBound *num.Uint,
price *num.Uint,
) ([]*types.OrderConfirmation, error) {
var (
uncrossedOrder *types.OrderConfirmation
Expand All @@ -614,7 +604,7 @@ func (b *OrderBook) uncrossBookSide(
}

// try to get the market price value from the order
trades, affectedOrders, _, err := opSide.uncross(order, false, uncrossBound)
trades, affectedOrders, _, err := opSide.uncross(order, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -911,8 +901,7 @@ func (b *OrderBook) GetTrades(order *types.Order) ([]*types.Trade, error) {
b.latestTimestamp = order.CreatedAt
}

idealPrice := b.theoreticalBestTradePrice(order)
trades, err := b.getOppositeSide(order.Side).fakeUncross(order, true, idealPrice)
trades, err := b.getOppositeSide(order.Side).fakeUncross(order, true)
// it's fine for the error to be a wash trade here,
// it's just be stopped when really uncrossing.
if err != nil && err != ErrWashTrade {
Expand Down Expand Up @@ -961,25 +950,6 @@ func (b *OrderBook) ReSubmitSpecialOrders(order *types.Order) {
b.add(order)
}

// theoreticalBestTradePrice returns the best possible price the incoming order could trade
// as if the spread were as small as possible. This will be used to construct the first
// interval to query offbook orders matching with the other side.
func (b *OrderBook) theoreticalBestTradePrice(order *types.Order) *num.Uint {
bp, _, err := b.getSide(order.Side).BestPriceAndVolume()
if err != nil {
return nil
}

switch order.Side {
case types.SideBuy:
return bp.Add(bp, num.UintOne())
case types.SideSell:
return bp.Sub(bp, num.UintOne())
default:
panic("unexpected order side")
}
}

// SubmitOrder Add an order and attempt to uncross the book, returns a TradeSet protobuf message object.
func (b *OrderBook) SubmitOrder(order *types.Order) (*types.OrderConfirmation, error) {
if err := b.validateOrder(order); err != nil {
Expand All @@ -1006,9 +976,7 @@ func (b *OrderBook) SubmitOrder(order *types.Order) (*types.OrderConfirmation, e
// uncross with opposite

defer b.buy.uncrossFinished()

idealPrice := b.theoreticalBestTradePrice(order)
trades, impactedOrders, lastTradedPrice, err = b.getOppositeSide(order.Side).uncross(order, true, idealPrice)
trades, impactedOrders, lastTradedPrice, err = b.getOppositeSide(order.Side).uncross(order, true)
if !lastTradedPrice.IsZero() {
b.lastTradedPrice = lastTradedPrice
}
Expand Down
2 changes: 1 addition & 1 deletion core/matching/orderbook_amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func testMatchOrdersBothSide(t *testing.T) {
assert.Len(t, trades, 8)

// uncross
expectOffbookOrders(t, tst, price, num.NewUint(oPrice-1), num.NewUint(120))
expectOffbookOrders(t, tst, price, nil, num.NewUint(120))
expectOffbookOrders(t, tst, price, num.NewUint(120), num.NewUint(110))
expectOffbookOrders(t, tst, price, num.NewUint(110), num.NewUint(90))
tst.obs.EXPECT().NotifyFinished().Times(1)
Expand Down
Loading
Loading