mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
add max grid config and fix max price formatting
This commit is contained in:
parent
14abe3fb7e
commit
8e0b5d11a7
54
config/grid-max.yaml
Normal file
54
config/grid-max.yaml
Normal 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user