profit_chart_info.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. package logic
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "eta/eta_index_lib/models"
  6. "eta/eta_index_lib/models/future_good"
  7. "eta/eta_index_lib/utils"
  8. "fmt"
  9. "github.com/dengsgo/math-engine/engine"
  10. "github.com/shopspring/decimal"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. // ChartInfoDateReq 图表的日期数据(日期相关)
  17. type ChartInfoDateReq struct {
  18. Type int `description:"配置类型"`
  19. Date string `description:"固定日期"`
  20. Value int `description:"N天的值"`
  21. Color string `description:"颜色"`
  22. Name string `description:"别名"`
  23. }
  24. // SectionScatterReq 截面散点请求
  25. type SectionScatterReq struct {
  26. XName string `description:"x轴名称"`
  27. XNameEn string `description:"x轴名称(英文)"`
  28. XUnitName string `description:"x轴单位名称"`
  29. XUnitNameEn string `description:"x轴单位名称(英文)"`
  30. YName string `description:"y轴名称"`
  31. YNameEn string `description:"y轴名称(英文)"`
  32. YUnitName string `description:"y轴单位名称"`
  33. YUnitNameEn string `description:"y轴单位名称(英文)"`
  34. XMinValue string `description:"X轴的最小值"`
  35. XMaxValue string `description:"X轴的最大值"`
  36. YMinValue string `description:"Y轴的最小值"`
  37. YMaxValue string `description:"Y轴的最大值"`
  38. //EdbList []SectionScatterEdbItemReq `description:"指标数据"`
  39. SeriesList []SectionScatterSeriesItemReq `description:"系列数据"`
  40. }
  41. // SectionScatterSeriesItemReq 系列的请求
  42. type SectionScatterSeriesItemReq struct {
  43. Name string `description:"系列名"`
  44. NameEn string `description:"系列名(英文名)"`
  45. Color string `description:"颜色"`
  46. EdbInfoList []SectionScatterEdbItemReq
  47. ShowTrendLine bool `description:"是否展示趋势线"`
  48. ShowFitEquation bool `description:"是否展示方程式"`
  49. ShowRSquare bool `description:"是否展示R平方"`
  50. }
  51. // SectionScatterEdbItemReq 截面散点请求的指标
  52. type SectionScatterEdbItemReq struct {
  53. XEdbInfoId int `description:"X轴的指标ID"`
  54. YEdbInfoId int `description:"Y轴的指标ID"`
  55. Name string `description:"别名"`
  56. NameEn string `description:"英文别名"`
  57. XDateType int `description:"X轴的日期配置类型"`
  58. XDate string `description:"X轴的日期固定日期"`
  59. XDateValue int `description:"X轴的日期N天的值"`
  60. YDateType int `description:"Y轴的日期配置类型"`
  61. YDate string `description:"Y轴的日期固定日期"`
  62. YDateValue int `description:"Y轴的日期N天的值"`
  63. IsShow bool `description:"是否展示"`
  64. }
  65. // XData 商品价格曲线的的x轴数据
  66. type XData struct {
  67. Name string `description:"别名"`
  68. NameEn string `description:"英文别名"`
  69. }
  70. // YData 柱方图的y轴数据
  71. type YData struct {
  72. Date string `description:"数据日期"`
  73. ConfigDate time.Time `description:"配置的日期" json:"-"`
  74. Color string `description:"数据颜色"`
  75. Name string `description:"别名"`
  76. NameEn string `description:"英文别名"`
  77. Value []float64 `description:"每个指标的值"`
  78. NoDataEdbList []int `description:"没有数据的指标列表"`
  79. XEdbInfoIdList []int `description:"对应X轴的指标id列表"`
  80. NameList []string `description:"每个值对应的名称"`
  81. EnNameList []string `description:"每个值对应的英文名称"`
  82. EdbValMap map[int]float64 `description:"指标与值的对应" json:"-"`
  83. M []int `description:"对应开始日期的间隔值" json:"-"`
  84. }
  85. // BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
  86. type BarChartInfoEdbItemReq struct {
  87. EdbInfoId int `description:"指标ID"`
  88. Name string `description:"别名"`
  89. NameEn string `description:"英文别名"`
  90. Source int `description:"1:ETA图库;2:商品价格"`
  91. }
  92. // ChartInfoReq 图表预览请求数据
  93. type ChartInfoReq struct {
  94. FutureGoodEdbInfoIdList []models.EdbInfoFromTag `description:"指标信息"`
  95. CalculateFormula string `description:"计算公式"`
  96. BaseEdbInfoId int `description:"基础的指标id"`
  97. DateList []ChartInfoDateReq `description:"日期配置"`
  98. ProfitNameEn string `description:"利润英文名称"`
  99. EdbInfoIdList []int `description:"现货指标ID列表"`
  100. XDataList []XData `description:"横轴配置"`
  101. }
  102. // RefreshByChartId 根据图表id刷新图表
  103. func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
  104. // 查找图表
  105. chartInfo, err := models.GetChartInfoById(chartInfoId)
  106. if err != nil {
  107. if err.Error() == utils.ErrNoRow() {
  108. err = nil
  109. }
  110. return
  111. }
  112. if chartInfo.Source != utils.CHART_SOURCE_FUTURE_GOOD_PROFIT {
  113. return
  114. }
  115. var extraConf ChartInfoReq
  116. err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConf)
  117. if err != nil {
  118. errMsg = "商品利润曲线图配置异常"
  119. return
  120. }
  121. // 查找商品利润图表的扩展信息
  122. chartInfoFutureGoodProfit := new(future_good.ChartInfoFutureGoodProfit)
  123. if err = chartInfoFutureGoodProfit.GetItemById(chartInfoId); err != nil {
  124. errMsg = "获取失败"
  125. if err.Error() == utils.ErrNoRow() {
  126. err = nil
  127. }
  128. return
  129. }
  130. if len(extraConf.EdbInfoIdList) == 0 {
  131. extraConf.EdbInfoIdList = append(extraConf.EdbInfoIdList, extraConf.BaseEdbInfoId)
  132. }
  133. baseEdbInfo := new(models.EdbInfo)
  134. // ETA指标
  135. edbInfoListTmp, err := models.GetEdbInfoByIdList(extraConf.EdbInfoIdList)
  136. if err != nil {
  137. errMsg = "获取失败"
  138. return
  139. }
  140. //按照请求顺序排序
  141. edbInfoMap := make(map[int]*models.EdbInfo)
  142. for _, v := range edbInfoListTmp {
  143. edbInfoMap[v.EdbInfoId] = v
  144. }
  145. edbInfoList := make([]*models.EdbInfo, 0)
  146. for _, v := range extraConf.EdbInfoIdList {
  147. edbInfoList = append(edbInfoList, edbInfoMap[v])
  148. }
  149. edbInfoListMap := make(map[int]*models.EdbInfo)
  150. for k, v := range edbInfoList {
  151. edbInfoList[k].EdbNameSource = v.EdbName
  152. edbInfoListMap[v.EdbInfoId] = v
  153. if v.EdbInfoId == extraConf.BaseEdbInfoId {
  154. baseEdbInfo = v
  155. }
  156. }
  157. // 商品数据库指标
  158. futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
  159. zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
  160. for _, v := range extraConf.FutureGoodEdbInfoIdList {
  161. if _, ok := futureGoodEdbInfoMap[v.EdbInfoId]; ok {
  162. continue
  163. }
  164. zlFutureGoodEdbInfo, tmpErr := future_good.GetFutureGoodEdbInfo(v.EdbInfoId)
  165. if tmpErr != nil {
  166. err = tmpErr
  167. errMsg = "获取失败"
  168. return
  169. }
  170. futureGoodEdbInfoMap[v.EdbInfoId] = zlFutureGoodEdbInfo
  171. zlFutureGoodEdbInfoList = append(zlFutureGoodEdbInfoList, zlFutureGoodEdbInfo)
  172. }
  173. xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, edbInfoList, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList, extraConf.XDataList)
  174. xDataListByte, err := json.Marshal(xDataList)
  175. if err != nil {
  176. errMsg = "保存失败"
  177. err = errors.New("X轴数据转换失败,ERR:" + err.Error())
  178. return
  179. }
  180. yDataListByte, err := json.Marshal(yDataList)
  181. if err != nil {
  182. errMsg = "保存失败"
  183. err = errors.New("Y轴数据转换失败,ERR:" + err.Error())
  184. return
  185. }
  186. extraUpdateCol := make([]string, 0)
  187. chartInfoFutureGoodProfit.XValue = string(xDataListByte)
  188. chartInfoFutureGoodProfit.YValue = string(yDataListByte)
  189. //chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润"
  190. chartInfoFutureGoodProfit.ModifyTime = time.Now()
  191. extraUpdateCol = []string{"XValue", "YValue", "ProfitName", "ModifyTime"}
  192. err = chartInfoFutureGoodProfit.Update(extraUpdateCol)
  193. return
  194. }
  195. // GetProfitChartEdbData 获取利润图表的指标数据
  196. 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) {
  197. if baseEdbInfo == nil {
  198. err = errors.New("ETA指标未选取")
  199. return
  200. }
  201. if len(edbInfoList) == 0 {
  202. edbInfoList = append(edbInfoList, baseEdbInfo)
  203. }
  204. if len(zlFutureGoodEdbInfoList) <= 0 {
  205. err = errors.New("商品指标未选取")
  206. return
  207. }
  208. // 标签与期货商品指标的关联关系
  209. tagEdbIdMap := make(map[string]int)
  210. // 有效的期货商品指标
  211. futureGoodEdbInfoIdMap := make(map[int]int)
  212. {
  213. tmpTagEdbIdMap := make(map[string]int)
  214. for _, v := range edbInfoFromTagList {
  215. tmpTagEdbIdMap[v.FromTag] = v.EdbInfoId
  216. }
  217. formulaMap := CheckFormula(formulaStr)
  218. for _, tag := range formulaMap {
  219. tagEdbIdMap[tag] = tmpTagEdbIdMap[tag]
  220. futureGoodEdbInfoIdMap[tmpTagEdbIdMap[tag]] = tmpTagEdbIdMap[tag]
  221. }
  222. }
  223. // 指标对应的所有数据
  224. //edbDataListMap := make(map[int][]*models.EdbDataList)
  225. // 普通的指标数据
  226. baseDataListMap := make(map[int][]*models.EdbDataList)
  227. for _, v := range edbInfoList {
  228. baseDataList := make([]*models.EdbDataList, 0)
  229. baseDataList, err = models.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "")
  230. if err != nil {
  231. return
  232. }
  233. baseDataListMap[v.EdbInfoId] = baseDataList
  234. }
  235. latestDate := zlFutureGoodEdbInfoList[0].EndDate
  236. latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
  237. earliestDateTime := latestDateTime // 数据的最早日期,目的是为了找出最早的合约
  238. for _, barChartInfoDate := range chartInfoDateList {
  239. var findDateTime time.Time
  240. switch barChartInfoDate.Type {
  241. case 1: //最新值
  242. findDateTime = latestDateTime
  243. case 2: //近期几天
  244. findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
  245. case 3: // 固定日期
  246. //寻找固定日期的数据
  247. tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
  248. if tmpErr != nil {
  249. err = tmpErr
  250. return
  251. }
  252. findDateTime = tmpFindDateTime
  253. default:
  254. err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
  255. return
  256. }
  257. if findDateTime.IsZero() {
  258. err = errors.New("错误的日期")
  259. return
  260. }
  261. if findDateTime.Before(earliestDateTime) {
  262. earliestDateTime = findDateTime
  263. }
  264. }
  265. monthNum := (latestDateTime.Year()-earliestDateTime.Year())*12 + int(latestDateTime.Month()-earliestDateTime.Month())
  266. // 存储主力合约下的所有月份合约
  267. futureGoodEdbInfoDateMap := make(map[int]map[string]*future_good.FutureGoodEdbInfo)
  268. futureGoodDataListMap := make(map[int][]*models.EdbDataList, 0)
  269. // 特殊的商品期货合约(只有M+N的合约,没有固定日期的合约)
  270. specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0)
  271. isAllChina := true // 是否都是国内的期货合约
  272. for _, v := range zlFutureGoodEdbInfoList {
  273. if v.RegionType != "国内" {
  274. isAllChina = false
  275. break
  276. }
  277. }
  278. nMap := make(map[string]string)
  279. var maxN int // 最大N值
  280. for _, v := range zlFutureGoodEdbInfoList {
  281. // 如果不是有效的商品期货指标,那么就过滤掉,不做数据查询处理,避免没必要的请求
  282. if _, ok := futureGoodEdbInfoIdMap[v.FutureGoodEdbInfoId]; !ok {
  283. continue
  284. }
  285. // 获取期货指标以及期货数据
  286. tmpFutureGoodEdbInfoList, tmpErr := future_good.GetChildFutureGoodEdbInfoListByParentId(v.FutureGoodEdbInfoId)
  287. if tmpErr != nil {
  288. err = tmpErr
  289. return
  290. }
  291. childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
  292. if tmpErr != nil {
  293. err = tmpErr
  294. return
  295. }
  296. if maxN < tmpMaxN {
  297. maxN = tmpMaxN
  298. }
  299. futureGoodEdbInfoDateMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap
  300. if v.FutureGoodEdbType == 2 {
  301. specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = make(map[int]*future_good.FutureGoodEdbInfo)
  302. }
  303. // 获取数据
  304. for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
  305. nMap[date] = date
  306. dataList := make([]*models.EdbDataList, 0)
  307. tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
  308. if tmpErr != nil {
  309. return
  310. }
  311. for _, tmpData := range tmpDataList {
  312. dataList = append(dataList, &models.EdbDataList{
  313. EdbDataId: tmpData.FutureGoodEdbDataId,
  314. EdbInfoId: tmpData.FutureGoodEdbInfoId,
  315. DataTime: tmpData.DataTime.Format(utils.FormatDate),
  316. DataTimestamp: tmpData.DataTimestamp,
  317. Value: tmpData.Close,
  318. })
  319. }
  320. futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList
  321. if childFutureGoodEdbInfo.FutureGoodEdbType == 2 {
  322. specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo
  323. }
  324. }
  325. }
  326. // 找出所有的N值,并进行正序排列
  327. dateList := make([]string, 0)
  328. for _, n := range nMap {
  329. dateList = append(dateList, n)
  330. }
  331. sort.Slice(dateList, func(i, j int) bool {
  332. return dateList[i] < dateList[j]
  333. })
  334. var reqEdbInfoIds []int
  335. for _, v := range edbInfoList {
  336. reqEdbInfoIds = append(reqEdbInfoIds, v.EdbInfoId)
  337. tmp := XData{
  338. Name: v.EdbName,
  339. NameEn: v.EdbNameEn,
  340. }
  341. xDataList = append(xDataList, tmp)
  342. }
  343. var edbIdList []int
  344. futureGoodNameMap := make(map[int]map[int]string)
  345. edbIdList, yDataList, futureGoodNameMap, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN, reqEdbInfoIds)
  346. // todo 最后处理数据
  347. tmpXDataList, newYDataList, err := handleProfitResultData(xDataList, futureGoodNameMap, yDataList, earliestDateTime, edbIdList)
  348. if err != nil {
  349. return
  350. }
  351. if len(reqXDataList) == 0 {
  352. xDataList = tmpXDataList
  353. } else {
  354. xDataList = reqXDataList
  355. }
  356. yDataList = newYDataList
  357. return
  358. }
  359. // ProfitChartChartData 获取数据
  360. 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) {
  361. // 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
  362. //earliestDateTime time.Time
  363. // ETA指标数据
  364. allBaseEdbDateDataMap := make(map[int]map[string]float64)
  365. for edbInfoId, baseDataList := range baseDataListMap {
  366. baseEdbDateData := make(map[string]float64)
  367. for _, edbData := range baseDataList {
  368. baseEdbDateData[edbData.DataTime] = edbData.Value
  369. }
  370. allBaseEdbDateDataMap[edbInfoId] = baseEdbDateData
  371. }
  372. // 商品指标数据
  373. edbDataMap := make(map[int]map[string]float64)
  374. for edbInfoId, edbDataList := range futureGoodEdbDataListMap {
  375. edbDateData := make(map[string]float64)
  376. for _, edbData := range edbDataList {
  377. edbDateData[edbData.DataTime] = edbData.Value
  378. }
  379. edbDataMap[edbInfoId] = edbDateData
  380. }
  381. latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
  382. yDataList = make([]YData, 0) //y轴的数据列表
  383. // 将计算公式中的字母转大写
  384. formulaStr = strings.ToUpper(formulaStr)
  385. futureGoodNameMap = make(map[int]map[int]string)
  386. for tmpk, barChartInfoDate := range chartInfoDateList {
  387. yDataMap := make(map[int]float64)
  388. var maxDate time.Time
  389. var findDateTime time.Time
  390. switch barChartInfoDate.Type {
  391. case 1: //最新值
  392. findDateTime = latestDateTime
  393. case 2: //近期几天
  394. findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
  395. case 3: // 固定日期
  396. //寻找固定日期的数据
  397. tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
  398. if tmpErr != nil {
  399. err = tmpErr
  400. return
  401. }
  402. findDateTime = tmpFindDateTime
  403. default:
  404. err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
  405. return
  406. }
  407. if findDateTime.IsZero() {
  408. err = errors.New("错误的日期")
  409. return
  410. }
  411. findDataList := make([]float64, 0) // 当前日期的数据值
  412. noDataIdList := make([]int, 0) // 没有数据的指标id
  413. noDataIdMap := make(map[int]int, 0) // 没有数据的指标map
  414. xEdbInfoIdList := make([]int, 0) // 当前数据的指标id列表
  415. // 现货指标
  416. index := 0
  417. var realDateTime time.Time
  418. // 现货指标
  419. baseEdbDateData, ok := allBaseEdbDateDataMap[baseEdbInfo.EdbInfoId]
  420. if !ok {
  421. err = fmt.Errorf("指标id: %d 没有数据", baseEdbInfo.EdbInfoId)
  422. return
  423. }
  424. realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataListMap[baseEdbInfo.EdbInfoId], baseEdbDateData, edbDataMap)
  425. if tmpErr != nil {
  426. err = tmpErr
  427. return
  428. }
  429. if isFind {
  430. maxDate = realDateTime
  431. }
  432. edbIdList = make([]int, 0) //普通指标ID
  433. for _, edbInfoId := range reqEdbInfoIds {
  434. if edbInfoId == baseEdbInfo.EdbInfoId {
  435. findDataList = append(findDataList, findDataValue)
  436. yDataMap[index] = findDataValue
  437. xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
  438. edbIdList = append(edbIdList, edbInfoId)
  439. index += 1
  440. continue
  441. }
  442. baseEdbDateDataTmp, ok := allBaseEdbDateDataMap[edbInfoId]
  443. if !ok {
  444. err = fmt.Errorf("指标id: %d 没有数据", edbInfoId)
  445. return
  446. }
  447. findDataValueTmp, isFindTmp := baseEdbDateDataTmp[realDateTime.Format(utils.FormatDate)]
  448. if !isFindTmp {
  449. noDataIdList = append(noDataIdList, edbInfoId)
  450. noDataIdMap[edbInfoId] = edbInfoId
  451. }
  452. findDataList = append(findDataList, findDataValueTmp)
  453. yDataMap[index] = findDataValueTmp
  454. xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
  455. edbIdList = append(edbIdList, edbInfoId)
  456. index += 1
  457. }
  458. mList := make([]int, 0) // 间隔月份
  459. tmpNameMap := make(map[int]string)
  460. // 最小开始的n值
  461. //minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
  462. for _, date := range dateList {
  463. currDate, _ := time.ParseInLocation(utils.FormatYearMonthDate, date, time.Local)
  464. // 如果当前的n值小于最小开始的n值,那么就不处理
  465. //if n < minN {
  466. // continue
  467. //}
  468. //findDateTime
  469. // 获取当前日期相对开始日期的期数
  470. tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
  471. if tmpN <= 0 {
  472. continue
  473. }
  474. // 如果期数大于最大期数,那么就退出当前匹配
  475. if tmpN >= maxN {
  476. break
  477. }
  478. zlAndChildEdbId := make(map[int]int)
  479. childFutureGoodEdbInfoIdList := make([]int, 0)
  480. for zlFutureGoodEdbInfoId, futureGoodEdbInfoList := range futureGoodEdbInfoMap {
  481. // 判断是否特殊合约
  482. if childFutureGoodEdbInfoIdMap, ok := specialFutureGoodEdbInfoMap[zlFutureGoodEdbInfoId]; ok {
  483. if childFutureGoodEdbInfo, ok2 := childFutureGoodEdbInfoIdMap[tmpN]; ok2 {
  484. childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
  485. zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
  486. }
  487. } else {
  488. if childFutureGoodEdbInfo, ok2 := futureGoodEdbInfoList[date]; ok2 {
  489. childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
  490. zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
  491. }
  492. }
  493. }
  494. // 合约不全,不参与计算
  495. //if len(childFutureGoodEdbInfoIdList) != lenZlFutureGoodEdbInfo {
  496. // continue
  497. //}
  498. calculateMap := make(map[int]float64)
  499. for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList {
  500. tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][realDateTime.Format(utils.FormatDate)]
  501. if tmpIsFind && tmpFindDataValue != 0 {
  502. calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue
  503. }
  504. }
  505. // 合约的数据不全,不参与计算
  506. //if len(calculateMap) != lenZlFutureGoodEdbInfo {
  507. // continue
  508. //}
  509. newTagEdbIdMap := make(map[string]int)
  510. for tag, zlEdbId := range tagEdbIdMap {
  511. if tag == "A" {
  512. nameTmp := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbName, "(")
  513. nameTmpEn := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbNameEn, "(")
  514. if len(nameTmp) > 1 && len(nameTmpEn) > 1 {
  515. nameTmp[1] = strings.Trim(nameTmp[1], ")")
  516. nameTmpEn[1] = strings.Trim(nameTmpEn[1], ")")
  517. tmpNameMap[tmpN+1] = nameTmp[1] + "-" + nameTmpEn[1]
  518. }
  519. }
  520. newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId]
  521. }
  522. //, formulaStr string, tagEdbIdMap map[string]int
  523. formulaFormStr := ReplaceFormula(newTagEdbIdMap, calculateMap, formulaStr)
  524. //计算公式异常,那么就移除该指标
  525. if formulaFormStr == `` {
  526. //removeDateList = append(removeDateList, sk)
  527. fmt.Println("异常了")
  528. continue
  529. }
  530. calVal, e := engine.ParseAndExec(formulaFormStr)
  531. //calVal, err := calResult.Float64()
  532. if e != nil {
  533. err = errors.New("计算失败:获取计算值失败 Err:" + e.Error() + ";formulaStr:" + formulaFormStr)
  534. fmt.Println(err)
  535. return
  536. }
  537. //yDataMap[n] = calVal
  538. //xEdbInfoIdList = append(xEdbInfoIdList, n)
  539. calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
  540. yDataMap[tmpN] = calVal
  541. xEdbInfoIdList = append(xEdbInfoIdList, tmpN)
  542. findDataList = append(findDataList, calVal)
  543. }
  544. yName := barChartInfoDate.Name
  545. yNameEn := barChartInfoDate.Name
  546. if yName == `` {
  547. if barChartInfoDate.Type == 2 {
  548. yName = strconv.Itoa(barChartInfoDate.Value) + "天前"
  549. if barChartInfoDate.Value == 1 {
  550. yNameEn = strconv.Itoa(barChartInfoDate.Value) + "day ago"
  551. } else {
  552. yNameEn = strconv.Itoa(barChartInfoDate.Value) + " days ago"
  553. }
  554. } else {
  555. yName = maxDate.Format(utils.FormatDate)
  556. yNameEn = maxDate.Format(utils.FormatDate)
  557. }
  558. }
  559. yDate := "0000-00-00"
  560. if !maxDate.IsZero() {
  561. yDate = maxDate.Format(utils.FormatDate)
  562. }
  563. {
  564. hasDataIndexList := make([]int, 0)
  565. for dataK, edbInfoId := range xEdbInfoIdList {
  566. if _, ok := noDataIdMap[edbInfoId]; !ok { // 如果是没有数据的指标id
  567. hasDataIndexList = append(hasDataIndexList, dataK)
  568. }
  569. }
  570. lenHasDataIndex := len(hasDataIndexList)
  571. if lenHasDataIndex > 0 {
  572. for lenHasDataI := 1; lenHasDataI < lenHasDataIndex; lenHasDataI++ {
  573. perK := hasDataIndexList[lenHasDataI-1] //上一个有数据的指标下标
  574. currK := hasDataIndexList[lenHasDataI] //当前有数据的指标下标
  575. preVal := findDataList[perK] //上一个有数据的坐标的值
  576. currVal := findDataList[currK] //当前有数据的指标的值
  577. // 环差值
  578. hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(currK - perK)))
  579. var tmpI int64
  580. // 将两个中间的数据做平均值补全
  581. for hcI := perK + 1; hcI < currK; hcI++ {
  582. tmpI++
  583. findDataList[hcI], _ = decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(tmpI))).RoundCeil(4).Float64()
  584. }
  585. }
  586. }
  587. }
  588. futureGoodNameMap[tmpk] = tmpNameMap
  589. yDataList = append(yDataList, YData{
  590. Date: yDate,
  591. ConfigDate: realDateTime,
  592. Value: findDataList,
  593. NoDataEdbList: noDataIdList,
  594. XEdbInfoIdList: xEdbInfoIdList,
  595. Color: barChartInfoDate.Color,
  596. Name: yName,
  597. NameEn: yNameEn,
  598. EdbValMap: yDataMap,
  599. M: mList,
  600. })
  601. }
  602. return
  603. }
  604. // getFutureGoodEdbInfoList 获取适用的指标列表
  605. func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNum int) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
  606. maxN := 36 //最大36期合约
  607. futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
  608. futureGoodEdbInfoDateMap = make(map[string]*future_good.FutureGoodEdbInfo)
  609. earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
  610. if zlFutureGoodEdbInfo.RegionType == "国内" {
  611. startMonth := int(earliestDateTime.Month())
  612. if startMonth == 1 {
  613. futureGoodEdbInfoList = tmpFutureGoodEdbInfoList
  614. } else {
  615. // 因为是下标,所以对应想下标是需要-1的
  616. index := startMonth - 1
  617. futureGoodEdbInfoList = tmpFutureGoodEdbInfoList[index:]
  618. futureGoodEdbInfoList = append(futureGoodEdbInfoList, tmpFutureGoodEdbInfoList[:index]...)
  619. }
  620. lenFutureGoodEdbInfoList := len(futureGoodEdbInfoList)
  621. //futureGoodEdbInfoList
  622. //if isAllChina {
  623. //}
  624. // 如果全是国内指标,那么只需要拼上多出的几期合约即可
  625. maxN = lenFutureGoodEdbInfoList + monthNum
  626. for i := 1; i < maxN; i++ {
  627. k := i % lenFutureGoodEdbInfoList
  628. futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k]
  629. }
  630. //需求池604,只要是国内合约,最大必须是12期
  631. newMaxN = 12
  632. return
  633. }
  634. for _, v := range tmpFutureGoodEdbInfoList {
  635. //海外的连续日期,目前
  636. if v.FutureGoodEdbType == 2 {
  637. if v.Month <= maxN {
  638. futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, v.Month, 0).Format(utils.FormatYearMonthDate)] = v
  639. if v.Month > newMaxN {
  640. newMaxN = v.Month
  641. }
  642. }
  643. continue
  644. }
  645. if v.Year < earliestDateTime.Year() {
  646. continue
  647. }
  648. // 小于等于当前年,那么就肯定是ok的
  649. if v.Year == earliestDateTime.Year() && v.Month <= int(earliestDateTime.Month()) {
  650. continue
  651. }
  652. subYear := v.Year - earliestDateTime.Year()
  653. subMonth := v.Month - int(earliestDateTime.Month())
  654. // 如果(当前年-最新日期的年份) * 12个月 + (当前月-最新日期的月份) 小于总月份
  655. tmpN := subYear*12 + subMonth
  656. if tmpN < maxN {
  657. tmpDateTime := time.Date(v.Year, time.Month(v.Month), 0, 0, 0, 0, 0, time.Local)
  658. futureGoodEdbInfoDateMap[tmpDateTime.Format(utils.FormatYearMonthDate)] = v
  659. if tmpN > newMaxN {
  660. newMaxN = tmpN
  661. }
  662. continue
  663. }
  664. }
  665. return
  666. }
  667. // handleProfitResultData 处理成最终的结果数据
  668. func handleProfitResultData(xDataListInit []XData, futureNameMap map[int]map[int]string, yDataList []YData, earliestDateTime time.Time, allEdbInfoIds []int) (xDataList []XData, newYDataList []YData, err error) {
  669. newYDataList = yDataList
  670. xDataList = xDataListInit
  671. nMap := make(map[int]int)
  672. nList := make([]int, 0)
  673. nListEdbMap := make(map[int]struct{})
  674. for _, v := range yDataList {
  675. for _, n := range v.XEdbInfoIdList {
  676. if utils.InArrayByInt(allEdbInfoIds, n) {
  677. if _, ok := nListEdbMap[n]; !ok {
  678. nList = append(nList, n)
  679. nListEdbMap[n] = struct{}{}
  680. }
  681. } else {
  682. nMap[n] = n
  683. }
  684. }
  685. }
  686. // 找出所有的N值,并进行正序排列
  687. nListTmp := make([]int, 0)
  688. for _, n := range nMap {
  689. nListTmp = append(nListTmp, n)
  690. }
  691. sort.Slice(nListTmp, func(i, j int) bool {
  692. return nListTmp[i] < nListTmp[j]
  693. })
  694. nList = append(nList, nListTmp...)
  695. for _, n := range nList {
  696. if utils.InArrayByInt(allEdbInfoIds, n) {
  697. continue
  698. }
  699. xDataList = append(xDataList, XData{
  700. Name: fmt.Sprint("M+", n),
  701. NameEn: fmt.Sprint("M+", n),
  702. })
  703. }
  704. for yIndex, yData := range yDataList {
  705. newYDataList[yIndex].XEdbInfoIdList = []int{}
  706. newYDataList[yIndex].Value = []float64{}
  707. tmpNList := nList
  708. xEdbInfoIdList := yData.XEdbInfoIdList
  709. valIndex := 0
  710. needNum := 0
  711. for _, n := range tmpNList {
  712. if len(xEdbInfoIdList) > 0 {
  713. currN := xEdbInfoIdList[0]
  714. // 当前距离最早的日期相差的N数
  715. if n == currN { // todo 改成所有的基础现货指标
  716. if needNum > 0 {
  717. currVal := yData.Value[valIndex]
  718. preVal := yData.Value[valIndex-1]
  719. hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(needNum + 1)))
  720. for tmpNum := 0; tmpNum < needNum; tmpNum++ {
  721. newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, 0)
  722. // 赋值平均值
  723. tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
  724. newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
  725. }
  726. }
  727. if utils.InArrayByInt(allEdbInfoIds, currN) {
  728. newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN)
  729. } else {
  730. newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
  731. }
  732. newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
  733. valIndex++
  734. needNum = 0
  735. if len(xEdbInfoIdList) > 0 {
  736. xEdbInfoIdList = xEdbInfoIdList[1:]
  737. }
  738. } else {
  739. needNum++
  740. }
  741. }
  742. }
  743. }
  744. maxI := 0
  745. for _, yData := range newYDataList {
  746. lenEdb := len(yData.XEdbInfoIdList)
  747. for i := 0; i < lenEdb; i++ {
  748. if yData.XEdbInfoIdList[i] != 0 && !utils.InArrayByInt(yData.NoDataEdbList, yData.XEdbInfoIdList[i]) {
  749. if maxI < i {
  750. maxI = i
  751. }
  752. }
  753. }
  754. }
  755. earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
  756. xDataList = xDataList[0 : maxI+1]
  757. for yIndex, yData := range newYDataList {
  758. if len(yData.XEdbInfoIdList) > maxI+1 {
  759. newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
  760. }
  761. if len(yData.Value) > maxI+1 {
  762. newYDataList[yIndex].Value = yData.Value[0 : maxI+1]
  763. }
  764. nameList := make([]string, 0)
  765. enNameList := make([]string, 0)
  766. for k1, n := range newYDataList[yIndex].XEdbInfoIdList {
  767. if utils.InArrayByInt(allEdbInfoIds, n) { // 现货价不处理
  768. tmpItem := xDataListInit[k1]
  769. nameList = append(nameList, tmpItem.Name)
  770. enNameList = append(enNameList, tmpItem.NameEn)
  771. continue
  772. }
  773. if n <= 0 {
  774. nameList = append(nameList, `无合约`)
  775. enNameList = append(enNameList, `no contract`)
  776. } else {
  777. nameTmp := futureNameMap[yIndex][n]
  778. nameTmpSlice := strings.Split(nameTmp, "-")
  779. nameList = append(nameList, nameTmpSlice[0])
  780. enNameList = append(enNameList, nameTmpSlice[1])
  781. }
  782. }
  783. newYDataList[yIndex].NameList = nameList
  784. newYDataList[yIndex].EnNameList = enNameList
  785. }
  786. return
  787. }
  788. func CheckFormula(formula string) map[string]string {
  789. mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN"}
  790. str := strings.ToUpper(formula)
  791. for _, v := range mathFormula {
  792. str = strings.Replace(str, v, "", -1)
  793. }
  794. str = strings.Replace(str, "(", "", -1)
  795. str = strings.Replace(str, ")", "", -1)
  796. byteMap := make(map[string]string)
  797. for i := 0; i < len(str); i++ {
  798. byteInt := str[i]
  799. if byteInt >= 65 && byteInt <= 90 {
  800. byteStr := string(byteInt)
  801. if _, ok := byteMap[byteStr]; !ok {
  802. byteMap[byteStr] = byteStr
  803. }
  804. }
  805. }
  806. return byteMap
  807. }
  808. func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaStr string) string {
  809. funMap := GetFormulaMap()
  810. for k, v := range funMap {
  811. formulaStr = strings.Replace(formulaStr, k, v, -1)
  812. }
  813. replaceCount := 0
  814. for tag, edbInfoId := range tagEdbIdMap {
  815. if val, valOk := valArr[edbInfoId]; valOk { //值存在
  816. dvStr := fmt.Sprintf("%v", val)
  817. formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
  818. replaceCount++
  819. }
  820. }
  821. for k, v := range funMap {
  822. formulaStr = strings.Replace(formulaStr, v, k, -1)
  823. }
  824. fmt.Println(formulaStr)
  825. if replaceCount == len(tagEdbIdMap) {
  826. return formulaStr
  827. } else {
  828. return ""
  829. }
  830. }
  831. func GetFormulaMap() map[string]string {
  832. funMap := make(map[string]string)
  833. funMap["MAX"] = "[@@]"
  834. funMap["MIN"] = "[@!]"
  835. funMap["ABS"] = "[@#]"
  836. funMap["CEIL"] = "[@$]"
  837. funMap["COS"] = "[@%]"
  838. funMap["FLOOR"] = "[@^]"
  839. funMap["MOD"] = "[@&]"
  840. funMap["POW"] = "[@*]"
  841. funMap["ROUND"] = "[@(]"
  842. return funMap
  843. }
  844. // GetNeedDateData 获取合约内需要的日期数据
  845. 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) {
  846. //dataList := edbDataListMap[edbInfoId] //指标的所有数据值
  847. if len(dataList) <= 0 {
  848. // 没有数据的指标id
  849. return
  850. }
  851. //最早的日期
  852. minDateTime, err := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
  853. if err != nil {
  854. return
  855. }
  856. // 该日期存在数据的期货指标的最小数量,目前是现货和期货各1个,总共2个
  857. maxCount := 1
  858. for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
  859. tmpDate := tmpDateTime.Format(utils.FormatDate)
  860. tmpValue, ok := edbDataMap[tmpDate]
  861. if !ok {
  862. continue
  863. }
  864. // 该日期存在数据的指标数量
  865. count := 0
  866. for _, currEdbDataMap := range allEdbDataMap {
  867. _, tmpIsFind := currEdbDataMap[tmpDate]
  868. if tmpIsFind {
  869. count++
  870. if count >= maxCount {
  871. continue
  872. }
  873. }
  874. }
  875. // 该日期存在数据的期货指标数量小于2个,那么要继续往前找
  876. if count < maxCount {
  877. continue
  878. }
  879. //如果能找到数据,那么就返回
  880. // 数据为0,也直接返回,做无值处理
  881. if tmpValue == 0 {
  882. return
  883. }
  884. findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
  885. findDataValue = tmpValue
  886. isFind = true
  887. return
  888. }
  889. return
  890. }