code.vegaprotocol.io/vega@v0.79.0/core/execution/spot/market_snapshot.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 spot
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sort"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/assets"
    25  	"code.vegaprotocol.io/vega/core/execution/common"
    26  	"code.vegaprotocol.io/vega/core/execution/stoporders"
    27  	"code.vegaprotocol.io/vega/core/fee"
    28  	liquiditytarget "code.vegaprotocol.io/vega/core/liquidity/target/spot"
    29  	"code.vegaprotocol.io/vega/core/liquidity/v2"
    30  	"code.vegaprotocol.io/vega/core/matching"
    31  	"code.vegaprotocol.io/vega/core/monitor"
    32  	"code.vegaprotocol.io/vega/core/monitor/price"
    33  	"code.vegaprotocol.io/vega/core/positions"
    34  	"code.vegaprotocol.io/vega/core/products"
    35  	"code.vegaprotocol.io/vega/core/risk"
    36  	"code.vegaprotocol.io/vega/core/settlement"
    37  	"code.vegaprotocol.io/vega/core/types"
    38  	vgcontext "code.vegaprotocol.io/vega/libs/context"
    39  	"code.vegaprotocol.io/vega/libs/num"
    40  	"code.vegaprotocol.io/vega/logging"
    41  	snapshot "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    42  
    43  	"golang.org/x/exp/maps"
    44  )
    45  
    46  func NewMarketFromSnapshot(
    47  	ctx context.Context,
    48  	log *logging.Logger,
    49  	em *types.ExecSpotMarket,
    50  	riskConfig risk.Config,
    51  	positionConfig positions.Config,
    52  	settlementConfig settlement.Config,
    53  	matchingConfig matching.Config,
    54  	feeConfig fee.Config,
    55  	liquidityConfig liquidity.Config,
    56  	collateralEngine common.Collateral,
    57  	oracleEngine products.OracleEngine,
    58  	timeService common.TimeService,
    59  	broker common.Broker,
    60  	stateVarEngine common.StateVarEngine,
    61  	baseAssetDetails *assets.Asset,
    62  	quoteAssetDetails *assets.Asset,
    63  	marketActivityTracker *common.MarketActivityTracker,
    64  	peggedOrderNotify func(int64),
    65  	referralDiscountRewardService fee.ReferralDiscountRewardService,
    66  	volumeDiscountService fee.VolumeDiscountService,
    67  	volumeRebateService fee.VolumeRebateService,
    68  	banking common.Banking,
    69  ) (*Market, error) {
    70  	mkt := em.Market
    71  	if len(em.Market.ID) == 0 {
    72  		return nil, common.ErrEmptyMarketID
    73  	}
    74  	positionFactor := num.DecimalFromFloat(10).Pow(num.DecimalFromInt64(mkt.PositionDecimalPlaces))
    75  	priceFactor := num.DecimalOne()
    76  	if exp := int(quoteAssetDetails.DecimalPlaces()) - int(mkt.DecimalPlaces); exp != 0 {
    77  		priceFactor = num.DecimalFromInt64(10).Pow(num.DecimalFromInt64(int64(exp)))
    78  	}
    79  	baseFactor := num.DecimalFromFloat(10).Pow(num.DecimalFromInt64(int64(baseAssetDetails.DecimalPlaces()) - mkt.PositionDecimalPlaces))
    80  	as := monitor.NewAuctionStateFromSnapshot(mkt, em.AuctionState)
    81  
    82  	// @TODO -> the raw auctionstate shouldn't be something exposed to the matching engine
    83  	// as far as matching goes: it's either an auction or not
    84  	book := matching.NewCachedOrderBook(
    85  		log, matchingConfig, mkt.ID, as.InAuction(), peggedOrderNotify)
    86  	assets, err := mkt.GetAssets()
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	if len(assets) != 2 {
    92  		return nil, fmt.Errorf("expecting base asset and quote asset for spot market")
    93  	}
    94  
    95  	baseAsset := assets[BaseAssetIndex]
    96  	quoteAsset := assets[QuoteAssetIndex]
    97  
    98  	var feeEngine *fee.Engine
    99  	if em.FeesStats != nil {
   100  		feeEngine, err = fee.NewFromState(log, feeConfig, *mkt.Fees, quoteAsset, positionFactor, em.FeesStats)
   101  		if err != nil {
   102  			return nil, fmt.Errorf("unable to instantiate fee engine: %w", err)
   103  		}
   104  	} else {
   105  		feeEngine, err = fee.New(log, feeConfig, *mkt.Fees, quoteAsset, positionFactor)
   106  		if err != nil {
   107  			return nil, fmt.Errorf("unable to instantiate fee engine: %w", err)
   108  		}
   109  	}
   110  
   111  	tsCalc := liquiditytarget.NewSnapshotEngine(*mkt.LiquidityMonitoringParameters.TargetStakeParameters, mkt.ID, positionFactor)
   112  	riskModel, err := risk.NewModel(mkt.TradableInstrument.RiskModel, quoteAsset)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("unable to instantiate risk model: %w", err)
   115  	}
   116  	pMonitor, err := price.NewMonitorFromSnapshot(mkt.ID, quoteAsset, em.PriceMonitor, mkt.PriceMonitoringSettings, riskModel, as, stateVarEngine, log)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("unable to instantiate price monitoring engine: %w", err)
   119  	}
   120  	els := common.NewEquitySharesFromSnapshot(em.EquityShare)
   121  	liquidity := liquidity.NewSnapshotEngine(liquidityConfig, log, timeService, broker, riskModel, pMonitor, book, as, quoteAsset, mkt.ID, stateVarEngine, positionFactor, mkt.LiquiditySLAParams)
   122  
   123  	// if we're upgrading and the market liquidity state is nil, all we can do is take the old SLA values which will *probably* be the right ones
   124  	if vgcontext.InProgressUpgrade(ctx) && em.MarketLiquidity == nil {
   125  		em.MarketLiquidity = &snapshot.MarketLiquidity{
   126  			PriceRange: mkt.LiquiditySLAParams.PriceRange.String(),
   127  		}
   128  	}
   129  
   130  	marketLiquidity, err := common.NewMarketLiquidityFromSnapshot(log, liquidity, collateralEngine, broker, book, els, marketActivityTracker, feeEngine, common.SpotMarketType, mkt.ID, quoteAsset, priceFactor, em.MarketLiquidity, nil)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	// backward compatibility check for nil
   135  	stopOrders := stoporders.New(log)
   136  	if em.StopOrders != nil {
   137  		stopOrders = stoporders.NewFromProto(log, em.StopOrders)
   138  	} else {
   139  		// use the last markPrice for the market to initialise stopOrders price
   140  		if em.LastTradedPrice != nil {
   141  			stopOrders.PriceUpdated(em.LastTradedPrice.Clone())
   142  		}
   143  	}
   144  
   145  	expiringStopOrders := common.NewExpiringOrders()
   146  	if em.ExpiringStopOrders != nil {
   147  		expiringStopOrders = common.NewExpiringOrdersFromState(em.ExpiringStopOrders)
   148  	}
   149  
   150  	now := timeService.GetTimeNow()
   151  	allowedSellers := map[string]struct{}{}
   152  	for _, v := range mkt.AllowedSellers {
   153  		allowedSellers[v] = struct{}{}
   154  	}
   155  	market := &Market{
   156  		log:                           log,
   157  		mkt:                           mkt,
   158  		closingAt:                     time.Unix(0, mkt.MarketTimestamps.Close),
   159  		timeService:                   timeService,
   160  		matching:                      book,
   161  		collateral:                    collateralEngine,
   162  		broker:                        broker,
   163  		fee:                           feeEngine,
   164  		referralDiscountRewardService: referralDiscountRewardService,
   165  		volumeDiscountService:         volumeDiscountService,
   166  		volumeRebateService:           volumeRebateService,
   167  		liquidity:                     marketLiquidity,
   168  		liquidityEngine:               liquidity,
   169  		parties:                       map[string]struct{}{},
   170  		tsCalc:                        tsCalc,
   171  		feeSplitter:                   common.NewFeeSplitterFromSnapshot(em.FeeSplitter, now),
   172  		as:                            as,
   173  		pMonitor:                      pMonitor,
   174  		peggedOrders:                  common.NewPeggedOrdersFromSnapshot(log, timeService, em.PeggedOrders),
   175  		expiringOrders:                common.NewExpiringOrdersFromState(em.ExpiringOrders),
   176  		equityShares:                  els,
   177  		lastBestBidPrice:              em.LastBestBid.Clone(),
   178  		lastBestAskPrice:              em.LastBestAsk.Clone(),
   179  		lastMidBuyPrice:               em.LastMidBid.Clone(),
   180  		lastMidSellPrice:              em.LastMidAsk.Clone(),
   181  		markPrice:                     em.CurrentMarkPrice,
   182  		lastTradedPrice:               em.LastTradedPrice,
   183  		priceFactor:                   priceFactor,
   184  		lastMarketValueProxy:          em.LastMarketValueProxy,
   185  		lastEquityShareDistributed:    time.Unix(0, em.LastEquityShareDistributed),
   186  		marketActivityTracker:         marketActivityTracker,
   187  		positionFactor:                positionFactor,
   188  		stateVarEngine:                stateVarEngine,
   189  		baseFactor:                    baseFactor,
   190  		baseAsset:                     baseAsset,
   191  		quoteAsset:                    quoteAsset,
   192  		stopOrders:                    stopOrders,
   193  		expiringStopOrders:            expiringStopOrders,
   194  		hasTraded:                     em.HasTraded,
   195  		orderHoldingTracker:           NewHoldingAccountTracker(mkt.ID, log, collateralEngine),
   196  		banking:                       banking,
   197  		allowedSellers:                allowedSellers,
   198  	}
   199  	liquidity.SetGetStaticPricesFunc(market.getBestStaticPricesDecimal)
   200  	for _, p := range em.Parties {
   201  		market.parties[p] = struct{}{}
   202  	}
   203  
   204  	if em.Closed {
   205  		market.closed = true
   206  		stateVarEngine.UnregisterStateVariable(quoteAsset, mkt.ID)
   207  	}
   208  	market.quoteAssetDP = uint32(quoteAssetDetails.DecimalPlaces())
   209  	pap, err := market.NewProtocolAutomatedPurchaseFromSnapshot(ctx, oracleEngine, em.ProtocolAutomatedPurchase)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	market.pap = pap
   214  	return market, nil
   215  }
   216  
   217  func (m *Market) GetState() *types.ExecSpotMarket {
   218  	parties := maps.Keys(m.parties)
   219  	sort.Strings(parties)
   220  	quoteAssetQuantum, _ := m.collateral.GetAssetQuantum(m.quoteAsset)
   221  
   222  	var pap *snapshot.ProtocolAutomatedPurchase
   223  	if m.pap != nil {
   224  		pap = m.pap.IntoProto()
   225  	}
   226  	em := &types.ExecSpotMarket{
   227  		Market:                     m.mkt.DeepClone(),
   228  		PriceMonitor:               m.pMonitor.GetState(),
   229  		AuctionState:               m.as.GetState(),
   230  		PeggedOrders:               m.peggedOrders.GetState(),
   231  		ExpiringOrders:             m.expiringOrders.GetState(),
   232  		LastBestBid:                m.lastBestBidPrice.Clone(),
   233  		LastBestAsk:                m.lastBestAskPrice.Clone(),
   234  		LastMidBid:                 m.lastMidBuyPrice.Clone(),
   235  		LastMidAsk:                 m.lastMidSellPrice.Clone(),
   236  		LastMarketValueProxy:       m.lastMarketValueProxy,
   237  		CurrentMarkPrice:           m.markPrice,
   238  		LastTradedPrice:            m.lastTradedPrice,
   239  		LastEquityShareDistributed: m.lastEquityShareDistributed.UnixNano(),
   240  		EquityShare:                m.equityShares.GetState(),
   241  		FeeSplitter:                m.feeSplitter.GetState(),
   242  		NextMTM:                    m.nextMTM.UnixNano(),
   243  		Parties:                    parties,
   244  		Closed:                     m.closed,
   245  		HasTraded:                  m.hasTraded,
   246  		FeesStats:                  m.fee.GetState(quoteAssetQuantum),
   247  		MarketLiquidity:            m.liquidity.GetState(),
   248  		StopOrders:                 m.stopOrders.ToProto(),
   249  		ExpiringStopOrders:         m.expiringStopOrders.GetState(),
   250  		ProtocolAutomatedPurchase:  pap,
   251  	}
   252  
   253  	return em
   254  }
   255  
   256  func (m *Market) GetNewStateProviders() []types.StateProvider {
   257  	return []types.StateProvider{m.matching, m.tsCalc, m.orderHoldingTracker, m.liquidityEngine.V2StateProvider()}
   258  }
   259  
   260  func (m *Market) GetFillPrice(volume uint64, side types.Side) (*num.Uint, error) {
   261  	return m.matching.GetFillPrice(volume, side)
   262  }