package trading import ( "fmt" "sig-pub/api/pb" "sig-pub/pkg/client" "sig-pub/pkg/indicator" "sig-pub/pkg/publish" "sig-pub/pkg/strategy" "sig-pub/pkg/types" "sig-pub/pkg/utils/collect" ) type TradingService struct { marketClientAside *client.TradeInstanceAside exchangeClient pb.ExchangeServiceClient klineStore *KlineStore indicatorsW *collect.SyncMap[string, indicator.IWindowIndicator] // 注册窗口指标 sigStrategies *collect.SyncMap[string, strategy.ISigStrategy] // 注册信号策略 publisher publish.Publisher[int64, *TradingPlan] tradingPlan chan *TradingPlan } func NewTradingService( marketClientAside *client.TradeInstanceAside, exchangeClient pb.ExchangeServiceClient, ) *TradingService { return &TradingService{ marketClientAside: marketClientAside, exchangeClient: exchangeClient, klineStore: NewKlineSeriesStore(exchangeClient), indicatorsW: collect.NewSyncMap[string, indicator.IWindowIndicator](), sigStrategies: collect.NewSyncMap[string, strategy.ISigStrategy](), } } // 初始化历史k线, 订阅实时k线 func (svc *TradingService) Init() (err error) { if err = svc.klineStore.Init(); err != nil { return } // indicator registry { svc.MustRegisterWindowIndicator(&indicator.RSI{}) svc.MustRegisterWindowIndicator(&indicator.SMA{}) } // strategy registry { svc.MustRegisterStrategy(&strategy.GoldX{}) } // strategy initial // 初始化策略执行器 64 return } // RegisterWindowIndicator func (svc *TradingService) RegisterWindowIndicator(ind indicator.IWindowIndicator) (err error) { indName := ind.Name() _, loaded := svc.indicatorsW.LoadOrStore(indName, ind) if loaded { err = fmt.Errorf("window indicator name %s already duplicated", indName) return } return } func (svc *TradingService) MustRegisterWindowIndicator(ind indicator.IWindowIndicator) { if err := svc.RegisterWindowIndicator(ind); err != nil { panic(err) } } // RegisterStrategy func (svc *TradingService) RegisterStrategy(strategy strategy.ISigStrategy) (err error) { strategyName := strategy.Meta().Name _, loaded := svc.sigStrategies.LoadOrStore(strategyName, strategy) if loaded { err = fmt.Errorf("strategy name %s already duplicated", strategyName) return } return } func (svc *TradingService) MustRegisterStrategy(strategy strategy.ISigStrategy) { if err := svc.RegisterStrategy(strategy); err != nil { panic(err) } } // RunStrategy 运行策略 // todo 止盈止损... func (svc *TradingService) RunQuantPlan(plan TradingPlan) (err error) { strategy, ok := svc.sigStrategies.Load(plan.StrategyName) if !ok { err = fmt.Errorf("strategy %s not exists", plan.StrategyName) return } runner := strategy.New() _ = runner runner.Update(nil) return } // IndicatorSeries 获取指标实时或历史序列数据, 闭区间 func (svc *TradingService) IndicatorSeries(req *pb.ReqIndicatorSeries, rsp *pb.RspIndicatorSeries) (err error) { // indicatorName string, exchange pb.ExchangeType, instId string, interval types.Interval, window int indicator, ok := svc.indicatorsW.Load(req.Indicator) if !ok { err = fmt.Errorf("indicator %s not exists", req.Indicator) return } interval := types.Interval(req.Interval) intervalAdd, ok := types.SupportedIntervals[interval] if !ok { err = fmt.Errorf("unsupport interval %s", interval) return } if req.Count <= 0 { req.Count = 100 } if req.Count > 0 { // ... } // todo trade instance status check before, after, count := req.Before, req.After, req.Count var indCtx IOffsetIndicatorContext // 查询实时指标数据 if before == 0 && after == 0 { klineSeries, err1 := svc.klineStore.GetKlineSeires(req.Exchange, req.InstId, interval) if err1 != nil { err = err1 return } // recover todo out of range indCtx = NewIndicatorContext(klineSeries) } else { // 查询历史指标数据 // todo calc before after... ctx := NewHistoryIndicatorContext(svc.exchangeClient) if count := int32((after-before)/intervalAdd(0, 1) + 1); count > 100 { } before = intervalAdd(before, int64(-req.Window)) // 多拉取窗口大小的k线数据 totalK := 0 if totalK, err = ctx.Init(req.Exchange, req.InstId, interval, before, after); err != nil { return } count = int32(totalK) - req.Window indCtx = ctx } rsp.Matrix = make([]float64, 0, req.Count) rsp.Times = make([]int64, 0, req.Count) for i := range count { indCtx.SetOffset(int16(i)) vector := indicator.Calculate(indCtx, int16(req.Window)) rsp.Matrix = append(rsp.Matrix, vector) rsp.Times = append(rsp.Times, indCtx.Get(0).Ts) } return } // StrategySeries 简单策略信号测试 func (svc *TradingService) StrategySeries(req *pb.ReqStrategySeries, rsp *pb.RspStrategySeries) (err error) { strategy, ok := svc.sigStrategies.Load(req.Strategy) if !ok { err = fmt.Errorf("strategy %s not exists", req.Strategy) return } interval := types.Interval(req.Interval) intervalAdd, ok := types.SupportedIntervals[interval] if !ok { err = fmt.Errorf("unsupport interval %s", interval) return } _ = intervalAdd klineSeries, err1 := svc.klineStore.GetKlineSeires(req.Exchange, req.InstId, interval) if err1 != nil { err = err1 return } // recover todo out of range strategyCtx := NewStrategyContext(klineSeries, svc.indicatorsW) for i := range req.Count { strategyCtx.SetOffset(int16(i)) strategy.Update(strategyCtx) } rsp.Signal = strategyCtx.signal rsp.Times = strategyCtx.signalTimes rsp.Wins = strategyCtx.wins // 信号点胜率判断 wins := collect.Filter(rsp.Wins, func(_ int, win bool) bool { return win }) rsp.WinRate = float64(len(wins)) / float64(len(rsp.Wins)) return }