package logic import ( "encoding/json" "errors" "eta/eta_index_lib/models" "eta/eta_index_lib/models/future_good" "eta/eta_index_lib/utils" "fmt" "github.com/dengsgo/math-engine/engine" "github.com/shopspring/decimal" "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:商品价格"` } // ChartInfoReq 图表预览请求数据 type ChartInfoReq struct { FutureGoodEdbInfoIdList []models.EdbInfoFromTag `description:"指标信息"` CalculateFormula string `description:"计算公式"` BaseEdbInfoId int `description:"基础的指标id"` DateList []ChartInfoDateReq `description:"日期配置"` ProfitNameEn string `description:"利润英文名称"` } // RefreshByChartId 根据图表id刷新图表 func RefreshByChartId(chartInfoId int) (err error, errMsg string) { // 查找图表 chartInfo, err := models.GetChartInfoById(chartInfoId) if err != nil { if err.Error() == utils.ErrNoRow() { err = nil } return } if chartInfo.Source != utils.CHART_SOURCE_FUTURE_GOOD_PROFIT { return } var extraConf ChartInfoReq err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConf) if err != nil { errMsg = "商品利润曲线图配置异常" return } // 查找商品利润图表的扩展信息 chartInfoFutureGoodProfit := new(future_good.ChartInfoFutureGoodProfit) if err = chartInfoFutureGoodProfit.GetItemById(chartInfoId); err != nil { errMsg = "获取失败" if err.Error() == utils.ErrNoRow() { err = nil } return } baseEdbInfo, err := models.GetEdbInfoById(extraConf.BaseEdbInfoId) if err != nil { errMsg = "获取失败" return } // 商品数据库指标 futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo) zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0) for _, v := range extraConf.FutureGoodEdbInfoIdList { if _, ok := futureGoodEdbInfoMap[v.EdbInfoId]; ok { continue } zlFutureGoodEdbInfo, tmpErr := future_good.GetFutureGoodEdbInfo(v.EdbInfoId) if tmpErr != nil { err = tmpErr errMsg = "获取失败" return } futureGoodEdbInfoMap[v.EdbInfoId] = zlFutureGoodEdbInfo zlFutureGoodEdbInfoList = append(zlFutureGoodEdbInfoList, zlFutureGoodEdbInfo) } xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList) xDataListByte, err := json.Marshal(xDataList) if err != nil { errMsg = "保存失败" err = errors.New("X轴数据转换失败,ERR:" + err.Error()) return } yDataListByte, err := json.Marshal(yDataList) if err != nil { errMsg = "保存失败" err = errors.New("Y轴数据转换失败,ERR:" + err.Error()) return } extraUpdateCol := make([]string, 0) chartInfoFutureGoodProfit.XValue = string(xDataListByte) chartInfoFutureGoodProfit.YValue = string(yDataListByte) chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润" chartInfoFutureGoodProfit.ModifyTime = time.Now() extraUpdateCol = []string{"XValue", "YValue", "ProfitName", "ModifyTime"} err = chartInfoFutureGoodProfit.Update(extraUpdateCol) return } // GetProfitChartEdbData 获取利润图表的指标数据 func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag) (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.SubSource, 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()) // 存储主力合约下的所有月份合约 futureGoodEdbInfoDateMap := make(map[int]map[string]*future_good.FutureGoodEdbInfo) futureGoodDataListMap := make(map[int][]*models.EdbDataList, 0) // 特殊的商品期货合约(只有M+N的合约,没有固定日期的合约) specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0) isAllChina := true // 是否都是国内的期货合约 for _, v := range zlFutureGoodEdbInfoList { if v.RegionType != "国内" { isAllChina = false break } } nMap := make(map[string]string) 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 } futureGoodEdbInfoDateMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap if v.FutureGoodEdbType == 2 { specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = make(map[int]*future_good.FutureGoodEdbInfo) } // 获取数据 for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap { nMap[date] = date 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 if childFutureGoodEdbInfo.FutureGoodEdbType == 2 { specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo } } } // 找出所有的N值,并进行正序排列 dateList := make([]string, 0) for _, n := range nMap { dateList = append(dateList, n) } sort.Slice(dateList, func(i, j int) bool { return dateList[i] < dateList[j] }) _, yDataList, err = ProfitChartChartData(baseDataList, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN) if err != nil { return } 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[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN 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, edbDataMap) 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 _, date := range dateList { currDate, _ := time.ParseInLocation(utils.FormatYearMonthDate, date, time.Local) // 如果当前的n值小于最小开始的n值,那么就不处理 //if n < minN { // continue //} //findDateTime // 获取当前日期相对开始日期的期数 //tmpN := (currDate.Year()-findDateTime.Year())*12 + int(currDate.Month()-findDateTime.Month()) // 用实际日期的月份作为基准,往前推12个月(2024-5-13 16:26:43修改) tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month()) if tmpN <= 0 { continue } // 如果期数大于最大期数,那么就退出当前匹配 if tmpN >= maxN { break } zlAndChildEdbId := make(map[int]int) childFutureGoodEdbInfoIdList := make([]int, 0) for zlFutureGoodEdbInfoId, futureGoodEdbInfoList := range futureGoodEdbInfoMap { // 判断是否特殊合约 if childFutureGoodEdbInfoIdMap, ok := specialFutureGoodEdbInfoMap[zlFutureGoodEdbInfoId]; ok { if childFutureGoodEdbInfo, ok2 := childFutureGoodEdbInfoIdMap[tmpN]; ok2 { childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId) zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId } } else { if childFutureGoodEdbInfo, ok2 := futureGoodEdbInfoList[date]; ok2 { childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId) zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId } } } // 合约不全,不参与计算 //if len(childFutureGoodEdbInfoIdList) != lenZlFutureGoodEdbInfo { // continue //} calculateMap := make(map[int]float64) for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList { tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][realDateTime.Format(utils.FormatDate)] if tmpIsFind && tmpFindDataValue != 0 { calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue } } // 合约的数据不全,不参与计算 //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 //} calVal, err := engine.ParseAndExec(formulaFormStr) //calVal, err := calResult.Float64() if err != nil { // 分母为0的报错,忽略该循环 if utils.IsDivideZero(err) { //removeDateList = append(removeDateList, sk) continue } err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr) fmt.Println(err) return nil, nil, err } //nanCheck := fmt.Sprintf("%0.f", calVal) //yDataMap[n] = calVal //xEdbInfoIdList = append(xEdbInfoIdList, n) nanCheck := fmt.Sprintf("%0.f", calVal) // 分母为0.0的报错 if nanCheck == "NaN" || nanCheck == "+Inf" || nanCheck == "-Inf" { continue } calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64() yDataMap[tmpN] = calVal xEdbInfoIdList = append(xEdbInfoIdList, tmpN) 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: realDateTime, 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) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) { maxN := 36 //最大36期合约 futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0) futureGoodEdbInfoDateMap = make(map[string]*future_good.FutureGoodEdbInfo) earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local) 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 { // // 如果全是国内指标,那么只需要拼上多出的几期合约即可 // maxN = lenFutureGoodEdbInfoList + monthNum //} // 如果全是国内指标,那么只需要拼上多出的几期合约即可 maxN = lenFutureGoodEdbInfoList + monthNum for i := 1; i < maxN; i++ { k := i % lenFutureGoodEdbInfoList futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k] if i > newMaxN { newMaxN = i } } //需求池604,只要是国内合约,最大必须是12期 newMaxN = 12 return } for _, v := range tmpFutureGoodEdbInfoList { //海外的连续日期,目前 if v.FutureGoodEdbType == 2 { if v.Month <= newMaxN { futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, v.Month, 0).Format(utils.FormatYearMonthDate)] = 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 < newMaxN { tmpDateTime := time.Date(v.Year, time.Month(v.Month), 0, 0, 0, 0, 0, time.Local) futureGoodEdbInfoDateMap[tmpDateTime.Format(utils.FormatYearMonthDate)] = 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)))).Round(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] } currDate, _ := time.ParseInLocation(utils.FormatDate, yData.Date, time.Local) 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 currDateTime := currDate.AddDate(0, n-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, allEdbDataMap map[int]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 } // 该日期存在数据的期货指标的最小数量,目前是现货和期货各1个,总共2个 maxCount := 1 for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) { tmpDate := tmpDateTime.Format(utils.FormatDate) tmpValue, ok := edbDataMap[tmpDate] if !ok { continue } // 该日期存在数据的指标数量 count := 0 for _, currEdbDataMap := range allEdbDataMap { _, tmpIsFind := currEdbDataMap[tmpDate] if tmpIsFind { count++ if count >= maxCount { continue } } } // 该日期存在数据的期货指标数量小于2个,那么要继续往前找 if count < maxCount { continue } //如果能找到数据,那么就返回 // 数据为0,也直接返回,做无值处理 if tmpValue == 0 { return } findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local) findDataValue = tmpValue isFind = true return } return }