mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
Merge pull request #1629 from c9s/kbearXD/dca2/refactor
REFACTOR: [dca2] refactor dca2 strategy to make it can back testing
This commit is contained in:
commit
e86decea6e
|
@ -1,7 +0,0 @@
|
||||||
package dca2
|
|
||||||
|
|
||||||
// DevMode, if Enabled is true. it means it will check the running field
|
|
||||||
type DevMode struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
IsNewAccount bool `json:"isNewAccount"`
|
|
||||||
}
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
|
||||||
"github.com/c9s/bbgo/pkg/exchange/retry"
|
"github.com/c9s/bbgo/pkg/exchange/retry"
|
||||||
"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,21 +32,6 @@ func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error {
|
||||||
|
|
||||||
s.debugOrders(createdOrders)
|
s.debugOrders(createdOrders)
|
||||||
|
|
||||||
if s.DevMode != nil && s.DevMode.Enabled && s.DevMode.IsNewAccount {
|
|
||||||
if len(createdOrders) > 0 {
|
|
||||||
s.ProfitStats.FromOrderID = createdOrders[0].OrderID
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, createdOrder := range createdOrders {
|
|
||||||
if s.ProfitStats.FromOrderID > createdOrder.OrderID {
|
|
||||||
s.ProfitStats.FromOrderID = createdOrder.OrderID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.DevMode.IsNewAccount = false
|
|
||||||
bbgo.Sync(ctx, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (s *Strategy) emitNextState(nextState State) {
|
||||||
// TakeProfitReady -> the takeProfit order filled ->
|
// TakeProfitReady -> the takeProfit order filled ->
|
||||||
func (s *Strategy) runState(ctx context.Context) {
|
func (s *Strategy) runState(ctx context.Context) {
|
||||||
s.logger.Info("[DCA] runState")
|
s.logger.Info("[DCA] runState")
|
||||||
stateTriggerTicker := time.NewTicker(5 * time.Second)
|
stateTriggerTicker := time.NewTicker(1 * time.Minute)
|
||||||
defer stateTriggerTicker.Stop()
|
defer stateTriggerTicker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -83,7 +83,7 @@ func (s *Strategy) runState(ctx context.Context) {
|
||||||
s.logger.Info("[DCA] runState DONE")
|
s.logger.Info("[DCA] runState DONE")
|
||||||
return
|
return
|
||||||
case <-stateTriggerTicker.C:
|
case <-stateTriggerTicker.C:
|
||||||
// s.logger.Infof("[DCA] triggerNextState current state: %d", s.state)
|
// move triggerNextState to the end of next state handler, this ticker is used to avoid the state is stopped unexpectedly
|
||||||
s.triggerNextState()
|
s.triggerNextState()
|
||||||
case nextState := <-s.nextStateC:
|
case nextState := <-s.nextStateC:
|
||||||
// next state == current state -> skip
|
// next state == current state -> skip
|
||||||
|
@ -120,6 +120,8 @@ func (s *Strategy) runState(ctx context.Context) {
|
||||||
case TakeProfitReady:
|
case TakeProfitReady:
|
||||||
s.runTakeProfitReady(ctx, nextState)
|
s.runTakeProfitReady(ctx, nextState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.triggerNextState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,9 +180,6 @@ func (s *Strategy) runOpenPositionReady(_ context.Context, next State) {
|
||||||
func (s *Strategy) runOpenPositionOrderFilled(_ context.Context, next State) {
|
func (s *Strategy) runOpenPositionOrderFilled(_ context.Context, next State) {
|
||||||
s.updateState(OpenPositionOrdersCancelling)
|
s.updateState(OpenPositionOrdersCancelling)
|
||||||
s.logger.Info("[State] OpenPositionOrderFilled -> OpenPositionOrdersCancelling")
|
s.logger.Info("[State] OpenPositionOrderFilled -> OpenPositionOrdersCancelling")
|
||||||
|
|
||||||
// after open position cancelling, immediately trigger open position cancelled to cancel the other orders
|
|
||||||
s.emitNextState(OpenPositionOrdersCancelled)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) runOpenPositionOrdersCancelling(ctx context.Context, next State) {
|
func (s *Strategy) runOpenPositionOrdersCancelling(ctx context.Context, next State) {
|
||||||
|
@ -191,9 +190,6 @@ func (s *Strategy) runOpenPositionOrdersCancelling(ctx context.Context, next Sta
|
||||||
}
|
}
|
||||||
s.updateState(OpenPositionOrdersCancelled)
|
s.updateState(OpenPositionOrdersCancelled)
|
||||||
s.logger.Info("[State] OpenPositionOrdersCancelling -> OpenPositionOrdersCancelled")
|
s.logger.Info("[State] OpenPositionOrdersCancelling -> OpenPositionOrdersCancelled")
|
||||||
|
|
||||||
// after open position cancelled, immediately trigger take profit ready to open take-profit order
|
|
||||||
s.emitNextState(TakeProfitReady)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) runOpenPositionOrdersCancelled(ctx context.Context, next State) {
|
func (s *Strategy) runOpenPositionOrdersCancelled(ctx context.Context, next State) {
|
||||||
|
|
|
@ -79,9 +79,6 @@ type Strategy struct {
|
||||||
// UseCancelAllOrdersApiWhenClose uses a different API to cancel all the orders on the market when closing a grid
|
// UseCancelAllOrdersApiWhenClose uses a different API to cancel all the orders on the market when closing a grid
|
||||||
UseCancelAllOrdersApiWhenClose bool `json:"useCancelAllOrdersApiWhenClose"`
|
UseCancelAllOrdersApiWhenClose bool `json:"useCancelAllOrdersApiWhenClose"`
|
||||||
|
|
||||||
// dev mode
|
|
||||||
DevMode *DevMode `json:"devMode"`
|
|
||||||
|
|
||||||
// log
|
// log
|
||||||
logger *logrus.Entry
|
logger *logrus.Entry
|
||||||
LogFields logrus.Fields `json:"logFields"`
|
LogFields logrus.Fields `json:"logFields"`
|
||||||
|
@ -179,12 +176,6 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
s.Position = types.NewPositionFromMarket(s.Market)
|
s.Position = types.NewPositionFromMarket(s.Market)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if dev mode is on and it's not a new strategy
|
|
||||||
if s.DevMode != nil && s.DevMode.Enabled && !s.DevMode.IsNewAccount {
|
|
||||||
s.ProfitStats = newProfitStats(s.Market, s.QuoteInvestment)
|
|
||||||
s.Position = types.NewPositionFromMarket(s.Market)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set ttl for persistence
|
// set ttl for persistence
|
||||||
s.Position.SetTTL(s.PersistenceTTL.Duration())
|
s.Position.SetTTL(s.PersistenceTTL.Duration())
|
||||||
s.ProfitStats.SetTTL(s.PersistenceTTL.Duration())
|
s.ProfitStats.SetTTL(s.PersistenceTTL.Duration())
|
||||||
|
@ -219,7 +210,7 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
}
|
}
|
||||||
|
|
||||||
s.OrderExecutor = bbgo.NewGeneralOrderExecutor(session, s.Symbol, ID, instanceID, s.Position)
|
s.OrderExecutor = bbgo.NewGeneralOrderExecutor(session, s.Symbol, ID, instanceID, s.Position)
|
||||||
s.OrderExecutor.SetMaxRetries(10)
|
s.OrderExecutor.SetMaxRetries(50)
|
||||||
s.OrderExecutor.BindEnvironment(s.Environment)
|
s.OrderExecutor.BindEnvironment(s.Environment)
|
||||||
s.OrderExecutor.Bind()
|
s.OrderExecutor.Bind()
|
||||||
|
|
||||||
|
@ -271,23 +262,23 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
})
|
})
|
||||||
|
|
||||||
session.MarketDataStream.OnKLine(func(kline types.KLine) {
|
session.MarketDataStream.OnKLine(func(kline types.KLine) {
|
||||||
// check price here
|
switch s.state {
|
||||||
if s.state != OpenPositionOrderFilled {
|
case OpenPositionOrderFilled:
|
||||||
|
if s.takeProfitPrice.IsZero() {
|
||||||
|
s.logger.Warn("take profit price should not be 0 when there is at least one open-position order filled, please check it")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
compRes := kline.Close.Compare(s.takeProfitPrice)
|
||||||
|
// price doesn't hit the take profit price
|
||||||
|
if compRes < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.emitNextState(OpenPositionOrdersCancelling)
|
||||||
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.takeProfitPrice.IsZero() {
|
|
||||||
s.logger.Warn("take profit price should not be 0 when there is at least one open-position order filled, please check it")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
compRes := kline.Close.Compare(s.takeProfitPrice)
|
|
||||||
// price doesn't hit the take profit price
|
|
||||||
if compRes < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.emitNextState(OpenPositionOrdersCancelling)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
session.UserDataStream.OnAuth(func() {
|
session.UserDataStream.OnAuth(func() {
|
||||||
|
@ -298,7 +289,7 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
// no need to recover when two situation
|
// no need to recover when two situation
|
||||||
// 1. recoverWhenStart is false
|
// 1. recoverWhenStart is false
|
||||||
// 2. dev mode is on and it's not new strategy
|
// 2. dev mode is on and it's not new strategy
|
||||||
if !s.RecoverWhenStart || (s.DevMode != nil && s.DevMode.Enabled && !s.DevMode.IsNewAccount) {
|
if !s.RecoverWhenStart {
|
||||||
s.updateState(WaitToOpenPosition)
|
s.updateState(WaitToOpenPosition)
|
||||||
} else {
|
} else {
|
||||||
// recover
|
// recover
|
||||||
|
@ -343,6 +334,11 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
// start to sync periodically
|
// start to sync periodically
|
||||||
go s.syncPeriodically(ctx)
|
go s.syncPeriodically(ctx)
|
||||||
|
|
||||||
|
// try to trigger position opening immediately
|
||||||
|
if s.state == WaitToOpenPosition {
|
||||||
|
s.emitNextState(PositionOpening)
|
||||||
|
}
|
||||||
|
|
||||||
// start running state machine
|
// start running state machine
|
||||||
s.runState(ctx)
|
s.runState(ctx)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user