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:"利润英文名称"` EdbInfoIdList []int `description:"现货指标ID列表"` XDataList []XData `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 } if len(extraConf.EdbInfoIdList) == 0 { extraConf.EdbInfoIdList = append(extraConf.EdbInfoIdList, extraConf.BaseEdbInfoId) } baseEdbInfo := new(models.EdbInfo) // ETA指标 edbInfoListTmp, err := models.GetEdbInfoByIdList(extraConf.EdbInfoIdList) if err != nil { errMsg = "获取失败" return } //按照请求顺序排序 edbInfoMap := make(map[int]*models.EdbInfo) for _, v := range edbInfoListTmp { edbInfoMap[v.EdbInfoId] = v } edbInfoList := make([]*models.EdbInfo, 0) for _, v := range extraConf.EdbInfoIdList { edbInfoList = append(edbInfoList, edbInfoMap[v]) } edbInfoListMap := make(map[int]*models.EdbInfo) for k, v := range edbInfoList { edbInfoList[k].EdbNameSource = v.EdbName edbInfoListMap[v.EdbInfoId] = v if v.EdbInfoId == extraConf.BaseEdbInfoId { baseEdbInfo = v } } // 商品数据库指标 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, edbInfoList, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList, extraConf.XDataList) 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, edbInfoList []*models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag, reqXDataList []XData) (xDataList []XData, yDataList []YData, err error) { if baseEdbInfo == nil { err = errors.New("ETA指标未选取") return } if len(edbInfoList) == 0 { edbInfoList = append(edbInfoList, baseEdbInfo) } 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] } } // 指标对应的所有数据 //edbDataListMap := make(map[int][]*models.EdbDataList) // 普通的指标数据 baseDataListMap := make(map[int][]*models.EdbDataList) for _, v := range edbInfoList { baseDataList := make([]*models.EdbDataList, 0) baseDataList, err = models.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "") if err != nil { return } baseDataListMap[v.EdbInfoId] = baseDataList } 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) hasChina := false // 是否包含国内的期货合约 for _, v := range zlFutureGoodEdbInfoList { if v.RegionType == "国内" { hasChina = true 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, 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 } } } // 需求池604,只要包含了国内合约,最大必须是12期 if hasChina { maxN = 12 } // 找出所有的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] }) var reqEdbInfoIds []int for _, v := range edbInfoList { reqEdbInfoIds = append(reqEdbInfoIds, v.EdbInfoId) tmp := XData{ Name: v.EdbName, NameEn: v.EdbNameEn, } xDataList = append(xDataList, tmp) } var edbIdList []int futureGoodNameMap := make(map[int]map[int]string) edbIdList, yDataList, futureGoodNameMap, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN, reqEdbInfoIds) // todo 最后处理数据 tmpXDataList, newYDataList, err := handleProfitResultData(xDataList, futureGoodNameMap, yDataList, earliestDateTime, edbIdList) if err != nil { return } if len(reqXDataList) == 0 { xDataList = tmpXDataList } else { xDataList = reqXDataList } yDataList = newYDataList return } // ProfitChartChartData 获取数据 func ProfitChartChartData(baseEdbInfo *models.EdbInfo, baseDataListMap map[int][]*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, reqEdbInfoIds []int) (edbIdList []int, yDataList []YData, futureGoodNameMap map[int]map[int]string, err error) { // 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3}) //earliestDateTime time.Time // ETA指标数据 allBaseEdbDateDataMap := make(map[int]map[string]float64) for edbInfoId, baseDataList := range baseDataListMap { baseEdbDateData := make(map[string]float64) for _, edbData := range baseDataList { baseEdbDateData[edbData.DataTime] = edbData.Value } allBaseEdbDateDataMap[edbInfoId] = baseEdbDateData } // 商品指标数据 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) futureGoodNameMap = make(map[int]map[int]string) for tmpk, 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 noDataIdMap := make(map[int]int, 0) // 没有数据的指标map xEdbInfoIdList := make([]int, 0) // 当前数据的指标id列表 // 现货指标 index := 0 var realDateTime time.Time // 现货指标 baseEdbDateData, ok := allBaseEdbDateDataMap[baseEdbInfo.EdbInfoId] if !ok { err = fmt.Errorf("指标id: %d 没有数据", baseEdbInfo.EdbInfoId) return } realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataListMap[baseEdbInfo.EdbInfoId], baseEdbDateData, edbDataMap) if tmpErr != nil { err = tmpErr return } if isFind { maxDate = realDateTime } edbIdList = make([]int, 0) //普通指标ID for _, edbInfoId := range reqEdbInfoIds { if edbInfoId == baseEdbInfo.EdbInfoId { findDataList = append(findDataList, findDataValue) yDataMap[index] = findDataValue xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId) edbIdList = append(edbIdList, edbInfoId) index += 1 continue } baseEdbDateDataTmp, ok := allBaseEdbDateDataMap[edbInfoId] if !ok { err = fmt.Errorf("指标id: %d 没有数据", edbInfoId) return } findDataValueTmp, isFindTmp := baseEdbDateDataTmp[realDateTime.Format(utils.FormatDate)] if !isFindTmp { noDataIdList = append(noDataIdList, edbInfoId) noDataIdMap[edbInfoId] = edbInfoId } findDataList = append(findDataList, findDataValueTmp) yDataMap[index] = findDataValueTmp xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId) edbIdList = append(edbIdList, edbInfoId) index += 1 } mList := make([]int, 0) // 间隔月份 tmpNameMap := make(map[int]string) // 最小开始的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()-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 { if tag == "A" { nameTmp := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbName, "(") nameTmpEn := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbNameEn, "(") if len(nameTmp) > 1 && len(nameTmpEn) > 1 { nameTmp[1] = strings.Trim(nameTmp[1], ")") nameTmpEn[1] = strings.Trim(nameTmpEn[1], ")") tmpNameMap[tmpN+1] = nameTmp[1] + "-" + nameTmpEn[1] } } newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId] } //, formulaStr string, tagEdbIdMap map[string]int formulaFormStr := ReplaceFormula(newTagEdbIdMap, calculateMap, formulaStr) //计算公式异常,那么就移除该指标 if formulaFormStr == `` { //removeDateList = append(removeDateList, sk) fmt.Println("异常了") continue } calVal, e := engine.ParseAndExec(formulaFormStr) //calVal, err := calResult.Float64() if e != nil { err = errors.New("计算失败:获取计算值失败 Err:" + e.Error() + ";formulaStr:" + formulaFormStr) fmt.Println(err) return } //yDataMap[n] = calVal //xEdbInfoIdList = append(xEdbInfoIdList, n) 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) } { hasDataIndexList := make([]int, 0) for dataK, edbInfoId := range xEdbInfoIdList { if _, ok := noDataIdMap[edbInfoId]; !ok { // 如果是没有数据的指标id hasDataIndexList = append(hasDataIndexList, dataK) } } lenHasDataIndex := len(hasDataIndexList) if lenHasDataIndex > 0 { for lenHasDataI := 1; lenHasDataI < lenHasDataIndex; lenHasDataI++ { perK := hasDataIndexList[lenHasDataI-1] //上一个有数据的指标下标 currK := hasDataIndexList[lenHasDataI] //当前有数据的指标下标 preVal := findDataList[perK] //上一个有数据的坐标的值 currVal := findDataList[currK] //当前有数据的指标的值 // 环差值 hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(currK - perK))) var tmpI int64 // 将两个中间的数据做平均值补全 for hcI := perK + 1; hcI < currK; hcI++ { tmpI++ findDataList[hcI], _ = decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(tmpI))).RoundCeil(4).Float64() } } } } futureGoodNameMap[tmpk] = tmpNameMap 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, 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 for i := 1; i < maxN; i++ { k := i % lenFutureGoodEdbInfoList futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k] } //需求池604,只要是国内合约,最大必须是12期 newMaxN = 12 return } for _, v := range tmpFutureGoodEdbInfoList { //海外的连续日期,目前 if v.FutureGoodEdbType == 2 { if v.Month <= maxN { 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 < maxN { 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(xDataListInit []XData, futureNameMap map[int]map[int]string, yDataList []YData, earliestDateTime time.Time, allEdbInfoIds []int) (xDataList []XData, newYDataList []YData, err error) { newYDataList = yDataList xDataList = xDataListInit nMap := make(map[int]int) nList := make([]int, 0) nListEdbMap := make(map[int]struct{}) for _, v := range yDataList { for _, n := range v.XEdbInfoIdList { if utils.InArrayByInt(allEdbInfoIds, n) { if _, ok := nListEdbMap[n]; !ok { nList = append(nList, n) nListEdbMap[n] = struct{}{} } } else { nMap[n] = n } } } // 找出所有的N值,并进行正序排列 nListTmp := make([]int, 0) for _, n := range nMap { nListTmp = append(nListTmp, n) } sort.Slice(nListTmp, func(i, j int) bool { return nListTmp[i] < nListTmp[j] }) nList = append(nList, nListTmp...) for _, n := range nList { if utils.InArrayByInt(allEdbInfoIds, n) { 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 { // todo 改成所有的基础现货指标 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) } } if utils.InArrayByInt(allEdbInfoIds, currN) { newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN) } else { 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+1] 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 k1, n := range newYDataList[yIndex].XEdbInfoIdList { if utils.InArrayByInt(allEdbInfoIds, n) { // 现货价不处理 tmpItem := xDataListInit[k1] nameList = append(nameList, tmpItem.Name) enNameList = append(enNameList, tmpItem.NameEn) continue } if n <= 0 { nameList = append(nameList, `无合约`) enNameList = append(enNameList, `no contract`) } else { nameTmp := futureNameMap[yIndex][n] nameTmpSlice := strings.Split(nameTmp, "-") nameList = append(nameList, nameTmpSlice[0]) enNameList = append(enNameList, nameTmpSlice[1]) } } 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) } fmt.Println(formulaStr) 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 }