|
@@ -0,0 +1,732 @@
|
|
|
+package logic
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "github.com/shopspring/decimal"
|
|
|
+ "github.com/yidane/formula"
|
|
|
+ "hongze/hongze_edb_lib/models"
|
|
|
+ "hongze/hongze_edb_lib/models/future_good"
|
|
|
+ "hongze/hongze_edb_lib/utils"
|
|
|
+ "sort"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+// ChartInfoDateReq 图表的日期数据(日期相关)
|
|
|
+type ChartInfoDateReq struct {
|
|
|
+ Type int `description:"配置类型"`
|
|
|
+ Date string `description:"固定日期"`
|
|
|
+ Value int `description:"N天的值"`
|
|
|
+ Color string `description:"颜色"`
|
|
|
+ Name string `description:"别名"`
|
|
|
+}
|
|
|
+
|
|
|
+// SectionScatterReq 截面散点请求
|
|
|
+type SectionScatterReq struct {
|
|
|
+ XName string `description:"x轴名称"`
|
|
|
+ XNameEn string `description:"x轴名称(英文)"`
|
|
|
+ XUnitName string `description:"x轴单位名称"`
|
|
|
+ XUnitNameEn string `description:"x轴单位名称(英文)"`
|
|
|
+ YName string `description:"y轴名称"`
|
|
|
+ YNameEn string `description:"y轴名称(英文)"`
|
|
|
+ YUnitName string `description:"y轴单位名称"`
|
|
|
+ YUnitNameEn string `description:"y轴单位名称(英文)"`
|
|
|
+ XMinValue string `description:"X轴的最小值"`
|
|
|
+ XMaxValue string `description:"X轴的最大值"`
|
|
|
+ YMinValue string `description:"Y轴的最小值"`
|
|
|
+ YMaxValue string `description:"Y轴的最大值"`
|
|
|
+ //EdbList []SectionScatterEdbItemReq `description:"指标数据"`
|
|
|
+ SeriesList []SectionScatterSeriesItemReq `description:"系列数据"`
|
|
|
+}
|
|
|
+
|
|
|
+// SectionScatterSeriesItemReq 系列的请求
|
|
|
+type SectionScatterSeriesItemReq struct {
|
|
|
+ Name string `description:"系列名"`
|
|
|
+ NameEn string `description:"系列名(英文名)"`
|
|
|
+ Color string `description:"颜色"`
|
|
|
+ EdbInfoList []SectionScatterEdbItemReq
|
|
|
+ ShowTrendLine bool `description:"是否展示趋势线"`
|
|
|
+ ShowFitEquation bool `description:"是否展示方程式"`
|
|
|
+ ShowRSquare bool `description:"是否展示R平方"`
|
|
|
+}
|
|
|
+
|
|
|
+// SectionScatterEdbItemReq 截面散点请求的指标
|
|
|
+type SectionScatterEdbItemReq struct {
|
|
|
+ XEdbInfoId int `description:"X轴的指标ID"`
|
|
|
+ YEdbInfoId int `description:"Y轴的指标ID"`
|
|
|
+ Name string `description:"别名"`
|
|
|
+ NameEn string `description:"英文别名"`
|
|
|
+ XDateType int `description:"X轴的日期配置类型"`
|
|
|
+ XDate string `description:"X轴的日期固定日期"`
|
|
|
+ XDateValue int `description:"X轴的日期N天的值"`
|
|
|
+ YDateType int `description:"Y轴的日期配置类型"`
|
|
|
+ YDate string `description:"Y轴的日期固定日期"`
|
|
|
+ YDateValue int `description:"Y轴的日期N天的值"`
|
|
|
+ IsShow bool `description:"是否展示"`
|
|
|
+}
|
|
|
+
|
|
|
+// XData 商品价格曲线的的x轴数据
|
|
|
+type XData struct {
|
|
|
+ Name string `description:"别名"`
|
|
|
+ NameEn string `description:"英文别名"`
|
|
|
+}
|
|
|
+
|
|
|
+// YData 柱方图的y轴数据
|
|
|
+type YData struct {
|
|
|
+ Date string `description:"数据日期"`
|
|
|
+ ConfigDate time.Time `description:"配置的日期" json:"-"`
|
|
|
+ Color string `description:"数据颜色"`
|
|
|
+ Name string `description:"别名"`
|
|
|
+ NameEn string `description:"英文别名"`
|
|
|
+ Value []float64 `description:"每个指标的值"`
|
|
|
+ NoDataEdbList []int `description:"没有数据的指标列表"`
|
|
|
+ XEdbInfoIdList []int `description:"对应X轴的指标id列表"`
|
|
|
+ NameList []string `description:"每个值对应的名称"`
|
|
|
+ EnNameList []string `description:"每个值对应的英文名称"`
|
|
|
+ EdbValMap map[int]float64 `description:"指标与值的对应" json:"-"`
|
|
|
+ M []int `description:"对应开始日期的间隔值" json:"-"`
|
|
|
+}
|
|
|
+
|
|
|
+// BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
|
|
|
+type BarChartInfoEdbItemReq struct {
|
|
|
+ EdbInfoId int `description:"指标ID"`
|
|
|
+ Name string `description:"别名"`
|
|
|
+ NameEn string `description:"英文别名"`
|
|
|
+ Source int `description:"1:ETA图库;2:商品价格"`
|
|
|
+}
|
|
|
+
|
|
|
+// GetProfitChartEdbData 获取利润图表的指标数据
|
|
|
+func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag) (barConfigEdbInfoIdList []BarChartInfoEdbItemReq, xEdbIdValue []int, xDataList []XData, yDataList []YData, err error) {
|
|
|
+
|
|
|
+ if baseEdbInfo == nil {
|
|
|
+ err = errors.New("ETA指标未选取")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if len(zlFutureGoodEdbInfoList) <= 0 {
|
|
|
+ err = errors.New("商品指标未选取")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标签与期货商品指标的关联关系
|
|
|
+ tagEdbIdMap := make(map[string]int)
|
|
|
+ // 有效的期货商品指标
|
|
|
+ futureGoodEdbInfoIdMap := make(map[int]int)
|
|
|
+ {
|
|
|
+ tmpTagEdbIdMap := make(map[string]int)
|
|
|
+ for _, v := range edbInfoFromTagList {
|
|
|
+ tmpTagEdbIdMap[v.FromTag] = v.EdbInfoId
|
|
|
+ }
|
|
|
+ formulaMap := CheckFormula(formulaStr)
|
|
|
+ for _, tag := range formulaMap {
|
|
|
+ tagEdbIdMap[tag] = tmpTagEdbIdMap[tag]
|
|
|
+ futureGoodEdbInfoIdMap[tmpTagEdbIdMap[tag]] = tmpTagEdbIdMap[tag]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 普通的指标数据
|
|
|
+ baseDataList := make([]*models.EdbDataList, 0)
|
|
|
+ baseDataList, err = models.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.EdbInfoId, "", "")
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ latestDate := zlFutureGoodEdbInfoList[0].EndDate
|
|
|
+ latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
|
|
|
+
|
|
|
+ earliestDateTime := latestDateTime // 数据的最早日期,目的是为了找出最早的合约
|
|
|
+ for _, barChartInfoDate := range chartInfoDateList {
|
|
|
+ var findDateTime time.Time
|
|
|
+ switch barChartInfoDate.Type {
|
|
|
+ case 1: //最新值
|
|
|
+ findDateTime = latestDateTime
|
|
|
+ case 2: //近期几天
|
|
|
+ findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
|
|
|
+ case 3: // 固定日期
|
|
|
+ //寻找固定日期的数据
|
|
|
+ tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ findDateTime = tmpFindDateTime
|
|
|
+ default:
|
|
|
+ err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if findDateTime.IsZero() {
|
|
|
+ err = errors.New("错误的日期")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if findDateTime.Before(earliestDateTime) {
|
|
|
+ earliestDateTime = findDateTime
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ monthNum := (latestDateTime.Year()-earliestDateTime.Year())*12 + int(latestDateTime.Month()-earliestDateTime.Month())
|
|
|
+
|
|
|
+ // 存储主力合约下的所有月份合约
|
|
|
+ futureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo)
|
|
|
+ futureGoodDataListMap := make(map[int][]*models.EdbDataList, 0)
|
|
|
+
|
|
|
+ isAllChina := true // 是否都是国内的期货合约
|
|
|
+ for _, v := range zlFutureGoodEdbInfoList {
|
|
|
+ if v.RegionType != "国内" {
|
|
|
+ isAllChina = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ nMap := make(map[int]int)
|
|
|
+ var maxN int // 最大N值
|
|
|
+ for _, v := range zlFutureGoodEdbInfoList {
|
|
|
+ // 如果不是有效的商品期货指标,那么就过滤掉,不做数据查询处理,避免没必要的请求
|
|
|
+ if _, ok := futureGoodEdbInfoIdMap[v.FutureGoodEdbInfoId]; !ok {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 获取期货指标以及期货数据
|
|
|
+ tmpFutureGoodEdbInfoList, tmpErr := future_good.GetChildFutureGoodEdbInfoListByParentId(v.FutureGoodEdbInfoId)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if maxN < tmpMaxN {
|
|
|
+ maxN = tmpMaxN
|
|
|
+ }
|
|
|
+ futureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap
|
|
|
+
|
|
|
+ // 获取数据
|
|
|
+ for n, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
|
|
|
+ nMap[n] = n
|
|
|
+
|
|
|
+ dataList := make([]*models.EdbDataList, 0)
|
|
|
+
|
|
|
+ tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
|
|
|
+ if tmpErr != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, tmpData := range tmpDataList {
|
|
|
+ dataList = append(dataList, &models.EdbDataList{
|
|
|
+ EdbDataId: tmpData.FutureGoodEdbDataId,
|
|
|
+ EdbInfoId: tmpData.FutureGoodEdbInfoId,
|
|
|
+ DataTime: tmpData.DataTime.Format(utils.FormatDate),
|
|
|
+ DataTimestamp: tmpData.DataTimestamp,
|
|
|
+ Value: tmpData.Close,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找出所有的N值,并进行正序排列
|
|
|
+ nList := make([]int, 0)
|
|
|
+ for _, n := range nMap {
|
|
|
+ nList = append(nList, n)
|
|
|
+ }
|
|
|
+ sort.Slice(nList, func(i, j int) bool {
|
|
|
+ return nList[i] < nList[j]
|
|
|
+ })
|
|
|
+
|
|
|
+ _, yDataList, err = ProfitChartChartData(baseDataList, futureGoodEdbInfoMap, futureGoodDataListMap, chartInfoDateList, latestDate, formulaStr, tagEdbIdMap, earliestDateTime, nList)
|
|
|
+
|
|
|
+ tmpXDataList, newYDataList, err := handleProfitResultData(baseEdbInfo, yDataList, earliestDateTime)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ xDataList = []XData{
|
|
|
+ {
|
|
|
+ Name: "现货利润",
|
|
|
+ NameEn: "Spot Price",
|
|
|
+ },
|
|
|
+ }
|
|
|
+ xDataList = append(xDataList, tmpXDataList...)
|
|
|
+ yDataList = newYDataList
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// ProfitChartChartData 获取数据
|
|
|
+func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, formulaStr string, tagEdbIdMap map[string]int, earliestDateTime time.Time, nList []int) (edbIdList []int, yDataList []YData, err error) {
|
|
|
+ // 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
|
|
|
+ //earliestDateTime time.Time
|
|
|
+ // ETA指标数据
|
|
|
+ baseEdbDateData := make(map[string]float64)
|
|
|
+ for _, edbData := range baseDataList {
|
|
|
+ baseEdbDateData[edbData.DataTime] = edbData.Value
|
|
|
+ }
|
|
|
+
|
|
|
+ // 商品指标数据
|
|
|
+ edbDataMap := make(map[int]map[string]float64)
|
|
|
+ for edbInfoId, edbDataList := range futureGoodEdbDataListMap {
|
|
|
+ edbDateData := make(map[string]float64)
|
|
|
+ for _, edbData := range edbDataList {
|
|
|
+ edbDateData[edbData.DataTime] = edbData.Value
|
|
|
+ }
|
|
|
+ edbDataMap[edbInfoId] = edbDateData
|
|
|
+ }
|
|
|
+
|
|
|
+ latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
|
|
|
+
|
|
|
+ yDataList = make([]YData, 0) //y轴的数据列表
|
|
|
+
|
|
|
+ // 将计算公式中的字母转大写
|
|
|
+ formulaStr = strings.ToUpper(formulaStr)
|
|
|
+
|
|
|
+ for _, barChartInfoDate := range chartInfoDateList {
|
|
|
+ yDataMap := make(map[int]float64)
|
|
|
+ var maxDate time.Time
|
|
|
+
|
|
|
+ var findDateTime time.Time
|
|
|
+ switch barChartInfoDate.Type {
|
|
|
+ case 1: //最新值
|
|
|
+ findDateTime = latestDateTime
|
|
|
+ case 2: //近期几天
|
|
|
+ findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
|
|
|
+ case 3: // 固定日期
|
|
|
+ //寻找固定日期的数据
|
|
|
+ tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ findDateTime = tmpFindDateTime
|
|
|
+ default:
|
|
|
+ err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if findDateTime.IsZero() {
|
|
|
+ err = errors.New("错误的日期")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ findDataList := make([]float64, 0) // 当前日期的数据值
|
|
|
+ noDataIdList := make([]int, 0) // 没有数据的指标id
|
|
|
+ xEdbInfoIdList := make([]int, 0) // 当前数据的指标id列表
|
|
|
+
|
|
|
+ // 现货指标
|
|
|
+ realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataList, baseEdbDateData)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ findDataList = append(findDataList, findDataValue)
|
|
|
+ yDataMap[0] = findDataValue
|
|
|
+ if isFind {
|
|
|
+ maxDate = realDateTime
|
|
|
+ }
|
|
|
+
|
|
|
+ xEdbInfoIdList = append(xEdbInfoIdList, 0)
|
|
|
+
|
|
|
+ mList := make([]int, 0) // 间隔月份
|
|
|
+
|
|
|
+ // 最小开始的n值
|
|
|
+ minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
|
|
|
+ for _, n := range nList {
|
|
|
+ // 如果当前的n值小于最小开始的n值,那么就不处理
|
|
|
+ if n < minN {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ zlAndChildEdbId := make(map[int]int)
|
|
|
+ childFutureGoodEdbInfoIdList := make([]int, 0)
|
|
|
+ for futureGoodEdbInfoId, futureGoodEdbInfo := range futureGoodEdbInfoMap {
|
|
|
+ if childFutureGoodEdbInfo, ok := futureGoodEdbInfo[n]; ok {
|
|
|
+ childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
|
|
|
+ zlAndChildEdbId[futureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合约不全,不参与计算
|
|
|
+ //if len(childFutureGoodEdbInfoIdList) != lenZlFutureGoodEdbInfo {
|
|
|
+ // continue
|
|
|
+ //}
|
|
|
+
|
|
|
+ calculateMap := make(map[int]float64)
|
|
|
+ for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList {
|
|
|
+ tmpRealDateTime := findDateTime
|
|
|
+ tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][findDateTime.Format(utils.FormatDate)]
|
|
|
+
|
|
|
+ if tmpIsFind {
|
|
|
+ calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue
|
|
|
+ if maxDate.IsZero() || maxDate.Before(tmpRealDateTime) {
|
|
|
+ maxDate = tmpRealDateTime
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 合约的数据不全,不参与计算
|
|
|
+ //if len(calculateMap) != lenZlFutureGoodEdbInfo {
|
|
|
+ // continue
|
|
|
+ //}
|
|
|
+
|
|
|
+ newTagEdbIdMap := make(map[string]int)
|
|
|
+ for tag, zlEdbId := range tagEdbIdMap {
|
|
|
+ newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId]
|
|
|
+ }
|
|
|
+ //, formulaStr string, tagEdbIdMap map[string]int
|
|
|
+ formulaFormStr := ReplaceFormula(newTagEdbIdMap, calculateMap, formulaStr)
|
|
|
+ //计算公式异常,那么就移除该指标
|
|
|
+ if formulaFormStr == `` {
|
|
|
+ //removeDateList = append(removeDateList, sk)
|
|
|
+ //fmt.Println("异常了")
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ expression := formula.NewExpression(formulaFormStr)
|
|
|
+ calResult, evaluateErr := expression.Evaluate()
|
|
|
+ if evaluateErr != nil {
|
|
|
+ // 分母为0的报错
|
|
|
+ if strings.Contains(evaluateErr.Error(), "divide by zero") {
|
|
|
+ //removeDateList = append(removeDateList, sk)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ err = errors.New("计算失败:Err:" + evaluateErr.Error() + ";formulaStr:" + formulaFormStr)
|
|
|
+ fmt.Println(err)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果计算结果是NAN,那么就退出当前循环
|
|
|
+ if calResult.IsNan() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ calVal, tmpErr := calResult.Float64()
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
|
|
|
+ fmt.Println(err)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ //yDataMap[n] = calVal
|
|
|
+ //xEdbInfoIdList = append(xEdbInfoIdList, n)
|
|
|
+ yDataMap[n+minN] = calVal
|
|
|
+ xEdbInfoIdList = append(xEdbInfoIdList, n+minN)
|
|
|
+ findDataList = append(findDataList, calVal)
|
|
|
+ }
|
|
|
+ yName := barChartInfoDate.Name
|
|
|
+ yNameEn := barChartInfoDate.Name
|
|
|
+ if yName == `` {
|
|
|
+ if barChartInfoDate.Type == 2 {
|
|
|
+ yName = strconv.Itoa(barChartInfoDate.Value) + "天前"
|
|
|
+ if barChartInfoDate.Value == 1 {
|
|
|
+ yNameEn = strconv.Itoa(barChartInfoDate.Value) + "day ago"
|
|
|
+ } else {
|
|
|
+ yNameEn = strconv.Itoa(barChartInfoDate.Value) + " days ago"
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ yName = maxDate.Format(utils.FormatDate)
|
|
|
+ yNameEn = maxDate.Format(utils.FormatDate)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ yDate := "0000-00-00"
|
|
|
+ if !maxDate.IsZero() {
|
|
|
+ yDate = maxDate.Format(utils.FormatDate)
|
|
|
+ }
|
|
|
+
|
|
|
+ yDataList = append(yDataList, YData{
|
|
|
+ Date: yDate,
|
|
|
+ ConfigDate: findDateTime,
|
|
|
+ Value: findDataList,
|
|
|
+ NoDataEdbList: noDataIdList,
|
|
|
+ XEdbInfoIdList: xEdbInfoIdList,
|
|
|
+ Color: barChartInfoDate.Color,
|
|
|
+ Name: yName,
|
|
|
+ NameEn: yNameEn,
|
|
|
+ EdbValMap: yDataMap,
|
|
|
+ M: mList,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// getFutureGoodEdbInfoList 获取适用的指标列表
|
|
|
+func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNum int) (futureGoodEdbInfoMap map[int]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
|
|
|
+ maxM := 32 //最大32期合约
|
|
|
+ futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
|
|
|
+ futureGoodEdbInfoMap = make(map[int]*future_good.FutureGoodEdbInfo)
|
|
|
+ if zlFutureGoodEdbInfo.RegionType == "国内" {
|
|
|
+ startMonth := int(earliestDateTime.Month())
|
|
|
+ if startMonth == 1 {
|
|
|
+ futureGoodEdbInfoList = tmpFutureGoodEdbInfoList
|
|
|
+ } else {
|
|
|
+ // 因为是下标,所以对应想下标是需要-1的
|
|
|
+ index := startMonth - 1
|
|
|
+ futureGoodEdbInfoList = tmpFutureGoodEdbInfoList[index:]
|
|
|
+ futureGoodEdbInfoList = append(futureGoodEdbInfoList, tmpFutureGoodEdbInfoList[:index]...)
|
|
|
+ }
|
|
|
+
|
|
|
+ lenFutureGoodEdbInfoList := len(futureGoodEdbInfoList)
|
|
|
+ //futureGoodEdbInfoList
|
|
|
+ if isAllChina {
|
|
|
+ // 如果全是国内指标,那么只需要拼上多出的几期合约即可
|
|
|
+ maxM = lenFutureGoodEdbInfoList + monthNum
|
|
|
+ }
|
|
|
+
|
|
|
+ for i := 1; i < maxM; i++ {
|
|
|
+ k := i % lenFutureGoodEdbInfoList
|
|
|
+ futureGoodEdbInfoMap[i] = futureGoodEdbInfoList[k]
|
|
|
+ if i > newMaxN {
|
|
|
+ newMaxN = i
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, v := range tmpFutureGoodEdbInfoList {
|
|
|
+ //海外的连续日期,目前
|
|
|
+ if v.FutureGoodEdbType == 2 {
|
|
|
+ if v.Month <= maxM {
|
|
|
+ futureGoodEdbInfoMap[v.Month] = v
|
|
|
+ if v.Month > newMaxN {
|
|
|
+ newMaxN = v.Month
|
|
|
+ }
|
|
|
+ }
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if v.Year < earliestDateTime.Year() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 小于等于当前年,那么就肯定是ok的
|
|
|
+ if v.Year == earliestDateTime.Year() && v.Month <= int(earliestDateTime.Month()) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ subYear := v.Year - earliestDateTime.Year()
|
|
|
+ subMonth := v.Month - int(earliestDateTime.Month())
|
|
|
+ // 如果(当前年-最新日期的年份) * 12个月 + (当前月-最新日期的月份) 小于总月份
|
|
|
+ tmpN := subYear*12 + subMonth
|
|
|
+ if tmpN < maxM {
|
|
|
+ futureGoodEdbInfoMap[tmpN] = v
|
|
|
+ if tmpN > newMaxN {
|
|
|
+ newMaxN = tmpN
|
|
|
+ }
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// handleProfitResultData 处理成最终的结果数据
|
|
|
+func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earliestDateTime time.Time) (xDataList []XData, newYDataList []YData, err error) {
|
|
|
+ xDataList = make([]XData, 0)
|
|
|
+ newYDataList = yDataList
|
|
|
+
|
|
|
+ nMap := make(map[int]int)
|
|
|
+
|
|
|
+ for _, v := range yDataList {
|
|
|
+ for _, n := range v.XEdbInfoIdList {
|
|
|
+ nMap[n] = n
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找出所有的N值,并进行正序排列
|
|
|
+ nList := make([]int, 0)
|
|
|
+ for _, n := range nMap {
|
|
|
+ nList = append(nList, n)
|
|
|
+ }
|
|
|
+ sort.Slice(nList, func(i, j int) bool {
|
|
|
+ return nList[i] < nList[j]
|
|
|
+ })
|
|
|
+
|
|
|
+ for _, n := range nList {
|
|
|
+ if n == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ xDataList = append(xDataList, XData{
|
|
|
+ Name: fmt.Sprint("M+", n),
|
|
|
+ NameEn: fmt.Sprint("M+", n),
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ for yIndex, yData := range yDataList {
|
|
|
+ newYDataList[yIndex].XEdbInfoIdList = []int{}
|
|
|
+ newYDataList[yIndex].Value = []float64{}
|
|
|
+ tmpNList := nList
|
|
|
+
|
|
|
+ xEdbInfoIdList := yData.XEdbInfoIdList
|
|
|
+ valIndex := 0
|
|
|
+ needNum := 0
|
|
|
+ for _, n := range tmpNList {
|
|
|
+ if len(xEdbInfoIdList) > 0 {
|
|
|
+ currN := xEdbInfoIdList[0]
|
|
|
+ // 当前距离最早的日期相差的N数
|
|
|
+ if n == currN {
|
|
|
+ if needNum > 0 {
|
|
|
+ currVal := yData.Value[valIndex]
|
|
|
+ preVal := yData.Value[valIndex-1]
|
|
|
+ hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(needNum + 1)))
|
|
|
+
|
|
|
+ for tmpNum := 0; tmpNum < needNum; tmpNum++ {
|
|
|
+ newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, 0)
|
|
|
+
|
|
|
+ // 赋值平均值
|
|
|
+ tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
|
|
|
+ newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
|
|
|
+ newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
|
|
|
+ valIndex++
|
|
|
+ needNum = 0
|
|
|
+ if len(xEdbInfoIdList) > 0 {
|
|
|
+ xEdbInfoIdList = xEdbInfoIdList[1:]
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ needNum++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ maxI := 0
|
|
|
+ for _, yData := range newYDataList {
|
|
|
+ lenEdb := len(yData.XEdbInfoIdList)
|
|
|
+ for i := 0; i < lenEdb; i++ {
|
|
|
+ if yData.XEdbInfoIdList[i] != 0 && !utils.InArrayByInt(yData.NoDataEdbList, yData.XEdbInfoIdList[i]) {
|
|
|
+ if maxI < i {
|
|
|
+ maxI = i
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
|
|
|
+ xDataList = xDataList[0:maxI]
|
|
|
+ for yIndex, yData := range newYDataList {
|
|
|
+ if len(yData.XEdbInfoIdList) > maxI+1 {
|
|
|
+ newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
|
|
|
+ }
|
|
|
+ if len(yData.Value) > maxI+1 {
|
|
|
+ newYDataList[yIndex].Value = yData.Value[0 : maxI+1]
|
|
|
+ }
|
|
|
+
|
|
|
+ nameList := make([]string, 0)
|
|
|
+ enNameList := make([]string, 0)
|
|
|
+ for _, n := range newYDataList[yIndex].XEdbInfoIdList {
|
|
|
+ if n == 1 { // 现货价不处理
|
|
|
+ nameList = append(nameList, baseEdbInfo.EdbName)
|
|
|
+ enNameList = append(enNameList, baseEdbInfo.EdbNameEn)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if n <= 0 {
|
|
|
+ nameList = append(nameList, `无合约`)
|
|
|
+ enNameList = append(enNameList, `no contract`)
|
|
|
+ } else {
|
|
|
+ var date string
|
|
|
+
|
|
|
+ currDate, _ := time.ParseInLocation(utils.FormatDate, yData.Date, time.Local)
|
|
|
+ subN := (currDate.Year()-earliestDateTime.Year())*12 + int(currDate.Month()-earliestDateTime.Month())
|
|
|
+ currDateTime := earliestDateTime.AddDate(0, n-subN-1, 0)
|
|
|
+ month := int(currDateTime.Month())
|
|
|
+ date = fmt.Sprintf("%d%d", currDateTime.Year(), month)
|
|
|
+ if month < 10 {
|
|
|
+ date = fmt.Sprintf("%d0%d", currDateTime.Year(), month)
|
|
|
+ }
|
|
|
+ nameList = append(nameList, date)
|
|
|
+ enNameList = append(enNameList, date)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newYDataList[yIndex].NameList = nameList
|
|
|
+ newYDataList[yIndex].EnNameList = enNameList
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func CheckFormula(formula string) map[string]string {
|
|
|
+ mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN"}
|
|
|
+
|
|
|
+ str := strings.ToUpper(formula)
|
|
|
+ for _, v := range mathFormula {
|
|
|
+ str = strings.Replace(str, v, "", -1)
|
|
|
+ }
|
|
|
+ str = strings.Replace(str, "(", "", -1)
|
|
|
+ str = strings.Replace(str, ")", "", -1)
|
|
|
+
|
|
|
+ byteMap := make(map[string]string)
|
|
|
+ for i := 0; i < len(str); i++ {
|
|
|
+ byteInt := str[i]
|
|
|
+ if byteInt >= 65 && byteInt <= 90 {
|
|
|
+ byteStr := string(byteInt)
|
|
|
+ if _, ok := byteMap[byteStr]; !ok {
|
|
|
+ byteMap[byteStr] = byteStr
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return byteMap
|
|
|
+}
|
|
|
+
|
|
|
+func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaStr string) string {
|
|
|
+ funMap := GetFormulaMap()
|
|
|
+ for k, v := range funMap {
|
|
|
+ formulaStr = strings.Replace(formulaStr, k, v, -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ replaceCount := 0
|
|
|
+ for tag, edbInfoId := range tagEdbIdMap {
|
|
|
+ if val, valOk := valArr[edbInfoId]; valOk { //值存在
|
|
|
+ dvStr := fmt.Sprintf("%v", val)
|
|
|
+ formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
|
|
|
+ replaceCount++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for k, v := range funMap {
|
|
|
+ formulaStr = strings.Replace(formulaStr, v, k, -1)
|
|
|
+ }
|
|
|
+ if replaceCount == len(tagEdbIdMap) {
|
|
|
+ return formulaStr
|
|
|
+ } else {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func GetFormulaMap() map[string]string {
|
|
|
+ funMap := make(map[string]string)
|
|
|
+ funMap["MAX"] = "[@@]"
|
|
|
+ funMap["MIN"] = "[@!]"
|
|
|
+ funMap["ABS"] = "[@#]"
|
|
|
+ funMap["CEIL"] = "[@$]"
|
|
|
+ funMap["COS"] = "[@%]"
|
|
|
+ funMap["FLOOR"] = "[@^]"
|
|
|
+ funMap["MOD"] = "[@&]"
|
|
|
+ funMap["POW"] = "[@*]"
|
|
|
+ funMap["ROUND"] = "[@(]"
|
|
|
+ return funMap
|
|
|
+}
|
|
|
+
|
|
|
+// GetNeedDateData 获取合约内需要的日期数据
|
|
|
+func GetNeedDateData(needDateTime time.Time, dataList []*models.EdbDataList, edbDataMap map[string]float64) (findDateTime time.Time, findDataValue float64, isFind bool, err error) {
|
|
|
+ //dataList := edbDataListMap[edbInfoId] //指标的所有数据值
|
|
|
+ if len(dataList) <= 0 {
|
|
|
+ // 没有数据的指标id
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ //最早的日期
|
|
|
+ minDateTime, err := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
|
|
|
+ tmpDate := tmpDateTime.Format(utils.FormatDate)
|
|
|
+ if tmpValue, ok := edbDataMap[tmpDate]; ok { //如果能找到数据,那么就返回
|
|
|
+ // 数据为0,也直接返回,做无值处理
|
|
|
+ if tmpValue == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
|
|
|
+ findDataValue = tmpValue
|
|
|
+ isFind = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|