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

fix: avoid trading across discontinuity at and AMMs base price #11725

Merged
merged 1 commit into from
Oct 1, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

- [11672](https://github.com/vegaprotocol/vega/issues/11672) - Add missing fees in GraphQL bindings.
- [11681](https://github.com/vegaprotocol/vega/issues/11681) - Account for conflicts inserting funding payment records.
- [11715](https://github.com/vegaprotocol/vega/issues/11715) - Trade across an `AMMs` base price in two steps since the curves either side will be different.
- [11684](https://github.com/vegaprotocol/vega/issues/11684) - Better error when `Arbitrum` bridge details are missing from validator configuration.
- [11696](https://github.com/vegaprotocol/vega/issues/11696) - Add binding for estimate fees API.
- [11699](https://github.com/vegaprotocol/vega/issues/11699) - Update factors of programs when they are updated.
Expand Down
16 changes: 15 additions & 1 deletion core/execution/amm/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,22 @@ func (e *Engine) partition(agg *types.Order, inner, outer *num.Uint) ([]*Pool, [
// pool is active in range add it to the slice
active = append(active, p)

// we hit a discontinuity where an AMM's two curves meet if we try to trade over its base-price
// so we partition the inner/outer price range at the base price so that we instead trade across it
// in two steps.
boundary := p.upper.low
if inner != nil && outer != nil {
if boundary.LT(outer) && boundary.GT(inner) {
bounds[boundary.String()] = boundary.Clone()
}
} else if outer == nil && boundary.GT(inner) {
bounds[boundary.String()] = boundary.Clone()
} else if inner == nil && boundary.LT(outer) {
bounds[boundary.String()] = boundary.Clone()
}

// if a pool's upper or lower boundary exists within (inner, outer) then we consider that a sub-level
boundary := p.upper.high
boundary = p.upper.high
if outer == nil || boundary.LT(outer) {
bounds[boundary.String()] = boundary.Clone()
}
Expand Down
13 changes: 8 additions & 5 deletions core/execution/amm/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,14 @@ func testBasicSubmitOrder(t *testing.T) {
assert.Equal(t, "2021", ba.String())

orders = tst.engine.SubmitOrder(agg, num.NewUint(2020), num.NewUint(1990))
require.Len(t, orders, 1)
assert.Equal(t, "2004", orders[0].Price.String())
// note that this volume being bigger than 242367 above means we've moved back to position, then flipped
// sign, and took volume from the other curve.
assert.Equal(t, 362325, int(orders[0].Size))

// two orders because we have to split it when we trade across the base-price as thats where we move from one curve to the other.
require.Len(t, orders, 2)
assert.Equal(t, "2009", orders[0].Price.String())
assert.Equal(t, 236855, int(orders[0].Size))

assert.Equal(t, "1994", orders[1].Price.String())
assert.Equal(t, 125470, int(orders[1].Size))
}

func testSubmitOrderAtBestPrice(t *testing.T) {
Expand Down
44 changes: 23 additions & 21 deletions core/integration/features/amm/0090-VAMM-006-014.feature
Original file line number Diff line number Diff line change
Expand Up @@ -348,30 +348,31 @@ Feature: Ensure the vAMM positions follow the market correctly
# In a single trade, move the mid price to 110
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party4 | ETH/MAR22 | buy | 440 | 110 | 1 | TYPE_LIMIT | TIF_GTC |
| party4 | ETH/MAR22 | buy | 440 | 110 | 2 | TYPE_LIMIT | TIF_GTC |
Then the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | ref price | mid price | static mid price | best offer price | best bid price |
| 94 | TRADING_MODE_CONTINUOUS | 100 | 110 | 110 | 111 | 109 |
And the following trades should be executed:
| buyer | price | size | seller | is amm |
| party4 | 95 | 440 | vamm1-id | true |
| party4 | 94 | 371 | vamm1-id | true |
| party4 | 104 | 69 | vamm1-id | true |
# Check the resulting position, vAMM switched from long to short
When the network moves ahead "1" blocks
Then the parties should have the following profit and loss:
| party | volume | unrealised pnl | realised pnl | is amm |
| party1 | 1 | -5 | 0 | |
| party2 | -1 | 5 | 0 | |
| party3 | -371 | -371 | 0 | |
| party4 | 440 | 0 | 0 | |
| vamm1-id | -69 | 0 | 371 | true |
| party1 | 1 | 4 | 0 | |
| party2 | -1 | -4 | 0 | |
| party3 | -371 | -3710 | 0 | |
| party4 | 440 | 3710 | 0 | |
| vamm1-id | -69 | 0 | 0 | true |

# Now return the mid price back to 100
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party5 | ETH/MAR22 | sell | 69 | 100 | 1 | TYPE_LIMIT | TIF_GTC |
Then the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | ref price | mid price | static mid price | best offer price | best bid price |
| 95 | TRADING_MODE_CONTINUOUS | 100 | 100 | 100 | 101 | 99 |
| 104 | TRADING_MODE_CONTINUOUS | 100 | 100 | 100 | 101 | 99 |
And the following trades should be executed:
| buyer | price | size | seller | is amm |
| vamm1-id | 104 | 69 | party5 | true |
Expand All @@ -382,9 +383,9 @@ Feature: Ensure the vAMM positions follow the market correctly
| party1 | 1 | 4 | 0 | |
| party2 | -1 | -4 | 0 | |
| party3 | -371 | -3710 | 0 | |
| party4 | 440 | 3960 | 0 | |
| party4 | 440 | 3710 | 0 | |
| party5 | -69 | 0 | 0 | |
| vamm1-id | 0 | 0 | -250 | true |
| vamm1-id | 0 | 0 | 0 | true |

@VAMM
Scenario: 0090-VAMM-013: If other traders trade to move the market mid price to 90 and then move the mid price back to 100 in several trades of varying size, the vAMM will have a position of 0.
Expand Down Expand Up @@ -431,7 +432,7 @@ Feature: Ensure the vAMM positions follow the market correctly
| party5 | 130 | 0 | 0 | |
| vamm1-id | 0 | 0 | -257 | true |

@VAMM
@VAMM3
Scenario: 0090-VAMM-014: If other traders trade to move the market mid price to 90 and then in one trade move the mid price to 110 then trade to move the mid price to 120 the vAMM will have a larger (more negative) but comparable position to if they had been moved straight from 100 to 120.
# Move mid price to 90
When the parties place the following orders:
Expand All @@ -455,30 +456,31 @@ Feature: Ensure the vAMM positions follow the market correctly
# In a single trade, move the mid price to 110
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party4 | ETH/MAR22 | buy | 420 | 110 | 1 | TYPE_LIMIT | TIF_GTC |
| party4 | ETH/MAR22 | buy | 420 | 110 | 2 | TYPE_LIMIT | TIF_GTC |
Then the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | ref price | mid price | static mid price | best offer price | best bid price |
| 95 | TRADING_MODE_CONTINUOUS | 100 | 110 | 110 | 111 | 109 |
And the following trades should be executed:
| buyer | price | size | seller | is amm |
| party4 | 95 | 420 | vamm1-id | true |
| party4 | 94 | 350 | vamm1-id | true |
| party4 | 104 | 70 | vamm1-id | true |
# Check the resulting position, vAMM switched from long to short
When the network moves ahead "1" blocks
Then the parties should have the following profit and loss:
| party | volume | unrealised pnl | realised pnl | is amm |
| party1 | 1 | -5 | 0 | |
| party2 | -1 | 5 | 0 | |
| party3 | -350 | 0 | 0 | |
| party4 | 420 | 0 | 0 | |
| vamm1-id | -70 | 0 | 0 | true |
| party1 | 1 | 4 | 0 | |
| party2 | -1 | -4 | 0 | |
| party3 | -350 | -3150 | 0 | |
| party4 | 420 | 3500 | 0 | |
| vamm1-id | -70 | 0 | -350 | true |

# Now further increase the mid price, move it up to 120
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party5 | ETH/MAR22 | buy | 65 | 120 | 1 | TYPE_LIMIT | TIF_GTC |
Then the market data for the market "ETH/MAR22" should be:
| mark price | trading mode | ref price | mid price | static mid price | best offer price | best bid price |
| 95 | TRADING_MODE_CONTINUOUS | 100 | 120 | 120 | 121 | 119 |
| 104 | TRADING_MODE_CONTINUOUS | 100 | 120 | 120 | 121 | 119 |
And the following trades should be executed:
| buyer | price | size | seller | is amm |
| party5 | 114 | 65 | vamm1-id | true |
Expand All @@ -489,6 +491,6 @@ Feature: Ensure the vAMM positions follow the market correctly
| party1 | 1 | 14 | 0 | |
| party2 | -1 | -14 | 0 | |
| party3 | -350 | -6650 | 0 | |
| party4 | 420 | 7980 | 0 | |
| party4 | 420 | 7700 | 0 | |
| party5 | 65 | 0 | 0 | |
| vamm1-id | -135 | -1330 | 0 | true |
| vamm1-id | -135 | -700 | -350 | true |
2 changes: 1 addition & 1 deletion core/integration/features/amm/0090-VAMM-028.feature
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Feature: Ensure the vAMM positions follow the market correctly
# Now to move from 110 down to 90, the volume ought to be 421 (=347+74)
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party6 | ETH/MAR23 | sell | 421 | 90 | 1 | TYPE_LIMIT | TIF_GTC |
| party6 | ETH/MAR23 | sell | 421 | 90 | 2 | TYPE_LIMIT | TIF_GTC |
Then the market data for the market "ETH/MAR23" should be:
| mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | best offer price | best bid price |
| 100 | TRADING_MODE_CONTINUOUS | 13915 | 1000 | 348 | 100 | 90 | 90 | 91 | 89 |
Loading
Loading