Merge pull request #1386 from c9s/c9s/refactor-wall-strategy

REFACTOR: [wall] refactor wall strategy with common.Strategy
This commit is contained in:
c9s 2023-11-01 17:06:17 +08:00 committed by GitHub
commit 2ffc617dac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 85 deletions

View File

@ -10,6 +10,14 @@ sessions:
exchange: max exchange: max
envVarPrefix: MAX envVarPrefix: MAX
logging:
trade: true
order: true
# fields:
# env: prod
exchangeStrategies: exchangeStrategies:
- on: max - on: max
@ -33,6 +41,6 @@ exchangeStrategies:
byLayer: byLayer:
linear: linear:
domain: [ 1, 3 ] domain: [ 1, 3 ]
range: [ 10.0, 30.0 ] range: [ 10000.0, 30000.0 ]

View File

@ -6,12 +6,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/c9s/bbgo/pkg/core"
"github.com/c9s/bbgo/pkg/util"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/strategy/common"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
@ -31,9 +30,10 @@ func init() {
} }
type Strategy struct { type Strategy struct {
Environment *bbgo.Environment *common.Strategy
StandardIndicatorSet *bbgo.StandardIndicatorSet
Market types.Market Environment *bbgo.Environment
Market types.Market
// Symbol is the market symbol you want to trade // Symbol is the market symbol you want to trade
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
@ -60,18 +60,8 @@ type Strategy struct {
session *bbgo.ExchangeSession session *bbgo.ExchangeSession
// persistence fields
Position *types.Position `json:"position,omitempty" persistence:"position"`
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
activeAdjustmentOrders *bbgo.ActiveOrderBook activeAdjustmentOrders *bbgo.ActiveOrderBook
activeWallOrders *bbgo.ActiveOrderBook activeWallOrders *bbgo.ActiveOrderBook
orderStore *core.OrderStore
tradeCollector *core.TradeCollector
groupID uint32
stopC chan struct{}
} }
func (s *Strategy) ID() string { func (s *Strategy) ID() string {
@ -149,7 +139,6 @@ func (s *Strategy) placeAdjustmentOrders(ctx context.Context, orderExecutor bbgo
Price: askPrice, Price: askPrice,
Quantity: quantity, Quantity: quantity,
Market: s.Market, Market: s.Market,
GroupID: s.groupID,
}) })
case types.SideTypeSell: case types.SideTypeSell:
@ -175,7 +164,6 @@ func (s *Strategy) placeAdjustmentOrders(ctx context.Context, orderExecutor bbgo
Price: bidPrice, Price: bidPrice,
Quantity: quantity, Quantity: quantity,
Market: s.Market, Market: s.Market,
GroupID: s.groupID,
}) })
} }
@ -189,12 +177,13 @@ func (s *Strategy) placeAdjustmentOrders(ctx context.Context, orderExecutor bbgo
return err return err
} }
s.orderStore.Add(createdOrders...)
s.activeAdjustmentOrders.Add(createdOrders...) s.activeAdjustmentOrders.Add(createdOrders...)
return nil return nil
} }
func (s *Strategy) placeWallOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor) error { func (s *Strategy) placeWallOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor) error {
log.Infof("placing wall orders...")
var submitOrders []types.SubmitOrder var submitOrders []types.SubmitOrder
var startPrice = s.FixedPrice var startPrice = s.FixedPrice
for i := 0; i < s.NumLayers; i++ { for i := 0; i < s.NumLayers; i++ {
@ -217,7 +206,6 @@ func (s *Strategy) placeWallOrders(ctx context.Context, orderExecutor bbgo.Order
Price: price, Price: price,
Quantity: quantity, Quantity: quantity,
Market: s.Market, Market: s.Market,
GroupID: s.groupID,
} }
submitOrders = append(submitOrders, order) submitOrders = append(submitOrders, order)
switch s.Side { switch s.Side {
@ -240,74 +228,27 @@ func (s *Strategy) placeWallOrders(ctx context.Context, orderExecutor bbgo.Order
return err return err
} }
s.orderStore.Add(createdOrders...) log.Infof("wall orders placed: %+v", createdOrders)
s.activeWallOrders.Add(createdOrders...) s.activeWallOrders.Add(createdOrders...)
return err return err
} }
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error { 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())
// initial required information // initial required information
s.session = session s.session = session
// calculate group id for orders
instanceID := s.InstanceID()
s.groupID = util.FNV32(instanceID)
// If position is nil, we need to allocate a new position for calculation
if s.Position == nil {
s.Position = types.NewPositionFromMarket(s.Market)
}
if s.ProfitStats == nil {
s.ProfitStats = types.NewProfitStats(s.Market)
}
// Always update the position fields
s.Position.Strategy = ID
s.Position.StrategyInstanceID = instanceID
s.stopC = make(chan struct{})
s.activeWallOrders = bbgo.NewActiveOrderBook(s.Symbol) s.activeWallOrders = bbgo.NewActiveOrderBook(s.Symbol)
s.activeWallOrders.BindStream(session.UserDataStream) s.activeWallOrders.BindStream(session.UserDataStream)
s.activeAdjustmentOrders = bbgo.NewActiveOrderBook(s.Symbol) s.activeAdjustmentOrders = bbgo.NewActiveOrderBook(s.Symbol)
s.activeAdjustmentOrders.BindStream(session.UserDataStream) s.activeAdjustmentOrders.BindStream(session.UserDataStream)
s.orderStore = core.NewOrderStore(s.Symbol)
s.orderStore.BindStream(session.UserDataStream)
s.tradeCollector = core.NewTradeCollector(s.Symbol, s.Position, s.orderStore)
s.tradeCollector.OnTrade(func(trade types.Trade, profit, netProfit fixedpoint.Value) {
bbgo.Notify(trade)
s.ProfitStats.AddTrade(trade)
if profit.Compare(fixedpoint.Zero) == 0 {
s.Environment.RecordPosition(s.Position, trade, nil)
} else {
log.Infof("%s generated profit: %v", s.Symbol, profit)
p := s.Position.NewProfit(trade, profit, netProfit)
p.Strategy = ID
p.StrategyInstanceID = instanceID
bbgo.Notify(&p)
s.ProfitStats.AddProfit(p)
bbgo.Notify(&s.ProfitStats)
s.Environment.RecordPosition(s.Position, trade, &p)
}
})
s.tradeCollector.OnPositionUpdate(func(position *types.Position) {
log.Infof("position changed: %s", s.Position)
bbgo.Notify(s.Position)
})
s.tradeCollector.BindStream(session.UserDataStream)
session.UserDataStream.OnStart(func() { session.UserDataStream.OnStart(func() {
if err := s.placeWallOrders(ctx, orderExecutor); err != nil { if err := s.placeWallOrders(ctx, s.OrderExecutor); err != nil {
log.WithError(err).Errorf("can not place order") log.WithError(err).Errorf("can not place order")
} }
}) })
@ -318,9 +259,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
// check if there is a canceled order had partially filled. // check if there is a canceled order had partially filled.
s.tradeCollector.Process() s.OrderExecutor.TradeCollector().Process()
if err := s.placeAdjustmentOrders(ctx, orderExecutor); err != nil { if err := s.placeAdjustmentOrders(ctx, s.OrderExecutor); err != nil {
log.WithError(err).Errorf("can not place order") log.WithError(err).Errorf("can not place order")
} }
}) })
@ -331,9 +272,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
// check if there is a canceled order had partially filled. // check if there is a canceled order had partially filled.
s.tradeCollector.Process() s.OrderExecutor.TradeCollector().Process()
if err := s.placeWallOrders(ctx, orderExecutor); err != nil { if err := s.placeWallOrders(ctx, s.OrderExecutor); err != nil {
log.WithError(err).Errorf("can not place order") log.WithError(err).Errorf("can not place order")
} }
@ -342,9 +283,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
// check if there is a canceled order had partially filled. // check if there is a canceled order had partially filled.
s.tradeCollector.Process() s.OrderExecutor.TradeCollector().Process()
if err := s.placeAdjustmentOrders(ctx, orderExecutor); err != nil { if err := s.placeAdjustmentOrders(ctx, s.OrderExecutor); err != nil {
log.WithError(err).Errorf("can not place order") log.WithError(err).Errorf("can not place order")
} }
}) })
@ -365,9 +306,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
// check if there is a canceled order had partially filled. // check if there is a canceled order had partially filled.
s.tradeCollector.Process() s.OrderExecutor.TradeCollector().Process()
if err := s.placeWallOrders(ctx, orderExecutor); err != nil { if err := s.placeWallOrders(ctx, s.OrderExecutor); err != nil {
log.WithError(err).Errorf("can not place order") log.WithError(err).Errorf("can not place order")
} }
} }
@ -377,7 +318,6 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) { bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
close(s.stopC)
if err := s.activeWallOrders.GracefulCancel(ctx, s.session.Exchange); err != nil { if err := s.activeWallOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
log.WithError(err).Errorf("graceful cancel order error") log.WithError(err).Errorf("graceful cancel order error")
@ -387,7 +327,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
log.WithError(err).Errorf("graceful cancel order error") log.WithError(err).Errorf("graceful cancel order error")
} }
s.tradeCollector.Process() // check if there is a canceled order had partially filled.
s.OrderExecutor.TradeCollector().Process()
}) })
return nil return nil