diff --git a/api/pub.proto b/api/pub.proto index b83febc..2fc7cda 100644 --- a/api/pub.proto +++ b/api/pub.proto @@ -168,9 +168,9 @@ message IndicatorPlotSeries { string state = 1; int32 type = 2; google.protobuf.Struct props = 3; - repeated IndicatorStateToPlotProps state2Props = 4; + repeated IndicatorStateEnumProps stateEnumProps = 4; } -message IndicatorStateToPlotProps { +message IndicatorStateEnumProps { string state = 1; map enumProps = 2; } diff --git a/api/trading.proto b/api/trading.proto index 8fda022..7a6ece4 100644 --- a/api/trading.proto +++ b/api/trading.proto @@ -33,7 +33,7 @@ message RspIndicatorSeries { } message IndicatorState { string state = 1; - repeated double value = 2; + repeated double matrix = 2; } message ReqStrategySeries { diff --git a/internal/trading/trading_grpc_server.go b/internal/trading/trading_grpc_server.go index 1ea5f4c..4732c5d 100644 --- a/internal/trading/trading_grpc_server.go +++ b/internal/trading/trading_grpc_server.go @@ -3,8 +3,11 @@ package trading import ( "context" "sig-pub/api/pb" + "sig-pub/pkg/indicator" "sig-pub/pkg/types" "sig-pub/pkg/utils/times" + + "google.golang.org/protobuf/types/known/structpb" ) type TradingGrpcServer struct { @@ -27,8 +30,48 @@ func (svr *TradingGrpcServer) IndicatorPlots(ctx context.Context, req *pb.ReqInd if err != nil { return } - for _, plot := range plots { - _ = plot + rsp = new(pb.RspIndicatorPlots) + for name, plot := range plots { + p := &pb.IndicatorPlot{Indicator: name} + p.Series, err = mappingIndicatorPlotSeries(plot.Series) + if err != nil { + return nil, err + } + for _, series := range plot.StateSeries { + ps, err := mappingIndicatorPlotSeries(series) + if err != nil { + return rsp, err + } + p.StateSeries = append(p.StateSeries, ps) + } + rsp.Plots = append(rsp.Plots, p) + } + return +} + +func mappingIndicatorPlotSeries(ps indicator.PlotSeries) (splot *pb.IndicatorPlotSeries, err error) { + seriesProps, err := structpb.NewStruct(ps.Props) + if err != nil { + return + } + splot = &pb.IndicatorPlotSeries{ + State: ps.State, + Type: int32(ps.Type), + Props: seriesProps, + StateEnumProps: nil, + } + for state, valueProps := range ps.State2Props { + s2p := &pb.IndicatorStateEnumProps{ + State: state, + EnumProps: make(map[int32]*structpb.Struct), + } + for v, props := range valueProps { + s2p.EnumProps[int32(v)], err = structpb.NewStruct(props) + if err != nil { + return + } + } + splot.StateEnumProps = append(splot.StateEnumProps, s2p) } return } @@ -45,8 +88,8 @@ func (svr *TradingGrpcServer) IndicatorSeries(ctx context.Context, req *pb.ReqIn rsp.Times = times for state, value := range states { rsp.States = append(rsp.States, &pb.IndicatorState{ - State: state, - Value: value, + State: state, + Matrix: value, }) } return diff --git a/internal/trading/trading_service.go b/internal/trading/trading_service.go index 6560d0d..3a2dcef 100644 --- a/internal/trading/trading_service.go +++ b/internal/trading/trading_service.go @@ -265,7 +265,7 @@ func (svc *TradingService) IndicatorSeries(ctx context.Context, indicatorName st // 状态填充 for _, state := range indicatorStates { sv, _ := indicatorContext.State().Get(state, 0) - states[state] = append(states[state], sv) + states[state] = append(states[state], math.Round(sv*pow)/pow) } return }) diff --git a/pkg/indicator/indicator_plot.go b/pkg/indicator/indicator_plot.go index c8481ac..5ba17a0 100644 --- a/pkg/indicator/indicator_plot.go +++ b/pkg/indicator/indicator_plot.go @@ -29,10 +29,14 @@ const ( ) // Series 绘图颜色 -type Color string +const ( + ColorRed string = "red" + ColorGreen string = "green" + ColorYellow string = "yellow" + ColorBlue string = "blue" +) const ( - ColorRed Color = "red" - ColorGreen Color = "green" - ColorYellow Color = "yellow" +// calc tokens: > >= == < <= + - * / % +// 大于等于小于 In, NotIn ... ) diff --git a/pkg/indicator/indicator_registry.go b/pkg/indicator/indicator_registry.go index a1cc56d..621f915 100644 --- a/pkg/indicator/indicator_registry.go +++ b/pkg/indicator/indicator_registry.go @@ -22,6 +22,7 @@ func (r *IndicatorRegistry) Init() (err error) { r.MustRegistIndicator(&SMA{}) r.MustRegistIndicator(&ATR{}) r.MustRegistIndicator(&EMA{}) + r.MustRegistIndicator(&MACD{}) r.MustRegistIndicator(&MacdDIF{}) r.MustRegistIndicator(&MacdDEA{}) r.MustRegistIndicator(&Macd{}) diff --git a/pkg/indicator/macd.go b/pkg/indicator/macd.go index fe2b170..763f3ff 100644 --- a/pkg/indicator/macd.go +++ b/pkg/indicator/macd.go @@ -1,110 +1,68 @@ package indicator -import ( - "sig-pub/pkg/types" -) +import "sig-pub/pkg/types" -// Macd macd柱状图计算 - -// Macd 拆分成: Macd(柱状图), MacdDIF线, MacdDEA(信号线) -// 计算 MacdDIF 线 (DIF): 反映短期趋势与长期趋势的“收敛/散度” -// Macd: https://www.investopedia.com/terms/m/macd.asp -type Macd struct { +type MACD struct { } -func (c *Macd) Meta() IndicatorMeta { +func (c *MACD) Meta() IndicatorMeta { return IndicatorMeta{ - Name: "Macd", + Name: "MACD", Input: []types.InputArg{ - {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, - {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, - {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期"}, + {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, // 12 + {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, // 26 + {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期"}, // 9 }, - } -} - -func (c *Macd) CandlePeriods(ctx IIndicatorContext) int16 { - return max( - ctx.Indicator("MacdDIF", ctx.Input()).CandlePeriods(), - ctx.Indicator("MacdDEA", ctx.Input()).CandlePeriods(), - ) -} - -func (c *Macd) Calculate(ctx IIndicatorContext) (vector float64) { - macd_dea := ctx.Indicator("MacdDEA", ctx.Input()).Get(0) - macd_dif := ctx.Indicator("MacdDIF", ctx.Input()).Get(0) - vector = (macd_dif - macd_dea) * 2 - return -} - -type MacdDIF struct { -} - -// indicator interface -func (c *MacdDIF) Meta() IndicatorMeta { - return IndicatorMeta{ - Name: "MacdDIF", - Input: []types.InputArg{ - {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, - {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, + State: []string{"dif", "dea"}, + Plot: Plot{ + Series: PlotSeries{Type: PlotSeriesHistogram, Props: PlotProps{"color": ColorGreen}, State2Props: map[string]map[float64]PlotProps{ + "vector": { + -1: {"vector >= 0": 1, "color": ColorRed}, + 1: {"vector < 0": 1, "color": ColorGreen}, + }, + }}, + StateSeries: []PlotSeries{ + {State: "dif", Type: PlotSeriesLine, Props: PlotProps{"color": ColorYellow}}, + {State: "dea", Type: PlotSeriesLine, Props: PlotProps{"color": ColorBlue}}, + }, }, } } -func (c *MacdDIF) CandlePeriods(ctx IIndicatorContext) int16 { +func (c *MACD) CandlePeriods(ctx IIndicatorContext) int16 { return max( ctx.Indicator("EMA", ctx.Input().Int16("fast")).CandlePeriods(), ctx.Indicator("EMA", ctx.Input().Int16("slow")).CandlePeriods(), - ) + ) + ctx.Input().Int16("singal") + 1 } -// Calculate 计算单根k线sma指标 -func (c *MacdDIF) Calculate(ctx IIndicatorContext) (vector float64) { - fast := ctx.Input().Int16("fast") // 12 - slow := ctx.Input().Int16("slow") // 26 +// Calculate macd计算从第max(fast, slow)期开始稳定 +func (c *MACD) Calculate(ctx IIndicatorContext) (vector float64) { + fast := ctx.Input().Int16("fast") + slow := ctx.Input().Int16("slow") + singal := ctx.Input().Int16("singal") - // macd计算从第max(fast, slow)期开始稳定 + // macd dif fastEma := ctx.Indicator("EMA", fast).Get(0) slowEma := ctx.Indicator("EMA", slow).Get(0) - macd := fastEma - slowEma - vector = macd - return -} - -// MacdDEA macd信号线计算 -type MacdDEA struct { -} + macd_dif := fastEma - slowEma + ctx.State().Set("dif", macd_dif) -func (c *MacdDEA) Meta() IndicatorMeta { - return IndicatorMeta{ - Name: "MacdDEA", - Input: []types.InputArg{ - {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, - {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, - {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期"}, - }, - } -} - -func (c *MacdDEA) CandlePeriods(ctx IIndicatorContext) int16 { - return ctx.Indicator("MacdDIF", ctx.Input()).CandlePeriods() + ctx.Input().Int16("singal") + 1 -} - -func (c *MacdDEA) Calculate(ctx IIndicatorContext) (vector float64) { - singal := ctx.Input().Int16("singal") // 9 - - deaPrev, ok := ctx.State().Get("_vector", 1) + // macd dea + deaPrev, ok := ctx.State().Get("dea", 1) if !ok { // 初始值前9期的 MACD_DIF SMA - macdDifs := ctx.Indicator("MacdDIF", ctx.Input()).Series(1, singal) - deaPrev = macdDifs.Avg() + difs, ok := ctx.State().Series("dif", 1, singal) + if !ok { + return + } + deaPrev = difs.Avg() } - macd_dif := ctx.Indicator("MacdDIF", ctx.Input()).Get(0) - // 计算DEA - beta := 2 / float64(singal+1) - dea := beta*macd_dif + (1-beta)*deaPrev - ctx.State().Set("_vector", dea) - vector = dea + // macd hist + beta := 2 / float64(singal+1) + macd_dea := beta*macd_dif + (1-beta)*deaPrev + ctx.State().Set("dea", macd_dea) + vector = (macd_dif - macd_dea) * 2 return } diff --git a/pkg/indicator/macd0.go b/pkg/indicator/macd0.go new file mode 100644 index 0000000..fe2b170 --- /dev/null +++ b/pkg/indicator/macd0.go @@ -0,0 +1,110 @@ +package indicator + +import ( + "sig-pub/pkg/types" +) + +// Macd macd柱状图计算 + +// Macd 拆分成: Macd(柱状图), MacdDIF线, MacdDEA(信号线) +// 计算 MacdDIF 线 (DIF): 反映短期趋势与长期趋势的“收敛/散度” +// Macd: https://www.investopedia.com/terms/m/macd.asp +type Macd struct { +} + +func (c *Macd) Meta() IndicatorMeta { + return IndicatorMeta{ + Name: "Macd", + Input: []types.InputArg{ + {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, + {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, + {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期"}, + }, + } +} + +func (c *Macd) CandlePeriods(ctx IIndicatorContext) int16 { + return max( + ctx.Indicator("MacdDIF", ctx.Input()).CandlePeriods(), + ctx.Indicator("MacdDEA", ctx.Input()).CandlePeriods(), + ) +} + +func (c *Macd) Calculate(ctx IIndicatorContext) (vector float64) { + macd_dea := ctx.Indicator("MacdDEA", ctx.Input()).Get(0) + macd_dif := ctx.Indicator("MacdDIF", ctx.Input()).Get(0) + vector = (macd_dif - macd_dea) * 2 + return +} + +type MacdDIF struct { +} + +// indicator interface +func (c *MacdDIF) Meta() IndicatorMeta { + return IndicatorMeta{ + Name: "MacdDIF", + Input: []types.InputArg{ + {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, + {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, + }, + } +} + +func (c *MacdDIF) CandlePeriods(ctx IIndicatorContext) int16 { + return max( + ctx.Indicator("EMA", ctx.Input().Int16("fast")).CandlePeriods(), + ctx.Indicator("EMA", ctx.Input().Int16("slow")).CandlePeriods(), + ) +} + +// Calculate 计算单根k线sma指标 +func (c *MacdDIF) Calculate(ctx IIndicatorContext) (vector float64) { + fast := ctx.Input().Int16("fast") // 12 + slow := ctx.Input().Int16("slow") // 26 + + // macd计算从第max(fast, slow)期开始稳定 + fastEma := ctx.Indicator("EMA", fast).Get(0) + slowEma := ctx.Indicator("EMA", slow).Get(0) + macd := fastEma - slowEma + vector = macd + return +} + +// MacdDEA macd信号线计算 +type MacdDEA struct { +} + +func (c *MacdDEA) Meta() IndicatorMeta { + return IndicatorMeta{ + Name: "MacdDEA", + Input: []types.InputArg{ + {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, + {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, + {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期"}, + }, + } +} + +func (c *MacdDEA) CandlePeriods(ctx IIndicatorContext) int16 { + return ctx.Indicator("MacdDIF", ctx.Input()).CandlePeriods() + ctx.Input().Int16("singal") + 1 +} + +func (c *MacdDEA) Calculate(ctx IIndicatorContext) (vector float64) { + singal := ctx.Input().Int16("singal") // 9 + + deaPrev, ok := ctx.State().Get("_vector", 1) + if !ok { + // 初始值前9期的 MACD_DIF SMA + macdDifs := ctx.Indicator("MacdDIF", ctx.Input()).Series(1, singal) + deaPrev = macdDifs.Avg() + } + macd_dif := ctx.Indicator("MacdDIF", ctx.Input()).Get(0) + // 计算DEA + beta := 2 / float64(singal+1) + dea := beta*macd_dif + (1-beta)*deaPrev + ctx.State().Set("_vector", dea) + + vector = dea + return +} diff --git a/pkg/strategy/gold_x.go b/pkg/strategy/gold_x.go index 793caf8..b62c9a4 100644 --- a/pkg/strategy/gold_x.go +++ b/pkg/strategy/gold_x.go @@ -32,16 +32,16 @@ func (s *GoldX) Init(input types.Input) (err error) { func (s *GoldX) CandlePeriods(ctx ISingleSigStrategyContext) int16 { return max( - ctx.Indicator("Macd", ctx.Input()).CandlePeriods(), - ctx.Indicator("MacdDIF", ctx.Input()).CandlePeriods(), - ctx.Indicator("MacdDEA", ctx.Input()).CandlePeriods(), + ctx.Indicator("MACD", ctx.Input()).CandlePeriods(), ) } func (s *GoldX) Update(ctx ISingleSigStrategyContext) (side types.Side) { - macdHist := ctx.Indicator("Macd", ctx.Input()).Get(0) // macd柱状图 - macdDea := ctx.Indicator("MacdDEA", ctx.Input()).Series(0, 2) // macd_dea信号线 - macdDif := ctx.Indicator("MacdDIF", ctx.Input()).Series(0, 2) // macd_dif线 + macd := ctx.Indicator("MACD", ctx.Input()) + + macdHist := macd.Get(0) + macdDea := macd.StateSeries("dea", 0, 2) + macdDif := macd.StateSeries("dif", 0, 2) // todo 包装方法 crossover/crossunder // 1.MACD DIF线接近或上穿零轴(表示整体多头市场) diff --git a/pkg/strategy/super_trend2_macd.go b/pkg/strategy/super_trend2_macd.go index aeec604..53c770e 100644 --- a/pkg/strategy/super_trend2_macd.go +++ b/pkg/strategy/super_trend2_macd.go @@ -35,9 +35,7 @@ func (s *SuperTrend2Macd) CandlePeriods(ctx ISingleSigStrategyContext) int16 { return max( ctx.Indicator("SuperTrend", types.Input{"window": 10, "mul": 3}).CandlePeriods(), ctx.Indicator("SuperTrend", types.Input{"window": 14, "mul": 2}).CandlePeriods(), - ctx.Indicator("MacdDEA", types.Input{"fast": 12, "slow": 26, "singal": 9}).CandlePeriods(), - ctx.Indicator("MacdDIF", types.Input{"fast": 12, "slow": 26, "singal": 9}).CandlePeriods(), - 21, + ctx.Indicator("MACD", types.Input{"fast": 12, "slow": 26, "singal": 9}).CandlePeriods(), ) } @@ -51,8 +49,9 @@ func (s *SuperTrend2Macd) Update(ctx ISingleSigStrategyContext) (side types.Side trend1Directions := superTrend1.StateSeries("direction", 0, 2) trend2Directions := superTrend2.StateSeries("direction", 0, 2) - macdDea := ctx.Indicator("MacdDEA", types.Input{"fast": 12, "slow": 26, "singal": 9}).Series(0, 2) // macd_dea信号线 - macdDif := ctx.Indicator("MacdDIF", types.Input{"fast": 12, "slow": 26, "singal": 9}).Series(0, 2) // macd_dif线 + macd := ctx.Indicator("MACD", types.Input{"fast": 12, "slow": 26, "singal": 9}) + macdDea := macd.StateSeries("dea", 0, 2) + macdDif := macd.StateSeries("dif", 0, 2) crossover := macdDif[0] > macdDea[0] && macdDif[1] < macdDea[1] // 金叉 crossunder := macdDif[0] < macdDea[0] && macdDif[1] > macdDea[1] // 死叉