@ -3,6 +3,7 @@ package trading
import (
"context"
"fmt"
"io"
"sig-pub/api/pb"
"sig-pub/pkg/client"
"sig-pub/pkg/data"
@ -13,12 +14,14 @@ import (
"sig-pub/pkg/types"
"sig-pub/pkg/utils/collect"
"sig-pub/pkg/utils/lang"
"sig-pub/pkg/utils/times"
"sig-pub/pkg/zlog"
"sig-pub/internal/trading/backtest"
"sig-pub/internal/trading/sig"
"github.com/bytedance/sonic"
"google.golang.org/grpc"
)
type TradingService struct {
@ -153,8 +156,48 @@ func (svc *TradingService) getTradingPlan(plan *entity.TradePlan, sigKlineSeries
// }()
}
// fetchHistoryKlineSeries 请求k线数据流式处理
func ( svc * TradingService ) 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 := svc . exchangeClient . HistoryKlineStream ( ctx , req , grpc . UseCompressor ( "snappy" ) )
if err != nil {
return
}
var msg * pb . RspHistoryKlineStream
recvTimes , recvTotal := 0 , 0
watch := times . NewWatch ( )
for {
select {
case <- ctx . Done ( ) :
err = ctx . Err ( )
return
default :
}
msg , err = stream . Recv ( )
if err == io . EOF {
err = nil
break
}
if err != nil {
return
}
recvTimes ++
recvTotal += len ( msg . Klines )
for _ , k := range msg . Klines {
kline := new ( types . Kline )
kline . ParsePBKline ( sr . Exchange , k )
if err = recvFn ( kline ) ; err != nil {
return
}
}
}
zlog . Debugf ( "fetch history kline series: recv=%d, total=%d, use %s" , recvTimes , recvTotal , watch . ElapsedFmt ( "." ) )
return
}
// IndicatorSeries 获取指标实时或历史序列数据, 闭区间
func ( svc * TradingService ) IndicatorSeries ( indicatorName string , window uint32 , sr * pb . SeriesRange ) ( matrix [ ] float64 , times [ ] int64 , err error ) {
func ( svc * TradingService ) IndicatorSeries ( ctx context . Context , indicatorName string , window uint32 , sr * pb . SeriesRange ) ( matrix [ ] float64 , times [ ] int64 , err error ) {
// indicatorName string, exchange pb.ExchangeType, instId string, interval types.Interval, window int
indicator , ok := svc . indicatorReg . IndicatorW ( indicatorName )
if ! ok {
@ -168,42 +211,35 @@ func (svc *TradingService) IndicatorSeries(indicatorName string, window uint32,
return
}
// before, after, count := sr.Before, sr.After, sr.Count
// var indCtx IOffsetIndicatorContext
// // 查询实时指标数据
// if before == 0 && after == 0 {
// klineSeries, err1 := svc.klineStore.GetKlineSeires(sr.Exchange, sr.InstId, interval)
// if err1 != nil {
// err = err1
// return
// }
// // recover todo out of range
// indCtx = NewIndicatorContext(klineSeries)
// }
// 查询历史指标数据
sr . Window = window
indCtx := sig . NewHistoryIndicatorContext ( svc . exchangeClient )
totalK := 0
if totalK , err = indCtx . Init ( sr ) ; err != nil {
requiredSeries := int ( indicator . RequiredSeries ( int16 ( window ) ) )
sr . WindowExtra = uint32 ( requiredSeries )
kSeries := sig . NewKlineSeries ( sr . Exchange , sr . InstId , types . Interval ( sr . Interval ) )
indicatorContext := sig . NewIndicatorContext ( kSeries )
matrix = make ( [ ] float64 , 0 , 200 )
times = make ( [ ] int64 , 0 , 200 )
err = svc . 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
}
count := totalK - int ( window )
matrix = make ( [ ] float64 , 0 , sr . Count )
times = make ( [ ] int64 , 0 , sr . Count )
for i := count - 1 ; i >= 0 ; i -- {
indCtx . SetOffset ( int16 ( i ) )
vector := indicator . Calculate ( indCtx , int16 ( window ) )
if kSeries . Length ( ) < requiredSeries {
return
}
vector := indicator . Calculate ( indicatorContext , int16 ( window ) )
matrix = append ( matrix , vector )
times = append ( times , indCtx . Get ( 0 ) . Ts )
times = append ( times , indicatorContext . Get ( 0 ) . Ts )
return
} )
if err != nil {
return
}
return
}
// StrategySeries 简单策略信号测试
// todo 去掉 HistoryIndicatorContext, 像backtest使用stream来一个算一个
func ( svc * TradingService ) StrategySeries ( req * pb . ReqStrategySeries , rsp * pb . RspStrategySeries ) ( err error ) {
func ( svc * TradingService ) StrategySeries ( ctx context . Context , req * pb . ReqStrategySeries , rsp * pb . RspStrategySeries ) ( err error ) {
// sigStrategy
sigStrategy , ok := svc . strategyReg . NewSigStrategy ( req . SigStrategy )
if ! ok {
@ -222,54 +258,31 @@ func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.Rsp
return
}
// klineSeries, err1 := svc.klineStore.GetKlineSeires(req.Exchange, req.InstId, interval)
// if err1 != nil {
// err = err1
// return
// }
// recover todo out of range
requiredSeries := int ( sigStrategy . RequiredSeries ( ) )
sr := req . Series
exchange := sr . Exchange
s eries := sig . NewKlineSeries ( e xchange, sr . InstId , interval )
count , totalK := 0 , 0
indctx := sig . NewIndicatorContext ( series )
_ = indctx
indicatorContext := sig . NewHistoryIndicatorContext ( svc . exchangeClient )
req . Series . Window += indicator . MaxWindow
if totalK , err = indicatorContext . Init ( req . Series ) ; err != nil {
sr . WindowExtra = uint32 ( requiredSeries )
kSeries := sig . NewKlineSeries ( sr . Exchange , sr . InstId , interval )
indicatorContext := sig . NewIndicatorContext ( kSeries )
strategyContext := sig . NewStrategyContext ( indicatorContext , svc . indicatorReg )
err = svc . 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
}
count = totalK - indicator . MaxWindow
strategyContext := sig . NewStrategyContext ( indicatorContext , svc . indicatorReg )
for i := count - 1 ; i >= 0 ; i -- {
strategyContext . SetOffset ( int16 ( i ) )
sigSide := sigStrategy . Update ( strategyContext )
if sigSide == types . SideLong || sigSide == types . SideShort {
if sigSide . IsValid ( ) {
side := lang . Ternary ( sigSide == types . SideLong , pb . Side_BUY , pb . Side_SELL )
signalK := strategyContext . Get ( 0 )
rsp . Signal = append ( rsp . Signal , side )
rsp . Times = append ( rsp . Times , signalK . Ts )
{
// todo delete
win := false
if indicatorContext . GetOffset ( ) > 0 {
nextK := indicatorContext . Get ( - 1 )
if side == pb . Side_BUY {
win = nextK . Close . Cmp ( signalK . Close ) > 0
} else {
win = nextK . Close . Cmp ( signalK . Close ) < 0
}
}
rsp . Wins = append ( rsp . Wins , win )
}
rsp . Times = append ( rsp . Times , k . Ts )
}
return
} )
if err != nil {
return
}
// 信号点胜率判断
wins := collect . Filter ( rsp . Wins , func ( _ int , win bool ) bool { return win } )
rsp . WinRate = float64 ( len ( wins ) ) / float64 ( len ( rsp . Wins ) )
return
}