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; //
}
//
message IndicatorMeta {
string name = 1; //
string desc = 2; //
repeated InputArg input = 3; //
repeated string state = 4; //
repeated IndicatorPlotSeries plots = 5; //
}
//
message IndicatorPlot {
string indicator = 1;
repeated IndicatorPlotSeries plots = 9;
}
message IndicatorPlotSeries {
string state = 1;
int32 type = 2;
google.protobuf.Struct props = 3;
repeated IndicatorPlotExp exps = 4;
string name = 1;
string state = 2;
int32 type = 3;
google.protobuf.Struct props = 4;
repeated IndicatorPlotExp exps = 5;
}
message IndicatorPlotExp {
string exp = 1;
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 {
string name = 1; // names

19
api/trading.proto

@ -7,6 +7,12 @@ option go_package = "./pb";
//
service TradingService {
//
rpc Indicators(ReqIndicators) returns (RspIndicators);
//
rpc IndicatorMetas(ReqIndicatorMetas) returns (RspIndicatorMetas);
//
rpc IndicatorPlots(ReqIndicatorPlots) returns (RspIndicatorPlots);
@ -26,6 +32,19 @@ service TradingService {
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 {
repeated string indicators = 1;
}

4
config/exchange.toml

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

68
internal/trading/trading_grpc_server.go

@ -2,8 +2,10 @@ package trading
import (
"context"
"fmt"
"sig-pub/api/pb"
"sig-pub/pkg/types"
"sig-pub/pkg/utils/collect"
"sig-pub/pkg/utils/lang"
"sig-pub/pkg/utils/times"
"sig-pub/pkg/zlog"
@ -26,6 +28,71 @@ func (svr *TradingGrpcServer) Init() (err error) {
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) {
indPlots, err := svr.tradingService.IndicatorPlots(req.Indicators...)
if err != nil {
@ -37,6 +104,7 @@ func (svr *TradingGrpcServer) IndicatorPlots(ctx context.Context, req *pb.ReqInd
p := &pb.IndicatorPlot{Indicator: indName}
for _, plot := range plots {
ps := &pb.IndicatorPlotSeries{
Name: plot.Name,
State: plot.State,
Type: int32(plot.Type),
}

39
internal/trading/trading_service.go

@ -18,6 +18,7 @@ import (
"sig-pub/pkg/utils/lang"
"sig-pub/pkg/utils/times"
"sig-pub/pkg/zlog"
"sort"
"sig-pub/internal/trading/backtest"
"sig-pub/internal/trading/sig"
@ -199,16 +200,50 @@ func (svc *TradingService) fetchHistoryKlineSeries(ctx context.Context, sr *pb.S
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
func (svc *TradingService) IndicatorPlots(indicatorNames ...string) (indPlots map[string][]indicator.Plot, err error) {
indPlots = make(map[string][]indicator.Plot, len(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)
if !ok {
err = fmt.Errorf("indicator %s not exists", indicatorName)
return
}
plots := ind.Meta().Plots
plots = ind.Meta().Plots
if len(plots) == 0 {
plots = append(plots, indicator.Plot{
State: "vector",
@ -216,8 +251,6 @@ func (svc *TradingService) IndicatorPlots(indicatorNames ...string) (indPlots ma
Props: indicator.PlotProps{"color": indicator.ColorBlue},
})
}
indPlots[indicatorName] = plots
}
return
}

87
pkg/indicator/boll.go

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

3
pkg/indicator/indicator_plot.go

@ -1,6 +1,7 @@
package indicator
type Plot struct {
Name string `json:"name"` // 绘图名称
State string `json:"state"` // vector, stateName
Type PlotType `json:"series"` // 绘图类型 线/柱
Props PlotProps `json:"props"` // 绘图属性
@ -23,6 +24,7 @@ const (
PlotLine
PlotHistogram
PlotArea
PlotShadow
)
// Series 绘图颜色
@ -34,4 +36,5 @@ const (
ColorYellow string = "#cdcf36"
ColorBlue string = "#556cd6"
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(&OBV{})
r.MustRegistIndicator(&WOBV{})
r.MustRegistIndicator(&BollMB{})
r.MustRegistIndicator(&BollUB{})
r.MustRegistIndicator(&BollLB{})
r.MustRegistIndicator(&Boll{})
r.MustRegistIndicator(&SuperTrend{})
r.MustRegistIndicator(&ADX{})
r.MustRegistIndicator(&KDJ{})
@ -55,3 +53,7 @@ func (r *IndicatorRegistry) MustRegistIndicator(ind IIndicator) {
func (r *IndicatorRegistry) Indicator(name string) (indW IIndicator, ok bool) {
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"},
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": ColorGreen2}},
}},
{State: "dif", Type: PlotLine, Props: PlotProps{"color": ColorYellow}},
{State: "dea", Type: PlotLine, Props: PlotProps{"color": ColorRed}},
{Name: "DIF线", State: "dif", Type: PlotLine, Props: PlotProps{"color": ColorYellow}},
{Name: "DEA线", State: "dea", Type: PlotLine, Props: PlotProps{"color": ColorRed}},
},
}
}

2
pkg/types/input.go

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

Loading…
Cancel
Save