all: use types.LooseFormatTime to parse loose format date time string

This commit is contained in:
c9s 2022-01-25 00:24:12 +08:00
parent 5f7676f0c1
commit 007207e24f
5 changed files with 85 additions and 60 deletions

View File

@ -30,10 +30,11 @@ package backtest
import (
"context"
"fmt"
"github.com/c9s/bbgo/pkg/cache"
"sync"
"time"
"github.com/c9s/bbgo/pkg/cache"
"github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/bbgo"
@ -74,14 +75,12 @@ func NewExchange(sourceName types.ExchangeName, sourceExchange types.Exchange, s
return nil, err
}
startTime, err := config.ParseStartTime()
if err != nil {
return nil, err
}
endTime, err := config.ParseEndTime()
if err != nil {
return nil, err
var startTime, endTime time.Time
startTime = config.StartTime.Time()
if config.EndTime != nil {
endTime = config.EndTime.Time()
} else {
endTime = time.Now()
}
account := &types.Account{

View File

@ -94,16 +94,9 @@ type Session struct {
IsolatedMarginSymbol string `json:"isolatedMarginSymbol,omitempty" yaml:"isolatedMarginSymbol,omitempty"`
}
var supportedTimeFormats = []string{
time.RFC3339,
time.RFC822,
"2006-01-02T15:04:05",
"2006-01-02",
}
type Backtest struct {
StartTime string `json:"startTime" yaml:"startTime"`
EndTime string `json:"endTime" yaml:"endTime"`
StartTime types.LooseFormatTime `json:"startTime,omitempty" yaml:"startTime,omitempty"`
EndTime *types.LooseFormatTime `json:"endTime,omitempty" yaml:"endTime,omitempty"`
// RecordTrades is an option, if set to true, back-testing should record the trades into database
RecordTrades bool `json:"recordTrades,omitempty" yaml:"recordTrades,omitempty"`
@ -112,32 +105,6 @@ type Backtest struct {
Session string `json:"session" yaml:"session"`
}
func parseTimeWithFormats(strTime string, formats []string) (time.Time, error) {
for _, format := range formats {
tt, err := time.Parse(format, strTime)
if err == nil {
return tt, nil
}
}
return time.Time{}, fmt.Errorf("failed to parse time %s, valid formats are %+v", strTime, formats)
}
func (t Backtest) ParseEndTime() (time.Time, error) {
if len(t.EndTime) == 0 {
return time.Time{}, errors.New("backtest.endTime must be defined")
}
return parseTimeWithFormats(t.EndTime, supportedTimeFormats)
}
func (t Backtest) ParseStartTime() (time.Time, error) {
if len(t.StartTime) == 0 {
return time.Time{}, errors.New("backtest.startTime must be defined")
}
return parseTimeWithFormats(t.StartTime, supportedTimeFormats)
}
type BacktestAccount struct {
// TODO: MakerFeeRate should replace the commission fields
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`

View File

@ -11,16 +11,17 @@ import (
"strings"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/c9s/bbgo/pkg/accounting/pnl"
"github.com/c9s/bbgo/pkg/backtest"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/cmd/cmdutil"
"github.com/c9s/bbgo/pkg/service"
"github.com/c9s/bbgo/pkg/types"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
@ -156,24 +157,25 @@ var BacktestCmd = &cobra.Command{
return errors.New("backtest config is not defined")
}
now := time.Now()
var now = time.Now()
var startTime, endTime time.Time
startTime = userConfig.Backtest.StartTime.Time()
// set default start time to the past 6 months
if len(userConfig.Backtest.StartTime) == 0 {
userConfig.Backtest.StartTime = now.AddDate(0, -6, 0).Format("2006-01-02")
}
if len(userConfig.Backtest.EndTime) == 0 {
userConfig.Backtest.EndTime = now.Format("2006-01-02")
// userConfig.Backtest.StartTime = now.AddDate(0, -6, 0).Format("2006-01-02")
if userConfig.Backtest.EndTime != nil {
endTime = userConfig.Backtest.EndTime.Time()
} else {
endTime = now
}
_ = endTime
if len(userConfig.CrossExchangeStrategies) > 0 {
log.Warnf("backtest does not support CrossExchangeStrategy, strategies won't be added.")
}
startTime, err := userConfig.Backtest.ParseStartTime()
if err != nil {
return err
}
log.Infof("starting backtest with startTime %s", startTime.Format(time.ANSIC))
environ := bbgo.NewEnvironment()

View File

@ -6,6 +6,8 @@ import (
"fmt"
"strconv"
"time"
"github.com/c9s/bbgo/pkg/util"
)
type NanosecondTimestamp time.Time
@ -198,3 +200,43 @@ func (t *Time) Scan(src interface{}) error {
return fmt.Errorf("datatype.Time scan error, type: %T is not supported, value; %+v", src, src)
}
var looseTimeFormats = []string{
time.RFC3339,
time.RFC822,
"2006-01-02T15:04:05",
"2006-01-02",
}
// LooseFormatTime parses date time string with a wide range of formats.
type LooseFormatTime time.Time
func (t *LooseFormatTime) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err == nil {
return t.UnmarshalJSON([]byte(str))
}
var bin []byte
err := unmarshal(&bin)
if err != nil {
return err
}
return t.UnmarshalJSON(bin)
}
func (t *LooseFormatTime) UnmarshalJSON(data []byte) error {
tv, err := util.ParseTimeWithFormats(string(data), looseTimeFormats)
if err != nil {
return err
}
*t = LooseFormatTime(tv)
return nil
}
func (t LooseFormatTime) Time() time.Time {
return time.Time(t)
}

View File

@ -1,6 +1,7 @@
package util
import (
"fmt"
"math/rand"
"time"
)
@ -21,4 +22,18 @@ func Over24Hours(since time.Time) bool {
func UnixMilli() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
}
}
func ParseTimeWithFormats(strTime string, formats []string) (time.Time, error) {
for _, format := range formats {
tt, err := time.Parse(format, strTime)
if err == nil {
return tt, nil
}
}
return time.Time{}, fmt.Errorf("failed to parse time %s, valid formats are %+v", strTime, formats)
}