@ -21,10 +21,10 @@ type SigStrategyBacktester struct {
sigStrategyType strategy . SigStrategyType
sigStrategy strategy . ISigStrategy
indicatorReg * indicator . IndicatorRegistry
exchangeService Client pb . ExchangeServiceClient
exchangeClient pb . ExchangeServiceClient
// 回测过程中订阅k线
intervalSubscribe map [ types . Interval ] [ ] func ( k types . Kline )
intervalSubscribe map [ types . Interval ] [ ] func ( interval types . Interval , k * types . Kline ) ( err error )
}
func NewSigStrategyBacktester (
@ -37,13 +37,13 @@ func NewSigStrategyBacktester(
sigStrategyType : sigStrategyType ,
sigStrategy : sigStrategy ,
indicatorReg : indicatorReg ,
exchangeService Client : exchangeServiceClient ,
intervalSubscribe : make ( map [ types . Interval ] [ ] func ( k types . Kline ) ) ,
exchangeClient : exchangeServiceClient ,
intervalSubscribe : make ( map [ types . Interval ] [ ] func ( interval types . Interval , k * types . Kline ) ( err error ) ) ,
}
}
// SubKline 在回测过程中订阅k线
func ( b * SigStrategyBacktester ) SubKline ( interval types . Interval , recv func ( k types . Kline ) ) {
func ( b * SigStrategyBacktester ) SubKline ( interval types . Interval , recv func ( interval types . Interval , k * types . Kline ) ( err error ) ) {
b . intervalSubscribe [ interval ] = append ( b . intervalSubscribe [ interval ] , recv )
}
@ -66,14 +66,19 @@ func (b *SigStrategyBacktester) singleStrategySeries(ctx context.Context, sigStr
kSeries := sig . NewKlineSeries ( sr . Exchange , sr . InstId , interval )
indicatorContext := sig . NewIndicatorContext ( kSeries )
strategyContext := sig . NewStrategyContext ( indicatorContext , b . indicatorReg )
requiredSeries := int ( sigStrategy . RequiredSeries ( ) )
sr . WindowExtra = uint32 ( requiredSeries - 1 )
err = b . fetchHistoryKlineSeries ( ctx , sr , func ( k * types . Kline ) ( err error ) {
if lastTs , serial := kSeries . Update ( k ) ; ! serial {
err = fmt . Errorf ( "kline not series: %s(%s), interval=%s, lastTs=%d" , sr . InstId , sr . Exchange , interval , lastTs )
requiredIntervalSeries := types . NewIntervalState [ int16 ] ( )
requiredIntervalSeries . Set ( interval , int16 ( max ( 1 , requiredSeries ) ) )
intervalKlineSeries := types . NewIntervalState [ * sig . KlineSeries ] ( )
intervalKlineSeries . Set ( interval , kSeries )
err = b . multiIntervalSeries ( ctx , sr , requiredIntervalSeries , intervalKlineSeries , func ( driver bool , interval types . Interval , k * types . Kline ) ( err error ) {
if ! driver {
return
}
kSeries := intervalKlineSeries . Get ( interval )
if kSeries . Length ( ) < requiredSeries {
return
}
@ -88,55 +93,91 @@ func (b *SigStrategyBacktester) singleStrategySeries(ctx context.Context, sigStr
return
}
// multiIntervalSeries 多周期k线数据拉取
func ( b * SigStrategyBacktester ) multiIntervalSeries ( sr * pb . SeriesRange , otherInterval [ ] types . Interval ,
revcFn func ( driver bool , interval types . Interval , k * types . Kline ) ( err error ) ) ( err error ) {
// intervalStrategySeries 多周期策略
func ( b * SigStrategyBacktester ) intervalStrategySeries ( ctx context . Context , intervalSigStrategy strategy . IIntervalSigStrategy , sr * pb . SeriesRange , recvSignal func ( sigSide types . Side , k types . Kline ) ( err error ) ) ( err error ) {
// 各周期所需k线数量
requiredIntervalSeries := intervalSigStrategy . RequiredIntervalSeries ( )
// 各周期 series
intervalKlineSeries := types . NewIntervalState [ * sig . KlineSeries ] ( )
// 策略上下文
intervalStrategyContext := sig . NewIntervalStrategyContext ( intervalKlineSeries , b . indicatorReg )
err = b . multiIntervalSeries ( ctx , sr , requiredIntervalSeries , intervalKlineSeries , func ( driver bool , interval types . Interval , k * types . Kline ) ( err error ) {
if ! driver {
return
}
update := true
requiredIntervalSeries . Range ( func ( interval types . Interval , require int16 ) {
if update && require > 0 {
series := intervalKlineSeries . Get ( interval )
update = series . Length ( ) >= int ( require )
}
} )
if ! update {
return
}
sigSide := intervalSigStrategy . Update ( intervalStrategyContext )
if sigSide . IsValid ( ) {
if err = recvSignal ( sigSide , * k ) ; err != nil {
return
}
}
return
} )
return
}
// intervalStrategySeries 多周期策略
func ( b * SigStrategyBacktester ) intervalStrategySeries ( ctx context . Context , intervalSigStrategy strategy . IIntervalSigStrategy , sr * pb . SeriesRange , recvSignal func ( sigSide types . Side , k types . Kline ) ( err error ) ) ( err error ) {
// multiIntervalSeries 多周期k线数据拉取
// intervalKlineSeries: 各周期 series 从外部传入方便外部处理逻辑
func ( b * SigStrategyBacktester ) multiIntervalSeries ( ctx context . Context , sr * pb . SeriesRange ,
requiredIntervalSeries * types . IntervalState [ int16 ] ,
intervalKlineSeries * types . IntervalState [ * sig . KlineSeries ] ,
recvFn func ( driver bool , interval types . Interval , k * types . Kline ) ( err error ) ) ( err error ) {
// 查询主周期时间范围
rsp , err := b . exchangeClient . SeriesRange ( ctx , & pb . ReqSeriesRange { Series : sr } )
if err != nil {
return
}
before , after := rsp . Before , rsp . After
driverInterval := types . Interval ( sr . Interval )
driverIntervalAdder := types . SupportedIntervals [ driverInterval ]
// 各周期所需k线数量
requiredIntervalSeries := intervalSigStrategy . RequiredIntervalSeries ( )
// 驱动周期外其他周期
otherIntervals := make ( [ ] types . Interval , 0 , 3 )
requiredIntervalSeries . Range ( func ( interval types . Interval , series int16 ) {
if series > 0 {
var otherIntervals [ ] types . Interval
// 运行时周期
requiredIntervalSeries . Range ( func ( interval types . Interval , window int16 ) {
if interval != driverInterval && window > 0 {
otherIntervals = append ( otherIntervals , interval )
}
} )
// 运行时订阅周期
for interval := range b . intervalSubscribe {
if collect . NotIn ( interval , otherIntervals ... ) {
if interval != driverInterval && collect . NotIn ( interval , otherIntervals ... ) {
otherIntervals = append ( otherIntervals , interval )
}
}
otherIntervals = collect . Filter ( otherIntervals , func ( _ int , interval types . Interval ) bool { return interval != driverInterval } )
// otherIntervals = collect.Filter(otherIntervals, func(_ int, interval types.Interval) bool { return interval != driverInterval })
// 通知其他周期更新的channel
otherIntervalCh := types . NewIntervalState [ [ ] chan int64 ] ( )
// otherIntervalDstCh := types.NewIntervalState[chan int64]()
otherIntervalSyncCh := types . NewIntervalState [ chan int64 ] ( )
for _ , interval := range otherIntervals {
otherIntervalCh . Set ( interval , [ ] chan int64 { make ( chan int64 ) , make ( chan int64 ) } )
otherIntervalSync Ch . Set ( interval , make ( chan int64 ) )
}
// 各周期 series
intervalKlineSeries := types . NewIntervalState [ * sig . KlineSeries ] ( )
// 其他周期数据拉取
stopCh := make ( chan struct { } )
for _ , interval := range otherIntervals {
kSeries := sig . NewKlineSeries ( sr . Exchange , sr . InstId , interval )
kSeries := intervalKlineSeries . Get ( interval )
if kSeries == nil {
kSeries = sig . NewKlineSeries ( sr . Exchange , sr . InstId , interval )
intervalKlineSeries . Set ( interval , kSeries )
}
go func ( interval types . Interval , kSeries * sig . KlineSeries ) {
ch := otherIntervalCh . Get ( interval )
srcCh := ch [ 0 ]
dstCh := ch [ 1 ]
syncCh := otherIntervalSyncCh . Get ( interval )
intervalAdder := types . SupportedIntervals [ interval ]
driverTs := int64 ( 0 )
isr := & pb . SeriesRange { Exchange : sr . Exchange , InstId : sr . InstId , Before : sr . Before , After : sr . After , Count : sr . Count , Open : sr . Open , Live : sr . Live , Desc : sr . Desc , Limit : sr . Limit }
isr := & pb . SeriesRange { Exchange : sr . Exchange , InstId : sr . InstId , Open : false , Live : sr . Live , Desc : sr . Desc }
isr . Before = intervalAdder ( before , - 1 )
isr . After = after
isr . Interval = string ( interval )
isr . WindowExtra = uint32 ( requiredIntervalSeries . Get ( interval ) - 1 )
isr . WindowExtra = uint32 ( max ( 0 , requiredIntervalSeries . Get ( interval ) - 1 ) )
err1 := b . fetchHistoryKlineSeries ( ctx , isr , func ( k * types . Kline ) ( err error ) {
closeTs := intervalAdder ( k . Ts , 1 )
// 与驱动周期series保持同步更新
@ -144,12 +185,14 @@ func (b *SigStrategyBacktester) intervalStrategySeries(ctx context.Context, inte
waitLoop :
for {
if driverTs != 0 {
dst Ch <- 0 // 通知更新完毕
sync Ch <- 0 // 通知更新完毕
}
select {
case <- ctx . Done ( ) :
return fmt . Errorf ( "kline series canceled" )
case <- stopCh :
return io . EOF
case driverTs = <- srcCh :
case driverTs = <- syncCh : // 等待主周期通知更新
if closeTs <= driverTs {
break waitLoop
}
@ -160,81 +203,82 @@ func (b *SigStrategyBacktester) intervalStrategySeries(ctx context.Context, inte
err = fmt . Errorf ( "kline not series: %s(%s), interval=%s, lastTs=%d" , sr . InstId , sr . Exchange , interval , lastTs )
return
}
// 回调周期订阅
if subs , ok := b . intervalSubscribe [ interval ] ; ok {
for _ , subFn := range subs {
if err = subFn ( interval , k ) ; err != nil {
return
}
}
}
// 运行时周期
if requiredIntervalSeries . Get ( interval ) > 0 {
return recvFn ( false , interval , k )
}
return
} )
if err1 == nil {
otherIntervalCh . Set ( interval , nil ) // 该周期数据拉取结束
dstCh <- 0 // 通知更新完毕
otherIntervalSync Ch . Set ( interval , nil ) // 该周期数据拉取结束
sync Ch <- 0 // 通知更新完毕
} else if err1 != io . EOF {
zlog . Errorf ( "fetch history interval error: inst=%s(%s) interval=%s, err=%v" , sr . InstId , sr . Exchange , interval , err1 )
zlog . Errorf ( "fetch interval history error: inst=%s(%s) interval=%s, err=%v" , sr . InstId , sr . Exchange , interval , err1 )
err = err1
close ( stopCh )
}
} ( interval , kSeries )
}
// 策略上下文
intervalStrategyContext := sig . NewIntervalStrategyContext ( intervalKlineSeries , b . indicatorReg )
// 驱动周期数据拉取
driverSeries := sig . NewKlineSeries ( sr . Exchange , sr . InstId , driverInterval )
driverSeries := intervalKlineSeries . Get ( driverInterval )
if driverSeries == nil {
driverSeries = sig . NewKlineSeries ( sr . Exchange , sr . InstId , driverInterval )
intervalKlineSeries . Set ( driverInterval , driverSeries )
sr . WindowExtra = uint32 ( requiredIntervalSeries . Get ( driverInterval ) - 1 )
err = b . fetchHistoryKlineSeries ( ctx , sr , func ( k * types . Kline ) ( err error ) {
driverTS := driverIntervalAdder ( k . Ts , 1 )
otherIntervalCh . Range ( func ( interval types . Interval , ch [ ] chan int64 ) {
if len ( ch ) == 2 {
// 通知其它周期先更新
select {
case <- stopCh :
err = io . EOF
}
sr . WindowExtra = max ( sr . WindowExtra , uint32 ( max ( 0 , requiredIntervalSeries . Get ( driverInterval ) - 1 ) ) )
err0 := b . fetchHistoryKlineSeries ( ctx , sr , func ( k * types . Kline ) ( err error ) {
if lastTs , serial := driverSeries . Update ( k ) ; ! serial {
err = fmt . Errorf ( "kline not series: %s(%s), interval=%s, lastTs=%d" , sr . InstId , sr . Exchange , driverInterval , lastTs )
return
case ch [ 0 ] <- driverTS :
// 等待其它周期更新完毕
}
driverTS := driverIntervalAdder ( k . Ts , 1 )
otherIntervalSyncCh . RangeBreak ( func ( interval types . Interval , syncCh chan int64 ) bool {
if syncCh != nil {
syncCh <- driverTS // 通知其他周期更新到主周期时间
select {
case <- syncCh : // 等待其它周期更新完毕
case <- ctx . Done ( ) :
err = fmt . Errorf ( "kline series canceled" )
return false
case <- stopCh :
err = io . EOF
return
case <- ch [ 1 ] :
}
return false
}
}
return true
} )
if err != nil {
return
}
// zlog.Debugf("driver series update: %s, %d", driverInterval, driverTS)
if lastTs , serial := driverSeries . Update ( k ) ; ! serial {
err = fmt . Errorf ( "kline not series: %s(%s), interval=%s, lastTs=%d" , sr . InstId , sr . Exchange , driverInterval , lastTs )
return
}
// 检查满足策略执行条件
update := true
requiredIntervalSeries . Range ( func ( interval types . Interval , require int16 ) {
if update && require > 0 {
series := intervalKlineSeries . Get ( interval )
update = series . Length ( ) >= int ( require )
}
} )
if ! update {
return
}
// intervalKlineSeries.Range(func(interval types.Interval, v *sig.KlineSeries) {
// if v != nil {
// zlog.Debugf("strategy update: interval series %s, %d", interval, v.Length())
// zlog.Debugf("interval series update: %s, %d", interval, v.Length())
// }
// })
sigSide := intervalSigStrategy . Update ( intervalStrategyContext )
if sigSide . IsValid ( ) {
if err = recvSignal ( sigSide , * k ) ; err != nil {
// 回调周期订阅
if subs , ok := b . intervalSubscribe [ driverInterval ] ; ok {
for _ , subFn := range subs {
if err = subFn ( driverInterval , k ) ; err != nil {
return
}
}
return
}
return recvFn ( true , driverInterval , k )
} )
if err != io . EOF {
if err0 != io . EOF {
close ( stopCh )
if err0 != nil {
err = err0
zlog . Errorf ( "fetch driver interval history error: inst=%s(%s) interval=%s, err=%v" , sr . InstId , sr . Exchange , driverInterval , err0 )
}
}
return
}
@ -243,13 +287,14 @@ func (b *SigStrategyBacktester) intervalStrategySeries(ctx context.Context, inte
func ( b * SigStrategyBacktester ) fetchHistoryKlineSeries ( ctx context . Context , sr * pb . SeriesRange , recvFn func ( k * types . Kline ) error ) ( err error ) {
// fetch history klines via stream
req := & pb . ReqHistoryKlineStream { Series : sr }
stream , err := b . exchangeService Client . HistoryKlineStream ( ctx , req , grpc . UseCompressor ( "snappy" ) )
stream , err := b . exchangeClient . HistoryKlineStream ( ctx , req , grpc . UseCompressor ( "snappy" ) )
if err != nil {
return
}
var msg * pb . RspHistoryKlineStream
recvTimes , recvTotal := 0 , 0
watch := times . NewWatch ( )
recvLoop :
for {
select {
case <- ctx . Done ( ) :
@ -263,7 +308,7 @@ func (b *SigStrategyBacktester) fetchHistoryKlineSeries(ctx context.Context, sr
break
}
if err != nil {
return
break
}
recvTimes ++
recvTotal += len ( msg . Klines )
@ -271,10 +316,39 @@ func (b *SigStrategyBacktester) fetchHistoryKlineSeries(ctx context.Context, sr
kline := new ( types . Kline )
kline . ParsePBKline ( sr . Exchange , k )
if err = recvFn ( kline ) ; err != nil {
return
break recvLoop
}
}
}
zlog . Debugf ( "fetch history kline series: inst=%s(%s), interval=%s, recv=%d, total=%d, use %s" , sr . InstId , sr . Exchange , sr . Interval , recvTimes , recvTotal , watch . ElapsedFmt ( "." ) )
return
}
// Deprecated
// _singleStrategySeries 单周期策略
func ( b * SigStrategyBacktester ) _singleStrategySeries ( ctx context . Context , sigStrategy strategy . ISingleSigStrategy , sr * pb . SeriesRange , recvSignal func ( sigSide types . Side , k types . Kline ) ( err error ) ) ( err error ) {
interval := types . Interval ( sr . Interval )
kSeries := sig . NewKlineSeries ( sr . Exchange , sr . InstId , interval )
indicatorContext := sig . NewIndicatorContext ( kSeries )
strategyContext := sig . NewStrategyContext ( indicatorContext , b . indicatorReg )
requiredSeries := int ( sigStrategy . RequiredSeries ( ) )
sr . WindowExtra = uint32 ( max ( 0 , requiredSeries - 1 ) )
err = b . fetchHistoryKlineSeries ( ctx , sr , func ( k * types . Kline ) ( err error ) {
if lastTs , serial := kSeries . Update ( k ) ; ! serial {
err = fmt . Errorf ( "kline not series: %s(%s), interval=%s, lastTs=%d" , sr . InstId , sr . Exchange , interval , lastTs )
return
}
if kSeries . Length ( ) < requiredSeries {
return
}
sigSide := sigStrategy . Update ( strategyContext )
if sigSide . IsValid ( ) {
if err = recvSignal ( sigSide , * k ) ; err != nil {
return
}
}
return
} )
return
}