diff --git a/api/trading.proto b/api/trading.proto index deb5b0a..12e1c5c 100644 --- a/api/trading.proto +++ b/api/trading.proto @@ -6,6 +6,8 @@ option go_package = "./pb"; service TradingService { rpc SubIndicator(IndicatorSubReq) returns (stream Indicator); // 订阅指标 + rpc IndicatorSeries(ReqIndicatorSeries) returns (RspIndicatorSeries); // 获取指标序列 + rpc StrategySeries(ReqStrategySeries) returns (RspStrategySeries); // 获取指标序列 } message IndicatorSubReq { @@ -22,3 +24,32 @@ message Indicator { int64 Ts = 5; bytes payload = 8; } + +message ReqIndicatorSeries { + string indicator = 1; + int32 window = 2; // 指标窗口大小 + ExchangeType exchange = 3; + string instId = 4; + string interval = 5; + int64 before = 6; // 0表示最新 + int64 after = 7; // 0表示最新 + int32 count = 8; // k线条数,before或after其中一个为0时有效 +} +message RspIndicatorSeries{ + repeated double matrix = 1; + repeated int64 times = 2; +} + +message ReqStrategySeries { + string strategy = 1; + ExchangeType exchange = 3; + string instId = 4; + string interval = 5; + int64 before = 6; // 0表示最新 + int64 after = 7; // 0表示最新 + int32 count = 8; // k线条数,before或after其中一个为0时有效 +} +message RspStrategySeries { + repeated int32 signal = 1; // 0.sell,1.buy + repeated int64 times = 2; +} diff --git a/cmd/trading/main.go b/cmd/trading/main.go index d5a2a2b..82344b3 100644 --- a/cmd/trading/main.go +++ b/cmd/trading/main.go @@ -69,7 +69,7 @@ func main() { } // grpc server - tradingGrpcServer := trading.NewTradingGrpcServer() + tradingGrpcServer := trading.NewTradingGrpcServer(tradingService) if err := tradingGrpcServer.Init(); err != nil { panic(err) } diff --git a/internal/trading/indicator_context.go b/internal/trading/indicator_context.go new file mode 100644 index 0000000..f74b8c7 --- /dev/null +++ b/internal/trading/indicator_context.go @@ -0,0 +1,122 @@ +package trading + +import ( + "context" + "fmt" + "io" + "sig-pub/api/pb" + "sig-pub/pkg/indicator" + "sig-pub/pkg/types" + "sig-pub/pkg/types/series" + "sig-pub/pkg/zlog" + + "google.golang.org/grpc" +) + +type IOffsetIndicatorContext interface { + indicator.IIndicatorContext + SetOffset(offset int16) +} + +// IndicatorContext 指标上下文, 提供k线序列给指标计算使用 +type IndicatorContext struct { + IOffsetIndicatorContext + kSeries *KlineSeries + offset int16 +} + +func NewIndicatorContext(kSeries *KlineSeries) *IndicatorContext { + return &IndicatorContext{ + kSeries: kSeries, + } +} + +func (c *IndicatorContext) SetOffset(offset int16) { + c.offset = offset +} + +func (c *IndicatorContext) Get(offset int16) (kline types.Kline) { + offset += c.offset + k, ok := c.kSeries.Get(offset) + if !ok { + lastTs := c.kSeries.LastTs() + zlog.Warningf("get kline series offset out of range: offset=%d, lastTs=%d", offset, lastTs) + panic(fmt.Errorf("get kline series offset out of range: offset=%d", offset)) + } + return k +} + +func (c *IndicatorContext) Series(offset, count int16) (klines series.Klines) { + offset += c.offset + ks, ok := c.kSeries.Series(offset, count) + if !ok { + zlog.Warningf("get kline series offset out of range: offset=%d, count=%d, lastTs=%d", offset, count, c.kSeries.LastTs()) + panic(fmt.Errorf("get kline series offset out of range: offset=%d, count=%d", offset, count)) + } + return ks +} + +type HistoryIndicatorContext struct { + IOffsetIndicatorContext + exchangeClient pb.ExchangeServiceClient + context *IndicatorContext +} + +func NewHistoryIndicatorContext(exchangeClient pb.ExchangeServiceClient) *HistoryIndicatorContext { + return &HistoryIndicatorContext{ + exchangeClient: exchangeClient, + } +} +func (c *HistoryIndicatorContext) Init(exchange pb.ExchangeType, instId string, interval types.Interval, before, after int64) (totalK int, err error) { + // fetch history series + req := &pb.ReqHistoryKlineStream{ + Exchange: exchange, + InstId: instId, + Interval: string(interval), + Count: 0, + Before: before, + After: after, + } + stream, err := c.exchangeClient.HistoryKlineStream(context.Background(), req, grpc.UseCompressor("snappy")) + if err != nil { + zlog.Errorf("fetch history kline stream error: instId=%s(%s), interval=%s, %#v, err=%v", instId, exchange, interval, req, err) + return + } + + klineSeries := NewKlineSeries(exchange, instId, interval) + for { + msg, err0 := stream.Recv() + if err0 == io.EOF { + break + } + if err0 != nil { + err = err0 + zlog.Error("fetch kline stream recv error: ", err0) + return + } + // zlog.Debugf("recv: %s(%s), %s, branch=%d, ts=%d~%d", instId, exchange, interval, len(msg.Klines), msg.Klines[0].Ts, msg.Klines[len(msg.Klines)-1].Ts) + totalK += len(msg.Klines) + for _, k := range msg.Klines { + kline := new(types.Kline) + kline.ParsePBKline(exchange, k) + if lastTs, ok := klineSeries.Update(kline); !ok { + err = fmt.Errorf("history stream kline not series: last=%d", lastTs) + return + } + } + } + c.context = NewIndicatorContext(klineSeries) + return +} + +func (c *HistoryIndicatorContext) SetOffset(offset int16) { + c.context.SetOffset(offset) +} + +func (c *HistoryIndicatorContext) Get(offset int16) (kline types.Kline) { + return c.context.Get(offset) +} + +func (c *HistoryIndicatorContext) Series(offset, count int16) (klines series.Klines) { + return c.context.Series(offset, count) +} diff --git a/internal/trading/indicator_series.go b/internal/trading/indicator_series.go new file mode 100644 index 0000000..1254ec3 --- /dev/null +++ b/internal/trading/indicator_series.go @@ -0,0 +1,38 @@ +package trading + +import ( + "sig-pub/pkg/indicator" + "sig-pub/pkg/types/series" +) + +// WindowIndicatorSeries 封装 +type WindowIndicatorSeries struct { + indicator.IIndicatorSeries + window int16 + indicator indicator.IWindowIndicator + indicatorContext IOffsetIndicatorContext +} + +func NewWindowIndicatorSeries(window int16, indicator indicator.IWindowIndicator, indicatorContext IOffsetIndicatorContext) *WindowIndicatorSeries { + return &WindowIndicatorSeries{ + window: window, + indicator: indicator, + indicatorContext: indicatorContext, + } +} + +func (s *WindowIndicatorSeries) Get(offset int16) (vector float64) { + s.indicatorContext.SetOffset(offset) + vector = s.indicator.Calculate(s.indicatorContext, s.window) + return +} + +func (s *WindowIndicatorSeries) Series(offset, count int16) (matrix series.Floats) { + for i := range count { + offset += i + s.indicatorContext.SetOffset(offset) + vector := s.indicator.Calculate(s.indicatorContext, s.window) + matrix.Push(vector) + } + return +} diff --git a/internal/trading/kline_series.go b/internal/trading/kline_series.go index d2fb0c9..46d5247 100644 --- a/internal/trading/kline_series.go +++ b/internal/trading/kline_series.go @@ -58,34 +58,44 @@ func NewKlineSeries(exchange pb.ExchangeType, instId string, interval types.Inte } // Get [0]当前k线 -func (s *KlineSeries) Get(start int16) types.Kline { - index := len(s.klines) - 1 - int(start) - if index >= 0 && index < len(s.klines)-1 { - return *(s.klines[index]) +func (s *KlineSeries) Get(offset int16) (k types.Kline, ok bool) { + if ok = offset >= 0 && offset < MaxSeriesKlines; !ok { + return } + s.RLock() + defer s.RUnlock() - // todo query store - ts := s.Interval.MustAddMul(s.lastTs, int64(-start)) - for _, k := range s.klines { - if k.Ts == ts { - return *k - } + length := len(s.klines) + index := (length - 1) - int(offset) + if ok = index > 0 && index < length; !ok { + return } - - panic("kline not exists") + return *(s.klines[index]), true } -// Series [start...end] -func (s *KlineSeries) Series(start, end int16) (klines series.Klines) { +// Series 闭区间升序[count...offset] +func (s *KlineSeries) Series(offset, count int16) (klines series.Klines, ok bool) { + if ok = offset >= 0 && offset < MaxSeriesKlines; !ok { + return + } + if ok = count >= 0 && offset+count < MaxSeriesKlines; !ok { + return + } + s.RLock() defer s.RUnlock() - endTs := s.Interval.MustAddMul(s.lastTs, int64(-start)) - startTs := s.Interval.MustAddMul(s.lastTs, int64(-end)) - _ = endTs - _ = startTs - // return a.klineStore.GetRange(startTs, endTs) - // todo - return + + length := len(s.klines) + indexEnd := (length - 1) - int(offset) + indexStart := (length - 1) - int(offset) - int(count) + if ok = indexEnd >= 0 && indexEnd < length && indexStart >= 0 && indexStart < length; !ok { + return + } + klines = make(series.Klines, 0, indexEnd-indexStart+1) + for i := indexStart; i <= indexEnd; i++ { + klines = append(klines, *(s.klines[i])) + } + return klines, true } func (s *KlineSeries) LastTs() int64 { diff --git a/internal/trading/kline_store.go b/internal/trading/kline_store.go index b1b47ce..3bd85d6 100644 --- a/internal/trading/kline_store.go +++ b/internal/trading/kline_store.go @@ -2,6 +2,7 @@ package trading import ( "context" + "fmt" "io" "math" "sig-pub/api/pb" @@ -301,3 +302,23 @@ func (s *KlineStore) Update(exchange pb.ExchangeType, instId string, kline *type // pubKey := fmt.Sprintf("/kline/%s/%s/%s/%d", exchangeType, tradeInst.InstId, kline.Interval, confirm) // interval/okx/BTC_USDT/1m,3m,5m } + +// GetKlineSeires 获取k线序列 +func (s *KlineStore) GetKlineSeires(exchange pb.ExchangeType, instId string, interval types.Interval) (klineSeries *KlineSeries, err error) { + if _, ok := types.SupportedIntervals[interval]; !ok { + err = fmt.Errorf("unsupport interval: %s", interval) + return + } + if !s.store.IsSupport(exchange) { + zlog.Warningf("unsupport exchange: %v", exchange) + return + } + instsSeries := s.store.Get(exchange) + instSeries, ok := instsSeries.Load(instId) + if !ok { + err = fmt.Errorf("trade instance %s not support", instId) + return + } + klineSeries = instSeries.IntervalKlines.Get(interval) + return +} diff --git a/internal/trading/strategy_context.go b/internal/trading/strategy_context.go new file mode 100644 index 0000000..9875128 --- /dev/null +++ b/internal/trading/strategy_context.go @@ -0,0 +1,61 @@ +package trading + +import ( + "fmt" + "sig-pub/pkg/indicator" + "sig-pub/pkg/strategy" + "sig-pub/pkg/types" + "sig-pub/pkg/types/series" + "sig-pub/pkg/utils/collect" + "sig-pub/pkg/zlog" +) + +type IOffsetStrategyContext interface { + strategy.ISigStrategyContext + SetOffset(offset int16) +} + +type StrategyContext struct { + IOffsetStrategyContext + + indicatorContext *IndicatorContext + indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator] +} + +func NewStrategyContext(klineSeries *KlineSeries, indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator]) *StrategyContext { + return &StrategyContext{ + indicatorContext: NewIndicatorContext(klineSeries), + indicatorsW: indicatorsW, + } +} + +func (c *StrategyContext) SetOffset(offset int16) { + c.indicatorContext.SetOffset(offset) +} + +func (c *StrategyContext) Get(offset int16) (kline types.Kline) { + return c.indicatorContext.Get(offset) +} + +func (c *StrategyContext) Series(offset, count int16) (klines series.Klines) { + return c.indicatorContext.Series(offset, count) +} + +// Buy 发出多信号 +func (c *StrategyContext) Buy() { + zlog.Infof("signal buy: %d", c.Get(0).Ts) +} + +// Sell 发出空信号 +func (c *StrategyContext) Sell() { + zlog.Infof("signal sell: %d", c.Get(0).Ts) +} + +// 获取窗口类型指标 +func (c *StrategyContext) IndicatorW(name string, window int16) (s indicator.IIndicatorSeries) { + indicator, ok := c.indicatorsW.Load(name) + if !ok { + panic(fmt.Errorf("indicatorW %s not exists", name)) + } + return NewWindowIndicatorSeries(window, indicator, c.indicatorContext) +} diff --git a/internal/trading/trading_grpc_server.go b/internal/trading/trading_grpc_server.go index 7afc3f1..c170109 100644 --- a/internal/trading/trading_grpc_server.go +++ b/internal/trading/trading_grpc_server.go @@ -1,17 +1,33 @@ package trading import ( + "context" "sig-pub/api/pb" ) type TradingGrpcServer struct { pb.UnimplementedTradingServiceServer + tradingService *TradingService } -func NewTradingGrpcServer() *TradingGrpcServer { - return &TradingGrpcServer{} +func NewTradingGrpcServer(tradingService *TradingService) *TradingGrpcServer { + return &TradingGrpcServer{ + tradingService: tradingService, + } } -func (svr TradingGrpcServer) Init() (err error) { +func (svr *TradingGrpcServer) Init() (err error) { + return +} + +func (svr *TradingGrpcServer) IndicatorSeries(ctx context.Context, req *pb.ReqIndicatorSeries) (rsp *pb.RspIndicatorSeries, err error) { + rsp = &pb.RspIndicatorSeries{} + err = svr.tradingService.IndicatorSeries(req, rsp) + return +} + +func (svr *TradingGrpcServer) StrategySeries(ctx context.Context, req *pb.ReqStrategySeries) (rsp *pb.RspStrategySeries, err error) { + rsp = &pb.RspStrategySeries{} + err = svr.tradingService.StrategySeries(req, rsp) return } diff --git a/internal/trading/trading_service.go b/internal/trading/trading_service.go index da2923c..e732b2f 100644 --- a/internal/trading/trading_service.go +++ b/internal/trading/trading_service.go @@ -7,6 +7,7 @@ import ( "sig-pub/pkg/indicator" "sig-pub/pkg/publish" "sig-pub/pkg/strategy" + "sig-pub/pkg/types" "sig-pub/pkg/utils/collect" ) @@ -14,11 +15,11 @@ type TradingService struct { marketClientAside *client.TradeInstanceAside exchangeClient pb.ExchangeServiceClient - klineStore *KlineStore - indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator] // 注册窗口指标 - strategies *collect.SyncMap[string, strategy.ISigStrategy] // 注册信号策略 - publisher publish.Publisher[int64, *TradingPlan] - tradingPlan chan *TradingPlan + klineStore *KlineStore + indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator] // 注册窗口指标 + sigStrategies *collect.SyncMap[string, strategy.ISigStrategy] // 注册信号策略 + publisher publish.Publisher[int64, *TradingPlan] + tradingPlan chan *TradingPlan } func NewTradingService( @@ -30,22 +31,22 @@ func NewTradingService( exchangeClient: exchangeClient, klineStore: NewKlineSeriesStore(exchangeClient), indicatorsW: collect.NewSyncMap[string, indicator.IWindowIndicator](), - strategies: collect.NewSyncMap[string, strategy.ISigStrategy](), + sigStrategies: collect.NewSyncMap[string, strategy.ISigStrategy](), } } // 初始化历史k线, 订阅实时k线 -func (svr *TradingService) Init() (err error) { - if err = svr.klineStore.Init(); err != nil { +func (svc *TradingService) Init() (err error) { + if err = svc.klineStore.Init(); err != nil { return } // indicator registry { - svr.MustRegisterWindowIndicator(&indicator.RSI{}) + svc.MustRegisterWindowIndicator(&indicator.RSI{}) } // strategy registry { - svr.MustRegisterStrategy(&strategy.GoldX{}) + svc.MustRegisterStrategy(&strategy.GoldX{}) } // strategy initial // 初始化策略执行器 64 @@ -54,9 +55,9 @@ func (svr *TradingService) Init() (err error) { } // RegisterWindowIndicator -func (svr *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicator) (err error) { +func (svc *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicator) (err error) { indName := ind.Name() - _, loaded := svr.indicatorsW.LoadOrStore(indName, ind) + _, loaded := svc.indicatorsW.LoadOrStore(indName, ind) if loaded { err = fmt.Errorf("window indicator name %s already duplicated", indName) return @@ -64,16 +65,16 @@ func (svr *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicato return } -func (svr *TradingService) MustRegisterWindowIndicator(ind indicator.IWindowIndicator) { - if err := svr.RegisterWindowIndicator(ind); err != nil { +func (svc *TradingService) MustRegisterWindowIndicator(ind indicator.IWindowIndicator) { + if err := svc.RegisterWindowIndicator(ind); err != nil { panic(err) } } // RegisterStrategy -func (svr *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err error) { +func (svc *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err error) { strategyName := strategy.Meta().Name - _, loaded := svr.strategies.LoadOrStore(strategyName, strategy) + _, loaded := svc.sigStrategies.LoadOrStore(strategyName, strategy) if loaded { err = fmt.Errorf("strategy name %s already duplicated", strategyName) return @@ -81,16 +82,16 @@ func (svr *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err return } -func (svr *TradingService) MustRegisterStrategy(strategy strategy.ISigStrategy) { - if err := svr.RegisterStrategy(strategy); err != nil { +func (svc *TradingService) MustRegisterStrategy(strategy strategy.ISigStrategy) { + if err := svc.RegisterStrategy(strategy); err != nil { panic(err) } } // RunStrategy 运行策略 // todo 止盈止损... -func (svr *TradingService) RunQuantPlan(plan TradingPlan) (err error) { - strategy, ok := svr.strategies.Load(plan.StrategyName) +func (svc *TradingService) RunQuantPlan(plan TradingPlan) (err error) { + strategy, ok := svc.sigStrategies.Load(plan.StrategyName) if !ok { err = fmt.Errorf("strategy %s not exists", plan.StrategyName) return @@ -100,3 +101,94 @@ func (svr *TradingService) RunQuantPlan(plan TradingPlan) (err error) { runner.Update(nil) return } + +// IndicatorSeries 获取指标实时或历史序列数据, 闭区间 +func (svc *TradingService) IndicatorSeries(req *pb.ReqIndicatorSeries, rsp *pb.RspIndicatorSeries) (err error) { + // indicatorName string, exchange pb.ExchangeType, instId string, interval types.Interval, window int + indicator, ok := svc.indicatorsW.Load(req.Indicator) + if !ok { + err = fmt.Errorf("indicator %s not exists", req.Indicator) + return + } + interval := types.Interval(req.Interval) + intervalAdd, ok := types.SupportedIntervals[interval] + if !ok { + err = fmt.Errorf("unsupport interval %s", interval) + return + } + if req.Count <= 0 { + req.Count = 100 + } + if req.Count > 0 { + // ... + } + + // todo trade instance status check + + before, after, count := req.Before, req.After, req.Count + var indCtx IOffsetIndicatorContext + // 查询实时指标数据 + if before == 0 && after == 0 { + klineSeries, err1 := svc.klineStore.GetKlineSeires(req.Exchange, req.InstId, interval) + if err1 != nil { + err = err1 + return + } + // recover todo out of range + indCtx = NewIndicatorContext(klineSeries) + } else { + // 查询历史指标数据 + // todo calc before after... + ctx := NewHistoryIndicatorContext(svc.exchangeClient) + if count := int32((after-before)/intervalAdd(0, 1) + 1); count > 100 { + + } + + before = intervalAdd(before, int64(-req.Window-1)) // 多拉取窗口大小的k线数据 + totalK := 0 + if totalK, err = ctx.Init(req.Exchange, req.InstId, interval, before, after); err != nil { + return + } + count = int32(totalK) - req.Window + indCtx = ctx + } + + rsp.Matrix = make([]float64, 0, req.Count) + rsp.Times = make([]int64, 0, req.Count) + for i := range count { + indCtx.SetOffset(int16(i)) + vector := indicator.Calculate(indCtx, int16(req.Window)) + rsp.Matrix = append(rsp.Matrix, vector) + rsp.Times = append(rsp.Times, indCtx.Get(0).Ts) + } + return +} + +// StrategySeries 简单策略信号测试 +func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.RspStrategySeries) (err error) { + strategy, ok := svc.sigStrategies.Load(req.Strategy) + if !ok { + err = fmt.Errorf("strategy %s not exists", req.Strategy) + return + } + interval := types.Interval(req.Interval) + intervalAdd, ok := types.SupportedIntervals[interval] + if !ok { + err = fmt.Errorf("unsupport interval %s", interval) + return + } + _ = intervalAdd + + klineSeries, err1 := svc.klineStore.GetKlineSeires(req.Exchange, req.InstId, interval) + if err1 != nil { + err = err1 + return + } + // recover todo out of range + strategyCtx := NewStrategyContext(klineSeries, svc.indicatorsW) + for i := range req.Count { + strategyCtx.SetOffset(int16(i)) + strategy.Update(strategyCtx) + } + return +} diff --git a/pkg/grpc/interceptor/recover_interceptor.go b/pkg/grpc/interceptor/recover_interceptor.go index 2381834..5eb3779 100755 --- a/pkg/grpc/interceptor/recover_interceptor.go +++ b/pkg/grpc/interceptor/recover_interceptor.go @@ -14,11 +14,11 @@ import ( func RecoverInterceptor(ctx context.Context, req any, server *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { defer func() { if r := recover(); r != nil { - switch r.(type) { + switch r := r.(type) { case error: - err = r.(error) + err = r case string: - err = errors.New(r.(string)) + err = errors.New(r) default: err = errors.New("grpc server recover error") } @@ -27,6 +27,7 @@ func RecoverInterceptor(ctx context.Context, req any, server *grpc.UnaryServerIn } }() + // zlog.Debugf("call method: %s", server.FullMethod) // -> /TradingService/IndicatorSeries resp, err = handler(ctx, req) if err == nil { if empty, ok := resp.(*emptypb.Empty); ok && empty == nil { diff --git a/pkg/indicator/base.go b/pkg/indicator/base.go index d43eeb8..ef50a3e 100644 --- a/pkg/indicator/base.go +++ b/pkg/indicator/base.go @@ -8,22 +8,22 @@ import ( // IIndicator 指标基础计算接口 type IIndicator interface { Name() string - Calculate(kSeries IKlineSeries) (vector float64) + Calculate(kSeries IIndicatorContext) (vector float64) } // IIndicator 窗口指标基础计算接口 type IWindowIndicator interface { Name() string - Calculate(kSeries IKlineSeries, window int16) (vector float64) + Calculate(ctx IIndicatorContext, window int16) (vector float64) } -// IKlineSeries k线序列, strategy服务提供 -type IKlineSeries interface { +// IIndicatorContext k线序列, trading服务提供 +type IIndicatorContext interface { Get(offset int16) (kline types.Kline) Series(offset, count int16) (klines series.Klines) } -// IIndicatorSeries 指标序列, 供策略读取, strategy服务提供 +// IIndicatorSeries 指标序列, 供策略读取, trading服务提供 type IIndicatorSeries interface { Get(offset int16) (vector float64) Series(offset, count int16) (matrix series.Floats) diff --git a/pkg/indicator/rsi.go b/pkg/indicator/rsi.go index c4dc62f..3bcad19 100644 --- a/pkg/indicator/rsi.go +++ b/pkg/indicator/rsi.go @@ -17,9 +17,9 @@ func (c *RSI) Name() string { } // Calculate 计算单根k线rsi指标 -func (c *RSI) Calculate(kSeries IKlineSeries, window int16) (vector float64) { +func (c *RSI) Calculate(ctx IIndicatorContext, window int16) (vector float64) { // 读k线, 计算 - klineSeries := kSeries.Series(0, int16(window)) // 7根 + klineSeries := ctx.Series(0, int16(window)) // 7根 closeSeries := klineSeries.Close() closeDiff := closeSeries.Diff() diff --git a/pkg/strategy/sig_strategy.go b/pkg/strategy/sig_strategy.go index 3f54288..e550f4b 100644 --- a/pkg/strategy/sig_strategy.go +++ b/pkg/strategy/sig_strategy.go @@ -18,6 +18,8 @@ type ISigStrategy interface { } // todo Meta 策略调参, 回测引擎自动调参回测(最佳参数) argGenerator.next() (arg, ok) +// argA range -> [1,...,5], argB:=[0.1,...,0.7], argC:=[true,false] +// 可变参数组合 argValidate(argA, ArgB, argC...) bool(true则使用该组合进行回测,记录组合参数回测结果) type ISigStrategyAdjustable interface { ISigStrategy NextParams() map[string]any // 根据当前策略参数, 返回下一批策略参数(并行回测 stateless) @@ -41,7 +43,7 @@ type ISigStrategyContext interface { // Series [offset...end] Series(offset, count int16) (klines series.Klines) // 获取窗口类型指标 - IndicatorW(name string, window int) indicator.IIndicatorSeries + IndicatorW(name string, window int16) indicator.IIndicatorSeries } // DriverIntervalKey 生成周期驱动事件key