567 lines
17 KiB
Go
567 lines
17 KiB
Go
package core
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"net/http"
|
||
"os"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/go-redis/redis"
|
||
logrus "github.com/sirupsen/logrus"
|
||
// "phyer.click/sardine/utils"
|
||
)
|
||
|
||
type Series struct {
|
||
InstID string `json:"instID"`
|
||
Period string `json:"Period"`
|
||
Count int `json:"count,number"`
|
||
Scale float64 `json:"scale,number"`
|
||
LastUpdateTime int64 `json:"lastUpdateTime,number"`
|
||
UpdateNickName string
|
||
LastCandle1m Candle `json:"lastCandle1m"`
|
||
CandleSeries *PixelList `json:"candleSerie"`
|
||
Ma7Series *PixelList `json:"ma7Serie"`
|
||
Ma30Series *PixelList `json:"ma30Serie"`
|
||
}
|
||
|
||
type SeriesInfo struct {
|
||
InstID string `json:"instID"`
|
||
Period string `json:"period"`
|
||
InsertedNew bool `json:"insertedNew,bool"`
|
||
Score float64 `json:"score,number"`
|
||
}
|
||
type SeriesInfoScore struct {
|
||
InstID string `json:"instID"`
|
||
Score float64 `json:"score,number"`
|
||
}
|
||
|
||
// TODO
|
||
// redis key: verticalReportItem|BTC-USDT|4H-15m|ts:1643002300000
|
||
// sortedSet: verticalLimit|2D-4H|rank|sortedSet
|
||
type VerticalReportItem struct {
|
||
InstID string
|
||
Period string
|
||
ReportTime int64
|
||
LastUpdate int64
|
||
LastUpdateTime string
|
||
Interval int
|
||
TrigerValue float64
|
||
AdvUpSellPrice float64
|
||
AdvDownSellPrice float64
|
||
Rank float64
|
||
ShearForce float64
|
||
VerticalElevation float64
|
||
SecondPeriod string
|
||
}
|
||
|
||
// type Segment struct {
|
||
// IndextStart int
|
||
// IndexEnd int
|
||
//
|
||
// }
|
||
|
||
// 根据instId 和period 从 PlateMap里拿到coaster,创建对应的 series,
|
||
func (sr *Series) Refresh(cr *Core) error {
|
||
curCo, err := cr.GetCoasterFromPlate(sr.InstID, sr.Period)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
ma30List := curCo.Ma30List.List
|
||
ma30len := len(ma30List)
|
||
if ma30len == 0 {
|
||
err = errors.New("ma30List is empty:" + sr.InstID + "," + sr.Period)
|
||
return err
|
||
}
|
||
baseMaX := ma30List[ma30len-1]
|
||
|
||
ma30Pxl, err := curCo.Ma30List.MakePixelList(cr, baseMaX, sr.Scale)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sr.Ma30Series = ma30Pxl
|
||
ma7Pxl, err := curCo.Ma7List.MakePixelList(cr, baseMaX, sr.Scale)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sr.Ma7Series = ma7Pxl
|
||
curCo.CandleList.RecursiveBubbleS(len(curCo.CandleList.List), "asc")
|
||
candlePxl, err := curCo.CandleList.MakePixelList(cr, baseMaX, sr.Scale)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sr.CandleSeries = candlePxl
|
||
// bj, _ := json.Marshal(sr.Ma30Series)
|
||
// fmt.Println("sr.Ma30Series:", sr.Period, sr.InstID, string(bj))
|
||
sr.LastUpdateTime = sr.Ma30Series.LastUpdateTime
|
||
// fmt.Println("candlePxl: ", candlePxl)
|
||
return nil
|
||
}
|
||
|
||
func (sr *Series) SetToKey(cr *Core) (string, error) {
|
||
if sr == nil || sr.CandleSeries == nil {
|
||
return "", errors.New("sr.CandlesSeries == nil")
|
||
}
|
||
sr.CandleSeries.RecursiveBubbleS(sr.CandleSeries.Count, "asc")
|
||
sr.CandleSeries.ReIndex()
|
||
sr.CandleSeries.RecursiveBubbleS(sr.CandleSeries.Count, "asc")
|
||
// sr.CandleSeries.RecursiveBubbleX(sr.CandleSeries.Count, "asc")
|
||
sr.Ma7Series.RecursiveBubbleS(sr.CandleSeries.Count, "asc")
|
||
// sr.Ma7Series.RecursiveBubbleX(sr.CandleSeries.Count, "asc")
|
||
sr.Ma30Series.RecursiveBubbleS(sr.CandleSeries.Count, "asc")
|
||
// sr.Ma30Series.RecursiveBubbleX(sr.CandleSeries.Count, "asc")
|
||
now := time.Now().UnixMilli()
|
||
sr.LastUpdateTime = now
|
||
sr.CandleSeries.LastUpdateTime = now
|
||
sr.CandleSeries.UpdateNickName = utils.GetRandomString(12)
|
||
sr.UpdateNickName = utils.GetRandomString(12)
|
||
js, _ := json.Marshal(*sr)
|
||
seriesName := sr.InstID + "|" + sr.Period + "|series"
|
||
res, err := cr.RedisLocalCli.Set(seriesName, string(js), 0).Result()
|
||
if err != nil {
|
||
logrus.Panic(utils.GetFuncName(), err, " seriesSetToKey1: instId:", sr.InstID, " period: ", sr.Period, " lastUpdate:", sr.LastUpdateTime, " md5:", utils.Md5V(string(js)))
|
||
}
|
||
res, err = cr.RedisLocal2Cli.Set(seriesName, string(js), 0).Result()
|
||
return res, err
|
||
}
|
||
func PrintSerieY(cr *Core, list []redis.Z, period string, count int) {
|
||
// fmt.Println("PrintSerieY start")
|
||
env := os.Getenv("GO_ENV")
|
||
isProduction := env == "production"
|
||
//TODO 只有非产线环境,才会显示此列表
|
||
if !isProduction {
|
||
fmt.Println("seriesYTop count:", count, "period:", period, "sort start")
|
||
}
|
||
seiScrList := []*SeriesInfoScore{}
|
||
for _, v := range list {
|
||
sei := SeriesInfo{}
|
||
seiScr := SeriesInfoScore{}
|
||
json.Unmarshal([]byte(v.Member.(string)), &sei)
|
||
seiScr.InstID = sei.InstID
|
||
seiScr.Score = v.Score
|
||
seiScrList = append(seiScrList, &seiScr)
|
||
|
||
// if k < count {
|
||
// if !isProduction {
|
||
// fmt.Println("seriesYTop", count, "No.", k+1, "period"+period, "InstID:", sei.InstID, "score:", v.Score)
|
||
// }
|
||
// 拉扯极限报告
|
||
// }
|
||
// if k == count+1 {
|
||
// if !isProduction {
|
||
// fmt.Println("seriesYTop end -------" + "period" + period + "-------------------------------------")
|
||
// fmt.Println("seriesYLast start -------" + "period" + period + "-------------------------------------")
|
||
// }
|
||
// }
|
||
// if k > len(list)-count-1 {
|
||
// if !isProduction {
|
||
// fmt.Println("seriesYLast", count, "No.", k+1, "period"+period, "InstID:", sei.InstID, "score:", v.Score)
|
||
// }
|
||
// }
|
||
}
|
||
|
||
bj, _ := json.Marshal(seiScrList)
|
||
reqBody := bytes.NewBuffer(bj)
|
||
cr.Env = os.Getenv("GO_ENV")
|
||
cr.FluentBitUrl = os.Getenv("SARDINE_FluentBitUrl")
|
||
fullUrl := "http://" + cr.FluentBitUrl + "/seriesY." + period
|
||
|
||
res, err := http.Post(fullUrl, "application/json", reqBody)
|
||
fmt.Println("requested, response:", fullUrl, reqBody, res)
|
||
if err != nil {
|
||
logrus.Error(err)
|
||
}
|
||
|
||
if !isProduction {
|
||
fmt.Println("seriesYLast count:", count, "period:", period, "sort end")
|
||
}
|
||
}
|
||
|
||
func (sei *SeriesInfo) Process(cr *Core) {
|
||
curSe, err := cr.GetPixelSeries(sei.InstID, sei.Period)
|
||
if err != nil {
|
||
logrus.Warn("GetPixelSeries: ", err)
|
||
return
|
||
}
|
||
|
||
// TODO 金拱门
|
||
// list := cr.GetMyCcyBalanceName()
|
||
go func(se Series) {
|
||
|
||
threeSeg := [][]int{[]int{0, 19}, []int{19, 22}, []int{22, 23}}
|
||
|
||
ma7Seg := se.MakeSegment(cr, 0, 23, threeSeg, "ma7")
|
||
go func() {
|
||
cr.SegmentItemChan <- ma7Seg
|
||
}()
|
||
|
||
ma30Seg := se.MakeSegment(cr, 0, 23, threeSeg, "ma30")
|
||
|
||
go func() {
|
||
cr.SegmentItemChan <- ma30Seg
|
||
}()
|
||
|
||
}(curSe)
|
||
|
||
cli := cr.RedisLocalCli
|
||
go func(se Series) {
|
||
// 拉扯极限报告
|
||
willReport := os.Getenv("SARDINE_SERIESTOREPORT") == "true"
|
||
logrus.Info("willReport:", willReport)
|
||
// fmt.Println("willReport:", willReport)
|
||
if !willReport {
|
||
return
|
||
}
|
||
err = curSe.AddToYSorted(cr)
|
||
if err != nil {
|
||
logrus.Warn("sei addToYSorted err: ", err)
|
||
return
|
||
}
|
||
// 所有维度拉扯极限
|
||
go func(se Series) {
|
||
if se.InstID != "BTC-USDT" {
|
||
return
|
||
}
|
||
list, err := cli.ZRevRangeWithScores("series|YValue|sortedSet|period"+se.Period, 0, -1).Result()
|
||
if err != nil {
|
||
fmt.Println("series sorted err", err)
|
||
}
|
||
PrintSerieY(cr, list, se.Period, 20)
|
||
}(se)
|
||
|
||
}(curSe)
|
||
// TODO 刘海儿检测, 监测金拱门中的刘海儿,预警下跌趋势, 其实有没有金拱门并不重要,刘海儿比金拱门更有说服力
|
||
go func(se Series) {
|
||
// 如何定义刘海:目前定义如下,3m以上的周期时,当7个或小于7个周期内的时间内发生了一次下坠和一次上升,下坠幅度达到2%以上,并随后的上升中收复了下坠的幅度,那么疑似刘海儿发生。用的周期越少,越强烈,探底和抬升的幅度越大越强烈,所处的维度越高越强烈,比如15m的没有1H的强烈
|
||
// 如果发生在BTC身上,那么将影响所有
|
||
// se.CheckLiuhai() {
|
||
//
|
||
// }
|
||
|
||
}(curSe)
|
||
go func(se Series) {
|
||
allow := os.Getenv("SARDINE_SERIESINFOTOCHNL") == "true"
|
||
if !allow {
|
||
return
|
||
}
|
||
time.Sleep(0 * time.Second)
|
||
sei := SeriesInfo{
|
||
InstID: curSe.InstID,
|
||
Period: curSe.Period,
|
||
}
|
||
cr.AddToGeneralSeriesInfoChnl(&sei)
|
||
}(curSe)
|
||
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------
|
||
|
||
// 拉扯极限相关: 加入seriesY值排行榜, 用于生成拉扯极限
|
||
func (srs *Series) AddToYSorted(cr *Core) error {
|
||
setName := "series|YValue|sortedSet|period" + srs.Period
|
||
srs.CandleSeries.RecursiveBubbleS(srs.CandleSeries.Count, "asc")
|
||
length := len(srs.CandleSeries.List)
|
||
if length != srs.Count {
|
||
err := errors.New("AddToYSorted err: 数据量不够")
|
||
return err
|
||
}
|
||
lastCandlePixel1 := srs.CandleSeries.List[srs.Count-1]
|
||
sei := SeriesInfo{
|
||
InstID: srs.InstID,
|
||
Period: srs.Period,
|
||
}
|
||
bj, _ := json.Marshal(sei)
|
||
// TODO -200 是个无效的值,如果遇到-200就赋予0值,这个办法不好,后面考虑不用sortedSet,而用自定义对象更好些。
|
||
if lastCandlePixel1.Y == -200 {
|
||
lastCandlePixel1.Y = 0
|
||
}
|
||
z := redis.Z{
|
||
Score: float64(lastCandlePixel1.Y),
|
||
Member: string(bj),
|
||
}
|
||
// TODO ZAdd 有可能由于bug或者key不一样的原因,让列表变长,需要想办法怎么定期请空
|
||
if lastCandlePixel1.Score != 0 {
|
||
cr.RedisLocalCli.ZAdd(setName, z).Result()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 垂直极限排名有一定片面性。暂时先不开放。垂直极限推荐最高的,可能是个不太容易📈上来的股票,甚至垃圾股,而且过一会儿可能跌的更多,所以就算使用这个功能,也仅供参考,
|
||
func (vir *VerticalReportItem) AddToVeriticalLimitSorted(cr *Core, srs *Series, period2 string) error {
|
||
// redis key: verticalReportItem|BTC-USDT|4H-15m|ts:1643002300000
|
||
// sortedSet: verticalLimit|2D-4H|rank|sortedSet
|
||
|
||
setName := "verticalLimit|" + srs.Period + "-" + period2 + "|rank|sortedSet"
|
||
tms := strconv.FormatInt(srs.LastUpdateTime, 10)
|
||
keyName := "verticalLimit|" + srs.InstID + "|" + srs.Period + "-" + period2 + "|ts:" + tms
|
||
z := redis.Z{
|
||
Score: float64(srs.LastUpdateTime),
|
||
Member: keyName,
|
||
}
|
||
if vir.Rank != -1 && vir.Rank != 0 {
|
||
extt := 48 * time.Hour
|
||
ot := time.Now().Add(extt * -1)
|
||
oti := ot.UnixMilli()
|
||
count, _ := cr.RedisLocalCli.ZRemRangeByScore(setName, "0", strconv.FormatInt(oti, 10)).Result()
|
||
if count > 0 {
|
||
logrus.Warning("移出过期的引用数量:", setName, count, "ZRemRangeByScore ", setName, 0, strconv.FormatInt(oti, 10))
|
||
}
|
||
cr.RedisLocalCli.ZAdd(setName, z).Result()
|
||
bj, _ := json.Marshal(vir)
|
||
cr.RedisLocalCli.Set(keyName, bj, 48*time.Hour).Result()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (vri *VerticalReportItem) Report(cr *Core) error {
|
||
dd := DingdingMsg{
|
||
Topic: "垂直极限触发",
|
||
RobotName: "pengpeng",
|
||
AtAll: true,
|
||
Ctype: "markdown",
|
||
Content: "",
|
||
}
|
||
ary1 := []string{}
|
||
str := "``币名: ``" + vri.InstID + "\n"
|
||
str1 := fmt.Sprintln("``基础维度:``", vri.Period)
|
||
str2 := fmt.Sprintln("``剪切维度:``", vri.SecondPeriod)
|
||
str21 := fmt.Sprintln("``观察周期:``", vri.Interval)
|
||
str3 := fmt.Sprintln("``平均仰角:``", vri.VerticalElevation)
|
||
str4 := fmt.Sprintln("``剪切力:``", vri.ShearForce)
|
||
str5 := fmt.Sprintln("``Rank:``", vri.Rank)
|
||
score := vri.TrigerValue
|
||
str6 := fmt.Sprintln("``触发买入价位:``", score)
|
||
str7 := fmt.Sprintln("``建议止盈价位:``", vri.AdvUpSellPrice)
|
||
str8 := fmt.Sprintln("``建议止损价位:``", vri.AdvDownSellPrice)
|
||
str9 := "----------------------\n"
|
||
ary1 = append(ary1, str, str1, str2, str21, str3, str4, str5, str6, str7, str8, str9)
|
||
dd.AddItemListGrp("垂直极限", 2, ary1)
|
||
ary2 := []string{}
|
||
|
||
tm := time.Now().Format("01-02:15:04")
|
||
rtime := fmt.Sprintln("``报告时间:``", tm)
|
||
ctype := fmt.Sprintln("``类型:``", "极限触发,已弃用")
|
||
from := "来自: " + os.Getenv("HOSTNAME")
|
||
ary2 = append(ary2, rtime, ctype, from)
|
||
dd.AddItemListGrp("", 2, ary2)
|
||
dd.PostToRobot("pengpeng", cr)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (vri *VerticalReportItem) Show(cr *Core) error {
|
||
ary1 := []string{}
|
||
str := "``币名: ``" + vri.InstID + "\n"
|
||
str1 := fmt.Sprintln("``基础维度:``", vri.Period)
|
||
str2 := fmt.Sprintln("``剪切维度:``", vri.SecondPeriod)
|
||
str21 := fmt.Sprintln("``观察周期:``", vri.Interval)
|
||
str3 := fmt.Sprintln("``平均仰角:``", vri.VerticalElevation)
|
||
str4 := fmt.Sprintln("``剪切力:``", vri.ShearForce)
|
||
str5 := fmt.Sprintln("``Rank:``", vri.Rank)
|
||
score := vri.TrigerValue
|
||
str6 := fmt.Sprintln("``触发买入价位:``", score)
|
||
str7 := fmt.Sprintln("``建议止盈价位:``", vri.AdvUpSellPrice)
|
||
str8 := fmt.Sprintln("``建议止损价位:``", vri.AdvDownSellPrice)
|
||
str9 := "----------------------\n"
|
||
ary1 = append(ary1, str, str1, str2, str21, str3, str4, str5, str6, str7, str8, str9)
|
||
for _, v := range ary1 {
|
||
fmt.Println("verticalReportItem: ", v)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// TODO 求某个PixelList里两个点之间的仰角,从ridx开始,往lidx的元素画一条直线,的仰角
|
||
|
||
func (srs *Series) GetElevation(cr *Core, ctype string, lIdx int, rIdx int) (float64, error) {
|
||
yj := float64(0)
|
||
switch ctype {
|
||
case "candle":
|
||
{
|
||
yj = (srs.CandleSeries.List[rIdx].Y - srs.CandleSeries.List[lIdx].Y) / float64(rIdx-lIdx)
|
||
}
|
||
case "ma7":
|
||
{
|
||
yj = (srs.Ma7Series.List[rIdx].Y - srs.Ma7Series.List[lIdx].Y) / float64(rIdx-lIdx)
|
||
}
|
||
case "ma30":
|
||
{
|
||
yj = (srs.Ma30Series.List[rIdx].Y - srs.Ma30Series.List[lIdx].Y) / float64(rIdx-lIdx)
|
||
|
||
}
|
||
}
|
||
return yj, nil
|
||
}
|
||
|
||
// TODO 求极值,在某个线段上。一个最大值,一个最小值
|
||
func (srs *Series) GetExtremum(cr *Core, lIdx int, rIdx int, ctype string) (*Extremum, error) {
|
||
ext := Extremum{
|
||
Max: &Pixel{},
|
||
Min: &Pixel{},
|
||
}
|
||
|
||
switch ctype {
|
||
case "candle":
|
||
{
|
||
done := false
|
||
for k, v := range srs.CandleSeries.List {
|
||
if k < lIdx {
|
||
continue
|
||
}
|
||
if v.X == 0 && v.Y == 0 {
|
||
continue
|
||
}
|
||
if k > rIdx {
|
||
continue
|
||
}
|
||
if !done {
|
||
ext.Max = srs.CandleSeries.List[k]
|
||
ext.Min = srs.CandleSeries.List[k]
|
||
done = true
|
||
}
|
||
|
||
if v.Y > ext.Max.Y {
|
||
ext.Max = v
|
||
}
|
||
if v.Y < ext.Min.Y {
|
||
ext.Min = v
|
||
}
|
||
}
|
||
// ext = nil
|
||
}
|
||
case "ma7":
|
||
{
|
||
done := false
|
||
for k, v := range srs.Ma7Series.List {
|
||
if k < lIdx {
|
||
continue
|
||
}
|
||
if v.X == 0 && v.Y == 0 {
|
||
continue
|
||
}
|
||
if k > rIdx {
|
||
continue
|
||
}
|
||
if !done {
|
||
ext.Max = srs.Ma7Series.List[k]
|
||
ext.Min = srs.Ma7Series.List[k]
|
||
done = true
|
||
}
|
||
if v.Y > ext.Max.Y {
|
||
ext.Max = v
|
||
}
|
||
if v.Y < ext.Min.Y {
|
||
ext.Min = v
|
||
}
|
||
}
|
||
// ext = nil
|
||
}
|
||
case "ma30":
|
||
{
|
||
|
||
done := false
|
||
for k, v := range srs.Ma30Series.List {
|
||
if k < lIdx {
|
||
continue
|
||
}
|
||
if v.X == 0 && v.Y == 0 {
|
||
continue
|
||
}
|
||
if k > rIdx {
|
||
continue
|
||
}
|
||
if !done {
|
||
ext.Max = srs.Ma30Series.List[k]
|
||
ext.Min = srs.Ma30Series.List[k]
|
||
done = true
|
||
}
|
||
if v.Y > ext.Max.Y {
|
||
ext.Max = v
|
||
}
|
||
if v.Y < ext.Min.Y {
|
||
ext.Min = v
|
||
}
|
||
}
|
||
// ext = nil
|
||
}
|
||
}
|
||
return &ext, nil
|
||
}
|
||
|
||
// TODO 获取垂直极限列表
|
||
// 筛选条件:
|
||
//
|
||
// 1. 极大值未发生在最后周期的,排除
|
||
// 2. n周期内,有仰角小于0的,排除
|
||
// 注意: 仰角极值未必发生在最后一个周期
|
||
//
|
||
// 对剩下的币种结果,计算:
|
||
//
|
||
// 1. n周期平均仰角: s
|
||
// 2. 最后周期仰角: p
|
||
//
|
||
// 筛选出最后仰角高于n周期平均仰角的币列表,
|
||
// 以最后仰角为结果,得到一个值 p
|
||
// 对此列表集合,得到每个的15分钟维度拉扯极限,每个计算后得到一个结果 f,
|
||
//
|
||
// f值权重更高,p值权重降一个量级,求出分值用于排名,
|
||
//
|
||
// rank = 2 * (lcjx * -1) * (1 + avgElevation) * (1 + lastElevation) * (1 + lastElevation)
|
||
//
|
||
// 存储在sortedSet里,命名:
|
||
// verticalLimit|15m~4H|rank|sortedSet
|
||
// return rank, err
|
||
func (vir *VerticalReportItem) MakeVerticalLimit(cr *Core, srs *Series, startIdx int, endIdx int, period2 string) (err error) {
|
||
count := len(srs.CandleSeries.List) - 1
|
||
lastMa30Pixel := srs.Ma30Series.List[count]
|
||
// func (srs *Series) GetExtremum(cr *Core, lIdx int, rIdx int, ctype string) (*Extremum, error) {
|
||
ext, err := srs.GetExtremum(cr, startIdx, endIdx, "ma30")
|
||
if err != nil {
|
||
logrus.Warn(utils.GetFuncName(), ":", err)
|
||
}
|
||
|
||
if ext.Max.Score < 1.05*lastMa30Pixel.Score {
|
||
lbj, _ := json.Marshal(lastMa30Pixel)
|
||
lext, _ := json.Marshal(ext)
|
||
err = errors.New(fmt.Sprintln("当前pixel不是极值", " lastMa30Pixel: ", string(lbj), " ext: ", string(lext)))
|
||
return err
|
||
} else {
|
||
err = errors.New(fmt.Sprintln("当前pixel满足极值", lastMa30Pixel))
|
||
}
|
||
|
||
yj, err := srs.GetElevation(cr, "ma30", startIdx, endIdx)
|
||
if err != nil {
|
||
logrus.Warn(utils.GetFuncName(), ":", err)
|
||
}
|
||
|
||
vir.VerticalElevation = yj
|
||
lcjx, _ := LacheJixian(cr, srs, period2)
|
||
vir.ShearForce = lcjx
|
||
vir.TrigerValue = srs.CandleSeries.List[len(srs.CandleSeries.List)-1].Score
|
||
vir.AdvUpSellPrice = vir.TrigerValue * 1.04
|
||
vir.AdvDownSellPrice = vir.TrigerValue * 0.98
|
||
// 计算rank的公式如下
|
||
// rank := 2 * (lcjx * -1) * (1 + avgElevation) * (1 + lastElevation) * (1 + lastElevation)
|
||
// vir.Rank = rank
|
||
return nil
|
||
}
|
||
|
||
// 计算剪切力
|
||
func LacheJixian(cr *Core, srs *Series, period string) (float64, error) {
|
||
curSe, _ := cr.GetPixelSeries(srs.InstID, period)
|
||
return curSe.CandleSeries.List[len(srs.CandleSeries.List)-1].Y, nil
|
||
}
|
||
|
||
// type SegmentItem struct {
|
||
// InstID string
|
||
// Period string
|
||
// ReportTime int64
|
||
// lastUpdate int64
|
||
// Interval int
|
||
// Direct string // up, down
|
||
// VerticalElevation float64
|
||
// }
|