history.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package quote
  2. import (
  3. "encoding/json"
  4. "mtp2_if/global/app"
  5. "mtp2_if/global/e"
  6. "mtp2_if/logger"
  7. "mtp2_if/models"
  8. "net/http"
  9. "sort"
  10. "time"
  11. "github.com/gin-gonic/gin"
  12. )
  13. // HistoryData 历史数据
  14. type HistoryData struct {
  15. Opened float32 `json:"Opened"` // 开盘价
  16. Highest float32 `json:"highest"` // 收盘价
  17. Lowest float32 `json:"lowest"` // 最低价
  18. Closed float32 `json:"closed"` // 收盘价
  19. TotleVolume float32 `json:"totleVolume"` // 总量
  20. TotleTurnover float32 `json:"totleTurnover"` // 总金额
  21. HoldVolume float32 `json:"holdVolume"` // 持仓量
  22. Settle float32 `json:"settle"` // 结算价
  23. TimeStamp time.Time `json:"timeStamp"` // 开盘时间
  24. }
  25. // QueryTSDataReq 分时图数据查询请求参数
  26. type QueryTSDataReq struct {
  27. GoodsCode string `form:"goodsCode" binding:"required"` // 商品代码
  28. }
  29. // QueryTSDataRsp 分时图数据查询返回模型
  30. type QueryTSDataRsp struct {
  31. GoodsCode string `json:"goodsCode"` // 商品代码
  32. DecimalPlace int `json:"decimalPlace"` // 小数位
  33. TradeDate string `json:"tradeDate"` // 交易日
  34. StartTime string `json:"startTime"` // 开始时间
  35. EndTime string `json:"endTime"` // 结束时间
  36. PreSettle float32 `json:"preSettle"` // 昨结
  37. HistoryDatas []HistoryData `json:"historyDatas"` // 历史数据
  38. }
  39. // QueryTSData 分时图数据查询
  40. // @Summary 分时图数据查询
  41. // @Produce json
  42. // @Param GoodsCode query string true "商品代码"
  43. // @Success 200 {object} QueryTSDataRsp
  44. // @Failure 500 {object} app.Response
  45. // @Router /History/QueryTSData [get]
  46. // @Tags 通用服务
  47. func QueryTSData(c *gin.Context) {
  48. appG := app.Gin{C: c}
  49. // 获取请求参数
  50. var req QueryTSDataReq
  51. if err := appG.C.ShouldBindQuery(&req); err != nil {
  52. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  53. appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
  54. return
  55. }
  56. var queryTSDataRsp QueryTSDataRsp
  57. // FIXME: - 一些不常变化的数据(如市场信息、商品信息等)应缓存到Redis中, 或缓存到服务内存
  58. market, err := models.GetMarketByGoodsCode(req.GoodsCode)
  59. if err != nil {
  60. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  61. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  62. return
  63. }
  64. if market == nil {
  65. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  66. appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
  67. return
  68. }
  69. // 获取目标品种交易日
  70. // FIXME: - 由于mtp2.0目前未同步外部交易所品种的当前交易日,
  71. // 故通道交易的品种目前只能在交易系统的外部市场中获
  72. // 取统一的交易日,后期应要求服务端同步外部数据
  73. marketRun, err := models.GetMarketRun(int(market.Marketid))
  74. if marketRun == nil {
  75. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  76. appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKETRUN_FAILED, nil)
  77. return
  78. }
  79. // 获取目标品种的开休市计划
  80. var runSteps []map[string]interface{}
  81. // 通道交易外部市场开休市计划表 - QuoteSourceGroupRunStep; 其它市场的 - MarketRunStepDetail
  82. if market.Trademode == 15 {
  83. // 外部市场
  84. sourceRunSteps, err := models.FindQuoteSourceGroupRunStepsByMarket(*market)
  85. if err != nil {
  86. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  87. appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
  88. return
  89. }
  90. for v := range sourceRunSteps {
  91. // struct -> json
  92. if jsonBytes, err := json.Marshal(v); err == nil {
  93. // json -> struct
  94. var runStepMap map[string]interface{}
  95. json.Unmarshal(jsonBytes, &runStepMap)
  96. runSteps = append(runSteps, runStepMap)
  97. }
  98. }
  99. }
  100. // 非外部市场或外部市场没有配置QuoteSourceGroupRunStep表数据的情况下,都从MarketRunStepDetail中获取数据
  101. if len(runSteps) == 0 {
  102. sourceRunSteps, err := models.FindMarketRunStepDetails(int(market.Marketid))
  103. if err != nil {
  104. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  105. appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
  106. return
  107. }
  108. for _, v := range sourceRunSteps {
  109. // struct -> json
  110. if jsonBytes, err := json.Marshal(v); err == nil {
  111. // json -> struct
  112. var runStepMap map[string]interface{}
  113. json.Unmarshal(jsonBytes, &runStepMap)
  114. runSteps = append(runSteps, runStepMap)
  115. }
  116. }
  117. }
  118. // 构建分时图可直接使用的开休市数据
  119. // 这里有一个知识点:TRADEWEEKDAY 与 STARTWEEKDAY,以及 TRADEWEEKDAY 与 ENDWEEKDAY 之间相差最多一天(管理端限制),
  120. // 所以目前并不支持正真的周五夜盘模式。我们在实现时不用做得太复杂。
  121. // 当前交易日(周几)对应的开休市计划
  122. tradeDate, err := time.ParseInLocation("2006-01-02", marketRun.Tradedate, time.Local)
  123. if err != nil {
  124. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  125. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  126. return
  127. }
  128. curWeekRunSteps := make([]map[string]interface{}, 0)
  129. for _, v := range runSteps {
  130. tradeWeekDay := v["tradeweekday"].(int)
  131. if tradeWeekDay == int(tradeDate.Weekday()) {
  132. curWeekRunSteps = append(curWeekRunSteps, v)
  133. }
  134. }
  135. // 获取不到可用的开休市计划
  136. if len(curWeekRunSteps) == 0 {
  137. logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
  138. appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
  139. return
  140. }
  141. // 按 SECTIONID 顺序排序(管理端会按时间顺序添加开休市计划)
  142. sort.Slice(curWeekRunSteps, func(i int, j int) bool {
  143. return curWeekRunSteps[i]["sectionid"].(int) < curWeekRunSteps[j]["sectionid"].(int)
  144. })
  145. // 把各开休市时间段转化为真实的时间
  146. //
  147. // 查询成功
  148. logger.GetLogger().Debugln("QueryTSData successed: %v", queryTSDataRsp)
  149. appG.Response(http.StatusOK, e.SUCCESS, queryTSDataRsp)
  150. }