Skip to content

Commit

Permalink
Fix preview amounts after supporting precisions (#674)
Browse files Browse the repository at this point in the history
* Update marketmaking formula & fix previews

* Fix min tradable amount
  • Loading branch information
altafan authored Feb 13, 2023
1 parent aafa4f6 commit 3c82b18
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 412 deletions.
43 changes: 23 additions & 20 deletions internal/core/application/trade_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type TradeService interface {
GetMarketPrice(
ctx context.Context,
market Market,
) (*decimal.Decimal, uint64, error)
) (decimal.Decimal, uint64, error)
GetTradePreview(
ctx context.Context,
market Market,
Expand Down Expand Up @@ -190,46 +190,49 @@ func (t *tradeService) GetTradableMarkets(ctx context.Context) (
func (t *tradeService) GetMarketPrice(
ctx context.Context,
market Market,
) (*decimal.Decimal, uint64, error) {
) (decimal.Decimal, uint64, error) {
if err := market.Validate(); err != nil {
return nil, 0, err
return decimal.Zero, 0, err
}

mkt, _, err := t.repoManager.MarketRepository().GetMarketByAssets(
ctx, market.BaseAsset, market.QuoteAsset,
)
if err != nil {
log.Debugf("error while retrieving market: %s", err)
return nil, 0, ErrServiceUnavailable
return decimal.Zero, 0, ErrServiceUnavailable
}
if mkt == nil {
return nil, 0, ErrMarketNotExist
return decimal.Zero, 0, ErrMarketNotExist
}

balance, err := getUnlockedBalanceForMarket(t.repoManager, ctx, mkt)
if err != nil {
log.WithError(err).Debug("error while retrieving balance")
return nil, 0, ErrServiceUnavailable
return decimal.Zero, 0, ErrServiceUnavailable
}

price, err := mkt.SpotPrice(balance.BaseAmount, balance.QuoteAmount)
if err != nil {
log.WithError(err).Debug("error while retrieving spot price")
return nil, 0, ErrServiceUnavailable
}
spotPrice := &price.QuotePrice
minAmount := uint64(mkt.FixedFee.BaseFee) + 1
if minAmount == 1 {
if mkt.BaseAssetPrecision > mkt.QuoteAssetPrecision {
minAmountDec := price.BasePrice.Div(
decimal.NewFromFloat(math.Pow10(int(mkt.QuoteAssetPrecision))),
).Mul(decimal.NewFromFloat(math.Pow10(int(mkt.BaseAssetPrecision))))
if minAmountDec.GreaterThanOrEqual(decimal.NewFromInt(1)) {
minAmount = minAmountDec.BigInt().Uint64()
}
}
return decimal.Zero, 0, ErrServiceUnavailable
}

// 1 sat of base asset * quote price is the ideal min tradable amount but
// there are max 8 decimal in blockchain, so if the value is < 1, the min
// tradable amount is 1 / value.
// For example, if 1 sat of base asset * quote price = 0.001, the min
// tradable amount is 1 / 0.001 = 100 sats of base asset (fees excluded).
minAmount := decimal.NewFromFloat(math.Pow10(-int(mkt.BaseAssetPrecision))).Mul(price.QuotePrice)
if one := decimal.NewFromFloat(1); minAmount.LessThan(one) {
minAmount = one.Div(minAmount)
}
minTradableAmount := minAmount.BigInt().Uint64()
if mkt.FixedFee.BaseFee > 0 {
minTradableAmount += uint64(mkt.FixedFee.BaseFee)
}
return spotPrice, minAmount, nil

return price.QuotePrice, minTradableAmount, nil
}

func (t *tradeService) GetTradePreview(
Expand Down
12 changes: 7 additions & 5 deletions internal/core/domain/market_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ func NewMarket(
}

return &Market{
AccountIndex: accountIndex,
BaseAsset: baseAsset,
QuoteAsset: quoteAsset,
Fee: feeInBasisPoint,
Strategy: mm.NewStrategyFromFormula(formula.BalancedReserves{}),
AccountIndex: accountIndex,
BaseAsset: baseAsset,
QuoteAsset: quoteAsset,
BaseAssetPrecision: baseAssetPrecision,
QuoteAssetPrecision: quoteAssetPrecision,
Fee: feeInBasisPoint,
Strategy: mm.NewStrategyFromFormula(formula.BalancedReserves{}),
}, nil
}

Expand Down
75 changes: 47 additions & 28 deletions internal/core/domain/market_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package domain

import (
"fmt"
"math"

"github.com/shopspring/decimal"
mm "github.com/tdex-network/tdex-daemon/pkg/marketmaking"
"github.com/tdex-network/tdex-daemon/pkg/marketmaking/formula"
"github.com/tdex-network/tdex-daemon/pkg/mathutil"
)

// IsZero ...
Expand Down Expand Up @@ -248,26 +250,40 @@ func (m *Market) Preview(
return nil, err
}

previewAmount, err := formula(args, amount)
if err != nil {
return nil, err
assetPrecision := math.Pow10(int(m.BaseAssetPrecision))
if !isBaseAsset {
assetPrecision = math.Pow10(int(m.QuoteAssetPrecision))
}
amountDecimal := mathutil.Div(amount, uint64(assetPrecision))

previewAmount, err = m.chargeFixedFees(
baseBalance, quoteBalance, previewAmount, isBaseAsset, isBuy,
)
previewAmount, err := formula(args, amountDecimal)
if err != nil {
return nil, err
}

previewAsset := m.BaseAsset
previewAssetPrecision := math.Pow10(int(m.BaseAssetPrecision))
if isBaseAsset {
previewAsset = m.QuoteAsset
previewAssetPrecision = math.Pow10(int(m.QuoteAssetPrecision))
}

previewAmountInSats := previewAmount.Mul(
decimal.NewFromFloat(previewAssetPrecision),
).BigInt().Uint64()
previewAmountInSats, err = m.chargeFixedFees(
baseBalance, quoteBalance, previewAmountInSats, isBaseAsset, isBuy,
)
if err != nil {
return nil, err
}
if previewAmountInSats == 0 {
return nil, ErrMarketPreviewAmountTooLow
}

return &PreviewInfo{
Price: price,
Amount: previewAmount,
Amount: previewAmountInSats,
Asset: previewAsset,
}, nil
}
Expand All @@ -280,7 +296,7 @@ func (m *Market) SpotPrice(

func (m *Market) formula(
isBaseAsset, isBuy bool,
) func(interface{}, uint64) (uint64, error) {
) func(interface{}, decimal.Decimal) (decimal.Decimal, error) {
formula := m.getStrategySafe().Formula()
if isBuy {
if isBaseAsset {
Expand All @@ -298,11 +314,12 @@ func (m *Market) formula(
func (m *Market) formulaOptsForPluggable(
baseBalance, quoteBalance uint64, isBaseAsset, isBuy bool,
) interface{} {
balanceIn := baseBalance
balanceOut := quoteBalance
bp := uint64(math.Pow10(int(m.BaseAssetPrecision)))
qp := uint64(math.Pow10(int(m.QuoteAssetPrecision)))
balanceIn := mathutil.Div(baseBalance, bp)
balanceOut := mathutil.Div(quoteBalance, qp)
if isBuy {
balanceIn = quoteBalance
balanceOut = baseBalance
balanceIn, balanceOut = balanceOut, balanceIn
}

price := m.BaseAssetPrice()
Expand All @@ -321,11 +338,12 @@ func (m *Market) formulaOptsForPluggable(
func (m *Market) formulaOptsForBalanced(
baseBalance, quoteBalance uint64, isBaseAsset, isBuy bool,
) interface{} {
balanceIn := baseBalance
balanceOut := quoteBalance
bp := uint64(math.Pow10(int(m.BaseAssetPrecision)))
qp := uint64(math.Pow10(int(m.QuoteAssetPrecision)))
balanceIn := mathutil.Div(baseBalance, bp)
balanceOut := mathutil.Div(quoteBalance, qp)
if isBuy {
balanceIn = quoteBalance
balanceOut = baseBalance
balanceIn, balanceOut = balanceOut, balanceIn
}

return formula.BalancedReservesOpts{
Expand All @@ -347,24 +365,25 @@ func (m *Market) priceForStrategy(baseBalance, quoteBalance uint64) (Prices, err
func (m *Market) priceFromBalances(
baseBalance, quoteBalance uint64,
) (price Prices, err error) {
toIface := func(o formula.BalancedReservesOpts) interface{} {
return o
}

bp := uint64(math.Pow10(int(m.BaseAssetPrecision)))
qp := uint64(math.Pow10(int(m.QuoteAssetPrecision)))
balanceIn := mathutil.Div(quoteBalance, qp)
balanceOut := mathutil.Div(baseBalance, bp)
basePrice, err := m.getStrategySafe().Formula().SpotPrice(
toIface(formula.BalancedReservesOpts{
BalanceIn: quoteBalance,
BalanceOut: baseBalance,
}),
formula.BalancedReservesOpts{
BalanceIn: balanceIn,
BalanceOut: balanceOut,
},
)
if err != nil {
return
}
balanceIn, balanceOut = balanceOut, balanceIn
quotePrice, err := m.getStrategySafe().Formula().SpotPrice(
toIface(formula.BalancedReservesOpts{
BalanceIn: baseBalance,
BalanceOut: quoteBalance,
}),
formula.BalancedReservesOpts{
BalanceIn: balanceIn,
BalanceOut: balanceOut,
},
)
if err != nil {
return
Expand Down
Loading

0 comments on commit 3c82b18

Please sign in to comment.