You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

949 lines
33 KiB

2 months ago
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(&param)
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(&param)
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(&param)
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(&param)
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(&param)
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(&param)
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
}