Skip to content

Commit

Permalink
Merge pull request #10861 from vegaprotocol/10858-auction-state-sync
Browse files Browse the repository at this point in the history
fix: ensure we hit leave auction trigger on mark-price calc, and hand…
  • Loading branch information
jeremyletang committed Mar 11, 2024
2 parents d4a23cd + 7e4f9ae commit 0471be2
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 7 deletions.
8 changes: 4 additions & 4 deletions core/execution/common/mark_price.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,11 @@ func (mpc *CompositePriceCalculator) updateMarkPriceIfNotInAuction(ctx context.C
return nil
}
priceMonitor.CheckPrice(ctx, as, []*types.Trade{{Price: mpcCandidate, Size: 1}}, true, true)
if !as.InAuction() {
mpc.price = mpcCandidate
return nil
if as.InAuction() || as.AuctionStart() {
return fmt.Errorf("price monitoring failed for the new mark price")
}
return fmt.Errorf("price monitoring failed for the new mark price")
mpc.price = mpcCandidate
return nil
}

// CalculateMarkPrice is called at the end of each mark price calculation interval and calculates the mark price
Expand Down
4 changes: 3 additions & 1 deletion core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,6 @@ func (m *Market) leaveAuction(ctx context.Context, now time.Time) {

m.mkt.State = types.MarketStateActive
m.mkt.TradingMode = types.MarketTradingModeContinuous
m.tradableInstrument.Instrument.UpdateAuctionState(ctx, false)
m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt))

m.updateLiquidityFee(ctx)
Expand Down Expand Up @@ -1612,6 +1611,9 @@ func (m *Market) leaveAuction(ctx context.Context, now time.Time) {

// update auction state, so we know what the new tradeMode ought to be
endEvt := m.as.Left(ctx, now)
// we tell the perp that we've left auction, we might re-enter just a bit down but thats fine as
// we will at least keep the in/out orders in sync
m.tradableInstrument.Instrument.UpdateAuctionState(ctx, false)

for _, uncrossedOrder := range uncrossedOrders {
updatedOrders = append(updatedOrders, uncrossedOrder.Order)
Expand Down
2 changes: 1 addition & 1 deletion core/integration/features/perpetual.feature
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ Feature: Simple test creating a perpetual market.
| 976 | TRADING_MODE_CONTINUOUS | AUCTION_TRIGGER_UNSPECIFIED | 1 |
And the following funding period events should be emitted:
| start | end | internal twap | external twap |
| 1575072002 | | 9760000000000000 | |
| 1575072002 | | | |

# perps payment doesn't happen in the absence of oracle data
When the oracles broadcast data with block time signed with "0xCAFECAFE1":
Expand Down
107 changes: 107 additions & 0 deletions core/integration/features/price-mon-trigger-leaving-auction.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Feature: replicating the incentive panic from issue 10858.
Background:
Given the following network parameters are set:
| name | value |
| network.markPriceUpdateMaximumFrequency | 1s |
And the price monitoring named "my-price-monitoring":
| horizon | probability | auction extension |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 30 | 0.99999 | 1 |
| 120 | 0.9999999 | 5 |
| 120 | 0.9999999 | 5 |
| 120 | 0.9999999 | 5 |
| 120 | 0.9999999 | 5 |
| 120 | 0.9999999 | 5 |
| 200 | 0.9999999 | 5 |
| 200 | 0.9999999 | 5 |
| 200 | 0.9999999 | 5 |
| 200 | 0.9999999 | 5 |
| 200 | 0.9999999 | 5 |
And the liquidity monitoring parameters:
| name | triggering ratio | time window | scaling factor |
| lqm-params | 0.00 | 24h | 1e-9 |
And the simple risk model named "simple-risk-model":
| long | short | max move up | min move down | probability of trading |
| 0.1 | 0.1 | 100 | -100 | 0.2 |

# this is just an example of setting up oracles
And the composite price oracles from "0xCAFECAFE1":
| name | price property | price type | price decimals |
| oracle1 | price1.USD.value | TYPE_INTEGER | 0 |
| oracle2 | price2.USD.value | TYPE_INTEGER | 0 |
| oracle3 | price3.USD.value | TYPE_INTEGER | 0 |

And the markets:
| id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | price type | decay weight | decay power | cash amount | source weights | source staleness tolerance | oracle1 | oracle2 | oracle3 |
| ETH/FEB23 | ETH | USD | lqm-params | simple-risk-model | default-margin-calculator | 1 | default-none | my-price-monitoring | default-eth-for-future | 0.25 | 0 | default-futures | weight | 1 | 1 | 0 | 0,0,0,1,0 | 1m0s,1m0s,1m0s,1m0s,1m0s | oracle1 | oracle2 | oracle3 |

@MPP
Scenario: Oracle data significantly higher than trade price, submitted before leaving opening auction
Given the parties deposit on asset's general account the following amount:
| party | asset | amount |
| buySideProvider | USD | 100000000000 |
| sellSideProvider | USD | 100000000000 |
| party | USD | 948050 |

When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | reference |
| buySideProvider | ETH/FEB23 | buy | 10 | 14900 | 0 | TYPE_LIMIT | TIF_GTC | |
| buySideProvider | ETH/FEB23 | buy | 1 | 15000 | 0 | TYPE_LIMIT | TIF_GTC | |
| buySideProvider | ETH/FEB23 | buy | 3 | 15900 | 0 | TYPE_LIMIT | TIF_GTC | |
| party | ETH/FEB23 | sell | 3 | 15900 | 0 | TYPE_LIMIT | TIF_GTC | |
| sellSideProvider | ETH/FEB23 | sell | 2 | 15920 | 0 | TYPE_LIMIT | TIF_GTC | sell-2 |
| sellSideProvider | ETH/FEB23 | sell | 1 | 15940 | 0 | TYPE_LIMIT | TIF_GTC | sell-3 |
| sellSideProvider | ETH/FEB23 | sell | 3 | 15960 | 0 | TYPE_LIMIT | TIF_GTC | sell-4 |
| sellSideProvider | ETH/FEB23 | sell | 5 | 15990 | 0 | TYPE_LIMIT | TIF_GTC | sell-5 |
| sellSideProvider | ETH/FEB23 | sell | 2 | 16000 | 0 | TYPE_LIMIT | TIF_GTC | sell-7 |
| sellSideProvider | ETH/FEB23 | sell | 4 | 16020 | 0 | TYPE_LIMIT | TIF_GTC | sell-8 |
| sellSideProvider | ETH/FEB23 | sell | 1 | 100000 | 0 | TYPE_LIMIT | TIF_GTC | |


# AC 0009-MRKP-012
When the network moves ahead "2" blocks
Then the mark price should be "15900" for the market "ETH/FEB23"
And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/FEB23"

And the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | reference |
| buySideProvider | ETH/FEB23 | buy | 1 | 10 | 0 | TYPE_LIMIT | TIF_GTC | cancel1 |
| sellSideProvider | ETH/FEB23 | sell | 1 | 10 | 0 | TYPE_LIMIT | TIF_GTC | cancel2 |

#When the network moves ahead "1" blocks
#Then the mark price should be "15900" for the market "ETH/FEB23"

And the trading mode should be "TRADING_MODE_MONITORING_AUCTION" for the market "ETH/FEB23"

# Broadcast significantly higher prices via the oracle
Then the oracles broadcast data with block time signed with "0xCAFECAFE1":
| name | value | time offset |
| price1.USD.value | 26000 | 0s |
| price2.USD.value | 25900 | 0s |
| price3.USD.value | 25940 | 0s |

Then the parties cancel the following orders:
| party | reference |
| buySideProvider | cancel1 |
| sellSideProvider | cancel2 |

And the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | reference |
| buySideProvider | ETH/FEB23 | buy | 1 | 15900 | 0 | TYPE_LIMIT | TIF_GTC | cancel1 |
| sellSideProvider | ETH/FEB23 | sell | 1 | 15900 | 0 | TYPE_LIMIT | TIF_GTC | cancel2 |

When the network moves ahead "10" blocks
Then the mark price should be "15900" for the market "ETH/FEB23"

When the network moves ahead "2" blocks
Then the mark price should be "15900" for the market "ETH/FEB23"



20 changes: 19 additions & 1 deletion core/products/perpetual.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,25 @@ func (a *auctionIntervals) update(t int64, enter bool) {
}

if enter {
a.auctionStart = t
if len(a.auctions) == 0 {
a.auctionStart = t
return
}

st, nd := a.auctions[len(a.auctions)-2], a.auctions[len(a.auctions)-1]
if t != nd {
a.auctionStart = t
return
}

a.auctions = slices.Delete(a.auctions, len(a.auctions)-2, len(a.auctions))
a.auctionStart = st
return
}

if t == a.auctionStart {
// left an auction as soon as we entered it, no need to log it
a.auctionStart = 0
return
}

Expand Down
26 changes: 26 additions & 0 deletions core/products/perpetual_auctions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,32 @@ func testPastFundingPayment(t *testing.T) {
assert.Equal(t, int64(expectedTWAP), fundingPayment.Int64())
}

func TestZeroLengthAuctionPeriods(t *testing.T) {
perp := testPerpetual(t)
defer perp.ctrl.Finish()

// set of the data points such that difference in averages is 0
points := getTestDataPoints(t)

// tell the perpetual that we are ready to accept settlement stuff
whenLeaveOpeningAuction(t, perp, points[0].t)

// enter auction
whenAuctionStateChanges(t, perp, points[0].t, true)

// send in some data points with a TWAP difference
submitDataWithDifference(t, perp, points, 10)

// leave auction
whenAuctionStateChanges(t, perp, points[len(points)-1].t, false)

// but then enter again straight away
whenAuctionStateChanges(t, perp, points[len(points)-1].t, true)

fundingPayment := whenTheFundingPeriodEnds(t, perp, points[len(points)-1].t)
assert.Equal(t, "0", fundingPayment.String())
}

func TestFairgroundPanic(t *testing.T) {
perp := testPerpetual(t)
defer perp.ctrl.Finish()
Expand Down

0 comments on commit 0471be2

Please sign in to comment.