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 }