mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
FEATURE: add metrics for dca2
add log to debug
This commit is contained in:
parent
3e087e8af3
commit
5936cf32c7
116
pkg/strategy/dca2/metrics.go
Normal file
116
pkg/strategy/dca2/metrics.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package dca2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
metricsState *prometheus.GaugeVec
|
||||||
|
metricsNumOfActiveOrders *prometheus.GaugeVec
|
||||||
|
metricsNumOfOpenOrders *prometheus.GaugeVec
|
||||||
|
metricsProfit *prometheus.GaugeVec
|
||||||
|
)
|
||||||
|
|
||||||
|
func labelKeys(labels prometheus.Labels) []string {
|
||||||
|
var keys []string
|
||||||
|
for k := range labels {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeLabels(a, b prometheus.Labels) prometheus.Labels {
|
||||||
|
labels := prometheus.Labels{}
|
||||||
|
for k, v := range a {
|
||||||
|
labels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range b {
|
||||||
|
labels[k] = v
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func initMetrics(extendedLabels []string) {
|
||||||
|
if metricsState == nil {
|
||||||
|
metricsState = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_dca2_state",
|
||||||
|
Help: "state of this DCA2 strategy",
|
||||||
|
},
|
||||||
|
append([]string{
|
||||||
|
"exchange",
|
||||||
|
"symbol",
|
||||||
|
}, extendedLabels...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if metricsNumOfActiveOrders == nil {
|
||||||
|
metricsNumOfActiveOrders = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_dca2_num_of_active_orders",
|
||||||
|
Help: "number of active orders",
|
||||||
|
},
|
||||||
|
append([]string{
|
||||||
|
"exchange",
|
||||||
|
"symbol",
|
||||||
|
}, extendedLabels...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if metricsNumOfOpenOrders == nil {
|
||||||
|
metricsNumOfOpenOrders = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_dca2_num_of_open_orders",
|
||||||
|
Help: "number of open orders",
|
||||||
|
},
|
||||||
|
append([]string{
|
||||||
|
"exchange",
|
||||||
|
"symbol",
|
||||||
|
}, extendedLabels...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if metricsProfit == nil {
|
||||||
|
metricsProfit = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "bbgo_dca2_profit",
|
||||||
|
Help: "profit of this DCA@ strategy",
|
||||||
|
},
|
||||||
|
append([]string{
|
||||||
|
"exchange",
|
||||||
|
"symbol",
|
||||||
|
"round",
|
||||||
|
}, extendedLabels...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var metricsRegistered = false
|
||||||
|
|
||||||
|
func registerMetrics() {
|
||||||
|
if metricsRegistered {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
initMetrics(nil)
|
||||||
|
|
||||||
|
prometheus.MustRegister(
|
||||||
|
metricsState,
|
||||||
|
metricsNumOfActiveOrders,
|
||||||
|
metricsNumOfOpenOrders,
|
||||||
|
metricsProfit,
|
||||||
|
)
|
||||||
|
|
||||||
|
metricsRegistered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateProfitMetrics(round int64, profit float64) {
|
||||||
|
labels := mergeLabels(baseLabels, prometheus.Labels{
|
||||||
|
"round": strconv.FormatInt(round, 10),
|
||||||
|
})
|
||||||
|
metricsProfit.With(labels).Set(profit)
|
||||||
|
}
|
|
@ -67,7 +67,7 @@ func (s *Strategy) recover(ctx context.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.state = state
|
s.updateState(state)
|
||||||
s.logger.Info("recover stats DONE")
|
s.logger.Info("recover stats DONE")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -46,6 +46,13 @@ func (s *Strategy) initializeNextStateC() bool {
|
||||||
return isInitialize
|
return isInitialize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) updateState(state State) {
|
||||||
|
s.state = state
|
||||||
|
|
||||||
|
s.logger.Infof("[state] update state to %d", state)
|
||||||
|
metricsState.With(baseLabels).Set(float64(s.state))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Strategy) emitNextState(nextState State) {
|
func (s *Strategy) emitNextState(nextState State) {
|
||||||
select {
|
select {
|
||||||
case s.nextStateC <- nextState:
|
case s.nextStateC <- nextState:
|
||||||
|
@ -63,17 +70,22 @@ 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")
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
stateTriggerTicker := time.NewTicker(5 * time.Second)
|
||||||
defer ticker.Stop()
|
defer stateTriggerTicker.Stop()
|
||||||
|
|
||||||
|
monitorTicker := time.NewTicker(10 * time.Minute)
|
||||||
|
defer monitorTicker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
s.logger.Info("[DCA] runState DONE")
|
s.logger.Info("[DCA] runState DONE")
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-stateTriggerTicker.C:
|
||||||
s.logger.Infof("[DCA] triggerNextState current state: %d", s.state)
|
// s.logger.Infof("[DCA] triggerNextState current state: %d", s.state)
|
||||||
s.triggerNextState()
|
s.triggerNextState()
|
||||||
|
case <-monitorTicker.C:
|
||||||
|
s.updateNumOfOrdersMetrics(ctx)
|
||||||
case nextState := <-s.nextStateC:
|
case nextState := <-s.nextStateC:
|
||||||
s.logger.Infof("[DCA] currenct state: %d, next state: %d", s.state, nextState)
|
s.logger.Infof("[DCA] currenct state: %d, next state: %d", s.state, nextState)
|
||||||
|
|
||||||
|
@ -131,7 +143,7 @@ func (s *Strategy) runWaitToOpenPositionState(ctx context.Context, next State) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.state = PositionOpening
|
s.updateState(PositionOpening)
|
||||||
s.logger.Info("[State] WaitToOpenPosition -> PositionOpening")
|
s.logger.Info("[State] WaitToOpenPosition -> PositionOpening")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,17 +153,17 @@ func (s *Strategy) runPositionOpening(ctx context.Context, next State) {
|
||||||
s.logger.WithError(err).Error("failed to place dca orders, please check it.")
|
s.logger.WithError(err).Error("failed to place dca orders, please check it.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.state = OpenPositionReady
|
s.updateState(OpenPositionReady)
|
||||||
s.logger.Info("[State] PositionOpening -> OpenPositionReady")
|
s.logger.Info("[State] PositionOpening -> OpenPositionReady")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) runOpenPositionReady(_ context.Context, next State) {
|
func (s *Strategy) runOpenPositionReady(_ context.Context, next State) {
|
||||||
s.state = OpenPositionOrderFilled
|
s.updateState(OpenPositionOrderFilled)
|
||||||
s.logger.Info("[State] OpenPositionReady -> OpenPositionOrderFilled")
|
s.logger.Info("[State] OpenPositionReady -> OpenPositionOrderFilled")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) runOpenPositionOrderFilled(_ context.Context, next State) {
|
func (s *Strategy) runOpenPositionOrderFilled(_ context.Context, next State) {
|
||||||
s.state = 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
|
// after open position cancelling, immediately trigger open position cancelled to cancel the other orders
|
||||||
|
@ -164,7 +176,7 @@ func (s *Strategy) runOpenPositionOrdersCancelling(ctx context.Context, next Sta
|
||||||
s.logger.WithError(err).Error("failed to cancel maker orders")
|
s.logger.WithError(err).Error("failed to cancel maker orders")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.state = 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
|
// after open position cancelled, immediately trigger take profit ready to open take-profit order
|
||||||
|
@ -177,7 +189,7 @@ func (s *Strategy) runOpenPositionOrdersCancelled(ctx context.Context, next Stat
|
||||||
s.logger.WithError(err).Error("failed to open take profit orders")
|
s.logger.WithError(err).Error("failed to open take profit orders")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.state = TakeProfitReady
|
s.updateState(TakeProfitReady)
|
||||||
s.logger.Info("[State] OpenPositionOrdersCancelled -> TakeProfitReady")
|
s.logger.Info("[State] OpenPositionOrdersCancelled -> TakeProfitReady")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +212,6 @@ func (s *Strategy) runTakeProfitReady(ctx context.Context, next State) {
|
||||||
|
|
||||||
// set the start time of the next round
|
// set the start time of the next round
|
||||||
s.startTimeOfNextRound = time.Now().Add(s.CoolDownInterval.Duration())
|
s.startTimeOfNextRound = time.Now().Add(s.CoolDownInterval.Duration())
|
||||||
s.state = WaitToOpenPosition
|
s.updateState(WaitToOpenPosition)
|
||||||
s.logger.Info("[State] TakeProfitReady -> WaitToOpenPosition")
|
s.logger.Info("[State] TakeProfitReady -> WaitToOpenPosition")
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,15 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/util/tradingutil"
|
"github.com/c9s/bbgo/pkg/util/tradingutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = "dca2"
|
const (
|
||||||
|
ID = "dca2"
|
||||||
|
orderTag = "dca2"
|
||||||
|
)
|
||||||
|
|
||||||
const orderTag = "dca2"
|
var (
|
||||||
|
log = logrus.WithField("strategy", ID)
|
||||||
var log = logrus.WithField("strategy", ID)
|
baseLabels prometheus.Labels
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bbgo.RegisterStrategy(ID, &Strategy{})
|
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||||
|
@ -119,6 +123,7 @@ func (s *Strategy) Defaults() error {
|
||||||
|
|
||||||
s.LogFields["symbol"] = s.Symbol
|
s.LogFields["symbol"] = s.Symbol
|
||||||
s.LogFields["strategy"] = ID
|
s.LogFields["strategy"] = ID
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +140,23 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: types.Interval1m})
|
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: types.Interval1m})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) newPrometheusLabels() prometheus.Labels {
|
||||||
|
labels := prometheus.Labels{
|
||||||
|
"exchange": "default",
|
||||||
|
"symbol": s.Symbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Session != nil {
|
||||||
|
labels["exchange"] = s.Session.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.PrometheusLabels == nil {
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeLabels(s.PrometheusLabels, labels)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||||
instanceID := s.InstanceID()
|
instanceID := s.InstanceID()
|
||||||
s.Session = session
|
s.Session = session
|
||||||
|
@ -146,6 +168,15 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
s.Position = types.NewPositionFromMarket(s.Market)
|
s.Position = types.NewPositionFromMarket(s.Market)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prometheus
|
||||||
|
if s.PrometheusLabels != nil {
|
||||||
|
initMetrics(labelKeys(s.PrometheusLabels))
|
||||||
|
}
|
||||||
|
registerMetrics()
|
||||||
|
|
||||||
|
// prometheus labels
|
||||||
|
baseLabels = s.newPrometheusLabels()
|
||||||
|
|
||||||
// if dev mode is on and it's not a new strategy
|
// if dev mode is on and it's not a new strategy
|
||||||
if s.DevMode != nil && s.DevMode.Enabled && !s.DevMode.IsNewAccount {
|
if s.DevMode != nil && s.DevMode.Enabled && !s.DevMode.IsNewAccount {
|
||||||
s.ProfitStats = newProfitStats(s.Market, s.QuoteInvestment)
|
s.ProfitStats = newProfitStats(s.Market, s.QuoteInvestment)
|
||||||
|
@ -192,6 +223,9 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
default:
|
default:
|
||||||
s.logger.Infof("[DCA] unsupported side (%s) of order: %s", o.Side, o)
|
s.logger.Infof("[DCA] unsupported side (%s) of order: %s", o.Side, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update metrics when filled
|
||||||
|
s.updateNumOfOrdersMetrics(ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
session.MarketDataStream.OnKLine(func(kline types.KLine) {
|
session.MarketDataStream.OnKLine(func(kline types.KLine) {
|
||||||
|
@ -218,7 +252,7 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
||||||
// 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.DevMode != nil && s.DevMode.Enabled && !s.DevMode.IsNewAccount) {
|
||||||
s.state = WaitToOpenPosition
|
s.updateState(WaitToOpenPosition)
|
||||||
} else {
|
} else {
|
||||||
// recover
|
// recover
|
||||||
if err := s.recover(ctx); err != nil {
|
if err := s.recover(ctx); err != nil {
|
||||||
|
@ -400,9 +434,23 @@ func (s *Strategy) CalculateAndEmitProfit(ctx context.Context) error {
|
||||||
|
|
||||||
// emit profit
|
// emit profit
|
||||||
s.EmitProfit(s.ProfitStats)
|
s.EmitProfit(s.ProfitStats)
|
||||||
|
updateProfitMetrics(s.ProfitStats.Round, s.ProfitStats.CurrentRoundProfit.Float64())
|
||||||
|
|
||||||
s.ProfitStats.NewRound()
|
s.ProfitStats.NewRound()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) updateNumOfOrdersMetrics(ctx context.Context) {
|
||||||
|
// update open orders metrics
|
||||||
|
openOrders, err := s.Session.Exchange.QueryOpenOrders(ctx, s.Symbol)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.WithError(err).Warn("failed to query open orders to update num of the orders metrics")
|
||||||
|
} else {
|
||||||
|
metricsNumOfOpenOrders.With(baseLabels).Set(float64(len(openOrders)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update active orders metrics
|
||||||
|
metricsNumOfActiveOrders.With(baseLabels).Set(float64(s.OrderExecutor.ActiveMakerOrders().NumOfOrders()))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user