package processor import ( "encoding/json" "fmt" "github.com/360EntSecGroup-Skylar/excelize" "github.com/gin-gonic/gin" "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "log" "net/http" "strconv" "strings" "time" "wss-pool/cmd/common" "wss-pool/config" "wss-pool/internal" "wss-pool/internal/data" "wss-pool/internal/data/business" "wss-pool/internal/data/mysqlbusiness" red "wss-pool/internal/redis" "wss-pool/logging/applogger" "wss-pool/pkg/model" "wss-pool/pkg/model/stock" ) var topIc = "https://" var TotalSize int = 10 var FreeSymbolKey = "USER:MARKET:" var MarketType = map[int]string{ 1: "spots", 2: "contract", 3: "US", 4: "Indonesia", 5: "Malaysia", 6: "Thailand", 7: "India", 9: "Singapore", 12: "HongKong", 14: "UK", 15: "France", 16: "Germany", 17: "Brazil", 18: "Japan", 19: "Forex", } type StockSymbol struct { Code string `json:"code"` Name string `json:"name"` MarketType int `json:"market_type"` TradeNumericCode string `json:"trade_numeric_code"` } // ExchangeSymbolList Obtain stock list data func ExchangeSymbolList(c *gin.Context) { /* 股票代码查询业务逻辑 1、列表查询传入交易所代码 2、搜索 1、带有交易所代码--模糊查询当前交易所的股票 2、不带有交易所代码--模糊查询所有股票 3、查询股票列表不排序,搜索排序(通过code排序) */ symbol := internal.ReplaceStr(c.Query("symbol")) // 国家(美股,马股) 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")) // 搜索数据(模糊 code := internal.ReplaceStr(c.Query("code")) // 多个代码 primaryExchange := internal.ReplaceStr(c.Query("primaryExchange")) // 交易所 if pageSize <= 0 || pageNumber <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "分页参数不能为零", internal.QueryError)) return } var condition = symbol var filter bson.M if len(search) > 0 { //香港股票代码特殊处理 if symbol == "HongKong" { numberWithoutLeadingZeros := strings.TrimLeft(search, "0") if numberWithoutLeadingZeros == "" { numberWithoutLeadingZeros = "0" } search = numberWithoutLeadingZeros } if len(primaryExchange) > 0 { filter = bson.M{"Country": condition, "Exchange": primaryExchange, "$or": []bson.M{{"Code": bson.M{"$regex": search}}, {"Name": bson.M{"$regex": search}}, {"NumericCode": bson.M{"$regex": search}}}, "YesterdayClose": bson.M{"$ne": ""}} } else { filter = bson.M{"Country": condition, "$or": []bson.M{{"Code": bson.M{"$regex": search}}, {"Name": bson.M{"$regex": search}}, {"NumericCode": bson.M{"$regex": search}}}, "YesterdayClose": bson.M{"$ne": ""}, "Exchange": bson.M{"$exists": true}} } } else { if len(primaryExchange) > 0 { filter = bson.M{"Country": condition, "Exchange": primaryExchange, "YesterdayClose": bson.M{"$ne": ""}} } else { filter = bson.M{"Country": condition, "YesterdayClose": bson.M{"$ne": ""}, "Exchange": bson.M{"$exists": true}} } } codeSearch := "Code" if symbol == "Malaysia" { codeSearch = "NumericCode" } str_s := strings.Split(code, "-") if len(code) > 0 { if len(primaryExchange) > 0 { filter = bson.M{"Country": condition, "Exchange": primaryExchange, "YesterdayClose": bson.M{"$ne": ""}, codeSearch: bson.M{"$in": str_s}} } else { filter = bson.M{"Country": condition, "YesterdayClose": bson.M{"$ne": ""}, codeSearch: bson.M{"$in": str_s}, "Exchange": bson.M{"$exists": true}} } } total, err := data.MgoFindTotal(data.StockList, filter) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("查询数据表: %v", data.StockList) //applogger.Debug("查询数据总数: %v", total) pageData := make([]stock.StockPolygon, 0) if sort == 0 { sort = -1 } sortField := "Vol" if symbol == "US" { sortField = "DP" } //applogger.Debug("查询条件: %v", filter) var md stock.MgoPageSize md.PageSize = pageSize md.PageNumber = pageNumber md.Total = total if err = data.MgoPagingFindStruct(data.StockList, filter, int64(pageSize), int64(pageNumber), sortField, sort, &pageData); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } //applogger.Debug("查询数据: %", len(pageData)) if len(pageData) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } var dataStockPolygon = make([]stock.StockPolygon, 0) for k, v := range pageData { key := business.StockClosingPrice[fmt.Sprintf("%sNew", v.Locale)] pageData[k].ClosePrice, err = red.Hget(key, v.Code) if err != nil { continue } if common.IsExistStock(v.Locale, v.Code) { dataStockPolygon = append(dataStockPolygon, pageData[k]) } } md.Data = dataStockPolygon c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } // ExchangeFreeSymbolList exchange free symbol list func ExchangeFreeSymbolList(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 pageSize <= 0 || pageNumber <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "分页参数不能为零", internal.QueryError)) return } if len(id) <= 0 || market_type <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "参数不能为空", internal.QueryError)) return } var md stock.MgoPageSize var dataStockPolygon = make([]stock.StockPolygon, 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 = dataStockPolygon 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 = dataStockPolygon c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QueryError)) return } // 组合需要查询自选缓存股票code var symbolList []string var condition = MarketType[market_type] for _, value := range freeList { if market_type == value.MarketType { symbolList = append(symbolList, value.Code) } } //applogger.Debug("查询自选股票列表: %v", symbolList) codeSearch := "Code" if condition == "Malaysia" { codeSearch = "NumericCode" } filter := bson.M{"Country": condition, "YesterdayClose": bson.M{"$ne": ""}, codeSearch: bson.M{"$in": symbolList}, "Exchange": bson.M{"$exists": true}} total, err := data.MgoFindTotal(data.StockList, filter) if err != nil { md.Total = int64(len(symbolList)) md.Data = dataStockPolygon c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QueryError)) return } //applogger.Debug("查询数据表: %v", data.StockList) //applogger.Debug("查询数据总数: %v", total) //applogger.Debug("查询条件: %v", filter) md.Total = total var pageData = make([]stock.StockPolygon, 0) sortField := "Vol" if condition == "US" { sortField = "DP" } if err = data.MgoPagingFindStruct(data.StockList, filter, int64(pageSize), int64(pageNumber), sortField, -1, &pageData); err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } if len(pageData) <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) return } for k, v := range pageData { key := business.StockClosingPrice[fmt.Sprintf("%sNew", v.Locale)] pageData[k].ClosePrice, err = red.Hget(key, v.Code) if err != nil { continue } if common.IsExistStock(v.Locale, v.Code) { dataStockPolygon = append(dataStockPolygon, pageData[k]) } } md.Data = dataStockPolygon c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, md, internal.QuerySuccess)) } func UpdateKeepDecimal(c *gin.Context) { param := model.Data{} err := c.BindJSON(¶m) if err != nil { applogger.Error("BindJSON", err.Error()) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "param error", internal.QueryError)) return } else if param.StockCode == "" { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "stock_code null", internal.QueryError)) return } else if param.KeepDecimal == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "keep_decimal error", internal.QueryError)) return } else if param.Currency == "" { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "currency error", internal.QueryError)) return } filterS := bson.D{{"Code", bson.M{ "$eq": param.StockCode, }}, {"Currency", bson.M{ "$eq": param.Currency, }}} updateData := bson.M{ "$set": bson.M{ "Code": param.StockCode, "KeepDecimal": param.KeepDecimal, }} if err := data.MgoUpdateOne(data.StockList, filterS, updateData); err != nil { applogger.Error("MgoBulkWrite update err:%v", err) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err.Error(), internal.QueryError)) return } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "ok", internal.QuerySuccess)) } func SymbolToExcel(c *gin.Context) { country := internal.ReplaceStr(c.Query("country")) filter := bson.M{} if country != "" { filter = bson.M{"Country": country} } else { country = "total" } res := make([]stock.StockPolygon, 0) data.MgoFindRes(data.StockList, filter, &res) // 创建Excel文件 file := excelize.NewFile() sheetName := "Sheet1" // 写入表头 file.SetCellValue(sheetName, "A1", "Code") file.SetCellValue(sheetName, "B1", "Name") file.SetCellValue(sheetName, "C1", "Country") file.SetCellValue(sheetName, "D1", "PrimaryExchange") file.SetCellValue(sheetName, "E1", "Symbol") file.SetCellValue(sheetName, "F1", "NumericCode") file.SetCellValue(sheetName, "G1", "Intro") file.SetCellValue(sheetName, "H1", "JapanIntro") file.SetCellValue(sheetName, "I1", "JapanName") // 写入数据 row := 2 // 从第二行开始写入数据 for _, val := range res { if val.PrimaryExchange == "" { applogger.Error(val.Locale, val.Code, " not Exchange") continue } file.SetCellValue(sheetName, "A"+strconv.Itoa(row), val.Code) file.SetCellValue(sheetName, "B"+strconv.Itoa(row), val.Name) file.SetCellValue(sheetName, "C"+strconv.Itoa(row), val.Locale) file.SetCellValue(sheetName, "D"+strconv.Itoa(row), val.PrimaryExchange) file.SetCellValue(sheetName, "E"+strconv.Itoa(row), val.Symbol) file.SetCellValue(sheetName, "F"+strconv.Itoa(row), val.NumericCode) file.SetCellValue(sheetName, "G"+strconv.Itoa(row), val.Intro) file.SetCellValue(sheetName, "H"+strconv.Itoa(row), val.JapanIntro) file.SetCellValue(sheetName, "I"+strconv.Itoa(row), val.JapanName) row++ } // 保存文件 err := file.SaveAs(fmt.Sprintf("/home/ubuntu/wss-server/%s.xlsx", country)) if err != nil { log.Fatal(err) } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "", internal.QuerySuccess)) } func ExcelToSymbolByJapanJson(c *gin.Context) { data.Mgo_init(config.Config.Mongodb) f, err := excelize.OpenFile("/home/ubuntu/wss-server/Japan.xlsx") if err != nil { applogger.Error("ExcelToSymbol err:%v", err) return } var dataList []mongo.WriteModel // 获取 Sheet1 上所有单元格 rows := f.GetRows("Sheet1") for k, row := range rows { if k == 0 { continue } applogger.Debug("Code:%v", row[0]) applogger.Debug("Name:%v", row[1]) applogger.Debug("Country:%v", row[2]) applogger.Debug("PrimaryExchange:%v", row[3]) applogger.Debug("Symbol:%v", row[4]) applogger.Debug("NumericCode:%v", row[5]) applogger.Debug("Intro:%v", row[6]) applogger.Debug("JapanIntro:%v", row[7]) applogger.Debug("JapanName:%v", row[8]) filter := bson.D{{"Code", bson.M{ "$eq": row[0], }}, {"Country", bson.M{ "$eq": row[2], }}} update := bson.D{{"$set", bson.D{ {"Code", row[0]}, {"Name", row[1]}, {"Country", row[2]}, {"PrimaryExchange", row[3]}, {"Symbol", row[4]}, {"NumericCode", row[5]}, {"Intro", row[6]}, {"JapanIntro", row[7]}, {"JapanName", row[8]}, }}} models := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update).SetUpsert(true) dataList = append(dataList, models) } if len(dataList) > 0 { if err = data.MgoBulkWrite(data.StockList, 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, "", "ok")) } func ExcelToSymbolByJapan(c *gin.Context) { data.Mgo_init(config.Config.Mongodb) f, err := excelize.OpenFile("/home/ubuntu/wss-server/Japan_edited.xlsx") if err != nil { applogger.Error("ExcelToSymbol err:%v", err) return } var dataList []mongo.WriteModel // 获取 Sheet1 上所有单元格 rows := f.GetRows("Sheet1") for k, row := range rows { if k == 0 { continue } applogger.Debug("Name:%v", row[0]) applogger.Debug("Symbol:%v", row[1]) applogger.Debug("JapanName:%v", row[2]) filter := bson.D{{"Symbol", bson.M{"$eq": row[1]}}} update := bson.D{{"$set", bson.D{{"JapanName", row[2]}}}} models := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update).SetUpsert(true) dataList = append(dataList, models) } if len(dataList) > 0 { if err = data.MgoBulkWrite(data.StockList, 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, "", "ok")) } func ExcelToForexCode(c *gin.Context) { data.Mgo_init(config.Config.Mongodb) f, err := excelize.OpenFile("/home/ubuntu/wss-server/forex_code.xlsx") if err != nil { applogger.Error("ExcelToSymbol err:%v", err) return } var dataList []mongo.WriteModel applogger.Debug("这里执行了。。。。。") // 获取 Sheet1 上所有单元格 rows := f.GetRows("Sheet1") for k, row := range rows { if k == 0 { continue } applogger.Debug("code:%v", row[0]) applogger.Debug("name:%v", row[1]) applogger.Debug("category:%v", row[2]) applogger.Debug("symbol:%v", row[3]) filter := bson.D{{"code", bson.M{"$eq": row[0]}}} update := bson.D{{"$set", bson.D{ {"code", row[0]}, {"name", row[1]}, {"category", row[2]}, {"symbol", row[3]}, }}} models := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update).SetUpsert(true) dataList = append(dataList, models) } applogger.Debug("这里执行了。。。。。111111") if len(dataList) > 0 { if err = data.MgoBulkWrite(data.ForexListBak, 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, "", "ok")) } func TickerToExcel(c *gin.Context) { filter := bson.M{} res := make([]stock.ForexData, 0) data.MgoFindRes(data.StockList, filter, &res) // 创建Excel文件 file := excelize.NewFile() sheetName := "Sheet1" // 写入表头 file.SetCellValue(sheetName, "A1", "code") file.SetCellValue(sheetName, "B1", "Name") file.SetCellValue(sheetName, "C1", "Country") file.SetCellValue(sheetName, "D1", "PrimaryExchange") file.SetCellValue(sheetName, "E1", "Symbol") file.SetCellValue(sheetName, "F1", "NumericCode") // 写入数据 row := 2 // 从第二行开始写入数据 for _, val := range res { file.SetCellValue(sheetName, "A"+strconv.Itoa(row), val.Ticker) file.SetCellValue(sheetName, "B"+strconv.Itoa(row), "") file.SetCellValue(sheetName, "C"+strconv.Itoa(row), "") file.SetCellValue(sheetName, "D"+strconv.Itoa(row), "") file.SetCellValue(sheetName, "E"+strconv.Itoa(row), "") file.SetCellValue(sheetName, "F"+strconv.Itoa(row), "") row++ } // 保存文件 //err := file.SaveAs(fmt.Sprintf("/home/ubuntu/wss-server/%s.xlsx", "forex")) err := file.SaveAs(fmt.Sprintf("./cmd/%s.xlsx", "forex")) if err != nil { log.Fatal(err) } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "", internal.QuerySuccess)) } func OptionToExcel(c *gin.Context) { symbol := internal.ReplaceStr(c.Query("option")) filter := bson.M{"Country": symbol} res := make([]model.OptionPolygon, 0) data.MgoFindRes(data.OptionList, filter, &res) // 创建Excel文件 file := excelize.NewFile() sheetName := "Sheet1" // 写入表头 file.SetCellValue(sheetName, "A1", "Code") file.SetCellValue(sheetName, "B1", "Name") file.SetCellValue(sheetName, "C1", "Country") file.SetCellValue(sheetName, "D1", "Tape") // 写入数据 row := 2 // 从第二行开始写入数据 for _, val := range res { file.SetCellValue(sheetName, "A"+strconv.Itoa(row), val.Stock) file.SetCellValue(sheetName, "B"+strconv.Itoa(row), val.Stock) file.SetCellValue(sheetName, "C"+strconv.Itoa(row), val.Country) file.SetCellValue(sheetName, "D"+strconv.Itoa(row), val.PrimaryExchange) row++ } // 保存文件 err := file.SaveAs(fmt.Sprintf("%s.xlsx", symbol)) if err != nil { log.Fatal(err) } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, "", internal.QuerySuccess)) } // IntraDisCal IntraDisCal data func IntraDisCal(c *gin.Context) { symbol := strings.ToUpper(internal.ReplaceStr(c.Query("symbol"))) // 股票代码 intMin := internal.IntegerInit(internal.ReplaceStr(c.Query("interval"))) // 查询间隔 from := internal.IntegerInit(internal.ReplaceStr(c.Query("from"))) // 开始时间 applogger.Debug("Incoming parameters:%v", symbol, intMin) if from <= 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusUnauthorized, "parameter error", internal.QueryError)) return } //filter := bson.M{"Code": symbol, "YesterdayClose": bson.M{"$ne": ""}, "BeforeClose": bson.M{"$ne": ""}} intTime := intMin * 60 * 1000 match := bson.D{ {"$match", bson.D{ {"s", symbol}, {"t", bson.D{{"$gte", from}}}}, }} group := bson.D{{ "$group", bson.D{ {"_id", bson.D{{"$subtract", bson.A{"$t", bson.D{{"$mod", bson.A{"$t", intTime}}}}}}}, {"fisrtTime", bson.D{{"$first", "$t"}}}, {"lastTime", bson.D{{"$last", "$t"}}}, {"datetime", bson.D{{"$first", bson.D{{"$dateToString", bson.D{{"format", "%Y-%m-%d %H:%M:%S"}, {"date", bson.D{{"$add", bson.A{time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), "$t", 28800000}}}}}}}}}}, {"timestamp", bson.D{{"$first", "$t"}}}, {"open", bson.D{{"$max", bson.D{{"$toDouble", "$p"}}}}}, {"high", bson.D{{"$max", bson.D{{"$toDouble", "$h"}}}}}, {"low", bson.D{{"$min", bson.D{{"$toDouble", "$l"}}}}}, {"close", bson.D{{"$min", bson.D{{"$toDouble", "$cl"}}}}}, {"volume", bson.D{{"$sum", "$v"}}}, }}} sort := bson.D{{"$sort", bson.D{{"timestamp", 1}}}} operations := mongo.Pipeline{match, group, sort} applogger.Debug("mongodb filter info: %v", operations) mapList, err := data.MgoAggregate(data.StockUs, operations) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusUnauthorized, "MgoAggregate err", internal.QueryError)) return } applogger.Debug("data info: %v", mapList) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, mapList, internal.QuerySuccess)) } // FindShareBySymbol 自选列表查询服务 func FindShareBySymbol(c *gin.Context) { auth := internal.ReplaceStr(c.Query("auth")) systemBoursesId := internal.IntegerInit(internal.ReplaceStr(c.Query("systemBoursesId"))) bourseType := internal.IntegerInit(internal.ReplaceStr(c.Query("bourseType"))) if len(auth) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusUnauthorized, internal.QueryToken, internal.QueryError)) return } check, userId, err := mysqlbusiness.GetBoUsers(auth) if err != nil { applogger.Error("GetBoUsers err: %v", err) c.JSON(http.StatusOK, internal.GinResult(http.StatusUnauthorized, internal.QueryToken, internal.QueryError)) return } if !check { c.JSON(http.StatusOK, internal.GinResult(http.StatusUnauthorized, internal.QueryToken, internal.QueryError)) return } usList, err := mysqlbusiness.GetBoUserOptionalStocksNew(bourseType, systemBoursesId, userId) if err != nil { applogger.Error("GetBoUserOptionalStocks err : %v", err) c.JSON(http.StatusOK, internal.GinResult(http.StatusUnauthorized, "", internal.QueryError)) return } var keys []bson.M for _, value := range usList { code := value.Stockcode keys = append(keys, bson.M{"Code": code}) } filter := bson.M{"$or": keys} pagedData, err := data.MgoFind(data.StockList, filter) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusInternalServerError, err, internal.QueryError)) return } pagedDataMap := make(map[string]stock.StockShare) for _, vue := range pagedData.([]primitive.M) { var stockM stock.StockShare code := vue["Code"] beforeClose := vue["BeforeClose"] yesterdayClose := vue["YesterdayClose"] fullName := vue["Name"] stockM.BeforeClose = beforeClose.(string) stockM.YesterdayClose = yesterdayClose.(string) stockM.Name = fullName.(string) pagedDataMap[code.(string)] = stockM } applogger.Debug("") var dataList []model.Data for _, value := range usList { var md model.Data dataBool := false vue, ok := pagedDataMap[value.Stockcode] if ok { md.BeforeClose = decimal.RequireFromString(vue.BeforeClose) md.YesterdayClose = decimal.RequireFromString(vue.YesterdayClose) md.FullName = vue.Name md.Id = value.Id md.BourseType = value.Boursetype md.SystemBoursesId = value.Systemboursesid md.UserId = value.Userid md.StockCode = value.Stockcode dataList = append(dataList, md) dataBool = true } if !dataBool { md.Id = value.Id md.BourseType = value.Boursetype md.SystemBoursesId = value.Systemboursesid md.UserId = value.Userid md.StockCode = value.Stockcode md.FullName = "" dataList = append(dataList, md) } dataBool = false } c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, dataList, internal.QuerySuccess)) } // Fundamentals 获取个人股票信息 func Fundamentals(c *gin.Context) { // https://eodhistoricaldata.com/api/fundamentals/AAPL.US?api_token=demo symbol := internal.ReplaceStr(c.Query("symbol")) region := internal.ReplaceStr(c.Query("region")) filter := internal.ReplaceStr(c.Query("filter")) var param string param = fmt.Sprintf("api_token=%v", config.Config.ShareGather.FinancialKey) if len(filter) > 0 { param = param + "&" + fmt.Sprintf("filter=%v", filter) } if len(symbol) == 0 { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, "参数错误", internal.QueryError)) return } qe := fmt.Sprintf("%v.%v", symbol, region) url := fmt.Sprintf("%v%v/api/fundamentals/%v?%v", topIc, config.Config.ShareGather.FinancialHost, qe, param) applogger.Debug("url info:%v", url) bodyStr := make(map[string]interface{}) data, err := red.Get_Cache_Data(symbol) applogger.Debug("数据信息:%v", data) if err != nil { applogger.Error("Get_Cache_Data err: %v", err) } if len(data) == 0 { bodyStr, err = internal.HttpGetDoNew(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("bodyStr info:%v", bodyStr) jsonStr, err := json.Marshal(bodyStr) if err != nil { applogger.Debug("http data json Marshal err: %v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } if err = red.Set_Cache_Data(symbol, jsonStr, 1440); err != nil { applogger.Error("write Set_Cache_Data info err: %v", err) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } } else { if err := json.Unmarshal([]byte(data), &bodyStr); err != nil { applogger.Error("select redis data json Unmarshal err: %v", err) } } //applogger.Debug("data info:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // FundamentalsNew Obtain individual stock information data (add close) func FundamentalsNew(c *gin.Context) { // https://eodhistoricaldata.com/api/fundamentals/AAPL.US?api_token=demo symbol := internal.ReplaceStr(c.Query("symbol")) region := internal.ReplaceStr(c.Query("region")) filter := internal.ReplaceStr(c.Query("filter")) filterM := bson.M{"Code": symbol} pagedData, err := data.MgoFind(data.StockList, filterM) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } var yesterdayClose, beforeClose string for _, vue := range pagedData.([]primitive.M) { yesterdayClose = vue["YesterdayClose"].(string) beforeClose = vue["BeforeClose"].(string) } applogger.Debug("data info: %v", yesterdayClose, beforeClose) var param string param = fmt.Sprintf("api_token=%v", config.Config.ShareGather.FinancialKey) if len(filter) > 0 { param = param + "&" + fmt.Sprintf("filter=%v", filter) } qe := fmt.Sprintf("%v.%v", symbol, region) url := fmt.Sprintf("%v%v/api/fundamentals/%v?%v", topIc, config.Config.ShareGather.FinancialHost, qe, param) applogger.Debug("info url:%v", url) bodyStr := make(map[string]interface{}) data, err := red.Get_Cache_Data(symbol) if err != nil { applogger.Error("Get_Cache_Data err: %v", err) } if len(data) == 0 { bodyStr, err = internal.HttpGetDoNew(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("bodyStr info:%v", bodyStr) jsonStr, err := json.Marshal(bodyStr) if err != nil { //applogger.Debug("http data json Marshal err: %v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } if err := red.Set_Cache_Data(symbol, jsonStr, 1440); err != nil { applogger.Error("write Set_Cache_Data info err: %v", err) c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } } else { if err := json.Unmarshal([]byte(data), &bodyStr); err != nil { applogger.Error("select redis data json Unmarshal err: %v", err) } } bodyStr["YesterdayClose"] = yesterdayClose bodyStr["BeforeClose"] = beforeClose c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // Intraday Daily historical data func Intraday(c *gin.Context) { // https://eodhistoricaldata.com/api/intraday/AAPL.US?api_token=647dd6744b94f4.20894198&fmt=json&from=1564752900&to=1564753200&interval=1m symbol := internal.ReplaceStr(c.Query("symbol")) region := internal.ReplaceStr(c.Query("region")) from := internal.ReplaceStr(c.Query("from")) interval := internal.ReplaceStr(c.Query("interval")) to := internal.ReplaceStr(c.Query("to")) var param string param = fmt.Sprintf("api_token=%v", config.Config.ShareGather.FinancialKey) if len(from) > 0 { param = param + "&" + fmt.Sprintf("from=%v", from) } if len(to) > 0 { param = param + "&" + fmt.Sprintf("to=%v", to) } if len(interval) > 0 { param = param + "&" + fmt.Sprintf("interval=%v", interval) } qe := fmt.Sprintf("%v.%v", symbol, region) url := fmt.Sprintf("%v%v/api/intraday/%v?fmt=json&%v", topIc, config.Config.ShareGather.FinancialHost, qe, param) applogger.Debug("url data info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // Eod End of Day Historical Data func Eod(c *gin.Context) { // https://eodhistoricaldata.com/api/eod/MCD.US?api_token=647dd6744b94f4.20894198&period=d&order=d&from=2017-01-05&to=2017-02-10&fmt=json symbol := internal.ReplaceStr(c.Query("symbol")) region := internal.ReplaceStr(c.Query("region")) period := internal.ReplaceStr(c.Query("period")) order := internal.ReplaceStr(c.Query("order")) from := internal.ReplaceStr(c.Query("from")) to := internal.ReplaceStr(c.Query("to")) // 条件组装 var param string param = fmt.Sprintf("api_token=%v", config.Config.ShareGather.FinancialKey) if len(order) > 0 { param = param + "&" + fmt.Sprintf("from=%v", from) } if len(period) > 0 { param = param + "&" + fmt.Sprintf("period=%v", period) } if len(from) > 0 { param = param + "&" + fmt.Sprintf("from=%v", from) } if len(to) > 0 { param = param + "&" + fmt.Sprintf("to=%v", to) } qe := fmt.Sprintf("%v.%v", symbol, region) url := fmt.Sprintf("%v%v/api/eod/%v?fmt=json&%v", topIc, config.Config.ShareGather.FinancialHost, qe, param) applogger.Debug("url data info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } /****https://polygon.io/docs/stocks/get_v2_aggs_grouped_locale_us_market_stocks__date*****/ // Aggregates 股票聚合条形图 func Aggregates(c *gin.Context) { stocksTicker := internal.ReplaceStr(c.Query("stocksTicker")) // AAPL resolution := internal.ReplaceStr(c.Query("multiplier")) //multiplier from := internal.ReplaceStr(c.Query("from")) // from to := internal.ReplaceStr(c.Query("to")) //fmt.Println(resolution, to) if strings.Contains("5,15,30,60,1", resolution) && !common.IsOpeningUS() { to = fmt.Sprintf("%d", common.GetToTime()/1000) } //fmt.Println(to) //else if timespan == "minute" && multiplier == 15 && common.IsOpeningUS() { // to = fmt.Sprintf("%d", common.GenerateSingaporeMinTimestamp(15).UnixMilli()) //} else if timespan == "minute" && multiplier == 5 && common.IsOpeningUS() { // to = fmt.Sprintf("%d", common.GenerateSingaporeMinTimestamp(5).UnixMilli()) //} url := fmt.Sprintf("%v%vstock/candle?symbol=%s&resolution=%s&from=%s&to=%s&token=%s", topIc, config.Config.FinnhubUs.FinnhubHost, stocksTicker, resolution, from, to, config.Config.FinnhubUs.FinnhubKey) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // Grouped 获取整个股票/股票市场的每日开盘价、最高价、最低价和收盘价 (OHLC) func Grouped(c *gin.Context) { // /v2/aggs/grouped/locale/us/market/stocks/{date} // https://api.polygon.io/v2/aggs/grouped/locale/us/market/stocks/2023-01-09?adjusted=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb date := internal.ReplaceStr(c.Query("date")) // date param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v2/aggs/grouped/locale/us/market/stocks/%v?adjusted=true&%v", topIc, config.Config.ShareGather.PolygonHost, date, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // OpenClose 股票每日开盘/收盘 func OpenClose(c *gin.Context) { // /v1/open-close/{stocksTicker}/{date} // https://api.polygon.io/v1/open-close/AAPL/2023-01-09?adjusted=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb stocksTicker := internal.ReplaceStr(c.Query("stocksTicker")) // AAPL date := internal.ReplaceStr(c.Query("date")) // date param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v1/open-close/%v/%v?adjusted=true&%v", topIc, config.Config.ShareGather.PolygonHost, stocksTicker, date, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // PreviousClose 上一收盘价 func PreviousClose(c *gin.Context) { // /v2/aggs/ticker/{stocksTicker}/prev // https://api.polygon.io/v2/aggs/ticker/AAPL/prev?adjusted=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb stocksTicker := internal.ReplaceStr(c.Query("stocksTicker")) // AAPL param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) var tickerList []string if len(stocksTicker) > 0 { tickerList = strings.Split(stocksTicker, ",") } var codeCloseList []stock.Results for _, ticker := range tickerList { url := fmt.Sprintf("%v%v/v2/aggs/ticker/%v/prev?adjusted=true&%v", topIc, config.Config.ShareGather.PolygonHost, ticker, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //var code string //var closePrice float64 //for key, value := range bodyStr { // switch key { // case "results": // boDay := value.([]interface{}) // for _, vue := range boDay { // switch vue.(type) { // case map[string]interface{}: // da := vue.(map[string]interface{}) // for e, v := range da { // switch e { // case "T": // code = v.(string) // case "c": // closePrice = v.(float64) // default: // } // } // default: // } // } // } //} item := stock.AggsTicke{} json.Unmarshal([]byte(bodyStr), &item) if len(item.Results) > 0 { codeCloseList = append(codeCloseList, item.Results[0]) } } applogger.Debug("data info:%v", codeCloseList) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, codeCloseList, internal.QuerySuccess)) } // Trades 获取给定时间范围内股票代码的交易 func Trades(c *gin.Context) { ticker := internal.ReplaceStr(c.Query("ticker")) // AAPL date := internal.ReplaceStr(c.Query("date")) param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/trades/%s?timestamp=%s&order=desc&%v", topIc, config.Config.ShareGather.PolygonHost, ticker, date, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // LastTrade 获取给定股票的最新交易 func LastTrade(c *gin.Context) { // /v2/last/trade/{stocksTicker} // https://api.polygon.io/v2/last/trade/AAPL?apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb stocksTicker := internal.ReplaceStr(c.Query("stocksTicker")) // AAPL param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v2/last/trade/%v?%v", topIc, config.Config.ShareGather.PolygonHost, stocksTicker, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // Quotes TODO: 获取给定时间范围内股票代码的交易 func Quotes(c *gin.Context) { // https://api.polygon.io/v3/quotes/AAPL?apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb } // LastQuote 股票的最新NBBO(报价)刻度 func LastQuote(c *gin.Context) { // /v2/last/nbbo/{stocksTicker} // https://api.polygon.io/v2/last/nbbo/AAPL?apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb stocksTicker := internal.ReplaceStr(c.Query("stocksTicker")) // AAPL param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v2/last/nbbo/%v?%v", topIc, config.Config.ShareGather.PolygonHost, stocksTicker, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // SnapshotAllTickers 所有交易股票代码的最新市场数据 func SnapshotAllTickers(c *gin.Context) { // /v2/snapshot/locale/us/markets/stocks/tickers // https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/tickers?include_otc=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v2/snapshot/locale/us/markets/stocks/tickers?include_otc=true&%v", topIc, config.Config.ShareGather.PolygonHost, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // SnapshotGainersLosers 获取股票/股票市场当前前20名涨幅或跌幅的最新市场数据 func SnapshotGainersLosers(c *gin.Context) { // /v2/snapshot/locale/us/markets/stocks/{direction} // https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/gainers?include_otc=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb // https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/losers?include_otc=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb direction := internal.ReplaceStr(c.Query("direction")) // AAPL param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v2/snapshot/locale/us/markets/stocks/%v?include_otc=true&%v", topIc, config.Config.ShareGather.PolygonHost, direction, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // SnapshotOneTicker 获取单个交易股票行情的最新市场数据 func SnapshotOneTicker(c *gin.Context) { // /v2/snapshot/locale/us/markets/stocks/tickers/{stocksTicker} // https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/tickers/AAPL?apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb stocksTicker := internal.ReplaceStr(c.Query("stocksTicker")) // AAPL param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v2/snapshot/locale/us/markets/stocks/tickers/%v?%v", topIc, config.Config.ShareGather.PolygonHost, stocksTicker, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ReferenceTicker TODO: 所有股票代码 func ReferenceTicker(c *gin.Context) { // /v3/reference/tickers // https://api.polygon.io/v3/reference/tickers?active=true&apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) url := fmt.Sprintf("%v%v/v3/reference/tickers?active=true&%v", topIc, config.Config.ShareGather.PolygonHost, param) applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // ReferenceTickerDetails 股票代码详细信息 func ReferenceTickerDetails(c *gin.Context) { // /v3/reference/tickers/{ticker} // https://api.polygon.io/v3/reference/tickers/AAPL?apiKey=CDGMfPJmyiEX5dbjagLSEipf5Y4XbXVb ticker := internal.ReplaceStr(c.Query("ticker")) // AAPL //param := fmt.Sprintf("apiKey=%v", config.Config.ShareGather.PolygonKey) // //url := fmt.Sprintf("%v%v/v3/reference/tickers/%v?%v", // topIc, config.Config.ShareGather.PolygonHost, ticker, param) // //applogger.Debug("Url info:%v", url) // //bodyStr, err := internal.HttpGet(url) //if err != nil { // c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) // return //} var bodyStr string //if strings.Contains(bodyStr, "Ticker not found") { filter := bson.M{"Country": "US", "Code": ticker} projection := bson.M{} sort := bson.M{} result, _ := data.MgoFindProjection(data.StockList, filter, projection, sort, 0) if len(result) > 0 { bodyStr = fmt.Sprintf(`{"results": {"ticker": "%s","name": "%s","market": "stocks","locale": "us","primary_exchange": "%s","description": "%s"},"status": "OK"}`, business.TypeCheck(result[0]["Code"]), business.TypeCheck(result[0]["Name"]), business.TypeCheck(result[0]["Exchange"]), business.TypeCheck(result[0]["Intro"])) } // } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) } // 股票资讯 func ReferenceTickerNews(c *gin.Context) { ticker := internal.ReplaceStr(c.Query("ticker")) // AAPL url := fmt.Sprintf("%v%vcompany-news?symbol=%s&from=%s&to=%s&token=%s", topIc, config.Config.FinnhubUs.FinnhubHost, ticker, common.NewsUsTime(-7), common.NewsUsTime(0), config.Config.FinnhubUs.FinnhubKey) if ticker == "" { url = fmt.Sprintf("%v%vnews?category=general&token=%s", topIc, config.Config.FinnhubUs.FinnhubHost, config.Config.FinnhubUs.FinnhubKey) } applogger.Debug("Url info:%v", url) bodyStr, err := internal.HttpGet(url) if err != nil { c.JSON(http.StatusOK, internal.GinResult(http.StatusBadRequest, err, internal.QueryError)) return } //applogger.Debug("第三方数据接收:%v", bodyStr) c.JSON(http.StatusOK, internal.GinResult(http.StatusOK, bodyStr, internal.QuerySuccess)) }