add exchange field in the table so that we can reuse the kline objects for backtest

This commit is contained in:
c9s 2020-11-08 12:13:34 +08:00
parent 4b0bab31fb
commit 6bd3573287
6 changed files with 83 additions and 12 deletions

View File

@ -36,6 +36,22 @@ riskControls:
minBaseAssetBalance: 0.1
maxOrderAmount: 100.0
backtest:
# for testing max draw down (MDD) at 03-12
# see here for more details
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
startTime: "2020-01-01"
symbols:
- BTCUSDT
account:
makerCommission: 15
takerCommission: 15
buyerCommission: 0
sellerCommission: 0
balances:
BTC: 0.1
USDT: 10000.0
exchangeStrategies:
- on: binance
buyandhold:

View File

@ -43,6 +43,22 @@ riskControls:
minBaseAssetBalance: 0.0
maxOrderAmount: 200.0
backtest:
# for testing max draw down (MDD) at 03-12
# see here for more details
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
startTime: "2020-01-01"
symbols:
- BTCUSDT
account:
makerCommission: 15
takerCommission: 15
buyerCommission: 0
sellerCommission: 0
balances:
BTC: 0.1
USDT: 10000.0
exchangeStrategies:
- on: max
grid:

View File

@ -2,7 +2,7 @@
CREATE TABLE `klines`
(
`gid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`exchange` VARCHAR(10) NOT NULL,
`start_time` DATETIME(3) NOT NULL,
`end_time` DATETIME(3) NOT NULL,
`interval` VARCHAR(3) NOT NULL,
@ -26,7 +26,7 @@ CREATE TABLE `binance_klines` LIKE `klines`;
CREATE TABLE `max_klines` LIKE `klines`;
-- +goose Down
DROP INDEX klines_start_time_symbol_interval ON `klines`;
DROP INDEX `klines_end_time_symbol_interval` ON `klines`;
DROP TABLE `binance_klines`;
DROP TABLE `okex_klines`;
DROP TABLE `max_klines`;

View File

@ -14,7 +14,7 @@ import (
)
type Exchange struct {
sourceExchange types.ExchangeName
sourceName types.ExchangeName
publicExchange types.Exchange
srv *service.BacktestService
startTime time.Time
@ -30,8 +30,8 @@ type Exchange struct {
doneC chan struct{}
}
func NewExchange(sourceExchange types.ExchangeName, srv *service.BacktestService, config *bbgo.Backtest) *Exchange {
ex, err := newPublicExchange(sourceExchange)
func NewExchange(sourceName types.ExchangeName, srv *service.BacktestService, config *bbgo.Backtest) *Exchange {
ex, err := newPublicExchange(sourceName)
if err != nil {
panic(err)
}
@ -55,7 +55,7 @@ func NewExchange(sourceExchange types.ExchangeName, srv *service.BacktestService
account.UpdateBalances(balances)
e := &Exchange{
sourceExchange: sourceExchange,
sourceName: sourceName,
publicExchange: ex,
srv: srv,
config: config,
@ -171,7 +171,14 @@ func (e *Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap,
}
func (e Exchange) QueryKLines(ctx context.Context, symbol string, interval types.Interval, options types.KLineQueryOptions) ([]types.KLine, error) {
return e.publicExchange.QueryKLines(ctx, symbol, interval, options)
if options.EndTime != nil {
return e.srv.QueryKLinesBackward(e.sourceName, symbol, interval, *options.EndTime)
}
if options.StartTime != nil {
return e.srv.QueryKLinesForward(e.sourceName, symbol, interval, *options.StartTime)
}
return nil, errors.New("endTime or startTime can not be nil")
}
func (e Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) ([]types.Trade, error) {

View File

@ -83,6 +83,38 @@ func (s *BacktestService) QueryLast(ex types.ExchangeName, symbol string, interv
return nil, rows.Err()
}
func (s *BacktestService) QueryKLinesForward(exchange types.ExchangeName, symbol string, interval types.Interval, startTime time.Time) ([]types.KLine, error) {
sql := "SELECT * FROM `binance_klines` WHERE `end_time` >= :startTime AND `symbol` = :symbol AND `interval` = :interval ORDER BY end_time ASC"
sql = strings.ReplaceAll(sql, "binance_klines", exchange.String()+"_klines")
rows, err := s.DB.NamedQuery(sql, map[string]interface{}{
"startTime": startTime,
"symbol": symbol,
"interval": interval,
})
if err != nil {
return nil, err
}
return s.scanRows(rows)
}
func (s *BacktestService) QueryKLinesBackward(exchange types.ExchangeName, symbol string, interval types.Interval, endTime time.Time) ([]types.KLine, error) {
sql := "SELECT * FROM `binance_klines` WHERE `end_time` <= :endTime AND `symbol` = :symbol AND `interval` = :interval ORDER BY end_time ASC"
sql = strings.ReplaceAll(sql, "binance_klines", exchange.String()+"_klines")
rows, err := s.DB.NamedQuery(sql, map[string]interface{}{
"endTime": endTime,
"symbol": symbol,
"interval": interval,
})
if err != nil {
return nil, err
}
return s.scanRows(rows)
}
func (s *BacktestService) QueryKLinesCh(since time.Time, exchange types.Exchange, symbols []string, intervals []types.Interval) (chan types.KLine, chan error) {
sql := "SELECT * FROM `binance_klines` WHERE `end_time` >= :since AND `symbol` IN (:symbols) AND `interval` IN (:intervals) ORDER BY end_time ASC"
sql = strings.ReplaceAll(sql, "binance_klines", exchange.Name().String()+"_klines")
@ -160,8 +192,8 @@ func (s *BacktestService) Insert(kline types.KLine) error {
return errors.New("kline.Exchange field should not be empty")
}
sql := "INSERT INTO `binance_klines` (`start_time`, `end_time`, `symbol`, `interval`, `open`, `high`, `low`, `close`, `closed`, `volume`)" +
"VALUES (:start_time, :end_time, :symbol, :interval, :open, :high, :low, :close, :closed, :volume)"
sql := "INSERT INTO `binance_klines` (`exchange`, `start_time`, `end_time`, `symbol`, `interval`, `open`, `high`, `low`, `close`, `closed`, `volume`)" +
"VALUES (:exchange, :start_time, :end_time, :symbol, :interval, :open, :high, :low, :close, :closed, :volume)"
sql = strings.ReplaceAll(sql, "binance_klines", kline.Exchange+"_klines")
_, err := s.DB.NamedExec(sql, kline)

View File

@ -46,7 +46,7 @@ type KLineQueryOptions struct {
// KLine uses binance's kline as the standard structure
type KLine struct {
GID uint64 `json:"gid" db:"gid"`
Exchange string `json:"exchange"`
Exchange string `json:"exchange" db:"exchange"`
Symbol string `json:"symbol" db:"symbol"`
@ -62,9 +62,9 @@ type KLine struct {
Volume float64 `json:"volume" db:"volume"`
QuoteVolume float64 `json:"quoteVolume" db:"quote_volume"`
LastTradeID uint64 `json:"lastTradeID" db:"last_trade_id"`
LastTradeID uint64 `json:"lastTradeID" db:"last_trade_id"`
NumberOfTrades uint64 `json:"numberOfTrades" db:"num_trades"`
Closed bool `json:"closed" db:"closed"`
Closed bool `json:"closed" db:"closed"`
}
func (k KLine) GetStartTime() time.Time {