fix quantity format

This commit is contained in:
c9s 2021-02-11 00:21:56 +08:00
parent 88411a134b
commit ffa001fc29
13 changed files with 67 additions and 38 deletions

View File

@ -114,7 +114,6 @@ func main() {
}
// for setup mode, we don't start the trader
trader.Subscribe()
if err := trader.Run(ctx); err != nil {
log.WithError(err).Error("failed to start trader")
return

1
go.sum
View File

@ -140,7 +140,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=

View File

@ -40,7 +40,6 @@ func TestSimplePriceMatching_LimitOrder(t *testing.T) {
BaseCurrency: "BTC",
MinNotional: 0.001,
MinAmount: 10.0,
MinLot: 0.001,
MinQuantity: 0.001,
}

View File

@ -6,7 +6,7 @@ import (
"math"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
@ -33,7 +33,7 @@ type ExchangeOrderExecutionRouter struct {
func (e *ExchangeOrderExecutionRouter) SubmitOrdersTo(ctx context.Context, session string, orders ...types.SubmitOrder) (types.OrderSlice, error) {
es, ok := e.sessions[session]
if !ok {
return nil, fmt.Errorf("exchange Session %s not found", session)
return nil, fmt.Errorf("exchange session %s not found", session)
}
formattedOrders, err := formatOrders(es, orders)
@ -85,7 +85,7 @@ func (e *ExchangeOrderExecutor) SubmitOrders(ctx context.Context, orders ...type
e.Notify(":memo: Submitting %s %s %s order with quantity: %s", order.Symbol, order.Type, order.Side, order.QuantityString, order)
}
logrus.Infof("submitting order: %s", order.String())
log.Infof("submitting order: %s", order.String())
}
e.notifySubmitOrders(formattedOrders...)
@ -94,7 +94,7 @@ func (e *ExchangeOrderExecutor) SubmitOrders(ctx context.Context, orders ...type
}
type BasicRiskController struct {
Logger *logrus.Logger
Logger *log.Logger
MaxOrderAmount fixedpoint.Value `json:"maxOrderAmount,omitempty"`
MinQuoteBalance fixedpoint.Value `json:"minQuoteBalance,omitempty"`
@ -258,12 +258,12 @@ func (c *BasicRiskController) ProcessOrders(session *ExchangeSession, orders ...
continue
}
if quantity < market.MinLot {
if quantity < market.MinQuantity {
addError(
fmt.Errorf(
"can not place sell order, quantity %f is less than the minimal lot %f, order: %s",
quantity,
market.MinLot,
market.MinQuantity,
order.String()))
continue
}

View File

@ -148,6 +148,8 @@ func (trader *Trader) Subscribe() {
}
func (trader *Trader) Run(ctx context.Context) error {
trader.Subscribe()
if err := trader.environment.Init(ctx); err != nil {
return err
}

View File

@ -214,8 +214,6 @@ var BacktestCmd = &cobra.Command{
log.Warnf("backtest does not support CrossExchangeStrategy, strategies won't be added.")
}
trader.Subscribe()
if err := trader.Run(ctx); err != nil {
return err
}

View File

@ -8,6 +8,7 @@ import (
_ "github.com/c9s/bbgo/pkg/strategy/grid"
_ "github.com/c9s/bbgo/pkg/strategy/mirrormaker"
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
_ "github.com/c9s/bbgo/pkg/strategy/sat"
_ "github.com/c9s/bbgo/pkg/strategy/swing"
_ "github.com/c9s/bbgo/pkg/strategy/trailingstop"
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"

View File

@ -267,8 +267,6 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer
return err
}
trader.Subscribe()
if err := trader.Run(ctx); err != nil {
return err
}

View File

@ -119,10 +119,9 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
// maxQty defines the maximum quantity/icebergQty allowed.
// stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by.
if f := symbol.LotSizeFilter(); f != nil {
market.MinLot = util.MustParseFloat(f.MinQuantity)
market.MinQuantity = util.MustParseFloat(f.MinQuantity)
market.MaxQuantity = util.MustParseFloat(f.MaxQuantity)
// market.StepSize = util.MustParseFloat(f.StepSize)
market.StepSize = util.MustParseFloat(f.StepSize)
}
if f := symbol.PriceFilter(); f != nil {
@ -438,8 +437,11 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
req := e.Client.NewCreateMarginOrderService().
Symbol(order.Symbol).
Type(orderType).
Side(binance.SideType(order.Side)).
NewClientOrderID(clientOrderID)
Side(binance.SideType(order.Side))
if len(clientOrderID) > 0 {
req.NewClientOrderID(clientOrderID)
}
// use response result format
req.NewOrderRespType(binance.NewOrderRespTypeRESULT)
@ -460,15 +462,19 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
req.Quantity(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
}
if len(order.PriceString) > 0 {
req.Price(order.PriceString)
} else if order.Market.Symbol != "" {
req.Price(order.Market.FormatPrice(order.Price))
} else {
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
// set price field for limit orders
switch order.Type {
case types.OrderTypeStopLimit, types.OrderTypeLimit:
if len(order.PriceString) > 0 {
req.Price(order.PriceString)
} else if order.Market.Symbol != "" {
req.Price(order.Market.FormatPrice(order.Price))
}
}
// set stop price
switch order.Type {
case types.OrderTypeStopLimit, types.OrderTypeStopMarket:
if len(order.StopPriceString) == 0 {
return nil, fmt.Errorf("stop price string can not be empty")

View File

@ -351,7 +351,7 @@ func (s *Stream) read(ctx context.Context) {
return
default:
if err := s.Conn.SetReadDeadline(time.Now().Add(1 * time.Minute)); err != nil {
if err := s.Conn.SetReadDeadline(time.Now().Add(3 * time.Minute)); err != nil {
log.WithError(err).Errorf("set read deadline error: %s", err.Error())
}

View File

@ -113,12 +113,14 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
BaseCurrency: toGlobalCurrency(m.BaseUnit),
MinNotional: m.MinQuoteAmount,
MinAmount: m.MinQuoteAmount,
MinLot: 1.0 / math.Pow10(m.BaseUnitPrecision), // make it like 0.0001
MinQuantity: m.MinBaseAmount,
MaxQuantity: 10000.0,
MinPrice: 1.0 / math.Pow10(m.QuoteUnitPrecision), // used in the price formatter
MaxPrice: 10000.0,
TickSize: 1.0 / math.Pow10(m.QuoteUnitPrecision),
MinQuantity: m.MinBaseAmount,
MaxQuantity: 10000.0,
StepSize: 1.0 / math.Pow10(m.BaseUnitPrecision), // make it like 0.0001
MinPrice: 1.0 / math.Pow10(m.QuoteUnitPrecision), // used in the price formatter
MaxPrice: 10000.0,
TickSize: 1.0 / math.Pow10(m.QuoteUnitPrecision),
}
markets[symbol] = market

View File

@ -53,14 +53,15 @@ type Market struct {
QuoteCurrency string
BaseCurrency string
// The MIN_NOTIONAL filter defines the minimum notional value allowed for an order on a symbol. An order's notional value is the price * quantity
// The MIN_NOTIONAL filter defines the minimum notional value allowed for an order on a symbol.
// An order's notional value is the price * quantity
MinNotional float64
MinAmount float64
// The LOT_SIZE filter defines the quantity
MinLot float64
MinQuantity float64
MaxQuantity float64
StepSize float64
MinPrice float64
MaxPrice float64
@ -86,17 +87,25 @@ func (m Market) FormatPriceCurrency(val float64) string {
func (m Market) FormatPrice(val float64) string {
// p := math.Pow10(m.PricePrecision)
prec := int(math.Abs(math.Log10(m.MinPrice)))
return formatPrice(val, m.TickSize)
}
func formatPrice(price float64, tickSize float64) string {
prec := int(math.Round(math.Abs(math.Log10(tickSize))))
p := math.Pow10(prec)
val = math.Trunc(val*p) / p
return strconv.FormatFloat(val, 'f', prec, 64)
price = math.Trunc(price*p) / p
return strconv.FormatFloat(price, 'f', prec, 64)
}
func (m Market) FormatQuantity(val float64) string {
prec := int(math.Abs(math.Log10(m.MinLot)))
return formatQuantity(val, m.StepSize)
}
func formatQuantity(quantity float64, lot float64) string {
prec := int(math.Round(math.Abs(math.Log10(lot))))
p := math.Pow10(prec)
val = math.Trunc(val*p) / p
return strconv.FormatFloat(val, 'f', prec, 64)
quantity = math.Trunc(quantity*p) / p
return strconv.FormatFloat(quantity, 'f', prec, 64)
}
func (m Market) FormatVolume(val float64) string {

View File

@ -8,6 +8,22 @@ import (
"github.com/stretchr/testify/assert"
)
func TestFormatQuantity(t *testing.T) {
quantity := formatQuantity(0.12511, 0.01)
assert.Equal(t, "0.12", quantity)
quantity = formatQuantity(0.12511, 0.001)
assert.Equal(t, "0.125", quantity)
}
func TestFormatPrice(t *testing.T) {
price := formatPrice(26.288256, 0.0001)
assert.Equal(t, "26.2882", price)
price = formatPrice(26.288656, 0.001)
assert.Equal(t, "26.288", price)
}
func TestDurationParse(t *testing.T) {
type A struct {
Duration Duration `json:"duration"`