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 }