You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

266 lines
7.5 KiB

package trading
import (
"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/zlog"
"sig-pub/internal/trading/sig"
"github.com/bytedance/sonic"
)
type TradingService struct {
marketClientAside *client.TradeInstanceAside
exchangeClient pb.ExchangeServiceClient
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,
) *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](),
}
}
// 初始化历史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)
}
}
}
}
// runTradingPlan 运行交易计划
// 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)
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)
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)
return
}
sigStrategy, ok := svc.strategyReg.NewSigStrategy(plan.SigStrategy)
if !ok {
err = fmt.Errorf("strategy %s not exists", plan.SigStrategy)
return
}
sigKlineSeries, err := svc.klineSeriesStore.GetKlineSeires(exchange, instId, sigInterval)
if err != nil {
return
}
if err = tradingPlan.Init(); err != nil {
return
}
sigIndCtx := sig.NewIndicatorContext(sigKlineSeries)
if err = tradingPlan.InitSigStrategy(sigStrategy, *sigStrategyParam, sigIndCtx); err != nil {
return
}
return
}
// 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
}
const (
MaxIndicatorWindow = 128
)
// 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
}
err = sigStrategy.Init(strategy.SigStrategyParam{
Interval: types.Interval(req.Series.Interval),
Param: req.SigParam,
})
if 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 += MaxIndicatorWindow
if totalK, err = indicatorContext.Init(req.Series); err != nil {
return
}
count = totalK - MaxIndicatorWindow
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 {
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
}