Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REFACTOR: refactor profit fixer #1567

Merged
merged 4 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions pkg/strategy/common/profit_fixer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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) aggregateAllTrades(ctx context.Context, market types.Market, since, until time.Time) ([]types.Trade, error) {
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", 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 nil, err
}

allTrades = types.SortTradesAscending(allTrades)
return allTrades, nil
}

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)
allTrades, err := f.aggregateAllTrades(ctx, f.market, since, until)
if err != nil {
return err
}

return f.FixFromTrades(allTrades, stats, position)
}

func (f *ProfitFixer) FixFromTrades(allTrades []types.Trade, stats *types.ProfitStats, position *types.Position) error {
for _, trade := range allTrades {
profit, netProfit, madeProfit := position.AddTrade(trade)
if madeProfit {
p := position.NewProfit(trade, profit, netProfit)
stats.AddProfit(p)
}
}

log.Infof("profitFixer fix finished: profitStats and position are updated from %d trades", len(allTrades))
return nil
}
88 changes: 0 additions & 88 deletions pkg/strategy/xdepthmaker/profitfixer.go
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 3 additions & 2 deletions pkg/strategy/xdepthmaker/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Loading