mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +00:00
convert: move moq check/adjustment to types.Market
This commit is contained in:
parent
ce8063654d
commit
8b6a8aeb7b
|
@ -162,7 +162,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
|
||||
bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) {
|
||||
s.collectPendingQuantity(ctx)
|
||||
|
||||
|
||||
_ = s.orderExecutor.GracefulCancel(ctx)
|
||||
})
|
||||
|
||||
|
@ -327,27 +327,16 @@ func (s *Strategy) convertBalance(ctx context.Context, fromAsset string, availab
|
|||
switch fromAsset {
|
||||
|
||||
case market.BaseCurrency:
|
||||
log.Infof("converting %s %s to %s...", available, fromAsset, market.QuoteCurrency)
|
||||
|
||||
available = market.TruncateQuantity(available)
|
||||
|
||||
// from = Base -> action = sell
|
||||
if available.Compare(market.MinQuantity) < 0 {
|
||||
log.Debugf("asset %s %s is less than minQuantity %s, skip convert", available, fromAsset, market.MinQuantity)
|
||||
return nil
|
||||
}
|
||||
|
||||
price := ticker.Sell
|
||||
if s.UseTakerOrder {
|
||||
price = ticker.Buy
|
||||
}
|
||||
|
||||
quoteAmount := price.Mul(available)
|
||||
if quoteAmount.Compare(market.MinNotional) < 0 {
|
||||
log.Debugf("asset %s %s (%s %s) is less than minNotional %s, skip convert",
|
||||
available, fromAsset,
|
||||
quoteAmount, market.QuoteCurrency,
|
||||
market.MinNotional)
|
||||
log.Infof("converting %s %s to %s...", available, fromAsset, market.QuoteCurrency)
|
||||
|
||||
quantity, ok := market.GreaterThanMinimalOrderQuantity(types.SideTypeSell, price, available)
|
||||
if !ok {
|
||||
log.Debugf("asset %s %s is less than MoQ, skip convert", available, fromAsset)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -355,7 +344,7 @@ func (s *Strategy) convertBalance(ctx context.Context, fromAsset string, availab
|
|||
Symbol: market.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeLimit,
|
||||
Quantity: available,
|
||||
Quantity: quantity,
|
||||
Price: price,
|
||||
Market: market,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
|
@ -365,36 +354,16 @@ func (s *Strategy) convertBalance(ctx context.Context, fromAsset string, availab
|
|||
}
|
||||
|
||||
case market.QuoteCurrency:
|
||||
log.Infof("converting %s %s to %s...", available, fromAsset, market.BaseCurrency)
|
||||
|
||||
available = market.TruncateQuoteQuantity(available)
|
||||
|
||||
// from = Quote -> action = buy
|
||||
if available.Compare(market.MinNotional) < 0 {
|
||||
log.Debugf("asset %s %s is less than minNotional %s, skip convert", available, fromAsset, market.MinNotional)
|
||||
return nil
|
||||
}
|
||||
|
||||
price := ticker.Buy
|
||||
if s.UseTakerOrder {
|
||||
price = ticker.Sell
|
||||
}
|
||||
|
||||
quantity := available.Div(price)
|
||||
quantity = market.TruncateQuantity(quantity)
|
||||
if quantity.Compare(market.MinQuantity) < 0 {
|
||||
log.Debugf("asset %s %s is less than minQuantity %s, skip convert",
|
||||
quantity, fromAsset,
|
||||
market.MinQuantity)
|
||||
return nil
|
||||
}
|
||||
log.Infof("converting %s %s to %s...", available, fromAsset, market.BaseCurrency)
|
||||
|
||||
notional := quantity.Mul(price)
|
||||
if notional.Compare(market.MinNotional) < 0 {
|
||||
log.Debugf("asset %s %s (%s %s) is less than minNotional %s, skip convert",
|
||||
quantity, fromAsset,
|
||||
notional, market.QuoteCurrency,
|
||||
market.MinNotional)
|
||||
quantity, ok := market.GreaterThanMinimalOrderQuantity(types.SideTypeBuy, price, available)
|
||||
if !ok {
|
||||
log.Debugf("asset %s %s is less than MoQ, skip convert", available, fromAsset)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ func (m Market) TruncateQuantity(quantity fixedpoint.Value) fixedpoint.Value {
|
|||
}
|
||||
|
||||
// TruncateQuoteQuantity uses the tick size to truncate floating number, in order to avoid the rounding issue
|
||||
// this is usually used for calculating the order size from the quote quantity.
|
||||
func (m Market) TruncateQuoteQuantity(quantity fixedpoint.Value) fixedpoint.Value {
|
||||
var ts = m.TickSize.Float64()
|
||||
var prec = int(math.Round(math.Log10(ts) * -1.0))
|
||||
|
@ -84,6 +85,51 @@ func (m Market) TruncateQuoteQuantity(quantity fixedpoint.Value) fixedpoint.Valu
|
|||
return fixedpoint.MustNewFromString(qs)
|
||||
}
|
||||
|
||||
// GreaterThanMinimalOrderQuantity ensures that your given balance could fit the minimal order quantity
|
||||
// when side = sell, then available = base balance
|
||||
// when side = buy, then available = quote balance
|
||||
// The balance will be truncated first in order to calculate the minimal notional and minimal quantity
|
||||
// The adjusted (truncated) order quantity will be returned
|
||||
func (m Market) GreaterThanMinimalOrderQuantity(side SideType, price, available fixedpoint.Value) (fixedpoint.Value, bool) {
|
||||
switch side {
|
||||
case SideTypeSell:
|
||||
available = m.TruncateQuantity(available)
|
||||
|
||||
if available.Compare(m.MinQuantity) < 0 {
|
||||
return fixedpoint.Zero, false
|
||||
}
|
||||
|
||||
quoteAmount := price.Mul(available)
|
||||
if quoteAmount.Compare(m.MinNotional) < 0 {
|
||||
return fixedpoint.Zero, false
|
||||
}
|
||||
|
||||
return available, true
|
||||
|
||||
case SideTypeBuy:
|
||||
available = m.TruncateQuoteQuantity(available)
|
||||
|
||||
if available.Compare(m.MinNotional) < 0 {
|
||||
return fixedpoint.Zero, false
|
||||
}
|
||||
|
||||
quantity := available.Div(price)
|
||||
quantity = m.TruncateQuantity(quantity)
|
||||
if quantity.Compare(m.MinQuantity) < 0 {
|
||||
return fixedpoint.Zero, false
|
||||
}
|
||||
|
||||
notional := quantity.Mul(price)
|
||||
if notional.Compare(m.MinNotional) < 0 {
|
||||
return fixedpoint.Zero, false
|
||||
}
|
||||
|
||||
return quantity, true
|
||||
}
|
||||
|
||||
return available, true
|
||||
}
|
||||
|
||||
// RoundDownQuantityByPrecision uses the volume precision to round down the quantity
|
||||
// This is different from the TruncateQuantity, which uses StepSize (it uses fewer fractions to truncate)
|
||||
func (m Market) RoundDownQuantityByPrecision(quantity fixedpoint.Value) fixedpoint.Value {
|
||||
|
|
|
@ -13,6 +13,31 @@ import (
|
|||
|
||||
var s func(string) fixedpoint.Value = fixedpoint.MustNewFromString
|
||||
|
||||
func TestMarket_GreaterThanMinimalOrderQuantity(t *testing.T) {
|
||||
market := Market{
|
||||
Symbol: "BTCUSDT",
|
||||
LocalSymbol: "BTCUSDT",
|
||||
PricePrecision: 8,
|
||||
VolumePrecision: 8,
|
||||
QuoteCurrency: "USDT",
|
||||
BaseCurrency: "BTC",
|
||||
MinNotional: number(10.0),
|
||||
MinAmount: number(10.0),
|
||||
MinQuantity: number(0.0001),
|
||||
StepSize: number(0.00001),
|
||||
TickSize: number(0.01),
|
||||
}
|
||||
|
||||
_, ok := market.GreaterThanMinimalOrderQuantity(SideTypeSell, number(20000.0), number(0.00051))
|
||||
assert.True(t, ok)
|
||||
|
||||
_, ok = market.GreaterThanMinimalOrderQuantity(SideTypeBuy, number(20000.0), number(10.0))
|
||||
assert.True(t, ok)
|
||||
|
||||
_, ok = market.GreaterThanMinimalOrderQuantity(SideTypeBuy, number(20000.0), number(0.99999))
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestFormatQuantity(t *testing.T) {
|
||||
quantity := formatQuantity(
|
||||
s("0.12511"),
|
||||
|
|
Loading…
Reference in New Issue
Block a user