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 // for setup mode, we don't start the trader
trader.Subscribe()
if err := trader.Run(ctx); err != nil { if err := trader.Run(ctx); err != nil {
log.WithError(err).Error("failed to start trader") log.WithError(err).Error("failed to start trader")
return 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 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/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-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/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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", BaseCurrency: "BTC",
MinNotional: 0.001, MinNotional: 0.001,
MinAmount: 10.0, MinAmount: 10.0,
MinLot: 0.001,
MinQuantity: 0.001, MinQuantity: 0.001,
} }

View File

@ -6,7 +6,7 @@ import (
"math" "math"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types" "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) { func (e *ExchangeOrderExecutionRouter) SubmitOrdersTo(ctx context.Context, session string, orders ...types.SubmitOrder) (types.OrderSlice, error) {
es, ok := e.sessions[session] es, ok := e.sessions[session]
if !ok { 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) 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) 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...) e.notifySubmitOrders(formattedOrders...)
@ -94,7 +94,7 @@ func (e *ExchangeOrderExecutor) SubmitOrders(ctx context.Context, orders ...type
} }
type BasicRiskController struct { type BasicRiskController struct {
Logger *logrus.Logger Logger *log.Logger
MaxOrderAmount fixedpoint.Value `json:"maxOrderAmount,omitempty"` MaxOrderAmount fixedpoint.Value `json:"maxOrderAmount,omitempty"`
MinQuoteBalance fixedpoint.Value `json:"minQuoteBalance,omitempty"` MinQuoteBalance fixedpoint.Value `json:"minQuoteBalance,omitempty"`
@ -258,12 +258,12 @@ func (c *BasicRiskController) ProcessOrders(session *ExchangeSession, orders ...
continue continue
} }
if quantity < market.MinLot { if quantity < market.MinQuantity {
addError( addError(
fmt.Errorf( fmt.Errorf(
"can not place sell order, quantity %f is less than the minimal lot %f, order: %s", "can not place sell order, quantity %f is less than the minimal lot %f, order: %s",
quantity, quantity,
market.MinLot, market.MinQuantity,
order.String())) order.String()))
continue continue
} }

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import (
_ "github.com/c9s/bbgo/pkg/strategy/grid" _ "github.com/c9s/bbgo/pkg/strategy/grid"
_ "github.com/c9s/bbgo/pkg/strategy/mirrormaker" _ "github.com/c9s/bbgo/pkg/strategy/mirrormaker"
_ "github.com/c9s/bbgo/pkg/strategy/pricealert" _ "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/swing"
_ "github.com/c9s/bbgo/pkg/strategy/trailingstop" _ "github.com/c9s/bbgo/pkg/strategy/trailingstop"
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker" _ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"

View File

@ -267,8 +267,6 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer
return err return err
} }
trader.Subscribe()
if err := trader.Run(ctx); err != nil { if err := trader.Run(ctx); err != nil {
return err 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. // maxQty defines the maximum quantity/icebergQty allowed.
// stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by. // stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by.
if f := symbol.LotSizeFilter(); f != nil { if f := symbol.LotSizeFilter(); f != nil {
market.MinLot = util.MustParseFloat(f.MinQuantity)
market.MinQuantity = util.MustParseFloat(f.MinQuantity) market.MinQuantity = util.MustParseFloat(f.MinQuantity)
market.MaxQuantity = util.MustParseFloat(f.MaxQuantity) market.MaxQuantity = util.MustParseFloat(f.MaxQuantity)
// market.StepSize = util.MustParseFloat(f.StepSize) market.StepSize = util.MustParseFloat(f.StepSize)
} }
if f := symbol.PriceFilter(); f != nil { if f := symbol.PriceFilter(); f != nil {
@ -438,8 +437,11 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
req := e.Client.NewCreateMarginOrderService(). req := e.Client.NewCreateMarginOrderService().
Symbol(order.Symbol). Symbol(order.Symbol).
Type(orderType). Type(orderType).
Side(binance.SideType(order.Side)). Side(binance.SideType(order.Side))
NewClientOrderID(clientOrderID)
if len(clientOrderID) > 0 {
req.NewClientOrderID(clientOrderID)
}
// use response result format // use response result format
req.NewOrderRespType(binance.NewOrderRespTypeRESULT) 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)) req.Quantity(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
} }
if len(order.PriceString) > 0 { // set price field for limit orders
req.Price(order.PriceString) switch order.Type {
} else if order.Market.Symbol != "" { case types.OrderTypeStopLimit, types.OrderTypeLimit:
req.Price(order.Market.FormatPrice(order.Price)) if len(order.PriceString) > 0 {
} else { req.Price(order.PriceString)
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64)) } else if order.Market.Symbol != "" {
req.Price(order.Market.FormatPrice(order.Price))
}
} }
// set stop price
switch order.Type { switch order.Type {
case types.OrderTypeStopLimit, types.OrderTypeStopMarket: case types.OrderTypeStopLimit, types.OrderTypeStopMarket:
if len(order.StopPriceString) == 0 { if len(order.StopPriceString) == 0 {
return nil, fmt.Errorf("stop price string can not be empty") 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 return
default: 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()) 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), BaseCurrency: toGlobalCurrency(m.BaseUnit),
MinNotional: m.MinQuoteAmount, MinNotional: m.MinQuoteAmount,
MinAmount: m.MinQuoteAmount, MinAmount: m.MinQuoteAmount,
MinLot: 1.0 / math.Pow10(m.BaseUnitPrecision), // make it like 0.0001
MinQuantity: m.MinBaseAmount, MinQuantity: m.MinBaseAmount,
MaxQuantity: 10000.0, MaxQuantity: 10000.0,
MinPrice: 1.0 / math.Pow10(m.QuoteUnitPrecision), // used in the price formatter StepSize: 1.0 / math.Pow10(m.BaseUnitPrecision), // make it like 0.0001
MaxPrice: 10000.0,
TickSize: 1.0 / math.Pow10(m.QuoteUnitPrecision), 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 markets[symbol] = market

View File

@ -53,14 +53,15 @@ type Market struct {
QuoteCurrency string QuoteCurrency string
BaseCurrency 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 MinNotional float64
MinAmount float64 MinAmount float64
// The LOT_SIZE filter defines the quantity // The LOT_SIZE filter defines the quantity
MinLot float64
MinQuantity float64 MinQuantity float64
MaxQuantity float64 MaxQuantity float64
StepSize float64
MinPrice float64 MinPrice float64
MaxPrice float64 MaxPrice float64
@ -86,17 +87,25 @@ func (m Market) FormatPriceCurrency(val float64) string {
func (m Market) FormatPrice(val float64) string { func (m Market) FormatPrice(val float64) string {
// p := math.Pow10(m.PricePrecision) // 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) p := math.Pow10(prec)
val = math.Trunc(val*p) / p price = math.Trunc(price*p) / p
return strconv.FormatFloat(val, 'f', prec, 64) return strconv.FormatFloat(price, 'f', prec, 64)
} }
func (m Market) FormatQuantity(val float64) string { 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) p := math.Pow10(prec)
val = math.Trunc(val*p) / p quantity = math.Trunc(quantity*p) / p
return strconv.FormatFloat(val, 'f', prec, 64) return strconv.FormatFloat(quantity, 'f', prec, 64)
} }
func (m Market) FormatVolume(val float64) string { func (m Market) FormatVolume(val float64) string {

View File

@ -8,6 +8,22 @@ import (
"github.com/stretchr/testify/assert" "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) { func TestDurationParse(t *testing.T) {
type A struct { type A struct {
Duration Duration `json:"duration"` Duration Duration `json:"duration"`