Skip to content

Commit

Permalink
Merge pull request #11668 from vegaprotocol/stop-amm-md-looking-crossed
Browse files Browse the repository at this point in the history
fix: market-depth for AMM's when stepping over fair-price adds volume…
  • Loading branch information
wwestgarth authored Sep 10, 2024
2 parents 873fb3f + ea8279a commit 311e11f
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 24 deletions.
26 changes: 20 additions & 6 deletions datanode/service/market_depth_amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ func (m *MarketDepth) expandByLevels(pool entities.AMMPool, levels []*level, pri

estimated := []bool{}
orders := []*types.Order{}

extraVolume := int64(0)
for i := range levels {
if i == len(levels)-1 {
break
Expand Down Expand Up @@ -296,20 +298,32 @@ func (m *MarketDepth) expandByLevels(pool entities.AMMPool, levels []*level, pri
side = types.SideSell
retPrice = level2.price

// if we've stepped over the pool's position we need to split the step
// if we've stepped over the pool's position we need to split the volume and add it to the outer levels
if v1.GreaterThan(ammDefn.position) {
volume := v1.Sub(ammDefn.position).Abs().IntPart()
o := m.makeOrder(level1.price, ammDefn.partyID, uint64(volume), types.SideBuy)
orders = append(orders, o)
estimated = append(estimated, level1.estimated)

// now set v1 ready for the second half of this split
v1 = ammDefn.position
// we want to add the volume to the previous order, because thats the price in marketDP when rounded away
// from the fair-price
if len(orders) != 0 {
o := orders[len(orders)-1]
o.Size += uint64(volume)
o.Remaining += uint64(volume)
}

// we need to add this volume to the price level we step to next
extraVolume = ammDefn.position.Sub(v2).Abs().IntPart()
continue
}
}
// calculate the volume
volume := v1.Sub(v2).Abs().IntPart()

// this is extra volume from when we stepped over the AMM's fair-price
if extraVolume != 0 {
volume += extraVolume
extraVolume = 0
}

orders = append(
orders,
m.makeOrder(retPrice, ammDefn.partyID, uint64(volume), side),
Expand Down
79 changes: 61 additions & 18 deletions datanode/service/market_depth_amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func Test_0015_NP_OBES_002(t *testing.T) {
marketID := vgcrypto.RandomHash()

mds.orders.EXPECT().GetLiveOrders(gomock.Any()).Return([]entities.Order{}, nil)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)

// mid-price is 100
Expand Down Expand Up @@ -143,7 +143,7 @@ func TestAMMMarketDepth(t *testing.T) {
marketID := vgcrypto.RandomHash()

ensureLiveOrders(t, mds, marketID)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)

Expand Down Expand Up @@ -180,7 +180,7 @@ func TestAMMMarketDepth(t *testing.T) {
assert.Equal(t, 120, int(mds.service.GetAMMVolume(marketID, false)))
assert.Equal(t, 260, int(mds.service.GetTotalVolume(marketID)))

assert.Equal(t, "1996", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "1995", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "1998", mds.service.GetBestAskPrice(marketID).String())

// now the AMM is updated so that its definition has changed, namely that its curve when short is removed
Expand All @@ -194,7 +194,7 @@ func TestAMMMarketDepth(t *testing.T) {
assert.Equal(t, 65, int(mds.service.GetAMMVolume(marketID, true)))
assert.Equal(t, 60, int(mds.service.GetAMMVolume(marketID, false)))
assert.Equal(t, 145, int(mds.service.GetTotalVolume(marketID)))
assert.Equal(t, "1996", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "1995", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "1998", mds.service.GetBestAskPrice(marketID).String())

// and there should definitely be no volume at 2001
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestAMMInitialiseNoAMM(t *testing.T) {
newMarket := vgcrypto.RandomHash()
pool := getAMMDefinition(t, newMarket)

ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)
mds.service.OnAMMUpdate(pool, time.Now(), 1000)
Expand All @@ -252,7 +252,7 @@ func TestAMMStepOverFairPrice(t *testing.T) {

marketID := vgcrypto.RandomHash()
ensureLiveOrders(t, mds, marketID)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)

Expand Down Expand Up @@ -280,11 +280,12 @@ func TestAMMStepOverFairPrice(t *testing.T) {
37,
)

assert.Equal(t, "1999", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "2000", mds.service.GetBestAskPrice(marketID).String())
assert.Equal(t, 2, int(mds.service.GetVolumeAtPrice(marketID, types.SideBuy, 1999)))
assert.Equal(t, 1, int(mds.service.GetVolumeAtPrice(marketID, types.SideSell, 2000)))
assert.Equal(t, 3, int(mds.service.GetVolumeAtPrice(marketID, types.SideSell, 2001)))
assert.Equal(t, "1998", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "2001", mds.service.GetBestAskPrice(marketID).String())
assert.Equal(t, 0, int(mds.service.GetVolumeAtPrice(marketID, types.SideBuy, 1999)))
assert.Equal(t, 0, int(mds.service.GetVolumeAtPrice(marketID, types.SideSell, 2000)))
assert.Equal(t, 5, int(mds.service.GetVolumeAtPrice(marketID, types.SideBuy, 1998)))
assert.Equal(t, 4, int(mds.service.GetVolumeAtPrice(marketID, types.SideSell, 2001)))
}

func TestAMMSmallBounds(t *testing.T) {
Expand All @@ -300,7 +301,7 @@ func TestAMMSmallBounds(t *testing.T) {

marketID := vgcrypto.RandomHash()
ensureLiveOrders(t, mds, marketID)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)

Expand Down Expand Up @@ -331,7 +332,7 @@ func TestEstimatedStepOverAMMBound(t *testing.T) {

marketID := vgcrypto.RandomHash()
ensureLiveOrders(t, mds, marketID)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)

Expand Down Expand Up @@ -360,7 +361,7 @@ func TestExpansionMuchBiggerThanAMMs(t *testing.T) {
marketID := vgcrypto.RandomHash()

ensureLiveOrders(t, mds, marketID)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)

Expand All @@ -386,7 +387,7 @@ func TestMidPriceMove(t *testing.T) {
marketID := vgcrypto.RandomHash()

ensureLiveOrders(t, mds, marketID)
ensureDecimalPlaces(t, mds)
ensureDecimalPlaces(t, mds, 1, 1)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: 0}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(2000)}, nil)

Expand Down Expand Up @@ -421,6 +422,29 @@ func TestMidPriceMove(t *testing.T) {
assert.Equal(t, "3000", mds.service.GetBestAskPrice(marketID).String()) // this is an actual order volume not AMM volume
}

func TestFairgroundAMM(t *testing.T) {
ctx := context.Background()

mds := getService(t)
defer mds.ctrl.Finish()

marketID := vgcrypto.RandomHash()

mds.orders.EXPECT().GetLiveOrders(gomock.Any()).Return(nil, nil)
ensureDecimalPlaces(t, mds, 9, 5)
mds.pos.EXPECT().GetByMarketAndParty(gomock.Any(), gomock.Any(), gomock.Any()).Return(entities.Position{OpenVolume: -69005905}, nil)
mds.marketData.EXPECT().GetMarketDataByID(gomock.Any(), gomock.Any()).Times(1).Return(entities.MarketData{MidPrice: num.DecimalFromInt64(12955)}, nil)

pool := getAMMDefinitionTestnet(t, marketID)
mds.amm.EXPECT().ListActive(gomock.Any()).Return([]entities.AMMPool{pool}, nil).Times(1)
mds.service.Initialise(ctx)

// AMM's fair price is 129543034, so +/- one each side is 129533034, 129553034
// the we round *away* from the fair price and get:
assert.Equal(t, "12953", mds.service.GetBestBidPrice(marketID).String())
assert.Equal(t, "12956", mds.service.GetBestAskPrice(marketID).String())
}

func ensureLiveOrders(t *testing.T, mds *MDS, marketID string) {
t.Helper()
mds.orders.EXPECT().GetLiveOrders(gomock.Any()).Return([]entities.Order{
Expand Down Expand Up @@ -485,6 +509,25 @@ func getAMMDefinitionMid100(t *testing.T, marketID string) entities.AMMPool {
}
}

func getAMMDefinitionTestnet(t *testing.T, marketID string) entities.AMMPool {
t.Helper()

// position -69005905

return entities.AMMPool{
PartyID: entities.PartyID(vgcrypto.RandomHash()),
AmmPartyID: entities.PartyID(vgcrypto.RandomHash()),
MarketID: entities.MarketID(marketID),
ParametersLowerBound: ptr.From(num.DecimalFromInt64(11403)),
LowerVirtualLiquidity: num.DecimalFromFloat(32934372037780.849503179454583540865465761125),
LowerTheoreticalPosition: num.DecimalFromFloat(158269985.323671339473934),
ParametersBase: num.DecimalFromInt64(12670),
ParametersUpperBound: ptr.From(num.DecimalFromInt64(13937)),
UpperVirtualLiquidity: num.DecimalFromFloat(70393727154384.2551793351731482811200266360637),
UpperTheoreticalPosition: num.DecimalFromFloat(291036775.097792633711267),
}
}

func ensureAMMs(t *testing.T, mds *MDS, marketID string) entities.AMMPool {
t.Helper()

Expand All @@ -493,7 +536,7 @@ func ensureAMMs(t *testing.T, mds *MDS, marketID string) entities.AMMPool {
return pool
}

func ensureDecimalPlaces(t *testing.T, mds *MDS) {
func ensureDecimalPlaces(t *testing.T, mds *MDS, adp, mdp int) {
t.Helper()

market := entities.Market{
Expand All @@ -506,13 +549,13 @@ func ensureDecimalPlaces(t *testing.T, mds *MDS) {
},
},
},
DecimalPlaces: 1,
DecimalPlaces: mdp,
TickSize: ptr.From(num.DecimalOne()),
}
mds.markets.EXPECT().GetByID(gomock.Any(), gomock.Any()).Return(market, nil)

asset := entities.Asset{
Decimals: 1,
Decimals: adp,
}
mds.assets.EXPECT().GetByID(gomock.Any(), gomock.Any()).Return(asset, nil)
}

0 comments on commit 311e11f

Please sign in to comment.