Skip to content

Commit

Permalink
Merge pull request #11727 from vegaprotocol/amm-combine-expanded-for-…
Browse files Browse the repository at this point in the history
…uncross

feat: combine AMM uncrossing orders to increase performance an reduce…
  • Loading branch information
jeremyletang authored Oct 1, 2024
2 parents 7949197 + 0f99c0a commit 0e458e3
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [11714](https://github.com/vegaprotocol/vega/issues/11714) - Improve `AMM` performance by caching best prices and volumes.
- [11642](https://github.com/vegaprotocol/vega/issues/11642) - `AMMs` with empty price levels are now allowed.
- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added.
- [11726](https://github.com/vegaprotocol/vega/issues/11726) - Combined `AMM` uncrossing orders for better performance when uncrossing the book.
- [11711](https://github.com/vegaprotocol/vega/issues/11711) - Manage closed team membership by updating the allow list.
- [11722](https://github.com/vegaprotocol/vega/issues/11722) - Expose active protocol automated purchase identifier in market data API.

Expand Down
52 changes: 49 additions & 3 deletions core/integration/features/amm/0090-VAMM-auction.feature
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Feature: vAMM rebasing when created or amended
When the opening auction period ends for market "ETH/MAR22"
Then the following trades should be executed:
| buyer | price | size | seller | is amm |
| vamm1-id | 100 | 46 | vamm2-id | true |
| vamm1-id | 100 | 91 | vamm2-id | true |

Then the network moves ahead "1" blocks

Expand Down Expand Up @@ -406,7 +406,7 @@ Feature: vAMM rebasing when created or amended
When the opening auction period ends for market "ETH/MAR22"
Then the following trades should be executed:
| buyer | price | size | seller | is amm |
| vamm2-id | 100 | 46 | vamm1-id | true |
| vamm2-id | 100 | 91 | vamm1-id | true |


Then the network moves ahead "1" blocks
Expand Down Expand Up @@ -548,4 +548,50 @@ Feature: vAMM rebasing when created or amended

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 |
| 155 | TRADING_MODE_CONTINUOUS | 109 | 0 |


@VAMM3
Scenario: Two AMMs crossed with large order expansion

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

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

Then the parties submit the following AMM:
| party | market id | amount | slippage | base | lower bound | upper bound | proposed fee |
| vamm2 | ETH/MAR22 | 1000000 | 0.05 | 250 | 200 | 300 | 0.03 |
Then the AMM pool status should be:
| party | market id | amount | status | base | lower bound | upper bound |
| vamm2 | ETH/MAR22 | 1000000 | STATUS_ACTIVE | 250 | 200 | 300 |


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

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

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

# note that this is one big order instead of a hundred small orders
Then the following trades should be executed:
| buyer | price | size | seller | is amm |
| vamm2-id | 201 | 2238 | vamm1-id | true |

Then the network moves ahead "1" blocks

# two AMMs are now prices at ~100 which is between their base values
And the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | best bid price | best offer price |
| 201 | TRADING_MODE_CONTINUOUS | 202 | 204 |
21 changes: 20 additions & 1 deletion core/matching/indicative_price_and_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,30 @@ func (ipv *IndicativePriceAndVolume) ExtractOffbookOrders(price *num.Uint, side
cpm = func(p *num.Uint) bool { return p.GT(price) }
}

var combined *types.Order
for _, o := range oo {
if cpm(o.Price) {
continue
}
orders = append(orders, o)

// we want to combine all the uncrossing orders into one big one of the combined volume so that
// we only uncross with 1 order and not 1000s of expanded ones for a single AMM. We can take the price
// to the best of the lot so that it trades -- it'll get overridden by the uncrossing price after uncrossing
// anyway.
if combined == nil {
combined = o.Clone()
orders = append(orders, combined)
} else {
combined.Size += o.Size
combined.Remaining += o.Remaining

if side == types.SideBuy {
combined.Price = num.Max(combined.Price, o.Price)
} else {
combined.Price = num.Min(combined.Price, o.Price)
}
}

volume += o.Size

// if we're extracted enough we can stop now
Expand Down
49 changes: 24 additions & 25 deletions core/matching/orderbook_amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,17 +305,19 @@ func TestIndicativeTradesAMMOnly(t *testing.T) {
expectCrossedAMMs(t, tst, 100, 150)
tst.book.EnterAuction()

ret := []*types.Order{createOrder(t, tst, 100, num.NewUint(100))}
ret := createOrder(t, tst, 10, num.NewUint(100))
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(o *types.Order, _, _ *num.Uint) []*types.Order {
o.Remaining = 0
return ret
ret.Size = o.Remaining
ret.Remaining = o.Remaining
return []*types.Order{ret.Clone()}
},
)

trades, err := tst.book.GetIndicativeTrades()
require.NoError(t, err)
assert.Equal(t, 26, len(trades))
assert.Equal(t, 1, len(trades))
assert.Equal(t, 260, int(trades[0].Size))
}

func TestIndicativeTradesAMMOrderbookNotCrosses(t *testing.T) {
Expand All @@ -337,17 +339,19 @@ func TestIndicativeTradesAMMOrderbookNotCrosses(t *testing.T) {
_, err = tst.book.SubmitOrder(o)
require.NoError(t, err)

ret := []*types.Order{createOrder(t, tst, 100, num.NewUint(100))}
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).Times(26).DoAndReturn(
ret := createOrder(t, tst, 10, num.NewUint(100))
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(o *types.Order, _, _ *num.Uint) []*types.Order {
o.Remaining = 0
return ret
ret.Size = o.Remaining
ret.Remaining = o.Remaining
return []*types.Order{ret.Clone()}
},
)

trades, err := tst.book.GetIndicativeTrades()
require.NoError(t, err)
assert.Equal(t, 26, len(trades))
assert.Equal(t, 1, len(trades))
assert.Equal(t, 260, int(trades[0].Size))
}

func TestIndicativeTradesAMMCrossedOrders(t *testing.T) {
Expand All @@ -358,34 +362,29 @@ func TestIndicativeTradesAMMCrossedOrders(t *testing.T) {
expectCrossedAMMs(t, tst, 100, 150)
tst.book.EnterAuction()

// submit an order each side outside of the crossed region
o := createOrder(t, tst, 10, num.NewUint(110))
o.Side = types.SideBuy
_, err := tst.book.SubmitOrder(o)
require.NoError(t, err)

o = createOrder(t, tst, 5, num.NewUint(125))
o := createOrder(t, tst, 5, num.NewUint(125))
o.Side = types.SideSell
_, err = tst.book.SubmitOrder(o)
_, err := tst.book.SubmitOrder(o)
require.NoError(t, err)

o = createOrder(t, tst, 5, num.NewUint(126))
o.Side = types.SideSell
_, err = tst.book.SubmitOrder(o)
require.NoError(t, err)

ret := []*types.Order{createOrder(t, tst, 100, num.NewUint(100))}
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(ret)
ret := createOrder(t, tst, 250, num.NewUint(100))
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]*types.Order{ret.Clone()})

ret = []*types.Order{createOrder(t, tst, 5, num.NewUint(100))}
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(ret)

ret = []*types.Order{createOrder(t, tst, 100, num.NewUint(100))}
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).Times(23).Return(ret)
tst.obs.EXPECT().SubmitOrder(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil)

trades, err := tst.book.GetIndicativeTrades()
require.NoError(t, err)
assert.Equal(t, 27, len(trades))
assert.Equal(t, 3, len(trades))
var total int
for _, t := range trades {
total += int(t.Size)
}
assert.Equal(t, 260, total)
}

func TestUncrossedBookDoesNotExpandAMMs(t *testing.T) {
Expand Down

0 comments on commit 0e458e3

Please sign in to comment.