mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
fix quantity format
This commit is contained in:
parent
88411a134b
commit
ffa001fc29
|
@ -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
1
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set price field for limit orders
|
||||||
|
switch order.Type {
|
||||||
|
case types.OrderTypeStopLimit, types.OrderTypeLimit:
|
||||||
if len(order.PriceString) > 0 {
|
if len(order.PriceString) > 0 {
|
||||||
req.Price(order.PriceString)
|
req.Price(order.PriceString)
|
||||||
} else if order.Market.Symbol != "" {
|
} else if order.Market.Symbol != "" {
|
||||||
req.Price(order.Market.FormatPrice(order.Price))
|
req.Price(order.Market.FormatPrice(order.Price))
|
||||||
} else {
|
}
|
||||||
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,9 +113,11 @@ 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,
|
||||||
|
StepSize: 1.0 / math.Pow10(m.BaseUnitPrecision), // make it like 0.0001
|
||||||
|
|
||||||
MinPrice: 1.0 / math.Pow10(m.QuoteUnitPrecision), // used in the price formatter
|
MinPrice: 1.0 / math.Pow10(m.QuoteUnitPrecision), // used in the price formatter
|
||||||
MaxPrice: 10000.0,
|
MaxPrice: 10000.0,
|
||||||
TickSize: 1.0 / math.Pow10(m.QuoteUnitPrecision),
|
TickSize: 1.0 / math.Pow10(m.QuoteUnitPrecision),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
Loading…
Reference in New Issue
Block a user