Skip to content

Commit

Permalink
Decision Info
Browse files Browse the repository at this point in the history
  • Loading branch information
martonp committed Aug 1, 2023
1 parent 318dde8 commit 7d02c3a
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 16 deletions.
20 changes: 20 additions & 0 deletions client/mm/mm.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,26 @@ func (m *MarketMaker) handleNotification(n core.Notification) {
}
}

func (m *MarketMaker) DecisionInfo(cfg *BotConfig) (interface{}, error) {
mkt, err := m.core.ExchangeMarket(cfg.Host, cfg.BaseAsset, cfg.QuoteAsset)
if err != nil {
return nil, fmt.Errorf("failed to get market %s-%d-%d: %w", cfg.Host, cfg.BaseAsset, cfg.QuoteAsset, err)
}

orderbook, feed, err := m.core.SyncBook(cfg.Host, cfg.BaseAsset, cfg.QuoteAsset)
if err != nil {
return nil, fmt.Errorf("failed to sync order book for market %s-%d-%d: %w", cfg.Host, cfg.BaseAsset, cfg.QuoteAsset, err)
}
defer feed.Close()

switch {
case cfg.MMCfg != nil:
return getBasicMMDecisionInfo(cfg, mkt, orderbook)

Check failure on line 725 in client/mm/mm.go

View workflow job for this annotation

GitHub Actions / Go CI (1.19)

not enough arguments in call to getBasicMMDecisionInfo

Check failure on line 725 in client/mm/mm.go

View workflow job for this annotation

GitHub Actions / Go CI (1.20)

not enough arguments in call to getBasicMMDecisionInfo
default:
return nil, fmt.Errorf("only basic market making is supported at this time")
}
}

// Run starts the MarketMaker. There can only be one BotConfig per dex market.
func (m *MarketMaker) Run(ctx context.Context, cfgs []*BotConfig, pw []byte) error {
if !m.running.CompareAndSwap(false, true) {
Expand Down
115 changes: 99 additions & 16 deletions client/mm/mm_basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,80 @@ type MarketMakingConfig struct {
SplitBuffer *uint64 `json:"splitBuffer"`
}

type basicMMDecisionInfo struct {
MidGap uint64 `json:"midGap"`
OracleRate uint64 `json:"oracleRate"`
FiatRate uint64 `json:"fiatRate"`
BasisPrice uint64 `json:"basisPrice"`

BaseSingleLotFees uint64 `json:"baseSingleLotFees"`
QuoteSingleLotFees uint64 `json:"quoteSingleLotFees"`
HalfGap uint64 `json:"halfGap"`

SellOrders []*rateLots `json:"sellOrders"`
BuyOrders []*rateLots `json:"buyOrders"`
}

func getBasicMMDecisionInfo(cfg *BotConfig, mkt *core.Market, ob dexOrderBook, oracle oracle,
baseFiatRate, quoteFiatRate float64, core clientCore, log dex.Logger) (*basicMMDecisionInfo, error) {
if cfg.MMCfg == nil {
return nil, fmt.Errorf("no market making config")
}

midGap, err := ob.MidGap()
if err != nil && !errors.Is(err, orderbook.ErrEmptyOrderbook) {
return nil, fmt.Errorf("MidGap error: %v", err)
}

oracleRate := oracle.getMarketPrice(cfg.BaseAsset, cfg.QuoteAsset)
msgOracleRate := mkt.ConventionalRateToMsg(oracleRate)
fiatRate := uint64(baseFiatRate + quoteFiatRate) // TODO
basisPrice := basisPrice(ob, oracle, cfg.MMCfg, mkt, fiatRate, log)

info := &basicMMDecisionInfo{
MidGap: midGap,
OracleRate: msgOracleRate,
FiatRate: fiatRate,
BasisPrice: basisPrice,
}

if basisPrice == 0 {
return info, nil
}

var halfSpread uint64
if needBreakEvenHalfSpread(cfg.MMCfg.GapStrategy) {
hs, baseFees, quoteFees, err := calculateHalfSpread(basisPrice, cfg.Host, cfg.BaseAsset, cfg.QuoteAsset, mkt, core)
if err != nil {
return nil, fmt.Errorf("calculateHalfSpread error: %v", err)
}
halfSpread = hs
info.HalfGap = halfSpread
info.BaseSingleLotFees = baseFees
info.QuoteSingleLotFees = quoteFees
}

info.BuyOrders = make([]*rateLots, 0, len(cfg.MMCfg.BuyPlacements))
for _, p := range cfg.MMCfg.BuyPlacements {
rate := orderPrice(basisPrice, halfSpread, cfg.MMCfg.GapStrategy, p.GapFactor, false, mkt)
info.BuyOrders = append(info.BuyOrders, &rateLots{
Rate: rate,
Lots: p.Lots,
})
}

info.SellOrders = make([]*rateLots, 0, len(cfg.MMCfg.SellPlacements))
for _, p := range cfg.MMCfg.SellPlacements {
rate := orderPrice(basisPrice, halfSpread, cfg.MMCfg.GapStrategy, p.GapFactor, true, mkt)
info.SellOrders = append(info.SellOrders, &rateLots{
Rate: rate,
Lots: p.Lots,
})
}

return info, nil
}

func needBreakEvenHalfSpread(strat GapStrategy) bool {
return strat == GapStrategyAbsolutePlus || strat == GapStrategyPercentPlus || strat == GapStrategyMultiplier
}
Expand Down Expand Up @@ -344,37 +418,46 @@ func (m *basicMarketMaker) basisPrice() uint64 {
return basisPrice(m.book, m.oracle, m.cfg, m.mkt, m.fiatRateV.Load(), m.log)
}

func (m *basicMarketMaker) halfSpread(basisPrice uint64) (uint64, error) {
func calculateHalfSpread(basisPrice uint64, host string, base, quote uint32, mkt *core.Market, c clientCore) (halfSpread, baseFees, quoteFees uint64, err error) {
form := &core.SingleLotFeesForm{
Host: m.host,
Base: m.base,
Quote: m.quote,
Host: host,
Base: base,
Quote: quote,
Sell: true,
}

if basisPrice == 0 { // prevent divide by zero later
return 0, fmt.Errorf("basis price cannot be zero")
return 0, 0, 0, fmt.Errorf("basis price cannot be zero")
}

baseFees, quoteFees, err := m.core.SingleLotFees(form)
baseFees, quoteFees, err = c.SingleLotFees(form)
if err != nil {
return 0, fmt.Errorf("SingleLotFees error: %v", err)
return 0, 0, 0, fmt.Errorf("SingleLotFees error: %v", err)
}

form.Sell = false
newQuoteFees, newBaseFees, err := m.core.SingleLotFees(form)
newQuoteFees, newBaseFees, err := c.SingleLotFees(form)
if err != nil {
return 0, fmt.Errorf("SingleLotFees error: %v", err)
return 0, 0, 0, fmt.Errorf("SingleLotFees error: %v", err)
}

baseFees += newBaseFees
quoteFees += newQuoteFees

g := float64(calc.BaseToQuote(basisPrice, baseFees)+quoteFees) /
float64(baseFees+2*m.mkt.LotSize) * m.mkt.AtomToConv
float64(baseFees+2*mkt.LotSize) * mkt.AtomToConv

halfGap := uint64(math.Round(g * calc.RateEncodingFactor))

return halfGap, baseFees, quoteFees, nil
}

func (m *basicMarketMaker) halfSpread(basisPrice uint64) (uint64, error) {
halfGap, baseFees, quoteFees, err := calculateHalfSpread(basisPrice, m.host, m.base, m.quote, m.mkt, m.core)
if err != nil {
return 0, err
}

m.log.Tracef("halfSpread: base basis price = %d, lot size = %d, base fees = %d, quote fees = %d, half-gap = %d",
basisPrice, m.mkt.LotSize, baseFees, quoteFees, halfGap)

Expand All @@ -385,8 +468,8 @@ func (m *basicMarketMaker) placeMultiTrade(placements []*rateLots, sell bool) {
qtyRates := make([]*core.QtyRate, 0, len(placements))
for _, p := range placements {
qtyRates = append(qtyRates, &core.QtyRate{
Qty: p.lots * m.mkt.LotSize,
Rate: p.rate,
Qty: p.Lots * m.mkt.LotSize,
Rate: p.Rate,
})
}

Expand Down Expand Up @@ -485,8 +568,8 @@ type rebalancer interface {
}

type rateLots struct {
rate uint64
lots uint64
Rate uint64 `json:"rate"`
Lots uint64 `json:"lots"`
placementIndex int
}

Expand Down Expand Up @@ -690,8 +773,8 @@ func basicMMRebalance(newEpoch uint64, m rebalancer, c clientCore, cfg *MarketMa
}
remainingBalance -= requiredPerLot * lotsToPlace
rlPlacements = append(rlPlacements, &rateLots{
rate: placementRate,
lots: lotsToPlace,
Rate: placementRate,
Lots: lotsToPlace,
placementIndex: i,
})
}
Expand Down

0 comments on commit 7d02c3a

Please sign in to comment.