|
|
|
|
@ -9,9 +9,11 @@ import (
|
|
|
|
|
"sig-pub/pkg/data" |
|
|
|
|
"sig-pub/pkg/storage/kvrocks" |
|
|
|
|
"sig-pub/pkg/types" |
|
|
|
|
"sig-pub/pkg/utils/times" |
|
|
|
|
"sig-pub/pkg/zlog" |
|
|
|
|
"sync" |
|
|
|
|
"sync/atomic" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"google.golang.org/grpc" |
|
|
|
|
) |
|
|
|
|
@ -68,7 +70,6 @@ func (svc *ExchangeGrpcServer) subscribeExchanges() {
|
|
|
|
|
|
|
|
|
|
var exchangeInstIds []string |
|
|
|
|
var processingInsts []types.TradeInstance |
|
|
|
|
exchange.Lock() |
|
|
|
|
for _, inst := range insts { |
|
|
|
|
exchangeInstIds = append(exchangeInstIds, inst.ExchangeInstId) |
|
|
|
|
tradeInst := &types.TradeInstance{ |
|
|
|
|
@ -79,13 +80,17 @@ func (svc *ExchangeGrpcServer) subscribeExchanges() {
|
|
|
|
|
ExchangeInstId: inst.ExchangeInstId, |
|
|
|
|
Exchange: exchange.ExType, |
|
|
|
|
} |
|
|
|
|
exchange.Insts[inst.ExchangeInstId] = tradeInst |
|
|
|
|
exchange.Insts.Store(inst.ExchangeInstId, &ExchangeTradeInstance{ |
|
|
|
|
Inst: tradeInst, |
|
|
|
|
Status: 0, |
|
|
|
|
LiveMarkTs: 0, |
|
|
|
|
HistoryMarkTs: 0, |
|
|
|
|
}) |
|
|
|
|
// 待初始化币种数据
|
|
|
|
|
if inst.Status == data.StatusProcessing { |
|
|
|
|
processingInsts = append(processingInsts, *tradeInst) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
exchange.Unlock() |
|
|
|
|
|
|
|
|
|
// instIds := []string{"BTC-USDT", "DOGE-USDT-SWAP"}
|
|
|
|
|
err = exchange.Subscriber.SubscribeKline(exchangeInstIds...) |
|
|
|
|
@ -119,13 +124,14 @@ func (svc *ExchangeGrpcServer) consumerKline(exchange *Exchange, c <-chan *types
|
|
|
|
|
|
|
|
|
|
// 交易所 instid 转 sig-instid
|
|
|
|
|
var exInst *types.TradeInstance |
|
|
|
|
exchange.RLock() |
|
|
|
|
if inst, ok := exchange.Insts[channelK.InstId]; ok && inst != nil { |
|
|
|
|
exchange.RUnlock() |
|
|
|
|
exInst = inst |
|
|
|
|
if inst, ok := exchange.Insts.Load(channelK.ExgInstId); ok && inst != nil { |
|
|
|
|
exInst = inst.Inst |
|
|
|
|
// 标记交易产品开始订阅k线时间
|
|
|
|
|
if inst.LiveMarkTs == 0 && len(channelK.Klines) > 0 { |
|
|
|
|
inst.LiveMarkTs = channelK.Klines[0].Ts |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
exchange.RUnlock() |
|
|
|
|
zlog.Errorf("unknown exchange instId: %v, %s", channelK.Exchange, channelK.InstId) |
|
|
|
|
zlog.Errorf("unknown exchange instId: %v, %s", channelK.Exchange, channelK.ExgInstId) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -284,59 +290,70 @@ func (svc *ExchangeGrpcServer) SubscribeKline(stream grpc.BidiStreamingServer[pb
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
KlineBefore0 int64 = 1672502400000 // k线开始数据 2023-01-01 00:00:00 GMT+8
|
|
|
|
|
HistoryKlineTsKey string = "history-kline-ts:%s/%s/%s" // exchange:sig-instid:interval
|
|
|
|
|
HistoryKlineTsKey string = "history-kline-ts:%s:%s:%s" // exchange:sig-instid:interval
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// initialKline 初始化交易产品历史k线数据
|
|
|
|
|
func (svc *ExchangeGrpcServer) initialKlines(exchange *Exchange, insts []types.TradeInstance) { |
|
|
|
|
var err error |
|
|
|
|
defer func() { |
|
|
|
|
if err != nil { |
|
|
|
|
zlog.Error(err) |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
type task struct { |
|
|
|
|
type initialKlineTask struct { |
|
|
|
|
inst types.TradeInstance |
|
|
|
|
interval types.Interval |
|
|
|
|
afterTs int64 |
|
|
|
|
beforeTs int64 |
|
|
|
|
times int32 // 重试次数
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (t initialKlineTask) logKey() string { |
|
|
|
|
return fmt.Sprintf("%s:%s:%s:%d:%d", t.inst.Exchange, t.inst.InstId, t.interval, t.beforeTs, t.afterTs) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// initialKline 初始化交易产品历史k线数据
|
|
|
|
|
func (svc *ExchangeGrpcServer) initialKlines(exchange *Exchange, insts []types.TradeInstance) { |
|
|
|
|
// 任务 channel
|
|
|
|
|
ch := make(chan task) |
|
|
|
|
taskCh := make(chan initialKlineTask, 8) |
|
|
|
|
|
|
|
|
|
// 任务生成器
|
|
|
|
|
// 任务生成
|
|
|
|
|
go func() { |
|
|
|
|
for _, inst := range insts { |
|
|
|
|
// for interval, intervalAdder := range types.SupportedIntervals {
|
|
|
|
|
interval := types.Interval1h |
|
|
|
|
interval := types.Interval1d |
|
|
|
|
intervalAdder := types.SupportedIntervals[interval] |
|
|
|
|
// history 未补全前, history写 kvdb ts mark, 补全后 ws live 写 ts mark
|
|
|
|
|
tsKey := fmt.Sprintf(HistoryKlineTsKey, inst.Exchange, inst.InstId, interval) |
|
|
|
|
beforeTs, e := svc.kvdb.GetI64(context.Background(), tsKey) |
|
|
|
|
if e != nil { |
|
|
|
|
err = e |
|
|
|
|
return |
|
|
|
|
beforeTs, err := svc.kvdb.GetI64(context.Background(), tsKey) |
|
|
|
|
if err != nil { |
|
|
|
|
zlog.Error(err) |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
if beforeTs == 0 { |
|
|
|
|
beforeTs = intervalAdder(KlineBefore0, -1) |
|
|
|
|
} |
|
|
|
|
for { |
|
|
|
|
afterTs := intervalAdder(beforeTs, 101) |
|
|
|
|
ch <- task{ |
|
|
|
|
taskCh <- initialKlineTask{ |
|
|
|
|
inst: inst, |
|
|
|
|
interval: interval, |
|
|
|
|
afterTs: afterTs, |
|
|
|
|
beforeTs: beforeTs, |
|
|
|
|
times: 0, |
|
|
|
|
} |
|
|
|
|
beforeTs = intervalAdder(afterTs, -1) |
|
|
|
|
|
|
|
|
|
// 对比 ws 获取的实时k线
|
|
|
|
|
inst, ok := exchange.Insts.Load(inst.ExchangeInstId) |
|
|
|
|
if !ok { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
// 订阅完成
|
|
|
|
|
if inst.LiveMarkTs != 0 && beforeTs > inst.LiveMarkTs { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
beforeTs = afterTs |
|
|
|
|
// todo compare ws live ts
|
|
|
|
|
} |
|
|
|
|
// }
|
|
|
|
|
} |
|
|
|
|
close(ch) |
|
|
|
|
close(taskCh) |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
loc, _ := time.LoadLocation("Asia/Shanghai") |
|
|
|
|
|
|
|
|
|
// 任务消费器 8协程并行
|
|
|
|
|
wg := new(sync.WaitGroup) |
|
|
|
|
for range 11 { |
|
|
|
|
@ -344,27 +361,40 @@ func (svc *ExchangeGrpcServer) initialKlines(exchange *Exchange, insts []types.T
|
|
|
|
|
go func() { |
|
|
|
|
defer wg.Done() |
|
|
|
|
for { |
|
|
|
|
task, ok := <-ch |
|
|
|
|
task, ok := <-taskCh |
|
|
|
|
if !ok { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if task.times > 0 { |
|
|
|
|
zlog.Infof("retry fetch history kline task %d times: task -> %s", task.times, task.logKey()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
interval, afterTs, beforeTs := task.interval, task.afterTs, task.beforeTs |
|
|
|
|
klines, e := exchange.Fetcher.FetchHistoryKlines(context.Background(), task.inst.ExchangeInstId, interval, afterTs, beforeTs) |
|
|
|
|
if e != nil { |
|
|
|
|
// todo retry
|
|
|
|
|
err = e |
|
|
|
|
klines, err := exchange.Fetcher.FetchHistoryKlines(context.Background(), task.inst.ExchangeInstId, interval, afterTs, beforeTs) |
|
|
|
|
if err != nil { |
|
|
|
|
zlog.Errorf("fetch history kline task error: task -> %s, err -> %v", task.logKey(), err) |
|
|
|
|
// retry task
|
|
|
|
|
task.times++ |
|
|
|
|
taskCh <- task |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if len(klines) == 0 { |
|
|
|
|
continue // todo ....
|
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
zlog.Infof("fetch interval %s %d~%d klines: ret=%d~%d, %d klines", interval, afterTs, beforeTs, klines[0].Ts, klines[len(klines)-1].Ts, len(klines)) |
|
|
|
|
sts, ets := klines[0].Ts, klines[len(klines)-1].Ts |
|
|
|
|
ss := time.UnixMilli(sts).In(loc).Format(times.FORMAT_DATE) |
|
|
|
|
ee := time.UnixMilli(ets).In(loc).Format(times.FORMAT_DATE) |
|
|
|
|
zlog.Infof("fetch interval %s %d~%d klines: ret=%d~%d, %d klines, %s~%s", interval, afterTs, beforeTs, sts, ets, len(klines), ss, ee) |
|
|
|
|
|
|
|
|
|
// store to tsdb
|
|
|
|
|
err = svc.exchangeDataService.SaveKlines(task.inst, klines) |
|
|
|
|
if err != nil { |
|
|
|
|
zlog.Errorf("save history klines to tsdb error: task -> %s, err -> %v", task.logKey(), err) |
|
|
|
|
// retry task
|
|
|
|
|
task.times++ |
|
|
|
|
taskCh <- task |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|