From 7c53b02e871f50746df9970a6e1f4915a663642e Mon Sep 17 00:00:00 2001 From: Brian Stafford Date: Wed, 16 Oct 2024 19:47:57 -0500 Subject: [PATCH] add order summary and logging --- client/mm/exchange_adaptor.go | 342 ++++++++++++++++++-------- client/mm/exchange_adaptor_test.go | 326 ++++++++++++------------ client/mm/mm_arb_market_maker.go | 38 +-- client/mm/mm_arb_market_maker_test.go | 10 +- client/mm/mm_basic.go | 12 +- client/mm/mm_basic_test.go | 104 ++++---- client/mm/mm_simple_arb_test.go | 8 +- client/mm/mm_test.go | 10 +- client/mm/notification.go | 19 ++ server/market/market_test.go | 2 +- 10 files changed, 520 insertions(+), 351 deletions(-) diff --git a/client/mm/exchange_adaptor.go b/client/mm/exchange_adaptor.go index c73776cdaf..0ce7435c85 100644 --- a/client/mm/exchange_adaptor.go +++ b/client/mm/exchange_adaptor.go @@ -35,30 +35,36 @@ type BotBalance struct { Reserved uint64 `json:"reserved"` } -// multiTradePlacement represents a placement to be made on a DEX order book +// TradePlacement represents a placement to be made on a DEX order book // using the MultiTrade function. A non-zero counterTradeRate indicates that // the bot intends to make a counter-trade on a CEX when matches are made on // the DEX, and this must be taken into consideration in combination with the // bot's balance on the CEX when deciding how many lots to place. This // information is also used when considering deposits and withdrawals. -type multiTradePlacement struct { - lots uint64 - rate uint64 - counterTradeRate uint64 -} - -// orderFees represents the fees that will be required for a single lot of a +type TradePlacement struct { + Lots uint64 `json:"lots"` + Rate uint64 `json:"rate"` + CounterTradeRate uint64 `json:"counterTradeRate"` + RequiredDEX map[uint32]uint64 `json:"requiredDex"` + RequiredCEX uint64 `json:"requiredCex"` + OrderedLots uint64 `json:"orderedLots"` + UsedDEX map[uint32]uint64 `json:"usedDex"` + UsedCEX uint64 `json:"usedCex"` + Order *core.Order `json:"order"` +} + +// OrderFees represents the fees that will be required for a single lot of a // dex order. -type orderFees struct { +type OrderFees struct { *LotFeeRange - funding uint64 - // bookingFeesPerLot is the amount of fee asset that needs to be reserved + Funding uint64 `json:"funding"` + // BookingFeesPerLot is the amount of fee asset that needs to be reserved // for fees, per ordered lot. For all assets, this will include // LotFeeRange.Max.Swap. For non-token EVM assets (eth, matic) Max.Refund // will be added. If the asset is the parent chain of a token counter-asset, // Max.Redeem is added. This is a commonly needed sum in various validation // and optimization functions. - bookingFeesPerLot uint64 + BookingFeesPerLot uint64 `json:"bookingFeesPerLot"` } // botCoreAdaptor is an interface used by bots to access DEX related @@ -433,8 +439,8 @@ type unifiedExchangeAdaptor struct { subscriptionID *int feesMtx sync.RWMutex - buyFees *orderFees - sellFees *orderFees + buyFees *OrderFees + sellFees *OrderFees startTime atomic.Int64 eventLogID atomic.Uint64 @@ -626,9 +632,9 @@ func (u *unifiedExchangeAdaptor) SufficientBalanceForDEXTrade(rate, qty uint64, if err != nil { return false, err } - fees, fundingFees := buyFees.Max, buyFees.funding + fees, fundingFees := buyFees.Max, buyFees.Funding if sell { - fees, fundingFees = sellFees.Max, sellFees.funding + fees, fundingFees = sellFees.Max, sellFees.Funding } if balances[fromFeeAsset] < fundingFees { @@ -1013,6 +1019,164 @@ func (u *unifiedExchangeAdaptor) placeMultiTrade(placements []*dexOrderInfo, sel return orders, nil } +type OrderReport struct { + FromID uint32 `json:"fromID"` + FromFeeID uint32 `json:"fromFeeID"` + ToID uint32 `json:"toID"` + ToFeeID uint32 `json:"toFeeID"` + Sell bool `json:"sell"` + Fees *OrderFees `json:"buyFees"` + AvailableDEXBals map[uint32]*BotBalance `json:"availableDexBals"` + RequiredDEXBals map[uint32]uint64 `json:"requiredDexBals"` + RemainingBalances map[uint32]uint64 `json:"remainingBalances"` + AvailableCEXBal *BotBalance `json:"availableCexBal"` + RequiredCEXBal uint64 `json:"requiredCexBal"` + RemainingCEXBal uint64 `json:"remainingCexBal"` + Placements []*TradePlacement `json:"placements"` + LotsStanding uint64 `json:"lotsStanding"` + // RequiredPlacements is a copy of placements where the lots field is + // adjusted to take into account pending orders that are already on + // the books. + RequiredPlacements []*TradePlacement `json:"requiredPlacements"` + Cancels []*core.Order `json:"cancels"` + OrderError string `json:"orderError"` +} + +func (u *unifiedExchangeAdaptor) newOrderReport(sell bool, placements []*TradePlacement) *OrderReport { + buyFees, sellFees, err := u.orderFees() + if err != nil { + u.log.Errorf("multiTrade: error getting order fees: %v", err) + return nil + } + fees := buyFees + if sell { + fees = sellFees + } + requiredPlacements := make([]*TradePlacement, len(placements)) + for i, p := range placements { + pCopy := *p + requiredPlacements[i] = &pCopy + } + remainingBalances := make(map[uint32]uint64, 2) + availableBalances := make(map[uint32]*BotBalance, 2) + // First, determine the amount of balances the bot has available to place + // DEX trades taking into account dexReserves. + fromID, fromFeeID, toID, toFeeID := orderAssets(u.baseID, u.quoteID, sell) + for _, assetID := range []uint32{fromID, fromFeeID, toID, toFeeID} { + if _, found := availableBalances[assetID]; !found { + bal := u.DEXBalance(assetID) + availableBalances[assetID] = bal + remainingBalances[assetID] = bal.Available + } + } + // If the placements include a counterTradeRate, the CEX balance must also + // be taken into account to determine how many trades can be placed. + var remainingCEXBal uint64 + var availableCEXBal *BotBalance + if placements[0].CounterTradeRate > 0 { + availableCEXBal = u.CEXBalance(toID) + remainingCEXBal = availableCEXBal.Available + } + return &OrderReport{ + FromID: fromID, + FromFeeID: fromFeeID, + ToID: toID, + ToFeeID: toFeeID, + Sell: sell, + Fees: fees, + AvailableDEXBals: utils.CopyMap(availableBalances), + RequiredDEXBals: make(map[uint32]uint64, 2), // Filled in by multiTrade + RemainingBalances: remainingBalances, + AvailableCEXBal: availableCEXBal, + RemainingCEXBal: remainingCEXBal, + RequiredPlacements: requiredPlacements, + Cancels: make([]*core.Order, 0, len(placements)), + } +} + +func (u *unifiedExchangeAdaptor) logOrderReport(or *OrderReport) { + if u.log.Level() != dex.LevelTrace { + return + } + + fiatRates := u.fiatRates.Load().(map[uint32]float64) + uis := make(map[uint32]*dex.UnitInfo, 2) + for _, assetID := range []uint32{or.FromID, or.ToID, or.FromFeeID, or.ToFeeID} { + if _, found := fiatRates[assetID]; !found { + u.log.Error("Cannot log order report because fiat rate info for %d is missing", assetID) + return + } + if _, found := uis[assetID]; found { + continue + } + ui, err := asset.UnitInfo(assetID) + if err != nil { + u.log.Error("Cannot log order report because unit info for %d is missing", assetID) + return + } + uis[assetID] = &ui + } + + fiatValue := func(assetID uint32, v uint64) float64 { + return float64(v) / float64(uis[assetID].Conventional.ConversionFactor) * fiatRates[assetID] + } + + var lotsNeeded, lotsOrdered uint64 + var usedUSD, overLockedUSD float64 + lockedUSD := fiatValue(or.ToID, or.AvailableCEXBal.Pending+or.AvailableCEXBal.Locked) + for _, p := range or.RequiredPlacements { + lotsNeeded += p.Lots + lotsOrdered += p.OrderedLots + if p.Order != nil { + var used float64 + for assetID, v := range p.UsedDEX { + used += fiatValue(assetID, v) + } + // usedUSD += fiatValue(or.ToID, p.UsedCEX) + locked := fiatValue(or.FromID, p.Order.LockedAmt) + locked += fiatValue(or.FromFeeID, p.Order.ParentAssetLockedAmt) + locked += fiatValue(or.ToFeeID, p.Order.RedeemLockedAmt) + locked += fiatValue(or.FromFeeID, p.Order.RefundLockedAmt) + usedUSD += used + overLockedUSD += locked - used + } + } + + dexBals := make([]string, 0, 2) + requiredUSD := fiatValue(or.ToID, or.RequiredCEXBal) + for assetID, req := range or.RequiredDEXBals { + requiredUSD += fiatValue(assetID, req) + ui := uis[assetID] + bal := or.AvailableDEXBals[assetID] + pct := float64(bal.Available) / float64(req) * 100 + dexBals = append(dexBals, fmt.Sprintf( + "%s: %s/%s(%.2f%%)/%s", ui.Conventional.Unit, ui.FormatAtoms(req), ui.FormatAtoms(bal.Available), pct, ui.FormatAtoms(bal.Pending+bal.Locked), + )) + } + availableUSD := fiatValue(or.ToID, or.AvailableCEXBal.Available) + for assetID, b := range or.AvailableDEXBals { + availableUSD += fiatValue(assetID, b.Available) + } + + var lotsCanceled uint64 + for _, o := range or.Cancels { + lotsCanceled += (o.Qty - o.Filled) / u.lotSize + } + + u.log.Tracef( + "Summary for %s %s placements: %d lots standing, %d lots needed, %d lots ordered, %d lots canceled, %.2f USD available, "+ + "%.2f USD required, %.2f USD locked, %.2f USD over-locked", + u.mwh, sellStr(or.Sell), or.LotsStanding, lotsNeeded, lotsOrdered, lotsCanceled, availableUSD, requiredUSD, lockedUSD, + ) + toUI := uis[or.ToID] + dexPct := float64(or.AvailableCEXBal.Available) / float64(or.RequiredCEXBal) * 100 + u.log.Tracef( + "Balances (r/a/l): dex: %s, cex: %s: %s/%s(%.2f%%)/%s", + dexBals, toUI.Conventional.Unit, toUI.FormatAtoms(or.AvailableCEXBal.Available), + toUI.FormatAtoms(or.RequiredCEXBal), dexPct, toUI.FormatAtoms(or.AvailableCEXBal.Locked+or.AvailableCEXBal.Reserved), + ) +} + // MultiTrade places multiple orders on the DEX order book. The placements // arguments does not represent the trades that should be placed at this time, // but rather the amount of lots that the caller expects consistently have on @@ -1041,48 +1205,27 @@ func (u *unifiedExchangeAdaptor) placeMultiTrade(placements []*dexOrderInfo, sel // enough balance to place all of the orders, the lower priority trades that // were made in previous calls will be cancelled. func (u *unifiedExchangeAdaptor) multiTrade( - placements []*multiTradePlacement, + placements []*TradePlacement, sell bool, driftTolerance float64, currEpoch uint64, -) map[order.OrderID]*dexOrderInfo { +) (_ map[order.OrderID]*dexOrderInfo, or *OrderReport) { + or = u.newOrderReport(sell, placements) + defer u.logOrderReport(or) + if len(placements) == 0 { - return nil - } - buyFees, sellFees, err := u.orderFees() - if err != nil { - u.log.Errorf("multiTrade: error getting order fees: %v", err) - return nil + return } - fromID, fromFeeID, toID, toFeeID := orderAssets(u.baseID, u.quoteID, sell) - fees, fundingFees := buyFees.Max, buyFees.funding - if sell { - fees, fundingFees = sellFees.Max, sellFees.funding - } + fees, remainingBalances := or.Fees, or.RemainingBalances + fromFeeID, requiredPlacements := or.FromFeeID, or.RequiredPlacements - // First, determine the amount of balances the bot has available to place - // DEX trades taking into account dexReserves. - remainingBalances := map[uint32]uint64{} - for _, assetID := range []uint32{fromID, fromFeeID, toID, toFeeID} { - if _, found := remainingBalances[assetID]; !found { - remainingBalances[assetID] = u.DEXBalance(assetID).Available - } - } - if remainingBalances[fromFeeID] < fundingFees { + if remainingBalances[fromFeeID] < fees.Funding { u.log.Debugf("multiTrade: insufficient balance for funding fees. required: %d, have: %d", - fundingFees, remainingBalances[fromFeeID]) - return nil - } - remainingBalances[fromFeeID] -= fundingFees - - // If the placements include a counterTradeRate, the CEX balance must also - // be taken into account to determine how many trades can be placed. - accountForCEXBal := placements[0].counterTradeRate > 0 - var remainingCEXBal uint64 - if accountForCEXBal { - remainingCEXBal = u.CEXBalance(toID).Available + fees.Funding, remainingBalances[fromFeeID]) + return } + remainingBalances[fromFeeID] -= fees.Funding cancels := make([]dex.Bytes, 0, len(placements)) addCancel := func(o *core.Order) { @@ -1090,27 +1233,21 @@ func (u *unifiedExchangeAdaptor) multiTrade( u.log.Debugf("multiTrade: skipping cancel not past free cancel threshold") return } + or.Cancels = append(or.Cancels, o) cancels = append(cancels, o.ID) } - pendingOrders := u.groupedBookedOrders(sell) - - // requiredPlacements is a copy of placements where the lots field is - // adjusted to take into account pending orders that are already on - // the books. - requiredPlacements := make([]*multiTradePlacement, 0, len(placements)) // keptOrders is a list of pending orders that are not being cancelled, in // decreasing order of placementIndex. If the bot doesn't have enough // balance to place an order with a higher priority (lower placementIndex) // then the lower priority orders in this list will be cancelled. keptOrders := make([]*pendingDEXOrder, 0, len(placements)) - for _, p := range placements { - pCopy := *p - requiredPlacements = append(requiredPlacements, &pCopy) - } - for _, groupedOrders := range pendingOrders { + + for _, groupedOrders := range u.groupedBookedOrders(sell) { for _, o := range groupedOrders { order := o.currentState().order + lotsRemaining := (order.Qty - order.Filled) / u.lotSize + or.LotsStanding += lotsRemaining if o.placementIndex >= uint64(len(requiredPlacements)) { // This will happen if there is a reconfig in which there are // now less placements than before. @@ -1118,19 +1255,19 @@ func (u *unifiedExchangeAdaptor) multiTrade( continue } - mustCancel := !withinTolerance(order.Rate, placements[o.placementIndex].rate, driftTolerance) - if requiredPlacements[o.placementIndex].lots >= (order.Qty-order.Filled)/u.lotSize { - requiredPlacements[o.placementIndex].lots -= (order.Qty - order.Filled) / u.lotSize + mustCancel := !withinTolerance(order.Rate, placements[o.placementIndex].Rate, driftTolerance) + if requiredPlacements[o.placementIndex].Lots >= lotsRemaining { + requiredPlacements[o.placementIndex].Lots -= lotsRemaining } else { // This will happen if there is a reconfig in which this // placement index now requires less lots than before. mustCancel = true - requiredPlacements[o.placementIndex].lots = 0 + requiredPlacements[o.placementIndex].Lots = 0 } if mustCancel { u.log.Tracef("%s cancel with order rate = %s, placement rate = %s, drift tolerance = %.4f%%", - u.mwh, u.fmtRate(order.Rate), u.fmtRate(placements[o.placementIndex].rate), driftTolerance*100, + u.mwh, u.fmtRate(order.Rate), u.fmtRate(placements[o.placementIndex].Rate), driftTolerance*100, ) addCancel(order) } else { @@ -1142,20 +1279,21 @@ func (u *unifiedExchangeAdaptor) multiTrade( rateCausesSelfMatch := u.rateCausesSelfMatchFunc(sell) fundingReq := func(rate, lots, counterTradeRate uint64) (dexReq map[uint32]uint64, cexReq uint64) { + fromID, fromFeeID, toID, toFeeID := orderAssets(u.baseID, u.quoteID, sell) qty := u.lotSize * lots if !sell { qty = calc.BaseToQuote(rate, qty) } dexReq = make(map[uint32]uint64) dexReq[fromID] += qty - dexReq[fromFeeID] += fees.Swap * lots + dexReq[fromFeeID] += fees.Max.Swap * lots if u.isAccountLocker(fromID) { - dexReq[fromFeeID] += fees.Refund * lots + dexReq[fromFeeID] += fees.Max.Refund * lots } if u.isAccountLocker(toID) { - dexReq[toFeeID] += fees.Redeem * lots + dexReq[toFeeID] += fees.Max.Redeem * lots } - if accountForCEXBal { + if counterTradeRate > 0 { if sell { cexReq = calc.BaseToQuote(counterTradeRate, u.lotSize*lots) } else { @@ -1172,50 +1310,57 @@ func (u *unifiedExchangeAdaptor) multiTrade( return false } } - return remainingCEXBal >= cexReq + return or.RemainingCEXBal >= cexReq } orderInfos := make([]*dexOrderInfo, 0, len(requiredPlacements)) for i, placement := range requiredPlacements { - if placement.lots == 0 { + if placement.Lots == 0 { continue } - if rateCausesSelfMatch(placement.rate) { - u.log.Warnf("multiTrade: rate %d causes self match. Placements should be farther from mid-gap.", placement.rate) + placement.RequiredDEX, placement.RequiredCEX = fundingReq(placement.Rate, placement.Lots, placement.CounterTradeRate) + or.RequiredCEXBal += placement.RequiredCEX + for assetID, req := range placement.RequiredDEX { + or.RequiredDEXBals[assetID] += req + } + + if rateCausesSelfMatch(placement.Rate) { + u.log.Warnf("multiTrade: rate %d causes self match. Placements should be farther from mid-gap.", placement.Rate) continue } - searchN := int(placement.lots) + 1 + searchN := int(placement.Lots) + 1 lotsPlus1 := sort.Search(searchN, func(lotsi int) bool { - return !canAffordLots(placement.rate, uint64(lotsi), placement.counterTradeRate) + return !canAffordLots(placement.Rate, uint64(lotsi), placement.CounterTradeRate) }) var lotsToPlace uint64 if lotsPlus1 > 1 { lotsToPlace = uint64(lotsPlus1) - 1 - dexReq, cexReq := fundingReq(placement.rate, lotsToPlace, placement.counterTradeRate) - for assetID, v := range dexReq { + placement.UsedDEX, placement.UsedCEX = fundingReq(placement.Rate, lotsToPlace, placement.CounterTradeRate) + for assetID, v := range placement.UsedDEX { remainingBalances[assetID] -= v } - remainingCEXBal -= cexReq + or.RemainingCEXBal -= placement.UsedCEX orderInfos = append(orderInfos, &dexOrderInfo{ placementIndex: uint64(i), - counterTradeRate: placement.counterTradeRate, + counterTradeRate: placement.CounterTradeRate, placement: &core.QtyRate{ Qty: lotsToPlace * u.lotSize, - Rate: placement.rate, + Rate: placement.Rate, }, }) + placement.OrderedLots = lotsToPlace } // If there is insufficient balance to place a higher priority order, // cancel the lower priority orders. - if lotsToPlace < placement.lots { + if lotsToPlace < placement.Lots { u.log.Tracef("multiTrade(%s,%d) out of funds for more placements. %d of %d lots for rate %s", - sellStr(sell), i, lotsToPlace, placement.lots, u.fmtRate(placement.rate)) + sellStr(sell), i, lotsToPlace, placement.Lots, u.fmtRate(placement.Rate)) for _, o := range keptOrders { if o.placementIndex > uint64(i) { order := o.currentState().order @@ -1236,20 +1381,23 @@ func (u *unifiedExchangeAdaptor) multiTrade( if len(orderInfos) > 0 { orders, err := u.placeMultiTrade(orderInfos, sell) if err != nil { + or.OrderError = err.Error() u.log.Errorf("multiTrade: error placing orders: %v", err) - return nil + return } ordered := make(map[order.OrderID]*dexOrderInfo, len(placements)) for i, o := range orders { var orderID order.OrderID copy(orderID[:], o.ID) - ordered[orderID] = orderInfos[i] + oi := orderInfos[i] + ordered[orderID] = oi + placements[oi.placementIndex].Order = o } - return ordered + return ordered, or } - return nil + return nil, or } // DEXTrade places a single order on the DEX order book. @@ -2172,7 +2320,7 @@ func (u *unifiedExchangeAdaptor) atomicConversionRateFromFiat(fromID, toID uint3 // OrderFees returns the fees for a buy and sell order. The order fees are for // placing orders on the market specified by the exchangeAdaptorCfg used to // create the unifiedExchangeAdaptor. -func (u *unifiedExchangeAdaptor) orderFees() (buyFees, sellFees *orderFees, err error) { +func (u *unifiedExchangeAdaptor) orderFees() (buyFees, sellFees *OrderFees, err error) { u.feesMtx.RLock() defer u.feesMtx.RUnlock() @@ -2687,21 +2835,21 @@ func (u *unifiedExchangeAdaptor) lotCosts(sellVWAP, buyVWAP uint64) (*lotCosts, } perLot.dexBase = u.lotSize if u.baseID == u.baseFeeID { - perLot.dexBase += sellFees.bookingFeesPerLot + perLot.dexBase += sellFees.BookingFeesPerLot } perLot.cexBase = u.lotSize perLot.baseRedeem = buyFees.Max.Redeem - perLot.baseFunding = sellFees.funding + perLot.baseFunding = sellFees.Funding dexQuoteLot := calc.BaseToQuote(sellVWAP, u.lotSize) cexQuoteLot := calc.BaseToQuote(buyVWAP, u.lotSize) perLot.dexQuote = dexQuoteLot if u.quoteID == u.quoteFeeID { - perLot.dexQuote += buyFees.bookingFeesPerLot + perLot.dexQuote += buyFees.BookingFeesPerLot } perLot.cexQuote = cexQuoteLot perLot.quoteRedeem = sellFees.Max.Redeem - perLot.quoteFunding = buyFees.funding + perLot.quoteFunding = buyFees.Funding return perLot, nil } @@ -3147,7 +3295,7 @@ func (u *unifiedExchangeAdaptor) updateFeeRates() error { u.feesMtx.Lock() defer u.feesMtx.Unlock() - u.buyFees = &orderFees{ + u.buyFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: maxBuyFees, Estimated: &LotFees{ @@ -3156,10 +3304,10 @@ func (u *unifiedExchangeAdaptor) updateFeeRates() error { Refund: estQuoteFees.Refund, }, }, - funding: buyFundingFees, - bookingFeesPerLot: buyBookingFeesPerLot, + Funding: buyFundingFees, + BookingFeesPerLot: buyBookingFeesPerLot, } - u.sellFees = &orderFees{ + u.sellFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: maxSellFees, Estimated: &LotFees{ @@ -3168,8 +3316,8 @@ func (u *unifiedExchangeAdaptor) updateFeeRates() error { Refund: estBaseFees.Refund, }, }, - funding: sellFundingFees, - bookingFeesPerLot: sellBookingFeesPerLot, + Funding: sellFundingFees, + BookingFeesPerLot: sellBookingFeesPerLot, } return nil diff --git a/client/mm/exchange_adaptor_test.go b/client/mm/exchange_adaptor_test.go index 23ca49952e..2749b93286 100644 --- a/client/mm/exchange_adaptor_test.go +++ b/client/mm/exchange_adaptor_test.go @@ -85,18 +85,18 @@ func (db *tEventLogDB) runEvents(startTime int64, mkt *MarketWithHost, n uint64, return nil, nil } -func tFees(swap, redeem, refund, funding uint64) *orderFees { +func tFees(swap, redeem, refund, funding uint64) *OrderFees { lotFees := &LotFees{ Swap: swap, Redeem: redeem, Refund: refund, } - return &orderFees{ + return &OrderFees{ LotFeeRange: &LotFeeRange{ Max: lotFees, Estimated: lotFees, }, - funding: funding, + Funding: funding, } } @@ -541,7 +541,7 @@ func testDistribution(t *testing.T, baseID, quoteID uint32) { buyBookingFees, sellBookingFees := u.bookingFees(maxBuyFees, maxSellFees) - a.buyFees = &orderFees{ + a.buyFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: maxBuyFees, Estimated: &LotFees{ @@ -550,10 +550,10 @@ func testDistribution(t *testing.T, baseID, quoteID uint32) { Refund: buyRefundFees, }, }, - funding: buyFundingFees, - bookingFeesPerLot: buyBookingFees, + Funding: buyFundingFees, + BookingFeesPerLot: buyBookingFees, } - a.sellFees = &orderFees{ + a.sellFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: maxSellFees, Estimated: &LotFees{ @@ -561,8 +561,8 @@ func testDistribution(t *testing.T, baseID, quoteID uint32) { Redeem: sellRedeemFees, }, }, - funding: sellFundingFees, - bookingFeesPerLot: sellBookingFees, + Funding: sellFundingFees, + BookingFeesPerLot: sellBookingFees, } buyRate, _ := a.dexPlacementRate(buyVWAP, false) @@ -590,7 +590,7 @@ func testDistribution(t *testing.T, baseID, quoteID uint32) { cex.bidsVWAP[lotSize*sellLots] = vwapResult{avg: sellVWAP} minDexBase = sellLots*lotSize + sellFundingFees if baseID == u.baseFeeID { - minDexBase += sellLots * u.sellFees.bookingFeesPerLot + minDexBase += sellLots * u.sellFees.BookingFeesPerLot } if baseID == u.quoteFeeID { addBaseFees += buyRedeemFees * buyLots @@ -600,7 +600,7 @@ func testDistribution(t *testing.T, baseID, quoteID uint32) { minDexQuote = calc.BaseToQuote(buyRate, buyLots*lotSize) + buyFundingFees if quoteID == u.quoteFeeID { - minDexQuote += buyLots * a.buyFees.bookingFeesPerLot + minDexQuote += buyLots * a.buyFees.BookingFeesPerLot } if quoteID == u.baseFeeID { addQuoteFees += sellRedeemFees * sellLots @@ -902,38 +902,38 @@ func TestMultiTrade(t *testing.T) { return edge + rateStep } - sellPlacements := []*multiTradePlacement{ - {lots: 1, rate: 1e7, counterTradeRate: 0.9e7}, - {lots: 2, rate: 2e7, counterTradeRate: 1.9e7}, - {lots: 3, rate: 3e7, counterTradeRate: 2.9e7}, - {lots: 2, rate: 4e7, counterTradeRate: 3.9e7}, + sellPlacements := []*TradePlacement{ + {Lots: 1, Rate: 1e7, CounterTradeRate: 0.9e7}, + {Lots: 2, Rate: 2e7, CounterTradeRate: 1.9e7}, + {Lots: 3, Rate: 3e7, CounterTradeRate: 2.9e7}, + {Lots: 2, Rate: 4e7, CounterTradeRate: 3.9e7}, } - buyPlacements := []*multiTradePlacement{ - {lots: 1, rate: 4e7, counterTradeRate: 4.1e7}, - {lots: 2, rate: 3e7, counterTradeRate: 3.1e7}, - {lots: 3, rate: 2e7, counterTradeRate: 2.1e7}, - {lots: 2, rate: 1e7, counterTradeRate: 1.1e7}, + buyPlacements := []*TradePlacement{ + {Lots: 1, Rate: 4e7, CounterTradeRate: 4.1e7}, + {Lots: 2, Rate: 3e7, CounterTradeRate: 3.1e7}, + {Lots: 3, Rate: 2e7, CounterTradeRate: 2.1e7}, + {Lots: 2, Rate: 1e7, CounterTradeRate: 1.1e7}, } // cancelLastPlacement is the same as placements, but with the rate // and lots of the last order set to zero, which should cause pending // orders at that placementIndex to be cancelled. - cancelLastPlacement := func(sell bool) []*multiTradePlacement { - placements := make([]*multiTradePlacement, len(sellPlacements)) + cancelLastPlacement := func(sell bool) []*TradePlacement { + placements := make([]*TradePlacement, len(sellPlacements)) if sell { copy(placements, sellPlacements) } else { copy(placements, buyPlacements) } - placements[len(placements)-1] = &multiTradePlacement{} + placements[len(placements)-1] = &TradePlacement{} return placements } // removeLastPlacement simulates a reconfiguration is which the // last placement is removed. - removeLastPlacement := func(sell bool) []*multiTradePlacement { - placements := make([]*multiTradePlacement, len(sellPlacements)) + removeLastPlacement := func(sell bool) []*TradePlacement { + placements := make([]*TradePlacement, len(sellPlacements)) if sell { copy(placements, sellPlacements) } else { @@ -944,23 +944,23 @@ func TestMultiTrade(t *testing.T) { // reconfigToMorePlacements simulates a reconfiguration in which // the lots allocated to the placement at index 1 is reduced by 1. - reconfigToLessPlacements := func(sell bool) []*multiTradePlacement { - placements := make([]*multiTradePlacement, len(sellPlacements)) + reconfigToLessPlacements := func(sell bool) []*TradePlacement { + placements := make([]*TradePlacement, len(sellPlacements)) if sell { copy(placements, sellPlacements) } else { copy(placements, buyPlacements) } - placements[1] = &multiTradePlacement{ - lots: placements[1].lots - 1, - rate: placements[1].rate, - counterTradeRate: placements[1].counterTradeRate, + placements[1] = &TradePlacement{ + Lots: placements[1].Lots - 1, + Rate: placements[1].Rate, + CounterTradeRate: placements[1].CounterTradeRate, } return placements } pendingOrders := func(sell bool, baseID, quoteID uint32) map[order.OrderID]*pendingDEXOrder { - var placements []*multiTradePlacement + var placements []*TradePlacement if sell { placements = sellPlacements } else { @@ -977,7 +977,7 @@ func TestMultiTrade(t *testing.T) { Qty: 1 * lotSize, Sell: sell, ID: orderIDs[0][:], - Rate: driftToleranceEdge(placements[0].rate, true), + Rate: driftToleranceEdge(placements[0].Rate, true), Epoch: currEpoch - 1, BaseID: baseID, QuoteID: quoteID, @@ -987,7 +987,7 @@ func TestMultiTrade(t *testing.T) { Filled: lotSize, Sell: sell, ID: orderIDs[1][:], - Rate: driftToleranceEdge(placements[1].rate, true), + Rate: driftToleranceEdge(placements[1].Rate, true), Epoch: currEpoch - 2, BaseID: baseID, QuoteID: quoteID, @@ -996,7 +996,7 @@ func TestMultiTrade(t *testing.T) { Qty: lotSize, Sell: sell, ID: orderIDs[2][:], - Rate: driftToleranceEdge(placements[2].rate, false), + Rate: driftToleranceEdge(placements[2].Rate, false), Epoch: currEpoch - 2, BaseID: baseID, QuoteID: quoteID, @@ -1005,7 +1005,7 @@ func TestMultiTrade(t *testing.T) { Qty: lotSize, Sell: sell, ID: orderIDs[3][:], - Rate: driftToleranceEdge(placements[3].rate, true), + Rate: driftToleranceEdge(placements[3].Rate, true), Epoch: currEpoch - 2, BaseID: baseID, QuoteID: quoteID, @@ -1015,19 +1015,19 @@ func TestMultiTrade(t *testing.T) { toReturn := map[order.OrderID]*pendingDEXOrder{ orderIDs[0]: { // Should cancel, but cannot due to epoch > currEpoch - 2 placementIndex: 0, - counterTradeRate: placements[0].counterTradeRate, + counterTradeRate: placements[0].CounterTradeRate, }, orderIDs[1]: { placementIndex: 1, - counterTradeRate: placements[1].counterTradeRate, + counterTradeRate: placements[1].CounterTradeRate, }, orderIDs[2]: { placementIndex: 2, - counterTradeRate: placements[2].counterTradeRate, + counterTradeRate: placements[2].CounterTradeRate, }, orderIDs[3]: { placementIndex: 3, - counterTradeRate: placements[3].counterTradeRate, + counterTradeRate: placements[3].CounterTradeRate, }, } @@ -1119,12 +1119,12 @@ func TestMultiTrade(t *testing.T) { sellDexBalances map[uint32]uint64 sellCexBalances map[uint32]uint64 - sellPlacements []*multiTradePlacement + sellPlacements []*TradePlacement sellPendingOrders map[order.OrderID]*pendingDEXOrder buyCexBalances map[uint32]uint64 buyDexBalances map[uint32]uint64 - buyPlacements []*multiTradePlacement + buyPlacements []*TradePlacement buyPendingOrders map[order.OrderID]*pendingDEXOrder isAccountLocker map[uint32]bool @@ -1152,35 +1152,35 @@ func TestMultiTrade(t *testing.T) { // ---- Sell ---- sellDexBalances: map[uint32]uint64{ - 42: 4*lotSize + 4*sellFees.Max.Swap + sellFees.funding, + 42: 4*lotSize + 4*sellFees.Max.Swap + sellFees.Funding, 0: 0, }, sellCexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(sellPlacements[0].counterTradeRate, lotSize) + - b2q(sellPlacements[1].counterTradeRate, 2*lotSize) + - b2q(sellPlacements[2].counterTradeRate, 3*lotSize) + - b2q(sellPlacements[3].counterTradeRate, 2*lotSize), + 0: b2q(sellPlacements[0].CounterTradeRate, lotSize) + + b2q(sellPlacements[1].CounterTradeRate, 2*lotSize) + + b2q(sellPlacements[2].CounterTradeRate, 3*lotSize) + + b2q(sellPlacements[3].CounterTradeRate, 2*lotSize), }, sellPlacements: sellPlacements, sellPendingOrders: pendingOrders(true, 42, 0), expectedSellPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, - {Qty: lotSize, Rate: sellPlacements[3].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, + {Qty: lotSize, Rate: sellPlacements[3].Rate}, }, expectedSellPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, }, // ---- Buy ---- buyDexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(buyPlacements[1].rate, lotSize) + - b2q(buyPlacements[2].rate, 2*lotSize) + - b2q(buyPlacements[3].rate, lotSize) + - 4*buyFees.Max.Swap + buyFees.funding, + 0: b2q(buyPlacements[1].Rate, lotSize) + + b2q(buyPlacements[2].Rate, 2*lotSize) + + b2q(buyPlacements[3].Rate, lotSize) + + 4*buyFees.Max.Swap + buyFees.Funding, }, buyCexBalances: map[uint32]uint64{ 42: 8 * lotSize, @@ -1189,13 +1189,13 @@ func TestMultiTrade(t *testing.T) { buyPlacements: buyPlacements, buyPendingOrders: pendingOrders(false, 42, 0), expectedBuyPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, - {Qty: lotSize, Rate: buyPlacements[3].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, + {Qty: lotSize, Rate: buyPlacements[3].Rate}, }, expectedBuyPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, }, expectedCancels: []order.OrderID{orderIDs[2]}, @@ -1223,34 +1223,34 @@ func TestMultiTrade(t *testing.T) { // ---- Sell ---- sellDexBalances: map[uint32]uint64{ - 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.funding, + 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.Funding, 0: 0, }, sellCexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(sellPlacements[0].counterTradeRate, lotSize) + - b2q(sellPlacements[1].counterTradeRate, 2*lotSize) + - b2q(sellPlacements[2].counterTradeRate, 3*lotSize) + - b2q(sellPlacements[3].counterTradeRate, 2*lotSize), + 0: b2q(sellPlacements[0].CounterTradeRate, lotSize) + + b2q(sellPlacements[1].CounterTradeRate, 2*lotSize) + + b2q(sellPlacements[2].CounterTradeRate, 3*lotSize) + + b2q(sellPlacements[3].CounterTradeRate, 2*lotSize), }, sellPlacements: reconfigToLessPlacements(true), sellPendingOrders: secondPendingOrderNotFilled(true, 42, 0), expectedSellPlacements: []*core.QtyRate{ // {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, - {Qty: lotSize, Rate: sellPlacements[3].rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, + {Qty: lotSize, Rate: sellPlacements[3].Rate}, }, expectedSellPlacementsWithDecrement: []*core.QtyRate{ // {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, }, // ---- Buy ---- buyDexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(buyPlacements[2].rate, 2*lotSize) + - b2q(buyPlacements[3].rate, lotSize) + - 3*buyFees.Max.Swap + buyFees.funding, + 0: b2q(buyPlacements[2].Rate, 2*lotSize) + + b2q(buyPlacements[3].Rate, lotSize) + + 3*buyFees.Max.Swap + buyFees.Funding, }, buyCexBalances: map[uint32]uint64{ 42: 8 * lotSize, @@ -1260,12 +1260,12 @@ func TestMultiTrade(t *testing.T) { buyPendingOrders: secondPendingOrderNotFilled(false, 42, 0), expectedBuyPlacements: []*core.QtyRate{ // {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, - {Qty: lotSize, Rate: buyPlacements[3].rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, + {Qty: lotSize, Rate: buyPlacements[3].Rate}, }, expectedBuyPlacementsWithDecrement: []*core.QtyRate{ // {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, }, expectedCancels: []order.OrderID{orderIDs[1], orderIDs[2]}, @@ -1293,32 +1293,32 @@ func TestMultiTrade(t *testing.T) { // ---- Sell ---- sellDexBalances: map[uint32]uint64{ - 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.funding, + 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.Funding, 0: 0, }, sellCexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(sellPlacements[0].counterTradeRate, lotSize) + - b2q(sellPlacements[1].counterTradeRate, lotSize) + - b2q(sellPlacements[2].counterTradeRate, 3*lotSize) + - b2q(sellPlacements[3].counterTradeRate, 2*lotSize), + 0: b2q(sellPlacements[0].CounterTradeRate, lotSize) + + b2q(sellPlacements[1].CounterTradeRate, lotSize) + + b2q(sellPlacements[2].CounterTradeRate, 3*lotSize) + + b2q(sellPlacements[3].CounterTradeRate, 2*lotSize), }, sellPlacements: sellPlacements, sellPendingOrders: pendingOrdersSelfMatch(true, 42, 0), expectedSellPlacements: []*core.QtyRate{ - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, - {Qty: lotSize, Rate: sellPlacements[3].rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, + {Qty: lotSize, Rate: sellPlacements[3].Rate}, }, expectedSellPlacementsWithDecrement: []*core.QtyRate{ - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, }, // ---- Buy ---- buyDexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(buyPlacements[2].rate, 2*lotSize) + - b2q(buyPlacements[3].rate, lotSize) + - 3*buyFees.Max.Swap + buyFees.funding, + 0: b2q(buyPlacements[2].Rate, 2*lotSize) + + b2q(buyPlacements[3].Rate, lotSize) + + 3*buyFees.Max.Swap + buyFees.Funding, }, buyCexBalances: map[uint32]uint64{ 42: 7 * lotSize, @@ -1327,11 +1327,11 @@ func TestMultiTrade(t *testing.T) { buyPlacements: buyPlacements, buyPendingOrders: pendingOrdersSelfMatch(false, 42, 0), expectedBuyPlacements: []*core.QtyRate{ - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, - {Qty: lotSize, Rate: buyPlacements[3].rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, + {Qty: lotSize, Rate: buyPlacements[3].Rate}, }, expectedBuyPlacementsWithDecrement: []*core.QtyRate{ - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, }, expectedCancels: []order.OrderID{orderIDs[2]}, @@ -1356,33 +1356,33 @@ func TestMultiTrade(t *testing.T) { quoteID: 0, // ---- Sell ---- sellDexBalances: map[uint32]uint64{ - 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.funding, + 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.Funding, 0: 0, }, sellCexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(sellPlacements[0].counterTradeRate, lotSize) + - b2q(sellPlacements[1].counterTradeRate, 2*lotSize) + - b2q(sellPlacements[2].counterTradeRate, 3*lotSize) + - b2q(sellPlacements[3].counterTradeRate, lotSize), + 0: b2q(sellPlacements[0].CounterTradeRate, lotSize) + + b2q(sellPlacements[1].CounterTradeRate, 2*lotSize) + + b2q(sellPlacements[2].CounterTradeRate, 3*lotSize) + + b2q(sellPlacements[3].CounterTradeRate, lotSize), }, sellPlacements: cancelLastPlacement(true), sellPendingOrders: pendingOrders(true, 42, 0), expectedSellPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, }, expectedSellPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: lotSize, Rate: sellPlacements[2].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: lotSize, Rate: sellPlacements[2].Rate}, }, // ---- Buy ---- buyDexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(buyPlacements[1].rate, lotSize) + - b2q(buyPlacements[2].rate, 2*lotSize) + - 3*buyFees.Max.Swap + buyFees.funding, + 0: b2q(buyPlacements[1].Rate, lotSize) + + b2q(buyPlacements[2].Rate, 2*lotSize) + + 3*buyFees.Max.Swap + buyFees.Funding, }, buyCexBalances: map[uint32]uint64{ 42: 7 * lotSize, @@ -1391,12 +1391,12 @@ func TestMultiTrade(t *testing.T) { buyPlacements: cancelLastPlacement(false), buyPendingOrders: pendingOrders(false, 42, 0), expectedBuyPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, }, expectedBuyPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: lotSize, Rate: buyPlacements[2].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: lotSize, Rate: buyPlacements[2].Rate}, }, expectedCancels: []order.OrderID{orderIDs[3], orderIDs[2]}, @@ -1422,33 +1422,33 @@ func TestMultiTrade(t *testing.T) { quoteID: 0, // ---- Sell ---- sellDexBalances: map[uint32]uint64{ - 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.funding, + 42: 3*lotSize + 3*sellFees.Max.Swap + sellFees.Funding, 0: 0, }, sellCexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(sellPlacements[0].counterTradeRate, lotSize) + - b2q(sellPlacements[1].counterTradeRate, 2*lotSize) + - b2q(sellPlacements[2].counterTradeRate, 3*lotSize) + - b2q(sellPlacements[3].counterTradeRate, lotSize), + 0: b2q(sellPlacements[0].CounterTradeRate, lotSize) + + b2q(sellPlacements[1].CounterTradeRate, 2*lotSize) + + b2q(sellPlacements[2].CounterTradeRate, 3*lotSize) + + b2q(sellPlacements[3].CounterTradeRate, lotSize), }, sellPlacements: removeLastPlacement(true), sellPendingOrders: pendingOrders(true, 42, 0), expectedSellPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, }, expectedSellPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: lotSize, Rate: sellPlacements[2].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: lotSize, Rate: sellPlacements[2].Rate}, }, // ---- Buy ---- buyDexBalances: map[uint32]uint64{ 42: 0, - 0: b2q(buyPlacements[1].rate, lotSize) + - b2q(buyPlacements[2].rate, 2*lotSize) + - 3*buyFees.Max.Swap + buyFees.funding, + 0: b2q(buyPlacements[1].Rate, lotSize) + + b2q(buyPlacements[2].Rate, 2*lotSize) + + 3*buyFees.Max.Swap + buyFees.Funding, }, buyCexBalances: map[uint32]uint64{ 42: 7 * lotSize, @@ -1457,12 +1457,12 @@ func TestMultiTrade(t *testing.T) { buyPlacements: removeLastPlacement(false), buyPendingOrders: pendingOrders(false, 42, 0), expectedBuyPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, }, expectedBuyPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: lotSize, Rate: buyPlacements[2].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: lotSize, Rate: buyPlacements[2].Rate}, }, expectedCancels: []order.OrderID{orderIDs[3], orderIDs[2]}, @@ -1494,35 +1494,35 @@ func TestMultiTrade(t *testing.T) { // ---- Sell ---- sellDexBalances: map[uint32]uint64{ 966001: 4 * lotSize, - 966: 4*(sellFees.Max.Swap+sellFees.Max.Refund) + sellFees.funding, + 966: 4*(sellFees.Max.Swap+sellFees.Max.Refund) + sellFees.Funding, 60: 4 * sellFees.Max.Redeem, }, sellCexBalances: map[uint32]uint64{ 96601: 0, - 60: b2q(sellPlacements[0].counterTradeRate, lotSize) + - b2q(sellPlacements[1].counterTradeRate, 2*lotSize) + - b2q(sellPlacements[2].counterTradeRate, 3*lotSize) + - b2q(sellPlacements[3].counterTradeRate, 2*lotSize), + 60: b2q(sellPlacements[0].CounterTradeRate, lotSize) + + b2q(sellPlacements[1].CounterTradeRate, 2*lotSize) + + b2q(sellPlacements[2].CounterTradeRate, 3*lotSize) + + b2q(sellPlacements[3].CounterTradeRate, 2*lotSize), }, sellPlacements: sellPlacements, sellPendingOrders: pendingOrders(true, 966001, 60), expectedSellPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, - {Qty: lotSize, Rate: sellPlacements[3].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, + {Qty: lotSize, Rate: sellPlacements[3].Rate}, }, expectedSellPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: sellPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: sellPlacements[2].rate}, + {Qty: lotSize, Rate: sellPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: sellPlacements[2].Rate}, }, // ---- Buy ---- buyDexBalances: map[uint32]uint64{ 966: 4 * buyFees.Max.Redeem, - 60: b2q(buyPlacements[1].rate, lotSize) + - b2q(buyPlacements[2].rate, 2*lotSize) + - b2q(buyPlacements[3].rate, lotSize) + - 4*buyFees.Max.Swap + 4*buyFees.Max.Refund + buyFees.funding, + 60: b2q(buyPlacements[1].Rate, lotSize) + + b2q(buyPlacements[2].Rate, 2*lotSize) + + b2q(buyPlacements[3].Rate, lotSize) + + 4*buyFees.Max.Swap + 4*buyFees.Max.Refund + buyFees.Funding, }, buyCexBalances: map[uint32]uint64{ 966001: 8 * lotSize, @@ -1531,13 +1531,13 @@ func TestMultiTrade(t *testing.T) { buyPlacements: buyPlacements, buyPendingOrders: pendingOrders(false, 966001, 60), expectedBuyPlacements: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, - {Qty: lotSize, Rate: buyPlacements[3].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, + {Qty: lotSize, Rate: buyPlacements[3].Rate}, }, expectedBuyPlacementsWithDecrement: []*core.QtyRate{ - {Qty: lotSize, Rate: buyPlacements[1].rate}, - {Qty: 2 * lotSize, Rate: buyPlacements[2].rate}, + {Qty: lotSize, Rate: buyPlacements[1].Rate}, + {Qty: 2 * lotSize, Rate: buyPlacements[2].Rate}, }, expectedCancels: []order.OrderID{orderIDs[2]}, @@ -1613,13 +1613,13 @@ func TestMultiTrade(t *testing.T) { adaptor.buyFees = buyFees adaptor.sellFees = sellFees - var placements []*multiTradePlacement + var placements []*TradePlacement if sell { placements = test.sellPlacements } else { placements = test.buyPlacements } - res := adaptor.multiTrade(placements, sell, driftTolerance, currEpoch) + res, _ := adaptor.multiTrade(placements, sell, driftTolerance, currEpoch) expectedOrderIDs := test.expectedOrderIDs if decrement { @@ -1813,7 +1813,7 @@ func TestDEXTrade(t *testing.T) { baseID uint32 quoteID uint32 sell bool - placements []*multiTradePlacement + placements []*TradePlacement initialLockedFunds []*orderLockedFunds postTradeBalances map[uint32]*BotBalance @@ -1839,9 +1839,9 @@ func TestDEXTrade(t *testing.T) { sell: true, baseID: 42, quoteID: 0, - placements: []*multiTradePlacement{ - {lots: 5, rate: rate1}, - {lots: 5, rate: rate2}, + placements: []*TradePlacement{ + {Lots: 5, Rate: rate1}, + {Lots: 5, Rate: rate2}, }, initialLockedFunds: []*orderLockedFunds{ newOrderLockedFunds(orderIDs[0], basePerLot*5, 0, 0, 0), @@ -1970,9 +1970,9 @@ func TestDEXTrade(t *testing.T) { }, baseID: 42, quoteID: 0, - placements: []*multiTradePlacement{ - {lots: 5, rate: rate1}, - {lots: 5, rate: rate2}, + placements: []*TradePlacement{ + {Lots: 5, Rate: rate1}, + {Lots: 5, Rate: rate2}, }, initialLockedFunds: []*orderLockedFunds{ newOrderLockedFunds(orderIDs[0], 5*quotePerLot1, 0, 0, 0), @@ -2108,9 +2108,9 @@ func TestDEXTrade(t *testing.T) { sell: true, baseID: 60, quoteID: 966001, - placements: []*multiTradePlacement{ - {lots: 5, rate: rate1}, - {lots: 5, rate: rate2}, + placements: []*TradePlacement{ + {Lots: 5, Rate: rate1}, + {Lots: 5, Rate: rate2}, }, initialLockedFunds: []*orderLockedFunds{ newOrderLockedFunds(orderIDs[1], 5*basePerLot, 0, 5*redeemFees, 5*refundFees), @@ -2251,9 +2251,9 @@ func TestDEXTrade(t *testing.T) { }, baseID: 60, quoteID: 966001, - placements: []*multiTradePlacement{ - {lots: 5, rate: rate1}, - {lots: 5, rate: rate2}, + placements: []*TradePlacement{ + {Lots: 5, Rate: rate1}, + {Lots: 5, Rate: rate2}, }, initialLockedFunds: []*orderLockedFunds{ newOrderLockedFunds(orderIDs[0], 5*quoteLot1, 5*buyFees, 5*redeemFees, 5*refundFees), @@ -2405,8 +2405,8 @@ func TestDEXTrade(t *testing.T) { ParentAssetLockedAmt: o.parentAssetLockedAmt, RedeemLockedAmt: o.redeemLockedAmt, RefundLockedAmt: o.refundLockedAmt, - Rate: test.placements[i].rate, - Qty: test.placements[i].lots * lotSize, + Rate: test.placements[i].Rate, + Qty: test.placements[i].Lots * lotSize, }) } tCore.multiTradeResult = multiTradeResult @@ -2436,7 +2436,7 @@ func TestDEXTrade(t *testing.T) { t.Fatalf("%s: Connect error: %v", test.name, err) } - orders := adaptor.multiTrade(test.placements, test.sell, 0.01, 100) + orders, _ := adaptor.multiTrade(test.placements, test.sell, 0.01, 100) if len(orders) == 0 { t.Fatalf("%s: multi trade did not place orders", test.name) } @@ -2472,8 +2472,8 @@ func TestDEXTrade(t *testing.T) { ID: uint64(i + 1), DEXOrderEvent: &DEXOrderEvent{ ID: o.id.String(), - Rate: trade.rate, - Qty: trade.lots * lotSize, + Rate: trade.Rate, + Qty: trade.Lots * lotSize, Sell: test.sell, Transactions: []*asset.WalletTransaction{}, }, @@ -3681,8 +3681,8 @@ func TestCEXTrade(t *testing.T) { func TestOrderFeesInUnits(t *testing.T) { type test struct { name string - buyFees *orderFees - sellFees *orderFees + buyFees *OrderFees + sellFees *OrderFees rate uint64 market *MarketWithHost fiatRates map[uint32]float64 diff --git a/client/mm/mm_arb_market_maker.go b/client/mm/mm_arb_market_maker.go index 8fe0a8ce42..45f1947322 100644 --- a/client/mm/mm_arb_market_maker.go +++ b/client/mm/mm_arb_market_maker.go @@ -235,18 +235,18 @@ func (a *arbMarketMaker) dexPlacementRate(cexRate uint64, sell bool) (uint64, er return dexPlacementRate(cexRate, sell, a.cfg().Profit, a.market, feesInQuoteUnits, a.log) } -func (a *arbMarketMaker) ordersToPlace() (buys, sells []*multiTradePlacement) { - orders := func(cfgPlacements []*ArbMarketMakingPlacement, sellOnDEX bool) []*multiTradePlacement { - newPlacements := make([]*multiTradePlacement, 0, len(cfgPlacements)) +func (a *arbMarketMaker) ordersToPlace() (buys, sells []*TradePlacement) { + orders := func(cfgPlacements []*ArbMarketMakingPlacement, sellOnDEX bool) []*TradePlacement { + newPlacements := make([]*TradePlacement, 0, len(cfgPlacements)) var cumulativeCEXDepth uint64 for i, cfgPlacement := range cfgPlacements { cumulativeCEXDepth += uint64(float64(cfgPlacement.Lots*a.lotSize) * cfgPlacement.Multiplier) _, extrema, filled, err := a.CEX.VWAP(a.baseID, a.quoteID, sellOnDEX, cumulativeCEXDepth) if err != nil { a.log.Errorf("Error calculating vwap: %v", err) - newPlacements = append(newPlacements, &multiTradePlacement{ - rate: 0, - lots: 0, + newPlacements = append(newPlacements, &TradePlacement{ + Rate: 0, + Lots: 0, }) continue } @@ -259,9 +259,9 @@ func (a *arbMarketMaker) ordersToPlace() (buys, sells []*multiTradePlacement) { if !filled { a.log.Infof("CEX %s side has < %s on the orderbook.", sellStr(!sellOnDEX), a.fmtBase(cumulativeCEXDepth)) - newPlacements = append(newPlacements, &multiTradePlacement{ - rate: 0, - lots: 0, + newPlacements = append(newPlacements, &TradePlacement{ + Rate: 0, + Lots: 0, }) continue } @@ -269,17 +269,17 @@ func (a *arbMarketMaker) ordersToPlace() (buys, sells []*multiTradePlacement) { placementRate, err := a.dexPlacementRate(extrema, sellOnDEX) if err != nil { a.log.Errorf("Error calculating dex placement rate: %v", err) - newPlacements = append(newPlacements, &multiTradePlacement{ - rate: 0, - lots: 0, + newPlacements = append(newPlacements, &TradePlacement{ + Rate: 0, + Lots: 0, }) continue } - newPlacements = append(newPlacements, &multiTradePlacement{ - rate: placementRate, - lots: cfgPlacement.Lots, - counterTradeRate: extrema, + newPlacements = append(newPlacements, &TradePlacement{ + Rate: placementRate, + Lots: cfgPlacement.Lots, + CounterTradeRate: extrema, }) } @@ -354,8 +354,8 @@ func (a *arbMarketMaker) rebalance(epoch uint64) { } buys, sells := a.ordersToPlace() - buyInfos := a.multiTrade(buys, false, a.cfg().DriftTolerance, currEpoch) - sellInfos := a.multiTrade(sells, true, a.cfg().DriftTolerance, currEpoch) + buyInfos, buyReport := a.multiTrade(buys, false, a.cfg().DriftTolerance, currEpoch) + sellInfos, sellReport := a.multiTrade(sells, true, a.cfg().DriftTolerance, currEpoch) a.matchesMtx.Lock() for oid, info := range buyInfos { a.pendingOrders[oid] = info.counterTradeRate @@ -368,6 +368,8 @@ func (a *arbMarketMaker) rebalance(epoch uint64) { a.cancelExpiredCEXTrades() a.registerFeeGap() + + a.Broadcast(newEpochNote(a.mwh, buyReport, sellReport)) } func (a *arbMarketMaker) tryTransfers(currEpoch uint64) (actionTaken bool, err error) { diff --git a/client/mm/mm_arb_market_maker_test.go b/client/mm/mm_arb_market_maker_test.go index ab63639780..3dbed6907c 100644 --- a/client/mm/mm_arb_market_maker_test.go +++ b/client/mm/mm_arb_market_maker_test.go @@ -49,7 +49,7 @@ func TestArbMMRebalance(t *testing.T) { cex: newTBotCEXAdaptor(), pendingOrders: make(map[order.OrderID]uint64), } - a.buyFees = &orderFees{ + a.buyFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: &LotFees{ Redeem: buyRedeemFees, @@ -57,9 +57,9 @@ func TestArbMMRebalance(t *testing.T) { }, Estimated: &LotFees{}, }, - bookingFeesPerLot: buySwapFees, + BookingFeesPerLot: buySwapFees, } - a.sellFees = &orderFees{ + a.sellFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: &LotFees{ Redeem: sellRedeemFees, @@ -67,7 +67,7 @@ func TestArbMMRebalance(t *testing.T) { }, Estimated: &LotFees{}, }, - bookingFeesPerLot: sellSwapFees, + BookingFeesPerLot: sellSwapFees, } var buyLots, sellLots, minDexBase, minCexBase /* totalBase, */, minDexQuote, minCexQuote /*, totalQuote */ uint64 @@ -103,7 +103,7 @@ func TestArbMMRebalance(t *testing.T) { } minDexBase = sellLots * (lotSize + sellSwapFees) minCexBase = buyLots * lotSize - minDexQuote = calc.BaseToQuote(buyRate, buyLots*lotSize) + a.buyFees.bookingFeesPerLot*buyLots + minDexQuote = calc.BaseToQuote(buyRate, buyLots*lotSize) + a.buyFees.BookingFeesPerLot*buyLots minCexQuote = calc.BaseToQuote(sellRate, sellLots*lotSize) } diff --git a/client/mm/mm_basic.go b/client/mm/mm_basic.go index ae20c815da..0cb5fc5d74 100644 --- a/client/mm/mm_basic.go +++ b/client/mm/mm_basic.go @@ -318,7 +318,7 @@ func (m *basicMarketMaker) orderPrice(basisPrice, feeAdj uint64, sell bool, gapF return basisPrice - adj } -func (m *basicMarketMaker) ordersToPlace() (buyOrders, sellOrders []*multiTradePlacement, err error) { +func (m *basicMarketMaker) ordersToPlace() (buyOrders, sellOrders []*TradePlacement, err error) { basisPrice := m.calculator.basisPrice() if basisPrice == 0 { return nil, nil, fmt.Errorf("no basis price available") @@ -340,8 +340,8 @@ func (m *basicMarketMaker) ordersToPlace() (buyOrders, sellOrders []*multiTradeP m.name, m.fmtRate(basisPrice), m.fmtRate(feeAdj)) } - orders := func(orderPlacements []*OrderPlacement, sell bool) []*multiTradePlacement { - placements := make([]*multiTradePlacement, 0, len(orderPlacements)) + orders := func(orderPlacements []*OrderPlacement, sell bool) []*TradePlacement { + placements := make([]*TradePlacement, 0, len(orderPlacements)) for i, p := range orderPlacements { rate := m.orderPrice(basisPrice, feeAdj, sell, p.GapFactor) @@ -354,9 +354,9 @@ func (m *basicMarketMaker) ordersToPlace() (buyOrders, sellOrders []*multiTradeP if rate == 0 { lots = 0 } - placements = append(placements, &multiTradePlacement{ - rate: rate, - lots: lots, + placements = append(placements, &TradePlacement{ + Rate: rate, + Lots: lots, }) } return placements diff --git a/client/mm/mm_basic_test.go b/client/mm/mm_basic_test.go index 624df55aaa..1a4daa34f7 100644 --- a/client/mm/mm_basic_test.go +++ b/client/mm/mm_basic_test.go @@ -192,8 +192,8 @@ func TestBasicMMRebalance(t *testing.T) { cfgBuyPlacements []*OrderPlacement cfgSellPlacements []*OrderPlacement - expBuyPlacements []*multiTradePlacement - expSellPlacements []*multiTradePlacement + expBuyPlacements []*TradePlacement + expSellPlacements []*TradePlacement } tests := []*test{ { @@ -209,15 +209,15 @@ func TestBasicMMRebalance(t *testing.T) { {Lots: 2, GapFactor: 2}, {Lots: 1, GapFactor: 3}, }, - expBuyPlacements: []*multiTradePlacement{ - {lots: 1, rate: steppedRate(basisPrice-3*halfSpread, rateStep)}, - {lots: 2, rate: steppedRate(basisPrice-2*halfSpread, rateStep)}, - {lots: 3, rate: steppedRate(basisPrice-1*halfSpread, rateStep)}, + expBuyPlacements: []*TradePlacement{ + {Lots: 1, Rate: steppedRate(basisPrice-3*halfSpread, rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice-2*halfSpread, rateStep)}, + {Lots: 3, Rate: steppedRate(basisPrice-1*halfSpread, rateStep)}, }, - expSellPlacements: []*multiTradePlacement{ - {lots: 3, rate: steppedRate(basisPrice+1*halfSpread, rateStep)}, - {lots: 2, rate: steppedRate(basisPrice+2*halfSpread, rateStep)}, - {lots: 1, rate: steppedRate(basisPrice+3*halfSpread, rateStep)}, + expSellPlacements: []*TradePlacement{ + {Lots: 3, Rate: steppedRate(basisPrice+1*halfSpread, rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice+2*halfSpread, rateStep)}, + {Lots: 1, Rate: steppedRate(basisPrice+3*halfSpread, rateStep)}, }, }, { @@ -233,15 +233,15 @@ func TestBasicMMRebalance(t *testing.T) { {Lots: 2, GapFactor: 0.1}, {Lots: 1, GapFactor: 0.05}, }, - expBuyPlacements: []*multiTradePlacement{ - {lots: 1, rate: steppedRate(basisPrice-uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, - {lots: 2, rate: steppedRate(basisPrice-uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, - {lots: 3, rate: steppedRate(basisPrice-uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, + expBuyPlacements: []*TradePlacement{ + {Lots: 1, Rate: steppedRate(basisPrice-uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice-uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, + {Lots: 3, Rate: steppedRate(basisPrice-uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, }, - expSellPlacements: []*multiTradePlacement{ - {lots: 3, rate: steppedRate(basisPrice+uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, - {lots: 2, rate: steppedRate(basisPrice+uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, - {lots: 1, rate: steppedRate(basisPrice+uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, + expSellPlacements: []*TradePlacement{ + {Lots: 3, Rate: steppedRate(basisPrice+uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice+uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, + {Lots: 1, Rate: steppedRate(basisPrice+uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, }, }, { @@ -257,15 +257,15 @@ func TestBasicMMRebalance(t *testing.T) { {Lots: 2, GapFactor: 0.1}, {Lots: 1, GapFactor: 0.05}, }, - expBuyPlacements: []*multiTradePlacement{ - {lots: 1, rate: steppedRate(basisPrice-halfSpread-uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, - {lots: 2, rate: steppedRate(basisPrice-halfSpread-uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, - {lots: 3, rate: steppedRate(basisPrice-halfSpread-uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, + expBuyPlacements: []*TradePlacement{ + {Lots: 1, Rate: steppedRate(basisPrice-halfSpread-uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice-halfSpread-uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, + {Lots: 3, Rate: steppedRate(basisPrice-halfSpread-uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, }, - expSellPlacements: []*multiTradePlacement{ - {lots: 3, rate: steppedRate(basisPrice+halfSpread+uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, - {lots: 2, rate: steppedRate(basisPrice+halfSpread+uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, - {lots: 1, rate: steppedRate(basisPrice+halfSpread+uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, + expSellPlacements: []*TradePlacement{ + {Lots: 3, Rate: steppedRate(basisPrice+halfSpread+uint64(math.Round((float64(basisPrice)*0.15))), rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice+halfSpread+uint64(math.Round((float64(basisPrice)*0.1))), rateStep)}, + {Lots: 1, Rate: steppedRate(basisPrice+halfSpread+uint64(math.Round((float64(basisPrice)*0.05))), rateStep)}, }, }, { @@ -281,14 +281,14 @@ func TestBasicMMRebalance(t *testing.T) { {Lots: 2, GapFactor: .03}, {Lots: 1, GapFactor: .01}, }, - expBuyPlacements: []*multiTradePlacement{ - {lots: 1, rate: steppedRate(basisPrice-1e6, rateStep)}, - {lots: 2, rate: steppedRate(basisPrice-3e6, rateStep)}, + expBuyPlacements: []*TradePlacement{ + {Lots: 1, Rate: steppedRate(basisPrice-1e6, rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice-3e6, rateStep)}, }, - expSellPlacements: []*multiTradePlacement{ - {lots: 3, rate: steppedRate(basisPrice+6e6, rateStep)}, - {lots: 2, rate: steppedRate(basisPrice+3e6, rateStep)}, - {lots: 1, rate: steppedRate(basisPrice+1e6, rateStep)}, + expSellPlacements: []*TradePlacement{ + {Lots: 3, Rate: steppedRate(basisPrice+6e6, rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice+3e6, rateStep)}, + {Lots: 1, Rate: steppedRate(basisPrice+1e6, rateStep)}, }, }, { @@ -304,14 +304,14 @@ func TestBasicMMRebalance(t *testing.T) { {Lots: 2, GapFactor: .03}, {Lots: 1, GapFactor: .01}, }, - expBuyPlacements: []*multiTradePlacement{ - {lots: 1, rate: steppedRate(basisPrice-halfSpread-1e6, rateStep)}, - {lots: 2, rate: steppedRate(basisPrice-halfSpread-3e6, rateStep)}, + expBuyPlacements: []*TradePlacement{ + {Lots: 1, Rate: steppedRate(basisPrice-halfSpread-1e6, rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice-halfSpread-3e6, rateStep)}, }, - expSellPlacements: []*multiTradePlacement{ - {lots: 3, rate: steppedRate(basisPrice+halfSpread+6e6, rateStep)}, - {lots: 2, rate: steppedRate(basisPrice+halfSpread+3e6, rateStep)}, - {lots: 1, rate: steppedRate(basisPrice+halfSpread+1e6, rateStep)}, + expSellPlacements: []*TradePlacement{ + {Lots: 3, Rate: steppedRate(basisPrice+halfSpread+6e6, rateStep)}, + {Lots: 2, Rate: steppedRate(basisPrice+halfSpread+3e6, rateStep)}, + {Lots: 1, Rate: steppedRate(basisPrice+halfSpread+1e6, rateStep)}, }, }, } @@ -336,7 +336,7 @@ func TestBasicMMRebalance(t *testing.T) { mm.fiatRates.Store(map[uint32]float64{baseID: 1, quoteID: 1}) const sellSwapFees, sellRedeemFees = 3e6, 1e6 const buySwapFees, buyRedeemFees = 2e5, 1e5 - mm.buyFees = &orderFees{ + mm.buyFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: &LotFees{ Redeem: buyRedeemFees, @@ -344,9 +344,9 @@ func TestBasicMMRebalance(t *testing.T) { }, Estimated: &LotFees{}, }, - bookingFeesPerLot: buySwapFees, + BookingFeesPerLot: buySwapFees, } - mm.sellFees = &orderFees{ + mm.sellFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: &LotFees{ Redeem: sellRedeemFees, @@ -354,7 +354,7 @@ func TestBasicMMRebalance(t *testing.T) { }, Estimated: &LotFees{}, }, - bookingFeesPerLot: sellSwapFees, + BookingFeesPerLot: sellSwapFees, } mm.baseDexBalances[baseID] = lotSize * 50 mm.baseCexBalances[baseID] = lotSize * 50 @@ -382,11 +382,11 @@ func TestBasicMMRebalance(t *testing.T) { buyRateLots[p.Rate] = p.Qty / lotSize } for _, expBuy := range tt.expBuyPlacements { - if lots, found := buyRateLots[expBuy.rate]; !found { - t.Fatalf("buy rate %d not found", expBuy.rate) + if lots, found := buyRateLots[expBuy.Rate]; !found { + t.Fatalf("buy rate %d not found", expBuy.Rate) } else { - if expBuy.lots != lots { - t.Fatalf("wrong lots %d for buy at rate %d", lots, expBuy.rate) + if expBuy.Lots != lots { + t.Fatalf("wrong lots %d for buy at rate %d", lots, expBuy.Rate) } } } @@ -395,11 +395,11 @@ func TestBasicMMRebalance(t *testing.T) { sellRateLots[p.Rate] = p.Qty / lotSize } for _, expSell := range tt.expSellPlacements { - if lots, found := sellRateLots[expSell.rate]; !found { - t.Fatalf("sell rate %d not found", expSell.rate) + if lots, found := sellRateLots[expSell.Rate]; !found { + t.Fatalf("sell rate %d not found", expSell.Rate) } else { - if expSell.lots != lots { - t.Fatalf("wrong lots %d for sell at rate %d", lots, expSell.rate) + if expSell.Lots != lots { + t.Fatalf("wrong lots %d for sell at rate %d", lots, expSell.Rate) } } } diff --git a/client/mm/mm_simple_arb_test.go b/client/mm/mm_simple_arb_test.go index 8ac88b3d8c..473496db0c 100644 --- a/client/mm/mm_simple_arb_test.go +++ b/client/mm/mm_simple_arb_test.go @@ -516,7 +516,7 @@ func TestArbRebalance(t *testing.T) { a.CEX = tcex tcex.asksVWAP[lotSize] = vwapResult{avg: buyRate} tcex.bidsVWAP[lotSize] = vwapResult{avg: sellRate} - a.buyFees = &orderFees{ + a.buyFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: &LotFees{ Redeem: buyRedeemFees, @@ -526,9 +526,9 @@ func TestArbRebalance(t *testing.T) { Redeem: buyRedeemFees, }, }, - bookingFeesPerLot: buySwapFees, + BookingFeesPerLot: buySwapFees, } - a.sellFees = &orderFees{ + a.sellFees = &OrderFees{ LotFeeRange: &LotFeeRange{ Max: &LotFees{ Redeem: sellRedeemFees, @@ -538,7 +538,7 @@ func TestArbRebalance(t *testing.T) { Redeem: sellRedeemFees, }, }, - bookingFeesPerLot: sellSwapFees, + BookingFeesPerLot: sellSwapFees, } // arbEngine.setBotLoop(arbEngine.botLoop) a.cfgV.Store(&SimpleArbConfig{ diff --git a/client/mm/mm_test.go b/client/mm/mm_test.go index 956db9679c..6bb0b661a3 100644 --- a/client/mm/mm_test.go +++ b/client/mm/mm_test.go @@ -69,8 +69,8 @@ type tCore struct { assetBalances map[uint32]*core.WalletBalance assetBalanceErr error market *core.Market - singleLotSellFees *orderFees - singleLotBuyFees *orderFees + singleLotSellFees *OrderFees + singleLotBuyFees *OrderFees singleLotFeesErr error multiTradeResult []*core.Order noteFeed chan core.Notification @@ -243,8 +243,8 @@ type tBotCoreAdaptor struct { groupedBuys map[uint64][]*core.Order groupedSells map[uint64][]*core.Order orderUpdates chan *core.Order - buyFees *orderFees - sellFees *orderFees + buyFees *OrderFees + sellFees *OrderFees fiatExchangeRate uint64 buyFeesInBase uint64 sellFeesInBase uint64 @@ -273,7 +273,7 @@ func (c *tBotCoreAdaptor) ExchangeRateFromFiatSources() uint64 { return c.fiatExchangeRate } -func (c *tBotCoreAdaptor) OrderFees() (buyFees, sellFees *orderFees, err error) { +func (c *tBotCoreAdaptor) OrderFees() (buyFees, sellFees *OrderFees, err error) { return c.buyFees, c.sellFees, nil } diff --git a/client/mm/notification.go b/client/mm/notification.go index 42df01c83e..467b8a7a41 100644 --- a/client/mm/notification.go +++ b/client/mm/notification.go @@ -11,6 +11,7 @@ const ( NoteTypeRunStats = "runstats" NoteTypeRunEvent = "runevent" NoteTypeCEXNotification = "cexnote" + NoteTypeEpochReport = "mmepoch" ) type runStatsNote struct { @@ -71,3 +72,21 @@ func newCexUpdateNote(cexName string, topic db.Topic, note interface{}) *cexNoti Note: note, } } + +type epochReport struct { + db.Notification + Market *MarketWithHost `json:"market"` + BuyReport *OrderReport `json:"buyReport"` + SellReport *OrderReport `json:"sellReport"` +} + +const TopicEpochReport = "EpochReport" + +func newEpochNote(mwh *MarketWithHost, buyReport, sellReport *OrderReport) *epochReport { + return &epochReport{ + Notification: db.NewNotification(NoteTypeEpochReport, TopicEpochReport, "", "", db.Data), + Market: mwh, + BuyReport: buyReport, + SellReport: sellReport, + } +} diff --git a/server/market/market_test.go b/server/market/market_test.go index 277c56d01d..27f3f8e8eb 100644 --- a/server/market/market_test.go +++ b/server/market/market_test.go @@ -270,7 +270,7 @@ const ( tUserTier, tUserScore, tMaxScore = int64(1), int32(30), int32(60) ) -var parcelLimit = float64(calcParcelLimit(tUserTier, tUserScore, tMaxScore)) +var parcelLimit = float64(calc.ParcelLimit(tUserTier, tUserScore, tMaxScore)) func newTestMarket(opts ...any) (*Market, *TArchivist, *TAuth, func(), error) { // The DEX will make MasterCoinLockers for each asset.