mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 06:53:52 +00:00
Merge pull request #1411 from c9s/feature/dca2/new
FEATURE: new strategy dca2 perparation
This commit is contained in:
commit
64307af921
30
config/dca2.yaml
Normal file
30
config/dca2.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
backtest:
|
||||
startTime: "2023-06-01"
|
||||
endTime: "2023-07-01"
|
||||
sessions:
|
||||
- max
|
||||
symbols:
|
||||
- ETHUSDT
|
||||
accounts:
|
||||
binance:
|
||||
balances:
|
||||
USDT: 20_000.0
|
||||
|
||||
persistence:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
db: 0
|
||||
|
||||
exchangeStrategies:
|
||||
|
||||
- on: max
|
||||
dca2:
|
||||
symbol: ETHUSDT
|
||||
short: false
|
||||
budget: 5000
|
||||
maxOrderNum: 10
|
||||
priceDeviation: 1%
|
||||
takeProfitRatio: 1%
|
||||
coolDownInterval: 5m
|
|
@ -96,6 +96,9 @@ func (s *Stream) handleConnect() {
|
|||
case types.DepthLevelMedium:
|
||||
depth = 20
|
||||
|
||||
case types.DepthLevel1:
|
||||
depth = 1
|
||||
|
||||
case types.DepthLevel5:
|
||||
depth = 5
|
||||
|
||||
|
|
|
@ -69,9 +69,11 @@ func (s *Strategy) Initialize(ctx context.Context, environ *bbgo.Environment, se
|
|||
s.OrderExecutor.BindEnvironment(environ)
|
||||
s.OrderExecutor.BindProfitStats(s.ProfitStats)
|
||||
s.OrderExecutor.Bind()
|
||||
s.OrderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
||||
// bbgo.Sync(ctx, s)
|
||||
})
|
||||
/*
|
||||
s.OrderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
||||
bbgo.Sync(ctx, s)
|
||||
})
|
||||
*/
|
||||
|
||||
if !s.PositionHardLimit.IsZero() && !s.MaxPositionQuantity.IsZero() {
|
||||
log.Infof("positionHardLimit and maxPositionQuantity are configured, setting up PositionRiskControl...")
|
||||
|
|
19
pkg/strategy/dca2/debug.go
Normal file
19
pkg/strategy/dca2/debug.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package dca2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
func (s *Strategy) debugOrders(submitOrders []types.Order) {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("DCA ORDERS[\n")
|
||||
for i, order := range submitOrders {
|
||||
sb.WriteString(fmt.Sprintf("%3d) ", i+1) + order.String() + "\n")
|
||||
}
|
||||
sb.WriteString("] END OF DCA ORDERS")
|
||||
|
||||
s.logger.Info(sb.String())
|
||||
}
|
148
pkg/strategy/dca2/strategy.go
Normal file
148
pkg/strategy/dca2/strategy.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
package dca2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/strategy/common"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const ID = "dca2"
|
||||
|
||||
const orderTag = "dca2"
|
||||
|
||||
var log = logrus.WithField("strategy", ID)
|
||||
|
||||
func init() {
|
||||
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||
}
|
||||
|
||||
type Strategy struct {
|
||||
*common.Strategy
|
||||
|
||||
Environment *bbgo.Environment
|
||||
Market types.Market
|
||||
|
||||
Symbol string `json:"symbol"`
|
||||
|
||||
// setting
|
||||
Short bool `json:"short"`
|
||||
Budget fixedpoint.Value `json:"budget"`
|
||||
MaxOrderNum int64 `json:"maxOrderNum"`
|
||||
PriceDeviation fixedpoint.Value `json:"priceDeviation"`
|
||||
TakeProfitRatio fixedpoint.Value `json:"takeProfitRatio"`
|
||||
CoolDownInterval types.Duration `json:"coolDownInterval"`
|
||||
|
||||
// OrderGroupID is the group ID used for the strategy instance for canceling orders
|
||||
OrderGroupID uint32 `json:"orderGroupID"`
|
||||
|
||||
// log
|
||||
logger *logrus.Entry
|
||||
LogFields logrus.Fields `json:"logFields"`
|
||||
|
||||
// private field
|
||||
mu sync.Mutex
|
||||
makerSide types.SideType
|
||||
takeProfitSide types.SideType
|
||||
takeProfitPrice fixedpoint.Value
|
||||
startTimeOfNextRound time.Time
|
||||
}
|
||||
|
||||
func (s *Strategy) ID() string {
|
||||
return ID
|
||||
}
|
||||
|
||||
func (s *Strategy) Validate() error {
|
||||
if s.MaxOrderNum < 1 {
|
||||
return fmt.Errorf("maxOrderNum can not be < 1")
|
||||
}
|
||||
|
||||
if s.TakeProfitRatio.Sign() <= 0 {
|
||||
return fmt.Errorf("takeProfitSpread can not be <= 0")
|
||||
}
|
||||
|
||||
if s.PriceDeviation.Sign() <= 0 {
|
||||
return fmt.Errorf("margin can not be <= 0")
|
||||
}
|
||||
|
||||
// TODO: validate balance is enough
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Strategy) Defaults() error {
|
||||
if s.LogFields == nil {
|
||||
s.LogFields = logrus.Fields{}
|
||||
}
|
||||
|
||||
s.LogFields["symbol"] = s.Symbol
|
||||
s.LogFields["strategy"] = ID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Strategy) Initialize() error {
|
||||
s.logger = log.WithFields(s.LogFields)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Strategy) InstanceID() string {
|
||||
return fmt.Sprintf("%s-%s", ID, s.Symbol)
|
||||
}
|
||||
|
||||
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: types.Interval1m})
|
||||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
s.Strategy = &common.Strategy{}
|
||||
s.Strategy.Initialize(ctx, s.Environment, session, s.Market, ID, s.InstanceID())
|
||||
instanceID := s.InstanceID()
|
||||
|
||||
if s.Short {
|
||||
s.makerSide = types.SideTypeSell
|
||||
s.takeProfitSide = types.SideTypeBuy
|
||||
} else {
|
||||
s.makerSide = types.SideTypeBuy
|
||||
s.takeProfitSide = types.SideTypeSell
|
||||
}
|
||||
|
||||
if s.OrderGroupID == 0 {
|
||||
s.OrderGroupID = util.FNV32(instanceID) % math.MaxInt32
|
||||
}
|
||||
|
||||
// order executor
|
||||
s.OrderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
||||
s.logger.Infof("position: %s", s.Position.String())
|
||||
bbgo.Sync(ctx, s)
|
||||
|
||||
// update take profit price here
|
||||
})
|
||||
|
||||
session.MarketDataStream.OnKLine(func(kline types.KLine) {
|
||||
// check price here
|
||||
})
|
||||
|
||||
session.UserDataStream.OnAuth(func() {
|
||||
s.logger.Info("user data stream authenticated, start the process")
|
||||
// decide state here
|
||||
})
|
||||
|
||||
balances, err := session.Exchange.QueryAccountBalances(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
balance := balances[s.Market.QuoteCurrency]
|
||||
if balance.Available.Compare(s.Budget) < 0 {
|
||||
return fmt.Errorf("the available balance of %s is %s which is less than budget setting %s, please check it", s.Market.QuoteCurrency, balance.Available, s.Budget)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user