fix: legacy fixedpoint inf handling, refactor backtest kline consuming

This commit is contained in:
zenix 2022-08-30 21:02:21 +09:00
parent 20ee3fdfbb
commit c2d5a5961f
5 changed files with 66 additions and 33 deletions

View File

@ -70,6 +70,8 @@ type Exchange struct {
matchingBooksMutex sync.Mutex matchingBooksMutex sync.Mutex
markets types.MarketMap markets types.MarketMap
Src *ExchangeDataSource
} }
func NewExchange(sourceName types.ExchangeName, sourceExchange types.Exchange, srv *service.BacktestService, config *bbgo.Backtest) (*Exchange, error) { func NewExchange(sourceName types.ExchangeName, sourceExchange types.Exchange, srv *service.BacktestService, config *bbgo.Backtest) (*Exchange, error) {
@ -362,36 +364,35 @@ func (e *Exchange) SubscribeMarketData(startTime, endTime time.Time, extraInterv
return klineC, nil return klineC, nil
} }
func (e *Exchange) ConsumeKLine(k types.KLine, handlers []func(types.KLine, *ExchangeDataSource), src *ExchangeDataSource) { func (e *Exchange) ConsumeKLine(k types.KLine) {
matching, ok := e.matchingBook(k.Symbol) matching, ok := e.matchingBook(k.Symbol)
if !ok { if !ok {
log.Errorf("matching book of %s is not initialized", k.Symbol) log.Errorf("matching book of %s is not initialized", k.Symbol)
return return
} }
if matching.ParamCache == nil { if matching.klineCache == nil {
matching.ParamCache = make(map[types.Interval]Param) matching.klineCache = make(map[types.Interval]types.KLine)
} }
_, ok = matching.ParamCache[k.Interval]
kline1m, ok := matching.klineCache[k.Interval]
if ok { // pop out all the old if ok { // pop out all the old
for _, param := range matching.ParamCache { if kline1m.Interval != types.Interval1m {
if param.kline.Interval == types.Interval1m { panic("expect 1m kline, get " + kline1m.Interval.String())
// here we generate trades and order updates }
matching.processKLine(param.kline) // here we generate trades and order updates
matching.NextKLine = &k matching.processKLine(kline1m)
} matching.NextKLine = &k
for _, kline := range matching.klineCache {
// log.Errorf("kline %v, next %v", param.kline, matching.NextKLine) // log.Errorf("kline %v, next %v", param.kline, matching.NextKLine)
e.MarketDataStream.EmitKLineClosed(param.kline) e.MarketDataStream.EmitKLineClosed(kline)
for _, h := range param.callbacks { for _, h := range e.Src.Callbacks {
h(param.kline, param.src) h(kline, e.Src)
} }
} }
matching.ParamCache = make(map[types.Interval]Param) // reset the paramcache
} matching.klineCache = make(map[types.Interval]types.KLine)
matching.ParamCache[k.Interval] = Param{
callbacks: handlers,
src: src,
kline: k,
} }
matching.klineCache[k.Interval] = k
} }
func (e *Exchange) CloseMarketData() error { func (e *Exchange) CloseMarketData() error {

View File

@ -6,7 +6,8 @@ import (
) )
type ExchangeDataSource struct { type ExchangeDataSource struct {
C chan types.KLine C chan types.KLine
Exchange *Exchange Exchange *Exchange
Session *bbgo.ExchangeSession Session *bbgo.ExchangeSession
Callbacks []func(types.KLine, *ExchangeDataSource)
} }

View File

@ -47,12 +47,6 @@ func init() {
} }
} }
type Param struct {
callbacks []func(types.KLine, *ExchangeDataSource)
kline types.KLine
src *ExchangeDataSource
}
// SimplePriceMatching implements a simple kline data driven matching engine for backtest // SimplePriceMatching implements a simple kline data driven matching engine for backtest
//go:generate callbackgen -type SimplePriceMatching //go:generate callbackgen -type SimplePriceMatching
type SimplePriceMatching struct { type SimplePriceMatching struct {
@ -64,7 +58,7 @@ type SimplePriceMatching struct {
askOrders []types.Order askOrders []types.Order
closedOrders map[uint64]types.Order closedOrders map[uint64]types.Order
ParamCache map[types.Interval]Param klineCache map[types.Interval]types.KLine
LastPrice fixedpoint.Value LastPrice fixedpoint.Value
LastKLine types.KLine LastKLine types.KLine
NextKLine *types.KLine NextKLine *types.KLine

View File

@ -438,6 +438,9 @@ var BacktestCmd = &cobra.Command{
} }
runCtx, cancelRun := context.WithCancel(ctx) runCtx, cancelRun := context.WithCancel(ctx)
for _, exK := range exchangeSources {
exK.Callbacks = kLineHandlers
}
go func() { go func() {
defer cancelRun() defer cancelRun()
@ -446,7 +449,7 @@ var BacktestCmd = &cobra.Command{
if numOfExchangeSources == 1 { if numOfExchangeSources == 1 {
exSource := exchangeSources[0] exSource := exchangeSources[0]
for k := range exSource.C { for k := range exSource.C {
exSource.Exchange.ConsumeKLine(k, kLineHandlers, &exSource) exSource.Exchange.ConsumeKLine(k)
} }
if err := exSource.Exchange.CloseMarketData(); err != nil { if err := exSource.Exchange.CloseMarketData(); err != nil {
@ -467,7 +470,7 @@ var BacktestCmd = &cobra.Command{
break RunMultiExchangeData break RunMultiExchangeData
} }
exK.Exchange.ConsumeKLine(k, kLineHandlers, &exK) exK.Exchange.ConsumeKLine(k)
} }
} }
}() }()
@ -694,11 +697,13 @@ func toExchangeSources(sessions map[string]*bbgo.ExchangeSession, startTime, end
} }
sessionCopy := session sessionCopy := session
exchangeSources = append(exchangeSources, backtest.ExchangeDataSource{ src := backtest.ExchangeDataSource{
C: c, C: c,
Exchange: backtestEx, Exchange: backtestEx,
Session: sessionCopy, Session: sessionCopy,
}) }
backtestEx.Src = &src
exchangeSources = append(exchangeSources, src)
} }
return exchangeSources, nil return exchangeSources, nil
} }

View File

@ -23,6 +23,8 @@ type Value int64
const Zero = Value(0) const Zero = Value(0)
const One = Value(1e8) const One = Value(1e8)
const NegOne = Value(-1e8) const NegOne = Value(-1e8)
const PosInf = Value(math.MaxInt64)
const NegInf = Value(math.MinInt64)
type RoundingMode int type RoundingMode int
@ -81,6 +83,11 @@ func (v *Value) Scan(src interface{}) error {
} }
func (v Value) Float64() float64 { func (v Value) Float64() float64 {
if v == PosInf {
return math.Inf(1)
} else if v == NegInf {
return math.Inf(-1)
}
return float64(v) / DefaultPow return float64(v) / DefaultPow
} }
@ -92,10 +99,20 @@ func (v Value) Abs() Value {
} }
func (v Value) String() string { func (v Value) String() string {
if v == PosInf {
return "inf"
} else if v == NegInf {
return "-inf"
}
return strconv.FormatFloat(float64(v)/DefaultPow, 'f', -1, 64) return strconv.FormatFloat(float64(v)/DefaultPow, 'f', -1, 64)
} }
func (v Value) FormatString(prec int) string { func (v Value) FormatString(prec int) string {
if v == PosInf {
return "inf"
} else if v == NegInf {
return "-inf"
}
pow := math.Pow10(prec) pow := math.Pow10(prec)
return strconv.FormatFloat( return strconv.FormatFloat(
math.Trunc(float64(v)/DefaultPow*pow)/pow, 'f', prec, 64) math.Trunc(float64(v)/DefaultPow*pow)/pow, 'f', prec, 64)
@ -105,6 +122,11 @@ func (v Value) Percentage() string {
if v == 0 { if v == 0 {
return "0" return "0"
} }
if v == PosInf {
return "inf%"
} else if v == NegInf {
return "-inf%"
}
return strconv.FormatFloat(float64(v)/DefaultPow*100., 'f', -1, 64) + "%" return strconv.FormatFloat(float64(v)/DefaultPow*100., 'f', -1, 64) + "%"
} }
@ -112,6 +134,11 @@ func (v Value) FormatPercentage(prec int) string {
if v == 0 { if v == 0 {
return "0" return "0"
} }
if v == PosInf {
return "inf%"
} else if v == NegInf {
return "-inf%"
}
pow := math.Pow10(prec) pow := math.Pow10(prec)
result := strconv.FormatFloat( result := strconv.FormatFloat(
math.Trunc(float64(v)/DefaultPow*pow*100.)/pow, 'f', prec, 64) math.Trunc(float64(v)/DefaultPow*pow*100.)/pow, 'f', prec, 64)
@ -407,6 +434,11 @@ func Must(v Value, err error) Value {
} }
func NewFromFloat(val float64) Value { func NewFromFloat(val float64) Value {
if math.IsInf(val, 1) {
return PosInf
} else if math.IsInf(val, -1) {
return NegInf
}
return Value(int64(math.Trunc(val * DefaultPow))) return Value(int64(math.Trunc(val * DefaultPow)))
} }