Skip to content

Commit 8f33438

Browse files
authored
Merge pull request #11281 from vegaprotocol/fix-tradable-volume
fix: AMM tradable-volume query should respect its fair-price
2 parents e0bd34c + 00c0c6a commit 8f33438

File tree

4 files changed

+58
-26
lines changed

4 files changed

+58
-26
lines changed

core/execution/amm/engine_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,10 @@ func testSubmitOrderAcrossAMMBoundary(t *testing.T) {
265265

266266
// second round, 2 orders moving all pool's to the upper boundary of the second shortest
267267
assert.Equal(t, "2124", orders[3].Price.String())
268-
assert.Equal(t, "2125", orders[4].Price.String())
268+
assert.Equal(t, "2124", orders[4].Price.String())
269269

270270
// third round, 1 orders moving the last pool to its boundary
271-
assert.Equal(t, "2175", orders[5].Price.String())
271+
assert.Equal(t, "2174", orders[5].Price.String())
272272
}
273273

274274
func testSubmitOrderAcrossAMMBoundarySell(t *testing.T) {
@@ -333,7 +333,7 @@ func testBestPricesAndVolume(t *testing.T) {
333333
whenAMMIsSubmitted(t, tst, submit)
334334
}
335335

336-
tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(9).Return(
336+
tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).AnyTimes().Return(
337337
[]events.MarketPosition{&marketPosition{size: 0, averageEntry: num.NewUint(0)}},
338338
)
339339

@@ -344,9 +344,6 @@ func testBestPricesAndVolume(t *testing.T) {
344344
assert.Equal(t, 35781, int(avolume))
345345

346346
// test GetVolumeAtPrice returns the same volume given best bid/ask
347-
tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(6 * 2).Return(
348-
[]events.MarketPosition{&marketPosition{size: 0, averageEntry: num.NewUint(0)}},
349-
)
350347
bvAt := tst.engine.GetVolumeAtPrice(bid, types.SideSell)
351348
assert.Equal(t, bvolume, bvAt)
352349
avAt := tst.engine.GetVolumeAtPrice(ask, types.SideBuy)

core/execution/amm/pool.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,17 @@ func (p *Pool) TradableVolumeInRange(side types.Side, price1 *num.Uint, price2 *
572572
st, nd = nd, st
573573
}
574574

575+
fp := p.fairPrice()
576+
if side == types.SideSell {
577+
// want all buy volume so everything below fair price
578+
nd = num.Min(fp, nd)
579+
}
580+
581+
if side == types.SideBuy {
582+
// want all sell volume so everything above fair price
583+
st = num.Max(fp, st)
584+
}
585+
575586
var other *curve
576587
var volume uint64
577588
// get the curve based on the pool's current position, if the position is zero we take the curve the trade will put us in

core/execution/amm/pool_test.go

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,42 @@ func testTradeableVolumeInRange(t *testing.T) {
7878
expectedVolume: 702,
7979
},
8080
{
81-
name: "trade causes sign to flip and volume crosses curves",
81+
name: "buy trade causes sign to flip and full volume crosses curves",
82+
price1: num.NewUint(500),
83+
price2: num.NewUint(3500),
84+
side: types.SideBuy,
85+
expectedVolume: 1337,
86+
position: 700, // position at full lower boundary, incoming is by so whole volume of both curves is available
87+
},
88+
{
89+
name: "sell trade causes sign to flip and full volume crosses curves",
90+
price1: num.NewUint(500),
91+
price2: num.NewUint(3500),
92+
side: types.SideSell,
93+
expectedVolume: 1337,
94+
position: -700, // position at full upper boundary, incoming is by so whole volume of both curves is available
95+
},
96+
{
97+
name: "buy trade causes sign to flip and partial volume across both curves",
98+
price1: num.NewUint(500),
99+
price2: num.NewUint(3500),
100+
side: types.SideBuy,
101+
expectedVolume: 986,
102+
position: 350,
103+
},
104+
{
105+
name: "sell trade causes sign to flip and partial volume across both curves",
82106
price1: num.NewUint(500),
83107
price2: num.NewUint(3500),
84108
side: types.SideSell,
85-
expectedVolume: 1337, // 635 + 702
86-
position: 10,
109+
expectedVolume: 1053,
110+
position: -350,
87111
},
88112
}
89113

90114
for _, tt := range tests {
91115
t.Run(tt.name, func(t *testing.T) {
92-
ensurePosition(t, p.pos, tt.position, num.UintZero())
116+
ensurePositionN(t, p.pos, tt.position, num.UintZero(), 2)
93117
volume := p.pool.TradableVolumeInRange(tt.side, tt.price1, tt.price2)
94118
assert.Equal(t, int(tt.expectedVolume), int(volume))
95119
})
@@ -100,17 +124,17 @@ func testPoolPositionFactor(t *testing.T) {
100124
p := newTestPoolWithPositionFactor(t, num.DecimalFromInt64(1000))
101125
defer p.ctrl.Finish()
102126

103-
ensurePosition(t, p.pos, 0, num.UintZero())
127+
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
104128
volume := p.pool.TradableVolumeInRange(types.SideBuy, num.NewUint(2000), num.NewUint(2200))
105129
// with position factot of 1 the volume is 635
106130
assert.Equal(t, int(635395), int(volume))
107131

108-
ensurePosition(t, p.pos, 0, num.UintZero())
132+
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
109133
volume = p.pool.TradableVolumeInRange(types.SideSell, num.NewUint(1800), num.NewUint(2000))
110134
// with position factot of 1 the volume is 702
111135
assert.Equal(t, int(702411), int(volume))
112136

113-
ensurePosition(t, p.pos, -1, num.NewUint(2000))
137+
ensurePositionN(t, p.pos, -1, num.NewUint(2000), 1)
114138
// now best price should be the same as if the factor were 1, since its a price and not a volume
115139
fairPrice := p.pool.BestPrice(nil)
116140
assert.Equal(t, "2001", fairPrice.String())
@@ -182,30 +206,30 @@ func testOneSidedPool(t *testing.T) {
182206
defer p.ctrl.Finish()
183207

184208
// side with liquidity returns volume
185-
ensurePosition(t, p.pos, 0, num.UintZero())
209+
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
186210
volume := p.pool.TradableVolumeInRange(types.SideBuy, num.NewUint(2000), num.NewUint(2200))
187211
assert.Equal(t, int(635), int(volume))
188212

189213
// empty side returns no volume
190-
ensurePosition(t, p.pos, 0, num.UintZero())
214+
ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
191215
volume = p.pool.TradableVolumeInRange(types.SideSell, num.NewUint(1800), num.NewUint(2000))
192216
assert.Equal(t, int(0), int(volume))
193217

194218
// pool with short position and incoming sell only reports volume up to base
195219
// empty side returns no volume
196-
ensurePosition(t, p.pos, -10, num.UintZero())
220+
ensurePositionN(t, p.pos, -10, num.UintZero(), 2)
197221
volume = p.pool.TradableVolumeInRange(types.SideSell, num.NewUint(1800), num.NewUint(2200))
198-
assert.Equal(t, int(635), int(volume))
222+
assert.Equal(t, int(10), int(volume))
199223

200224
// fair price at 0 position is still ok
201225
ensurePosition(t, p.pos, 0, num.UintZero())
202226
price := p.pool.BestPrice(nil)
203-
assert.Equal(t, price.String(), "2000")
227+
assert.Equal(t, "2000", price.String())
204228

205229
// fair price at short position is still ok
206230
ensurePosition(t, p.pos, -10, num.UintZero())
207231
price = p.pool.BestPrice(nil)
208-
assert.Equal(t, price.String(), "2003")
232+
assert.Equal(t, "2003", price.String())
209233

210234
// fair price when long should panic since AMM should never be able to get into that state
211235
// fair price at short position is still ok
@@ -354,22 +378,22 @@ func TestNotebook(t *testing.T) {
354378

355379
pos := int64(0)
356380

357-
ensurePosition(t, p.pos, pos, num.UintZero())
381+
ensurePositionN(t, p.pos, pos, num.UintZero(), 2)
358382
volume := p.pool.TradableVolumeInRange(types.SideSell, base, low)
359383
assert.Equal(t, int(702), int(volume))
360384

361-
ensurePosition(t, p.pos, pos, num.UintZero())
385+
ensurePositionN(t, p.pos, pos, num.UintZero(), 2)
362386
volume = p.pool.TradableVolumeInRange(types.SideBuy, up, base)
363387
assert.Equal(t, int(635), int(volume))
364388

365389
lowmid := num.NewUint(1900)
366390
upmid := num.NewUint(2100)
367391

368-
ensurePosition(t, p.pos, pos, num.UintZero())
392+
ensurePositionN(t, p.pos, pos, num.UintZero(), 2)
369393
volume = p.pool.TradableVolumeInRange(types.SideSell, low, lowmid)
370394
assert.Equal(t, int(365), int(volume))
371395

372-
ensurePosition(t, p.pos, pos, num.UintZero())
396+
ensurePositionN(t, p.pos, pos, num.UintZero(), 2)
373397
volume = p.pool.TradableVolumeInRange(types.SideBuy, upmid, up)
374398
assert.Equal(t, int(306), int(volume))
375399

core/integration/features/amm/0090-VAMM-006-014.feature

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ Feature: Ensure the vAMM positions follow the market correctly
479479
| 95 | TRADING_MODE_CONTINUOUS | 100 | 120 | 120 | 121 | 119 |
480480
And the following trades should be executed:
481481
| buyer | price | size | seller | is amm |
482-
| party5 | 114 | 65 | vamm1-id | true |
482+
| party5 | 114 | 64 | vamm1-id | true |
483483
# Check the resulting position, vAMM further increased their position
484484
When the network moves ahead "1" blocks
485485
Then the parties should have the following profit and loss:
@@ -488,5 +488,5 @@ Feature: Ensure the vAMM positions follow the market correctly
488488
| party2 | -1 | -14 | 0 | |
489489
| party3 | -350 | -3150 | 0 | |
490490
| party4 | 420 | 7980 | 0 | |
491-
| party5 | 65 | 0 | 0 | |
492-
| vamm1-id | -135 | -1330 | -3500 | true |
491+
| party5 | 64 | 0 | 0 | |
492+
| vamm1-id | -134 | -1330 | -3500 | true |

0 commit comments

Comments
 (0)