base_from_ths_hf.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. package services
  2. import (
  3. "encoding/json"
  4. "eta/eta_index_lib/models"
  5. "eta/eta_index_lib/utils"
  6. "fmt"
  7. "github.com/rdlucklib/rdluck_tools/http"
  8. "net/url"
  9. "strings"
  10. "time"
  11. )
  12. const (
  13. ThsHfApiUrl = "https://quantapi.51ifind.com/api/v1/high_frequency"
  14. )
  15. // GetEdbDataFromThsHf 获取高频数据
  16. func GetEdbDataFromThsHf(thsParams models.ThsHfSearchEdbReq, terminalCode string) (indexes []*models.ThsHfIndexWithData, err error) {
  17. terminal, e := GetTerminal(utils.DATA_SOURCE_THS, terminalCode)
  18. if e != nil {
  19. err = fmt.Errorf("获取同花顺终端配置失败, %v", e)
  20. return
  21. }
  22. // 走API
  23. if utils.ThsDataMethod == "" || utils.ThsDataMethod == "api" {
  24. var token string
  25. token, e = GetAccessToken(false, terminal.Value)
  26. if e != nil {
  27. err = fmt.Errorf("获取同花顺API-AccessToken失败, %v", e)
  28. return
  29. }
  30. // TEST
  31. //token = "9eba1634116ea2aed9a5b12b6e12b0b5fcbe0847.signs_NTc2NjQ4MTA5"
  32. return getEdbDataFromThsHfHttp(thsParams, terminal.Value, token)
  33. }
  34. // 走公用机
  35. if terminal.ServerUrl == "" {
  36. err = fmt.Errorf("同花顺终端地址未配置")
  37. return
  38. }
  39. return getEdbDataFromThsHfApp(thsParams, 0, terminal.ServerUrl)
  40. }
  41. // getEdbDataFromThsHfHttp API-获取高频指标数据
  42. func getEdbDataFromThsHfHttp(thsParams models.ThsHfSearchEdbReq, refreshToken, accessToken string) (indexes []*models.ThsHfIndexWithData, err error) {
  43. defer func() {
  44. if err != nil {
  45. tips := fmt.Sprintf("同花顺高频指标API-getEdbDataFromThsHfHttp err: %v", err)
  46. utils.FileLog.Info(tips)
  47. }
  48. }()
  49. // 请求参数参考
  50. //dataMap := map[string]interface{}{
  51. // "codes": "CU2407.SHF,CU2408.SHF",
  52. // "indicators": "pct_chg",
  53. // "starttime": "2024-06-06 09:15:00",
  54. // "endtime": "2024-06-11 15:15:00",
  55. // "functionpara": map[string]interface {
  56. // }{
  57. // "Limitstart": "10:00:00",
  58. // "Limitend": "14:15:00",
  59. // "Interval": 60,
  60. // "Fill": "Previous",
  61. // "CPS": "forward4",
  62. // "Timeformat": "LocalTime",
  63. // "BaseDate": "2024-01-01",
  64. // },
  65. //}
  66. // 额外参数
  67. funcParams := map[string]interface{}{}
  68. funcParams["Interval"] = thsParams.Interval
  69. if thsParams.Fill != "" {
  70. funcParams["Fill"] = thsParams.Fill
  71. }
  72. if thsParams.CPS != "" {
  73. funcParams["CPS"] = thsParams.CPS
  74. }
  75. if thsParams.BaseDate != "" {
  76. funcParams["BaseDate"] = thsParams.BaseDate
  77. }
  78. // TEST
  79. //funcParams["Limitstart"] = "10:00:00"
  80. //funcParams["Limitend"] = "14:15:00"
  81. //funcParams["Fill"] = "Previous"
  82. //funcParams["CPS"] = "forward4"
  83. //funcParams["Timeformat"] = "LocalTime"
  84. //funcParams["BaseDate"] = "2024-01-01"
  85. dataMap := map[string]interface{}{
  86. "codes": thsParams.StockCode,
  87. "indicators": thsParams.EdbCode,
  88. "starttime": thsParams.StartTime,
  89. "endtime": thsParams.EndTime,
  90. "functionpara": funcParams,
  91. }
  92. // 请求接口
  93. body, e, _ := postCurl(ThsHfApiUrl, dataMap, 0, refreshToken, accessToken)
  94. if e != nil {
  95. utils.FileLog.Info(string(body))
  96. err = fmt.Errorf("同花顺API-请求失败, %v", e)
  97. return
  98. }
  99. apiResp := new(models.ThsHfApiResp)
  100. if e = json.Unmarshal(body, &apiResp); e != nil {
  101. err = fmt.Errorf("同花顺API-解析响应失败, %v", e)
  102. return
  103. }
  104. if apiResp.ErrorCode != 0 {
  105. err = fmt.Errorf("同花顺高频API-状态码: %d, 提示信息: %s", apiResp.ErrorCode, apiResp.ErrMsg)
  106. return
  107. }
  108. indexes = make([]*models.ThsHfIndexWithData, 0)
  109. if len(apiResp.Tables) == 0 {
  110. utils.FileLog.Info("同花顺高频API-无数据")
  111. return
  112. }
  113. // 结果示例
  114. // {
  115. // "errorcode": 0,
  116. // "errmsg": "Success!",
  117. // "tables": [{
  118. // "thscode": "CU2407.SHF",
  119. // "time": ["2024-07-01 10:00", "2024-07-01 11:15", "2024-07-01 14:15", "2024-07-01 15:00"],
  120. // "table": {
  121. // "open": [77930.000000, 77980.000000, 77910.000000, 77850.000000],
  122. // "close": [77980.000000, 77920.000000, 77850.000000, 77780.000000]
  123. // }
  124. // }, {
  125. // "thscode": "CU2408.SHF",
  126. // "time": ["2024-07-01 10:00", "2024-07-01 11:15", "2024-07-01 14:15", "2024-07-01 15:00"],
  127. // "table": {
  128. // "open": [78180.000000, 78280.000000, 78220.000000, 78110.000000],
  129. // "close": [78280.000000, 78220.000000, 78110.000000, 78060.000000]
  130. // }
  131. // }]
  132. // }
  133. // Tables中的每一个对应一个证券代码
  134. for _, v := range apiResp.Tables {
  135. if len(v.Time) == 0 || len(v.Table) == 0 {
  136. continue
  137. }
  138. // Table中的K-V对应指标代码-数据值序列
  139. for tk, tv := range v.Table {
  140. index := new(models.ThsHfIndexWithData)
  141. index.StockCode = v.ThsCode
  142. index.EdbCode = tk
  143. td := make([]*models.ThsHfIndexData, 0)
  144. tvl := len(tv)
  145. for k, t := range v.Time {
  146. if k >= tvl {
  147. continue
  148. }
  149. dt, e := time.ParseInLocation("2006-01-02 15:04", t, time.Local)
  150. if e != nil {
  151. utils.FileLog.Info(fmt.Sprintf("同花顺API-time parse t: %s, err: %v", t, e))
  152. continue
  153. }
  154. td = append(td, &models.ThsHfIndexData{
  155. DataTime: dt,
  156. Value: tv[k],
  157. })
  158. }
  159. index.IndexData = td
  160. indexes = append(indexes, index)
  161. }
  162. }
  163. return
  164. }
  165. // getEdbDataFromThsHfApp 公用机-获取高频指标数据
  166. func getEdbDataFromThsHfApp(thsParams models.ThsHfSearchEdbReq, num int, serverUrl string) (indexes []*models.ThsHfIndexWithData, err error) {
  167. var requestUrl string
  168. defer func() {
  169. if err != nil {
  170. utils.FileLog.Info(fmt.Sprintf("requestUrl: %s", requestUrl))
  171. utils.FileLog.Info(fmt.Sprintf("getEdbDataFromThsHfApp: %v", err))
  172. }
  173. }()
  174. //serverUrl = "http://wxmsgsen1.hzinsights.com:8040/"
  175. baseUrl := fmt.Sprintf("%s%s", serverUrl, "edbInfo/ths/hf?")
  176. // 额外参数
  177. var funcParam string
  178. if thsParams.Interval > 0 {
  179. funcParam += fmt.Sprintf("Interval:%d,", thsParams.Interval)
  180. }
  181. if thsParams.Fill != "" {
  182. funcParam += fmt.Sprintf("Fill:%s,", thsParams.Fill)
  183. }
  184. if thsParams.CPS != "" {
  185. funcParam += fmt.Sprintf("CPS:%s,", thsParams.CPS)
  186. }
  187. if thsParams.BaseDate != "" {
  188. funcParam += fmt.Sprintf("BaseDate:%s,", thsParams.BaseDate)
  189. }
  190. funcParam = strings.TrimRight(funcParam, ",")
  191. params := url.Values{}
  192. params.Add("codes", thsParams.StockCode)
  193. params.Add("indicators", thsParams.EdbCode)
  194. params.Add("function_para", funcParam)
  195. params.Add("start_time", thsParams.StartTime)
  196. params.Add("end_time", thsParams.EndTime)
  197. // 请求终端
  198. requestUrl = baseUrl + params.Encode()
  199. body, e := http.Get(requestUrl)
  200. if e != nil {
  201. err = fmt.Errorf("")
  202. return
  203. }
  204. dataBody := strings.TrimLeft(string(body), `"`)
  205. dataBody = strings.TrimRight(dataBody, `"`)
  206. dataBody = strings.ReplaceAll(dataBody, `\`, ``)
  207. //utils.FileLog.Info(dataBody)
  208. appResp := new(TerminalResponse)
  209. if e = json.Unmarshal([]byte(dataBody), &appResp); e != nil {
  210. err = fmt.Errorf("同花顺APP-解析响应失败, %v", e)
  211. return
  212. }
  213. if appResp.ErrorCode != 0 {
  214. //如果是同花顺登录session失效了,那么就重新请求获取数据
  215. if appResp.ErrorCode == -1020 && num == 0 {
  216. return getEdbDataFromThsHfApp(thsParams, 1, serverUrl)
  217. }
  218. err = fmt.Errorf("同花顺APP-状态码: %d, 提示信息: %s", appResp.ErrorCode, appResp.ErrMsg)
  219. return
  220. }
  221. // 响应结果示例
  222. // {
  223. // "errorcode": 0,
  224. // "errmsg": "Success!",
  225. // "data": [{
  226. // "time": "2024-06-04 09:30",
  227. // "thscode": "CU2406.SHF",
  228. // "open": 81900.0,
  229. // "close": 81820.0
  230. // }, {
  231. // "time": "2024-06-04 10:00",
  232. // "thscode": "CU2406.SHF",
  233. // "open": 81820.0,
  234. // "close": 81790.0
  235. // }, {
  236. // "time": "2024-06-04 10:45",
  237. // "thscode": "CU2406.SHF",
  238. // "open": 81820.0,
  239. // "close": 81950.0
  240. // }]
  241. // }
  242. indexes = make([]*models.ThsHfIndexWithData, 0)
  243. indexMap := make(map[string]*models.ThsHfIndexWithData)
  244. for _, stockData := range appResp.Data {
  245. strTime := stockData["time"].(string)
  246. dataTime, e := time.ParseInLocation("2006-01-02 15:04", strTime, time.Local)
  247. if e != nil {
  248. utils.FileLog.Info("数据日期格式有误, time: %s, %v", strTime, e)
  249. continue
  250. }
  251. stockCode := stockData["thscode"].(string)
  252. // 指标代码+数据
  253. for k, v := range stockData {
  254. if k == "time" || k == "thscode" {
  255. continue
  256. }
  257. if v == nil {
  258. continue
  259. }
  260. val, ok := v.(float64)
  261. if !ok {
  262. continue
  263. }
  264. mk := fmt.Sprintf("%s-%s", stockCode, k)
  265. if indexMap[mk] == nil {
  266. indexMap[mk] = new(models.ThsHfIndexWithData)
  267. indexMap[mk].StockCode = stockCode
  268. indexMap[mk].EdbCode = k
  269. indexMap[mk].IndexData = make([]*models.ThsHfIndexData, 0)
  270. }
  271. indexMap[mk].IndexData = append(indexMap[mk].IndexData, &models.ThsHfIndexData{
  272. DataTime: dataTime,
  273. Value: val,
  274. })
  275. }
  276. }
  277. for _, v := range indexMap {
  278. indexes = append(indexes, v)
  279. }
  280. return
  281. }