package trading import ( "context" "fmt" "sig-pub/api/pb" "sig-pub/pkg/client" "sig-pub/pkg/data" "sig-pub/pkg/data/entity" "sig-pub/pkg/indicator" "sig-pub/pkg/publish" "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 tradingDataPersist *TradingDataPersist klineSeriesStore *KlineSeriesStore indicatorReg *indicator.IndicatorRegistry // 注册窗口指标 strategyReg *strategy.SigStrategyRegistry // 注册信号策略 signalPublisher *publish.Publisher[int64, strategy.StrategyType] // planId -> strategyType tradingPlans *collect.SyncMap[int64, *sig.TradingPlan] // 运行中交易计划 } func NewTradingService( marketClientAside *client.TradeInstanceAside, exchangeClient pb.ExchangeServiceClient, tradingDataPersist *TradingDataPersist, ) *TradingService { return &TradingService{ 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](), } } // 初始化历史k线, 订阅实时k线 func (svc *TradingService) Init() (err error) { if err = svc.indicatorReg.Init(); err != nil { return } if err = svc.strategyReg.Init(); err != nil { return } if err = svc.klineSeriesStore.Init(); err != nil { return } go svc.consumerKlineSignal() // todo loading trading plan return } // consumerKlineSignal 订阅k线更新 func (svc *TradingService) consumerKlineSignal() { c := svc.klineSeriesStore.ConsumerKlineSignel() for { signalKey := <-c zlog.Debugf("signal: %s", signalKey) planIds, strategyTypes := svc.signalPublisher.Publisher(signalKey) for i, strategyType := range strategyTypes { planId := planIds[i] plan, ok := svc.tradingPlans.Load(planId) if !ok { zlog.Warningf("plan not running: id=%d", planId) continue } if plan.Status.Load() == int32(data.StatusOk) { plan.Update(strategyType) } } } } // getTradingPlan 获取交易计划 // todo 止盈止损策略, 下单策略... 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 } if _, ok := types.SupportedIntervals[interval]; !ok { err = fmt.Errorf("unsupport interval %d", plan.Exchange) return } // sigStrategy sigStrategyParam := make(strategy.StrategyParam) if err = sonic.UnmarshalString(plan.SigStrategyParam, &sigStrategyParam); err != nil { return } sigStrategy, ok := svc.strategyReg.NewSigStrategy(plan.SigStrategy) if !ok { err = fmt.Errorf("strategy %s not exists", plan.SigStrategy) 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 } 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 获取指标实时或历史序列数据, 闭区间 func (svc *TradingService) IndicatorSeries(indicatorName string, window uint32, sr *pb.SeriesRange) (matrix []float64, times []int64, err error) { // indicatorName string, exchange pb.ExchangeType, instId string, interval types.Interval, window int indicator, ok := svc.indicatorReg.IndicatorW(indicatorName) if !ok { err = fmt.Errorf("indicator %s not exists", indicatorName) return } interval := types.Interval(sr.Interval) _, ok = types.SupportedIntervals[interval] if !ok { err = fmt.Errorf("unsupport interval %s", interval) return } // before, after, count := sr.Before, sr.After, sr.Count // var indCtx IOffsetIndicatorContext // // 查询实时指标数据 // if before == 0 && after == 0 { // klineSeries, err1 := svc.klineStore.GetKlineSeires(sr.Exchange, sr.InstId, interval) // if err1 != nil { // err = err1 // return // } // // recover todo out of range // indCtx = NewIndicatorContext(klineSeries) // } // 查询历史指标数据 sr.Window = window indCtx := sig.NewHistoryIndicatorContext(svc.exchangeClient) totalK := 0 if totalK, err = indCtx.Init(sr); err != nil { return } count := totalK - int(window) matrix = make([]float64, 0, sr.Count) times = make([]int64, 0, sr.Count) for i := count - 1; i >= 0; i-- { indCtx.SetOffset(int16(i)) vector := indicator.Calculate(indCtx, int16(window)) matrix = append(matrix, vector) times = append(times, indCtx.Get(0).Ts) } return } // StrategySeries 简单策略信号测试 func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.RspStrategySeries) (err error) { // sigStrategy sigStrategy, ok := svc.strategyReg.NewSigStrategy(req.SigStrategy) if !ok { err = fmt.Errorf("strategy %s not exists", req.SigStrategy) return } // init sigStrategy if err = sigStrategy.Init(req.SigParam); err != nil { return } interval := types.Interval(req.Series.Interval) _, ok = types.SupportedIntervals[interval] if !ok { err = fmt.Errorf("unsupport interval %s", interval) return } // klineSeries, err1 := svc.klineStore.GetKlineSeires(req.Exchange, req.InstId, interval) // if err1 != nil { // err = err1 // return // } // recover todo out of range count, totalK := 0, 0 indicatorContext := sig.NewHistoryIndicatorContext(svc.exchangeClient) req.Series.Window += indicator.MaxWindow if totalK, err = indicatorContext.Init(req.Series); err != nil { return } count = totalK - indicator.MaxWindow strategyContext := sig.NewStrategyContext(indicatorContext, svc.indicatorReg) for i := count - 1; i >= 0; i-- { strategyContext.SetOffset(int16(i)) 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) { // todo delete win := false if indicatorContext.GetOffset() > 0 { nextK := indicatorContext.Get(-1) if side == pb.Side_BUY { win = nextK.Close.Cmp(signalK.Close) > 0 } else { win = nextK.Close.Cmp(signalK.Close) < 0 } } rsp.Wins = append(rsp.Wins, win) } } } // 信号点胜率判断 wins := collect.Filter(rsp.Wins, func(_ int, win bool) bool { return win }) 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 }