Skip to content

Commit

Permalink
Merge branch 'develop' into cometv1
Browse files Browse the repository at this point in the history
  • Loading branch information
ze97286 authored Jul 24, 2024
2 parents 8122dfb + 227afa8 commit dfc6a93
Show file tree
Hide file tree
Showing 26 changed files with 633 additions and 22 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
- [](https://github.com/vegaprotocol/vega/issues/xxx)


## 0.77.2

### 🐛 Fixes

- [11481](https://github.com/vegaprotocol/vega/issues/11481) - Fix margin check for cross margin market orders.
- [11474](https://github.com/vegaprotocol/vega/issues/11474) - Fail `checkTx` downstream for delayed transactions so they don't get included in more than one block.

## 0.77.1

### 🛠 Improvements
Expand All @@ -29,7 +36,8 @@

- [11453](https://github.com/vegaprotocol/vega/issues/11453) - Fix margin for fully collateralised markets.
- [11462](https://github.com/vegaprotocol/vega/issues/11462) - Update the time weighted notional when publishing live game scores.
- [11474](https://github.com/vegaprotocol/vega/issues/11474) - Fail `checkTx` downstream for delayed transactions so they don't get included in more than one block.
- [11474](https://github.com/vegaprotocol/vega/issues/11474) - Fail `checkTx` downstream for delayed transactions so they don't get included in more than one block.
- [11481](https://github.com/vegaprotocol/vega/issues/11481) - Fix margin check for cross margin market orders.

## 0.77.0

Expand Down
22 changes: 20 additions & 2 deletions core/execution/amm/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ type Engine struct {
// gets us from the price in the submission -> price in full asset dp
priceFactor num.Decimal
positionFactor num.Decimal
oneTick *num.Uint

// map of party -> pool
pools map[string]*Pool
Expand Down Expand Up @@ -157,6 +158,7 @@ func New(
marketActivityTracker *common.MarketActivityTracker,
parties common.Parties,
) *Engine {
oneTick, _ := num.UintFromDecimal(priceFactor)
return &Engine{
log: log,
broker: broker,
Expand All @@ -173,6 +175,7 @@ func New(
priceFactor: priceFactor,
positionFactor: positionFactor,
parties: parties,
oneTick: oneTick,
}
}

Expand Down Expand Up @@ -325,7 +328,7 @@ func (e *Engine) BestPricesAndVolumes() (*num.Uint, uint64, *num.Uint, uint64) {
fp := pool.BestPrice(nil)

// get the volume on the buy side by simulating an incoming sell order
bid := num.UintZero().Sub(fp, pool.oneTick)
bid := num.Max(pool.lower.low, num.UintZero().Sub(fp, pool.oneTick))
volume := pool.TradableVolumeInRange(types.SideSell, fp.Clone(), bid)

if volume != 0 {
Expand All @@ -338,7 +341,7 @@ func (e *Engine) BestPricesAndVolumes() (*num.Uint, uint64, *num.Uint, uint64) {
}

// get the volume on the sell side by simulating an incoming buy order
ask := num.UintZero().Add(fp, pool.oneTick)
ask := num.Min(pool.upper.high, num.UintZero().Add(fp, pool.oneTick))
volume = pool.TradableVolumeInRange(types.SideBuy, fp.Clone(), ask)
if volume != 0 {
if bestAsk == nil || ask.LT(bestAsk) {
Expand Down Expand Up @@ -493,6 +496,21 @@ func (e *Engine) partition(agg *types.Order, inner, outer *num.Uint) ([]*Pool, [
inner, outer = outer, inner
}

// if inner and outer are equal then we are wanting to trade with AMMs *only at* this given price
// this can happen quite easily during auction uncrossing where two AMMs have bases offset by 2
// and the crossed region is simply a point and not an interval. To be able to query the tradable
// volume of an AMM at a point, we need to first convert it to an interval by stepping one tick away first.
// This is because to get the BUY volume an AMM has at price P, we need to calculate the difference
// in its position between prices P -> P + 1. For SELL volume its the other way around and we
// need the difference in position from P - 1 -> P.
if inner.EQ(outer) {
if agg.Side == types.SideSell {
outer = num.UintZero().Add(outer, e.oneTick)
} else {
inner = num.UintZero().Sub(inner, e.oneTick)
}
}

if inner != nil {
bounds[inner.String()] = inner.Clone()
}
Expand Down
52 changes: 50 additions & 2 deletions core/execution/amm/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,49 @@ func testBestPricesAndVolume(t *testing.T) {
assert.Equal(t, avolume, avAt)
}

func TestBestPricesAndVolumeNearBound(t *testing.T) {
tst := getTestEngineWithFactors(t, num.DecimalFromInt64(100), num.DecimalFromFloat(10))

// create three pools
party, subAccount := getParty(t, tst)
submit := getPoolSubmission(t, party, tst.marketID)

expectSubaccountCreation(t, tst, party, subAccount)
whenAMMIsSubmitted(t, tst, submit)

tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(5).Return(
[]events.MarketPosition{&marketPosition{size: 0, averageEntry: num.NewUint(0)}},
)

bid, bvolume, ask, avolume := tst.engine.BestPricesAndVolumes()
assert.Equal(t, "199900", bid.String())
assert.Equal(t, "200100", ask.String())
assert.Equal(t, 1250, int(bvolume))
assert.Equal(t, 1192, int(avolume))

// lets move its position so that the fair price is within one tick of the AMMs upper boundary
tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(5).Return(
[]events.MarketPosition{&marketPosition{size: -222000, averageEntry: num.NewUint(0)}},
)

bid, bvolume, ask, avolume = tst.engine.BestPricesAndVolumes()
assert.Equal(t, "219890", bid.String())
assert.Equal(t, "220000", ask.String()) // make sure we are capped to the boundary and not 220090
assert.Equal(t, 1034, int(bvolume))
assert.Equal(t, 103, int(avolume))

// lets move its position so that the fair price is within one tick of the AMMs upper boundary
tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(5).Return(
[]events.MarketPosition{&marketPosition{size: 270400, averageEntry: num.NewUint(0)}},
)

bid, bvolume, ask, avolume = tst.engine.BestPricesAndVolumes()
assert.Equal(t, "180000", bid.String()) // make sure we are capped to the boundary and not 179904
assert.Equal(t, "180104", ask.String())
assert.Equal(t, 58, int(bvolume))
assert.Equal(t, 1463, int(avolume))
}

func testClosingReduceOnlyPool(t *testing.T) {
ctx := context.Background()
tst := getTestEngine(t)
Expand Down Expand Up @@ -709,7 +752,7 @@ type tstEngine struct {
assetID string
}

func getTestEngine(t *testing.T) *tstEngine {
func getTestEngineWithFactors(t *testing.T, priceFactor, positionFactor num.Decimal) *tstEngine {
t.Helper()
ctrl := gomock.NewController(t)
col := mocks.NewMockCollateral(ctrl)
Expand All @@ -730,7 +773,7 @@ func getTestEngine(t *testing.T) *tstEngine {
parties := cmocks.NewMockParties(ctrl)
parties.EXPECT().AssignDeriveKey(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()

eng := New(logging.NewTestLogger(), broker, col, marketID, assetID, pos, num.DecimalOne(), num.DecimalOne(), mat, parties)
eng := New(logging.NewTestLogger(), broker, col, marketID, assetID, pos, priceFactor, positionFactor, mat, parties)

// do an ontick to initialise the idgen
ctx := vgcontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
Expand All @@ -748,6 +791,11 @@ func getTestEngine(t *testing.T) *tstEngine {
}
}

func getTestEngine(t *testing.T) *tstEngine {
t.Helper()
return getTestEngineWithFactors(t, num.DecimalOne(), num.DecimalOne())
}

func getAccount(balance uint64) *types.Account {
return &types.Account{
Balance: num.NewUint(balance),
Expand Down
4 changes: 2 additions & 2 deletions core/execution/amm/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func NewPool(
positionFactor num.Decimal,
maxCalculationLevels *num.Uint,
) (*Pool, error) {
oneTick, _ := num.UintFromDecimal(num.DecimalOne().Mul(priceFactor))
oneTick, _ := num.UintFromDecimal(priceFactor)
pool := &Pool{
log: log,
ID: id,
Expand Down Expand Up @@ -164,7 +164,7 @@ func NewPoolFromProto(
party string,
priceFactor num.Decimal,
) (*Pool, error) {
oneTick, _ := num.UintFromDecimal(num.DecimalOne().Mul(priceFactor))
oneTick, _ := num.UintFromDecimal(priceFactor)

var lowerLeverage, upperLeverage *num.Decimal
if state.Parameters.LeverageAtLowerBound != nil {
Expand Down
11 changes: 11 additions & 0 deletions core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -2569,6 +2569,17 @@ func (m *Market) submitValidatedOrder(ctx context.Context, order *types.Order) (
return nil, nil, err
}
}
if order.Type == types.OrderTypeMarket && marginMode == types.MarginModeCrossMargin && !order.ReduceOnly && !pos.OrderReducesExposure(order) {
if err := m.checkMarginForOrder(ctx, posWithTrades, order); err != nil {
if m.log.GetLevel() <= logging.DebugLevel {
m.log.Debug("Unable to check/add margin for party",
logging.Order(*order), logging.Error(err))
}
_ = m.unregisterAndReject(
ctx, order, types.OrderErrorMarginCheckFailed)
return nil, nil, common.ErrMarginCheckFailed
}
}
}

// Send the aggressive order into matching engine
Expand Down
43 changes: 43 additions & 0 deletions core/integration/features/amm/0090-VAMM-auction.feature
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,49 @@ Feature: vAMM rebasing when created or amended
| 100 | TRADING_MODE_CONTINUOUS | 99 | 101 |


@VAMM
Scenario: two AMM's that cross at a single point i.e no overlap

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

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 | 100000 | 0.05 | 102 | 97 | 107 | 0.03 |
Then the AMM pool status should be:
| party | market id | amount | status | base | lower bound | upper bound |
| vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 102 | 97 | 107 |


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 | 101 | 92 |

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 | 101 | 92 | 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 |
| 101 | TRADING_MODE_CONTINUOUS | 100 | 102 |

@VAMM
Scenario: AMM crossed with SELL orders

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Feature: Regression test for issue 596
Then the parties should have the following account balances:
| party | asset | market id | margin | general |
| edd | BTC | ETH/DEC19 | 1491 | 8449 |
| chris | BTC | ETH/DEC19 | 1048 | 8503 |
| chris | BTC | ETH/DEC19 | 1077 | 8474 |
| barney | BTC | ETH/DEC19 | 594 | 9406 |
And the cumulated balance for all accounts should be worth "3041000"

Expand Down Expand Up @@ -169,7 +169,7 @@ Feature: Regression test for issue 596
Then the parties should have the following account balances:
| party | asset | market id | margin | general |
| edd | BTC | ETH/DEC19 | 1491 | 8449 |
| chris | BTC | ETH/DEC19 | 1048 | 8503 |
| chris | BTC | ETH/DEC19 | 1077 | 8474 |
| barney | BTC | ETH/DEC19 | 594 | 9406 |
And the cumulated balance for all accounts should be worth "3041000"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Feature: Regression test for issue 598
Then the parties should have the following account balances:
| party | asset | market id | margin | general |
| edd | BTC | ETH/DEC19 | 571 | 429 |
| chris | BTC | ETH/DEC19 | 109 | 790 |
| chris | BTC | ETH/DEC19 | 108 | 791 |

# next instruction will trade with barney
When the parties place the following orders with ticks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Feature: Test margin release on order cancel

And the parties should have the following account balances:
| party | asset | market id | margin | general |
| partyGuy | ETH | ETH/DEC19 | 415800 | 9485200 |
| partyGuy | ETH | ETH/DEC19 | 420000 | 9481000 |

Then the parties should have the following profit and loss:
| party | volume | unrealised pnl | realised pnl |
Expand Down
4 changes: 2 additions & 2 deletions core/integration/features/release-margins-issues.feature
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Feature: Test margin release on order cancel
| partyGuy | ETH | ETH/DEC19 | 0 | 10000 |


@MarginRelease
@MarginRelease @Chris
Scenario: a party place a new market order in the system, order, trade, party margin is updated, then place an GTC order which will trade, margin is 0ed
Given the parties deposit on asset's general account the following amount:
| party | asset | amount |
Expand Down Expand Up @@ -109,7 +109,7 @@ Feature: Test margin release on order cancel

And the parties should have the following account balances:
| party | asset | market id | margin | general |
| partyGuy | ETH | ETH/DEC19 | 416 | 9485 |
| partyGuy | ETH | ETH/DEC19 | 420 | 9481 |

Then the parties should have the following profit and loss:
| party | volume | unrealised pnl | realised pnl |
Expand Down
84 changes: 84 additions & 0 deletions core/integration/features/verified/auction_bug.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Feature: Indicative price within bounds but mark price outside bounds

Scenario:


Given the average block duration is "1"

And the following network parameters are set:
| name | value |
| network.markPriceUpdateMaximumFrequency | 0s |

And the following assets are registered:
| id | decimal places | quantum |
| USDT.0.1 | 0 | 1 |

Given the price monitoring named "pm":
| horizon | probability | auction extension |
| 60 | 0.999999999 | 5 |
| 60 | 0.999999999 | 5 |
| 120 | 0.999999999 | 10 |
| 120 | 0.999999999 | 10 |



And the composite price oracles from "0xCAFECAFE1":
| name | price property | price type | price decimals |
| oracle1 | price.USD.value | TYPE_INTEGER | 0 |
And the markets:
| id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | position decimal places | price type | decay weight | decay power | cash amount | source weights | source staleness tolerance | oracle1 |
| ETH/USDT | USDT | USDT.0.1 | default-log-normal-risk-model | default-margin-calculator | 1 | default-none | pm | default-eth-for-future | 1e-3 | 0 | default-futures | 0 | 0 | weight | 1 | 1 | 0 | 0,0,1,0 | 1m0s,1m0s,1m0s,1m0s | oracle1 |


Given the parties deposit on asset's general account the following amount:
| party | asset | amount |
| aux1 | USDT.0.1 | 10000000 |
| aux2 | USDT.0.1 | 10000000 |
| party1 | USDT.0.1 | 10000000 |
| party2 | USDT.0.1 | 10000000 |

Given the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| aux1 | ETH/USDT | buy | 100 | 999 | 0 | TYPE_LIMIT | TIF_GTC |
| aux1 | ETH/USDT | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC |
| aux2 | ETH/USDT | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC |
| aux2 | ETH/USDT | sell | 100 | 1001 | 0 | TYPE_LIMIT | TIF_GTC |
Then the opening auction period ends for market "ETH/USDT"
And the market data for the market "ETH/USDT" should be:
| mark price | trading mode | horizon | min bound | max bound |
| 1000 | TRADING_MODE_CONTINUOUS | 60 | 984 | 1016 |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 120 | 977 | 1024 |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 120 | 977 | 1024 |

Then the oracles broadcast data with block time signed with "0xCAFECAFE1":
| name | value | time offset |
| price.USD.value | 800 | 0s |


Given the network moves ahead "1" blocks
# And the parties place the following orders:
# | party | market id | side | volume | price | resulting trades | type | tif |
# | party1 | ETH/USDT | buy | 1 | 1001 | 0 | TYPE_LIMIT | TIF_GTC |
And the market data for the market "ETH/USDT" should be:
| mark price | trading mode | horizon | min bound | max bound | indicative price | timestamp | auction start | auction end |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 60 | 984 | 1016 | 0 | 1575072003000000000 | 1575072002000000000 | 1575072007000000000 |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 120 | 977 | 1024 | 0 | 1575072003000000000 | 1575072002000000000 | 1575072007000000000 |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 120 | 977 | 1024 | 0 | 1575072003000000000 | 1575072002000000000 | 1575072007000000000 |

# Advance 5 seconds to end of auction
Given the network moves ahead "5" blocks
# Auction ends at indicative price, trades excecuted
# And the following trades should be executed:
# | buyer | price | size | seller |
# | party1 | 1001 | 1 | aux2 |
# Market instantly reenters auction as latest price from oracle is outside bounds
And the market data for the market "ETH/USDT" should be:
| mark price | trading mode | horizon | min bound | max bound | indicative price | timestamp | auction start | auction end |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 60 | 984 | 1016 | 0 | 1575072008000000000 | 1575072008000000000 | 1575072013000000000 |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 120 | 977 | 1024 | 0 | 1575072008000000000 | 1575072008000000000 | 1575072013000000000 |
| 1000 | TRADING_MODE_MONITORING_AUCTION | 120 | 977 | 1024 | 0 | 1575072008000000000 | 1575072008000000000 | 1575072013000000000 |





Loading

0 comments on commit dfc6a93

Please sign in to comment.