Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REFACTOR: [dca2] refactor dca2 strategy to make it can back testing #1629

Merged
merged 3 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions pkg/strategy/dca2/dev_mode.go

This file was deleted.

16 changes: 0 additions & 16 deletions pkg/strategy/dca2/open_position.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"

"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/exchange/retry"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
Expand Down Expand Up @@ -33,21 +32,6 @@ func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error {

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
}

Expand Down
12 changes: 4 additions & 8 deletions pkg/strategy/dca2/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (s *Strategy) emitNextState(nextState State) {
// TakeProfitReady -> the takeProfit order filled ->
func (s *Strategy) runState(ctx context.Context) {
s.logger.Info("[DCA] runState")
stateTriggerTicker := time.NewTicker(5 * time.Second)
stateTriggerTicker := time.NewTicker(1 * time.Minute)
defer stateTriggerTicker.Stop()

for {
Expand All @@ -83,7 +83,7 @@ func (s *Strategy) runState(ctx context.Context) {
s.logger.Info("[DCA] runState DONE")
return
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()
case nextState := <-s.nextStateC:
// next state == current state -> skip
Expand Down Expand Up @@ -120,6 +120,8 @@ func (s *Strategy) runState(ctx context.Context) {
case TakeProfitReady:
s.runTakeProfitReady(ctx, nextState)
}

s.triggerNextState()
}
}
}
Expand Down Expand Up @@ -178,9 +180,6 @@ func (s *Strategy) runOpenPositionReady(_ context.Context, next State) {
func (s *Strategy) runOpenPositionOrderFilled(_ context.Context, next State) {
s.updateState(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) {
Expand All @@ -191,9 +190,6 @@ func (s *Strategy) runOpenPositionOrdersCancelling(ctx context.Context, next Sta
}
s.updateState(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) {
Expand Down
44 changes: 20 additions & 24 deletions pkg/strategy/dca2/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 bool `json:"useCancelAllOrdersApiWhenClose"`

// dev mode
DevMode *DevMode `json:"devMode"`

// log
logger *logrus.Entry
LogFields logrus.Fields `json:"logFields"`
Expand Down Expand Up @@ -179,12 +176,6 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
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
s.Position.SetTTL(s.PersistenceTTL.Duration())
s.ProfitStats.SetTTL(s.PersistenceTTL.Duration())
Expand Down Expand Up @@ -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.SetMaxRetries(10)
s.OrderExecutor.SetMaxRetries(50)
s.OrderExecutor.BindEnvironment(s.Environment)
s.OrderExecutor.Bind()

Expand Down Expand Up @@ -271,23 +262,23 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
})

session.MarketDataStream.OnKLine(func(kline types.KLine) {
// check price here
if s.state != OpenPositionOrderFilled {
return
}
switch s.state {
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
}

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
}

compRes := kline.Close.Compare(s.takeProfitPrice)
// price doesn't hit the take profit price
if compRes < 0 {
s.emitNextState(OpenPositionOrdersCancelling)
default:
return
}

s.emitNextState(OpenPositionOrdersCancelling)
})

session.UserDataStream.OnAuth(func() {
Expand All @@ -298,7 +289,7 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
// no need to recover when two situation
// 1. recoverWhenStart is false
// 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)
} else {
// recover
Expand Down Expand Up @@ -343,6 +334,11 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
// start to sync periodically
go s.syncPeriodically(ctx)

// try to trigger position opening immediately
if s.state == WaitToOpenPosition {
s.emitNextState(PositionOpening)
}

// start running state machine
s.runState(ctx)
}
Expand Down
Loading