Roc 1 year ago
parent
commit
e647d5186a

+ 732 - 0
logic/profit_chart_info.go

@@ -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
+}

+ 37 - 2
models/edb_data_base.go

@@ -1,13 +1,15 @@
 package models
 
 import (
+	"errors"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"hongze/hongze_edb_lib/utils"
+	"strconv"
 	"time"
 )
 
-//指标检索数据
+// 指标检索数据
 type EdbDataItem struct {
 	EdbCode   string `description:"指标编码"`
 	StartDate string `description:"起始日期"`
@@ -141,7 +143,7 @@ func GetAllEdbDataListByTo(to orm.TxOrmer, edbInfoId, source int) (existDataList
 	return
 }
 
-//新版本
+// 新版本
 type EdbDataV1 struct {
 	EdbDataId     int `orm:"column(edb_data_id);pk"`
 	EdbInfoId     int
@@ -153,3 +155,36 @@ type EdbDataV1 struct {
 	ModifyTime    time.Time
 	DataTimestamp int64
 }
+
+type EdbDataList struct {
+	EdbDataId     int     `description:" 指标数据ID"`
+	EdbInfoId     int     `description:"指标ID"`
+	DataTime      string  //`json:"-" description:"数据日期"`
+	DataTimestamp int64   `description:"数据日期"`
+	Value         float64 `description:"数据值"`
+}
+
+func GetEdbDataList(source, endInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	tableName := GetEdbDataTableName(source)
+	if tableName == "" {
+		err = errors.New("无效的渠道:" + strconv.Itoa(source))
+		list = make([]*EdbDataList, 0)
+		return list, err
+	}
+	var pars []interface{}
+	sql := `SELECT edb_data_id,edb_info_id,data_time,value,data_timestamp FROM %s WHERE edb_info_id=? `
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	sql = fmt.Sprintf(sql, tableName)
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, endInfoId, pars).QueryRows(&list)
+	return
+}

+ 18 - 0
models/future_good/future_good_edb_data.go

@@ -271,3 +271,21 @@ func GetAddSql(futureGoodEdbInfoId, futureGoodEdbCode, dataTime, tradeCode, open
 	addSql = fmt.Sprintf("(%s,'%s','%s','%s',%s,%s,%s,%s,%s,%s,%s,%s,'%s','%s',%s),", futureGoodEdbInfoId, futureGoodEdbCode, dataTime, tradeCode, open, high, low, close, volume, amt, oi, settle, nowStr, nowStr, timestampStr)
 	return
 }
+
+func GetFutureGoodEdbDataListByDate(futureGoodEdbInfoId int, startDate, endDate string) (list []*FutureGoodEdbData, err error) {
+	var pars []interface{}
+	sql := `SELECT * FROM future_good_edb_data WHERE 1=1 AND future_good_edb_info_id = ? `
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, futureGoodEdbInfoId, pars).QueryRows(&list)
+	return
+}

+ 13 - 0
models/future_good/future_good_edb_info.go

@@ -11,8 +11,13 @@ type FutureGoodEdbInfo struct {
 	FutureGoodEdbInfoId int       `orm:"column(future_good_edb_info_id);pk"`
 	FutureGoodEdbCode   string    `description:"期货指标code"`
 	FutureGoodEdbName   string    `description:"期货指标名称"`
+	FutureGoodEdbNameEn string    `description:"期货指标英文名称"`
 	ParentId            int       `description:"上级期货id"`
+	RegionType          string    `description:"交易所来源,海外还是国内"`
 	Exchange            string    `description:"所属交易所"`
+	FutureGoodEdbType   int       `description:"指标类型,1:年月是固定的合约;2:只有M+N期的合约,未固定年月"`
+	DateSourceId        int       `description:"画图时,日期来源的指标id"`
+	Year                int       `description:"所属年份"`
 	Month               int       `description:"所属月份"`
 	StartDate           string    `description:"起始日期"`
 	EndDate             string    `description:"终止日期"`
@@ -64,6 +69,14 @@ func AddFutureGoodEdbInfo(item *FutureGoodEdbInfo) (err error) {
 	return
 }
 
+// GetChildFutureGoodEdbInfoListByParentId 根据父级ID获取下面所有的指标数据列表
+func GetChildFutureGoodEdbInfoListByParentId(parentId int) (list []*FutureGoodEdbInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM future_good_edb_info WHERE parent_id = ? ORDER BY future_good_edb_info_id ASC `
+	_, err = o.Raw(sql, parentId).QueryRows(&list)
+	return
+}
+
 // Update 更新指标基础信息
 func (FutureGoodEdbInfo *FutureGoodEdbInfo) Update(cols []string) (err error) {
 	o := orm.NewOrm()