decred.org/dcrdex@v1.0.5/dex/market.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package dex
     5  
     6  import (
     7  	"fmt"
     8  	"math"
     9  	"strings"
    10  )
    11  
    12  const (
    13  	// defaultEpochDuration is the spec defined default markets epoch duration
    14  	// in milliseconds.
    15  	defaultEpochDuration uint64 = 20000
    16  
    17  	// Parcels are used to track user trading limits. A parcel is a number of
    18  	// lots. How many lots are in a parcel is defined as part of a market's
    19  	// configuration. Markets with low-fee assets might have very small lot
    20  	// sizes, so would set a large parcel size. A market with high-fee assets
    21  	// would have a larger lot size, so would set a small parcel size.
    22  
    23  	// Parcels are tracked accross all known markets, and trading limits are
    24  	// assessed globally.
    25  
    26  	// When calculating trading limits, each market will report its parcels
    27  	// for the user.
    28  
    29  	// Parcel limits are measured in units of "parcel weight", which is kind of
    30  	// like "effective quantity". For makers, parcel weight = quantity. Orders
    31  	// which are likely to be takers have their parcel weight doubled. The
    32  	// quantity in settling matches has the same weight as maker quantity.
    33  
    34  	// PerTierBaseParcelLimit is the number of parcels users can trade per tier.
    35  	PerTierBaseParcelLimit = 2
    36  	// ParcelLimitScoreMultiplier defines the maximum parcel limit multiplier
    37  	// that a user achieves with max score. The users parcel limit ranges from
    38  	// the starting value of tier*PerTierBaseParcelLimit to the maximum value of
    39  	// tier*PerTierBaseParcelLimit*ParcelLimitScoreMultiplier. By default,
    40  	// singly-tiered users have max lot limits limited to the range (8,24).
    41  	ParcelLimitScoreMultiplier = 3
    42  )
    43  
    44  // MarketInfo specifies a market that the Archiver must support.
    45  type MarketInfo struct {
    46  	Name                   string
    47  	Base                   uint32
    48  	Quote                  uint32
    49  	LotSize                uint64
    50  	ParcelSize             uint32 // number of lots in the base trading limit.
    51  	RateStep               uint64
    52  	EpochDuration          uint64 // msec
    53  	MarketBuyBuffer        float64
    54  	MaxUserCancelsPerEpoch uint32
    55  }
    56  
    57  func marketName(base, quote string) string {
    58  	return base + "_" + quote
    59  }
    60  
    61  // MarketName creates the string representation of a DEX market (e.g. "dcr_btc")
    62  // given the base and quote asset indexes defined in BIP-0044. See also
    63  // BipIDSymbol.
    64  func MarketName(baseID, quoteID uint32) (string, error) {
    65  	baseSymbol := BipIDSymbol(baseID)
    66  	if baseSymbol == "" {
    67  		return "", fmt.Errorf("base asset %d not found", baseID)
    68  	}
    69  	baseSymbol = strings.ToLower(baseSymbol)
    70  
    71  	quoteSymbol := BipIDSymbol(quoteID)
    72  	if quoteSymbol == "" {
    73  		return "", fmt.Errorf("quote asset %d not found", quoteID)
    74  	}
    75  	quoteSymbol = strings.ToLower(quoteSymbol)
    76  
    77  	return marketName(baseSymbol, quoteSymbol), nil
    78  }
    79  
    80  // NewMarketInfo creates a new market configuration (MarketInfo) from the given
    81  // base and quote asset indexes, order lot size, and epoch duration in
    82  // milliseconds. See also MarketName.
    83  // TODO: Only used in tests. Should just use NewMarketInfoFromSymbols instead.
    84  func NewMarketInfo(base, quote uint32, lotSize, rateStep, epochDuration uint64, marketBuyBuffer float64) (*MarketInfo, error) {
    85  	name, err := MarketName(base, quote)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	// Check for sensible epoch duration.
    91  	if epochDuration == 0 {
    92  		epochDuration = defaultEpochDuration
    93  	}
    94  
    95  	return &MarketInfo{
    96  		Name:                   name,
    97  		Base:                   base,
    98  		Quote:                  quote,
    99  		LotSize:                lotSize,
   100  		ParcelSize:             1,
   101  		RateStep:               rateStep,
   102  		EpochDuration:          epochDuration,
   103  		MarketBuyBuffer:        marketBuyBuffer,
   104  		MaxUserCancelsPerEpoch: math.MaxUint32,
   105  	}, nil
   106  }
   107  
   108  // NewMarketInfoFromSymbols is like NewMarketInfo, but the base and quote assets
   109  // are identified by their symbols as defined in the
   110  // decred.org/dcrdex/server/asset package.
   111  func NewMarketInfoFromSymbols(base, quote string, lotSize, rateStep, epochDuration uint64, parcelSize uint32, marketBuyBuffer float64) (*MarketInfo, error) {
   112  	base = strings.ToLower(base)
   113  	baseID, found := BipSymbolID(base)
   114  	if !found {
   115  		return nil, fmt.Errorf(`base asset symbol "%s" unrecognized`, base)
   116  	}
   117  
   118  	quote = strings.ToLower(quote)
   119  	quoteID, found := BipSymbolID(quote)
   120  	if !found {
   121  		return nil, fmt.Errorf(`quote asset symbol "%s" unrecognized`, quote)
   122  	}
   123  
   124  	// Check for sensible epoch duration.
   125  	if epochDuration == 0 {
   126  		epochDuration = defaultEpochDuration
   127  	}
   128  
   129  	return &MarketInfo{
   130  		Name:                   marketName(base, quote),
   131  		Base:                   baseID,
   132  		Quote:                  quoteID,
   133  		LotSize:                lotSize,
   134  		ParcelSize:             parcelSize,
   135  		RateStep:               rateStep,
   136  		EpochDuration:          epochDuration,
   137  		MarketBuyBuffer:        marketBuyBuffer,
   138  		MaxUserCancelsPerEpoch: math.MaxUint32,
   139  	}, nil
   140  }
   141  
   142  // String returns the market's Name.
   143  func (mi *MarketInfo) String() string {
   144  	return mi.Name
   145  }