Skip to content

Commit

Permalink
Merge pull request #10765 from vegaprotocol/10763-restart-external-twap
Browse files Browse the repository at this point in the history
fix: reinitialise external TWAP when we leave opening auction so we p…
  • Loading branch information
jeremyletang authored Feb 27, 2024
2 parents 36b665c + 9b8588b commit cfb23a5
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 11 deletions.
15 changes: 9 additions & 6 deletions core/products/perpetual.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ func (c *cachedTWAP) unwind(t int64) (*num.Uint, int) {
return nil, 0
}

// getTwapAsNilOrString return nil when TWAP cannot be calculated and a string (as pointer) otherwise.
func (c *cachedTWAP) getTwapAsNilOrString(t int64) *string {
// getTWAP will return the TWAP if we have enough data, else nil.
func (c *cachedTWAP) getTWAP(t int64) *string {
if !c.dataAvailable(t) {
return nil
}
Expand Down Expand Up @@ -594,13 +594,16 @@ func (p *Perpetual) UpdateAuctionState(ctx context.Context, enter bool) {

// left first auction, we can start the first funding-period
p.startedAt = t
// store internal data submitted at the end of opening auction

// we might have been sent useful internal points before officially leaving opening auction, such as
// the uncrossing price, so we make sure we keep those.
temp := p.internalTWAP
p.internalTWAP = NewCachedTWAP(p.log, t, p.auctions)
for _, pt := range temp.points {
p.internalTWAP.addPoint(pt)
}
p.broker.Send(events.NewFundingPeriodEvent(ctx, p.id, p.seq, p.startedAt, nil, nil, nil, p.internalTWAP.getTwapAsNilOrString(p.startedAt), p.externalTWAP.getTwapAsNilOrString(p.startedAt)))
p.externalTWAP = NewCachedTWAP(p.log, t, p.auctions)
p.broker.Send(events.NewFundingPeriodEvent(ctx, p.id, p.seq, p.startedAt, nil, nil, nil, p.internalTWAP.getTWAP(p.startedAt), p.externalTWAP.getTWAP(p.startedAt)))
}

// SubmitDataPoint this will add a data point produced internally by the core node.
Expand Down Expand Up @@ -729,7 +732,7 @@ func (p *Perpetual) handleSettlementCue(ctx context.Context, t int64) {

if !p.haveDataBeforeGivenTime(t) || t == p.startedAt {
// we have no points, or the interval is zero length so we just start a new interval
p.broker.Send(events.NewFundingPeriodEvent(ctx, p.id, p.seq, p.startedAt, ptr.From(t), nil, nil, p.internalTWAP.getTwapAsNilOrString(t), p.externalTWAP.getTwapAsNilOrString(t)))
p.broker.Send(events.NewFundingPeriodEvent(ctx, p.id, p.seq, p.startedAt, ptr.From(t), nil, nil, p.internalTWAP.getTWAP(t), p.externalTWAP.getTWAP(t)))
p.startNewFundingPeriod(ctx, t)
return
}
Expand Down Expand Up @@ -825,7 +828,7 @@ func (p *Perpetual) startNewFundingPeriod(ctx context.Context, endAt int64) {
evts = append(evts, events.NewFundingPeriodDataPointEvent(ctx, p.id, dp.price.String(), dp.t, p.seq, dataPointSourceInternal, iTWAP))
}
// send event to say our new period has started
p.broker.Send(events.NewFundingPeriodEvent(ctx, p.id, p.seq, p.startedAt, nil, nil, nil, p.internalTWAP.getTwapAsNilOrString(p.startedAt), p.externalTWAP.getTwapAsNilOrString(p.startedAt)))
p.broker.Send(events.NewFundingPeriodEvent(ctx, p.id, p.seq, p.startedAt, nil, nil, nil, p.internalTWAP.getTWAP(p.startedAt), p.externalTWAP.getTWAP(p.startedAt)))
if len(evts) > 0 {
p.broker.SendBatch(evts)
}
Expand Down
30 changes: 25 additions & 5 deletions core/products/perpetual_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import (
)

func TestPeriodicSettlement(t *testing.T) {
t.Run("incoming data ignored before leaving opening auction", testIncomingExternalDataIgnoredBeforeLeavingOpeningAuction)
t.Run("twap calculations after leaving opening auction", testTWAPAfterOpeningAuction)
t.Run("period end with no data point", testPeriodEndWithNoDataPoints)
t.Run("equal internal and external prices", testEqualInternalAndExternalPrices)
t.Run("constant difference long pays short", testConstantDifferenceLongPaysShort)
Expand Down Expand Up @@ -128,26 +128,45 @@ func TestRealData(t *testing.T) {
}
}

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

ctx := context.Background()

now := time.Unix(2000, 0).UnixNano()

// no error because its really a callback from the oracle engine, but we expect no events
perp.perpetual.AddTestExternalPoint(ctx, num.UintOne(), 2000)
perp.perpetual.AddTestExternalPoint(ctx, num.UintOne(), now)
data := perp.perpetual.GetData(2000)
require.Nil(t, data)

// internal data point recevied without error
perp.broker.EXPECT().Send(gomock.AssignableToTypeOf(&events.FundingPeriodDataPoint{})).Times(1)
err := perp.perpetual.SubmitDataPoint(ctx, num.UintOne(), 2000)
perp.broker.EXPECT().Send(gomock.Any()).Times(1)
err := perp.perpetual.SubmitDataPoint(ctx, num.NewUint(100000), now)
data = perp.perpetual.GetData(2000)
assert.NoError(t, err)
require.Nil(t, data)

// check that settlement cues are ignored, we expect no events when it is
perp.perpetual.PromptSettlementCue(ctx, 4000)

// now leaving opening auction
perp.broker.EXPECT().Send(gomock.Any()).Times(1)
perp.ts.EXPECT().GetTimeNow().Times(1).Return(time.Unix(2000, 0))
perp.perpetual.UpdateAuctionState(ctx, false)

// send in an external data-point which actually hits before opening auction time because it took a while to wobble through
perp.broker.EXPECT().Send(gomock.Any()).Times(1)
perp.perpetual.AddTestExternalPoint(ctx, num.NewUint(100000), now-int64(time.Minute))
fundingPayment := getFundingPayment(t, perp, now)
require.Equal(t, "0", fundingPayment)

// now another data point but both at the same time
dp := &testDataPoint{price: num.NewUint(200000), t: now + int64(time.Second)}
submitPointWithDifference(t, perp, dp, 0)
fundingPayment = getFundingPayment(t, perp, now+int64(time.Minute))
require.Equal(t, "0", fundingPayment)
}

func testPeriodEndWithNoDataPoints(t *testing.T) {
Expand Down Expand Up @@ -954,6 +973,7 @@ func submitPointWithDifference(t *testing.T, perp *tstPerp, p *testDataPoint, di
var internalPrice *num.Uint
perp.broker.EXPECT().Send(gomock.Any()).Times(2)
perp.perpetual.AddTestExternalPoint(ctx, p.price, p.t)
internalPrice = p.price.Clone()

if diff > 0 {
internalPrice = num.UintZero().Add(p.price, num.NewUint(uint64(diff)))
Expand Down

0 comments on commit cfb23a5

Please sign in to comment.