package processor import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "io/ioutil" "net/http" "strconv" "strings" "time" "wss-pool/config" "wss-pool/internal" "wss-pool/internal/data" red "wss-pool/internal/redis" "wss-pool/logging/applogger" "wss-pool/pkg/model" "wss-pool/pkg/model/stock" ) var RedisFOREX string = "FOREX:LIST:" var UrlKline = "https://quote.tradeswitcher.com/quote-b-api/kline" // 历史K线 var UrlBatchKline = "https://quote.tradeswitcher.com/quote-b-api/batch-kline" // 批量K线查询 var UrlDepthTick = "https://quote.tradeswitcher.com/quote-b-api/depth-tick" // 最新盘口报价查询 { "trace": "", "data": { "symbol_list": [ { "code": "GBPJPY" }, { "code": "CADJPY" } ] var UrlTradeTick = "https://quote.tradeswitcher.com/quote-b-api/trade-tick" // 最新成交报价查询 { "trace": "", "data": { "symbol_list": [ { "code": "GBPJPY" }, { "code": "CADJPY" } ] var TokenTrade = "bf8f33c446c4494286eccaa57a2e6fac-c-app" // ForexAggregates // // @Description: 外汇K线 https://api.polygon.io/v2/aggs/ticker/C:EURUSD/range/1/day/2023-01-09/2023-02-10?adjusted=true&sort=asc&apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexAggregates(c *gin.Context) { code := internal.ReplaceStr(c.Query("code")) // 外汇code multiplier := internal.IntegerInit(c.Query("multiplier")) // 乘数:默认1 timespan := internal.ReplaceStr(c.Query("timespan")) // 时间间隔:second\minute\hour\day\week\month\quarter\year(默认:day) from := internal.ReplaceStr(internal.ReplaceStr(c.Query("from"))) // 开始时间 to := internal.ReplaceStr(internal.ReplaceStr(c.Query("to"))) // 结束时间 sort := internal.ReplaceStr(internal.ReplaceStr(c.Query("sort"))) // 排序(默认:asc) if len(from) == 0 || len(to) == 0 || len(code) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } if multiplier == 0 { multiplier = 1 } if len(timespan) == 0 { timespan = "day" } if len(sort) == 0 { sort = "asc" } url := fmt.Sprintf("https://%v/v2/aggs/ticker/C:%v/range/%v/%v/%v/%v?adjusted=true&sort=%v&apiKey=%v", config.Config.ShareGather.PolygonHost, code, multiplier, timespan, from, to, sort, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // 历史k线查询 func ForexAggregatesNewGet(c *gin.Context) { codeStr := internal.ReplaceStr(c.Query("code")) // 外汇code kline_type := internal.IntegerInit(c.Query("kline_type")) // k线类型,1分钟K,2为5分钟K,3为15分钟K,4为30分钟K,5为小时K,6为2小时K,7为4小时K,8为日K,9为周K,10为月K query_kline_num := internal.IntegerInit(c.Query("query_kline_num")) // 查询多少根K线,最多1000根 code := model.Check_Symbol[codeStr] if len(code) == 0 { code = codeStr } if kline_type == 0 || query_kline_num == 0 || len(code) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } req, err := http.NewRequest("GET", UrlKline, nil) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } q := req.URL.Query() q.Add("token", TokenTrade) // 参数构造 var query model.ConstructParameters query.Trace = uuid.New().String() query.Data.Code = code query.Data.KlineType = kline_type query.Data.KlineTimestampEnd = 0 query.Data.QueryKlineNum = query_kline_num query.Data.AdjustType = 0 queryStr, err := json.Marshal(&query) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } q.Add("query", string(queryStr)) req.URL.RawQuery = q.Encode() resp, err := http.DefaultClient.Do(req) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } defer resp.Body.Close() bodyStr, err := ioutil.ReadAll(resp.Body) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } var klineNew model.KlineGetReturnStruct if err = json.Unmarshal(bodyStr, &klineNew); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } var group bson.D addFields := bson.D{ {"$addFields", bson.D{ {"dateObj", bson.D{{"$toDate", "$timestamp"}}}, {"num", bson.D{{"$toInt", "$volume"}}}}, }} match := bson.D{ {"$match", bson.D{ {"code", codeStr}, {"dateObj", bson.D{{"$gte", time.Now().Add(-1 * time.Hour)}}}, }}} //minDate := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) // TODO: 插针数据 var timeStamp int64 switch kline_type { case 1: // 分时线 timeStamp = int64(1 * 60 * 1000) // 1 分钟 = 1 * 60 秒 * 1000 毫秒 case 2: // 5分钟线 timeStamp = int64(5 * 60 * 1000) // 5分钟 = 5 * 60 秒 * 1000 毫秒 //case 3: // 15分钟 // timeStamp = int64(15 * 60 * 1000) // 15分钟 =15 * 60 秒 * 1000 毫秒 //case 4: // 30分钟 // timeStamp = int64(30 * 60 * 1000) // 30分钟 =30 * 60 秒 * 1000 毫秒 //case 5: // 1小时 // timeStamp = int64(60 * 60 * 1000) // 1小时 =60 *60 秒 * 1000 毫秒 default: c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, klineNew.Data, internal.QuerySuccess)) return } applogger.Debug("数据展示:%v", timeStamp) group = bson.D{ {"$group", bson.D{ {"_id", "$dateObj"}, //{"_id", bson.M{"$subtract": []interface{}{ // bson.M{"$subtract": []interface{}{"$dateObj", bson.M{"$dateFromString": bson.M{"dateString": "1970-01-01T00:00:00Z"}}}}, // bson.M{"$mod": []interface{}{bson.M{"$subtract": []interface{}{"$dateObj", bson.M{"$dateFromString": bson.M{"dateString": "1970-01-01T00:00:00Z"}}}}, timeStamp}}, //}}}, //{"_id", bson.M{ // "date": "$dateObj", // 用实际时间字段替换dateField // "minutes": bson.M{"$toInt": bson.M{"$divide": []interface{}{"$dateObj", timeStamp}}}, //}}, {"highestPrice", bson.D{{"$max", "$high_price"}}}, {"lowestPrice", bson.D{{"$min", "$low_price"}}}, {"openPrice", bson.D{{"$first", "$open_price"}}}, {"closePrice", bson.D{{"$last", "$close_price"}}}, {"volume", bson.D{{"$max", "$num"}}}, }}} project := bson.D{ {"$project", bson.D{ {"_id", 0}, {"time", bson.D{ {"$dateToString", bson.D{ {"format", "%Y-%m-%d %H:%M:%S"}, {"date", "$_id"}, }}}}, //{"time", bson.D{{"$toDate", "$_id"}}}, //{"time", "$_id"}, {"highestPrice", 1}, {"lowestPrice", 1}, {"openPrice", 1}, {"closePrice", 1}, {"volume", bson.D{{"$toString", "$volume"}}}, }}} sort := bson.D{{"$sort", bson.D{{"time", 1}}}} operations := mongo.Pipeline{addFields, match, group, project, sort} mapList, err := data.MgoAggregate(data.ForexKLine, operations) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "MgoAggregate err", internal.QueryError)) return } applogger.Debug("查询数据:%v", mapList) switch v := mapList.(type) { case []map[string]interface{}: v = mapList.([]map[string]interface{}) for _, value := range v { var md model.KlineList if highest, ok := value["highestPrice"].(string); ok { md.HighPrice = highest } if lowest, ok := value["lowestPrice"].(string); ok { md.LowPrice = lowest } if open, ok := value["openPrice"].(string); ok { md.OpenPrice = open } if closeP, ok := value["closePrice"].(string); ok { md.ClosePrice = closeP } if volume, ok := value["volume"].(string); ok { md.Volume = volume } if times, ok := value["time"].(string); ok { loc, err := time.LoadLocation("Local") // 加载本地时区 if err != nil { applogger.Error("加载时区失败:", err) continue } parsedTime, err := time.ParseInLocation("2006-01-02 15:04:05", times, loc) if err != nil { applogger.Error("解析时间字符串失败:", err) continue } md.Timestamp = strconv.Itoa(int(parsedTime.Unix())) } md.Turnover = strconv.Itoa(0) applogger.Debug("数据解析为:%v", md) klineNew.Data.KlineList = append(klineNew.Data.KlineList, md) } default: } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, klineNew.Data, internal.QuerySuccess)) } // 最新k线查询 func ForexAggregatesNewPost(c *gin.Context) { param := model.ConstructParametersPost{} err := c.BindJSON(¶m) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param error", internal.QueryError)) return } else if len(param.Trace) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param null", internal.QueryError)) return } else if len(param.Data.DataList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "token error", internal.QueryError)) return } queryStr, err := json.Marshal(¶m) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } bodyStr, err := internal.HttpPost(fmt.Sprintf("%v?token=%v", UrlBatchKline, TokenTrade), string(queryStr)) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } var klineNew model.KlinePostReturnStruct if err = json.Unmarshal([]byte(bodyStr), &klineNew); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, klineNew.Data.KlineList, internal.QuerySuccess)) } // 最新盘口报价查询 func ForexAggregatesDepthTick(c *gin.Context) { param := model.OrderBookOrTradeTick{} err := c.BindJSON(¶m) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param error", internal.QueryError)) return } else if len(param.Trace) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param null", internal.QueryError)) return } else if len(param.Data.SymbolList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "token error", internal.QueryError)) return } req, err := http.NewRequest("GET", UrlDepthTick, nil) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } // 参数构造 q := req.URL.Query() q.Add("token", TokenTrade) queryStr, err := json.Marshal(¶m) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } q.Add("query", string(queryStr)) req.URL.RawQuery = q.Encode() resp, err := http.DefaultClient.Do(req) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } defer resp.Body.Close() bodyStr, err := ioutil.ReadAll(resp.Body) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } var depthNew model.DepthReturnStruct if err = json.Unmarshal(bodyStr, &depthNew); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, depthNew.Data, internal.QuerySuccess)) } // 最新成交价批量查询 func ForexAggregatesTradeTick(c *gin.Context) { param := model.OrderBookOrTradeTick{} err := c.BindJSON(¶m) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param error", internal.QueryError)) return } else if len(param.Trace) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param null", internal.QueryError)) return } else if len(param.Data.SymbolList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "token error", internal.QueryError)) return } req, err := http.NewRequest("GET", UrlTradeTick, nil) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } // 参数构造 q := req.URL.Query() q.Add("token", TokenTrade) queryStr, err := json.Marshal(¶m) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } q.Add("query", string(queryStr)) req.URL.RawQuery = q.Encode() resp, err := http.DefaultClient.Do(req) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, nil, internal.QueryError)) return } defer resp.Body.Close() bodyStr, err := ioutil.ReadAll(resp.Body) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } var tradeNew model.TradeReturnStruct if err = json.Unmarshal(bodyStr, &tradeNew); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, tradeNew.Data, internal.QuerySuccess)) } // 查询大宗列表 func ForexSymbolListNew(c *gin.Context) { pageSize := internal.IntegerInit(internal.ReplaceStr(c.Query("pageSize"))) // 每页显示多少条数据 pageNumber := internal.IntegerInit(internal.ReplaceStr(c.Query("pageNumber"))) // 第几页 sort := internal.IntegerInit(internal.ReplaceStr(c.Query("sort"))) // Code排序 search := internal.ReplaceStr(c.Query("search")) // 搜索数据(模糊) category := internal.ReplaceStr(c.Query("category")) // 类型:外汇(FX)、贵金属(Metals)、能源(Energy) if len(category) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "Parameter cannot be empty", internal.QueryError)) return } var filter bson.M if len(search) > 0 { // 模糊搜索 switch category { case "FX": // 外汇 filter = bson.M{"$and": []bson.M{{"symbol": bson.M{"$regex": search}}, {"symbol": bson.M{"$regex": "USD$"}}, {"category": category}}} default: // 贵金属和能源 filter = bson.M{"symbol": bson.M{"$regex": search}, "category": category} } } else { // 正常加载列表 filter = bson.M{"$and": []bson.M{{"category": category}, {"symbol": bson.M{"$regex": "USD$"}}}} } total, err := data.MgoFindTotal(data.ForexListBak, filter) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } pageData := make([]stock.ForexDataNew, 0) if sort == 0 { sort = -1 } data.MgoPagingFindStruct(data.ForexListBak, filter, int64(pageSize), int64(pageNumber), "symbol", sort, &pageData) pageDataList := make([]stock.ForexDataNew, 0) redisShield := HashValue(RedisFOREX) // 过滤屏蔽的交易对 for _, value := range pageData { strMap, ok := redisShield[value.Code] if !ok { pageDataList = append(pageDataList, value) } else { if strMap.Status == 1 { pageDataList = append(pageDataList, value) } } } var md stock.MgoPageSize md.PageSize = pageSize md.PageNumber = pageNumber md.Total = total md.Data = pageDataList applogger.Debug("查询数据: %v", len(pageDataList)) if len(pageDataList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } // 查询大宗收藏列表 func ForexFreeSymbolListNew(c *gin.Context) { id := internal.ReplaceStr(c.Query("id")) // 用户ID market_type := internal.IntegerInit(internal.ReplaceStr(c.Query("market_type"))) // 外汇市场 pageSize := internal.IntegerInit(internal.ReplaceStr(c.Query("pageSize"))) // 每页显示多少条数据 pageNumber := internal.IntegerInit(internal.ReplaceStr(c.Query("pageNumber"))) // 第几页 if len(id) <= 0 || market_type <= 0 || pageSize <= 0 || pageNumber <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "Parameter cannot be empty", internal.QueryError)) return } var md stock.MgoPageSize pageDataList := make([]stock.ForexDataNew, 0) md.PageSize = pageSize md.PageNumber = pageNumber userIdKey := fmt.Sprintf("%v%v", FreeSymbolKey, id) result, err := red.Get_Cache_Byte(userIdKey) if err != nil { md.Data = pageDataList c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QueryError)) return } var freeList []StockSymbol if err = json.Unmarshal(result, &freeList); err != nil { md.Data = pageDataList c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QueryError)) return } // 组合需要查询自选缓存股票code var symbolList []string for _, value := range freeList { if market_type == value.MarketType { symbolList = append(symbolList, value.Code) } } filter := bson.M{"$and": []bson.M{{"symbol": bson.M{"$in": symbolList}}, {"symbol": bson.M{"$regex": "USD$"}}}} total, err := data.MgoFindTotal(data.ForexListBak, filter) if err != nil { md.Total = int64(len(symbolList)) md.Data = pageDataList c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } pageData := make([]stock.ForexDataNew, 0) if err = data.MgoPagingFindStruct(data.ForexListBak, filter, int64(pageSize), int64(pageNumber), "symbol", -1, &pageData); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } redisShield := HashValue(RedisFOREX) // 过滤屏蔽的交易对 for _, value := range pageData { strMap, ok := redisShield[value.Code] if !ok { pageDataList = append(pageDataList, value) } else { if strMap.Status == 1 { pageDataList = append(pageDataList, value) } } } md.Total = total md.Data = pageDataList applogger.Debug("查询数据: %v", len(pageDataList)) if len(pageDataList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } // 查询大宗成交报价 func ForexTradeList(c *gin.Context) { code := internal.ReplaceStr(c.Query("code")) // 外汇code limit := internal.IntegerInit(internal.ReplaceStr(c.Query("limit"))) // 查询多少条数据 if len(code) == 0 || limit == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "Parameter cannot be empty", internal.QueryError)) return } res := make([]model.ForexTradeList, 0) filter := bson.M{"code": code} if err := data.MgoFindForexToStr(data.ForexTradeList, filter, int64(limit), &res); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, []model.ForexTradeList{}, internal.QuerySuccess)) return } if len(res) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, []model.ForexTradeList{}, internal.QuerySuccess)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, res, internal.QuerySuccess)) } // ForexSymbolList // // @Description: 外汇代码列表查询 http://127.0.0.1:88/stock/share/exchange-symbol-list?pageNumber=1&pageSize=35&&search= // @param c func ForexSymbolList(c *gin.Context) { pageSize := internal.IntegerInit(internal.ReplaceStr(c.Query("pageSize"))) // 每页显示多少条数据 pageNumber := internal.IntegerInit(internal.ReplaceStr(c.Query("pageNumber"))) // 第几页 sort := internal.IntegerInit(internal.ReplaceStr(c.Query("sort"))) // Code排序 search := internal.ReplaceStr(c.Query("search")) // 搜索数据(模糊) var filter bson.M if len(search) > 0 { // 模糊搜索 filter = bson.M{"$and": []bson.M{{"ticker": bson.M{"$regex": search}}, {"ticker": bson.M{"$regex": "USD$"}}}} } else { filter = bson.M{"ticker": bson.M{"$regex": "USD$"}} } filter["day.o"] = bson.M{"$gt": 0.0} filter["day.h"] = bson.M{"$gt": 0.0} filter["day.l"] = bson.M{"$gt": 0.0} filter["day.c"] = bson.M{"$gt": 0.0} total, err := data.MgoFindTotal(data.ForexList, filter) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } pageData := make([]stock.ForexData, 0) if sort == 0 { sort = -1 } data.MgoPagingFindStruct(data.ForexList, filter, int64(pageSize), int64(pageNumber), "updated", sort, &pageData) pageDataList := make([]stock.ForexData, 0) redisShield := HashValue(RedisFOREX) // 过滤屏蔽的交易对 for _, value := range pageData { if value.Day.C == 0 && value.Day.O == 0 && value.Day.H == 0 && value.Day.L == 0 { continue } strMap, ok := redisShield[value.Ticker] if !ok { pageDataList = append(pageDataList, value) } else { if strMap.Status == 1 { pageDataList = append(pageDataList, value) } } } var md stock.MgoPageSize md.PageSize = pageSize md.PageNumber = pageNumber md.Total = total md.Data = pageDataList applogger.Debug("查询数据: %v", len(pageDataList)) if len(pageDataList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } // ForexFreeSymbolList func ForexFreeSymbolList(c *gin.Context) { id := internal.ReplaceStr(c.Query("id")) // 用户ID market_type := internal.IntegerInit(internal.ReplaceStr(c.Query("market_type"))) // 外汇市场 pageSize := internal.IntegerInit(internal.ReplaceStr(c.Query("pageSize"))) // 每页显示多少条数据 pageNumber := internal.IntegerInit(internal.ReplaceStr(c.Query("pageNumber"))) // 第几页 if len(id) <= 0 || market_type <= 0 || pageSize <= 0 || pageNumber <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "参数不能为空", internal.QueryError)) return } var md stock.MgoPageSize pageDataList := make([]stock.ForexData, 0) md.PageSize = pageSize md.PageNumber = pageNumber userIdKey := fmt.Sprintf("%v%v", FreeSymbolKey, id) result, err := red.Get_Cache_Byte(userIdKey) if err != nil { md.Data = pageDataList c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QueryError)) return } var freeList []StockSymbol if err = json.Unmarshal(result, &freeList); err != nil { md.Data = pageDataList c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QueryError)) return } // 组合需要查询自选缓存股票code var symbolList []string for _, value := range freeList { if market_type == value.MarketType { symbolList = append(symbolList, value.Code) } } filter := bson.M{"$and": []bson.M{{"ticker": bson.M{"$in": symbolList}}, {"ticker": bson.M{"$regex": "USD$"}}}} filter["day.o"] = bson.M{"$gt": 0.0} filter["day.h"] = bson.M{"$gt": 0.0} filter["day.l"] = bson.M{"$gt": 0.0} filter["day.c"] = bson.M{"$gt": 0.0} total, err := data.MgoFindTotal(data.ForexList, filter) if err != nil { md.Total = int64(len(symbolList)) md.Data = pageDataList c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } pageData := make([]stock.ForexData, 0) if err = data.MgoPagingFindStruct(data.ForexList, filter, int64(pageSize), int64(pageNumber), "updated", -1, &pageData); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } redisShield := HashValue(RedisFOREX) // 过滤屏蔽的交易对 for _, value := range pageData { if value.Day.C == 0 && value.Day.O == 0 && value.Day.H == 0 && value.Day.L == 0 { continue } strMap, ok := redisShield[value.Ticker] if !ok { pageDataList = append(pageDataList, value) } else { if strMap.Status == 1 { pageDataList = append(pageDataList, value) } } } md.Total = total md.Data = pageDataList applogger.Debug("查询数据: %v", len(pageDataList)) if len(pageDataList) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } // ForexAllTickers // // @Description: 查询所有外汇代码列表 https://api.polygon.io/v2/snapshot/locale/global/markets/forex/tickers?apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexAllTickers(c *gin.Context) { url := fmt.Sprintf("https://%v/v2/snapshot/locale/global/markets/forex/tickers?apiKey=%v", config.Config.ShareGather.PolygonHost, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) var listCode model.ForexDataResponse if err = json.Unmarshal([]byte(bodyStr), &listCode); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } var dataList []mongo.WriteModel for _, v := range listCode.Tickers { code := strings.Split(v.Ticker, ":") if len(code) < 2 { continue } codeStr := code[1] filter := bson.M{ "ticker": codeStr, } updateData := bson.M{} updateData["todaysChange"] = v.TodaysChange updateData["todaysChangePerc"] = v.TodaysChangePerc updateData["updated"] = v.Updated updateData["day"] = v.Day updateData["lastQuote"] = v.LastQuote updateData["min"] = v.Min updateData["prevDay"] = v.PrevDay update := bson.M{"$set": updateData} models := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update).SetUpsert(true) dataList = append(dataList, models) } if len(dataList) > 0 { if err = data.MgoBulkWrite(data.ForexList, dataList); err != nil { applogger.Error("MgoInsertMany err:%v", err) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "operation failure", internal.QueryError)) return } } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, dataList, internal.QuerySuccess)) } // ForexTicker // // @Description: 获取单个外汇数据 https://api.polygon.io/v2/snapshot/locale/global/markets/forex/tickers/C:EURUSD?apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexTicker(c *gin.Context) { code := internal.ReplaceStr(c.Query("code")) // 外汇代码 if len(code) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } url := fmt.Sprintf("https://%v/v2/snapshot/locale/global/markets/forex/tickers/C:%v?apiKey=%v", config.Config.ShareGather.PolygonHost, code, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ForexPreviousClose // // @Description: 获取指定外汇代码前一天数据 https://api.polygon.io/v2/aggs/ticker/C:EURUSD/prev?adjusted=true&apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexPreviousClose(c *gin.Context) { code := internal.ReplaceStr(c.Query("code")) // 外汇代码 if len(code) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } url := fmt.Sprintf("https://%v/v2/aggs/ticker/C:%v/prev?adjusted=true&apiKey=%v", config.Config.ShareGather.PolygonHost, code, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ForexGroupedDaily // // @Description: 获取每日分组数据 https://api.polygon.io/v2/aggs/grouped/locale/global/market/fx/2023-01-09?adjusted=true&apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexGroupedDaily(c *gin.Context) { date := internal.ReplaceStr(c.Query("date")) // 查询时间 if len(date) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } url := fmt.Sprintf("https://%v/v2/aggs/grouped/locale/global/market/fx/%v?adjusted=true&apiKey=%v", config.Config.ShareGather.PolygonHost, date, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ForexQuotesBBO // // @Description: 行情(BBO) 获取给定时间范围内某个股票代码的 BBO 报价。 https://api.polygon.io/v3/quotes/C:EUR-USD?order=desc&limit=1000&sort=timestamp&apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexQuotesBBO(c *gin.Context) { code := internal.ReplaceStr(c.Query("code")) // 代码 if len(code) >= 3 { firstThree := code[0:3] lastThree := code[len(code)-3:] // 从字符串长度减去 3 开始到结束 code = fmt.Sprintf("%v-%v", firstThree, lastThree) } else { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } url := fmt.Sprintf("https://%v/v3/quotes/C:%v?order=desc&limit=100&sort=timestamp&apiKey=%v", config.Config.ShareGather.PolygonHost, code, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ForexLastQuote // // @Description: 货币对的最后报价 https://api.polygon.io/v1/last_quote/currencies/AUD/USD?apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexLastQuote(c *gin.Context) { code := internal.ReplaceStr(c.Query("code")) // 代码 var firstCode, lastCode string if len(code) >= 3 { firstCode = code[0:3] lastCode = code[len(code)-3:] // 从字符串长度减去 3 开始到结束 } else { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } url := fmt.Sprintf("https://%v/v1/last_quote/currencies/%v/%v?apiKey=%v", config.Config.ShareGather.PolygonHost, firstCode, lastCode, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ForexRealTimeCurrency // // @Description: 实时货币兑换 https://api.polygon.io/v1/conversion/AUD/USD?amount=100&precision=2&apiKey=3uX9zgBRPFl6MS11t4CsIrIZ_s_o2nh9 // @param c func ForexRealTimeCurrency(c *gin.Context) { var firstCode, lastCode string code := internal.ReplaceStr(c.Query("code")) // 代码 amount := internal.IntegerInit(internal.ReplaceStr(c.Query("amount"))) // 查询数量 默认:100 precision := internal.IntegerInit(internal.ReplaceStr(c.Query("precision"))) // 精度 0,1,2,3,4 默认:2 if len(code) >= 3 { firstCode = code[0:3] lastCode = code[len(code)-3:] // 从字符串长度减去 3 开始到结束 } else { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "Parameter error!", internal.QueryError)) return } if amount < 1 { amount = 100 } if precision < 0 || precision > 4 { precision = 2 } url := fmt.Sprintf("https://%v/v1/conversion/%v/%v?amount=%v&precision=%v&apiKey=%v", config.Config.ShareGather.PolygonHost, firstCode, lastCode, amount, precision, config.Config.ShareGather.PolygonKey) applogger.Debug("查询数据信息:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, err, internal.QueryError)) return } applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // HashValue // // @Description: 获取屏蔽的外汇交易对 // @param hashListName // @return []stock.PHPData func HashValue(hashListName string) map[string]stock.PHPData { keys := red.Scan(hashListName) result := make(map[string]stock.PHPData, 0) for _, v := range keys { res, _ := red.HGetAll(v) item := stock.PHPData{} for field, value := range res { switch field { case "name": item.Name = value case "code": item.Code = value case "keep_decimal": item.KeepDecimal = value case "face_value": item.FaceValue, _ = strconv.Atoi(value) case "max_pry": item.MaxPry, _ = strconv.Atoi(value) case "min_pry": item.MinPry, _ = strconv.Atoi(value) case "logo_link": item.LogoLink = value case "status": item.Status, _ = strconv.Atoi(value) } } result[item.Code] = item } return result }