Skip to content

Commit

Permalink
mm: Add BotProblems to BotStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
martonp committed Jun 13, 2024
1 parent 18b2522 commit 16239b3
Show file tree
Hide file tree
Showing 16 changed files with 1,010 additions and 149 deletions.
10 changes: 10 additions & 0 deletions client/core/bookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,16 @@ func (dc *dexConnection) bookie(marketID string) *bookie {
return dc.books[marketID]
}

func (dc *dexConnection) midGap(base, quote uint32) (midGap uint64, err error) {
marketID := marketName(base, quote)
booky := dc.bookie(marketID)
if booky == nil {
return 0, fmt.Errorf("no bookie found for market %s", marketID)
}

return booky.MidGap()
}

// syncBook subscribes to the order book and returns the book and a BookFeed to
// receive order book updates. The BookFeed must be Close()d when it is no
// longer in use. Use stopBook to unsubscribed and clean up the feed.
Expand Down
146 changes: 138 additions & 8 deletions client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -6308,8 +6308,7 @@ func (c *Core) TradeAsync(pw []byte, form *TradeForm) (*InFlightOrder, error) {
_, err := c.sendTradeRequest(req)
if err != nil {
// If it's an OrderQuantityTooHigh error, send simplified notification
var mErr *msgjson.Error
if errors.As(err, &mErr) && mErr.Code == msgjson.OrderQuantityTooHigh {
if errors.Is(err, ErrOrderQtyTooHigh) {
topic := TopicOrderQuantityTooHigh
subject, details := c.formatDetails(topic, corder.Host)
c.notify(newOrderNoteWithTempID(topic, subject, details, db.ErrorLevel, corder, tempID))
Expand Down Expand Up @@ -6368,7 +6367,7 @@ func (c *Core) prepareForTradeRequestPrep(pw []byte, base, quote uint32, host st
return fail(err)
}
if dc.acct.suspended() {
return fail(newError(suspendedAcctErr, "may not trade while account is suspended"))
return fail(newError(suspendedAcctErr, "%w", ErrAccountSuspended))
}

mktID := marketName(base, quote)
Expand Down Expand Up @@ -6405,12 +6404,10 @@ func (c *Core) prepareForTradeRequestPrep(pw []byte, base, quote uint32, host st
w.mtx.RLock()
defer w.mtx.RUnlock()
if w.peerCount < 1 {
return fmt.Errorf("%s wallet has no network peers (check your network or firewall)",
unbip(w.AssetID))
return &WalletNoPeersError{w.AssetID}
}
if !w.synced {
return fmt.Errorf("%s still syncing. progress = %.2f%%", unbip(w.AssetID),
w.syncProgress*100)
return &WalletSyncError{w.AssetID, w.syncProgress}
}
return nil
}
Expand Down Expand Up @@ -10263,7 +10260,7 @@ func sendRequest(conn comms.WsConn, route string, request, response any, timeout
}

// Check the response error.
return <-errChan
return mapServerError(<-errChan)
}

// newPreimage creates a random order commitment. If you require a matching
Expand Down Expand Up @@ -11473,3 +11470,136 @@ func (c *Core) TakeAction(assetID uint32, actionID string, actionB json.RawMessa
}
return goGetter.TakeAction(actionID, actionB)
}

// calcParcelLimit computes the users score-scaled user parcel limit.
func calcParcelLimit(tier int64, score, maxScore int32) uint32 {
// Users limit starts at 2 parcels per tier.
lowerLimit := tier * dex.PerTierBaseParcelLimit
// Limit can scale up to 3x with score.
upperLimit := lowerLimit * dex.ParcelLimitScoreMultiplier
limitRange := upperLimit - lowerLimit
var scaleFactor float64
if score > 0 {
scaleFactor = float64(score) / float64(maxScore)
}
return uint32(lowerLimit) + uint32(math.Round(scaleFactor*float64(limitRange)))
}

func (c *Core) TradingLimits(host string) (userParcels, parcelLimit uint32, err error) {
exchange, err := c.Exchange(host)
if err != nil {
return 0, 0, err
}

dc, _, err := c.dex(host)
if err != nil {
return 0, 0, err
}

likelyTaker := func(o *Order, midGap uint64) bool {
if o.Type == order.MarketOrderType || o.TimeInForce == order.ImmediateTiF {
return true
}

if midGap == 0 {
return false
}

if o.Sell {
return o.Rate < midGap
}

return o.Rate > midGap
}

baseQty := func(o *Order, midGap, lotSize uint64) uint64 {
qty := o.Qty

if o.Type == order.MarketOrderType && !o.Sell {
if midGap == 0 {
qty = lotSize
} else {
qty = calc.QuoteToBase(midGap, qty)
}
}
return qty
}

epochWeight := func(o *Order, midGap, lotSize uint64) uint64 {
if o.Status >= order.OrderStatusBooked {
return 0
}

if likelyTaker(o, midGap) {
return 2 * baseQty(o, midGap, lotSize)
}

return baseQty(o, midGap, lotSize)
}

bookedWeight := func(o *Order) uint64 {
if o.Status != order.OrderStatusBooked {
return 0
}
return o.Qty - o.Filled
}

settlingWeight := func(o *Order) (weight uint64) {
for _, match := range o.Matches {
if (match.Side == order.Maker && match.Status >= order.MakerRedeemed) ||
(match.Side == order.Taker && match.Status >= order.MatchComplete) {
continue
}
weight += match.Qty
}
return
}

isEpochOrder := func(o *Order) bool {
return o.Status < order.OrderStatusExecuted
}

parcelLimit = calcParcelLimit(exchange.Auth.EffectiveTier, exchange.Auth.Rep.Score, int32(exchange.MaxScore))
for _, mkt := range exchange.Markets {
if len(mkt.InFlightOrders) == 0 && len(mkt.Orders) == 0 {
continue
}

var hasEpochOrder bool
for _, ord := range mkt.InFlightOrders {
if isEpochOrder(ord.Order) {
hasEpochOrder = true
break
}
}

if !hasEpochOrder {
for _, ord := range mkt.Orders {
if isEpochOrder(ord) {
hasEpochOrder = true
break
}
}
}

var midGap uint64
if hasEpochOrder {
midGap, err = dc.midGap(mkt.BaseID, mkt.QuoteID)
if err != nil {
return 0, 0, err
}
}

var mktWeight uint64
for _, ord := range mkt.InFlightOrders {
mktWeight += epochWeight(ord.Order, midGap, mkt.LotSize) + bookedWeight(ord.Order) + settlingWeight(ord.Order)
}
for _, ord := range mkt.Orders {
mktWeight += epochWeight(ord, midGap, mkt.LotSize) + bookedWeight(ord) + settlingWeight(ord)
}

userParcels += uint32(mktWeight / (uint64(mkt.ParcelSize) * mkt.LotSize))
}

return userParcels, parcelLimit, nil
}
49 changes: 49 additions & 0 deletions client/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package core
import (
"errors"
"fmt"

"decred.org/dcrdex/dex/msgjson"
)

// Error codes here are used on the frontend.
Expand Down Expand Up @@ -106,3 +108,50 @@ func UnwrapErr(err error) error {
}
return UnwrapErr(InnerErr)
}

var (
ErrOrderQtyTooHigh = errors.New("user order limit exceeded")
ErrAccountSuspended = errors.New("may not trade while account is suspended")
)

var serverErrsMap = map[int]error{
msgjson.OrderQuantityTooHigh: ErrOrderQtyTooHigh,
}

// mapServerError maps an error sent in a server response to a client core
// error.
func mapServerError(err error) error {
if err == nil {
return nil
}

var mErr *msgjson.Error
if !errors.As(err, &mErr) {
return err
}

if mappedErr, found := serverErrsMap[mErr.Code]; found {
return mappedErr
}

return err
}

// WalletNoPeersError should be returned when a wallet has no network peers.
type WalletNoPeersError struct {
AssetID uint32
}

func (e *WalletNoPeersError) Error() string {
return fmt.Sprintf("%s wallet has no network peers (check your network or firewall)", unbip(e.AssetID))
}

// WalletSyncError should be returned when a wallet is still syncing.
type WalletSyncError struct {
AssetID uint32
Progress float32
}

func (e *WalletSyncError) Error() string {
return fmt.Sprintf("%s still syncing. progress = %.2f%%", unbip(e.AssetID), e.Progress*100)
}
22 changes: 22 additions & 0 deletions client/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,28 @@ type Exchange struct {
PendingFee *PendingFeeState `json:"pendingFee,omitempty"`
}

func (e *Exchange) strongTier() uint64 {
weakStrength := e.Auth.WeakStrength
targetTier := e.Auth.TargetTier
effectiveTier := e.Auth.EffectiveTier

if effectiveTier > int64(targetTier) {
diff := effectiveTier - int64(targetTier)

if weakStrength >= diff {
return targetTier
} else {
return targetTier + uint64(diff-weakStrength)
}
}

if effectiveTier >= 0 {
return uint64(effectiveTier)
}

return 0
}

// newDisplayIDFromSymbols creates a display-friendly market ID for a base/quote
// symbol pair.
func newDisplayIDFromSymbols(base, quote string) string {
Expand Down
Loading

0 comments on commit 16239b3

Please sign in to comment.