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.
 
 

201 lines
5.6 KiB

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
}