Browse Source

fix:预测指标新增分段预测

Roc 2 years ago
parent
commit
d7851518f0
3 changed files with 716 additions and 13 deletions
  1. 157 11
      models/edb_info.go
  2. 13 2
      models/predict_edb_conf.go
  3. 546 0
      models/predict_edb_info_rule.go

+ 157 - 11
models/edb_info.go

@@ -4,7 +4,9 @@ import (
 	"errors"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
 	"hongze/hongze_edb_lib/utils"
+	"strconv"
 	"time"
 )
 
@@ -383,6 +385,138 @@ func GetChartPredictEdbInfoDataList(predictEdbConf PredictEdbConf, latestDateStr
 	return
 }
 
+// GetChartPredictEdbInfoDataListByConfList 获取图表的预测指标的未来数据
+func GetChartPredictEdbInfoDataListByConfList(predictEdbConfList []*PredictEdbConf, filtrateStartDateStr, latestDateStr, endDateStr, frequency string, realPredictEdbInfoData []*EdbInfoSearchData) (predictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
+	endDate, err := time.ParseInLocation(utils.FormatDate, endDateStr, time.Local)
+	if err != nil {
+		return
+	}
+
+	latestDate, err := time.ParseInLocation(utils.FormatDate, latestDateStr, time.Local)
+	if err != nil {
+		return
+	}
+
+	// 开始预测数据的时间
+	startDate := latestDate
+
+	// 如果有筛选时间的话
+	if filtrateStartDateStr != `` {
+		filtrateStartDate, tmpErr := time.ParseInLocation(utils.FormatDate, filtrateStartDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		//如果筛选时间晚于实际数据时间,那么就以筛选时间作为获取预测数据的时间
+		if filtrateStartDate.After(latestDate) {
+			startDate = filtrateStartDate.AddDate(0, 0, -1)
+		}
+	}
+
+	//var dateArr []string
+	// 对应日期的值
+	existMap := make(map[string]float64)
+	for _, v := range realPredictEdbInfoData {
+		//dateArr = append(dateArr, v.DataTime)
+		existMap[v.DataTime] = v.Value
+	}
+
+	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
+	//dataValue := lastDataValue
+	//预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值
+
+	for _, predictEdbConf := range predictEdbConfList {
+		dataEndTime := endDate
+		if predictEdbConf.EndDate.Before(dataEndTime) {
+			dataEndTime = predictEdbConf.EndDate
+		}
+
+		var tmpMinValue, tmpMaxValue float64 // 当前预测结果中的最大/最小值
+
+		switch predictEdbConf.RuleType {
+		case 1: //1:最新
+			var lastDataValue float64 //最新值
+			tmpAllData := make([]*EdbInfoSearchData, 0)
+			tmpAllData = append(tmpAllData, realPredictEdbInfoData...)
+			tmpAllData = append(tmpAllData, predictEdbInfoData...)
+			lenTmpAllData := len(tmpAllData)
+			if lenTmpAllData > 0 {
+				lastDataValue = tmpAllData[lenTmpAllData-1].Value
+			}
+			predictEdbInfoData = GetChartPredictEdbInfoDataListByRule1(predictEdbConf.PredictEdbInfoId, lastDataValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+			tmpMaxValue = lastDataValue
+			tmpMinValue = lastDataValue
+		case 2: //2:固定值
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			dataValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData = GetChartPredictEdbInfoDataListByRule1(predictEdbConf.PredictEdbInfoId, dataValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+
+			tmpMaxValue = dataValue
+			tmpMinValue = dataValue
+		case 3: //3:同比
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			tbValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTb(predictEdbConf.PredictEdbInfoId, tbValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 4: //4:同差
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			tcValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTc(predictEdbConf.PredictEdbInfoId, tcValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 5: //5:环比
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			hbValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleHb(predictEdbConf.PredictEdbInfoId, hbValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 6: //6:环差
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			hcValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleHc(predictEdbConf.PredictEdbInfoId, hcValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 7: //7:N期移动均值
+			nValue, tmpErr := strconv.Atoi(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(predictEdbConf.PredictEdbInfoId, nValue, startDate, dataEndTime, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
+		case 8: //8:N期段线性外推值
+			nValue, tmpErr := strconv.Atoi(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleNLinearRegression(predictEdbConf.PredictEdbInfoId, nValue, startDate, dataEndTime, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
+		}
+		//startDate = dataEndTime.AddDate(0, 0, 1)
+		startDate = dataEndTime
+		if tmpMinValue < minValue {
+			minValue = tmpMinValue
+		}
+		if tmpMaxValue < maxValue {
+			maxValue = tmpMaxValue
+		}
+	}
+
+	return
+}
+
 // GetPredictEdbDayList 获取预测指标日期列表
 func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayList []time.Time) {
 	//if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, frequency)
@@ -424,16 +558,17 @@ func GetPredictDataListByPredictEdbInfoId(edbInfoId, order int, startDate string
 // GetPredictDataListByPredictEdbInfo 根据预测指标信息获取预测指标的数据,order:1升序,其余值为降序
 func GetPredictDataListByPredictEdbInfo(edbInfo *EdbInfo, order int, startDate string) (dataList []*EdbInfoSearchData, sourceEdbInfoItem *EdbInfo, predictEdbConf *PredictEdbConf, err error, errMsg string) {
 	// 查找该预测指标配置
-	predictEdbConf, err = GetPredictEdbConfById(edbInfo.EdbInfoId)
+	predictEdbConfList, err := GetPredictEdbConfListById(edbInfo.EdbInfoId)
 	if err != nil && err.Error() != utils.ErrNoRow() {
 		errMsg = "获取预测指标配置信息失败"
 		return
 	}
-	if predictEdbConf == nil {
+	if len(predictEdbConfList) == 0 {
 		errMsg = "获取预测指标配置信息失败"
 		err = errors.New(errMsg)
 		return
 	}
+	predictEdbConf = predictEdbConfList[0]
 
 	// 来源指标
 	sourceEdbInfoItem, err = GetEdbInfoById(predictEdbConf.SourceEdbInfoId)
@@ -445,6 +580,7 @@ func GetPredictDataListByPredictEdbInfo(edbInfo *EdbInfo, order int, startDate s
 		return
 	}
 
+	allDataList := make([]*EdbInfoSearchData, 0)
 	//获取指标数据(实际已生成)
 	var condition string
 	var pars []interface{}
@@ -458,28 +594,38 @@ func GetPredictDataListByPredictEdbInfo(edbInfo *EdbInfo, order int, startDate s
 	if err != nil {
 		return
 	}
+	// 如果选择了日期,那么需要筛选所有的数据,用于未来指标的生成
+	if startDate != `` {
+		allDataList, err = GetEdbDataListAll(" AND edb_info_id=? ", []interface{}{sourceEdbInfoItem.EdbInfoId}, sourceEdbInfoItem.Source, 1)
+		if err != nil {
+			return
+		}
+	} else {
+		allDataList = dataList
+	}
 
 	// 获取预测指标未来的数据
 	predictDataList := make([]*EdbInfoSearchData, 0)
 
 	endDateStr := edbInfo.EndDate //预测指标的结束日期
 
-	predictDataList, err = GetChartPredictEdbInfoDataList(*predictEdbConf, sourceEdbInfoItem.LatestDate, sourceEdbInfoItem.LatestValue, endDateStr, edbInfo.Frequency, order)
+	//predictDataList, err = GetChartPredictEdbInfoDataList(*predictEdbConf, sourceEdbInfoItem.LatestDate, sourceEdbInfoItem.LatestValue, endDateStr, edbInfo.Frequency, order)
+	var predictMinValue, predictMaxValue float64
+	predictDataList, predictMinValue, predictMaxValue, err = GetChartPredictEdbInfoDataListByConfList(predictEdbConfList, startDate, sourceEdbInfoItem.LatestDate, endDateStr, edbInfo.Frequency, allDataList)
+
 	if err != nil {
 		return
 	}
 	dataList = append(dataList, predictDataList...)
 	if len(predictDataList) > 0 {
-		tmpValue := predictDataList[0]
-
-		// 如果最大值 小于 预测值,那么将预测值作为最大值数据返回
-		if edbInfo.MaxValue < tmpValue.Value {
-			edbInfo.MaxValue = tmpValue.Value
+		// 如果最小值 大于 预测值,那么将预测值作为最小值数据返回
+		if edbInfo.MinValue > predictMinValue {
+			edbInfo.MinValue = predictMinValue
 		}
 
-		// 如果最小值 大于 预测值,那么将预测值作为最小值数据返回
-		if edbInfo.MinValue > tmpValue.Value {
-			edbInfo.MinValue = tmpValue.Value
+		// 如果最大值 小于 预测值,那么将预测值作为最大值数据返回
+		if edbInfo.MaxValue < predictMaxValue {
+			edbInfo.MaxValue = predictMaxValue
 		}
 	}
 	return

+ 13 - 2
models/predict_edb_conf.go

@@ -7,10 +7,13 @@ import (
 )
 
 type PredictEdbConf struct {
-	PredictEdbInfoId int       `orm:"column(predict_edb_info_id);pk" description:"预测指标id"`
+	ConfigId         int       `orm:"column(config_id);pk" description:"规则id"`
+	PredictEdbInfoId int       `orm:"column(predict_edb_info_id)" description:"预测指标id"`
 	SourceEdbInfoId  int       `description:"来源指标id"`
-	RuleType         int       `description:"预测规则,1:最新,2:固定值"`
+	RuleType         int       `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值"`
 	FixedValue       float64   `description:"固定值"`
+	Value            string    `description:"配置的值"`
+	EndDate          time.Time `description:"截止日期"`
 	ModifyTime       time.Time `description:"修改时间"`
 	CreateTime       time.Time `description:"添加时间"`
 }
@@ -31,6 +34,14 @@ func GetPredictEdbConfCount(sourceEdbInfoId int) (count int, err error) {
 	return
 }
 
+// GetPredictEdbConfListById 根据预测指标id获取预测指标配置信息列表
+func GetPredictEdbConfListById(edbInfoId int) (items []*PredictEdbConf, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM predict_edb_conf WHERE predict_edb_info_id=? ORDER BY config_id ASC`
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
+	return
+}
+
 // ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId 根据来源指标修改预测指标的最新数据信息
 func ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(sourceEdbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
 	o := orm.NewOrm()

+ 546 - 0
models/predict_edb_info_rule.go

@@ -0,0 +1,546 @@
+package models
+
+import (
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"time"
+)
+
+// GetChartPredictEdbInfoDataListByRule1 根据规则1获取预测数据
+func GetChartPredictEdbInfoDataListByRule1(edbInfoId int, dataValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData) {
+	newPredictEdbInfoData = predictEdbInfoData
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
+	for k, v := range dayList {
+		newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + k,
+			DataTime:  v.Format(utils.FormatDate),
+			Value:     dataValue,
+		})
+		existMap[v.Format(utils.FormatDate)] = dataValue
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleTb 根据同比值规则获取预测数据
+//	2.1 同比: 在未来某一个时间段内,给定一个固定的同比增速a,用去年同期值X乘以同比增速(1+a),得到预测值Y=X(1+a)
+//	例: 今年1-3月值,100,100,120。给定同比增速a=0.1,则明年1-3月预测值为: 100*1.1=110,100*1.1=110,120*1.1=132。
+func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
+	for k, currentDate := range dayList {
+
+		tmpData := &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + index + k,
+			DataTime:  currentDate.Format(utils.FormatDate),
+			//Value:         dataValue,
+		}
+
+		var val float64
+		var calculateStatus bool //计算结果
+		//currentItem := existMap[av]
+		//上一年的日期
+		preDate := currentDate.AddDate(-1, 0, 0)
+		preDateStr := preDate.Format(utils.FormatDate)
+		if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
+			val = PredictTbzDiv(preValue, tbValue)
+			calculateStatus = true
+		} else {
+			switch frequency {
+			case "月度":
+				//向上和向下,各找一个月
+				nextDateDay := preDate
+				preDateDay := preDate
+				for i := 0; i <= 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = PredictTbzDiv(preValue, tbValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = PredictTbzDiv(preValue, tbValue)
+							calculateStatus = true
+							break
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+
+			case "季度", "年度":
+				if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
+					val = PredictTbzDiv(preValue, tbValue)
+					calculateStatus = true
+					break
+				}
+			default:
+				nextDateDay := preDate
+				preDateDay := preDate
+
+				for i := 0; i < 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = PredictTbzDiv(preValue, tbValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = PredictTbzDiv(preValue, tbValue)
+							calculateStatus = true
+							break
+						} else {
+							//fmt.Println("pre not find:", preDateStr, "i:", i)
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+			}
+		}
+
+		if calculateStatus {
+			tmpData.Value = val
+			newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+
+			// 最大最小值
+			if val < minValue {
+				minValue = val
+			}
+			if val < maxValue {
+				maxValue = val
+			}
+		}
+	}
+	return
+}
+
+// PredictTbzDiv 同比值计算
+// @params a float64 去年同期值
+// @params b float64 固定同比增速
+func PredictTbzDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 去年同期值
+		af := decimal.NewFromFloat(a)
+
+		// 同比增速
+		bf := decimal.NewFromFloat(b)
+
+		// 默认1
+		cf := decimal.NewFromFloat(1)
+
+		// 总增速
+		val := bf.Add(cf)
+
+		// 计算
+		result, _ = val.Mul(af).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleTc 根据同差值规则获取预测数据
+//	2.2 同差: 在未来某一个时间段内,给定一个固定的同比增加值a,用去年同期值X加上同比增加值A,得到预测值Y=X+a
+//	例: 今年1-3月值,100,100,120。给定同比增加值a=10,则明年1-3月预测值为: 100+10=110,100+10=110,120+10=130
+func GetChartPredictEdbInfoDataListByRuleTc(edbInfoId int, tcValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*EdbInfoSearchData, 0)
+	for k, currentDate := range dayList {
+
+		tmpData := &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + index + k,
+			DataTime:  currentDate.Format(utils.FormatDate),
+			//Value:         dataValue,
+		}
+
+		var val float64
+		var calculateStatus bool //计算结果
+		//currentItem := existMap[av]
+		//上一年的日期
+		preDate := currentDate.AddDate(-1, 0, 0)
+		preDateStr := preDate.Format(utils.FormatDate)
+		if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
+			val = PredictTczDiv(preValue, tcValue)
+			calculateStatus = true
+		} else {
+			switch frequency {
+			case "月度":
+				//向上和向下,各找一个月
+				nextDateDay := preDate
+				preDateDay := preDate
+				for i := 0; i <= 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = PredictTczDiv(preValue, tcValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = PredictTczDiv(preValue, tcValue)
+							calculateStatus = true
+							break
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+
+			case "季度", "年度":
+				if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
+					val = PredictTczDiv(preValue, tcValue)
+					calculateStatus = true
+					break
+				}
+			default:
+				nextDateDay := preDate
+				preDateDay := preDate
+
+				for i := 0; i < 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = PredictTczDiv(preValue, tcValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = PredictTczDiv(preValue, tcValue)
+							calculateStatus = true
+							break
+						} else {
+							//fmt.Println("pre not find:", preDateStr, "i:", i)
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+			}
+		}
+
+		if calculateStatus {
+			tmpData.Value = val
+			newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+
+			// 最大最小值
+			if val < minValue {
+				minValue = val
+			}
+			if val < maxValue {
+				maxValue = val
+			}
+		}
+	}
+	return
+}
+
+// PredictTczDiv 环差值计算
+// @params a float64 上一期值
+// @params b float64 固定的环比增加值
+func PredictTczDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 上一期值
+		af := decimal.NewFromFloat(a)
+
+		// 固定的环比增加值
+		bf := decimal.NewFromFloat(b)
+
+		// 计算
+		result, _ = af.Add(bf).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleHb 根据环比值规则获取预测数据
+//	环比:在未来某一个时间段内,给定一个固定的环比增速a,用上一期值X乘以环比增速(1+a),得到预测值Y=X(1+a)
+//	例: 最近1期值为100,给定环比增速a=0.2,则未来3期预测值为: 100*1.2=120,120*1.2=144,144*1.2=172.8
+func GetChartPredictEdbInfoDataListByRuleHb(edbInfoId int, hbValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpK := index + k - 1 //上1期的值
+
+		// 环比值计算
+		val := PredictHbzDiv(newPredictEdbInfoData[tmpK].Value, hbValue)
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + index + k,
+			DataTime:  currentDateStr,
+			Value:     val,
+		})
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// PredictHbzDiv 环比值计算
+// @params a float64 上一期值
+// @params b float64 固定的环比增速
+func PredictHbzDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 上一期值
+		af := decimal.NewFromFloat(a)
+
+		// 固定的环比增速
+		bf := decimal.NewFromFloat(b)
+
+		// 默认1
+		cf := decimal.NewFromFloat(1)
+
+		// 总增速
+		val := bf.Add(cf)
+
+		// 计算
+		result, _ = val.Mul(af).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleHc 根据环差值规则获取预测数据
+//	2.4 环差:在未来某一个时间段内,给定一个固定的环比增加值a,用上一期值X加上环比增加值a,得到预测值Y=X+a
+//	例: 最近1期值为100,给定环比增加值a=10,则未来3期预测值为: 100+10=110,110+10=120,120+10=130
+func GetChartPredictEdbInfoDataListByRuleHc(edbInfoId int, hcValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpK := index + k - 1 //上1期的值
+
+		// 环差别值计算
+		val := PredictHczDiv(newPredictEdbInfoData[tmpK].Value, hcValue)
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + index + k,
+			DataTime:  currentDateStr,
+			Value:     val,
+		})
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// PredictHczDiv 环差值计算
+// @params a float64 上一期值
+// @params b float64 固定的环比增加值
+func PredictHczDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 上一期值
+		af := decimal.NewFromFloat(a)
+
+		// 固定的环比增加值
+		bf := decimal.NewFromFloat(b)
+
+		// 计算
+		result, _ = af.Add(bf).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleNMoveMeanValue 根据N期移动均值规则获取预测数据
+//	2.5 N期移动均值:在未来某一个时间段内,下一期值等于过去N期值得平均值。
+//	例:最近3期值(N=3),为95,98,105则未来第1期值为 1/3*(95+98+105)=99.33, 未来第2期值为 1/3*(98+105+99.33)=100.78依次类推。
+func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
+	allDataList := make([]*EdbInfoSearchData, 0)
+	allDataList = append(allDataList, realPredictEdbInfoData...)
+	allDataList = append(allDataList, predictEdbInfoData...)
+
+	newPredictEdbInfoData = predictEdbInfoData
+
+	lenAllData := len(allDataList)
+	if lenAllData < nValue || lenAllData <= 0 {
+		return
+	}
+	if nValue <= 0 {
+		return
+	}
+	// 分母
+	decimalN := decimal.NewFromInt(int64(nValue))
+
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpIndex := lenAllData + k - 1 //上1期的值
+
+		// 数据集合中的最后一个数据
+		tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
+		for tmpK := 2; tmpK <= nValue; tmpK++ {
+			tmpIndex2 := tmpIndex - tmpK //上N期的值
+			tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
+			tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
+		}
+
+		// N期移动均值计算
+		val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64()
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		tmpData := &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
+			DataTime:  currentDateStr,
+			Value:     val,
+		}
+		newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+		allDataList = append(allDataList, tmpData)
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleNLinearRegression 根据N期移动均值规则获取预测数据
+//	2.6N期段线性外推值:给出过去N期值所确定的线性回归方程(Y=aX+b)在未来一段时间内的推算值。回归方程虽然比较复杂,但各种编程语言应该都有现成的模块或函数,应该无需自己编写。
+//	例1:过去5期值(N=5)分别为:3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:13,15,17。
+//
+//	例2:过去6期值(N=6)分别为:3,3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:12.33,14.05,15.76。例1和例2的区别在于,多加了一期数据,导致回归方程发生改变,从而预测值不同。
+func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
+	//var errMsg string
+	//defer func() {
+	//	if errMsg != `` {
+	//		go alarm_msg.SendAlarmMsg("更新上海的token失败;ERR:"+err.Error(), 3)
+	//	}
+	//}()
+	allDataList := make([]*EdbInfoSearchData, 0)
+	allDataList = append(allDataList, realPredictEdbInfoData...)
+	allDataList = append(allDataList, predictEdbInfoData...)
+
+	newPredictEdbInfoData = predictEdbInfoData
+
+	lenAllData := len(allDataList)
+	if lenAllData < nValue || lenAllData <= 0 {
+		return
+	}
+
+	if nValue <= 0 {
+		return
+	}
+
+	//获取后面的预测数据
+	// 获取线性方程公式的a、b的值
+	coordinateData := make([]Coordinate, 0)
+	for tmpK := nValue; tmpK > 0; tmpK-- {
+		tmpIndex2 := lenAllData - tmpK //上N期的值
+		tmpCoordinate := Coordinate{
+			X: float64(nValue - tmpK + 1),
+			Y: allDataList[tmpIndex2].Value,
+		}
+		coordinateData = append(coordinateData, tmpCoordinate)
+	}
+	a, b := getLinearResult(coordinateData)
+	//fmt.Println("a:", a, ";======b:", b)
+
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpK := nValue + k + 1
+
+		aDecimal := decimal.NewFromFloat(a)
+		xDecimal := decimal.NewFromInt(int64(tmpK))
+		bDecimal := decimal.NewFromFloat(b)
+
+		val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		tmpData := &EdbInfoSearchData{
+			EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
+			DataTime:  currentDateStr,
+			Value:     val,
+		}
+		newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+		allDataList = append(allDataList, tmpData)
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// Series is a container for a series of data
+type Series []Coordinate
+
+// Coordinate holds the data in a series
+type Coordinate struct {
+	X, Y float64
+}
+
+func getLinearResult(s []Coordinate) (gradient, intercept float64) {
+	if len(s) == 0 {
+		return
+	}
+
+	// Placeholder for the math to be done
+	var sum [5]float64
+
+	// Loop over data keeping index in place
+	i := 0
+	for ; i < len(s); i++ {
+		sum[0] += s[i].X
+		sum[1] += s[i].Y
+		sum[2] += s[i].X * s[i].X
+		sum[3] += s[i].X * s[i].Y
+		sum[4] += s[i].Y * s[i].Y
+	}
+
+	// Find gradient and intercept
+	f := float64(i)
+	gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
+	intercept = (sum[1] / f) - (gradient * sum[0] / f)
+
+	//fmt.Println("gradient:", gradient, ";intercept:", intercept)
+	// Create the new regression series
+	//for j := 0; j < len(s); j++ {
+	//	regressions = append(regressions, Coordinate{
+	//		X: s[j].X,
+	//		Y: s[j].X*gradient + intercept,
+	//	})
+	//}
+
+	return
+}