add max grid config and fix max price formatting

This commit is contained in:
c9s 2020-10-31 20:36:58 +08:00
parent 14abe3fb7e
commit 8e0b5d11a7
9 changed files with 108 additions and 18 deletions

54
config/grid-max.yaml Normal file
View File

@ -0,0 +1,54 @@
---
notifications:
slack:
defaultChannel: "dev-bbgo"
errorChannel: "bbgo-error"
# if you want to route channel by symbol
symbolChannels:
"^BTC": "btc"
"^ETH": "eth"
"^BNB": "bnb"
# object routing rules
routing:
trade: "$symbol"
order: "$symbol"
submitOrder: "$session" # not supported yet
pnL: "bbgo-pnl"
sessions:
binance:
exchange: binance
envVarPrefix: binance
max:
exchange: max
envVarPrefix: max
riskControls:
# This is the session-based risk controller, which let you configure different risk controller by session.
sessionBased:
# "max" is the session name that you want to configure the risk control
max:
# orderExecutors is one of the risk control
orderExecutors:
# symbol-routed order executor
bySymbol:
MAXUSDT:
# basic risk control order executor
basic:
minQuoteBalance: 1000.0
maxBaseAssetBalance: 5000.0
minBaseAssetBalance: 10.0
maxOrderAmount: 200.0
exchangeStrategies:
- on: max
grid:
symbol: MAXUSDT
interval: 1m
baseQuantity: 200.0
gridPips: 0.02
gridNumber: 2

View File

@ -22,11 +22,15 @@ sessions:
exchange: binance exchange: binance
envVarPrefix: binance envVarPrefix: binance
max:
exchange: max
envVarPrefix: max
riskControls: riskControls:
# This is the session-based risk controller, which let you configure different risk controller by session. # This is the session-based risk controller, which let you configure different risk controller by session.
sessionBased: sessionBased:
# "max" is the session name that you want to configure the risk control # "max" is the session name that you want to configure the risk control
binance: max:
# orderExecutors is one of the risk control # orderExecutors is one of the risk control
orderExecutors: orderExecutors:
# symbol-routed order executor # symbol-routed order executor

View File

@ -135,7 +135,7 @@ func (environ *Environment) Init(ctx context.Context) (err error) {
for interval := range types.SupportedIntervals { for interval := range types.SupportedIntervals {
kLines, err := session.Exchange.QueryKLines(ctx, symbol, interval.String(), types.KLineQueryOptions{ kLines, err := session.Exchange.QueryKLines(ctx, symbol, interval.String(), types.KLineQueryOptions{
EndTime: &now, EndTime: &now,
Limit: 100, Limit: 500, // indicators need at least 100
}) })
if err != nil { if err != nil {
return err return err

View File

@ -28,7 +28,6 @@ func (e *ExchangeOrderExecutionRouter) SubmitOrdersTo(ctx context.Context, sessi
return nil, err return nil, err
} }
// e.Notify(":memo: Submitting order to %s %s %s %s with quantity: %s", session, order.Symbol, order.Type, order.Side, order.QuantityString, order)
return es.Exchange.SubmitOrders(ctx, formattedOrders...) return es.Exchange.SubmitOrders(ctx, formattedOrders...)
} }
@ -44,9 +43,9 @@ func (e *ExchangeOrderExecutor) notifySubmitOrders(orders ...types.SubmitOrder)
// pass submit order as an interface object. // pass submit order as an interface object.
channel, ok := e.RouteObject(&order) channel, ok := e.RouteObject(&order)
if ok { if ok {
e.NotifyTo(channel, ":memo: Submitting %s %s %s order with quantity: %s", order.Symbol, order.Type, order.Side, order.QuantityString, &order) e.NotifyTo(channel, ":memo: Submitting %s %s %s order with quantity: %s at price: %s", order.Symbol, order.Type, order.Side, order.QuantityString, order.PriceString, &order)
} else { } else {
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 at price: %s", order.Symbol, order.Type, order.Side, order.QuantityString, order.PriceString, &order)
} }
} }
} }

View File

@ -362,26 +362,33 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol, interval string, opt
limit = options.Limit limit = options.Limit
} }
i, err := maxapi.ParseInterval(interval)
if err != nil {
return nil, err
}
// workaround for the kline query
if options.EndTime != nil && options.StartTime == nil {
startTime := options.EndTime.Add(- time.Duration(limit) * time.Minute * time.Duration(i))
options.StartTime = &startTime
}
if options.StartTime == nil { if options.StartTime == nil {
return nil, errors.New("start time can not be empty") return nil, errors.New("start time can not be empty")
} }
if options.EndTime != nil { log.Infof("querying kline %s %s %+v", symbol, interval, options)
return nil, errors.New("end time is not supported")
}
log.Infof("querying kline %s %s %v", symbol, interval, options)
// avoid rate limit // avoid rate limit
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
localKlines, err := e.client.PublicService.KLines(toLocalSymbol(symbol), interval, *options.StartTime, limit) localKLines, err := e.client.PublicService.KLines(toLocalSymbol(symbol), interval, *options.StartTime, limit)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var kLines []types.KLine var kLines []types.KLine
for _, k := range localKlines { for _, k := range localKLines {
kLines = append(kLines, k.KLine()) kLines = append(kLines, k.KLine())
} }

View File

@ -152,7 +152,7 @@ func mustParseTicker(v *fastjson.Value) Ticker {
type Interval int64 type Interval int64
func parseResolution(a string) (Interval, error) { func ParseInterval(a string) (Interval, error) {
switch strings.ToLower(a) { switch strings.ToLower(a) {
case "1m": case "1m":
@ -170,12 +170,21 @@ func parseResolution(a string) (Interval, error) {
case "1h": case "1h":
return 60, nil return 60, nil
case "2h":
return 60 * 2, nil
case "3h": case "3h":
return 60 * 3, nil return 60 * 3, nil
case "4h":
return 60 * 4, nil
case "6h": case "6h":
return 60 * 6, nil return 60 * 6, nil
case "8h":
return 60 * 8, nil
case "12h": case "12h":
return 60 * 12, nil return 60 * 12, nil
@ -190,7 +199,7 @@ func parseResolution(a string) (Interval, error) {
} }
return 0, errors.New("incorrect resolution") return 0, errors.Errorf("incorrect resolution: %q", a)
} }
type KLine struct { type KLine struct {
@ -224,7 +233,7 @@ func (s *PublicService) KLines(symbol string, resolution string, start time.Time
queries := url.Values{} queries := url.Values{}
queries.Set("market", symbol) queries.Set("market", symbol)
interval, err := parseResolution(resolution) interval, err := ParseInterval(resolution)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -232,7 +241,7 @@ func (s *PublicService) KLines(symbol string, resolution string, start time.Time
nilTime := time.Time{} nilTime := time.Time{}
if start != nilTime { if start != nilTime {
queries.Set("timestamp", strconv.FormatInt(start.Unix(), 64)) queries.Set("timestamp", strconv.FormatInt(start.Unix(), 10))
} }
if limit > 0 { if limit > 0 {

View File

@ -39,10 +39,18 @@ type BOLL struct {
} }
func (inc *BOLL) LastUpBand() float64 { func (inc *BOLL) LastUpBand() float64 {
if len(inc.UpBand) == 0 {
return 0.0
}
return inc.UpBand[len(inc.UpBand)-1] return inc.UpBand[len(inc.UpBand)-1]
} }
func (inc *BOLL) LastDownBand() float64 { func (inc *BOLL) LastDownBand() float64 {
if len(inc.DownBand) == 0 {
return 0.0
}
return inc.DownBand[len(inc.DownBand)-1] return inc.DownBand[len(inc.DownBand)-1]
} }
@ -85,6 +93,8 @@ func (inc *BOLL) calculateAndUpdate(kLines []types.KLine) {
// update end time // update end time
inc.EndTime = kLines[index].EndTime inc.EndTime = kLines[index].EndTime
// log.Infof("update boll: sma=%f, up=%f, down=%f", sma, upBand, downBand)
inc.EmitUpdate(sma, upBand, downBand) inc.EmitUpdate(sma, upBand, downBand)
} }

View File

@ -89,6 +89,10 @@ func (s *Strategy) updateBidOrders(orderExecutor bbgo.OrderExecutor, session *bb
} }
var downBand = s.boll.LastDownBand() var downBand = s.boll.LastDownBand()
if downBand <= 0.0 {
return
}
var startPrice = downBand var startPrice = downBand
var submitOrders []types.SubmitOrder var submitOrders []types.SubmitOrder
@ -130,6 +134,10 @@ func (s *Strategy) updateAskOrders(orderExecutor bbgo.OrderExecutor, session *bb
} }
var upBand = s.boll.LastUpBand() var upBand = s.boll.LastUpBand()
if upBand <= 0.0 {
return
}
var startPrice = upBand var startPrice = upBand
var submitOrders []types.SubmitOrder var submitOrders []types.SubmitOrder

View File

@ -45,11 +45,10 @@ 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))) prec := int(math.Abs(math.Log10(m.MinPrice)))
p := math.Pow10(prec) p := math.Pow10(prec)
val = math.Trunc(val*p) / p val = math.Trunc(val*p) / p
return strconv.FormatFloat(val, 'f', m.PricePrecision, 64) return strconv.FormatFloat(val, 'f', prec, 64)
} }
func (m Market) FormatQuantity(val float64) string { func (m Market) FormatQuantity(val float64) string {