Browse Source

indicator meta

main
strange 7 months ago
parent
commit
26f532b0a5
  1. 31
      api/pub.proto
  2. 19
      api/trading.proto
  3. 4
      config/exchange.toml
  4. 68
      internal/trading/trading_grpc_server.go
  5. 39
      internal/trading/trading_service.go
  6. 87
      pkg/indicator/boll.go
  7. 3
      pkg/indicator/indicator_plot.go
  8. 8
      pkg/indicator/indicator_registry.go
  9. 6
      pkg/indicator/macd.go
  10. 2
      pkg/types/input.go

31
api/pub.proto

@ -172,22 +172,45 @@ message Paging {
bool asc = 3; // bool asc = 3; //
} }
//
message IndicatorMeta {
string name = 1; //
string desc = 2; //
repeated InputArg input = 3; //
repeated string state = 4; //
repeated IndicatorPlotSeries plots = 5; //
}
// //
message IndicatorPlot { message IndicatorPlot {
string indicator = 1; string indicator = 1;
repeated IndicatorPlotSeries plots = 9; repeated IndicatorPlotSeries plots = 9;
} }
message IndicatorPlotSeries { message IndicatorPlotSeries {
string state = 1; string name = 1;
int32 type = 2; string state = 2;
google.protobuf.Struct props = 3; int32 type = 3;
repeated IndicatorPlotExp exps = 4; google.protobuf.Struct props = 4;
repeated IndicatorPlotExp exps = 5;
} }
message IndicatorPlotExp { message IndicatorPlotExp {
string exp = 1; string exp = 1;
google.protobuf.Struct props = 3; google.protobuf.Struct props = 3;
} }
//
message InputArg {
string name = 1;
string desc = 2;
int32 type = 3; //
repeated InputOption options = 4 ; // /
string default = 5; //
}
message InputOption {
string name = 1;
string desc = 2;
}
// //
message InputRange { message InputRange {
string name = 1; // names string name = 1; // names

19
api/trading.proto

@ -7,6 +7,12 @@ option go_package = "./pb";
// //
service TradingService { service TradingService {
//
rpc Indicators(ReqIndicators) returns (RspIndicators);
//
rpc IndicatorMetas(ReqIndicatorMetas) returns (RspIndicatorMetas);
// //
rpc IndicatorPlots(ReqIndicatorPlots) returns (RspIndicatorPlots); rpc IndicatorPlots(ReqIndicatorPlots) returns (RspIndicatorPlots);
@ -26,6 +32,19 @@ service TradingService {
rpc BacktestLog(ReqBacktestLog) returns (RspBacktestLog); rpc BacktestLog(ReqBacktestLog) returns (RspBacktestLog);
} }
message ReqIndicators {
}
message RspIndicators {
repeated string indicators = 1;
}
message ReqIndicatorMetas {
repeated string indicators = 1;
}
message RspIndicatorMetas {
repeated IndicatorMeta metas = 1;
}
message ReqIndicatorPlots { message ReqIndicatorPlots {
repeated string indicators = 1; repeated string indicators = 1;
} }

4
config/exchange.toml

@ -19,8 +19,8 @@ marketSubscribeLimit = 16
consumeBatch = 1024 consumeBatch = 1024
consumeLater = 2000 # 时间到达later或者数据累计到batch触发consume consumeLater = 2000 # 时间到达later或者数据累计到batch触发consume
# httpProxy = "" # httpProxy = ""
httpProxy = "http://192.168.1.5:7890" # httpProxy = "http://192.168.1.5:7890"
# httpProxy = "http://10.255.183.209:7890" httpProxy = "http://10.255.183.209:7890"
# 模拟盘API交易地址如下: # 模拟盘API交易地址如下:
# REST:https://www.okx.com # REST:https://www.okx.com

68
internal/trading/trading_grpc_server.go

@ -2,8 +2,10 @@ package trading
import ( import (
"context" "context"
"fmt"
"sig-pub/api/pb" "sig-pub/api/pb"
"sig-pub/pkg/types" "sig-pub/pkg/types"
"sig-pub/pkg/utils/collect"
"sig-pub/pkg/utils/lang" "sig-pub/pkg/utils/lang"
"sig-pub/pkg/utils/times" "sig-pub/pkg/utils/times"
"sig-pub/pkg/zlog" "sig-pub/pkg/zlog"
@ -26,6 +28,71 @@ func (svr *TradingGrpcServer) Init() (err error) {
return return
} }
func (svr *TradingGrpcServer) Indicators(ctx context.Context, req *pb.ReqIndicators) (rsp *pb.RspIndicators, err error) {
rsp = new(pb.RspIndicators)
rsp.Indicators = svr.tradingService.Indicators()
return
}
func (svr *TradingGrpcServer) IndicatorMetas(ctx context.Context, req *pb.ReqIndicatorMetas) (rsp *pb.RspIndicatorMetas, err error) {
indMetas, err := svr.tradingService.IndicatorMeta(req.Indicators...)
if err != nil {
return
}
indPlots, err := svr.tradingService.IndicatorPlots(req.Indicators...)
if err != nil {
return
}
rsp = new(pb.RspIndicatorMetas)
for _, indName := range req.Indicators {
// 指标元数据
meta := indMetas[indName]
p := &pb.IndicatorMeta{
Name: indName,
Desc: meta.Desc,
State: meta.State,
}
// 指标输入参数
for _, input := range meta.Input {
p.Input = append(p.Input, &pb.InputArg{
Name: input.Name,
Desc: input.Desc,
Type: int32(input.Type),
Default: lang.Ternary(input.Default == nil, "", fmt.Sprintf("%v", input.Default)),
Options: collect.Mapping(input.Options, func(opt types.InputOption) *pb.InputOption {
return &pb.InputOption{
Name: opt.Name,
Desc: opt.Desc,
}
}),
})
}
// 指标绘图属性
for _, plot := range indPlots[indName] {
ps := &pb.IndicatorPlotSeries{
Name: plot.Name,
State: plot.State,
Type: int32(plot.Type),
}
if ps.Props, err = structpb.NewStruct(plot.Props); err != nil {
return
}
for _, exp := range plot.Exps {
pe := &pb.IndicatorPlotExp{
Exp: exp.Exp,
}
if pe.Props, err = structpb.NewStruct(exp.Props); err != nil {
return
}
ps.Exps = append(ps.Exps, pe)
}
p.Plots = append(p.Plots, ps)
}
rsp.Metas = append(rsp.Metas, p)
}
return
}
func (svr *TradingGrpcServer) IndicatorPlots(ctx context.Context, req *pb.ReqIndicatorPlots) (rsp *pb.RspIndicatorPlots, err error) { func (svr *TradingGrpcServer) IndicatorPlots(ctx context.Context, req *pb.ReqIndicatorPlots) (rsp *pb.RspIndicatorPlots, err error) {
indPlots, err := svr.tradingService.IndicatorPlots(req.Indicators...) indPlots, err := svr.tradingService.IndicatorPlots(req.Indicators...)
if err != nil { if err != nil {
@ -37,6 +104,7 @@ func (svr *TradingGrpcServer) IndicatorPlots(ctx context.Context, req *pb.ReqInd
p := &pb.IndicatorPlot{Indicator: indName} p := &pb.IndicatorPlot{Indicator: indName}
for _, plot := range plots { for _, plot := range plots {
ps := &pb.IndicatorPlotSeries{ ps := &pb.IndicatorPlotSeries{
Name: plot.Name,
State: plot.State, State: plot.State,
Type: int32(plot.Type), Type: int32(plot.Type),
} }

39
internal/trading/trading_service.go

@ -18,6 +18,7 @@ import (
"sig-pub/pkg/utils/lang" "sig-pub/pkg/utils/lang"
"sig-pub/pkg/utils/times" "sig-pub/pkg/utils/times"
"sig-pub/pkg/zlog" "sig-pub/pkg/zlog"
"sort"
"sig-pub/internal/trading/backtest" "sig-pub/internal/trading/backtest"
"sig-pub/internal/trading/sig" "sig-pub/internal/trading/sig"
@ -199,16 +200,50 @@ func (svc *TradingService) fetchHistoryKlineSeries(ctx context.Context, sr *pb.S
return return
} }
// Indicators
func (svc *TradingService) Indicators() (indicatorNames []string) {
svc.indicatorReg.RangeIndicators(func(k string, v indicator.IIndicator) bool {
indicatorNames = append(indicatorNames, k)
return true
})
sort.Strings(indicatorNames)
return
}
// IndicatorMeta
func (svc *TradingService) IndicatorMeta(indicatorNames ...string) (indMetas map[string]indicator.IndicatorMeta, err error) {
indMetas = make(map[string]indicator.IndicatorMeta, len(indicatorNames))
for _, indicatorName := range indicatorNames {
ind, ok := svc.indicatorReg.Indicator(indicatorName)
if !ok {
err = fmt.Errorf("indicator %s not exists", indicatorName)
return nil, err
}
indMetas[indicatorName] = ind.Meta()
}
return
}
// IndicatorPlots // IndicatorPlots
func (svc *TradingService) IndicatorPlots(indicatorNames ...string) (indPlots map[string][]indicator.Plot, err error) { func (svc *TradingService) IndicatorPlots(indicatorNames ...string) (indPlots map[string][]indicator.Plot, err error) {
indPlots = make(map[string][]indicator.Plot, len(indicatorNames)) indPlots = make(map[string][]indicator.Plot, len(indicatorNames))
for _, indicatorName := range indicatorNames { for _, indicatorName := range indicatorNames {
plots, e := svc.indicatorPlots(indicatorName)
if e != nil {
return nil, e
}
indPlots[indicatorName] = plots
}
return
}
func (svc *TradingService) indicatorPlots(indicatorName string) (plots []indicator.Plot, err error) {
ind, ok := svc.indicatorReg.Indicator(indicatorName) ind, ok := svc.indicatorReg.Indicator(indicatorName)
if !ok { if !ok {
err = fmt.Errorf("indicator %s not exists", indicatorName) err = fmt.Errorf("indicator %s not exists", indicatorName)
return return
} }
plots := ind.Meta().Plots plots = ind.Meta().Plots
if len(plots) == 0 { if len(plots) == 0 {
plots = append(plots, indicator.Plot{ plots = append(plots, indicator.Plot{
State: "vector", State: "vector",
@ -216,8 +251,6 @@ func (svc *TradingService) IndicatorPlots(indicatorNames ...string) (indPlots ma
Props: indicator.PlotProps{"color": indicator.ColorBlue}, Props: indicator.PlotProps{"color": indicator.ColorBlue},
}) })
} }
indPlots[indicatorName] = plots
}
return return
} }

87
pkg/indicator/boll.go

@ -5,92 +5,51 @@ import (
"sig-pub/pkg/types" "sig-pub/pkg/types"
) )
// BollMB 布林带中轨 // Boll 布林带
type BollMB struct { type Boll struct {
} }
func (c *BollMB) Meta() IndicatorMeta { func (c *Boll) Meta() IndicatorMeta {
return IndicatorMeta{ return IndicatorMeta{
Name: "BollMB", Name: "Boll",
Input: []types.InputArg{ Input: []types.InputArg{
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"}, {Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"},
{Name: "pt", Type: types.InputTypeKPriceType, Desc: "k线序列类型"},
}, },
} State: []string{"ub", "lb"},
} Plots: []Plot{
{Name: "中轨", State: "vector", Type: PlotHistogram, Props: PlotProps{"color": ColorOrange}},
func (c *BollMB) CandlePeriods(ctx IIndicatorContext) int16 { {Name: "上轨", State: "ub", Type: PlotLine, Props: PlotProps{"color": ColorRed2}},
return ctx.Input().Int16("window") {Name: "下轨", State: "lb", Type: PlotLine, Props: PlotProps{"color": ColorRed2}},
} {Name: "布林带阴影", State: "ub,lb", Type: PlotShadow, Props: PlotProps{"color": "rgba(247, 169, 167, 0.3)"}},
func (c *BollMB) Calculate(ctx IIndicatorContext) (vector float64) {
window := ctx.Input().Int16("window")
closeSeries := ctx.Series(0, int16(window)).Close()
vector = closeSeries.Avg()
return
}
// BollUB 布林带上轨
type BollUB struct {
}
func (c *BollUB) Meta() IndicatorMeta {
return IndicatorMeta{
Name: "BollUB",
Input: []types.InputArg{
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"},
}, },
} }
} }
func (c *BollUB) CandlePeriods(ctx IIndicatorContext) int16 { func (c *Boll) CandlePeriods(ctx IIndicatorContext) int16 {
return ctx.Input().Int16("window") return ctx.Input().Int16("window")
} }
func (c *BollUB) Calculate(ctx IIndicatorContext) (vector float64) { func (c *Boll) Calculate(ctx IIndicatorContext) (vector float64) {
window := ctx.Input().Int16("window") window := ctx.Input().Int16("window")
closeSeries := ctx.Series(0, int16(window)).Close() pt := ctx.Input().PriceType()
mb := closeSeries.Avg() priceSeries := ctx.Series(0, int16(window)).Price(pt)
mb := priceSeries.Avg() // 中轨
vector = mb
// 标准差σ_t = sqrt(∑(P-MB)^2 / (n-1)) // 标准差σ_t = sqrt(∑(P-MB)^2 / (n-1))
sst := float64(0) sst := float64(0)
for _, p := range closeSeries { for _, p := range priceSeries {
sst += math.Pow(p-mb, 2) sst += math.Pow(p-mb, 2)
} }
sigma := math.Sqrt(sst / float64(window-1)) sigma := math.Sqrt(sst / float64(window-1))
vector = mb + 2*sigma // BollUB 布林带上轨
return ub := mb + 2*sigma
} ctx.State().Set("ub", ub)
// BollLB 布林带下轨 // BollLB 布林带下轨
type BollLB struct { lb := mb - 2*sigma
} ctx.State().Set("lb", lb)
func (c *BollLB) Meta() IndicatorMeta {
return IndicatorMeta{
Name: "BollLB",
Input: []types.InputArg{
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"},
},
}
}
func (c *BollLB) CandlePeriods(ctx IIndicatorContext) int16 {
return ctx.Input().Int16("window")
}
func (c *BollLB) Calculate(ctx IIndicatorContext) (vector float64) {
window := ctx.Input().Int16("window")
closeSeries := ctx.Series(0, int16(window)).Close()
mb := closeSeries.Avg()
// 标准差σ_t = sqrt(∑(P-MB)^2 / (n-1))
sst := float64(0)
for _, p := range closeSeries {
sst += math.Pow(p-mb, 2)
}
sigma := math.Sqrt(sst / float64(window-1))
vector = mb - 2*sigma
return return
} }

3
pkg/indicator/indicator_plot.go

@ -1,6 +1,7 @@
package indicator package indicator
type Plot struct { type Plot struct {
Name string `json:"name"` // 绘图名称
State string `json:"state"` // vector, stateName State string `json:"state"` // vector, stateName
Type PlotType `json:"series"` // 绘图类型 线/柱 Type PlotType `json:"series"` // 绘图类型 线/柱
Props PlotProps `json:"props"` // 绘图属性 Props PlotProps `json:"props"` // 绘图属性
@ -23,6 +24,7 @@ const (
PlotLine PlotLine
PlotHistogram PlotHistogram
PlotArea PlotArea
PlotShadow
) )
// Series 绘图颜色 // Series 绘图颜色
@ -34,4 +36,5 @@ const (
ColorYellow string = "#cdcf36" ColorYellow string = "#cdcf36"
ColorBlue string = "#556cd6" ColorBlue string = "#556cd6"
ColorPurple string = "#e48dce" ColorPurple string = "#e48dce"
ColorOrange string = "#dd5e0aff"
) )

8
pkg/indicator/indicator_registry.go

@ -25,9 +25,7 @@ func (r *IndicatorRegistry) Init() (err error) {
r.MustRegistIndicator(&MACD{}) r.MustRegistIndicator(&MACD{})
r.MustRegistIndicator(&OBV{}) r.MustRegistIndicator(&OBV{})
r.MustRegistIndicator(&WOBV{}) r.MustRegistIndicator(&WOBV{})
r.MustRegistIndicator(&BollMB{}) r.MustRegistIndicator(&Boll{})
r.MustRegistIndicator(&BollUB{})
r.MustRegistIndicator(&BollLB{})
r.MustRegistIndicator(&SuperTrend{}) r.MustRegistIndicator(&SuperTrend{})
r.MustRegistIndicator(&ADX{}) r.MustRegistIndicator(&ADX{})
r.MustRegistIndicator(&KDJ{}) r.MustRegistIndicator(&KDJ{})
@ -55,3 +53,7 @@ func (r *IndicatorRegistry) MustRegistIndicator(ind IIndicator) {
func (r *IndicatorRegistry) Indicator(name string) (indW IIndicator, ok bool) { func (r *IndicatorRegistry) Indicator(name string) (indW IIndicator, ok bool) {
return r.indicators.Load(name) return r.indicators.Load(name)
} }
func (r *IndicatorRegistry) RangeIndicators(fn func(k string, v IIndicator) bool) {
r.indicators.Range(fn)
}

6
pkg/indicator/macd.go

@ -18,12 +18,12 @@ func (c *MACD) Meta() IndicatorMeta {
}, },
State: []string{"dif", "dea"}, State: []string{"dif", "dea"},
Plots: []Plot{ Plots: []Plot{
{State: "vector", Type: PlotHistogram, Props: PlotProps{"color": ColorGreen2}, Exps: []PlotExp{ {Name: "MACD", State: "vector", Type: PlotHistogram, Props: PlotProps{"color": ColorGreen2}, Exps: []PlotExp{
{Exp: "vector < 0", Props: PlotProps{"color": ColorRed2}}, {Exp: "vector < 0", Props: PlotProps{"color": ColorRed2}},
{Exp: "vector >= 0", Props: PlotProps{"color": ColorGreen2}}, {Exp: "vector >= 0", Props: PlotProps{"color": ColorGreen2}},
}}, }},
{State: "dif", Type: PlotLine, Props: PlotProps{"color": ColorYellow}}, {Name: "DIF线", State: "dif", Type: PlotLine, Props: PlotProps{"color": ColorYellow}},
{State: "dea", Type: PlotLine, Props: PlotProps{"color": ColorRed}}, {Name: "DEA线", State: "dea", Type: PlotLine, Props: PlotProps{"color": ColorRed}},
}, },
} }
} }

2
pkg/types/input.go

@ -158,7 +158,7 @@ const (
InputTypeInt InputTypeInt
InputTypeUInt InputTypeUInt
InputTypeTime InputTypeTime
InputTypeKPriceType InputTypeKPriceType // K线价格类型
InputTypeUFloats // float数组 InputTypeUFloats // float数组
InputTypeUFloats2D // float二维数组 InputTypeUFloats2D // float二维数组
InputTypeSelect // 单选 InputTypeSelect // 单选

Loading…
Cancel
Save