mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 17:13:51 +00:00
177 lines
4.1 KiB
Go
177 lines
4.1 KiB
Go
package backtest
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
)
|
|
|
|
type Stream struct {
|
|
types.StandardStream
|
|
|
|
exchange *Exchange
|
|
}
|
|
|
|
func (s *Stream) Connect(ctx context.Context) error {
|
|
loadedSymbols := map[string]struct{}{}
|
|
loadedIntervals := map[types.Interval]struct{}{}
|
|
for _, sub := range s.Subscriptions {
|
|
loadedSymbols[sub.Symbol] = struct{}{}
|
|
|
|
switch sub.Channel {
|
|
case types.KLineChannel:
|
|
loadedIntervals[types.Interval(sub.Options.Interval)] = struct{}{}
|
|
|
|
default:
|
|
return errors.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)
|
|
}
|
|
|
|
// TODO: we can sync before we connect
|
|
/*
|
|
if err := backtestService.Sync(ctx, exchange, symbol, startTime); err != nil {
|
|
return err
|
|
}
|
|
*/
|
|
|
|
klineC, errC := s.exchange.srv.QueryKLinesCh(s.exchange.startTime, s.exchange, symbols, intervals)
|
|
for k := range klineC {
|
|
s.EmitKLineClosed(k)
|
|
}
|
|
|
|
if err := <-errC; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Stream) Close() error {
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
func (trader *BackTestTrader) RunStrategy(ctx context.Context, strategy SingleExchangeStrategy) (chan struct{}, error) {
|
|
logrus.Infof("[regression] number of kline data: %d", len(trader.SourceKLines))
|
|
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
|
|
if err := strategy.OnLoad(trader.Context, trader); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stream := &BackTestStream{}
|
|
if err := strategy.OnNewStream(stream); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tradeID int64 = 0
|
|
for _, kline := range trader.SourceKLines {
|
|
logrus.Debugf("kline %+v", kline)
|
|
|
|
fmt.Print(".")
|
|
|
|
stream.EmitKLineClosed(kline)
|
|
|
|
for _, order := range trader.pendingOrders {
|
|
switch order.Side {
|
|
case types.SideTypeBuy:
|
|
fmt.Print("B")
|
|
case types.SideTypeSell:
|
|
fmt.Print("S")
|
|
}
|
|
|
|
var price float64
|
|
if order.Type == types.OrderTypeLimit {
|
|
price = util.MustParseFloat(order.PriceString)
|
|
} else {
|
|
price = kline.GetClose()
|
|
}
|
|
|
|
volume := util.MustParseFloat(order.QuantityString)
|
|
fee := 0.0
|
|
feeCurrency := ""
|
|
|
|
trader.Context.Lock()
|
|
if order.Side == types.SideTypeBuy {
|
|
fee = price * volume * 0.001
|
|
feeCurrency = "USDT"
|
|
|
|
quote := trader.Context.Balances[trader.Context.Market.QuoteCurrency]
|
|
|
|
if quote.Available < volume*price {
|
|
logrus.Fatalf("quote balance not enough: %+v", quote)
|
|
}
|
|
quote.Available -= volume * price
|
|
trader.Context.Balances[trader.Context.Market.QuoteCurrency] = quote
|
|
|
|
base := trader.Context.Balances[trader.Context.Market.BaseCurrency]
|
|
base.Available += volume
|
|
trader.Context.Balances[trader.Context.Market.BaseCurrency] = base
|
|
|
|
} else {
|
|
fee = volume * 0.001
|
|
feeCurrency = "BTC"
|
|
|
|
base := trader.Context.Balances[trader.Context.Market.BaseCurrency]
|
|
if base.Available < volume {
|
|
logrus.Fatalf("base balance not enough: %+v", base)
|
|
}
|
|
|
|
base.Available -= volume
|
|
trader.Context.Balances[trader.Context.Market.BaseCurrency] = base
|
|
|
|
quote := trader.Context.Balances[trader.Context.Market.QuoteCurrency]
|
|
quote.Available += volume * price
|
|
trader.Context.Balances[trader.Context.Market.QuoteCurrency] = quote
|
|
}
|
|
trader.Context.Unlock()
|
|
|
|
trade := types.Trade{
|
|
ID: tradeID,
|
|
Price: price,
|
|
Quantity: volume,
|
|
Side: string(order.Side),
|
|
IsBuyer: order.Side == types.SideTypeBuy,
|
|
IsMaker: false,
|
|
Time: kline.EndTime,
|
|
Symbol: trader.Context.Symbol,
|
|
Fee: fee,
|
|
FeeCurrency: feeCurrency,
|
|
}
|
|
|
|
tradeID++
|
|
trader.AverageCostCalculator.AddTrade(trade)
|
|
|
|
trader.doneOrders = append(trader.doneOrders, order)
|
|
}
|
|
|
|
// clear pending orders
|
|
trader.pendingOrders = nil
|
|
}
|
|
|
|
fmt.Print("\n")
|
|
report := trader.AverageCostCalculator.Calculate()
|
|
report.Print()
|
|
|
|
logrus.Infof("wallet balance:")
|
|
for _, balance := range trader.Context.Balances {
|
|
logrus.Infof(" %s: %f", balance.Currency, balance.Available)
|
|
}
|
|
|
|
return done, nil
|
|
}
|
|
*/
|