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.
251 lines
7.0 KiB
251 lines
7.0 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" |
|
|
|
"github.com/bytedance/sonic" |
|
) |
|
|
|
type TradingService struct { |
|
marketClientAside *client.TradeInstanceAside |
|
exchangeClient pb.ExchangeServiceClient |
|
|
|
klineStore *KlineStore |
|
indicatorReg *indicator.IndicatorRegistry // 注册窗口指标 |
|
strategyReg *strategy.SigStrategyRegistry // 注册信号策略 |
|
signalPublisher *publish.Publisher[int64, strategy.StrategyType] // planId -> strategyType |
|
tradingPlans *collect.SyncMap[int64, *TradingPlan] // 运行中交易计划 |
|
} |
|
|
|
func NewTradingService( |
|
marketClientAside *client.TradeInstanceAside, |
|
exchangeClient pb.ExchangeServiceClient, |
|
) *TradingService { |
|
return &TradingService{ |
|
marketClientAside: marketClientAside, |
|
exchangeClient: exchangeClient, |
|
klineStore: NewKlineSeriesStore(exchangeClient), |
|
indicatorReg: indicator.NewIndicatorRegistry(), |
|
strategyReg: strategy.NewSigStrategyRegistry(), |
|
signalPublisher: publish.NewPublisher[int64, strategy.StrategyType](16), |
|
tradingPlans: collect.NewSyncMap[int64, *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.klineStore.Init(); err != nil { |
|
return |
|
} |
|
|
|
go svc.consumerKlineSignal() |
|
|
|
// todo loading trading plan |
|
return |
|
} |
|
|
|
// consumerKlineSignal 订阅k线更新 |
|
func (svc *TradingService) consumerKlineSignal() { |
|
c := svc.klineStore.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 := 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, []types.Interval{sigInterval}, exchange) |
|
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.klineStore.GetKlineSeires(exchange, instId, sigInterval) |
|
if err != nil { |
|
return |
|
} |
|
|
|
if err = tradingPlan.Init(); err != nil { |
|
return |
|
} |
|
if err = tradingPlan.InitSigStrategy(sigStrategy, *sigStrategyParam, sigKlineSeries); err != nil { |
|
return |
|
} |
|
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.indicatorReg.IndicatorW(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) { |
|
// sigStrategy |
|
sigStrategy, ok := svc.strategyReg.NewSigStrategy(req.Strategy) |
|
if !ok { |
|
err = fmt.Errorf("strategy %s not exists", req.Strategy) |
|
return |
|
} |
|
err = sigStrategy.Init(strategy.SigStrategyParam{ |
|
Interval: types.Interval(req.Interval), |
|
Param: req.SigParam, |
|
}) |
|
if err != nil { |
|
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.indicatorReg) |
|
for i := range req.Count { |
|
strategyCtx.SetOffset(int16(i)) |
|
sigStrategy.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 |
|
}
|
|
|