package services import ( "encoding/json" "eta/eta_index_lib/models" "eta/eta_index_lib/utils" "fmt" "github.com/rdlucklib/rdluck_tools/http" "net/url" "strings" "time" ) const ( ThsHfApiUrl = "https://quantapi.51ifind.com/api/v1/high_frequency" ) // GetEdbDataFromThsHf 获取高频数据 func GetEdbDataFromThsHf(thsParams models.ThsHfSearchEdbReq, terminalCode string) (indexes []*models.ThsHfIndexWithData, err error) { terminal, e := GetTerminal(utils.DATA_SOURCE_THS, terminalCode) if e != nil { err = fmt.Errorf("获取同花顺终端配置失败, %v", e) return } // 走API if utils.ThsDataMethod == "" || utils.ThsDataMethod == "api" { var token string token, e = GetAccessToken(false, terminal.Value) if e != nil { err = fmt.Errorf("获取同花顺API-AccessToken失败, %v", e) return } // TEST //token = "9eba1634116ea2aed9a5b12b6e12b0b5fcbe0847.signs_NTc2NjQ4MTA5" return getEdbDataFromThsHfHttp(thsParams, terminal.Value, token) } // 走公用机 if terminal.ServerUrl == "" { err = fmt.Errorf("同花顺终端地址未配置") return } return getEdbDataFromThsHfApp(thsParams, 0, terminal.ServerUrl) } // getEdbDataFromThsHfHttp API-获取高频指标数据 func getEdbDataFromThsHfHttp(thsParams models.ThsHfSearchEdbReq, refreshToken, accessToken string) (indexes []*models.ThsHfIndexWithData, err error) { defer func() { if err != nil { tips := fmt.Sprintf("同花顺高频指标API-getEdbDataFromThsHfHttp err: %v", err) utils.FileLog.Info(tips) } }() // 请求参数参考 //dataMap := map[string]interface{}{ // "codes": "CU2407.SHF,CU2408.SHF", // "indicators": "pct_chg", // "starttime": "2024-06-06 09:15:00", // "endtime": "2024-06-11 15:15:00", // "functionpara": map[string]interface { // }{ // "Limitstart": "10:00:00", // "Limitend": "14:15:00", // "Interval": 60, // "Fill": "Previous", // "CPS": "forward4", // "Timeformat": "LocalTime", // "BaseDate": "2024-01-01", // }, //} // 额外参数 funcParams := map[string]interface{}{} funcParams["Interval"] = thsParams.Interval if thsParams.Fill != "" { funcParams["Fill"] = thsParams.Fill } if thsParams.CPS != "" { funcParams["CPS"] = thsParams.CPS } if thsParams.BaseDate != "" { funcParams["BaseDate"] = thsParams.BaseDate } // TEST //funcParams["Limitstart"] = "10:00:00" //funcParams["Limitend"] = "14:15:00" //funcParams["Fill"] = "Previous" //funcParams["CPS"] = "forward4" //funcParams["Timeformat"] = "LocalTime" //funcParams["BaseDate"] = "2024-01-01" dataMap := map[string]interface{}{ "codes": thsParams.StockCode, "indicators": thsParams.EdbCode, "starttime": thsParams.StartTime, "endtime": thsParams.EndTime, "functionpara": funcParams, } // 请求接口 body, e, _ := postCurl(ThsHfApiUrl, dataMap, 0, refreshToken, accessToken) if e != nil { utils.FileLog.Info(string(body)) err = fmt.Errorf("同花顺API-请求失败, %v", e) return } apiResp := new(models.ThsHfApiResp) if e = json.Unmarshal(body, &apiResp); e != nil { err = fmt.Errorf("同花顺API-解析响应失败, %v", e) return } if apiResp.ErrorCode != 0 { err = fmt.Errorf("同花顺高频API-状态码: %d, 提示信息: %s", apiResp.ErrorCode, apiResp.ErrMsg) return } indexes = make([]*models.ThsHfIndexWithData, 0) if len(apiResp.Tables) == 0 { utils.FileLog.Info("同花顺高频API-无数据") return } // 结果示例 // { // "errorcode": 0, // "errmsg": "Success!", // "tables": [{ // "thscode": "CU2407.SHF", // "time": ["2024-07-01 10:00", "2024-07-01 11:15", "2024-07-01 14:15", "2024-07-01 15:00"], // "table": { // "open": [77930.000000, 77980.000000, 77910.000000, 77850.000000], // "close": [77980.000000, 77920.000000, 77850.000000, 77780.000000] // } // }, { // "thscode": "CU2408.SHF", // "time": ["2024-07-01 10:00", "2024-07-01 11:15", "2024-07-01 14:15", "2024-07-01 15:00"], // "table": { // "open": [78180.000000, 78280.000000, 78220.000000, 78110.000000], // "close": [78280.000000, 78220.000000, 78110.000000, 78060.000000] // } // }] // } // Tables中的每一个对应一个证券代码 for _, v := range apiResp.Tables { if len(v.Time) == 0 || len(v.Table) == 0 { continue } // Table中的K-V对应指标代码-数据值序列 for tk, tv := range v.Table { index := new(models.ThsHfIndexWithData) index.StockCode = v.ThsCode index.EdbCode = tk td := make([]*models.ThsHfIndexData, 0) tvl := len(tv) for k, t := range v.Time { if k >= tvl { continue } dt, e := time.ParseInLocation("2006-01-02 15:04", t, time.Local) if e != nil { utils.FileLog.Info(fmt.Sprintf("同花顺API-time parse t: %s, err: %v", t, e)) continue } td = append(td, &models.ThsHfIndexData{ DataTime: dt, Value: tv[k], }) } index.IndexData = td indexes = append(indexes, index) } } return } // getEdbDataFromThsHfApp 公用机-获取高频指标数据 func getEdbDataFromThsHfApp(thsParams models.ThsHfSearchEdbReq, num int, serverUrl string) (indexes []*models.ThsHfIndexWithData, err error) { var requestUrl string defer func() { if err != nil { utils.FileLog.Info(fmt.Sprintf("requestUrl: %s", requestUrl)) utils.FileLog.Info(fmt.Sprintf("getEdbDataFromThsHfApp: %v", err)) } }() //serverUrl = "http://wxmsgsen1.hzinsights.com:8040/" baseUrl := fmt.Sprintf("%s%s", serverUrl, "edbInfo/ths/hf?") // 额外参数 var funcParam string if thsParams.Interval > 0 { funcParam += fmt.Sprintf("Interval:%d,", thsParams.Interval) } if thsParams.Fill != "" { funcParam += fmt.Sprintf("Fill:%s,", thsParams.Fill) } if thsParams.CPS != "" { funcParam += fmt.Sprintf("CPS:%s,", thsParams.CPS) } if thsParams.BaseDate != "" { funcParam += fmt.Sprintf("BaseDate:%s,", thsParams.BaseDate) } funcParam = strings.TrimRight(funcParam, ",") params := url.Values{} params.Add("codes", thsParams.StockCode) params.Add("indicators", thsParams.EdbCode) params.Add("function_para", funcParam) params.Add("start_time", thsParams.StartTime) params.Add("end_time", thsParams.EndTime) // 请求终端 requestUrl = baseUrl + params.Encode() body, e := http.Get(requestUrl) if e != nil { err = fmt.Errorf("") return } dataBody := strings.TrimLeft(string(body), `"`) dataBody = strings.TrimRight(dataBody, `"`) dataBody = strings.ReplaceAll(dataBody, `\`, ``) //utils.FileLog.Info(dataBody) appResp := new(TerminalResponse) if e = json.Unmarshal([]byte(dataBody), &appResp); e != nil { err = fmt.Errorf("同花顺APP-解析响应失败, %v", e) return } if appResp.ErrorCode != 0 { //如果是同花顺登录session失效了,那么就重新请求获取数据 if appResp.ErrorCode == -1020 && num == 0 { return getEdbDataFromThsHfApp(thsParams, 1, serverUrl) } err = fmt.Errorf("同花顺APP-状态码: %d, 提示信息: %s", appResp.ErrorCode, appResp.ErrMsg) return } // 响应结果示例 // { // "errorcode": 0, // "errmsg": "Success!", // "data": [{ // "time": "2024-06-04 09:30", // "thscode": "CU2406.SHF", // "open": 81900.0, // "close": 81820.0 // }, { // "time": "2024-06-04 10:00", // "thscode": "CU2406.SHF", // "open": 81820.0, // "close": 81790.0 // }, { // "time": "2024-06-04 10:45", // "thscode": "CU2406.SHF", // "open": 81820.0, // "close": 81950.0 // }] // } indexes = make([]*models.ThsHfIndexWithData, 0) indexMap := make(map[string]*models.ThsHfIndexWithData) for _, stockData := range appResp.Data { strTime := stockData["time"].(string) dataTime, e := time.ParseInLocation("2006-01-02 15:04", strTime, time.Local) if e != nil { utils.FileLog.Info("数据日期格式有误, time: %s, %v", strTime, e) continue } stockCode := stockData["thscode"].(string) // 指标代码+数据 for k, v := range stockData { if k == "time" || k == "thscode" { continue } if v == nil { continue } val, ok := v.(float64) if !ok { continue } mk := fmt.Sprintf("%s-%s", stockCode, k) if indexMap[mk] == nil { indexMap[mk] = new(models.ThsHfIndexWithData) indexMap[mk].StockCode = stockCode indexMap[mk].EdbCode = k indexMap[mk].IndexData = make([]*models.ThsHfIndexData, 0) } indexMap[mk].IndexData = append(indexMap[mk].IndexData, &models.ThsHfIndexData{ DataTime: dataTime, Value: val, }) } } for _, v := range indexMap { indexes = append(indexes, v) } return }