predict_edb_info_rule.go 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. package models
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/nosixtools/solarlunar"
  6. "github.com/shopspring/decimal"
  7. "hongze/hongze_edb_lib/utils"
  8. "math"
  9. "strings"
  10. "time"
  11. )
  12. // GetChartPredictEdbInfoDataListByRule1 根据规则1获取预测数据
  13. func GetChartPredictEdbInfoDataListByRule1(edbInfoId int, dataValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData) {
  14. newPredictEdbInfoData = predictEdbInfoData
  15. //获取后面的预测数据
  16. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  17. predictEdbInfoData = make([]*EdbInfoSearchData, 0)
  18. for k, v := range dayList {
  19. newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
  20. EdbDataId: edbInfoId + 10000000000 + k,
  21. DataTime: v.Format(utils.FormatDate),
  22. Value: dataValue,
  23. })
  24. existMap[v.Format(utils.FormatDate)] = dataValue
  25. }
  26. return
  27. }
  28. // GetChartPredictEdbInfoDataListByRuleTb 根据同比值规则获取预测数据
  29. // 2.1 同比: 在未来某一个时间段内,给定一个固定的同比增速a,用去年同期值X乘以同比增速(1+a),得到预测值Y=X(1+a)
  30. // 例: 今年1-3月值,100,100,120。给定同比增速a=0.1,则明年1-3月预测值为: 100*1.1=110,100*1.1=110,120*1.1=132。
  31. func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  32. allDataList := make([]*EdbInfoSearchData, 0)
  33. allDataList = append(allDataList, realPredictEdbInfoData...)
  34. allDataList = append(allDataList, predictEdbInfoData...)
  35. newPredictEdbInfoData = predictEdbInfoData
  36. index := len(allDataList)
  37. //获取后面的预测数据
  38. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  39. predictEdbInfoData = make([]*EdbInfoSearchData, 0)
  40. for k, currentDate := range dayList {
  41. tmpData := &EdbInfoSearchData{
  42. EdbDataId: edbInfoId + 10000000000 + index + k,
  43. DataTime: currentDate.Format(utils.FormatDate),
  44. //Value: dataValue,
  45. }
  46. var val float64
  47. var calculateStatus bool //计算结果
  48. //currentItem := existMap[av]
  49. //上一年的日期
  50. preDate := currentDate.AddDate(-1, 0, 0)
  51. preDateStr := preDate.Format(utils.FormatDate)
  52. if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
  53. val = PredictTbzDiv(preValue, tbValue)
  54. calculateStatus = true
  55. } else {
  56. switch frequency {
  57. case "月度":
  58. //向上和向下,各找一个月
  59. nextDateDay := preDate
  60. preDateDay := preDate
  61. for i := 0; i <= 35; i++ {
  62. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  63. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  64. val = PredictTbzDiv(preValue, tbValue)
  65. calculateStatus = true
  66. break
  67. } else {
  68. preDateDayStr := preDateDay.Format(utils.FormatDate)
  69. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  70. val = PredictTbzDiv(preValue, tbValue)
  71. calculateStatus = true
  72. break
  73. }
  74. }
  75. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  76. preDateDay = preDateDay.AddDate(0, 0, -1)
  77. }
  78. case "季度", "年度":
  79. if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
  80. val = PredictTbzDiv(preValue, tbValue)
  81. calculateStatus = true
  82. break
  83. }
  84. default:
  85. nextDateDay := preDate
  86. preDateDay := preDate
  87. for i := 0; i < 35; i++ {
  88. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  89. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  90. val = PredictTbzDiv(preValue, tbValue)
  91. calculateStatus = true
  92. break
  93. } else {
  94. preDateDayStr := preDateDay.Format(utils.FormatDate)
  95. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  96. val = PredictTbzDiv(preValue, tbValue)
  97. calculateStatus = true
  98. break
  99. } else {
  100. //fmt.Println("pre not find:", preDateStr, "i:", i)
  101. }
  102. }
  103. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  104. preDateDay = preDateDay.AddDate(0, 0, -1)
  105. }
  106. }
  107. }
  108. if calculateStatus {
  109. tmpData.Value = val
  110. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  111. allDataList = append(allDataList, tmpData)
  112. existMap[tmpData.DataTime] = val
  113. // 最大最小值
  114. if val < minValue {
  115. minValue = val
  116. }
  117. if val > maxValue {
  118. maxValue = val
  119. }
  120. }
  121. }
  122. return
  123. }
  124. // PredictTbzDiv 同比值计算
  125. // @params a float64 去年同期值
  126. // @params b float64 固定同比增速
  127. func PredictTbzDiv(a, b float64) (result float64) {
  128. if b != 0 {
  129. // 去年同期值
  130. af := decimal.NewFromFloat(a)
  131. // 同比增速
  132. bf := decimal.NewFromFloat(b)
  133. // 默认1
  134. cf := decimal.NewFromFloat(1)
  135. // 总增速
  136. val := bf.Add(cf)
  137. // 计算
  138. result, _ = val.Mul(af).RoundCeil(4).Float64()
  139. } else {
  140. result = 0
  141. }
  142. return
  143. }
  144. // GetChartPredictEdbInfoDataListByRuleTc 根据同差值规则获取预测数据
  145. // 2.2 同差: 在未来某一个时间段内,给定一个固定的同比增加值a,用去年同期值X加上同比增加值A,得到预测值Y=X+a
  146. // 例: 今年1-3月值,100,100,120。给定同比增加值a=10,则明年1-3月预测值为: 100+10=110,100+10=110,120+10=130
  147. func GetChartPredictEdbInfoDataListByRuleTc(edbInfoId int, tcValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  148. allDataList := make([]*EdbInfoSearchData, 0)
  149. allDataList = append(allDataList, realPredictEdbInfoData...)
  150. allDataList = append(allDataList, predictEdbInfoData...)
  151. newPredictEdbInfoData = predictEdbInfoData
  152. index := len(allDataList)
  153. //获取后面的预测数据
  154. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  155. predictEdbInfoData = make([]*EdbInfoSearchData, 0)
  156. for k, currentDate := range dayList {
  157. tmpData := &EdbInfoSearchData{
  158. EdbDataId: edbInfoId + 10000000000 + index + k,
  159. DataTime: currentDate.Format(utils.FormatDate),
  160. //Value: dataValue,
  161. }
  162. var val float64
  163. var calculateStatus bool //计算结果
  164. //currentItem := existMap[av]
  165. //上一年的日期
  166. preDate := currentDate.AddDate(-1, 0, 0)
  167. preDateStr := preDate.Format(utils.FormatDate)
  168. if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
  169. val = PredictTczDiv(preValue, tcValue)
  170. calculateStatus = true
  171. } else {
  172. switch frequency {
  173. case "月度":
  174. //向上和向下,各找一个月
  175. nextDateDay := preDate
  176. preDateDay := preDate
  177. for i := 0; i <= 35; i++ {
  178. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  179. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  180. val = PredictTczDiv(preValue, tcValue)
  181. calculateStatus = true
  182. break
  183. } else {
  184. preDateDayStr := preDateDay.Format(utils.FormatDate)
  185. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  186. val = PredictTczDiv(preValue, tcValue)
  187. calculateStatus = true
  188. break
  189. }
  190. }
  191. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  192. preDateDay = preDateDay.AddDate(0, 0, -1)
  193. }
  194. case "季度", "年度":
  195. if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
  196. val = PredictTczDiv(preValue, tcValue)
  197. calculateStatus = true
  198. break
  199. }
  200. default:
  201. nextDateDay := preDate
  202. preDateDay := preDate
  203. for i := 0; i < 35; i++ {
  204. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  205. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  206. val = PredictTczDiv(preValue, tcValue)
  207. calculateStatus = true
  208. break
  209. } else {
  210. preDateDayStr := preDateDay.Format(utils.FormatDate)
  211. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  212. val = PredictTczDiv(preValue, tcValue)
  213. calculateStatus = true
  214. break
  215. } else {
  216. //fmt.Println("pre not find:", preDateStr, "i:", i)
  217. }
  218. }
  219. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  220. preDateDay = preDateDay.AddDate(0, 0, -1)
  221. }
  222. }
  223. }
  224. if calculateStatus {
  225. tmpData.Value = val
  226. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  227. allDataList = append(allDataList, tmpData)
  228. existMap[tmpData.DataTime] = val
  229. // 最大最小值
  230. if val < minValue {
  231. minValue = val
  232. }
  233. if val > maxValue {
  234. maxValue = val
  235. }
  236. }
  237. }
  238. return
  239. }
  240. // PredictTczDiv 环差值计算
  241. // @params a float64 上一期值
  242. // @params b float64 固定的环比增加值
  243. func PredictTczDiv(a, b float64) (result float64) {
  244. if b != 0 {
  245. // 上一期值
  246. af := decimal.NewFromFloat(a)
  247. // 固定的环比增加值
  248. bf := decimal.NewFromFloat(b)
  249. // 计算
  250. result, _ = af.Add(bf).RoundCeil(4).Float64()
  251. } else {
  252. result = 0
  253. }
  254. return
  255. }
  256. // GetChartPredictEdbInfoDataListByRuleHb 根据环比值规则获取预测数据
  257. // 环比:在未来某一个时间段内,给定一个固定的环比增速a,用上一期值X乘以环比增速(1+a),得到预测值Y=X(1+a)
  258. // 例: 最近1期值为100,给定环比增速a=0.2,则未来3期预测值为: 100*1.2=120,120*1.2=144,144*1.2=172.8
  259. func GetChartPredictEdbInfoDataListByRuleHb(edbInfoId int, hbValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  260. allDataList := make([]*EdbInfoSearchData, 0)
  261. allDataList = append(allDataList, realPredictEdbInfoData...)
  262. allDataList = append(allDataList, predictEdbInfoData...)
  263. newPredictEdbInfoData = predictEdbInfoData
  264. index := len(allDataList)
  265. //获取后面的预测数据
  266. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  267. for k, currentDate := range dayList {
  268. tmpK := index + k - 1 //上1期的值
  269. // 环比值计算
  270. val := PredictHbzDiv(allDataList[tmpK].Value, hbValue)
  271. currentDateStr := currentDate.Format(utils.FormatDate)
  272. tmpData := &EdbInfoSearchData{
  273. EdbDataId: edbInfoId + 10000000000 + index + k,
  274. DataTime: currentDateStr,
  275. Value: val,
  276. }
  277. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  278. allDataList = append(allDataList, tmpData)
  279. existMap[currentDateStr] = val
  280. // 最大最小值
  281. if val < minValue {
  282. minValue = val
  283. }
  284. if val > maxValue {
  285. maxValue = val
  286. }
  287. }
  288. return
  289. }
  290. // PredictHbzDiv 环比值计算
  291. // @params a float64 上一期值
  292. // @params b float64 固定的环比增速
  293. func PredictHbzDiv(a, b float64) (result float64) {
  294. if b != 0 {
  295. // 上一期值
  296. af := decimal.NewFromFloat(a)
  297. // 固定的环比增速
  298. bf := decimal.NewFromFloat(b)
  299. // 默认1
  300. cf := decimal.NewFromFloat(1)
  301. // 总增速
  302. val := bf.Add(cf)
  303. // 计算
  304. result, _ = val.Mul(af).RoundCeil(4).Float64()
  305. } else {
  306. result = 0
  307. }
  308. return
  309. }
  310. // GetChartPredictEdbInfoDataListByRuleHc 根据环差值规则获取预测数据
  311. // 2.4 环差:在未来某一个时间段内,给定一个固定的环比增加值a,用上一期值X加上环比增加值a,得到预测值Y=X+a
  312. // 例: 最近1期值为100,给定环比增加值a=10,则未来3期预测值为: 100+10=110,110+10=120,120+10=130
  313. func GetChartPredictEdbInfoDataListByRuleHc(edbInfoId int, hcValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  314. allDataList := make([]*EdbInfoSearchData, 0)
  315. allDataList = append(allDataList, realPredictEdbInfoData...)
  316. allDataList = append(allDataList, predictEdbInfoData...)
  317. newPredictEdbInfoData = predictEdbInfoData
  318. index := len(allDataList)
  319. //获取后面的预测数据
  320. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  321. for k, currentDate := range dayList {
  322. tmpK := index + k - 1 //上1期的值
  323. // 环差别值计算
  324. val := PredictHczDiv(allDataList[tmpK].Value, hcValue)
  325. currentDateStr := currentDate.Format(utils.FormatDate)
  326. tmpData := &EdbInfoSearchData{
  327. EdbDataId: edbInfoId + 10000000000 + index + k,
  328. DataTime: currentDateStr,
  329. Value: val,
  330. }
  331. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  332. allDataList = append(allDataList, tmpData)
  333. existMap[currentDateStr] = val
  334. // 最大最小值
  335. if val < minValue {
  336. minValue = val
  337. }
  338. if val > maxValue {
  339. maxValue = val
  340. }
  341. }
  342. return
  343. }
  344. // PredictHczDiv 环差值计算
  345. // @params a float64 上一期值
  346. // @params b float64 固定的环比增加值
  347. func PredictHczDiv(a, b float64) (result float64) {
  348. if b != 0 {
  349. // 上一期值
  350. af := decimal.NewFromFloat(a)
  351. // 固定的环比增加值
  352. bf := decimal.NewFromFloat(b)
  353. // 计算
  354. result, _ = af.Add(bf).RoundCeil(4).Float64()
  355. } else {
  356. result = 0
  357. }
  358. return
  359. }
  360. // GetChartPredictEdbInfoDataListByRuleNMoveMeanValue 根据N期移动均值规则获取预测数据
  361. // 2.5 N期移动均值:在未来某一个时间段内,下一期值等于过去N期值得平均值。
  362. // 例:最近3期值(N=3),为95,98,105则未来第1期值为 1/3*(95+98+105)=99.33, 未来第2期值为 1/3*(98+105+99.33)=100.78依次类推。
  363. func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  364. allDataList := make([]*EdbInfoSearchData, 0)
  365. allDataList = append(allDataList, realPredictEdbInfoData...)
  366. allDataList = append(allDataList, predictEdbInfoData...)
  367. newPredictEdbInfoData = predictEdbInfoData
  368. lenAllData := len(allDataList)
  369. if lenAllData < nValue || lenAllData <= 0 {
  370. return
  371. }
  372. if nValue <= 0 {
  373. return
  374. }
  375. // 分母
  376. decimalN := decimal.NewFromInt(int64(nValue))
  377. //获取后面的预测数据
  378. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  379. for k, currentDate := range dayList {
  380. tmpIndex := lenAllData + k - 1 //上1期的值
  381. // 数据集合中的最后一个数据
  382. tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
  383. for tmpK := 2; tmpK <= nValue; tmpK++ {
  384. tmpIndex2 := tmpIndex - tmpK //上N期的值
  385. tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
  386. tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
  387. }
  388. // N期移动均值计算
  389. val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64()
  390. currentDateStr := currentDate.Format(utils.FormatDate)
  391. tmpData := &EdbInfoSearchData{
  392. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  393. DataTime: currentDateStr,
  394. Value: val,
  395. }
  396. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  397. allDataList = append(allDataList, tmpData)
  398. existMap[currentDateStr] = val
  399. // 最大最小值
  400. if val < minValue {
  401. minValue = val
  402. }
  403. if val > maxValue {
  404. maxValue = val
  405. }
  406. }
  407. return
  408. }
  409. // GetChartPredictEdbInfoDataListByRuleNLinearRegression 根据N期移动均值规则获取预测数据
  410. // 2.6N期段线性外推值:给出过去N期值所确定的线性回归方程(Y=aX+b)在未来一段时间内的推算值。回归方程虽然比较复杂,但各种编程语言应该都有现成的模块或函数,应该无需自己编写。
  411. // 例1:过去5期值(N=5)分别为:3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:13,15,17。
  412. //
  413. // 例2:过去6期值(N=6)分别为:3,3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:12.33,14.05,15.76。例1和例2的区别在于,多加了一期数据,导致回归方程发生改变,从而预测值不同。
  414. func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
  415. //var errMsg string
  416. //defer func() {
  417. // if errMsg != `` {
  418. // go alarm_msg.SendAlarmMsg("更新上海的token失败;ERR:"+err.Error(), 3)
  419. // }
  420. //}()
  421. allDataList := make([]*EdbInfoSearchData, 0)
  422. allDataList = append(allDataList, realPredictEdbInfoData...)
  423. allDataList = append(allDataList, predictEdbInfoData...)
  424. newPredictEdbInfoData = predictEdbInfoData
  425. lenAllData := len(allDataList)
  426. if lenAllData < nValue || lenAllData <= 0 {
  427. return
  428. }
  429. if nValue <= 1 {
  430. return
  431. }
  432. //获取后面的预测数据
  433. // 获取线性方程公式的a、b的值
  434. coordinateData := make([]Coordinate, 0)
  435. for tmpK := nValue; tmpK > 0; tmpK-- {
  436. tmpIndex2 := lenAllData - tmpK //上N期的值
  437. tmpCoordinate := Coordinate{
  438. X: float64(nValue - tmpK + 1),
  439. Y: allDataList[tmpIndex2].Value,
  440. }
  441. coordinateData = append(coordinateData, tmpCoordinate)
  442. }
  443. a, b := getLinearResult(coordinateData)
  444. if math.IsNaN(a) || math.IsNaN(b) {
  445. err = errors.New("线性方程公式生成失败")
  446. return
  447. }
  448. //fmt.Println("a:", a, ";======b:", b)
  449. aDecimal := decimal.NewFromFloat(a)
  450. bDecimal := decimal.NewFromFloat(b)
  451. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  452. for k, currentDate := range dayList {
  453. tmpK := nValue + k + 1
  454. xDecimal := decimal.NewFromInt(int64(tmpK))
  455. val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
  456. currentDateStr := currentDate.Format(utils.FormatDate)
  457. tmpData := &EdbInfoSearchData{
  458. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  459. DataTime: currentDateStr,
  460. Value: val,
  461. }
  462. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  463. allDataList = append(allDataList, tmpData)
  464. existMap[currentDateStr] = val
  465. // 最大最小值
  466. if val < minValue {
  467. minValue = val
  468. }
  469. if val > maxValue {
  470. maxValue = val
  471. }
  472. }
  473. return
  474. }
  475. // Series is a container for a series of data
  476. type Series []Coordinate
  477. // Coordinate holds the data in a series
  478. type Coordinate struct {
  479. X, Y float64
  480. }
  481. func getLinearResult(s []Coordinate) (gradient, intercept float64) {
  482. if len(s) <= 1 {
  483. return
  484. }
  485. // Placeholder for the math to be done
  486. var sum [5]float64
  487. // Loop over data keeping index in place
  488. i := 0
  489. for ; i < len(s); i++ {
  490. sum[0] += s[i].X
  491. sum[1] += s[i].Y
  492. sum[2] += s[i].X * s[i].X
  493. sum[3] += s[i].X * s[i].Y
  494. sum[4] += s[i].Y * s[i].Y
  495. }
  496. // Find gradient and intercept
  497. f := float64(i)
  498. gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
  499. intercept = (sum[1] / f) - (gradient * sum[0] / f)
  500. //fmt.Println("gradient:", gradient, ";intercept:", intercept)
  501. // Create the new regression series
  502. //for j := 0; j < len(s); j++ {
  503. // regressions = append(regressions, Coordinate{
  504. // X: s[j].X,
  505. // Y: s[j].X*gradient + intercept,
  506. // })
  507. //}
  508. return
  509. }
  510. // GetChartPredictEdbInfoDataListByRuleTrendsHC 根据动态环比增加值的计算规则获取预测数据
  511. //
  512. // 研究员有对预测指标进行动态环差计算的需求,即预测指标使用环差规则进行预测时,环比增加值不是固定值,而是由几个预测指标计算得出的动态变化的值;
  513. // 需求说明:
  514. // 1、增加“动态环差”预测规则;
  515. // 2、环比增加值在弹窗设置;
  516. // 3、动态环差预测举例:
  517. // 指标A实际最新数据为2022-10-27(100);
  518. // 预测指标B预测数据为2022-10-28(240)、2022-10-29(300);
  519. // 预测指标C预测数据为2022-10-28(260)、2022-10-29(310);
  520. // 计算公式为B-C;
  521. // 则指标A至2022-10-29的预测值为2022-10-28(100+(240-260)=80)、2022-10-29(80+(300-310)=90);
  522. // 注:动态环比增加值的计算遵从计算指标的计算规则,即用于计算的指标若有部分指标缺少部分日期数据,则这部分日期数据不做计算,为空;若动态环比增加值某一天为空,则往前追溯最近一期有值的环比增加值作为该天的数值参与计算;
  523. func GetChartPredictEdbInfoDataListByRuleTrendsHC(edbInfoId, configId int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  524. allDataList := make([]*EdbInfoSearchData, 0)
  525. allDataList = append(allDataList, realPredictEdbInfoData...)
  526. allDataList = append(allDataList, predictEdbInfoData...)
  527. newPredictEdbInfoData = predictEdbInfoData
  528. lenAllData := len(allDataList)
  529. if lenAllData <= 0 {
  530. return
  531. }
  532. hcDataMap := make(map[string]float64) //规则计算的环差值map
  533. tmpPredictEdbRuleDataList, err := GetPredictEdbRuleDataItemList(edbInfoId, configId, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
  534. if err != nil {
  535. return
  536. }
  537. for _, v := range tmpPredictEdbRuleDataList {
  538. hcDataMap[v.DataTime] = v.Value
  539. }
  540. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  541. for k, currentDate := range dayList {
  542. // 最近一条数据
  543. tmpLenAllDataList := len(allDataList)
  544. lastValue := allDataList[tmpLenAllDataList-1].Value
  545. // 动态环差值数据
  546. currentDateStr := currentDate.Format(utils.FormatDate)
  547. hcVal, ok := hcDataMap[currentDateStr]
  548. if !ok {
  549. continue
  550. }
  551. lastValueDecimal := decimal.NewFromFloat(lastValue)
  552. hcValDecimal := decimal.NewFromFloat(hcVal)
  553. val, _ := lastValueDecimal.Add(hcValDecimal).RoundCeil(4).Float64()
  554. tmpData := &EdbInfoSearchData{
  555. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  556. //EdbInfoId: edbInfoId,
  557. DataTime: currentDateStr,
  558. Value: val,
  559. //DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
  560. }
  561. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  562. allDataList = append(allDataList, tmpData)
  563. existMap[currentDateStr] = val
  564. // 最大最小值
  565. if val < minValue {
  566. minValue = val
  567. }
  568. if val > maxValue {
  569. maxValue = val
  570. }
  571. }
  572. return
  573. }
  574. // GetChartPredictEdbInfoDataListByRuleFinalValueHc 根据 给定终值后插值 规则获取预测数据
  575. //
  576. // 项目背景:
  577. // 假设螺纹产量在2023年1月1号的预测值是255万吨,从当下到2023年1月1号,螺纹产量将会线性变化,那么每一期的螺纹产量是多少?
  578. // 算法:从当下(2022/10/28)到2023/1/1号,一共65天,从当前值(305.02)到255,差值-50.02,
  579. // 则每日环差为-50.02/65=-0.7695。因为数据点是周度频率,每周环差为,-0.3849*7=-5.3868。
  580. // 从以上计算过程可看出,“给定终值后差值”的算法,是在“环差”算法的基础上,做的一个改动。即这个”环差值”=【(终值-最新值)/终值与最新值得日期差】*数据频率
  581. // 需求说明:
  582. // 1、增加一个预测规则,名为“给定终值后插值”,给定预测截止日期和预测终值,计算最新数据日期至预测截止日期的时间差T,计算最新数据和预测终值的数据差S,数据频率与指标频度有关,日度=1,周度=7,旬度=10,月度=30,季度=90,年度=365,环差值=S/T*频率,预测数值=前一天数值+环差值;
  583. // 2、最新数据值和日期改动后,需重新计算环差值和预测数值;
  584. func GetChartPredictEdbInfoDataListByRuleFinalValueHc(edbInfoId int, finalValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
  585. allDataList := make([]*EdbInfoSearchData, 0)
  586. allDataList = append(allDataList, realPredictEdbInfoData...)
  587. allDataList = append(allDataList, predictEdbInfoData...)
  588. newPredictEdbInfoData = predictEdbInfoData
  589. index := len(allDataList)
  590. //获取后面的预测日期
  591. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  592. lenDay := len(dayList)
  593. if lenDay <= 0 {
  594. return
  595. }
  596. var hcValue float64
  597. lastValueDeciamal := decimal.NewFromFloat(allDataList[index-1].Value) // 实际数据的最后一个值
  598. finalValueDeciamal := decimal.NewFromFloat(finalValue) // 给定的终止数据
  599. dayDecimal := decimal.NewFromInt(int64(lenDay)) // 需要作为分母的期数
  600. hcValue, _ = finalValueDeciamal.Sub(lastValueDeciamal).Div(dayDecimal).Float64() // 计算出来的环差值
  601. //获取后面的预测数据
  602. predictEdbInfoData = make([]*EdbInfoSearchData, 0)
  603. lastK := lenDay - 1 // 最后的日期
  604. for k, currentDate := range dayList {
  605. tmpK := index + k - 1 //上1期的值
  606. var val float64
  607. // 环差别值计算
  608. if k == lastK { //如果是最后一天,那么就用最终值,否则就计算
  609. val = finalValue
  610. } else {
  611. val = PredictHczDiv(allDataList[tmpK].Value, hcValue)
  612. }
  613. currentDateStr := currentDate.Format(utils.FormatDate)
  614. tmpData := &EdbInfoSearchData{
  615. EdbDataId: edbInfoId + 10000000000 + index + k,
  616. //EdbInfoId: edbInfoId,
  617. DataTime: currentDateStr,
  618. Value: val,
  619. //DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
  620. }
  621. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  622. allDataList = append(allDataList, tmpData)
  623. existMap[currentDateStr] = val
  624. // 最大最小值
  625. if val < minValue {
  626. minValue = val
  627. }
  628. if val > maxValue {
  629. maxValue = val
  630. }
  631. }
  632. return
  633. }
  634. // SeasonConf 季节性规则的配置
  635. type SeasonConf struct {
  636. Calendar string `description:"公历、农历"`
  637. YearType int `description:"选择方式,1:连续N年;2:指定年份"`
  638. NValue int `description:"连续N年"`
  639. YearList []int `description:"指定年份列表"`
  640. }
  641. // GetChartPredictEdbInfoDataListByRuleSeason 根据 季节性 规则获取预测数据
  642. //
  643. // ETA预测规则:季节性
  644. // 已知选定指标A最近更新日期: 2022-12-6 200
  645. // 设置预测截止日期2023-01-06
  646. // 1、选择过去N年,N=3
  647. // 则过去N年为2021、2020、2019
  648. // 指标A日期 实际值 指标A日期
  649. // 2019/12/5 150 2019/12/6
  650. // 2020/12/5 180 2020/12/6
  651. // 2021/12/5 210 2021/12/6
  652. // 2019/12/31 200 2020/1/1
  653. // 2020/12/31 210 2021/1/1
  654. // 2021/12/31 250 2022/1/1
  655. //
  656. // 计算12.7预测值,求过去N年环差均值=[(100-150)+(160-180)+(250-210)]/3=-10
  657. // 则12.7预测值=12.6值+过去N年环差均值=200-10=190
  658. // 以此类推...
  659. //
  660. // 计算2023.1.2预测值,求过去N年环差均值=[(300-200)+(220-210)+(260-250)]/3=40
  661. // 则2023.1.2预测值=2023.1.1值+过去N年环差均值
  662. func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, yearsList []int, calendar string, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
  663. allDataList := make([]*EdbInfoSearchData, 0)
  664. allDataList = append(allDataList, realPredictEdbInfoData...)
  665. allDataList = append(allDataList, predictEdbInfoData...)
  666. newPredictEdbInfoData = predictEdbInfoData
  667. // 插值法数据处理
  668. handleDataMap := make(map[string]float64)
  669. err = HandleDataByLinearRegression(allDataList, handleDataMap)
  670. if err != nil {
  671. return
  672. }
  673. // 获取每个年份的日期数据需要平移的天数
  674. moveDayMap := make(map[int]int, 0) // 每个年份的春节公历
  675. {
  676. if calendar == "公历" {
  677. for _, year := range yearsList {
  678. moveDayMap[year] = 0 //公历就不平移了
  679. }
  680. } else {
  681. currentDay := time.Now()
  682. if currentDay.Month() >= 11 { //如果大于等于11月份,那么用的是下一年的春节
  683. currentDay = currentDay.AddDate(1, 0, 0)
  684. }
  685. currentYear := currentDay.Year()
  686. currentYearCjnl := fmt.Sprintf("%d-01-01", currentYear) //当年的春节农历
  687. currentYearCjgl := solarlunar.LunarToSolar(currentYearCjnl, false) //当年的春节公历
  688. currentYearCjglTime, tmpErr := time.ParseInLocation(utils.FormatDate, currentYearCjgl, time.Local)
  689. if tmpErr != nil {
  690. err = errors.New("当前春节公历日期转换失败:" + tmpErr.Error())
  691. return
  692. }
  693. // 指定的年份
  694. for _, year := range yearsList {
  695. tmpYearCjnl := fmt.Sprintf("%d-01-01", year) //指定年的春节农历
  696. tmpYearCjgl := solarlunar.LunarToSolar(tmpYearCjnl, false) //指定年的春节公历
  697. //moveDayList = append(moveDayList, 0) //公历就不平移了
  698. tmpYearCjglTime, tmpErr := time.ParseInLocation(utils.FormatDate, tmpYearCjgl, time.Local)
  699. if tmpErr != nil {
  700. err = errors.New(fmt.Sprintf("%d公历日期转换失败:%s", year, tmpErr.Error()))
  701. return
  702. }
  703. tmpCurrentYearCjglTime := currentYearCjglTime.AddDate(year-currentYear, 0, 0)
  704. moveDay := utils.GetTimeSubDay(tmpYearCjglTime, tmpCurrentYearCjglTime)
  705. moveDayMap[year] = moveDay //公历平移
  706. }
  707. }
  708. }
  709. index := len(allDataList)
  710. //获取后面的预测日期
  711. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  712. //获取后面的预测数据
  713. predictEdbInfoData = make([]*EdbInfoSearchData, 0)
  714. for k, currentDate := range dayList {
  715. // 如果遇到闰二月,如2.29,去掉该天数据
  716. if strings.Contains(currentDate.Format(utils.FormatDate), "02-29") {
  717. continue
  718. }
  719. tmpHistoryVal := decimal.NewFromFloat(0) //往期的差值总和
  720. tmpHistoryValNum := 0 // 往期差值计算的数量
  721. tmpLenAllDataList := len(allDataList)
  722. tmpK := tmpLenAllDataList - 1 //上1期数据的下标
  723. lastDayData := allDataList[tmpK] // 上1期的数据
  724. lastDayStr := lastDayData.DataTime
  725. lastDayVal := lastDayData.Value
  726. lastDay, tmpErr := time.ParseInLocation(utils.FormatDate, lastDayStr, time.Local)
  727. if tmpErr != nil {
  728. err = errors.New("获取上期日期转换失败:" + tmpErr.Error())
  729. }
  730. for _, year := range yearsList {
  731. moveDay := moveDayMap[year] //需要移动的天数
  732. var tmpHistoryCurrentVal, tmpHistoryLastVal float64
  733. var isFindHistoryCurrent, isFindHistoryLast bool //是否找到前几年的数据
  734. //前几年当日的日期
  735. tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, -moveDay)
  736. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  737. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
  738. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  739. tmpHistoryCurrentVal = val
  740. isFindHistoryCurrent = true
  741. break
  742. } else {
  743. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, -i)
  744. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  745. tmpHistoryCurrentVal = val
  746. isFindHistoryCurrent = true
  747. break
  748. }
  749. }
  750. }
  751. //前几年上一期的日期
  752. tmpHistoryLastDate := lastDay.AddDate(year-lastDay.Year(), 0, -moveDay)
  753. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  754. tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
  755. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  756. tmpHistoryLastVal = val
  757. isFindHistoryLast = true
  758. break
  759. } else {
  760. tmpDate := tmpHistoryLastDate.AddDate(0, 0, -i)
  761. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  762. tmpHistoryLastVal = val
  763. isFindHistoryLast = true
  764. break
  765. }
  766. }
  767. }
  768. // 如果两个日期对应的数据都找到了,那么计算两期的差值
  769. if isFindHistoryCurrent && isFindHistoryLast {
  770. af := decimal.NewFromFloat(tmpHistoryCurrentVal)
  771. bf := decimal.NewFromFloat(tmpHistoryLastVal)
  772. tmpHistoryVal = tmpHistoryVal.Add(af.Sub(bf))
  773. tmpHistoryValNum++
  774. }
  775. }
  776. //计算的差值与选择的年份数量不一致,那么当前日期不计算
  777. if tmpHistoryValNum != len(yearsList) {
  778. continue
  779. }
  780. lastDayValDec := decimal.NewFromFloat(lastDayVal)
  781. val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).Add(lastDayValDec).RoundCeil(4).Float64()
  782. currentDateStr := currentDate.Format(utils.FormatDate)
  783. tmpData := &EdbInfoSearchData{
  784. EdbDataId: edbInfoId + 10000000000 + index + k,
  785. //EdbInfoId: edbInfoId,
  786. DataTime: currentDateStr,
  787. Value: val,
  788. //DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
  789. }
  790. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  791. allDataList = append(allDataList, tmpData)
  792. existMap[currentDateStr] = val
  793. // 继续使用插值法补充新预测日期的数据之间的值
  794. err = HandleDataByLinearRegression([]*EdbInfoSearchData{
  795. lastDayData, tmpData,
  796. }, handleDataMap)
  797. if err != nil {
  798. return
  799. }
  800. // 最大最小值
  801. if val < minValue {
  802. minValue = val
  803. }
  804. if val > maxValue {
  805. maxValue = val
  806. }
  807. }
  808. return
  809. }
  810. // MoveAverageConf 移动平均同比规则的配置
  811. type MoveAverageConf struct {
  812. Year int `description:"指定年份"`
  813. NValue int `description:"N期的数据"`
  814. }
  815. // GetChartPredictEdbInfoDataListByRuleMoveAverageTb 根据 移动平均同比 规则获取预测数据
  816. //
  817. // ETA预测规则:季节性
  818. // 2、选择指定N年,N=3
  819. // 指定N年为2012、2015、2018
  820. // 指标A日期 实际值 指标A日期 实际值
  821. // 2012/12/5 150 2012/12/6 130
  822. // 2015/12/5 180 2015/12/6 150
  823. // 2018/12/5 210 2018/12/6 260
  824. // 2012/12/31 200 2013/1/1 200
  825. // 2015/12/31 210 2016/1/1 250
  826. // 2018/12/31 250 2019/1/1 270
  827. // 计算12.7预测值,求过去N年环差均值=[(130-150)+(150-180)+(290-210)]/3=10
  828. // 则12.7预测值=12.6值+过去N年环差均值=200+10=210
  829. // 以此类推...
  830. // 计算2023.1.2预测值,求过去N年环差均值=[(200-200)+(250-210)+(270-250)]/3=16.67
  831. // 则2023.1.2预测值=2023.1.1值+过去N年环差均值
  832. func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, year int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64, err error) {
  833. allDataList := make([]*EdbInfoSearchData, 0)
  834. allDataList = append(allDataList, realPredictEdbInfoData...)
  835. allDataList = append(allDataList, predictEdbInfoData...)
  836. newPredictEdbInfoData = predictEdbInfoData
  837. lenAllData := len(allDataList)
  838. if lenAllData < nValue || lenAllData <= 0 {
  839. return
  840. }
  841. if nValue <= 0 {
  842. return
  843. }
  844. // 分母
  845. decimalN := decimal.NewFromInt(int64(nValue))
  846. //获取后面的预测数据
  847. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  848. for k, currentDate := range dayList {
  849. tmpLenAllDataList := len(allDataList)
  850. tmpIndex := tmpLenAllDataList - 1 //上1期数据的下标
  851. averageDateList := make([]string, 0) //计算平均数的日期
  852. // 数据集合中的最后一个数据
  853. tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
  854. averageDateList = append(averageDateList, allDataList[tmpIndex].DataTime)
  855. for tmpK := 2; tmpK <= nValue; tmpK++ {
  856. tmpIndex2 := tmpIndex - tmpK //上N期的值
  857. tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
  858. tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
  859. averageDateList = append(averageDateList, allDataList[tmpIndex2].DataTime)
  860. }
  861. // 最近的N期平均值
  862. tmpAverageVal := tmpDecimalVal.Div(decimalN)
  863. var tmpHistoryCurrentVal float64 // 前几年当日的数据值
  864. var isFindHistoryCurrent, isFindHistoryLast bool //是否找到前几年的数据
  865. tmpHistoryDecimalVal := decimal.NewFromFloat(0) //前几年N期数据总值
  866. {
  867. // 前几年N期汇总期数
  868. tmpHistoryValNum := 0
  869. {
  870. //前几年当日的日期
  871. tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, 0)
  872. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  873. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
  874. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  875. tmpHistoryCurrentVal = val
  876. isFindHistoryCurrent = true
  877. break
  878. } else {
  879. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, -i)
  880. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  881. tmpHistoryCurrentVal = val
  882. isFindHistoryCurrent = true
  883. break
  884. }
  885. }
  886. }
  887. }
  888. for _, averageDate := range averageDateList {
  889. lastDay, tmpErr := time.ParseInLocation(utils.FormatDate, averageDate, time.Local)
  890. if tmpErr != nil {
  891. err = tmpErr
  892. return
  893. }
  894. //前几年上一期的日期
  895. tmpHistoryLastDate := lastDay.AddDate(year-lastDay.Year(), 0, 0)
  896. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  897. tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
  898. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  899. tmpDecimalVal2 := decimal.NewFromFloat(val)
  900. tmpHistoryDecimalVal = tmpHistoryDecimalVal.Add(tmpDecimalVal2)
  901. tmpHistoryValNum++
  902. break
  903. } else {
  904. tmpDate := tmpHistoryLastDate.AddDate(0, 0, -i)
  905. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  906. tmpDecimalVal2 := decimal.NewFromFloat(val)
  907. tmpHistoryDecimalVal = tmpHistoryDecimalVal.Add(tmpDecimalVal2)
  908. tmpHistoryValNum++
  909. break
  910. }
  911. }
  912. }
  913. }
  914. // 汇总期数与配置的N期数量一致
  915. if tmpHistoryValNum == nValue {
  916. isFindHistoryLast = true
  917. }
  918. }
  919. // 如果没有找到前几年的汇总数据,或者没有找到前几年当日的数据,那么退出当前循环,进入下一循环
  920. if !isFindHistoryLast || !isFindHistoryCurrent {
  921. continue
  922. }
  923. // 计算最近N期同比值
  924. tbVal := tmpAverageVal.Div(tmpHistoryDecimalVal)
  925. // 预测值结果 = 同比年份同期值(tmpHistoryCurrentVal的值)* 同比值(tbVal的值)
  926. val, _ := decimal.NewFromFloat(tmpHistoryCurrentVal).Mul(tbVal).RoundCeil(4).Float64()
  927. currentDateStr := currentDate.Format(utils.FormatDate)
  928. tmpData := &EdbInfoSearchData{
  929. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  930. //EdbInfoId: edbInfoId,
  931. DataTime: currentDateStr,
  932. Value: val,
  933. //DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
  934. }
  935. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  936. allDataList = append(allDataList, tmpData)
  937. existMap[currentDateStr] = val
  938. // 最大最小值
  939. if val < minValue {
  940. minValue = val
  941. }
  942. if val > maxValue {
  943. maxValue = val
  944. }
  945. }
  946. return
  947. }