Browse Source

rpc indicator series

main
strange 10 months ago
parent
commit
47fadb6f9f
  1. 31
      api/trading.proto
  2. 2
      cmd/trading/main.go
  3. 122
      internal/trading/indicator_context.go
  4. 38
      internal/trading/indicator_series.go
  5. 52
      internal/trading/kline_series.go
  6. 21
      internal/trading/kline_store.go
  7. 61
      internal/trading/strategy_context.go
  8. 22
      internal/trading/trading_grpc_server.go
  9. 132
      internal/trading/trading_service.go
  10. 7
      pkg/grpc/interceptor/recover_interceptor.go
  11. 10
      pkg/indicator/base.go
  12. 4
      pkg/indicator/rsi.go
  13. 4
      pkg/strategy/sig_strategy.go

31
api/trading.proto

@ -6,6 +6,8 @@ option go_package = "./pb";
service TradingService { service TradingService {
rpc SubIndicator(IndicatorSubReq) returns (stream Indicator); // rpc SubIndicator(IndicatorSubReq) returns (stream Indicator); //
rpc IndicatorSeries(ReqIndicatorSeries) returns (RspIndicatorSeries); //
rpc StrategySeries(ReqStrategySeries) returns (RspStrategySeries); //
} }
message IndicatorSubReq { message IndicatorSubReq {
@ -22,3 +24,32 @@ message Indicator {
int64 Ts = 5; int64 Ts = 5;
bytes payload = 8; 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;
}

2
cmd/trading/main.go

@ -69,7 +69,7 @@ func main() {
} }
// grpc server // grpc server
tradingGrpcServer := trading.NewTradingGrpcServer() tradingGrpcServer := trading.NewTradingGrpcServer(tradingService)
if err := tradingGrpcServer.Init(); err != nil { if err := tradingGrpcServer.Init(); err != nil {
panic(err) panic(err)
} }

122
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)
}

38
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
}

52
internal/trading/kline_series.go

@ -58,34 +58,44 @@ func NewKlineSeries(exchange pb.ExchangeType, instId string, interval types.Inte
} }
// Get [0]当前k线 // Get [0]当前k线
func (s *KlineSeries) Get(start int16) types.Kline { func (s *KlineSeries) Get(offset int16) (k types.Kline, ok bool) {
index := len(s.klines) - 1 - int(start) if ok = offset >= 0 && offset < MaxSeriesKlines; !ok {
if index >= 0 && index < len(s.klines)-1 { return
return *(s.klines[index])
} }
s.RLock()
defer s.RUnlock()
// todo query store length := len(s.klines)
ts := s.Interval.MustAddMul(s.lastTs, int64(-start)) index := (length - 1) - int(offset)
for _, k := range s.klines { if ok = index > 0 && index < length; !ok {
if k.Ts == ts { return
return *k
}
} }
return *(s.klines[index]), true
panic("kline not exists")
} }
// Series [start...end] // Series 闭区间升序[count...offset]
func (s *KlineSeries) Series(start, end int16) (klines series.Klines) { 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() s.RLock()
defer s.RUnlock() defer s.RUnlock()
endTs := s.Interval.MustAddMul(s.lastTs, int64(-start))
startTs := s.Interval.MustAddMul(s.lastTs, int64(-end)) length := len(s.klines)
_ = endTs indexEnd := (length - 1) - int(offset)
_ = startTs indexStart := (length - 1) - int(offset) - int(count)
// return a.klineStore.GetRange(startTs, endTs) if ok = indexEnd >= 0 && indexEnd < length && indexStart >= 0 && indexStart < length; !ok {
// todo return
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 { func (s *KlineSeries) LastTs() int64 {

21
internal/trading/kline_store.go

@ -2,6 +2,7 @@ package trading
import ( import (
"context" "context"
"fmt"
"io" "io"
"math" "math"
"sig-pub/api/pb" "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) // pubKey := fmt.Sprintf("/kline/%s/%s/%s/%d", exchangeType, tradeInst.InstId, kline.Interval, confirm)
// interval/okx/BTC_USDT/1m,3m,5m // 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
}

61
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)
}

22
internal/trading/trading_grpc_server.go

@ -1,17 +1,33 @@
package trading package trading
import ( import (
"context"
"sig-pub/api/pb" "sig-pub/api/pb"
) )
type TradingGrpcServer struct { type TradingGrpcServer struct {
pb.UnimplementedTradingServiceServer pb.UnimplementedTradingServiceServer
tradingService *TradingService
} }
func NewTradingGrpcServer() *TradingGrpcServer { func NewTradingGrpcServer(tradingService *TradingService) *TradingGrpcServer {
return &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 return
} }

132
internal/trading/trading_service.go

@ -7,6 +7,7 @@ import (
"sig-pub/pkg/indicator" "sig-pub/pkg/indicator"
"sig-pub/pkg/publish" "sig-pub/pkg/publish"
"sig-pub/pkg/strategy" "sig-pub/pkg/strategy"
"sig-pub/pkg/types"
"sig-pub/pkg/utils/collect" "sig-pub/pkg/utils/collect"
) )
@ -14,11 +15,11 @@ type TradingService struct {
marketClientAside *client.TradeInstanceAside marketClientAside *client.TradeInstanceAside
exchangeClient pb.ExchangeServiceClient exchangeClient pb.ExchangeServiceClient
klineStore *KlineStore klineStore *KlineStore
indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator] // 注册窗口指标 indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator] // 注册窗口指标
strategies *collect.SyncMap[string, strategy.ISigStrategy] // 注册信号策略 sigStrategies *collect.SyncMap[string, strategy.ISigStrategy] // 注册信号策略
publisher publish.Publisher[int64, *TradingPlan] publisher publish.Publisher[int64, *TradingPlan]
tradingPlan chan *TradingPlan tradingPlan chan *TradingPlan
} }
func NewTradingService( func NewTradingService(
@ -30,22 +31,22 @@ func NewTradingService(
exchangeClient: exchangeClient, exchangeClient: exchangeClient,
klineStore: NewKlineSeriesStore(exchangeClient), klineStore: NewKlineSeriesStore(exchangeClient),
indicatorsW: collect.NewSyncMap[string, indicator.IWindowIndicator](), indicatorsW: collect.NewSyncMap[string, indicator.IWindowIndicator](),
strategies: collect.NewSyncMap[string, strategy.ISigStrategy](), sigStrategies: collect.NewSyncMap[string, strategy.ISigStrategy](),
} }
} }
// 初始化历史k线, 订阅实时k线 // 初始化历史k线, 订阅实时k线
func (svr *TradingService) Init() (err error) { func (svc *TradingService) Init() (err error) {
if err = svr.klineStore.Init(); err != nil { if err = svc.klineStore.Init(); err != nil {
return return
} }
// indicator registry // indicator registry
{ {
svr.MustRegisterWindowIndicator(&indicator.RSI{}) svc.MustRegisterWindowIndicator(&indicator.RSI{})
} }
// strategy registry // strategy registry
{ {
svr.MustRegisterStrategy(&strategy.GoldX{}) svc.MustRegisterStrategy(&strategy.GoldX{})
} }
// strategy initial // strategy initial
// 初始化策略执行器 64 // 初始化策略执行器 64
@ -54,9 +55,9 @@ func (svr *TradingService) Init() (err error) {
} }
// RegisterWindowIndicator // RegisterWindowIndicator
func (svr *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicator) (err error) { func (svc *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicator) (err error) {
indName := ind.Name() indName := ind.Name()
_, loaded := svr.indicatorsW.LoadOrStore(indName, ind) _, loaded := svc.indicatorsW.LoadOrStore(indName, ind)
if loaded { if loaded {
err = fmt.Errorf("window indicator name %s already duplicated", indName) err = fmt.Errorf("window indicator name %s already duplicated", indName)
return return
@ -64,16 +65,16 @@ func (svr *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicato
return return
} }
func (svr *TradingService) MustRegisterWindowIndicator(ind indicator.IWindowIndicator) { func (svc *TradingService) MustRegisterWindowIndicator(ind indicator.IWindowIndicator) {
if err := svr.RegisterWindowIndicator(ind); err != nil { if err := svc.RegisterWindowIndicator(ind); err != nil {
panic(err) panic(err)
} }
} }
// RegisterStrategy // RegisterStrategy
func (svr *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err error) { func (svc *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err error) {
strategyName := strategy.Meta().Name strategyName := strategy.Meta().Name
_, loaded := svr.strategies.LoadOrStore(strategyName, strategy) _, loaded := svc.sigStrategies.LoadOrStore(strategyName, strategy)
if loaded { if loaded {
err = fmt.Errorf("strategy name %s already duplicated", strategyName) err = fmt.Errorf("strategy name %s already duplicated", strategyName)
return return
@ -81,16 +82,16 @@ func (svr *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err
return return
} }
func (svr *TradingService) MustRegisterStrategy(strategy strategy.ISigStrategy) { func (svc *TradingService) MustRegisterStrategy(strategy strategy.ISigStrategy) {
if err := svr.RegisterStrategy(strategy); err != nil { if err := svc.RegisterStrategy(strategy); err != nil {
panic(err) panic(err)
} }
} }
// RunStrategy 运行策略 // RunStrategy 运行策略
// todo 止盈止损... // todo 止盈止损...
func (svr *TradingService) RunQuantPlan(plan TradingPlan) (err error) { func (svc *TradingService) RunQuantPlan(plan TradingPlan) (err error) {
strategy, ok := svr.strategies.Load(plan.StrategyName) strategy, ok := svc.sigStrategies.Load(plan.StrategyName)
if !ok { if !ok {
err = fmt.Errorf("strategy %s not exists", plan.StrategyName) err = fmt.Errorf("strategy %s not exists", plan.StrategyName)
return return
@ -100,3 +101,94 @@ func (svr *TradingService) RunQuantPlan(plan TradingPlan) (err error) {
runner.Update(nil) runner.Update(nil)
return 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
}

7
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) { func RecoverInterceptor(ctx context.Context, req any, server *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
switch r.(type) { switch r := r.(type) {
case error: case error:
err = r.(error) err = r
case string: case string:
err = errors.New(r.(string)) err = errors.New(r)
default: default:
err = errors.New("grpc server recover error") 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) resp, err = handler(ctx, req)
if err == nil { if err == nil {
if empty, ok := resp.(*emptypb.Empty); ok && empty == nil { if empty, ok := resp.(*emptypb.Empty); ok && empty == nil {

10
pkg/indicator/base.go

@ -8,22 +8,22 @@ import (
// IIndicator 指标基础计算接口 // IIndicator 指标基础计算接口
type IIndicator interface { type IIndicator interface {
Name() string Name() string
Calculate(kSeries IKlineSeries) (vector float64) Calculate(kSeries IIndicatorContext) (vector float64)
} }
// IIndicator 窗口指标基础计算接口 // IIndicator 窗口指标基础计算接口
type IWindowIndicator interface { type IWindowIndicator interface {
Name() string Name() string
Calculate(kSeries IKlineSeries, window int16) (vector float64) Calculate(ctx IIndicatorContext, window int16) (vector float64)
} }
// IKlineSeries k线序列, strategy服务提供 // IIndicatorContext k线序列, trading服务提供
type IKlineSeries interface { type IIndicatorContext interface {
Get(offset int16) (kline types.Kline) Get(offset int16) (kline types.Kline)
Series(offset, count int16) (klines series.Klines) Series(offset, count int16) (klines series.Klines)
} }
// IIndicatorSeries 指标序列, 供策略读取, strategy服务提供 // IIndicatorSeries 指标序列, 供策略读取, trading服务提供
type IIndicatorSeries interface { type IIndicatorSeries interface {
Get(offset int16) (vector float64) Get(offset int16) (vector float64)
Series(offset, count int16) (matrix series.Floats) Series(offset, count int16) (matrix series.Floats)

4
pkg/indicator/rsi.go

@ -17,9 +17,9 @@ func (c *RSI) Name() string {
} }
// Calculate 计算单根k线rsi指标 // Calculate 计算单根k线rsi指标
func (c *RSI) Calculate(kSeries IKlineSeries, window int16) (vector float64) { func (c *RSI) Calculate(ctx IIndicatorContext, window int16) (vector float64) {
// 读k线, 计算 // 读k线, 计算
klineSeries := kSeries.Series(0, int16(window)) // 7根 klineSeries := ctx.Series(0, int16(window)) // 7根
closeSeries := klineSeries.Close() closeSeries := klineSeries.Close()
closeDiff := closeSeries.Diff() closeDiff := closeSeries.Diff()

4
pkg/strategy/sig_strategy.go

@ -18,6 +18,8 @@ type ISigStrategy interface {
} }
// todo Meta 策略调参, 回测引擎自动调参回测(最佳参数) argGenerator.next() (arg, ok) // 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 { type ISigStrategyAdjustable interface {
ISigStrategy ISigStrategy
NextParams() map[string]any // 根据当前策略参数, 返回下一批策略参数(并行回测 stateless) NextParams() map[string]any // 根据当前策略参数, 返回下一批策略参数(并行回测 stateless)
@ -41,7 +43,7 @@ type ISigStrategyContext interface {
// Series [offset...end] // Series [offset...end]
Series(offset, count int16) (klines series.Klines) Series(offset, count int16) (klines series.Klines)
// 获取窗口类型指标 // 获取窗口类型指标
IndicatorW(name string, window int) indicator.IIndicatorSeries IndicatorW(name string, window int16) indicator.IIndicatorSeries
} }
// DriverIntervalKey 生成周期驱动事件key // DriverIntervalKey 生成周期驱动事件key

Loading…
Cancel
Save