|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
package trading |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"sig-pub/api/pb" |
|
|
|
|
"sig-pub/pkg/client" |
|
|
|
|
@ -11,16 +12,19 @@ import (
|
|
|
|
|
"sig-pub/pkg/strategy" |
|
|
|
|
"sig-pub/pkg/types" |
|
|
|
|
"sig-pub/pkg/utils/collect" |
|
|
|
|
"sig-pub/pkg/utils/lang" |
|
|
|
|
"sig-pub/pkg/zlog" |
|
|
|
|
|
|
|
|
|
"sig-pub/internal/trading/backtest" |
|
|
|
|
"sig-pub/internal/trading/sig" |
|
|
|
|
|
|
|
|
|
"github.com/bytedance/sonic" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type TradingService struct { |
|
|
|
|
marketClientAside *client.TradeInstanceAside |
|
|
|
|
exchangeClient pb.ExchangeServiceClient |
|
|
|
|
marketClientAside *client.TradeInstanceAside |
|
|
|
|
exchangeClient pb.ExchangeServiceClient |
|
|
|
|
tradingDataPersist *TradingDataPersist |
|
|
|
|
|
|
|
|
|
klineSeriesStore *KlineSeriesStore |
|
|
|
|
indicatorReg *indicator.IndicatorRegistry // 注册窗口指标
|
|
|
|
|
@ -32,15 +36,17 @@ type TradingService struct {
|
|
|
|
|
func NewTradingService( |
|
|
|
|
marketClientAside *client.TradeInstanceAside, |
|
|
|
|
exchangeClient pb.ExchangeServiceClient, |
|
|
|
|
tradingDataPersist *TradingDataPersist, |
|
|
|
|
) *TradingService { |
|
|
|
|
return &TradingService{ |
|
|
|
|
marketClientAside: marketClientAside, |
|
|
|
|
exchangeClient: exchangeClient, |
|
|
|
|
klineSeriesStore: NewKlineSeriesStore(exchangeClient), |
|
|
|
|
indicatorReg: indicator.NewIndicatorRegistry(), |
|
|
|
|
strategyReg: strategy.NewSigStrategyRegistry(), |
|
|
|
|
signalPublisher: publish.NewPublisher[int64, strategy.StrategyType](16), |
|
|
|
|
tradingPlans: collect.NewSyncMap[int64, *sig.TradingPlan](), |
|
|
|
|
marketClientAside: marketClientAside, |
|
|
|
|
exchangeClient: exchangeClient, |
|
|
|
|
tradingDataPersist: tradingDataPersist, |
|
|
|
|
klineSeriesStore: NewKlineSeriesStore(exchangeClient), |
|
|
|
|
indicatorReg: indicator.NewIndicatorRegistry(), |
|
|
|
|
strategyReg: strategy.NewSigStrategyRegistry(), |
|
|
|
|
signalPublisher: publish.NewPublisher[int64, strategy.StrategyType](16), |
|
|
|
|
tradingPlans: collect.NewSyncMap[int64, *sig.TradingPlan](), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -84,47 +90,24 @@ func (svc *TradingService) consumerKlineSignal() {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// runTradingPlan 运行交易计划
|
|
|
|
|
// getTradingPlan 获取交易计划
|
|
|
|
|
// todo 止盈止损策略, 下单策略...
|
|
|
|
|
func (svc *TradingService) runTradingPlan(plan *entity.TradePlan) (err error) { |
|
|
|
|
var planId = plan.Id |
|
|
|
|
var instId = plan.InstId |
|
|
|
|
var exchange pb.ExchangeType |
|
|
|
|
var sigInterval types.Interval |
|
|
|
|
|
|
|
|
|
exchange = pb.ExchangeType(plan.Exchange) |
|
|
|
|
func (svc *TradingService) getTradingPlan(plan *entity.TradePlan, sigKlineSeries *sig.KlineSeries) (tradingPlan *sig.TradingPlan, err error) { |
|
|
|
|
exchange := pb.ExchangeType(plan.Exchange) |
|
|
|
|
interval := types.Interval(plan.Interval) |
|
|
|
|
instId := plan.InstId |
|
|
|
|
if !types.IsSupportExchange(exchange) { |
|
|
|
|
err = fmt.Errorf("unsupport exchange %d", plan.Exchange) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tradingPlan := sig.NewTradingPlan(*plan, svc.indicatorReg) |
|
|
|
|
if _, load := svc.tradingPlans.LoadOrStore(planId, tradingPlan); load { |
|
|
|
|
err = fmt.Errorf("plan already running: planId=%d", planId) |
|
|
|
|
if _, ok := types.SupportedIntervals[interval]; !ok { |
|
|
|
|
err = fmt.Errorf("unsupport interval %d", plan.Exchange) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
tradingPlan.Status.Store(int32(data.StatusProcessing)) |
|
|
|
|
|
|
|
|
|
defer func() { |
|
|
|
|
if err != nil { |
|
|
|
|
svc.tradingPlans.Delete(planId) |
|
|
|
|
} else { |
|
|
|
|
// 订阅交易信号策略k线周期
|
|
|
|
|
sigSubKey := strategy.DriverIntervalKey(instId, exchange, false, sigInterval) |
|
|
|
|
svc.signalPublisher.Subscribe(sigSubKey, planId, strategy.StrategyTypeSig) |
|
|
|
|
|
|
|
|
|
tradingPlan.Status.Store(int32(data.StatusOk)) |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
// sigStrategy
|
|
|
|
|
sigStrategyParam := new(strategy.SigStrategyParam) |
|
|
|
|
if err = sonic.UnmarshalString(plan.SigStrategyParam, sigStrategyParam); err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
sigInterval = types.Interval(sigStrategyParam.Interval) |
|
|
|
|
if _, ok := types.SupportedIntervals[sigInterval]; !ok { |
|
|
|
|
err = fmt.Errorf("unsupport interval %d", plan.Exchange) |
|
|
|
|
sigStrategyParam := make(strategy.StrategyParam) |
|
|
|
|
if err = sonic.UnmarshalString(plan.SigStrategyParam, &sigStrategyParam); err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
sigStrategy, ok := svc.strategyReg.NewSigStrategy(plan.SigStrategy) |
|
|
|
|
@ -132,19 +115,42 @@ func (svc *TradingService) runTradingPlan(plan *entity.TradePlan) (err error) {
|
|
|
|
|
err = fmt.Errorf("strategy %s not exists", plan.SigStrategy) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
sigKlineSeries, err := svc.klineSeriesStore.GetKlineSeires(exchange, instId, sigInterval) |
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
if sigKlineSeries == nil { |
|
|
|
|
sigKlineSeries, err = svc.klineSeriesStore.GetKlineSeires(exchange, instId, interval) |
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tradingPlan = sig.NewTradingPlan(*plan, svc.indicatorReg) |
|
|
|
|
if err = tradingPlan.Init(); err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
sigIndCtx := sig.NewIndicatorContext(sigKlineSeries) |
|
|
|
|
if err = tradingPlan.InitSigStrategy(sigStrategy, *sigStrategyParam, sigIndCtx); err != nil { |
|
|
|
|
sigIndicatorCtx := sig.NewIndicatorContext(sigKlineSeries) |
|
|
|
|
sigStrategyCtx := sig.NewStrategyContext(sigIndicatorCtx, svc.indicatorReg) |
|
|
|
|
if err = tradingPlan.InitSigStrategy(sigStrategy, sigStrategyParam, sigStrategyCtx); err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
// if _, load := svc.tradingPlans.LoadOrStore(planId, tradingPlan); load {
|
|
|
|
|
// err = fmt.Errorf("plan already running: planId=%d", planId)
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// tradingPlan.Status.Store(int32(data.StatusProcessing))
|
|
|
|
|
|
|
|
|
|
// defer func() {
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// svc.tradingPlans.Delete(planId)
|
|
|
|
|
// } else {
|
|
|
|
|
// // 订阅交易信号策略k线周期
|
|
|
|
|
// sigSubKey := strategy.DriverIntervalKey(instId, exchange, false, sigInterval)
|
|
|
|
|
// svc.signalPublisher.Subscribe(sigSubKey, planId, strategy.StrategyTypeSig)
|
|
|
|
|
|
|
|
|
|
// tradingPlan.Status.Store(int32(data.StatusOk))
|
|
|
|
|
// }
|
|
|
|
|
// }()
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IndicatorSeries 获取指标实时或历史序列数据, 闭区间
|
|
|
|
|
@ -195,10 +201,6 @@ func (svc *TradingService) IndicatorSeries(indicatorName string, window uint32,
|
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
MaxIndicatorWindow = 128 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// StrategySeries 简单策略信号测试
|
|
|
|
|
func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.RspStrategySeries) (err error) { |
|
|
|
|
// sigStrategy
|
|
|
|
|
@ -207,11 +209,8 @@ func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.Rsp
|
|
|
|
|
err = fmt.Errorf("strategy %s not exists", req.SigStrategy) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
err = sigStrategy.Init(strategy.SigStrategyParam{ |
|
|
|
|
Interval: types.Interval(req.Series.Interval), |
|
|
|
|
Param: req.SigParam, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
// init sigStrategy
|
|
|
|
|
if err = sigStrategy.Init(req.SigParam); err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -230,17 +229,18 @@ func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.Rsp
|
|
|
|
|
// recover todo out of range
|
|
|
|
|
count, totalK := 0, 0 |
|
|
|
|
indicatorContext := sig.NewHistoryIndicatorContext(svc.exchangeClient) |
|
|
|
|
req.Series.Window += MaxIndicatorWindow |
|
|
|
|
req.Series.Window += indicator.MaxWindow |
|
|
|
|
if totalK, err = indicatorContext.Init(req.Series); err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
count = totalK - MaxIndicatorWindow |
|
|
|
|
count = totalK - indicator.MaxWindow |
|
|
|
|
|
|
|
|
|
strategyContext := sig.NewStrategyContext(indicatorContext, svc.indicatorReg) |
|
|
|
|
for i := count - 1; i >= 0; i-- { |
|
|
|
|
strategyContext.SetOffset(int16(i)) |
|
|
|
|
side := sigStrategy.Update(strategyContext) |
|
|
|
|
if side == pb.Side_BUY || side == pb.Side_SELL { |
|
|
|
|
sigSide := sigStrategy.Update(strategyContext) |
|
|
|
|
if sigSide == types.SideBuy || sigSide == types.SideSell { |
|
|
|
|
side := lang.Ternary(sigSide == types.SideBuy, pb.Side_BUY, pb.Side_SELL) |
|
|
|
|
signalK := strategyContext.Get(0) |
|
|
|
|
rsp.Signal = append(rsp.Signal, side) |
|
|
|
|
rsp.Times = append(rsp.Times, signalK.Ts) |
|
|
|
|
@ -264,3 +264,26 @@ func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.Rsp
|
|
|
|
|
rsp.WinRate = float64(len(wins)) / float64(len(rsp.Wins)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Backtest 回测交易计划
|
|
|
|
|
func (svc *TradingService) Backtest(planId, stime, etime int64) (err error) { |
|
|
|
|
plan, err := svc.tradingDataPersist.GetTradePlanById(planId) |
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
exchange := pb.ExchangeType(plan.Exchange) |
|
|
|
|
interval := types.Interval(plan.Interval) |
|
|
|
|
|
|
|
|
|
sigKlineSeries := sig.NewKlineSeries(exchange, plan.InstId, interval) |
|
|
|
|
tradingPlan, err := svc.getTradingPlan(plan, sigKlineSeries) |
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
test := backtest.NewBacktest(svc.exchangeClient, svc.indicatorReg, svc.strategyReg) |
|
|
|
|
err = test.RunTradingPlan(context.Background(), tradingPlan, stime, etime, sigKlineSeries) |
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|