bbgo/pkg/strategy/xmaker/signal_trade.go

112 lines
2.4 KiB
Go
Raw Normal View History

2024-09-04 07:59:21 +00:00
package xmaker
import (
"context"
"sync"
2024-09-04 07:59:21 +00:00
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
var tradeVolumeWindowSignalMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "xmaker_trade_volume_window_signal",
Help: "",
}, []string{"symbol"})
func init() {
prometheus.MustRegister(tradeVolumeWindowSignalMetrics)
}
type TradeVolumeWindowSignal struct {
Threshold fixedpoint.Value `json:"threshold"`
Window types.Duration `json:"window"`
trades []types.Trade
symbol string
mu sync.Mutex
2024-09-04 07:59:21 +00:00
}
func (s *TradeVolumeWindowSignal) handleTrade(trade types.Trade) {
s.mu.Lock()
2024-09-04 07:59:21 +00:00
s.trades = append(s.trades, trade)
s.mu.Unlock()
2024-09-04 07:59:21 +00:00
}
func (s *TradeVolumeWindowSignal) Bind(ctx context.Context, session *bbgo.ExchangeSession, symbol string) error {
s.symbol = symbol
if s.Window == 0 {
s.Window = types.Duration(time.Minute)
}
if s.Threshold.IsZero() {
s.Threshold = fixedpoint.NewFromFloat(0.7)
}
session.MarketDataStream.OnMarketTrade(s.handleTrade)
return nil
}
func (s *TradeVolumeWindowSignal) filterTrades(now time.Time) []types.Trade {
startTime := now.Add(-time.Duration(s.Window))
startIdx := 0
s.mu.Lock()
defer s.mu.Unlock()
2024-09-04 07:59:21 +00:00
for idx, td := range s.trades {
// skip trades before the start time
if td.Time.Before(startTime) {
continue
}
startIdx = idx
break
}
trades := s.trades[startIdx:]
s.trades = trades
return trades
2024-09-04 07:59:21 +00:00
}
2024-09-04 08:09:58 +00:00
func (s *TradeVolumeWindowSignal) aggTradeVolume(trades []types.Trade) (buyVolume, sellVolume float64) {
2024-09-04 07:59:21 +00:00
for _, td := range trades {
if td.IsBuyer {
buyVolume += td.Quantity.Float64()
} else {
sellVolume += td.Quantity.Float64()
}
}
return buyVolume, sellVolume
}
2024-09-04 08:09:58 +00:00
func (s *TradeVolumeWindowSignal) CalculateSignal(_ context.Context) (float64, error) {
2024-09-04 07:59:21 +00:00
now := time.Now()
trades := s.filterTrades(now)
2024-09-04 08:09:58 +00:00
buyVolume, sellVolume := s.aggTradeVolume(trades)
2024-09-04 07:59:21 +00:00
totalVolume := buyVolume + sellVolume
threshold := s.Threshold.Float64()
buyRatio := buyVolume / totalVolume
sellRatio := sellVolume / totalVolume
sig := 0.0
if buyRatio > threshold {
sig = (buyRatio - threshold) / 2.0
} else if sellRatio > threshold {
sig = -(sellRatio - threshold) / 2.0
}
log.Infof("[TradeVolumeWindowSignal] %f buy/sell = %f/%f", sig, buyVolume, sellVolume)
2024-09-04 07:59:21 +00:00
tradeVolumeWindowSignalMetrics.WithLabelValues(s.symbol).Set(sig)
return sig, nil
}