diff --git a/README.md b/README.md index f4c9fa8..712e6d5 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,5 @@ strategy0: 趋势追踪,增长趋势, [investing-algorithm-framework](https://github.com/coding-kitties/investing-algorithm-framework) 回测信号可视化, /trading/strategySeries 一样从postgres拉信号/订单数据 + +go plugin插件化(指标/策略), docker中运行回测 diff --git a/internal/sig/sig_server.go b/internal/sig/sig_server.go index 74a978f..1fd7e05 100644 --- a/internal/sig/sig_server.go +++ b/internal/sig/sig_server.go @@ -94,7 +94,7 @@ func (s *SigServer) handleGrpcGenericCall(c *gin.Context) { // put session ctx := context.Background() ctx = session.PutSubject(ctx, session.NewRpcSubject("123456")) - ctx, cancel = context.WithTimeout(ctx, time.Second*10) + ctx, cancel = context.WithTimeout(ctx, time.Second*20) defer cancel() // todo config call options diff --git a/internal/trading/backtest/sig_strategy_backtester.go b/internal/trading/backtest/sig_strategy_backtester.go index 4cfe9b0..e3324e3 100644 --- a/internal/trading/backtest/sig_strategy_backtester.go +++ b/internal/trading/backtest/sig_strategy_backtester.go @@ -75,7 +75,7 @@ func (b *SigStrategyBacktester) singleStrategySeries(ctx context.Context, sigStr interval := types.Interval(sr.Interval) kSeries := intervalKlineSeries.ComputeIfAbsent(interval, func() *sig.KlineSeries { return sig.NewKlineSeries(sr.Exchange, sr.InstId, interval) }) strategyContext := sig.NewStrategyContext(sigStrategyInput, kSeries, b.indicatorReg) - requiredSeries := int(sigStrategy.RequiredSeries(sigStrategyInput)) + requiredSeries := int(sigStrategy.CandlePeriods(strategyContext)) requiredIntervalSeries := types.NewIntervalState[int16]() requiredIntervalSeries.Set(interval, int16(max(1, requiredSeries))) @@ -101,10 +101,10 @@ func (b *SigStrategyBacktester) singleStrategySeries(ctx context.Context, sigStr // intervalStrategySeries 多周期策略 func (b *SigStrategyBacktester) intervalStrategySeries(ctx context.Context, intervalSigStrategy strategy.IIntervalSigStrategy, sigStrategyInput types.Input, sr *pb.SeriesRange, intervalKlineSeries *types.IntervalState[*sig.KlineSeries], recvSignal func(sigSide types.Side, k types.Kline) (err error)) (err error) { - // 各周期所需k线数量 - requiredIntervalSeries := intervalSigStrategy.RequiredIntervalSeries(sigStrategyInput) // 策略上下文 intervalStrategyContext := sig.NewIntervalStrategyContext(sigStrategyInput, intervalKlineSeries, b.indicatorReg) + // 各周期所需k线数量 + requiredIntervalSeries := intervalSigStrategy.CandlePeriods(intervalStrategyContext) err = b.multiIntervalSeries(ctx, sr, requiredIntervalSeries, intervalKlineSeries, func(driver bool, interval types.Interval, k *types.Kline) (err error) { if !driver { return @@ -332,7 +332,7 @@ func (b *SigStrategyBacktester) _singleStrategySeries(ctx context.Context, sigSt kSeries := sig.NewKlineSeries(sr.Exchange, sr.InstId, interval) strategyContext := sig.NewStrategyContext(nil, kSeries, b.indicatorReg) - requiredSeries := int(sigStrategy.RequiredSeries(nil)) + requiredSeries := int(sigStrategy.CandlePeriods(nil)) sr.WindowExtra = uint32(max(0, requiredSeries-1)) err = b.fetchHistoryKlineSeries(ctx, sr, func(k *types.Kline) (err error) { if lastTs, serial := kSeries.Update(k); !serial { diff --git a/pkg/strategy/cross_star.go b/pkg/strategy/cross_star.go index b7baf9f..f42af00 100644 --- a/pkg/strategy/cross_star.go +++ b/pkg/strategy/cross_star.go @@ -34,7 +34,7 @@ func (s *CrossStar) Init(input types.Input) (err error) { // 校验参数, 并 return } -func (s *CrossStar) RequiredIntervalSeries(input types.Input) (iss *types.IntervalState[int16]) { +func (s *CrossStar) CandlePeriods(ctx IIntervalSigStrategyContext) (iss *types.IntervalState[int16]) { iss = types.NewIntervalState[int16]() iss.Set(types.Interval5m, 1) iss.Set(types.Interval15m, 2) diff --git a/pkg/strategy/gold_x.go b/pkg/strategy/gold_x.go index 2680151..93838ef 100644 --- a/pkg/strategy/gold_x.go +++ b/pkg/strategy/gold_x.go @@ -1,14 +1,12 @@ package strategy import ( - "fmt" "sig-pub/pkg/types" ) // GoldX 金叉策略 type GoldX struct { ISigStrategy - short, long int16 } func (s *GoldX) New() ISigStrategy { @@ -20,39 +18,47 @@ func (s *GoldX) Meta() StrategyMeta { Name: "GoldX", Desc: "金叉策略", Input: []types.InputArg{ - {Name: "short", Type: types.InputTypeUInt, Desc: "短周期"}, - {Name: "long", Type: types.InputTypeUInt, Desc: "长周期"}, + {Name: "fast", Type: types.InputTypeUInt, Desc: "macd快线周期"}, + {Name: "slow", Type: types.InputTypeUInt, Desc: "macd慢线周期"}, + {Name: "singal", Type: types.InputTypeUInt, Desc: "macd信号线周期"}, }, } } -func (s *GoldX) Init(input types.Input) (err error) { // 校验参数, 并根据参数初始化策略 - s.short = input.Int16("short") - s.long = input.Int16("long") - if s.long <= s.short { - err = fmt.Errorf("param short should bigger then short") - return - } +// Init 校验参数, 并根据参数初始化策略 +func (s *GoldX) Init(input types.Input) (err error) { return } -func (s *GoldX) RequiredSeries(input types.Input) int16 { - return max(s.long, s.short) + 1 +func (s *GoldX) CandlePeriods(ctx ISingleSigStrategyContext) int16 { + return max( + ctx.Indicator("macd", ctx.Input()).CandlePeriods(), + ctx.Indicator("macd_dea", ctx.Input()).CandlePeriods(), + ctx.Indicator("macd_hist", ctx.Input()).CandlePeriods(), + ) } func (s *GoldX) Update(ctx ISingleSigStrategyContext) (side types.Side) { - sma14 := ctx.Indicator("sma", s.short) - sma28 := ctx.Indicator("sma", s.long) - // 包装方法 crossover/crossunder - s14 := sma14.Series(0, 2) - s28 := sma28.Series(0, 2) - crossover := s14[0] > s28[0] && s14[1] < s28[1] // 上穿 - crossunder := s14[0] < s28[0] && s14[1] > s28[1] // 下穿 + macd := ctx.Indicator("macd", ctx.Input()).Series(0, 2) // macd线 + macdDea := ctx.Indicator("macd_dea", ctx.Input()).Series(0, 2) // macd信号线 + macdHist := ctx.Indicator("macd_hist", ctx.Input()).Get(0) // macd柱状图 + + // todo 包装方法 crossover/crossunder + // 1.MACD 线接近或上穿零轴(表示整体多头市场) + crossover := macd[0] > macdDea[0] && macd[1] < macdDea[1] // 上穿 + crossunder := macd[0] < macdDea[0] && macd[1] > macdDea[1] // 下穿 + if crossover { - return types.SideLong + // 2.附加确认条件: 柱状图从负值转为正值 + if macdHist > 0 { + // todo 3.成交量放大(结合 OBV 等指标验证资金流入)。 + return types.SideLong + } } if crossunder { - return types.SideShort + if macdHist < 0 { + return types.SideShort + } } return } diff --git a/pkg/strategy/sig_strategy.go b/pkg/strategy/sig_strategy.go index 19c2806..50724f1 100644 --- a/pkg/strategy/sig_strategy.go +++ b/pkg/strategy/sig_strategy.go @@ -22,7 +22,7 @@ type StrategyMeta struct { // ISingleSigStrategy 单周期单交易所策略 type ISingleSigStrategy interface { ISigStrategy - RequiredSeries(input types.Input) int16 // 需要的最小数据k线数, 回测时用, 若不定义则取最大窗口值 + CandlePeriods(ctx ISingleSigStrategyContext) int16 // 需要的最小数据k线数, 回测时用, 若不定义则取最大窗口值 Update(ctx ISingleSigStrategyContext) (side types.Side) } @@ -41,7 +41,7 @@ type ISingleSigStrategyContext interface { // 多周期k线策略接口 type IIntervalSigStrategy interface { ISigStrategy - RequiredIntervalSeries(input types.Input) (iss *types.IntervalState[int16]) // 需要的各周期最小数据k线数, 回测时用, 若不定义则取最大窗口值 + CandlePeriods(ctx IIntervalSigStrategyContext) (iss *types.IntervalState[int16]) // 需要的各周期最小数据k线数, 回测时用, 若不定义则取最大窗口值 Update(ctx IIntervalSigStrategyContext) (side types.Side) } diff --git a/pkg/strategy/super_trend.go b/pkg/strategy/super_trend.go index fe22428..e1d8fbb 100644 --- a/pkg/strategy/super_trend.go +++ b/pkg/strategy/super_trend.go @@ -56,8 +56,8 @@ func (s *SupertrendBOSWaves) Init(input types.Input) (err error) { // 校验参 return } -func (s *SupertrendBOSWaves) RequiredSeries(input types.Input) int16 { - return s.atrLength + 1 +func (s *SupertrendBOSWaves) CandlePeriods(ctx ISingleSigStrategyContext) int16 { + return ctx.Input().Int16("atrLength") + 1 } func (s *SupertrendBOSWaves) Update(ctx ISingleSigStrategyContext) (side types.Side) {