| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- package quote
- import (
- "encoding/json"
- "mtp2_if/global/app"
- "mtp2_if/global/e"
- "mtp2_if/logger"
- "mtp2_if/models"
- "mtp2_if/utils"
- "net/http"
- "sort"
- "time"
- "github.com/gin-gonic/gin"
- )
- // HistoryData 历史数据
- type HistoryData struct {
- Opened float64 `json:"o"` // 开盘价
- Highest float64 `json:"h"` // 最高价
- Lowest float64 `json:"l"` // 最低价
- Closed float64 `json:"c"` // 收盘价
- TotleVolume int `json:"tv"` // 总量
- TotleTurnover float64 `json:"tt"` // 总金额
- HoldVolume int `json:"hv"` // 持仓量
- Settle float64 `json:"s"` // 结算价,日线周期(包括)以上才有
- TimeStamp time.Time `json:"ts"` // 开盘时间
- }
- // QueryHistoryDatasReq 查询行情历史数据请求参数
- type QueryHistoryDatasReq struct {
- CycleType int `form:"cycleType" binding:"required"`
- GoodsCode string `form:"goodsCode" binding:"required"`
- StartTime string `form:"startTime"`
- EndTime string `form:"endTime"`
- Count int `form:"count"`
- IsAsc bool `form:"isAsc"`
- }
- // QueryHistoryDatas 查询行情历史数据
- // @Summary 查询行情历史数据
- // @Produce json
- // @Security ApiKeyAuth
- // @Param cycleType query int true "周期类型, 0-秒 1: 1分钟 2: 5分钟 3: 30分钟 4: 60分钟 120: 2小时 240: 4小时 11: 日线 10: Tik"
- // @Param goodsCode query string true "商品代码"
- // @Param startTime query string false "开始时间,格式:yyyy-MM-dd HH:mm:ss"
- // @Param endTime query string false "结束时间,格式:yyyy-MM-dd HH:mm:ss"
- // @Param count query int false "条数"
- // @Param IsAsc query bool false "是否按时间顺序排序(默认为时间倒序排序)"
- // @Success 200 {object} HistoryData
- // @Failure 500 {object} app.Response
- // @Router /Quote/QueryHistoryDatas [get]
- // @Tags 行情服务
- func QueryHistoryDatas(c *gin.Context) {
- appG := app.Gin{C: c}
- // 获取请求参数
- var req QueryHistoryDatasReq
- if err := appG.C.ShouldBindQuery(&req); err != nil {
- logger.GetLogger().Errorf("QueryHistoryDatas failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
- return
- }
- // 转换时间
- timeFormat := "2006-01-02 15:04:05" // go中的时间格式化必须是这个时间
- var startTime *time.Time
- if len(req.StartTime) > 0 {
- st, err := time.ParseInLocation(timeFormat, req.StartTime, time.Local)
- if err != nil {
- logger.GetLogger().Errorf("QueryHistoryDatas failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_QUERY_TIME_FORMAT_FAIL, nil)
- return
- }
- startTime = &st
- }
- var endTime *time.Time
- if len(req.EndTime) > 0 {
- et, err := time.ParseInLocation(timeFormat, req.EndTime, time.Local)
- if err != nil {
- logger.GetLogger().Errorf("QueryHistoryDatas failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_QUERY_TIME_FORMAT_FAIL, nil)
- return
- }
- endTime = &et
- }
- // 查询数据
- cycleDatas, err := models.GetHistoryCycleDatas(models.CycleType(req.CycleType), req.GoodsCode, startTime, endTime, req.Count, req.IsAsc)
- if err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
- return
- }
- // 获取目标商品信息
- goods, err := models.GetGoodsByGoodsCode(req.GoodsCode)
- if err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
- return
- }
- // 计算最终价格
- rst := make([]HistoryData, 0)
- for _, v := range cycleDatas {
- historyData := HistoryData{
- Opened: utils.IntToFloat64(v.Open, int(goods.Decimalplace)),
- Highest: utils.IntToFloat64(v.High, int(goods.Decimalplace)),
- Lowest: utils.IntToFloat64(v.Low, int(goods.Decimalplace)),
- Closed: utils.IntToFloat64(v.Close, int(goods.Decimalplace)),
- TotleVolume: v.TV,
- TotleTurnover: float64(v.TT),
- HoldVolume: v.HV,
- Settle: utils.IntToFloat64(v.SP, int(goods.Decimalplace)),
- TimeStamp: time.Unix(int64(v.ST), 0),
- }
- rst = append(rst, historyData)
- }
- // 查询成功
- logger.GetLogger().Debugln("QueryHistoryDatas successed: %v", rst)
- appG.Response(http.StatusOK, e.SUCCESS, rst)
- }
- // QueryTSDataReq 分时图数据查询请求参数
- type QueryTSDataReq struct {
- GoodsCode string `form:"goodsCode" binding:"required"` // 商品代码
- }
- // QueryTSDataRsp 分时图数据查询返回模型
- type QueryTSDataRsp struct {
- GoodsCode string `json:"goodsCode"` // 商品代码
- DecimalPlace int `json:"decimalPlace"` // 小数位
- TradeDate string `json:"tradeDate"` // 交易日
- StartTime string `json:"startTime"` // 开始时间
- EndTime string `json:"endTime"` // 结束时间
- PreSettle float32 `json:"preSettle"` // 昨结
- HistoryDatas []HistoryData `json:"historyDatas"` // 历史数据
- }
- // QueryTSData 分时图数据查询
- // @Summary 分时图数据查询
- // @Produce json
- // @Param GoodsCode query string true "商品代码"
- // @Success 200 {object} QueryTSDataRsp
- // @Failure 500 {object} app.Response
- // @Router /Quote/QueryTSData [get]
- // @Tags 行情服务
- func QueryTSData(c *gin.Context) {
- appG := app.Gin{C: c}
- // 获取请求参数
- var req QueryTSDataReq
- if err := appG.C.ShouldBindQuery(&req); err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
- return
- }
- var queryTSDataRsp QueryTSDataRsp
- // FIXME: - 一些不常变化的数据(如市场信息、商品信息等)应缓存到Redis中, 或缓存到服务内存
- market, err := models.GetMarketByGoodsCode(req.GoodsCode)
- if err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
- return
- }
- if market == nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKET_FAILED, nil)
- return
- }
- // 获取目标品种交易日
- // FIXME: - 由于mtp2.0目前未同步外部交易所品种的当前交易日,
- // 故通道交易的品种目前只能在交易系统的外部市场中获
- // 取统一的交易日,后期应要求服务端同步外部数据
- marketRun, err := models.GetMarketRun(int(market.Marketid))
- if marketRun == nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_GET_MARKETRUN_FAILED, nil)
- return
- }
- // 获取目标品种的开休市计划
- var runSteps []map[string]interface{}
- // 通道交易外部市场开休市计划表 - QuoteSourceGroupRunStep; 其它市场的 - MarketRunStepDetail
- if market.Trademode == 15 {
- // 外部市场
- sourceRunSteps, err := models.FindQuoteSourceGroupRunStepsByMarket(*market)
- if err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
- return
- }
- for v := range sourceRunSteps {
- // struct -> json
- if jsonBytes, err := json.Marshal(v); err == nil {
- // json -> struct
- var runStepMap map[string]interface{}
- json.Unmarshal(jsonBytes, &runStepMap)
- runSteps = append(runSteps, runStepMap)
- }
- }
- }
- // 非外部市场或外部市场没有配置QuoteSourceGroupRunStep表数据的情况下,都从MarketRunStepDetail中获取数据
- if len(runSteps) == 0 {
- sourceRunSteps, err := models.FindMarketRunStepDetails(int(market.Marketid))
- if err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
- return
- }
- for _, v := range sourceRunSteps {
- // struct -> json
- if jsonBytes, err := json.Marshal(v); err == nil {
- // json -> struct
- var runStepMap map[string]interface{}
- json.Unmarshal(jsonBytes, &runStepMap)
- runSteps = append(runSteps, runStepMap)
- }
- }
- }
- // 构建分时图可直接使用的开休市数据
- // 这里有一个知识点:TRADEWEEKDAY 与 STARTWEEKDAY,以及 TRADEWEEKDAY 与 ENDWEEKDAY 之间相差最多一天(管理端限制),
- // 所以目前并不支持正真的周五夜盘模式。我们在实现时不用做得太复杂。
- // 当前交易日(周几)对应的开休市计划
- tradeDate, err := time.ParseInLocation("2006-01-02", marketRun.Tradedate, time.Local)
- if err != nil {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
- return
- }
- curWeekRunSteps := make([]map[string]interface{}, 0)
- for _, v := range runSteps {
- tradeWeekDay := v["tradeweekday"].(int)
- if tradeWeekDay == int(tradeDate.Weekday()) {
- curWeekRunSteps = append(curWeekRunSteps, v)
- }
- }
- // 获取不到可用的开休市计划
- if len(curWeekRunSteps) == 0 {
- logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
- appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
- return
- }
- // 按 SECTIONID 顺序排序(管理端会按时间顺序添加开休市计划)
- sort.Slice(curWeekRunSteps, func(i int, j int) bool {
- return curWeekRunSteps[i]["sectionid"].(int) < curWeekRunSteps[j]["sectionid"].(int)
- })
- // 把各开休市时间段转化为真实的时间
- //
- // 查询成功
- logger.GetLogger().Debugln("QueryTSData successed: %v", queryTSDataRsp)
- appG.Response(http.StatusOK, e.SUCCESS, queryTSDataRsp)
- }
|