code.vegaprotocol.io/vega@v0.79.0/core/integration/steps/the_markets.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 steps
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/collateral"
    25  	"code.vegaprotocol.io/vega/core/datasource"
    26  	"code.vegaprotocol.io/vega/core/datasource/external/signedoracle"
    27  	"code.vegaprotocol.io/vega/core/integration/steps/market"
    28  	"code.vegaprotocol.io/vega/core/netparams"
    29  	"code.vegaprotocol.io/vega/core/types"
    30  	"code.vegaprotocol.io/vega/libs/num"
    31  	"code.vegaprotocol.io/vega/libs/ptr"
    32  	proto "code.vegaprotocol.io/vega/protos/vega"
    33  
    34  	"github.com/cucumber/godog"
    35  )
    36  
    37  func TheMarketsUpdated(
    38  	config *market.Config,
    39  	executionEngine Execution,
    40  	existing []types.Market,
    41  	netparams *netparams.Store,
    42  	table *godog.Table,
    43  ) ([]types.Market, error) {
    44  	rows := parseMarketsUpdateTable(table)
    45  	// existing markets to update
    46  	validByID := make(map[string]*types.Market, len(existing))
    47  	for i := range existing {
    48  		m := existing[i]
    49  		validByID[m.ID] = &existing[i]
    50  	}
    51  	updates := make([]types.UpdateMarket, 0, len(rows))
    52  	updated := make([]*types.Market, 0, len(rows))
    53  	for _, row := range rows {
    54  		upd := marketUpdateRow{row: row}
    55  		// check if market exists
    56  		current, ok := validByID[upd.id()]
    57  		if !ok {
    58  			return nil, fmt.Errorf("unknown market id %s", upd.id())
    59  		}
    60  
    61  		mUpdate, err := marketUpdate(config, current, upd)
    62  		if err != nil {
    63  			return existing, err
    64  		}
    65  
    66  		updates = append(updates, mUpdate)
    67  		updated = append(updated, current)
    68  	}
    69  	if err := updateMarkets(updated, updates, executionEngine); err != nil {
    70  		return nil, err
    71  	}
    72  	// we have been using pointers internally, so we should be returning the accurate state here.
    73  	return existing, nil
    74  }
    75  
    76  func TheMarkets(
    77  	config *market.Config,
    78  	executionEngine Execution,
    79  	collateralEngine *collateral.Engine,
    80  	netparams *netparams.Store,
    81  	now time.Time,
    82  	table *godog.Table,
    83  ) ([]types.Market, error) {
    84  	rows := parseMarketsTable(table)
    85  	markets := make([]types.Market, 0, len(rows))
    86  
    87  	for _, row := range rows {
    88  		mRow := marketRow{row: row}
    89  		isPerp := mRow.isPerp()
    90  		if !isPerp {
    91  			// check if we have a perp counterpart for this oracle, if so, swap to that
    92  			if oName := mRow.oracleConfig(); oName != config.OracleConfigs.CheckName(oName) {
    93  				isPerp = true
    94  			}
    95  		}
    96  		var mkt types.Market
    97  		if isPerp {
    98  			mkt = newPerpMarket(config, mRow)
    99  		} else {
   100  			mkt = newMarket(config, mRow)
   101  		}
   102  		markets = append(markets, mkt)
   103  	}
   104  
   105  	if err := enableMarketAssets(markets, collateralEngine); err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	if err := enableVoteAsset(collateralEngine); err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	for i, row := range rows {
   114  		if err := executionEngine.SubmitMarket(context.Background(), &markets[i], "proposerID", now); err != nil {
   115  			return nil, fmt.Errorf("couldn't submit market(%s): %v", markets[i].ID, err)
   116  		}
   117  		// only start opening auction if the market is explicitly marked to leave opening auction now
   118  		if !row.HasColumn("is passed") || row.Bool("is passed") {
   119  			if err := executionEngine.StartOpeningAuction(context.Background(), markets[i].ID); err != nil {
   120  				return nil, fmt.Errorf("could not start opening auction for market %s: %v", markets[i].ID, err)
   121  			}
   122  		}
   123  	}
   124  	return markets, nil
   125  }
   126  
   127  func TheSuccesorMarketIsEnacted(sID string, markets []types.Market, exec Execution) error {
   128  	for _, mkt := range markets {
   129  		if mkt.ID == sID {
   130  			parent := mkt.ParentMarketID
   131  			if err := exec.SucceedMarket(context.Background(), sID, parent); err != nil {
   132  				return fmt.Errorf("couldn't enact the successor market %s (parent: %s): %v", sID, parent, err)
   133  			}
   134  			return nil
   135  		}
   136  	}
   137  	return fmt.Errorf("couldn't enact successor market %s - no such market ID", sID)
   138  }
   139  
   140  func updateMarkets(markets []*types.Market, updates []types.UpdateMarket, executionEngine Execution) error {
   141  	for i, mkt := range markets {
   142  		if err := executionEngine.UpdateMarket(context.Background(), mkt); err != nil {
   143  			return fmt.Errorf("couldn't update market(%s) - updates %#v: %+v", mkt.ID, updates[i], err)
   144  		}
   145  	}
   146  	return nil
   147  }
   148  
   149  func enableMarketAssets(markets []types.Market, collateralEngine *collateral.Engine) error {
   150  	assetsToEnable := map[string]struct{}{}
   151  	for _, mkt := range markets {
   152  		assets, _ := mkt.GetAssets()
   153  		assetsToEnable[assets[0]] = struct{}{}
   154  	}
   155  	for assetToEnable := range assetsToEnable {
   156  		err := collateralEngine.EnableAsset(context.Background(), types.Asset{
   157  			ID: assetToEnable,
   158  			Details: &types.AssetDetails{
   159  				Quantum: num.DecimalOne(),
   160  				Symbol:  assetToEnable,
   161  			},
   162  		})
   163  		if err != nil && err != collateral.ErrAssetAlreadyEnabled {
   164  			return fmt.Errorf("couldn't enable asset(%s): %v", assetToEnable, err)
   165  		}
   166  	}
   167  	return nil
   168  }
   169  
   170  func enableVoteAsset(collateralEngine *collateral.Engine) error {
   171  	voteAsset := types.Asset{
   172  		ID: "VOTE",
   173  		Details: &types.AssetDetails{
   174  			Name:     "VOTE",
   175  			Symbol:   "VOTE",
   176  			Decimals: 5,
   177  			Source: &types.AssetDetailsBuiltinAsset{
   178  				BuiltinAsset: &types.BuiltinAsset{
   179  					MaxFaucetAmountMint: num.NewUint(10),
   180  				},
   181  			},
   182  		},
   183  	}
   184  
   185  	err := collateralEngine.EnableAsset(context.Background(), voteAsset)
   186  	if err != nil {
   187  		if err != collateral.ErrAssetAlreadyEnabled {
   188  			return fmt.Errorf("couldn't enable asset(%s): %v", voteAsset.ID, err)
   189  		}
   190  	}
   191  	return nil
   192  }
   193  
   194  // marketUpdate return the UpdateMarket type just for clear error reporting and sanity checks ATM.
   195  func marketUpdate(config *market.Config, existing *types.Market, row marketUpdateRow) (types.UpdateMarket, error) {
   196  	update := types.UpdateMarket{
   197  		MarketID: existing.ID,
   198  		Changes:  &types.UpdateMarketConfiguration{},
   199  	}
   200  	liqStrat := existing.LiquidationStrategy
   201  	if ls, ok := row.liquidationStrat(); ok {
   202  		lqs, err := config.LiquidationStrat.Get(ls)
   203  		if err != nil {
   204  			panic(err)
   205  		}
   206  		if liqStrat, err = types.LiquidationStrategyFromProto(lqs); err != nil {
   207  			panic(err)
   208  		}
   209  	}
   210  	update.Changes.LiquidationStrategy = liqStrat
   211  	existing.LiquidationStrategy = liqStrat
   212  
   213  	// product update
   214  	if oracle, ok := row.oracleConfig(); ok {
   215  		// update product -> use type switch even though currently only futures exist
   216  		switch ti := existing.TradableInstrument.Instrument.Product.(type) {
   217  		case *types.InstrumentFuture:
   218  			oracleSettlement, err := config.OracleConfigs.GetFuture(oracle, "settlement data")
   219  			if err != nil {
   220  				panic(err)
   221  			}
   222  			oracleTermination, err := config.OracleConfigs.GetFuture(oracle, "trading termination")
   223  			if err != nil {
   224  				panic(err)
   225  			}
   226  			// we probably want to X-check the current spec, and make sure only filters + pubkeys are changed
   227  			settleSpec := datasource.FromOracleSpecProto(oracleSettlement.Spec)
   228  			termSpec := datasource.FromOracleSpecProto(oracleTermination.Spec)
   229  			settlementDecimals := config.OracleConfigs.GetSettlementDataDP(oracle)
   230  			filters := settleSpec.Data.GetFilters()
   231  			futureUp := &types.UpdateFutureProduct{
   232  				QuoteName: ti.Future.QuoteName,
   233  				DataSourceSpecForSettlementData: *datasource.NewDefinition(
   234  					datasource.ContentTypeOracle,
   235  				).SetOracleConfig(
   236  					&signedoracle.SpecConfiguration{
   237  						Signers: settleSpec.Data.GetSigners(),
   238  						Filters: filters,
   239  					},
   240  				),
   241  				DataSourceSpecForTradingTermination: *datasource.NewDefinition(
   242  					datasource.ContentTypeOracle,
   243  				).SetOracleConfig(
   244  					&signedoracle.SpecConfiguration{
   245  						Signers: settleSpec.Data.GetSigners(),
   246  						Filters: filters,
   247  					},
   248  				),
   249  				DataSourceSpecBinding: datasource.SpecBindingForFutureFromProto(&proto.DataSourceSpecToFutureBinding{
   250  					SettlementDataProperty:     oracleSettlement.Binding.SettlementDataProperty,
   251  					TradingTerminationProperty: oracleTermination.Binding.TradingTerminationProperty,
   252  				}),
   253  			}
   254  			ti.Future.DataSourceSpecForSettlementData = datasource.SpecFromDefinition(*settleSpec.Data.SetFilterDecimals(uint64(settlementDecimals)))
   255  			ti.Future.DataSourceSpecForTradingTermination = termSpec
   256  			ti.Future.DataSourceSpecBinding = futureUp.DataSourceSpecBinding
   257  			// ensure we update the existing market
   258  			existing.TradableInstrument.Instrument.Product = ti
   259  			update.Changes.Instrument = &types.UpdateInstrumentConfiguration{
   260  				Product: &types.UpdateInstrumentConfigurationFuture{
   261  					Future: futureUp,
   262  				},
   263  			}
   264  		case *types.InstrumentPerps:
   265  			perp, err := config.OracleConfigs.GetFullPerp(oracle)
   266  			if err != nil {
   267  				panic(err)
   268  			}
   269  			pfp := types.PerpsFromProto(perp)
   270  			if pfp.DataSourceSpecForSettlementData == nil || pfp.DataSourceSpecForSettlementData.Data == nil {
   271  				panic("Oracle does not have a data source for settlement data")
   272  			}
   273  			if pfp.DataSourceSpecForSettlementSchedule == nil || pfp.DataSourceSpecForSettlementSchedule.Data == nil {
   274  				panic("Oracle does not have a data source for settlement schedule")
   275  			}
   276  			update.Changes.Instrument = &types.UpdateInstrumentConfiguration{
   277  				Product: &types.UpdateInstrumentConfigurationPerps{
   278  					Perps: &types.UpdatePerpsProduct{
   279  						QuoteName:                           pfp.QuoteName,
   280  						MarginFundingFactor:                 pfp.MarginFundingFactor,
   281  						ClampLowerBound:                     pfp.ClampLowerBound,
   282  						ClampUpperBound:                     pfp.ClampUpperBound,
   283  						FundingRateScalingFactor:            pfp.FundingRateScalingFactor,
   284  						FundingRateLowerBound:               pfp.FundingRateLowerBound,
   285  						FundingRateUpperBound:               pfp.FundingRateUpperBound,
   286  						DataSourceSpecForSettlementData:     *pfp.DataSourceSpecForSettlementData.Data,
   287  						DataSourceSpecForSettlementSchedule: *pfp.DataSourceSpecForSettlementSchedule.Data,
   288  						DataSourceSpecBinding:               pfp.DataSourceSpecBinding,
   289  					},
   290  				},
   291  			}
   292  			// apply update
   293  			ti.Perps.ClampLowerBound = pfp.ClampLowerBound
   294  			ti.Perps.ClampUpperBound = pfp.ClampUpperBound
   295  			ti.Perps.FundingRateScalingFactor = pfp.FundingRateScalingFactor
   296  			ti.Perps.FundingRateUpperBound = pfp.FundingRateUpperBound
   297  			ti.Perps.FundingRateLowerBound = pfp.FundingRateLowerBound
   298  			ti.Perps.MarginFundingFactor = pfp.MarginFundingFactor
   299  			ti.Perps.DataSourceSpecBinding = pfp.DataSourceSpecBinding
   300  			ti.Perps.DataSourceSpecForSettlementData = pfp.DataSourceSpecForSettlementData
   301  			ti.Perps.DataSourceSpecForSettlementSchedule = pfp.DataSourceSpecForSettlementSchedule
   302  			existing.TradableInstrument.Instrument.Product = ti
   303  		default:
   304  			panic("unsuported product")
   305  		}
   306  		update.Changes.Instrument.Code = existing.TradableInstrument.Instrument.Code
   307  	}
   308  	// price monitoring
   309  	if pm, ok := row.priceMonitoring(); ok {
   310  		priceMonitoring, err := config.PriceMonitoring.Get(pm)
   311  		if err != nil {
   312  			panic(err)
   313  		}
   314  		pmt := types.PriceMonitoringSettingsFromProto(priceMonitoring)
   315  		// update existing
   316  		existing.PriceMonitoringSettings.Parameters = pmt.Parameters
   317  		update.Changes.PriceMonitoringParameters = pmt.Parameters
   318  	}
   319  	// liquidity monitoring
   320  	if lm, ok := row.liquidityMonitoring(); ok {
   321  		liqMon, err := config.LiquidityMonitoring.GetType(lm)
   322  		if err != nil {
   323  			panic(err)
   324  		}
   325  		existing.LiquidityMonitoringParameters = liqMon
   326  		update.Changes.LiquidityMonitoringParameters = liqMon
   327  	}
   328  	// risk model
   329  	if rm, ok := row.riskModel(); ok {
   330  		tip := existing.TradableInstrument.IntoProto()
   331  		if err := config.RiskModels.LoadModel(rm, tip); err != nil {
   332  			panic(err)
   333  		}
   334  		current := types.TradableInstrumentFromProto(tip)
   335  		// find the correct params:
   336  		switch {
   337  		case current.GetSimpleRiskModel() != nil:
   338  			update.Changes.RiskParameters = types.UpdateMarketConfigurationSimple{
   339  				Simple: current.GetSimpleRiskModel().Params,
   340  			}
   341  		case current.GetLogNormalRiskModel() != nil:
   342  			update.Changes.RiskParameters = types.UpdateMarketConfigurationLogNormal{
   343  				LogNormal: current.GetLogNormalRiskModel(),
   344  			}
   345  		default:
   346  			panic("Unsupported risk model parameters")
   347  		}
   348  		// update existing
   349  		existing.TradableInstrument = current
   350  	}
   351  	// linear linSlippage factor
   352  	linSlippage, ok := row.tryLinearSlippageFactor()
   353  	if ok {
   354  		slippageD := num.DecimalFromFloat(linSlippage)
   355  		update.Changes.LinearSlippageFactor = slippageD
   356  		existing.LinearSlippageFactor = slippageD
   357  	} else {
   358  		update.Changes.LinearSlippageFactor = existing.LinearSlippageFactor
   359  	}
   360  
   361  	// quadratic slippage factor
   362  	quadSlippage, ok := row.tryQuadraticSlippageFactor()
   363  	if ok {
   364  		slippageD := num.DecimalFromFloat(quadSlippage)
   365  		update.Changes.QuadraticSlippageFactor = slippageD
   366  		existing.QuadraticSlippageFactor = slippageD
   367  	} else {
   368  		update.Changes.QuadraticSlippageFactor = existing.QuadraticSlippageFactor
   369  	}
   370  
   371  	if liquiditySla, ok := row.tryLiquiditySLA(); ok {
   372  		sla, err := config.LiquiditySLAParams.Get(liquiditySla)
   373  		if err != nil {
   374  			return update, err
   375  		}
   376  		slaParams := types.LiquiditySLAParamsFromProto(sla)
   377  		// update existing
   378  		existing.LiquiditySLAParams = slaParams
   379  		update.Changes.LiquiditySLAParameters = slaParams
   380  	}
   381  
   382  	update.Changes.LiquidityFeeSettings = existing.Fees.LiquidityFeeSettings
   383  	if liquidityFeeSettings, ok := row.tryLiquidityFeeSettings(); ok {
   384  		settings, err := config.FeesConfig.Get(liquidityFeeSettings)
   385  		if err != nil {
   386  			return update, err
   387  		}
   388  		s := types.LiquidityFeeSettingsFromProto(settings.LiquidityFeeSettings)
   389  		existing.Fees.LiquidityFeeSettings = s
   390  		update.Changes.LiquidityFeeSettings = s
   391  	}
   392  
   393  	if existing.MarkPriceConfiguration != nil {
   394  		markPriceConfig := existing.MarkPriceConfiguration.DeepClone()
   395  		markPriceConfig.CompositePriceType = row.markPriceType()
   396  
   397  		if row.row.HasColumn("decay power") {
   398  			markPriceConfig.DecayPower = row.decayPower()
   399  		}
   400  		if row.row.HasColumn("decay weight") {
   401  			markPriceConfig.DecayWeight = row.decayWeight()
   402  		}
   403  		if row.row.HasColumn("cash amount") {
   404  			markPriceConfig.CashAmount = row.cashAmount()
   405  		}
   406  		if row.row.HasColumn("source weights") {
   407  			markPriceConfig.SourceWeights = row.priceSourceWeights()
   408  		}
   409  		if row.row.HasColumn("source staleness tolerance") {
   410  			markPriceConfig.SourceStalenessTolerance = row.priceSourceStalnessTolerance()
   411  		}
   412  		if row.row.HasColumn("oracle1") {
   413  			markPriceConfig.DataSources, markPriceConfig.SpecBindingForCompositePrice = row.oracles(config)
   414  		}
   415  		update.Changes.MarkPriceConfiguration = markPriceConfig
   416  		existing.MarkPriceConfiguration = markPriceConfig
   417  	}
   418  	update.Changes.TickSize = row.tickSize()
   419  	return update, nil
   420  }
   421  
   422  func newPerpMarket(config *market.Config, row marketRow) types.Market {
   423  	fees, err := config.FeesConfig.Get(row.fees())
   424  	if err != nil {
   425  		panic(err)
   426  	}
   427  
   428  	perp, err := config.OracleConfigs.GetFullPerp(row.oracleConfig())
   429  	if err != nil {
   430  		panic(err)
   431  	}
   432  	pfp := types.PerpsFromProto(perp)
   433  	asset, quote := row.asset(), row.quoteName()
   434  	// long term, this should become redundant, but for the perps flag this is useful to have
   435  	if asset != pfp.SettlementAsset {
   436  		pfp.SettlementAsset = asset
   437  	}
   438  	if quote != pfp.QuoteName {
   439  		pfp.QuoteName = row.quoteName()
   440  	}
   441  
   442  	priceMonitoring, err := config.PriceMonitoring.Get(row.priceMonitoring())
   443  	if err != nil {
   444  		panic(err)
   445  	}
   446  
   447  	marginCalculator, err := config.MarginCalculators.Get(row.marginCalculator())
   448  	if err != nil {
   449  		panic(err)
   450  	}
   451  
   452  	liqMon, err := config.LiquidityMonitoring.GetType(row.liquidityMonitoring())
   453  	if err != nil {
   454  		panic(err)
   455  	}
   456  	lqs, err := config.LiquidationStrat.Get(row.liquidationStrat())
   457  	if err != nil {
   458  		panic(err)
   459  	}
   460  	liqStrat, err := types.LiquidationStrategyFromProto(lqs)
   461  	if err != nil {
   462  		panic(err)
   463  	}
   464  
   465  	linearSlippageFactor := row.linearSlippageFactor()
   466  	quadraticSlippageFactor := row.quadraticSlippageFactor()
   467  
   468  	slaParams, err := config.LiquiditySLAParams.Get(row.liquiditySLA())
   469  	if err != nil {
   470  		panic(err)
   471  	}
   472  
   473  	pCap := row.getCapped()
   474  	specs, binding := row.oracles(config)
   475  	markPriceConfig := &types.CompositePriceConfiguration{
   476  		CompositePriceType:           row.markPriceType(),
   477  		DecayWeight:                  row.decayWeight(),
   478  		DecayPower:                   row.decayPower(),
   479  		CashAmount:                   row.cashAmount(),
   480  		SourceWeights:                row.priceSourceWeights(),
   481  		SourceStalenessTolerance:     row.priceSourceStalnessTolerance(),
   482  		DataSources:                  specs,
   483  		SpecBindingForCompositePrice: binding,
   484  	}
   485  
   486  	m := types.Market{
   487  		TradingMode:           types.MarketTradingModeContinuous,
   488  		State:                 types.MarketStateActive,
   489  		ID:                    row.id(),
   490  		DecimalPlaces:         row.decimalPlaces(),
   491  		PositionDecimalPlaces: row.positionDecimalPlaces(),
   492  		Fees:                  types.FeesFromProto(fees),
   493  		LiquidationStrategy:   liqStrat,
   494  		TradableInstrument: &types.TradableInstrument{
   495  			Instrument: &types.Instrument{
   496  				ID:   fmt.Sprintf("Crypto/%s/Perpetual", row.id()),
   497  				Code: fmt.Sprintf("CRYPTO/%v", row.id()),
   498  				Name: fmt.Sprintf("%s perpetual", row.id()),
   499  				Metadata: &types.InstrumentMetadata{
   500  					Tags: []string{
   501  						"asset_class:fx/crypto",
   502  						"product:perpetual",
   503  					},
   504  				},
   505  				Product: &types.InstrumentPerps{
   506  					Perps: pfp,
   507  				},
   508  			},
   509  			MarginCalculator: types.MarginCalculatorFromProto(marginCalculator),
   510  		},
   511  		OpeningAuction:                openingAuction(row),
   512  		PriceMonitoringSettings:       types.PriceMonitoringSettingsFromProto(priceMonitoring),
   513  		LiquidityMonitoringParameters: liqMon,
   514  		LinearSlippageFactor:          num.DecimalFromFloat(linearSlippageFactor),
   515  		QuadraticSlippageFactor:       num.DecimalFromFloat(quadraticSlippageFactor),
   516  		LiquiditySLAParams:            types.LiquiditySLAParamsFromProto(slaParams),
   517  		MarkPriceConfiguration:        markPriceConfig,
   518  		TickSize:                      row.tickSize(),
   519  	}
   520  
   521  	if row.isSuccessor() {
   522  		m.ParentMarketID = row.parentID()
   523  		m.InsurancePoolFraction = row.insuranceFraction()
   524  		// increase opening auction duration by a given amount
   525  		m.OpeningAuction.Duration += row.successorAuction()
   526  	}
   527  
   528  	tip := m.TradableInstrument.IntoProto()
   529  	err = config.RiskModels.LoadModel(row.riskModel(), tip)
   530  	if row.IsCapped() {
   531  		tip.MarginCalculator.FullyCollateralised = ptr.From(pCap.FullyCollateralised)
   532  	}
   533  	m.TradableInstrument = types.TradableInstrumentFromProto(tip)
   534  	if err != nil {
   535  		panic(err)
   536  	}
   537  
   538  	return m
   539  }
   540  
   541  func newMarket(config *market.Config, row marketRow) types.Market {
   542  	fees, err := config.FeesConfig.Get(row.fees())
   543  	if err != nil {
   544  		panic(err)
   545  	}
   546  
   547  	oracleConfigForSettlement, err := config.OracleConfigs.GetFuture(row.oracleConfig(), "settlement data")
   548  	if err != nil {
   549  		panic(err)
   550  	}
   551  
   552  	oracleConfigForTradingTermination, err := config.OracleConfigs.GetFuture(row.oracleConfig(), "trading termination")
   553  	if err != nil {
   554  		panic(err)
   555  	}
   556  
   557  	settlementDataDecimals := config.OracleConfigs.GetSettlementDataDP(row.oracleConfig())
   558  	settlSpec := datasource.FromOracleSpecProto(oracleConfigForSettlement.Spec)
   559  	var binding proto.DataSourceSpecToFutureBinding
   560  	binding.SettlementDataProperty = oracleConfigForSettlement.Binding.SettlementDataProperty
   561  	binding.TradingTerminationProperty = oracleConfigForTradingTermination.Binding.TradingTerminationProperty
   562  
   563  	priceMonitoring, err := config.PriceMonitoring.Get(row.priceMonitoring())
   564  	if err != nil {
   565  		panic(err)
   566  	}
   567  
   568  	marginCalculator, err := config.MarginCalculators.Get(row.marginCalculator())
   569  	if err != nil {
   570  		panic(err)
   571  	}
   572  
   573  	liqMon, err := config.LiquidityMonitoring.GetType(row.liquidityMonitoring())
   574  	if err != nil {
   575  		panic(err)
   576  	}
   577  
   578  	lqs, err := config.LiquidationStrat.Get(row.liquidationStrat())
   579  	if err != nil {
   580  		panic(err)
   581  	}
   582  	liqStrat, err := types.LiquidationStrategyFromProto(lqs)
   583  	if err != nil {
   584  		panic(err)
   585  	}
   586  
   587  	linearSlippageFactor := row.linearSlippageFactor()
   588  	quadraticSlippageFactor := row.quadraticSlippageFactor()
   589  
   590  	slaParams, err := config.LiquiditySLAParams.Get(row.liquiditySLA())
   591  	if err != nil {
   592  		panic(err)
   593  	}
   594  
   595  	sources, bindings := row.oracles(config)
   596  	markPriceConfig := &types.CompositePriceConfiguration{
   597  		CompositePriceType:           row.markPriceType(),
   598  		DecayWeight:                  row.decayWeight(),
   599  		DecayPower:                   row.decayPower(),
   600  		CashAmount:                   row.cashAmount(),
   601  		SourceWeights:                row.priceSourceWeights(),
   602  		SourceStalenessTolerance:     row.priceSourceStalnessTolerance(),
   603  		DataSources:                  sources,
   604  		SpecBindingForCompositePrice: bindings,
   605  	}
   606  
   607  	pCap := row.getCapped()
   608  	m := types.Market{
   609  		TradingMode:           types.MarketTradingModeContinuous,
   610  		State:                 types.MarketStateActive,
   611  		ID:                    row.id(),
   612  		DecimalPlaces:         row.decimalPlaces(),
   613  		PositionDecimalPlaces: row.positionDecimalPlaces(),
   614  		Fees:                  types.FeesFromProto(fees),
   615  		LiquidationStrategy:   liqStrat,
   616  		TradableInstrument: &types.TradableInstrument{
   617  			Instrument: &types.Instrument{
   618  				ID:   fmt.Sprintf("Crypto/%s/Futures", row.id()),
   619  				Code: fmt.Sprintf("CRYPTO/%v", row.id()),
   620  				Name: fmt.Sprintf("%s future", row.id()),
   621  				Metadata: &types.InstrumentMetadata{
   622  					Tags: []string{
   623  						"asset_class:fx/crypto",
   624  						"product:futures",
   625  					},
   626  				},
   627  				Product: &types.InstrumentFuture{
   628  					Future: &types.Future{
   629  						SettlementAsset:                     row.asset(),
   630  						QuoteName:                           row.quoteName(),
   631  						DataSourceSpecForSettlementData:     datasource.SpecFromDefinition(*settlSpec.Data.SetFilterDecimals(uint64(settlementDataDecimals))),
   632  						DataSourceSpecForTradingTermination: datasource.SpecFromProto(oracleConfigForTradingTermination.Spec.ExternalDataSourceSpec.Spec),
   633  						DataSourceSpecBinding:               datasource.SpecBindingForFutureFromProto(&binding),
   634  						Cap:                                 pCap,
   635  					},
   636  				},
   637  			},
   638  			MarginCalculator: types.MarginCalculatorFromProto(marginCalculator),
   639  		},
   640  		OpeningAuction:                openingAuction(row),
   641  		PriceMonitoringSettings:       types.PriceMonitoringSettingsFromProto(priceMonitoring),
   642  		LiquidityMonitoringParameters: liqMon,
   643  		LinearSlippageFactor:          num.DecimalFromFloat(linearSlippageFactor),
   644  		QuadraticSlippageFactor:       num.DecimalFromFloat(quadraticSlippageFactor),
   645  		LiquiditySLAParams:            types.LiquiditySLAParamsFromProto(slaParams),
   646  		MarkPriceConfiguration:        markPriceConfig,
   647  		TickSize:                      row.tickSize(),
   648  		AllowedEmptyAmmLevels:         row.allowedEmptyAMMLevels(),
   649  	}
   650  
   651  	if row.isSuccessor() {
   652  		m.ParentMarketID = row.parentID()
   653  		m.InsurancePoolFraction = row.insuranceFraction()
   654  		// increase opening auction duration by a given amount
   655  		m.OpeningAuction.Duration += row.successorAuction()
   656  	}
   657  
   658  	tip := m.TradableInstrument.IntoProto()
   659  	if row.IsCapped() {
   660  		tip.MarginCalculator.FullyCollateralised = ptr.From(pCap.FullyCollateralised)
   661  	}
   662  	err = config.RiskModels.LoadModel(row.riskModel(), tip)
   663  	m.TradableInstrument = types.TradableInstrumentFromProto(tip)
   664  	if err != nil {
   665  		panic(err)
   666  	}
   667  
   668  	return m
   669  }
   670  
   671  func openingAuction(row marketRow) *types.AuctionDuration {
   672  	auction := &types.AuctionDuration{
   673  		Duration: row.auctionDuration(),
   674  	}
   675  
   676  	if auction.Duration <= 0 {
   677  		auction = nil
   678  	}
   679  	return auction
   680  }
   681  
   682  func parseMarketsTable(table *godog.Table) []RowWrapper {
   683  	return StrictParseTable(table, []string{
   684  		"id",
   685  		"quote name",
   686  		"asset",
   687  		"risk model",
   688  		"fees",
   689  		"data source config",
   690  		"price monitoring",
   691  		"margin calculator",
   692  		"auction duration",
   693  		"linear slippage factor",
   694  		"quadratic slippage factor",
   695  		"sla params",
   696  	}, []string{
   697  		"decimal places",
   698  		"position decimal places",
   699  		"liquidity monitoring",
   700  		"parent market id",
   701  		"insurance pool fraction",
   702  		"successor auction",
   703  		"is passed",
   704  		"market type",
   705  		"liquidation strategy",
   706  		"price type",
   707  		"decay weight",
   708  		"decay power",
   709  		"cash amount",
   710  		"source weights",
   711  		"source staleness tolerance",
   712  		"oracle1",
   713  		"oracle2",
   714  		"oracle3",
   715  		"oracle4",
   716  		"oracle5",
   717  		"tick size",
   718  		"max price cap",
   719  		"binary",
   720  		"fully collateralised",
   721  		"allowed empty amm levels",
   722  	})
   723  }
   724  
   725  func parseMarketsUpdateTable(table *godog.Table) []RowWrapper {
   726  	return StrictParseTable(table, []string{
   727  		"id",
   728  	}, []string{
   729  		"linear slippage factor",
   730  		"quadratic slippage factor",
   731  		"data source config",   // product update
   732  		"price monitoring",     // price monitoring update
   733  		"risk model",           // risk model update
   734  		"liquidity monitoring", // liquidity monitoring update
   735  		"sla params",
   736  		"liquidity fee settings",
   737  		"liquidation strategy",
   738  		"price type",
   739  		"decay weight",
   740  		"decay power",
   741  		"cash amount",
   742  		"source weights",
   743  		"source staleness tolerance",
   744  		"oracle1",
   745  		"oracle2",
   746  		"oracle3",
   747  		"oracle4",
   748  		"oracle5",
   749  		"tick size",
   750  	})
   751  }
   752  
   753  type marketRow struct {
   754  	row RowWrapper
   755  }
   756  
   757  type marketUpdateRow struct {
   758  	row RowWrapper
   759  }
   760  
   761  func (r marketUpdateRow) id() string {
   762  	return r.row.MustStr("id")
   763  }
   764  
   765  func (r marketUpdateRow) tickSize() *num.Uint {
   766  	if r.row.HasColumn("tick size") {
   767  		return num.MustUintFromString(r.row.MustStr("tick size"), 10)
   768  	}
   769  	return num.UintOne()
   770  }
   771  
   772  func (r marketUpdateRow) oracleConfig() (string, bool) {
   773  	if r.row.HasColumn("data source config") {
   774  		oc := r.row.MustStr("data source config")
   775  		return oc, true
   776  	}
   777  	return "", false
   778  }
   779  
   780  func (r marketUpdateRow) priceMonitoring() (string, bool) {
   781  	if r.row.HasColumn("price monitoring") {
   782  		pm := r.row.MustStr("price monitoring")
   783  		return pm, true
   784  	}
   785  	return "", false
   786  }
   787  
   788  func (r marketUpdateRow) riskModel() (string, bool) {
   789  	if r.row.HasColumn("risk model") {
   790  		rm := r.row.MustStr("risk model")
   791  		return rm, true
   792  	}
   793  	return "", false
   794  }
   795  
   796  func (r marketUpdateRow) liquidityMonitoring() (string, bool) {
   797  	if r.row.HasColumn("liquidity monitoring") {
   798  		lm := r.row.MustStr("liquidity monitoring")
   799  		return lm, true
   800  	}
   801  	return "", false
   802  }
   803  
   804  func (r marketUpdateRow) liquidationStrat() (string, bool) {
   805  	if r.row.HasColumn("liquidation strategy") {
   806  		ls := r.row.MustStr("liquidation strategy")
   807  		return ls, true
   808  	}
   809  	return "", false
   810  }
   811  
   812  func (r marketUpdateRow) priceSourceWeights() []num.Decimal {
   813  	if !r.row.HasColumn("source weights") {
   814  		return []num.Decimal{num.DecimalZero(), num.DecimalZero(), num.DecimalZero(), num.DecimalZero()}
   815  	}
   816  	weights := strings.Split(r.row.mustColumn("source weights"), ",")
   817  	d := make([]num.Decimal, 0, len(weights))
   818  	for _, v := range weights {
   819  		d = append(d, num.MustDecimalFromString(v))
   820  	}
   821  	return d
   822  }
   823  
   824  func (r marketUpdateRow) compositePriceOracleFromName(config *market.Config, name string) (*datasource.Spec, *datasource.SpecBindingForCompositePrice) {
   825  	if !r.row.HasColumn(name) {
   826  		return nil, nil
   827  	}
   828  
   829  	rawSpec, binding, err := config.OracleConfigs.GetOracleDefinitionForCompositePrice(r.row.Str(name))
   830  	if err != nil {
   831  		return nil, nil
   832  	}
   833  	spec := datasource.FromOracleSpecProto(rawSpec)
   834  	filters := spec.Data.GetFilters()
   835  	ds := datasource.NewDefinition(datasource.ContentTypeOracle).SetOracleConfig(
   836  		&signedoracle.SpecConfiguration{
   837  			Signers: spec.Data.GetSigners(),
   838  			Filters: filters,
   839  		},
   840  	)
   841  	return datasource.SpecFromDefinition(*ds), &datasource.SpecBindingForCompositePrice{PriceSourceProperty: binding.PriceSourceProperty}
   842  }
   843  
   844  func (r marketUpdateRow) oracles(config *market.Config) ([]*datasource.Spec, []*datasource.SpecBindingForCompositePrice) {
   845  	specs := []*datasource.Spec{}
   846  	bindings := []*datasource.SpecBindingForCompositePrice{}
   847  	names := []string{"oracle1", "oracle2", "oracle3", "oracle4", "oracle5"}
   848  	for _, v := range names {
   849  		spec, binding := r.compositePriceOracleFromName(config, v)
   850  		if spec == nil {
   851  			continue
   852  		}
   853  		specs = append(specs, spec)
   854  		bindings = append(bindings, binding)
   855  	}
   856  	if len(specs) > 0 {
   857  		return specs, bindings
   858  	}
   859  
   860  	return nil, nil
   861  }
   862  
   863  func (r marketUpdateRow) priceSourceStalnessTolerance() []time.Duration {
   864  	if !r.row.HasColumn("source staleness tolerance") {
   865  		return []time.Duration{1000, 1000, 1000, 1000}
   866  	}
   867  	durations := strings.Split(r.row.mustColumn("source staleness tolerance"), ",")
   868  	d := make([]time.Duration, 0, len(durations))
   869  	for _, v := range durations {
   870  		dur, err := time.ParseDuration(v)
   871  		if err != nil {
   872  			panic(err)
   873  		}
   874  		d = append(d, dur)
   875  	}
   876  	return d
   877  }
   878  
   879  func (r marketUpdateRow) cashAmount() *num.Uint {
   880  	if !r.row.HasColumn("cash amount") {
   881  		return num.UintZero()
   882  	}
   883  	return num.MustUintFromString(r.row.mustColumn("cash amount"), 10)
   884  }
   885  
   886  func (r marketUpdateRow) decayPower() num.Decimal {
   887  	if !r.row.HasColumn("decay power") {
   888  		return num.DecimalZero()
   889  	}
   890  	return num.MustDecimalFromString(r.row.mustColumn("decay power"))
   891  }
   892  
   893  func (r marketUpdateRow) decayWeight() num.Decimal {
   894  	if !r.row.HasColumn("decay weight") {
   895  		return num.DecimalZero()
   896  	}
   897  	return num.MustDecimalFromString(r.row.mustColumn("decay weight"))
   898  }
   899  
   900  func (r marketUpdateRow) markPriceType() types.CompositePriceType {
   901  	if !r.row.HasColumn("price type") {
   902  		return types.CompositePriceTypeByLastTrade
   903  	}
   904  	if r.row.mustColumn("price type") == "last trade" {
   905  		return types.CompositePriceTypeByLastTrade
   906  	} else if r.row.mustColumn("price type") == "median" {
   907  		return types.CompositePriceTypeByMedian
   908  	} else if r.row.mustColumn("price type") == "weight" {
   909  		return types.CompositePriceTypeByWeight
   910  	} else {
   911  		panic("invalid price type")
   912  	}
   913  }
   914  
   915  func (r marketRow) tickSize() *num.Uint {
   916  	if r.row.HasColumn("tick size") {
   917  		return num.MustUintFromString(r.row.MustStr("tick size"), 10)
   918  	}
   919  	return num.UintOne()
   920  }
   921  
   922  func (r marketRow) allowedEmptyAMMLevels() uint64 {
   923  	if r.row.HasColumn("allowed empty AMM levels") {
   924  		return r.row.MustU64("allowed empty AMM levels")
   925  	}
   926  	return 100
   927  }
   928  
   929  func (r marketRow) id() string {
   930  	return r.row.MustStr("id")
   931  }
   932  
   933  func (r marketRow) liquidationStrat() string {
   934  	if r.row.HasColumn("liquidation strategy") {
   935  		ls := r.row.MustStr("liquidation strategy")
   936  		return ls
   937  	}
   938  	return ""
   939  }
   940  
   941  func (r marketRow) decimalPlaces() uint64 {
   942  	if !r.row.HasColumn("decimal places") {
   943  		return 0
   944  	}
   945  	return r.row.MustU64("decimal places")
   946  }
   947  
   948  func (r marketRow) positionDecimalPlaces() int64 {
   949  	if !r.row.HasColumn("position decimal places") {
   950  		return 0
   951  	}
   952  	return r.row.MustI64("position decimal places")
   953  }
   954  
   955  func (r marketRow) quoteName() string {
   956  	return r.row.MustStr("quote name")
   957  }
   958  
   959  func (r marketRow) asset() string {
   960  	return r.row.MustStr("asset")
   961  }
   962  
   963  func (r marketRow) riskModel() string {
   964  	return r.row.MustStr("risk model")
   965  }
   966  
   967  func (r marketRow) fees() string {
   968  	return r.row.MustStr("fees")
   969  }
   970  
   971  func (r marketRow) oracleConfig() string {
   972  	return r.row.MustStr("data source config")
   973  }
   974  
   975  func (r marketRow) priceMonitoring() string {
   976  	return r.row.MustStr("price monitoring")
   977  }
   978  
   979  func (r marketRow) marginCalculator() string {
   980  	return r.row.MustStr("margin calculator")
   981  }
   982  
   983  func (r marketRow) auctionDuration() int64 {
   984  	return r.row.MustI64("auction duration")
   985  }
   986  
   987  func (r marketRow) liquidityMonitoring() string {
   988  	if !r.row.HasColumn("liquidity monitoring") {
   989  		return "default-parameters"
   990  	}
   991  	return r.row.MustStr("liquidity monitoring")
   992  }
   993  
   994  func (r marketRow) liquiditySLA() string {
   995  	return r.row.MustStr("sla params")
   996  }
   997  
   998  func (r marketUpdateRow) tryLiquiditySLA() (string, bool) {
   999  	if r.row.HasColumn("sla params") {
  1000  		sla := r.row.MustStr("sla params")
  1001  		return sla, true
  1002  	}
  1003  	return "", false
  1004  }
  1005  
  1006  func (r marketUpdateRow) tryLiquidityFeeSettings() (string, bool) {
  1007  	if r.row.HasColumn("liquidity fee settings") {
  1008  		s := r.row.MustStr("liquidity fee settings")
  1009  		return s, true
  1010  	}
  1011  	return "", false
  1012  }
  1013  
  1014  func (r marketRow) linearSlippageFactor() float64 {
  1015  	if !r.row.HasColumn("linear slippage factor") {
  1016  		// set to 0.1 by default
  1017  		return 0.001
  1018  	}
  1019  	return r.row.MustF64("linear slippage factor")
  1020  }
  1021  
  1022  func (r marketRow) priceSourceWeights() []num.Decimal {
  1023  	if !r.row.HasColumn("source weights") {
  1024  		return []num.Decimal{num.DecimalZero(), num.DecimalZero(), num.DecimalZero(), num.DecimalZero()}
  1025  	}
  1026  	weights := strings.Split(r.row.mustColumn("source weights"), ",")
  1027  	d := make([]num.Decimal, 0, len(weights))
  1028  	for _, v := range weights {
  1029  		d = append(d, num.MustDecimalFromString(v))
  1030  	}
  1031  	return d
  1032  }
  1033  
  1034  func (r marketRow) priceSourceStalnessTolerance() []time.Duration {
  1035  	if !r.row.HasColumn("source staleness tolerance") {
  1036  		return []time.Duration{1000, 1000, 1000, 1000}
  1037  	}
  1038  	durations := strings.Split(r.row.mustColumn("source staleness tolerance"), ",")
  1039  	d := make([]time.Duration, 0, len(durations))
  1040  	for _, v := range durations {
  1041  		dur, err := time.ParseDuration(v)
  1042  		if err != nil {
  1043  			panic(err)
  1044  		}
  1045  		d = append(d, dur)
  1046  	}
  1047  	return d
  1048  }
  1049  
  1050  func (r marketRow) cashAmount() *num.Uint {
  1051  	if !r.row.HasColumn("cash amount") {
  1052  		return num.UintZero()
  1053  	}
  1054  	return num.MustUintFromString(r.row.mustColumn("cash amount"), 10)
  1055  }
  1056  
  1057  func (r marketRow) decayPower() num.Decimal {
  1058  	if !r.row.HasColumn("decay power") {
  1059  		return num.DecimalZero()
  1060  	}
  1061  	return num.MustDecimalFromString(r.row.mustColumn("decay power"))
  1062  }
  1063  
  1064  func (r marketRow) decayWeight() num.Decimal {
  1065  	if !r.row.HasColumn("decay weight") {
  1066  		return num.DecimalZero()
  1067  	}
  1068  	return num.MustDecimalFromString(r.row.mustColumn("decay weight"))
  1069  }
  1070  
  1071  func (r marketRow) markPriceType() types.CompositePriceType {
  1072  	if !r.row.HasColumn("price type") {
  1073  		return types.CompositePriceTypeByLastTrade
  1074  	}
  1075  	if r.row.mustColumn("price type") == "last trade" {
  1076  		return types.CompositePriceTypeByLastTrade
  1077  	} else if r.row.mustColumn("price type") == "median" {
  1078  		return types.CompositePriceTypeByMedian
  1079  	} else if r.row.mustColumn("price type") == "weight" {
  1080  		return types.CompositePriceTypeByWeight
  1081  	} else {
  1082  		panic("invalid price type")
  1083  	}
  1084  }
  1085  
  1086  func (r marketRow) compositePriceOracleFromName(config *market.Config, name string) (*datasource.Spec, *datasource.SpecBindingForCompositePrice) {
  1087  	if !r.row.HasColumn(name) {
  1088  		return nil, nil
  1089  	}
  1090  	rawSpec, binding, err := config.OracleConfigs.GetOracleDefinitionForCompositePrice(r.row.Str(name))
  1091  	if err != nil {
  1092  		return nil, nil
  1093  	}
  1094  	spec := datasource.FromOracleSpecProto(rawSpec)
  1095  	filters := spec.Data.GetFilters()
  1096  	ds := datasource.NewDefinition(datasource.ContentTypeOracle).SetOracleConfig(
  1097  		&signedoracle.SpecConfiguration{
  1098  			Signers: spec.Data.GetSigners(),
  1099  			Filters: filters,
  1100  		},
  1101  	)
  1102  	return datasource.SpecFromDefinition(*ds), &datasource.SpecBindingForCompositePrice{PriceSourceProperty: binding.PriceSourceProperty}
  1103  }
  1104  
  1105  func (r marketRow) oracles(config *market.Config) ([]*datasource.Spec, []*datasource.SpecBindingForCompositePrice) {
  1106  	specs := []*datasource.Spec{}
  1107  	bindings := []*datasource.SpecBindingForCompositePrice{}
  1108  	names := []string{"oracle1", "oracle2", "oracle3", "oracle4", "oracle5"}
  1109  	for _, v := range names {
  1110  		spec, binding := r.compositePriceOracleFromName(config, v)
  1111  		if spec == nil {
  1112  			continue
  1113  		}
  1114  		specs = append(specs, spec)
  1115  		bindings = append(bindings, binding)
  1116  	}
  1117  	if len(specs) > 0 {
  1118  		return specs, bindings
  1119  	}
  1120  
  1121  	return nil, nil
  1122  }
  1123  
  1124  func (r marketRow) quadraticSlippageFactor() float64 {
  1125  	if !r.row.HasColumn("quadratic slippage factor") {
  1126  		// set to 0.1 by default
  1127  		return 0.0
  1128  	}
  1129  	return r.row.MustF64("quadratic slippage factor")
  1130  }
  1131  
  1132  func (r marketRow) isSuccessor() bool {
  1133  	if pid, ok := r.row.StrB("parent market id"); !ok || len(pid) == 0 {
  1134  		return false
  1135  	}
  1136  	return true
  1137  }
  1138  
  1139  func (r marketRow) IsCapped() bool {
  1140  	return r.row.HasColumn("max price cap")
  1141  }
  1142  
  1143  func (r marketRow) getCapped() *types.FutureCap {
  1144  	if !r.IsCapped() {
  1145  		return nil
  1146  	}
  1147  	return &types.FutureCap{
  1148  		MaxPrice:            r.row.MustUint("max price cap"),
  1149  		Binary:              r.row.Bool("binary"),
  1150  		FullyCollateralised: r.row.Bool("fully collateralised"),
  1151  	}
  1152  }
  1153  
  1154  func (r marketRow) isPerp() bool {
  1155  	if mt, ok := r.row.StrB("market type"); !ok || mt != "perp" {
  1156  		return false
  1157  	}
  1158  	return true
  1159  }
  1160  
  1161  func (r marketRow) parentID() string {
  1162  	return r.row.MustStr("parent market id")
  1163  }
  1164  
  1165  func (r marketRow) insuranceFraction() num.Decimal {
  1166  	if !r.row.HasColumn("insurance pool fraction") {
  1167  		return num.DecimalZero()
  1168  	}
  1169  	return r.row.Decimal("insurance pool fraction")
  1170  }
  1171  
  1172  func (r marketRow) successorAuction() int64 {
  1173  	if !r.row.HasColumn("successor auction") {
  1174  		return 5 * r.auctionDuration() // five times auction duration
  1175  	}
  1176  	return r.row.MustI64("successor auction")
  1177  }
  1178  
  1179  func (r marketUpdateRow) tryLinearSlippageFactor() (float64, bool) {
  1180  	if r.row.HasColumn("linear slippage factor") {
  1181  		return r.row.MustF64("linear slippage factor"), true
  1182  	}
  1183  	return -1, false
  1184  }
  1185  
  1186  func (r marketUpdateRow) tryQuadraticSlippageFactor() (float64, bool) {
  1187  	if r.row.HasColumn("quadratic slippage factor") {
  1188  		return r.row.MustF64("quadratic slippage factor"), true
  1189  	}
  1190  	return -1, false
  1191  }