mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +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"
|
||||
"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"
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -120,6 +120,8 @@ func (s *Strategy) runState(ctx context.Context) {
|
|||
case TakeProfitReady:
|
||||
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) {
|
||||
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) {
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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"`
|
||||
|
@ -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())
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 {
|
||||
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
|
||||
}
|
||||
|
||||
compRes := kline.Close.Compare(s.takeProfitPrice)
|
||||
// price doesn't hit the take profit price
|
||||
if compRes < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.emitNextState(OpenPositionOrdersCancelling)
|
||||
default:
|
||||
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() {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user