code.vegaprotocol.io/vega@v0.79.0/core/execution/future/auction.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package future
    17  
    18  import (
    19  	"context"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/core/execution/common"
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  	"code.vegaprotocol.io/vega/logging"
    26  )
    27  
    28  func (m *Market) checkAuction(ctx context.Context, now time.Time, idgen common.IDGenerator) {
    29  	if !m.as.InAuction() {
    30  		// new block, check bond balance, top up if needed
    31  		m.checkBondBalance(ctx)
    32  		return
    33  	}
    34  
    35  	if m.mkt.State == types.MarketStateSuspendedViaGovernance {
    36  		if endTS := m.as.ExpiresAt(); endTS != nil && endTS.Before(now) {
    37  			m.as.ExtendAuctionSuspension(types.AuctionDuration{Duration: int64(m.minDuration.Seconds())})
    38  			return
    39  		}
    40  	}
    41  
    42  	// here we are in auction, we'll want to check
    43  	// the triggers if we are leaving
    44  	defer func() {
    45  		m.triggerStopOrders(ctx, idgen)
    46  	}()
    47  	indicativeUncrossingPrice := num.UintZero()
    48  
    49  	checkExceeded := m.mkt.State == types.MarketStatePending
    50  
    51  	// as soon as we have an indicative uncrossing price in opening auction it needs to be passed into the price monitoring engine so statevar calculation can start
    52  	isOpening := m.as.IsOpeningAuction()
    53  	if isOpening && !m.pMonitor.Initialised() {
    54  		indicativeUncrossingPrice = m.matching.OrderBook.GetIndicativePrice()
    55  		if !indicativeUncrossingPrice.IsZero() {
    56  			// pass the first uncrossing trades to price engine so state variables depending on it can be initialised
    57  			m.pMonitor.ResetPriceHistory(indicativeUncrossingPrice)
    58  			m.OnOpeningAuctionFirstUncrossingPrice()
    59  		}
    60  	}
    61  
    62  	if endTS := m.as.ExpiresAt(); endTS == nil || !endTS.Before(now) {
    63  		if isOpening && checkExceeded && m.as.ExceededMaxOpening(now) {
    64  			// cancel the market, exceeded opening auction
    65  			m.log.Debug("Market was cancelled because it failed to leave opening auction in time", logging.MarketID(m.GetID()))
    66  			m.terminateMarket(ctx, types.MarketStateCancelled, nil)
    67  		}
    68  		return
    69  	}
    70  	if indicativeUncrossingPrice.IsZero() {
    71  		indicativeUncrossingPrice = m.matching.OrderBook.GetIndicativePrice()
    72  	}
    73  
    74  	// opening auction
    75  	if isOpening {
    76  		if indicativeUncrossingPrice.IsZero() {
    77  			if checkExceeded && m.as.ExceededMaxOpening(now) {
    78  				m.log.Debug("Market was cancelled because it failed to leave opening auction in time", logging.MarketID(m.GetID()))
    79  				m.terminateMarket(ctx, types.MarketStateCancelled, nil)
    80  			}
    81  			return
    82  		}
    83  
    84  		// first check liquidity - before we mark auction as ready to leave
    85  		m.checkBondBalance(ctx)
    86  		if checkExceeded && m.as.ExceededMaxOpening(now) {
    87  			// cancel the market, exceeded opening auction
    88  			m.log.Debug("Market was cancelled because it failed to leave opening auction in time", logging.MarketID(m.GetID()))
    89  			m.terminateMarket(ctx, types.MarketStateCancelled, nil)
    90  			return
    91  		}
    92  		if e := m.as.AuctionExtended(ctx, now); e != nil {
    93  			m.broker.Send(e)
    94  			return
    95  		}
    96  		// opening auction requirements satisfied at this point, other requirements still need to be checked downstream though
    97  		m.as.SetReadyToLeave()
    98  
    99  		// if we don't have yet consensus for the floating point parameters, stay in the opening auction
   100  		if !m.CanLeaveOpeningAuction() {
   101  			m.log.Info("cannot leave opening auction - waiting for floating point to complete the first round")
   102  			return
   103  		}
   104  		m.log.Info("leaving opening auction for market", logging.String("market-id", m.mkt.ID))
   105  		m.leaveAuction(ctx, now)
   106  
   107  		m.equityShares.OpeningAuctionEnded()
   108  		// start the market fee window
   109  		m.feeSplitter.TimeWindowStart(now)
   110  
   111  		// reset SLA epoch
   112  		m.liquidity.OnEpochStart(ctx,
   113  			m.timeService.GetTimeNow(),
   114  			m.getCurrentMarkPrice(),
   115  			m.midPrice(),
   116  			m.getTargetStake(),
   117  			m.positionFactor,
   118  		)
   119  		return
   120  	}
   121  	// NOTE: This is a fix for the snapshot restores in case we're restoring a liquidity auction
   122  	// from a snapshot (or protocol upgrade) with state form before the liquidity monitoring was still
   123  	// in place. This can be removed once we've deployed the version without liquidity monitoring.
   124  	// Liquidity auctions are no longer a thing, we know we're not in opening auction here
   125  	// if we're not in price auction, we should just let the liquidity auction expire
   126  	if m.as.Trigger() == types.AuctionTriggerLiquidityTargetNotMet || m.as.Trigger() == types.AuctionTriggerUnableToDeployLPOrders {
   127  		m.as.SetReadyToLeave()
   128  	}
   129  
   130  	if m.as.Trigger() == types.AuctionTriggerLongBlock || m.as.ExtensionTrigger() == types.AuctionTriggerLongBlock {
   131  		if endTS := m.as.ExpiresAt(); endTS != nil && endTS.Before(now) {
   132  			m.as.SetReadyToLeave()
   133  		}
   134  	}
   135  
   136  	// price and liquidity auctions
   137  	isPrice := m.as.IsPriceAuction() || m.as.IsPriceExtension()
   138  	if !isPrice {
   139  		m.checkBondBalance(ctx)
   140  	}
   141  	if isPrice || m.as.CanLeave() {
   142  		m.pMonitor.CheckPrice(ctx, m.as, indicativeUncrossingPrice, true, true)
   143  	}
   144  	end := m.as.CanLeave()
   145  	if isPrice && end {
   146  		m.checkBondBalance(ctx)
   147  	}
   148  	if evt := m.as.AuctionExtended(ctx, m.timeService.GetTimeNow()); evt != nil {
   149  		m.broker.Send(evt)
   150  		end = false
   151  	}
   152  	// price monitoring engine and liquidity monitoring engine both indicated auction can end
   153  	if end {
   154  		// can we leave based on the book state?
   155  		m.leaveAuction(ctx, now)
   156  	}
   157  
   158  	// This is where FBA handling will go
   159  }