2024-12-12 09:21:23 +08:00
|
|
|
|
package core
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
2024-12-26 23:02:21 +08:00
|
|
|
|
// "reflect"
|
2024-12-25 16:33:20 +08:00
|
|
|
|
// "fmt"
|
2024-12-12 09:21:23 +08:00
|
|
|
|
"math/rand"
|
|
|
|
|
"os"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
simple "github.com/bitly/go-simplejson"
|
|
|
|
|
"github.com/go-redis/redis"
|
|
|
|
|
"github.com/phyer/texus/utils"
|
2024-12-15 00:17:59 +08:00
|
|
|
|
logrus "github.com/sirupsen/logrus"
|
2024-12-12 09:21:23 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Candle struct {
|
2024-12-18 12:25:43 +08:00
|
|
|
|
Id string `json:"_id"`
|
|
|
|
|
core *Core
|
2024-12-20 13:26:14 +08:00
|
|
|
|
InstID string `json:"instID"`
|
2024-12-20 12:51:10 +08:00
|
|
|
|
Period string `json:"period"`
|
2024-12-18 12:25:43 +08:00
|
|
|
|
Data []interface{}
|
2024-12-20 12:51:10 +08:00
|
|
|
|
From string `json:"from"`
|
2024-12-20 13:26:14 +08:00
|
|
|
|
Timestamp time.Time `json:"timeStamp"`
|
2024-12-18 12:25:43 +08:00
|
|
|
|
LastUpdate time.Time `json:"lastUpdate"`
|
2024-12-20 12:51:10 +08:00
|
|
|
|
Open float64 `json:"open"`
|
2024-12-20 13:26:14 +08:00
|
|
|
|
High float64 `json:"high"`
|
2024-12-20 12:51:10 +08:00
|
|
|
|
Low float64 `json:"low"`
|
|
|
|
|
Close float64 `json:"close"`
|
|
|
|
|
VolCcy float64 `json:"volCcy"`
|
|
|
|
|
Confirm bool `json:"confirm"`
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
2024-12-15 00:17:59 +08:00
|
|
|
|
type Sample interface {
|
|
|
|
|
SetToKey(cr *Core) ([]interface{}, error)
|
2024-12-14 20:41:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 00:17:59 +08:00
|
|
|
|
type SampleList interface {
|
|
|
|
|
// 从左边插入一个元素,把超过长度的元素顶出去
|
|
|
|
|
RPush(sp Sample) (Sample, error)
|
|
|
|
|
// 得到一个切片,end一般是0,代表末尾元素,start是负值,-3代表倒数第三个
|
|
|
|
|
// start:-10, end: -3 代表从倒数第10个到倒数第三个之间的元素组成的切片。
|
|
|
|
|
GetSectionOf(start int, end int) ([]*Sample, error)
|
|
|
|
|
}
|
2024-12-14 21:58:32 +08:00
|
|
|
|
type CandleList struct {
|
|
|
|
|
Count int `json:"count,number"`
|
|
|
|
|
LastUpdateTime int64 `json:"lastUpdateTime"`
|
|
|
|
|
UpdateNickName string `json:"updateNickName"`
|
|
|
|
|
List []*Candle `json:"list"`
|
|
|
|
|
}
|
2024-12-15 00:17:59 +08:00
|
|
|
|
type CandleSegment struct {
|
|
|
|
|
StartTime string `json:"startTime"`
|
|
|
|
|
Enabled bool `json:"enabled,bool"`
|
|
|
|
|
Seg string `json:"seg"`
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
type MatchCheck struct {
|
|
|
|
|
Minutes int64
|
|
|
|
|
Matched bool
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-13 16:27:37 +08:00
|
|
|
|
func (cd *Candle) Filter(cr *Core) bool {
|
|
|
|
|
myFocusList := cr.Cfg.Config.Get("focusList").MustArray()
|
|
|
|
|
founded := false
|
|
|
|
|
for _, v := range myFocusList {
|
2024-12-15 20:49:18 +08:00
|
|
|
|
if v.(string) == cd.InstID {
|
2024-12-13 16:27:37 +08:00
|
|
|
|
founded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return founded
|
|
|
|
|
}
|
2024-12-12 09:21:23 +08:00
|
|
|
|
func (mc *MatchCheck) SetMatched(value bool) {
|
|
|
|
|
mc.Matched = value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (core *Core) GetCandlesWithRest(instId string, kidx int, dura time.Duration, maxCandles int) error {
|
|
|
|
|
ary := []string{}
|
|
|
|
|
|
|
|
|
|
wsary := core.Cfg.CandleDimentions
|
|
|
|
|
for k, v := range wsary {
|
|
|
|
|
matched := false
|
|
|
|
|
// 这个算法的目的是:越靠后的candles维度,被命中的概率越低,第一个百分之百命中,后面开始越来越低, 每分钟都会发生这样的计算,
|
|
|
|
|
// 因为维度多了的话,照顾不过来
|
|
|
|
|
rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
n := (k*2 + 2) * 3
|
|
|
|
|
if n < 1 {
|
|
|
|
|
n = 1
|
|
|
|
|
}
|
|
|
|
|
b := rand.Intn(n)
|
|
|
|
|
if b < 8 {
|
|
|
|
|
matched = true
|
|
|
|
|
}
|
|
|
|
|
if matched {
|
|
|
|
|
ary = append(ary, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mdura := dura/(time.Duration(len(ary)+1)) - 50*time.Millisecond
|
|
|
|
|
// fmt.Println("loop4 Ticker Start instId, dura: ", instId, dura, dura/10, mdura, len(ary), " idx: ", kidx)
|
|
|
|
|
// time.Duration(len(ary)+1)
|
|
|
|
|
ticker := time.NewTicker(mdura)
|
|
|
|
|
done := make(chan bool)
|
|
|
|
|
idx := 0
|
|
|
|
|
go func(i int) {
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-ticker.C:
|
|
|
|
|
if i >= (len(ary)) {
|
|
|
|
|
done <- true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
b := rand.Intn(2)
|
|
|
|
|
maxCandles = maxCandles * (i + b) * 2
|
|
|
|
|
|
|
|
|
|
if maxCandles < 3 {
|
|
|
|
|
maxCandles = 3
|
|
|
|
|
}
|
|
|
|
|
if maxCandles > 30 {
|
|
|
|
|
maxCandles = 30
|
|
|
|
|
}
|
|
|
|
|
mx := strconv.Itoa(maxCandles)
|
|
|
|
|
// fmt.Println("loop4 getCandlesWithRest, instId, period,limit,dura, t: ", instId, ary[i], mx, mdura)
|
|
|
|
|
go func(ii int) {
|
|
|
|
|
restQ := RestQueue{
|
|
|
|
|
InstId: instId,
|
|
|
|
|
Bar: ary[ii],
|
|
|
|
|
Limit: mx,
|
|
|
|
|
Duration: mdura,
|
|
|
|
|
WithWs: true,
|
|
|
|
|
}
|
|
|
|
|
js, _ := json.Marshal(restQ)
|
2024-12-15 00:17:59 +08:00
|
|
|
|
core.RedisLocalCli.LPush("restQueue", js)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}(i)
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}(idx)
|
|
|
|
|
time.Sleep(dura - 10*time.Millisecond)
|
|
|
|
|
ticker.Stop()
|
|
|
|
|
// fmt.Println("loop4 Ticker stopped instId, dura: ", instId, dura, mdura)
|
|
|
|
|
done <- true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当前的时间毫秒数 对于某个时间段,比如3分钟,10分钟,是否可以被整除,
|
|
|
|
|
func IsModOf(curInt int64, duration time.Duration) bool {
|
|
|
|
|
vol := int64(0)
|
|
|
|
|
if duration < 24*time.Hour {
|
|
|
|
|
// 小于1天
|
|
|
|
|
vol = (curInt + 28800000)
|
|
|
|
|
} else if duration >= 24*time.Hour && duration < 48*time.Hour {
|
|
|
|
|
// 1天
|
|
|
|
|
vol = curInt - 1633881600000
|
|
|
|
|
} else if duration >= 48*time.Hour && duration < 72*time.Hour {
|
|
|
|
|
// 2天
|
|
|
|
|
vol = curInt - 1633795200000
|
|
|
|
|
} else if duration >= 72*time.Hour && duration < 120*time.Hour {
|
|
|
|
|
// 3天
|
|
|
|
|
vol = curInt - 1633708800000
|
|
|
|
|
} else if duration >= 120*time.Hour {
|
|
|
|
|
// 5天
|
|
|
|
|
vol = curInt - 1633795200000
|
|
|
|
|
} else {
|
|
|
|
|
// fmt.Println("noMatched:", curInt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mody := vol % duration.Milliseconds()
|
|
|
|
|
if mody == 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (core *Core) SaveCandle(instId string, period string, rsp *CandleData, dura time.Duration, withWs bool) {
|
|
|
|
|
leng := len(rsp.Data)
|
2024-12-25 16:33:20 +08:00
|
|
|
|
// fmt.Println("saveCandle leng: ", leng, " instId: ", instId, " period: ", period)
|
2024-12-26 15:58:55 +08:00
|
|
|
|
logrus.Info("saveCandles leng: ", leng, " instId: ", instId, " period: ", period, " length of rsp.Data: ", len(rsp.Data))
|
2025-01-18 23:41:14 +08:00
|
|
|
|
// softCandleSegmentList
|
|
|
|
|
segments := core.Cfg.Config.Get("softCandleSegmentList").MustArray()
|
2025-01-19 03:28:23 +08:00
|
|
|
|
logrus.Warn("lensof segments:", len(segments))
|
2025-01-18 23:41:14 +08:00
|
|
|
|
curSegStartTime := ""
|
2025-01-19 03:39:22 +08:00
|
|
|
|
for k, v := range segments {
|
|
|
|
|
logrus.Warn("fetch segments:", k, v)
|
2025-01-18 23:41:14 +08:00
|
|
|
|
cs := CandleSegment{}
|
2025-01-19 04:35:38 +08:00
|
|
|
|
sv, _ := json.Marshal(v)
|
|
|
|
|
json.Unmarshal(sv, &cs)
|
2025-01-18 23:41:14 +08:00
|
|
|
|
if !cs.Enabled {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-01-19 04:46:48 +08:00
|
|
|
|
logrus.Warn("fetch segments2: cs.Seg: ", cs.Seg, ", period:", period, ", cs.Seg == period: ", (cs.Seg == period))
|
2025-01-19 04:21:32 +08:00
|
|
|
|
|
2025-01-18 23:41:14 +08:00
|
|
|
|
if cs.Seg == period {
|
|
|
|
|
curSegStartTime = cs.StartTime
|
2025-01-19 04:51:30 +08:00
|
|
|
|
break
|
2025-01-18 23:41:14 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-19 04:21:32 +08:00
|
|
|
|
logrus.Warn("curSegStartTime:", curSegStartTime)
|
2025-01-18 23:41:14 +08:00
|
|
|
|
curTm, _ := time.ParseInLocation("2006-01-02 15:04.000", curSegStartTime, time.Local)
|
|
|
|
|
curTmi := curTm.UnixMilli()
|
|
|
|
|
|
2024-12-26 15:44:45 +08:00
|
|
|
|
for k, v := range rsp.Data {
|
2024-12-18 16:40:18 +08:00
|
|
|
|
tmi := ToInt64(v[0])
|
2024-12-26 23:00:54 +08:00
|
|
|
|
last := ToFloat64(v[4])
|
|
|
|
|
// ty := reflect.TypeOf(v[4]).Name()
|
|
|
|
|
// v4It, err := strconv.ParseInt(v[4].(string), 10, 64)
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// logrus.Info("saveCandles last is 0 is err: ", err, "v4It: ", v4It, "v[4]: ", v[4], "v[4] type: ", ty, "leng: ", leng, " instId: ", instId, " period: ", period, " length of rsp.Data: ", len(rsp.Data), " data:", rsp.Data)
|
|
|
|
|
// }
|
2024-12-24 12:17:21 +08:00
|
|
|
|
if last == 0 {
|
2024-12-26 23:00:54 +08:00
|
|
|
|
// logrus.Info("saveCandles last is 0: ", "v[4]: ", v[4], "v[4] type: ", ty, " v4It: ", v4It, "leng: ", leng, " instId: ", instId, " period: ", period, " length of rsp.Data: ", len(rsp.Data), " data:", rsp.Data)
|
2024-12-24 12:17:21 +08:00
|
|
|
|
continue
|
|
|
|
|
}
|
2025-01-18 23:41:14 +08:00
|
|
|
|
minutes, _ := core.PeriodToMinutes(period)
|
2025-01-19 05:00:22 +08:00
|
|
|
|
logrus.Warn("tmi: ", tmi, " curTim:", curTmi, " minutes: ", minutes)
|
2025-01-19 05:22:34 +08:00
|
|
|
|
if (tmi-curTmi)%(minutes*60000) != 0 {
|
2025-01-18 23:41:14 +08:00
|
|
|
|
logrus.Warn("saveCandles error: 当前记录中的时间戳:", curSegStartTime, ",并非周期节点:", period, " 忽略")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-18 16:40:18 +08:00
|
|
|
|
ts, _ := Int64ToTime(tmi)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
candle := Candle{
|
2024-12-18 12:56:03 +08:00
|
|
|
|
InstID: instId,
|
|
|
|
|
Period: period,
|
|
|
|
|
Data: v,
|
|
|
|
|
From: "rest",
|
2024-12-18 16:40:18 +08:00
|
|
|
|
Timestamp: ts,
|
2024-12-18 12:56:03 +08:00
|
|
|
|
LastUpdate: time.Now(),
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
2024-12-18 15:33:14 +08:00
|
|
|
|
|
2024-12-12 09:21:23 +08:00
|
|
|
|
//存到elasticSearch
|
|
|
|
|
candle.PushToWriteLogChan(core)
|
|
|
|
|
//保存rest得到的candle
|
|
|
|
|
// 发布到allCandles|publish, 给外部订阅者用于setToKey
|
|
|
|
|
arys := []string{ALLCANDLES_PUBLISH}
|
|
|
|
|
if withWs {
|
|
|
|
|
arys = append(arys, ALLCANDLES_INNER_PUBLISH)
|
2024-12-26 16:03:43 +08:00
|
|
|
|
time.Sleep(time.Duration(k*40) * time.Millisecond)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
// 如果candle都不需要存到redis,那么AddToGeneralCandleChnl也没有意义
|
|
|
|
|
saveCandle := os.Getenv("TEXUS_SAVECANDLE")
|
2024-12-26 15:58:55 +08:00
|
|
|
|
logrus.Info("saveCandles datas: k,v: ", k, v)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
if saveCandle == "true" {
|
2024-12-26 15:44:45 +08:00
|
|
|
|
go func(k int) {
|
2024-12-26 23:15:45 +08:00
|
|
|
|
time.Sleep(time.Duration(k*100) * time.Millisecond)
|
2024-12-26 15:44:45 +08:00
|
|
|
|
candle.SetToKey(core)
|
|
|
|
|
core.AddToGeneralCandleChnl(&candle, arys)
|
|
|
|
|
}(k)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (candle *Candle) PushToWriteLogChan(cr *Core) error {
|
2024-12-15 20:49:18 +08:00
|
|
|
|
did := candle.InstID + candle.Period + candle.Data[0].(string)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
candle.Id = HashString(did)
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl, _ := candle.ToStruct(cr)
|
|
|
|
|
logrus.Debug("cl: ", cl)
|
|
|
|
|
cd, err := json.Marshal(cl)
|
2024-12-18 15:33:14 +08:00
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("PushToWriteLog json marshal candle err: ", err)
|
2024-12-18 15:33:14 +08:00
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
candle = cl
|
2024-12-12 09:21:23 +08:00
|
|
|
|
wg := WriteLog{
|
|
|
|
|
Content: cd,
|
|
|
|
|
Tag: "sardine.log.candle." + candle.Period,
|
|
|
|
|
Id: candle.Id,
|
|
|
|
|
}
|
|
|
|
|
cr.WriteLogChan <- &wg
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Daoxu(arr []interface{}) {
|
|
|
|
|
var temp interface{}
|
|
|
|
|
length := len(arr)
|
|
|
|
|
for i := 0; i < length/2; i++ {
|
|
|
|
|
temp = arr[i]
|
|
|
|
|
arr[i] = arr[length-1-i]
|
|
|
|
|
arr[length-1-i] = temp
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func HashString(input string) string {
|
|
|
|
|
// 计算SHA-256哈希值
|
|
|
|
|
hash := sha256.Sum256([]byte(input))
|
|
|
|
|
// 转换为十六进制字符串
|
|
|
|
|
hashHex := hex.EncodeToString(hash[:])
|
|
|
|
|
// 返回前20位
|
|
|
|
|
return hashHex[:23]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cl *Candle) ToStruct(core *Core) (*Candle, error) {
|
|
|
|
|
// cl.Timestamp
|
|
|
|
|
// 将字符串转换为 int64 类型的时间戳
|
|
|
|
|
ts, err := strconv.ParseInt(cl.Data[0].(string), 10, 64)
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("Error parsing timestamp:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.Timestamp = time.Unix(ts/1000, (ts%1000)*1000000) // 纳秒级别
|
2024-12-12 09:21:23 +08:00
|
|
|
|
op, err := strconv.ParseFloat(cl.Data[1].(string), 64)
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("Error parsing string to float64:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.Open = op
|
2024-12-12 09:21:23 +08:00
|
|
|
|
hi, err := strconv.ParseFloat(cl.Data[2].(string), 64)
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("Error parsing string to float64:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.High = hi
|
2024-12-12 09:21:23 +08:00
|
|
|
|
lo, err := strconv.ParseFloat(cl.Data[3].(string), 64)
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("Error parsing string to float64:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.Low = lo
|
2024-12-12 09:21:23 +08:00
|
|
|
|
clse, err := strconv.ParseFloat(cl.Data[4].(string), 64)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("Error parsing string to float64:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.Close = clse
|
|
|
|
|
cl.VolCcy, err = strconv.ParseFloat(cl.Data[6].(string), 64)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("Error parsing string to float64:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if cl.Data[6].(string) == "1" {
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.Confirm = true
|
2024-12-12 09:21:23 +08:00
|
|
|
|
} else {
|
2024-12-27 12:57:20 +08:00
|
|
|
|
cl.Confirm = false
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
2024-12-27 12:57:20 +08:00
|
|
|
|
return cl, nil
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保证同一个 period, keyName ,在一个周期里,SaveToSortSet只会被执行一次
|
2024-12-15 00:17:59 +08:00
|
|
|
|
func (core *Core) SaveUniKey(period string, keyName string, extt time.Duration, tsi int64) {
|
2024-12-12 09:21:23 +08:00
|
|
|
|
|
2025-01-14 18:18:13 +08:00
|
|
|
|
refName := keyName + "|refer"
|
2025-01-19 22:15:49 +08:00
|
|
|
|
// refRes, _ := core.RedisLocalCli.GetSet(refName, 1).Result()
|
2025-01-14 18:18:13 +08:00
|
|
|
|
core.RedisLocalCli.Expire(refName, extt)
|
2025-01-18 23:41:14 +08:00
|
|
|
|
// 为保证唯一性机制,防止SaveToSortSet 被重复执行, ps: 不需要唯一,此操作幂等在redis里
|
|
|
|
|
// founded, _ := core.findInSortSet(period, keyName, extt, tsi)
|
2025-01-19 22:15:49 +08:00
|
|
|
|
// if len(refRes) != 0 {
|
|
|
|
|
// logrus.Error("refName exist: ", refName)
|
|
|
|
|
// return
|
|
|
|
|
// }
|
2024-12-12 09:21:23 +08:00
|
|
|
|
core.SaveToSortSet(period, keyName, extt, tsi)
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 02:18:45 +08:00
|
|
|
|
func (core *Core) findInSortSet(period string, keyName string, extt time.Duration, tsi int64) (bool, error) {
|
|
|
|
|
founded := false
|
|
|
|
|
ary := strings.Split(keyName, "ts:")
|
|
|
|
|
setName := ary[0] + "sortedSet"
|
|
|
|
|
opt := redis.ZRangeBy{
|
|
|
|
|
Min: ToString(tsi),
|
|
|
|
|
Max: ToString(tsi),
|
|
|
|
|
}
|
|
|
|
|
rs, err := core.RedisLocalCli.ZRangeByScore(setName, opt).Result()
|
|
|
|
|
if len(rs) > 0 {
|
|
|
|
|
founded = true
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
logrus.Error("err of ma7|ma30 add to redis:", err)
|
|
|
|
|
} else {
|
|
|
|
|
logrus.Info("sortedSet added to redis:", rs, keyName)
|
|
|
|
|
}
|
|
|
|
|
return founded, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 09:21:23 +08:00
|
|
|
|
// tsi: 上报时间timeStamp millinSecond
|
|
|
|
|
func (core *Core) SaveToSortSet(period string, keyName string, extt time.Duration, tsi int64) {
|
|
|
|
|
ary := strings.Split(keyName, "ts:")
|
|
|
|
|
setName := ary[0] + "sortedSet"
|
|
|
|
|
z := redis.Z{
|
|
|
|
|
Score: float64(tsi),
|
|
|
|
|
Member: keyName,
|
|
|
|
|
}
|
2024-12-15 00:17:59 +08:00
|
|
|
|
rs, err := core.RedisLocalCli.ZAdd(setName, z).Result()
|
2024-12-12 09:21:23 +08:00
|
|
|
|
if err != nil {
|
2025-01-14 00:00:43 +08:00
|
|
|
|
logrus.Warn("err of ma7|ma30 add to redis:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
} else {
|
2025-01-14 00:00:43 +08:00
|
|
|
|
logrus.Warn("sortedSet added to redis:", rs, keyName)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 00:17:59 +08:00
|
|
|
|
// 根据周期的文本内容,返回这代表多少个分钟
|
|
|
|
|
func (cr *Core) PeriodToMinutes(period string) (int64, error) {
|
2024-12-12 09:21:23 +08:00
|
|
|
|
ary := strings.Split(period, "")
|
|
|
|
|
beiStr := "1"
|
|
|
|
|
danwei := ""
|
2024-12-15 00:17:59 +08:00
|
|
|
|
if len(ary) == 0 {
|
|
|
|
|
err := errors.New(utils.GetFuncName() + " period is block")
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
2024-12-12 09:21:23 +08:00
|
|
|
|
if len(ary) == 3 {
|
|
|
|
|
beiStr = ary[0] + ary[1]
|
|
|
|
|
danwei = ary[2]
|
|
|
|
|
} else {
|
|
|
|
|
beiStr = ary[0]
|
|
|
|
|
danwei = ary[1]
|
|
|
|
|
}
|
|
|
|
|
cheng := 1
|
|
|
|
|
bei, _ := strconv.Atoi(beiStr)
|
|
|
|
|
switch danwei {
|
|
|
|
|
case "m":
|
|
|
|
|
{
|
|
|
|
|
cheng = bei
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "H":
|
|
|
|
|
{
|
|
|
|
|
cheng = bei * 60
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "D":
|
|
|
|
|
{
|
|
|
|
|
cheng = bei * 60 * 24
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "W":
|
|
|
|
|
{
|
|
|
|
|
cheng = bei * 60 * 24 * 7
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "M":
|
|
|
|
|
{
|
|
|
|
|
cheng = bei * 60 * 24 * 30
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "Y":
|
|
|
|
|
{
|
|
|
|
|
cheng = bei * 60 * 24 * 365
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
2024-12-15 00:17:59 +08:00
|
|
|
|
logrus.Warning("notmatch:", danwei, period)
|
|
|
|
|
panic("notmatch:" + period)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-15 00:17:59 +08:00
|
|
|
|
return int64(cheng), nil
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// type ScanCmd struct {
|
|
|
|
|
// baseCmd
|
|
|
|
|
//
|
|
|
|
|
// page []string
|
|
|
|
|
// cursor uint64
|
|
|
|
|
//
|
|
|
|
|
// process func(cmd Cmder) error
|
|
|
|
|
// }
|
|
|
|
|
func (core *Core) GetRangeKeyList(pattern string, from time.Time) ([]*simple.Json, error) {
|
|
|
|
|
// 比如,用来计算ma30或ma7,倒推多少时间范围,
|
2024-12-15 00:17:59 +08:00
|
|
|
|
redisCli := core.RedisLocalCli
|
2024-12-12 09:21:23 +08:00
|
|
|
|
cursor := uint64(0)
|
|
|
|
|
n := 0
|
|
|
|
|
allTs := []int64{}
|
|
|
|
|
var keys []string
|
|
|
|
|
for {
|
|
|
|
|
var err error
|
|
|
|
|
keys, cursor, _ = redisCli.Scan(cursor, pattern+"*", 2000).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
n += len(keys)
|
|
|
|
|
if n == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// keys, _ := redisCli.Keys(pattern + "*").Result()
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
keyAry := strings.Split(key, ":")
|
|
|
|
|
key = keyAry[1]
|
|
|
|
|
keyi64, _ := strconv.ParseInt(key, 10, 64)
|
|
|
|
|
allTs = append(allTs, keyi64)
|
|
|
|
|
}
|
|
|
|
|
nary := utils.RecursiveBubble(allTs, len(allTs))
|
|
|
|
|
tt := from.UnixMilli()
|
|
|
|
|
ff := tt - tt%60000
|
|
|
|
|
fi := int64(ff)
|
|
|
|
|
mary := []int64{}
|
|
|
|
|
for _, v := range nary {
|
|
|
|
|
if v < fi {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
mary = append(mary, v)
|
|
|
|
|
}
|
|
|
|
|
res := []*simple.Json{}
|
|
|
|
|
for _, v := range mary {
|
|
|
|
|
// if k > 1 {
|
|
|
|
|
// break
|
|
|
|
|
// }
|
|
|
|
|
nv := pattern + strconv.FormatInt(v, 10)
|
|
|
|
|
str, err := redisCli.Get(nv).Result()
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("err of redis get key:", nv, err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
cur, err := simple.NewJson([]byte(str))
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("err of create newJson:", str, err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
res = append(res, cur)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cl *Candle) SetToKey(core *Core) ([]interface{}, error) {
|
|
|
|
|
data := cl.Data
|
|
|
|
|
tsi, err := strconv.ParseInt(data[0].(string), 10, 64)
|
2024-12-27 12:40:59 +08:00
|
|
|
|
|
2024-12-12 09:21:23 +08:00
|
|
|
|
tss := strconv.FormatInt(tsi, 10)
|
2024-12-18 16:45:18 +08:00
|
|
|
|
tm, _ := Int64ToTime(tsi)
|
|
|
|
|
|
2024-12-15 20:49:18 +08:00
|
|
|
|
keyName := "candle" + cl.Period + "|" + cl.InstID + "|ts:" + tss
|
2024-12-12 09:21:23 +08:00
|
|
|
|
//过期时间:根号(当前candle的周期/1分钟)*10000
|
2024-12-18 13:32:49 +08:00
|
|
|
|
cl.LastUpdate = time.Now()
|
2024-12-18 16:45:18 +08:00
|
|
|
|
cl.Timestamp = tm
|
2024-12-17 11:21:53 +08:00
|
|
|
|
dt, err := json.Marshal(cl)
|
2024-12-18 15:19:18 +08:00
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("candle Save to String err:", err)
|
2024-12-18 15:19:18 +08:00
|
|
|
|
}
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Info("candle Save to String: ", string(dt))
|
2024-12-15 00:17:59 +08:00
|
|
|
|
exp, err := core.PeriodToMinutes(cl.Period)
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("err of PeriodToMinutes:", err)
|
2024-12-15 00:17:59 +08:00
|
|
|
|
}
|
2024-12-12 09:21:23 +08:00
|
|
|
|
// expf := float64(exp) * 60
|
2025-01-23 12:00:23 +08:00
|
|
|
|
length, _ := core.Cfg.Config.Get("sortedSet").Get("length").String()
|
|
|
|
|
ln := ToFloat64(length)
|
|
|
|
|
expf := float64(exp) * ln
|
2024-12-12 09:21:23 +08:00
|
|
|
|
extt := time.Duration(expf) * time.Minute
|
|
|
|
|
curVolstr, _ := data[5].(string)
|
|
|
|
|
curVol, err := strconv.ParseFloat(curVolstr, 64)
|
|
|
|
|
if err != nil {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("err of convert ts:", err)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
}
|
|
|
|
|
curVolCcystr, _ := data[6].(string)
|
|
|
|
|
curVolCcy, err := strconv.ParseFloat(curVolCcystr, 64)
|
|
|
|
|
curPrice := curVolCcy / curVol
|
|
|
|
|
if curPrice <= 0 {
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Error("price有问题", curPrice, "dt: ", string(dt), "from:", cl.From)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
err = errors.New("price有问题")
|
|
|
|
|
return cl.Data, err
|
|
|
|
|
}
|
2024-12-15 00:17:59 +08:00
|
|
|
|
redisCli := core.RedisLocalCli
|
2024-12-12 09:21:23 +08:00
|
|
|
|
// tm := time.UnixMilli(tsi).Format("2006-01-02 15:04")
|
2024-12-19 20:00:15 +08:00
|
|
|
|
logrus.Info("setToKey:", keyName, "ts: ", "price: ", curPrice, "from:", cl.From)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
redisCli.Set(keyName, dt, extt).Result()
|
2024-12-15 00:17:59 +08:00
|
|
|
|
core.SaveUniKey(cl.Period, keyName, extt, tsi)
|
2024-12-12 09:21:23 +08:00
|
|
|
|
return cl.Data, err
|
|
|
|
|
}
|
2024-12-15 17:40:14 +08:00
|
|
|
|
|
|
|
|
|
// 冒泡排序
|
|
|
|
|
func (cdl *CandleList) RecursiveBubbleS(length int, ctype string) error {
|
|
|
|
|
if length == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for idx, _ := range cdl.List {
|
|
|
|
|
if idx >= length-1 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
temp := Candle{}
|
|
|
|
|
pre := ToInt64(cdl.List[idx].Data[0])
|
|
|
|
|
nex := ToInt64(cdl.List[idx+1].Data[0])
|
|
|
|
|
daoxu := pre < nex
|
|
|
|
|
if ctype == "asc" {
|
|
|
|
|
daoxu = !daoxu
|
|
|
|
|
}
|
|
|
|
|
if daoxu { //改变成>,换成从小到大排序
|
|
|
|
|
temp = *cdl.List[idx]
|
|
|
|
|
cdl.List[idx] = cdl.List[idx+1]
|
|
|
|
|
cdl.List[idx+1] = &temp
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
length--
|
|
|
|
|
cdl.RecursiveBubbleS(length, ctype)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO 返回的Sample是被弹出队列的元素,如果没有就是nil
|
|
|
|
|
func (cdl *CandleList) RPush(sp *Candle) (Sample, error) {
|
|
|
|
|
last := Candle{}
|
|
|
|
|
tsi := ToInt64(sp.Data[0])
|
|
|
|
|
matched := false
|
|
|
|
|
// bj, _ := json.Marshal(*sp)
|
|
|
|
|
cdl.RecursiveBubbleS(len(cdl.List), "asc")
|
|
|
|
|
for k, v := range cdl.List {
|
|
|
|
|
if ToInt64(v.Data[0]) == tsi {
|
|
|
|
|
matched = true
|
|
|
|
|
cdl.List[k] = sp
|
|
|
|
|
bj, err := json.Marshal(sp)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logrus.Warning("err of convert cdl item:", err)
|
|
|
|
|
}
|
|
|
|
|
logrus.Debug("candleList RPush replace: ", string(bj), "v.Data[0]: ", v.Data[0], "tsi:", tsi)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if matched {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
if len(cdl.List) >= cdl.Count {
|
|
|
|
|
last = *cdl.List[0]
|
|
|
|
|
cdl.List = cdl.List[1:]
|
|
|
|
|
cdl.List = append(cdl.List, sp)
|
|
|
|
|
bj, err := json.Marshal(sp)
|
|
|
|
|
logrus.Debug("candleList RPush popup: ", string(bj), "len(cdl.List): ", len(cdl.List), "cdl.Count:", cdl.Count)
|
|
|
|
|
return &last, err
|
|
|
|
|
} else {
|
|
|
|
|
cdl.List = append(cdl.List, sp)
|
|
|
|
|
bj, err := json.Marshal(sp)
|
|
|
|
|
logrus.Debug("candleList RPush insert: ", string(bj), "len(cdl.List): ", len(cdl.List), "cdl.Count:", cdl.Count)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|