|
|
|
@ -1,14 +1,23 @@ |
|
|
|
package trading |
|
|
|
package trading |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
|
|
|
|
"context" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
|
|
|
|
"math" |
|
|
|
"sig-pub/api/pb" |
|
|
|
"sig-pub/api/pb" |
|
|
|
vmts "sig-pub/pkg/storage/tsdb/victoria_metrics" |
|
|
|
|
|
|
|
"sig-pub/pkg/types" |
|
|
|
"sig-pub/pkg/types" |
|
|
|
"sig-pub/pkg/utils/collect" |
|
|
|
"sig-pub/pkg/utils/collect" |
|
|
|
|
|
|
|
"sig-pub/pkg/utils/retry" |
|
|
|
|
|
|
|
"sig-pub/pkg/zlog" |
|
|
|
|
|
|
|
"sync" |
|
|
|
|
|
|
|
"sync/atomic" |
|
|
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"google.golang.org/grpc" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
type KlineStore struct { |
|
|
|
type KlineStore struct { |
|
|
|
vmdb vmts.VictoriaMetricsTSDB |
|
|
|
exchangeClient pb.ExchangeServiceClient |
|
|
|
store [3]*collect.ConcurrentMap[string, *KlineStoreInstance] // K线列表: []exchange<instId, interval, klines>
|
|
|
|
store [3]*collect.ConcurrentMap[string, *KlineStoreInstance] // K线列表: []exchange<instId, interval, klines>
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -19,50 +28,97 @@ func NewKlineSeriesStore() (kss *KlineStore) { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *KlineStore) Update(exchange pb.ExchangeType, instId string, kline *types.Kline) (k types.Kline) { |
|
|
|
// Update
|
|
|
|
|
|
|
|
// kline klineStore -> klineSeries -> strategy -> indicator -> klineSeries.Series
|
|
|
|
|
|
|
|
func (s *KlineStore) Update(exchange pb.ExchangeType, instId string, kline *types.Kline) { |
|
|
|
storeInst := s.store[exchange].ComputeIfAbsent(instId, func(k string) *KlineStoreInstance { |
|
|
|
storeInst := s.store[exchange].ComputeIfAbsent(instId, func(k string) *KlineStoreInstance { |
|
|
|
return NewKlineStoreInstance(exchange, k) |
|
|
|
return NewKlineStoreInstance(exchange, k) |
|
|
|
}) |
|
|
|
}) |
|
|
|
storeInst.Update(kline) |
|
|
|
if storeInst.padding.Load() { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
before, serial, err := storeInst.Update(kline) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
zlog.Error("update kline series error: instId=%s(%s)", instId, exchange, err) |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if !serial { |
|
|
|
|
|
|
|
// 拉取缺失的k线
|
|
|
|
|
|
|
|
s.paddingMissKlines(storeInst, exchange, instId, kline.Interval, kline.Ts, before) |
|
|
|
|
|
|
|
// emit kline event
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 拉取缺失的k线
|
|
|
|
|
|
|
|
func (s *KlineStore) paddingMissKlines(storeInst *KlineStoreInstance, exchange pb.ExchangeType, instId string, interval types.Interval, after, before int64) { |
|
|
|
|
|
|
|
// 拉取缺失的k线
|
|
|
|
|
|
|
|
rsp, err := retry.DoWithFixDelay(math.MaxInt32, 2*time.Second, func(retryTimes uint32) (rsp *pb.RspHistoryKline, err error) { |
|
|
|
|
|
|
|
rsp, err = s.exchangeClient.HistoryKline(context.Background(), &pb.ReqHistoryKline{ |
|
|
|
|
|
|
|
Exchange: exchange, |
|
|
|
|
|
|
|
InstId: instId, |
|
|
|
|
|
|
|
Interval: string(interval), |
|
|
|
|
|
|
|
After: uint64(after), |
|
|
|
|
|
|
|
Before: uint64(before), |
|
|
|
|
|
|
|
Live: false, |
|
|
|
|
|
|
|
}, grpc.UseCompressor("snappy")) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
zlog.Errorf("fetch missing klines error: instId=%s(%s) after=%d before=%d, err=%v", instId, exchange, after, before, err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
collect.Reverse(rsp.Klines) |
|
|
|
|
|
|
|
for _, kline := range rsp.Klines { |
|
|
|
|
|
|
|
k := new(types.Kline) |
|
|
|
|
|
|
|
k.ParsePBKline(exchange, kline) |
|
|
|
|
|
|
|
zlog.Infof("padding missing kline: instId=%s(%s) %s %#v", instId, exchange, interval, k) |
|
|
|
|
|
|
|
_, _, err := storeInst.Update(k) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
zlog.Error("update missing kline series error: instId=%s(%s)", instId, exchange, err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// KlineStoreInstance 单个交易产品所有周期k线
|
|
|
|
// KlineStoreInstance 单个交易产品所有周期k线
|
|
|
|
type KlineStoreInstance struct { |
|
|
|
type KlineStoreInstance struct { |
|
|
|
|
|
|
|
sync.RWMutex |
|
|
|
Exchange pb.ExchangeType |
|
|
|
Exchange pb.ExchangeType |
|
|
|
InstId string |
|
|
|
InstId string |
|
|
|
IntervalKlines *collect.SyncMap[types.Interval, *KlineSeries] |
|
|
|
intervalKlines *types.IntervalState[*KlineSeries] |
|
|
|
|
|
|
|
// tickK *types.Kline // 秒级k线
|
|
|
|
|
|
|
|
padding atomic.Bool // 是否正在拉取历史k线
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewKlineStoreInstance(exchange pb.ExchangeType, instId string) *KlineStoreInstance { |
|
|
|
func NewKlineStoreInstance(exchange pb.ExchangeType, instId string) *KlineStoreInstance { |
|
|
|
si := &KlineStoreInstance{ |
|
|
|
si := &KlineStoreInstance{ |
|
|
|
Exchange: exchange, |
|
|
|
Exchange: exchange, |
|
|
|
InstId: instId, |
|
|
|
InstId: instId, |
|
|
|
IntervalKlines: collect.NewSyncMap[types.Interval, *KlineSeries](), |
|
|
|
intervalKlines: types.NewIntervalState[*KlineSeries](), |
|
|
|
} |
|
|
|
} |
|
|
|
for interval := range types.SupportedIntervals { |
|
|
|
for interval := range types.SupportedIntervals { |
|
|
|
si.IntervalKlines.Store(interval, NewKlineSeries(exchange, instId, interval)) |
|
|
|
si.intervalKlines.Set(interval, NewKlineSeries(exchange, instId, interval)) |
|
|
|
} |
|
|
|
} |
|
|
|
return si |
|
|
|
return si |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Update 更新k线
|
|
|
|
// Update 更新k线
|
|
|
|
// kline klineStore -> klineSeries -> strategy -> indicator -> klineSeries.Series
|
|
|
|
// serial k线是否连续
|
|
|
|
func (si *KlineStoreInstance) Update(kline *types.Kline) (ok bool) { |
|
|
|
func (si *KlineStoreInstance) Update(kline *types.Kline) (before int64, serial bool, err error) { |
|
|
|
ks, ok := si.IntervalKlines.Load(kline.Interval) |
|
|
|
if _, ok := types.SupportedIntervals[kline.Interval]; !ok { |
|
|
|
if !ok { |
|
|
|
err = fmt.Errorf("unsupport interval: %s", kline.Interval) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if lastTs, serial := ks.Update(kline); !serial { |
|
|
|
si.Lock() |
|
|
|
// k线不完整
|
|
|
|
defer si.Unlock() |
|
|
|
// 拉取k线
|
|
|
|
|
|
|
|
start := lastTs |
|
|
|
|
|
|
|
end := kline.Ts |
|
|
|
|
|
|
|
_, _ = start, end |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ok = true |
|
|
|
ks := si.intervalKlines.Get(kline.Interval) |
|
|
|
// emit kline event
|
|
|
|
before, serial = ks.Update(kline) |
|
|
|
|
|
|
|
if !serial { |
|
|
|
|
|
|
|
si.padding.Store(true) |
|
|
|
|
|
|
|
} |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|