common: move out profit fixer to strategy/common

This commit is contained in:
c9s 2024-03-06 20:31:53 +08:00
parent 9a76d2e37a
commit 6a24059624
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
3 changed files with 94 additions and 90 deletions

View File

@ -0,0 +1,91 @@
package common
import (
"context"
"sync"
"time"
log "github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"github.com/c9s/bbgo/pkg/exchange/batch"
"github.com/c9s/bbgo/pkg/types"
)
// ProfitFixerConfig is used for fixing profitStats and position by re-playing the trade history
type ProfitFixerConfig struct {
TradesSince types.Time `json:"tradesSince,omitempty"`
}
// ProfitFixer implements a trade-history-based profit fixer
type ProfitFixer struct {
market types.Market
sessions map[string]types.ExchangeTradeHistoryService
}
func NewProfitFixer(market types.Market) *ProfitFixer {
return &ProfitFixer{
market: market,
sessions: make(map[string]types.ExchangeTradeHistoryService),
}
}
func (f *ProfitFixer) AddExchange(sessionName string, service types.ExchangeTradeHistoryService) {
f.sessions[sessionName] = service
}
func (f *ProfitFixer) batchQueryTrades(
ctx context.Context,
service types.ExchangeTradeHistoryService,
symbol string,
since, until time.Time,
) ([]types.Trade, error) {
q := &batch.TradeBatchQuery{ExchangeTradeHistoryService: service}
return q.QueryTrades(ctx, symbol, &types.TradeQueryOptions{
StartTime: &since,
EndTime: &until,
})
}
func (f *ProfitFixer) Fix(ctx context.Context, since, until time.Time, stats *types.ProfitStats, position *types.Position) error {
log.Infof("starting profitFixer with time range %s <=> %s", since, until)
var mu sync.Mutex
var allTrades = make([]types.Trade, 0, 1000)
g, subCtx := errgroup.WithContext(ctx)
for n, s := range f.sessions {
// allocate a copy of the iteration variables
sessionName := n
service := s
g.Go(func() error {
log.Infof("batch querying %s trade history from %s since %s until %s", f.market.Symbol, sessionName, since.String(), until.String())
trades, err := f.batchQueryTrades(subCtx, service, f.market.Symbol, since, until)
if err != nil {
log.WithError(err).Errorf("unable to batch query trades for fixer")
return err
}
mu.Lock()
allTrades = append(allTrades, trades...)
mu.Unlock()
return nil
})
}
if err := g.Wait(); err != nil {
return err
}
allTrades = types.SortTradesAscending(allTrades)
for _, trade := range allTrades {
profit, netProfit, madeProfit := position.AddTrade(trade)
if madeProfit {
p := position.NewProfit(trade, profit, netProfit)
stats.AddProfit(p)
}
}
log.Infof("profitFixer done: profitStats and position are updated from %d trades", len(allTrades))
return nil
}

View File

@ -1,89 +1 @@
package xdepthmaker
import (
"context"
"sync"
"time"
"golang.org/x/sync/errgroup"
"github.com/c9s/bbgo/pkg/exchange/batch"
"github.com/c9s/bbgo/pkg/types"
)
type ProfitFixerConfig struct {
TradesSince types.Time `json:"tradesSince,omitempty"`
}
// ProfitFixer implements a trade history based profit fixer
type ProfitFixer struct {
market types.Market
sessions map[string]types.ExchangeTradeHistoryService
}
func NewProfitFixer(market types.Market) *ProfitFixer {
return &ProfitFixer{
market: market,
sessions: make(map[string]types.ExchangeTradeHistoryService),
}
}
func (f *ProfitFixer) AddExchange(sessionName string, service types.ExchangeTradeHistoryService) {
f.sessions[sessionName] = service
}
func (f *ProfitFixer) batchQueryTrades(
ctx context.Context,
service types.ExchangeTradeHistoryService,
symbol string,
since, until time.Time,
) ([]types.Trade, error) {
q := &batch.TradeBatchQuery{ExchangeTradeHistoryService: service}
return q.QueryTrades(ctx, symbol, &types.TradeQueryOptions{
StartTime: &since,
EndTime: &until,
})
}
func (f *ProfitFixer) Fix(ctx context.Context, since, until time.Time, stats *types.ProfitStats, position *types.Position) error {
log.Infof("starting profitFixer with time range %s <=> %s", since, until)
var mu sync.Mutex
var allTrades = make([]types.Trade, 0, 1000)
g, subCtx := errgroup.WithContext(ctx)
for n, s := range f.sessions {
// allocate a copy of the iteration variables
sessionName := n
service := s
g.Go(func() error {
log.Infof("batch querying %s trade history from %s since %s until %s", f.market.Symbol, sessionName, since.String(), until.String())
trades, err := f.batchQueryTrades(subCtx, service, f.market.Symbol, since, until)
if err != nil {
log.WithError(err).Errorf("unable to batch query trades for fixer")
return err
}
mu.Lock()
allTrades = append(allTrades, trades...)
mu.Unlock()
return nil
})
}
if err := g.Wait(); err != nil {
return err
}
allTrades = types.SortTradesAscending(allTrades)
for _, trade := range allTrades {
profit, netProfit, madeProfit := position.AddTrade(trade)
if madeProfit {
p := position.NewProfit(trade, profit, netProfit)
stats.AddProfit(p)
}
}
log.Infof("profitFixer done: profitStats and position are updated from %d trades", len(allTrades))
return nil
}

View File

@ -14,6 +14,7 @@ import (
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/exchange/retry"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/strategy/common"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/bbgo/pkg/util"
)
@ -195,7 +196,7 @@ type Strategy struct {
// Pips is the pips of the layer prices
Pips fixedpoint.Value `json:"pips"`
ProfitFixerConfig *ProfitFixerConfig `json:"profitFixer"`
ProfitFixerConfig *common.ProfitFixerConfig `json:"profitFixer"`
// --------------------------------
// private fields
@ -332,7 +333,7 @@ func (s *Strategy) CrossRun(
s.CrossExchangeMarketMakingStrategy.Position = types.NewPositionFromMarket(makerMarket)
s.CrossExchangeMarketMakingStrategy.ProfitStats = types.NewProfitStats(makerMarket)
fixer := NewProfitFixer(makerMarket)
fixer := common.NewProfitFixer(makerMarket)
if ss, ok := makerSession.Exchange.(types.ExchangeTradeHistoryService); ok {
log.Infof("adding makerSession %s to profitFixer", makerSession.Name)
fixer.AddExchange(makerSession.Name, ss)