bbgo_origin/pkg/backtest/stream.go

124 lines
2.8 KiB
Go
Raw Normal View History

package backtest
import (
"context"
2020-11-09 08:34:35 +00:00
"fmt"
2021-12-25 14:37:38 +00:00
"time"
"github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/types"
)
var log = logrus.WithField("cmd", "backtest")
type Stream struct {
types.StandardStream
exchange *Exchange
}
func (s *Stream) Connect(ctx context.Context) error {
2020-11-06 19:18:05 +00:00
log.Infof("collecting backtest configurations...")
loadedSymbols := map[string]struct{}{}
2020-11-07 12:34:34 +00:00
loadedIntervals := map[types.Interval]struct{}{
// 1m interval is required for the backtest matching engine
2020-11-10 06:18:04 +00:00
types.Interval1m: {},
types.Interval1d: {},
2020-11-07 12:34:34 +00:00
}
for _, sub := range s.Subscriptions {
loadedSymbols[sub.Symbol] = struct{}{}
switch sub.Channel {
case types.KLineChannel:
loadedIntervals[types.Interval(sub.Options.Interval)] = struct{}{}
default:
2020-11-09 08:34:35 +00:00
return fmt.Errorf("stream channel %s is not supported in backtest", sub.Channel)
}
}
var symbols []string
for symbol := range loadedSymbols {
symbols = append(symbols, symbol)
}
var intervals []types.Interval
for interval := range loadedIntervals {
intervals = append(intervals, interval)
}
2020-11-06 19:18:05 +00:00
log.Infof("used symbols: %v and intervals: %v", symbols, intervals)
2021-12-23 06:14:48 +00:00
if !s.PublicOnly {
// user data stream
s.OnTradeUpdate(func(trade types.Trade) {
s.exchange.addTrade(trade)
})
// FIXME: here if we created two user data stream, since the callbacks are not de-registered we might have problem
s.exchange.matchingBooksMutex.Lock()
for _, matching := range s.exchange.matchingBooks {
matching.OnTradeUpdate(s.EmitTradeUpdate)
matching.OnOrderUpdate(s.EmitOrderUpdate)
matching.OnBalanceUpdate(s.EmitBalanceUpdate)
}
s.exchange.matchingBooksMutex.Unlock()
// assign user data stream back
s.exchange.userDataStream = s
}
2021-12-25 14:37:38 +00:00
s.EmitConnect()
s.EmitStart()
2021-12-23 06:14:48 +00:00
if s.PublicOnly {
go func() {
2021-12-25 14:37:38 +00:00
FeedMarketData(s, s.exchange, s.exchange.startTime, s.exchange.endTime, symbols, intervals)
}()
}
2021-12-25 14:37:38 +00:00
return nil
}
2020-11-07 12:11:07 +00:00
2021-12-25 14:37:38 +00:00
func FeedMarketData(s *Stream, ex *Exchange, startTime, endTime time.Time, symbols []string, intervals []types.Interval) {
log.Infof("querying klines from database...")
klineC, errC := ex.srv.QueryKLinesCh(startTime, endTime, ex, symbols, intervals)
numKlines := 0
for k := range klineC {
if k.Interval == types.Interval1m {
matching, ok := ex.matchingBook(k.Symbol)
if !ok {
log.Errorf("matching book of %s is not initialized", k.Symbol)
continue
}
2021-12-25 14:37:38 +00:00
// here we generate trades and order updates
matching.processKLine(k)
numKlines++
}
s.EmitKLineClosed(k)
}
2021-12-25 14:37:38 +00:00
if err := <-errC; err != nil {
log.WithError(err).Error("backtest data feed error")
}
if numKlines == 0 {
log.Error("kline data is empty, make sure you have sync the exchange market data")
}
if err := s.Close(); err != nil {
log.WithError(err).Error("stream close error")
}
}
func (s *Stream) Close() error {
2020-11-07 12:11:07 +00:00
close(s.exchange.doneC)
return nil
}
2021-12-25 14:37:38 +00:00