Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement auction uncrossing for AMMs
Browse files Browse the repository at this point in the history
wwestgarth committed May 24, 2024

Verified

This commit was signed with the committer’s verified signature.
1 parent e489055 commit 6313e20
Showing 30 changed files with 1,980 additions and 738 deletions.
33 changes: 23 additions & 10 deletions commands/amend_amm.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
package commands

import (
"errors"
"math/big"

"code.vegaprotocol.io/vega/libs/num"
@@ -69,31 +70,44 @@ func checkAmendAMM(cmd *commandspb.AmendAMM) Errors {

if cmd.ConcentratedLiquidityParameters != nil {
hasUpdate = true
if amount, _ := big.NewInt(0).SetString(cmd.ConcentratedLiquidityParameters.Base, 10); amount == nil {
var base, lowerBound, upperBound *big.Int
if base, _ = big.NewInt(0).SetString(cmd.ConcentratedLiquidityParameters.Base, 10); base == nil {
errs.FinalAddForProperty("amend_amm.concentrated_liquidity_parameters.base", ErrIsNotValidNumber)
} else if amount.Cmp(big.NewInt(0)) <= 0 {
} else if base.Cmp(big.NewInt(0)) <= 0 {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.base", ErrMustBePositive)
}

var haveLower, haveUpper bool
if cmd.ConcentratedLiquidityParameters.LowerBound != nil {
hasUpdate = true
if amount, _ := big.NewInt(0).SetString(*cmd.ConcentratedLiquidityParameters.LowerBound, 10); amount == nil {
haveLower = true
if lowerBound, _ = big.NewInt(0).SetString(*cmd.ConcentratedLiquidityParameters.LowerBound, 10); lowerBound == nil {
errs.FinalAddForProperty("amend_amm.concentrated_liquidity_parameters.lower_bound", ErrIsNotValidNumber)
} else if amount.Cmp(big.NewInt(0)) <= 0 {
} else if lowerBound.Cmp(big.NewInt(0)) <= 0 {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.lower_bound", ErrMustBePositive)
}
}
if cmd.ConcentratedLiquidityParameters.UpperBound != nil {
hasUpdate = true
if amount, _ := big.NewInt(0).SetString(*cmd.ConcentratedLiquidityParameters.UpperBound, 10); amount == nil {
haveUpper = true
if upperBound, _ = big.NewInt(0).SetString(*cmd.ConcentratedLiquidityParameters.UpperBound, 10); upperBound == nil {
errs.FinalAddForProperty("amend_amm.concentrated_liquidity_parameters.upper_bound", ErrIsNotValidNumber)
} else if amount.Cmp(big.NewInt(0)) <= 0 {
} else if upperBound.Cmp(big.NewInt(0)) <= 0 {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.upper_bound", ErrMustBePositive)
}
}

if !haveLower && !haveUpper {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.lower_bound", errors.New("lower_bound and upper_bound cannot both be empty"))
}

if base != nil && lowerBound != nil && base.Cmp(lowerBound) <= 0 {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.base", errors.New("should be a bigger value than lower_bound"))
}

if base != nil && upperBound != nil && base.Cmp(upperBound) >= 0 {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.base", errors.New("should be a smaller value than upper_bound"))
}

if cmd.ConcentratedLiquidityParameters.LeverageAtUpperBound != nil {
hasUpdate = true
if leverage, err := num.DecimalFromString(*cmd.ConcentratedLiquidityParameters.LeverageAtUpperBound); err != nil {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.leverage_at_upper_bound", ErrIsNotValidNumber)
} else if leverage.LessThan(num.DecimalZero()) {
@@ -102,7 +116,6 @@ func checkAmendAMM(cmd *commandspb.AmendAMM) Errors {
}

if cmd.ConcentratedLiquidityParameters.LeverageAtLowerBound != nil {
hasUpdate = true
if leverage, err := num.DecimalFromString(*cmd.ConcentratedLiquidityParameters.LeverageAtLowerBound); err != nil {
errs.AddForProperty("amend_amm.concentrated_liquidity_parameters.leverage_at_lower_bound", ErrIsNotValidNumber)
} else if leverage.LessThan(num.DecimalZero()) {
37 changes: 37 additions & 0 deletions commands/amend_amm_test.go
Original file line number Diff line number Diff line change
@@ -270,6 +270,43 @@ func TestCheckAmendAMM(t *testing.T) {
},
errStr: "* (no updates provided)",
},
{
submission: commandspb.AmendAMM{
MarketId: "e9982447fb4128f9968f9981612c5ea85d19b62058ec2636efc812dcbbc745ca",
SlippageTolerance: "0.09",
CommitmentAmount: ptr.From("10000"),
ConcentratedLiquidityParameters: &commandspb.AmendAMM_ConcentratedLiquidityParameters{
Base: "20000",
},
},
errStr: "amend_amm.concentrated_liquidity_parameters.lower_bound (lower_bound and upper_bound cannot both be empty)",
},
{
submission: commandspb.AmendAMM{
MarketId: "e9982447fb4128f9968f9981612c5ea85d19b62058ec2636efc812dcbbc745ca",
SlippageTolerance: "0.09",
CommitmentAmount: ptr.From("10000"),
ConcentratedLiquidityParameters: &commandspb.AmendAMM_ConcentratedLiquidityParameters{
LowerBound: ptr.From("10000"),
Base: "20000",
UpperBound: ptr.From("15000"),
},
},
errStr: "amend_amm.concentrated_liquidity_parameters.base (should be a smaller value than upper_bound)",
},
{
submission: commandspb.AmendAMM{
MarketId: "e9982447fb4128f9968f9981612c5ea85d19b62058ec2636efc812dcbbc745ca",
SlippageTolerance: "0.09",
CommitmentAmount: ptr.From("10000"),
ConcentratedLiquidityParameters: &commandspb.AmendAMM_ConcentratedLiquidityParameters{
LowerBound: ptr.From("25000"),
Base: "20000",
UpperBound: ptr.From("30000"),
},
},
errStr: "amend_amm.concentrated_liquidity_parameters.base (should be a bigger value than lower_bound)",
},
{
submission: commandspb.AmendAMM{
MarketId: "e9982447fb4128f9968f9981612c5ea85d19b62058ec2636efc812dcbbc745ca",
98 changes: 75 additions & 23 deletions core/execution/amm/engine.go
Original file line number Diff line number Diff line change
@@ -143,6 +143,7 @@ type Engine struct {
ammParties map[string]string

minCommitmentQuantum *num.Uint
maxCalculationLevels *num.Uint
}

func New(
@@ -247,6 +248,14 @@ func (e *Engine) OnMinCommitmentQuantumUpdate(ctx context.Context, c *num.Uint)
e.minCommitmentQuantum = c.Clone()
}

func (e *Engine) OnMaxCalculationLevelsUpdate(ctx context.Context, c *num.Uint) {
e.maxCalculationLevels = c.Clone()

for _, p := range e.poolsCpy {
p.maxCalculationLevels = e.maxCalculationLevels.Clone()
}
}

// OnMTM is called whenever core does an MTM and is a signal that any pool's that are closing and have 0 position can be fully removed.
func (e *Engine) OnMTM(ctx context.Context) {
rm := []string{}
@@ -444,23 +453,11 @@ func (e *Engine) submit(active []*Pool, agg *types.Order, inner, outer *num.Uint
logging.String("side", types.OtherSide(agg.Side).String()),
)

// construct the orders
o := &types.Order{
ID: e.idgen.NextID(),
MarketID: p.market,
Party: p.AMMParty,
Size: volume,
Remaining: volume,
Price: price,
Side: types.OtherSide(agg.Side),
TimeInForce: types.OrderTimeInForceFOK,
Type: types.OrderTypeMarket,
CreatedAt: agg.CreatedAt,
Status: types.OrderStatusFilled,
Reference: "vamm-" + p.AMMParty,
GeneratedOffbook: true,
}
o.OriginalPrice, _ = num.UintFromDecimal(o.Price.ToDecimal().Div(e.priceFactor))
// construct an order
o := p.makeOrder(volume, price, types.OtherSide(agg.Side), e.idgen)

// fill in extra details
o.CreatedAt = agg.CreatedAt

orders = append(orders, o)
p.updateEphemeralPosition(o)
@@ -507,6 +504,11 @@ func (e *Engine) partition(agg *types.Order, inner, outer *num.Uint) ([]*Pool, [
continue
}

// stop early trying to trade with itself, can happens during auction uncrossing
if agg.Party == p.AMMParty {
continue
}

// not active in range if its the pool's curves are wholly outside of [inner, outer]
if (inner != nil && p.upper.high.LT(inner)) || (outer != nil && p.lower.low.GT(outer)) {
continue
@@ -652,6 +654,7 @@ func (e *Engine) Create(
slippage,
e.priceFactor,
e.positionFactor,
e.maxCalculationLevels,
)
if err != nil {
e.broker.Send(
@@ -665,10 +668,10 @@ func (e *Engine) Create(
return nil, err
}

e.log.Debug("AMM created for market",
e.log.Debug("AMM created",
logging.String("owner", submit.Party),
logging.String("marketID", e.marketID),
logging.String("poolID", pool.ID),
logging.String("marketID", e.marketID),
)
return pool, nil
}
@@ -677,17 +680,19 @@ func (e *Engine) Create(
func (e *Engine) Confirm(
ctx context.Context,
pool *Pool,
) error {
e.log.Debug("AMM added for market",
) {
e.log.Debug("AMM confirmed",
logging.String("owner", pool.owner),
logging.String("marketID", e.marketID),
logging.String("poolID", pool.ID),
)

pool.status = types.AMMPoolStatusActive
pool.maxCalculationLevels = e.maxCalculationLevels

e.add(pool)
e.sendUpdate(ctx, pool)
e.parties.AssignDeriveKey(types.PartyID(pool.owner), pool.AMMParty)
return nil
}

// Amend takes the details of an amendment to an AMM and returns a copy of that pool with the updated curves along with the current pool.
@@ -717,7 +722,11 @@ func (e *Engine) Amend(
if err != nil {
return nil, nil, err
}

e.log.Debug("AMM amended",
logging.String("owner", amend.Party),
logging.String("marketID", e.marketID),
logging.String("poolID", pool.ID),
)
return updated, pool, nil
}

@@ -746,6 +755,11 @@ func (e *Engine) CancelAMM(

pool.status = types.AMMPoolStatusCancelled
e.remove(ctx, cancel.Party)
e.log.Debug("AMM cancelled",
logging.String("owner", cancel.Party),
logging.String("poolID", pool.ID),
logging.String("marketID", e.marketID),
)
return closeout, nil
}

@@ -907,6 +921,36 @@ func (e *Engine) GetAMMPoolsBySubAccount() map[string]common.AMMPool {
return ret
}

// OrderbookShape expands all registered AMM's into orders between the given prices. If `ammParty` is supplied then just the pool
// with that party id is expanded.
func (e *Engine) OrderbookShape(st, nd *num.Uint, ammParty *string) ([]*types.Order, []*types.Order) {
if ammParty == nil {
// no party give, expand all registered
buys, sells := []*types.Order{}, []*types.Order{}
for _, p := range e.poolsCpy {
b, s := p.OrderbookShape(st, nd, e.idgen)
buys = append(buys, b...)
sells = append(sells, s...)
}
return buys, sells
}

// asked to expand just one AMM, lets find it, first amm-party -> owning party
owner, ok := e.ammParties[*ammParty]
if !ok {
return nil, nil
}

// now owning party -> pool
p, ok := e.pools[owner]
if !ok {
return nil, nil
}

// expand it
return p.OrderbookShape(st, nd, e.idgen)
}

func (e *Engine) GetAllSubAccounts() []string {
ret := make([]string, 0, len(e.ammParties))
for _, subAccount := range e.ammParties {
@@ -915,6 +959,14 @@ func (e *Engine) GetAllSubAccounts() []string {
return ret
}

// GetAMMParty returns the AMM's key given the owners key.
func (e *Engine) GetAMMParty(party string) (string, error) {
if p, ok := e.pools[party]; ok {
return p.AMMParty, nil
}
return "", ErrNoPoolMatchingParty
}

func (e *Engine) add(p *Pool) {
e.pools[p.owner] = p
e.poolsCpy = append(e.poolsCpy, p)
2 changes: 1 addition & 1 deletion core/execution/amm/engine_test.go
Original file line number Diff line number Diff line change
@@ -568,7 +568,7 @@ func whenAMMIsSubmitted(t *testing.T, tst *tstEngine, submission *types.SubmitAM
ctx := context.Background()
pool, err := tst.engine.Create(ctx, submission, vgcrypto.RandomHash(), riskFactors, scalingFactors, slippage)
require.NoError(t, err)
require.NoError(t, tst.engine.Confirm(ctx, pool))
tst.engine.Confirm(ctx, pool)
}

func getParty(t *testing.T, tst *tstEngine) (string, string) {
Loading

0 comments on commit 6313e20

Please sign in to comment.