diff --git a/pkg/cmd/backtest.go b/pkg/cmd/backtest.go index e51d95fd9..8af5f0b1e 100644 --- a/pkg/cmd/backtest.go +++ b/pkg/cmd/backtest.go @@ -565,6 +565,7 @@ var BacktestCmd = &cobra.Command{ continue } + tradeState := sessionTradeStats[session.Name][symbol] profitFactor := tradeState.ProfitFactor winningRatio := tradeState.WinningRatio intervalProfits := tradeState.IntervalProfits[types.Interval1d] diff --git a/pkg/datasource/csvsource/tick_downloader.go b/pkg/datasource/csvsource/tick_downloader.go index c719b72d5..624c1f478 100644 --- a/pkg/datasource/csvsource/tick_downloader.go +++ b/pkg/datasource/csvsource/tick_downloader.go @@ -19,47 +19,11 @@ import ( "github.com/c9s/bbgo/pkg/types" ) -type MarketType string -type DataType string - -const ( - SPOT MarketType = "spot" - FUTURES MarketType = "futures" - TRADES DataType = "trades" - AGGTRADES DataType = "aggTrades" - // todo could be extended to: - - // LEVEL2 = 2 - // https://data.binance.vision/data/futures/um/daily/bookTicker/ADAUSDT/ADAUSDT-bookTicker-2023-11-19.zip - // update_id best_bid_price best_bid_qty best_ask_price best_ask_qty transaction_time event_time - // 3.52214E+12 0.3772 1632 0.3773 67521 1.70035E+12 1.70035E+12 - - // METRICS = 3 - // https://data.binance.vision/data/futures/um/daily/metrics/ADAUSDT/ADAUSDT-metrics-2023-11-19.zip - // create_time symbol sum_open_interest sum_open_interest_value count_toptrader_long_short_ratio sum_toptrader_long_short_ratio count_long_short_ratio sum_taker_long_short_vol_ratio - // 19/11/2023 00:00 ADAUSDT 141979878.00000000 53563193.89339590 2.33412322 1.21401178 2.46604727 0.55265805 - - // KLINES DataType = 4 - // https://public.bybit.com/kline_for_metatrader4/BNBUSDT/2021/BNBUSDT_15_2021-07-01_2021-07-31.csv.gz - // only few symbols but supported interval options 1m/ 5m/ 15m/ 30m/ 60m/ and only monthly - - // https://data.binance.vision/data/futures/um/daily/klines/1INCHBTC/30m/1INCHBTC-30m-2023-11-18.zip - // supported interval options 1s/ 1m/ 3m/ 5m/ 15m/ 30m/ 1h/ 2h/ 4h/ 6h/ 8h/ 12h/ 1d/ daily or monthly futures - - // this might be useful for backtesting against mark or index price - // especially index price can be used across exchanges - // https://data.binance.vision/data/futures/um/daily/indexPriceKlines/ADAUSDT/1h/ADAUSDT-1h-2023-11-19.zip - // https://data.binance.vision/data/futures/um/daily/markPriceKlines/ADAUSDT/1h/ADAUSDT-1h-2023-11-19.zip - - // OKex or Bybit do not support direct kLine, metrics or level2 csv download - -) - func Download( path, symbol string, exchange types.ExchangeName, - market MarketType, - granularity DataType, + market types.MarketType, + granularity types.MarketDataType, since, until time.Time, ) (err error) { for { @@ -104,8 +68,8 @@ func Download( func buildURL( exchange types.ExchangeName, symbol string, - market MarketType, - granularity DataType, + market types.MarketType, + granularity types.MarketDataType, fileName string, start time.Time, ) (url string, err error) { @@ -119,11 +83,11 @@ func buildURL( ) case types.ExchangeBinance: marketType := "spot" - if market == FUTURES { + if market == types.MarketTypeFutures { marketType = "futures/um" } dataType := "aggTrades" - if granularity == TRADES { + if granularity == types.MarketDataTypeTrades { dataType = "trades" } url = fmt.Sprintf("https://data.binance.vision/data/%s/daily/%s/%s/%s-%s-%s.zip", @@ -144,11 +108,11 @@ func buildURL( baseCoin := coins[0] quoteCoin := coins[1] marketType := "" // for spot market - if market == FUTURES { + if market == types.MarketTypeFutures { marketType = "-SWAP" } dataType := "aggtrades" - if granularity == TRADES { + if granularity == types.MarketDataTypeTrades { dataType = "trades" } url = fmt.Sprintf("https://static.okx.com/cdn/okex/traderecords/%s/daily/%s/%s-%s%s-%s-%s.zip", diff --git a/pkg/datasource/csvsource/tick_downloader_test.go b/pkg/datasource/csvsource/tick_downloader_test.go index c25ec9039..73175fa95 100644 --- a/pkg/datasource/csvsource/tick_downloader_test.go +++ b/pkg/datasource/csvsource/tick_downloader_test.go @@ -14,8 +14,8 @@ import ( type DownloadTester struct { Exchange types.ExchangeName Reader MakeCSVTickReader - Market MarketType - Granularity DataType + Market types.MarketType + Granularity types.MarketDataType Symbol string Path string } @@ -35,24 +35,24 @@ func Test_CSV_Download(t *testing.T) { { Exchange: types.ExchangeBinance, Reader: NewBinanceCSVTickReader, - Market: SPOT, - Granularity: AGGTRADES, + Market: types.MarketTypeSpot, + Granularity: types.MarketDataTypeAggTrades, Symbol: "FXSUSDT", Path: "testdata/binance/FXSUSDT", }, { Exchange: types.ExchangeBybit, Reader: NewBybitCSVTickReader, - Market: FUTURES, - Granularity: AGGTRADES, + Market: types.MarketTypeFutures, + Granularity: types.MarketDataTypeAggTrades, Symbol: "FXSUSDT", Path: "testdata/bybit/FXSUSDT", }, { Exchange: types.ExchangeOKEx, Reader: NewOKExCSVTickReader, - Market: SPOT, - Granularity: AGGTRADES, + Market: types.MarketTypeSpot, + Granularity: types.MarketDataTypeAggTrades, Symbol: "BTCUSDT", Path: "testdata/okex/BTCUSDT", }, diff --git a/pkg/datasource/csvsource/types.go b/pkg/datasource/csvsource/types.go index 432f93a7c..215889c0d 100644 --- a/pkg/datasource/csvsource/types.go +++ b/pkg/datasource/csvsource/types.go @@ -6,13 +6,13 @@ import ( ) type CsvConfig struct { - Market MarketType `json:"market"` - Granularity DataType `json:"granularity"` + Market types.MarketType `json:"market"` + Granularity types.MarketDataType `json:"granularity"` } type CsvTick struct { Exchange types.ExchangeName `json:"exchange"` - Market MarketType `json:"market"` + Market types.MarketType `json:"market"` TradeID uint64 `json:"tradeID"` Symbol string `json:"symbol"` TickDirection string `json:"tickDirection"` @@ -27,7 +27,7 @@ type CsvTick struct { func (c *CsvTick) ToGlobalTrade() (*types.Trade, error) { var isFutures bool - if c.Market == FUTURES { + if c.Market == types.MarketTypeFutures { isFutures = true } return &types.Trade{ diff --git a/pkg/service/backtest_csv.go b/pkg/service/backtest_csv.go index 3e85300e6..db8a8029e 100644 --- a/pkg/service/backtest_csv.go +++ b/pkg/service/backtest_csv.go @@ -16,14 +16,14 @@ import ( type BacktestServiceCSV struct { kLines map[types.Interval][]types.KLine path string - market csvsource.MarketType - granularity csvsource.DataType + market types.MarketType + granularity types.MarketDataType } func NewBacktestServiceCSV( path string, - market csvsource.MarketType, - granularity csvsource.DataType, + market types.MarketType, + granularity types.MarketDataType, ) BackTestable { return &BacktestServiceCSV{ kLines: make(map[types.Interval][]types.KLine), @@ -33,7 +33,9 @@ func NewBacktestServiceCSV( } } -func (s *BacktestServiceCSV) Verify(sourceExchange types.Exchange, symbols []string, startTime time.Time, endTime time.Time) error { +func (s *BacktestServiceCSV) Verify( + sourceExchange types.Exchange, symbols []string, startTime time.Time, endTime time.Time, +) error { // TODO: use isFutures here _, _, isIsolated, isolatedSymbol := exchange2.GetSessionAttributes(sourceExchange) // override symbol if isolatedSymbol is not empty @@ -55,7 +57,10 @@ func (s *BacktestServiceCSV) Verify(sourceExchange types.Exchange, symbols []str return nil } -func (s *BacktestServiceCSV) Sync(ctx context.Context, exchange types.Exchange, symbol string, intervals []types.Interval, startTime, endTime time.Time) error { +func (s *BacktestServiceCSV) Sync( + ctx context.Context, exchange types.Exchange, symbol string, intervals []types.Interval, + startTime, endTime time.Time, +) error { log.Infof("starting fresh csv sync %s %s: %s <=> %s", exchange.Name(), symbol, startTime, endTime) @@ -90,7 +95,9 @@ func (s *BacktestServiceCSV) Sync(ctx context.Context, exchange types.Exchange, } // QueryKLine queries the klines from the database -func (s *BacktestServiceCSV) QueryKLine(ex types.ExchangeName, symbol string, interval types.Interval, orderBy string, limit int) (*types.KLine, error) { +func (s *BacktestServiceCSV) QueryKLine( + ex types.ExchangeName, symbol string, interval types.Interval, orderBy string, limit int, +) (*types.KLine, error) { log.Infof("querying last kline exchange = %s AND symbol = %s AND interval = %s", ex, symbol, interval) if _, ok := s.kLines[interval]; !ok || len(s.kLines[interval]) == 0 { return nil, errors.New("interval not initialized") @@ -99,7 +106,9 @@ func (s *BacktestServiceCSV) QueryKLine(ex types.ExchangeName, symbol string, in } // QueryKLinesForward is used for querying klines to back-testing -func (s *BacktestServiceCSV) QueryKLinesForward(exchange types.ExchangeName, symbol string, interval types.Interval, startTime time.Time, limit int) ([]types.KLine, error) { +func (s *BacktestServiceCSV) QueryKLinesForward( + exchange types.ExchangeName, symbol string, interval types.Interval, startTime time.Time, limit int, +) ([]types.KLine, error) { // Sample implementation (modify as needed): var result []types.KLine @@ -122,7 +131,9 @@ func (s *BacktestServiceCSV) QueryKLinesForward(exchange types.ExchangeName, sym return result, nil } -func (s *BacktestServiceCSV) QueryKLinesBackward(exchange types.ExchangeName, symbol string, interval types.Interval, endTime time.Time, limit int) ([]types.KLine, error) { +func (s *BacktestServiceCSV) QueryKLinesBackward( + exchange types.ExchangeName, symbol string, interval types.Interval, endTime time.Time, limit int, +) ([]types.KLine, error) { var result []types.KLine // Access klines data based on interval @@ -146,7 +157,9 @@ func (s *BacktestServiceCSV) QueryKLinesBackward(exchange types.ExchangeName, sy return result, nil } -func (s *BacktestServiceCSV) QueryKLinesCh(since, until time.Time, exchange types.Exchange, symbols []string, intervals []types.Interval) (chan types.KLine, chan error) { +func (s *BacktestServiceCSV) QueryKLinesCh( + since, until time.Time, exchange types.Exchange, symbols []string, intervals []types.Interval, +) (chan types.KLine, chan error) { if len(symbols) == 0 { return returnError(errors.Errorf("symbols is empty when querying kline, please check your strategy setting. ")) } diff --git a/pkg/types/csvsource.go b/pkg/types/csvsource.go new file mode 100644 index 000000000..2e61fd81c --- /dev/null +++ b/pkg/types/csvsource.go @@ -0,0 +1,40 @@ +package types + +type MarketType string + +const ( + MarketTypeSpot MarketType = "spot" + MarketTypeFutures MarketType = "futures" +) + +type MarketDataType string + +const ( + MarketDataTypeTrades MarketDataType = "trades" + MarketDataTypeAggTrades MarketDataType = "aggTrades" + // TODO: could be extended to the following: + + // LEVEL2 = 2 + // https://data.binance.vision/data/futures/um/daily/bookTicker/ADAUSDT/ADAUSDT-bookTicker-2023-11-19.zip + // update_id best_bid_price best_bid_qty best_ask_price best_ask_qty transaction_time event_time + // 3.52214E+12 0.3772 1632 0.3773 67521 1.70035E+12 1.70035E+12 + + // METRICS = 3 + // https://data.binance.vision/data/futures/um/daily/metrics/ADAUSDT/ADAUSDT-metrics-2023-11-19.zip + // create_time symbol sum_open_interest sum_open_interest_value count_toptrader_long_short_ratio sum_toptrader_long_short_ratio count_long_short_ratio sum_taker_long_short_vol_ratio + // 19/11/2023 00:00 ADAUSDT 141979878.00000000 53563193.89339590 2.33412322 1.21401178 2.46604727 0.55265805 + + // KLINES MarketDataType = 4 + // https://public.bybit.com/kline_for_metatrader4/BNBUSDT/2021/BNBUSDT_15_2021-07-01_2021-07-31.csv.gz + // only few symbols but supported interval options 1m/ 5m/ 15m/ 30m/ 60m/ and only monthly + + // https://data.binance.vision/data/futures/um/daily/klines/1INCHBTC/30m/1INCHBTC-30m-2023-11-18.zip + // supported interval options 1s/ 1m/ 3m/ 5m/ 15m/ 30m/ 1h/ 2h/ 4h/ 6h/ 8h/ 12h/ 1d/ daily or monthly futures + + // this might be useful for backtesting against mark or index price + // especially index price can be used across exchanges + // https://data.binance.vision/data/futures/um/daily/indexPriceKlines/ADAUSDT/1h/ADAUSDT-1h-2023-11-19.zip + // https://data.binance.vision/data/futures/um/daily/markPriceKlines/ADAUSDT/1h/ADAUSDT-1h-2023-11-19.zip + + // OKex or Bybit do not support direct kLine, metrics or level2 csv download +)