mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 06:53:52 +00:00
fix: legacy fixedpoint inf handling, refactor backtest kline consuming
This commit is contained in:
parent
20ee3fdfbb
commit
c2d5a5961f
|
@ -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
|
// here we generate trades and order updates
|
||||||
matching.processKLine(param.kline)
|
matching.processKLine(kline1m)
|
||||||
matching.NextKLine = &k
|
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 {
|
||||||
|
|
|
@ -9,4 +9,5 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user