Ver Fonte

fix: 持仓分析表格合约加总

hsun há 1 semana atrás
pai
commit
a37ee4acb7

+ 27 - 0
controllers/trade_analysis/trade_analysis_correlation.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/data_manage/excel"
 	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
 	tradeAnalysisRequest "eta/eta_api/models/data_manage/trade_analysis/request"
@@ -68,6 +69,19 @@ func (this *TradeAnalysisCorrelationController) Preview() {
 		return
 	}
 
+	// 标的指标名称
+	if req.TableConfig.BaseEdbInfoId > 0 {
+		baseEdb, e := data_manage.GetEdbInfoById(req.TableConfig.BaseEdbInfoId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取标的指标失败, %v", e)
+			return
+		}
+		if baseEdb != nil {
+			req.TableConfig.BaseEdbName = baseEdb.EdbName
+		}
+	}
+
 	resp := new(tradeAnalysisResponse.CorrelationTableResp)
 	resp.TableName = strings.TrimSpace(req.TableName)
 	resp.ClassifyId = req.ClassifyId
@@ -282,6 +296,19 @@ func (this *TradeAnalysisCorrelationController) Detail() {
 		}
 	}
 
+	// 标的指标名称
+	if tableConfig.BaseEdbInfoId > 0 {
+		baseEdb, e := data_manage.GetEdbInfoById(tableConfig.BaseEdbInfoId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取标的指标失败, %v", e)
+			return
+		}
+		if baseEdb != nil {
+			tableConfig.BaseEdbName = baseEdb.EdbName
+		}
+	}
+
 	// 响应
 	resp := new(tradeAnalysisResponse.CorrelationTableResp)
 	resp.ExcelInfoId = excelItem.ExcelInfoId

+ 34 - 5
models/data_manage/trade_analysis/trade_analysis.go

@@ -21,11 +21,11 @@ const (
 	WarehouseDefaultFrequency = "日度"
 
 	GuangZhouTopCompanyAliasName = "日成交持仓排名" // 广期所TOP20对应的公司名称
-	GuangZhouSeatNameBuy         = "持买单量"       // 广期所指标名称中的多单名称
-	GuangZhouSeatNameSold        = "持卖单量"       // 广期所指标名称中的空单名称
-	GuangZhouTopSeatNameBuy      = "持买单量总计"   // 广期所指标名称中的TOP20多单名称
-	GuangZhouTopSeatNameSold     = "持卖单量总计"   // 广期所指标名称中的TOP20空单名称
-	GuangZhouTopSeatNameDeal     = "成交量总计"     // 广期所指标名称中的TOP20成交量名称
+	GuangZhouSeatNameBuy         = "持买单量"    // 广期所指标名称中的多单名称
+	GuangZhouSeatNameSold        = "持卖单量"    // 广期所指标名称中的空单名称
+	GuangZhouTopSeatNameBuy      = "持买单量总计"  // 广期所指标名称中的TOP20多单名称
+	GuangZhouTopSeatNameSold     = "持卖单量总计"  // 广期所指标名称中的TOP20空单名称
+	GuangZhouTopSeatNameDeal     = "成交量总计"   // 广期所指标名称中的TOP20成交量名称
 )
 
 const (
@@ -468,3 +468,32 @@ type ContractCompanyTradeEdbData struct {
 	DataTime time.Time `description:"数据日期"`
 	Val      int       `description:"数据值"`
 }
+
+// GetClassifyNewestDataTime 获取品种最新数据日期
+func GetClassifyNewestDataTime(exchange string, classifyNames []string) (dateTime time.Time, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
+		return
+	}
+	if len(classifyNames) == 0 {
+		return
+	}
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	sql := `SELECT data_time FROM %s WHERE classify_name IN (%s) ORDER BY data_time DESC LIMIT 1`
+	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(classifyNames)))
+	err = orm.NewOrmUsingDB("data").Raw(sql, classifyNames).QueryRow(&dateTime)
+	return
+}
+
+// GetGuangzhouClassifyNewestDataTime 广期所-获取品种最新数据日期
+func GetGuangzhouClassifyNewestDataTime(indexIds []int) (dateTime time.Time, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	cond := fmt.Sprintf(` AND base_from_trade_guangzhou_index_id IN (%s)`, utils.GetOrmInReplace(len(indexIds)))
+	pars := make([]interface{}, 0)
+	pars = append(pars, indexIds)
+	sql := fmt.Sprintf(`SELECT data_time FROM base_from_trade_guangzhou_data WHERE 1=1 %s ORDER BY data_time DESC LIMIT 1`, cond)
+	err = orm.NewOrmUsingDB("data").Raw(sql, pars).QueryRow(&dateTime)
+	return
+}

+ 1 - 0
models/data_manage/trade_analysis/trade_analysis_correlation.go

@@ -3,6 +3,7 @@ package trade_analysis
 // CorrelationTableExtraConfig 相关性表格配置
 type CorrelationTableExtraConfig struct {
 	BaseEdbInfoId    int                          `description:"标的指标ID"`
+	BaseEdbName      string                       `description:"标的指标名称(仅回显时用)"`
 	Exchange         string                       `description:"交易所标识"`
 	ClassifyName     string                       `description:"(单选)品种"`
 	CompanyNames     []string                     `description:"(多选)期货公司"`

+ 7 - 2
services/data/trade_analysis/trade_analysis_correlation.go

@@ -88,9 +88,14 @@ func GetCorrelationTableRowsDataByConfig(tableConfig tradeAnalysisModel.Correlat
 		}
 	}
 
+	// 获取品种最新数据日期
+	baseDate, e := GetTradeClassifyNewestDataTime(tableConfig.Exchange, []string{tableConfig.ClassifyName})
+	if e != nil {
+		err = fmt.Errorf("获取持仓品种最新数据日期失败, %v", e)
+		return
+	}
+
 	// 根据配置取出需要查询的合约
-	baseDate := time.Now() // 取合约以当日为基准(如主力合约为当日的主力合约)
-	//baseDate = time.Date(2024, 8, 12, 0, 0, 0, 0, time.Local)
 	contracts, e := GetTopContractsByType(tableConfig.ContractType, tableConfig.Exchange, tableConfig.ClassifyName, baseDate)
 	if e != nil {
 		err = fmt.Errorf("获取持仓相关性合约失败, %v", e)

+ 46 - 4
services/data/trade_analysis/trade_analysis_data.go

@@ -551,22 +551,28 @@ func GetTopContractRank(exchange string, classifyNames []string, dataDate time.T
 	items = make([]*tradeAnalysisModel.ContractTopRankData, 0)
 	for _, v := range rankData {
 		v.Exchange = exchange
+		// 郑商所-这里注意把查出来的品种和合约赋值,不然后续是乱的
+		if v.Exchange == tradeAnalysisModel.TradeExchangeZhengzhou {
+			v.ClassifyType = v.ClassifyName
+			v.ClassifyName = GetZhengzhouClassifyName(v.ClassifyName)
+		}
 		items = append(items, v)
 	}
 	return
 }
 
 // GetTableTradeData 获取多空分析表格持仓数据
-func GetTableTradeData(exchange string, classifyNames, contracts, companies []string, predictRatio float64, startDate, endDate time.Time, contractType int) (companyTradeData []*tradeAnalysisModel.ContractCompanyTradeData, err error) {
+func GetTableTradeData(exchange string, classifyName string, contracts []string, companyName string, predictRatio float64, startDate, endDate time.Time, contractType int) (companyTradeData []*tradeAnalysisModel.ContractCompanyTradeData, err error) {
+	companyTradeData = make([]*tradeAnalysisModel.ContractCompanyTradeData, 0)
+
 	// 获取合约持仓数据
-	contractTradeData, lastBuyVal, lastSoldVal, e := GetContractCompanyTradeData(exchange, classifyNames, contracts, companies, startDate, endDate)
+	contractTradeData, lastBuyVal, lastSoldVal, e := GetContractCompanyTradeData(exchange, []string{classifyName}, contracts, []string{companyName}, startDate, endDate)
 	if e != nil {
 		err = fmt.Errorf("获取合约-持仓数据失败, %v", e)
 		return
 	}
 
 	// 填充[合约-公司]预估数据, 并根据[公司-多合约]分组, [公司]算作一个指标, 指标值为[多个合约]的计算加总
-	companyTradeData = make([]*tradeAnalysisModel.ContractCompanyTradeData, 0)
 	companyContracts := make(map[string][]*tradeAnalysisModel.ContractCompanyTradeData)
 	for _, v := range contractTradeData {
 		td, fd, ed, e := PredictingTradeData(v.DataList, lastBuyVal[v.ClassifyType], lastSoldVal[v.ClassifyType], predictRatio)
@@ -664,7 +670,10 @@ func GetTableTradeData(exchange string, classifyNames, contracts, companies []st
 		sort.Slice(companyData.DataList, func(i, j int) bool {
 			return companyData.DataList[i].Date.Before(companyData.DataList[j].Date)
 		})
-		companyData.ClassifyType = strings.Join(contractArr, ",")
+		//companyData.ClassifyType = strings.Join(contractArr, ",")
+		companyData.Exchange = exchange
+		companyData.CompanyName = classifyName
+		companyData.ClassifyType = classifyName
 		companyTradeData = append(companyTradeData, companyData)
 	}
 	return
@@ -880,3 +889,36 @@ func GetContractCompanyTradeData(exchange string, classifyNames, contracts, comp
 	}
 	return
 }
+
+// GetTradeClassifyNewestDataTime 获取数据最新日期
+func GetTradeClassifyNewestDataTime(exchange string, classifyNames []string) (dataTime time.Time, err error) {
+	var tradeAnalysis TradeAnalysisInterface
+	switch exchange {
+	case tradeAnalysisModel.TradeExchangeZhengzhou:
+		tradeAnalysis = &ZhengzhouTradeAnalysis{}
+	case tradeAnalysisModel.TradeExchangeGuangzhou:
+		tradeAnalysis = &GuangzhouTradeAnalysis{}
+	default:
+		tradeAnalysis = &BaseTradeAnalysis{}
+	}
+	if exchange == tradeAnalysisModel.TradeExchangeZhengzhou {
+		classifies, e := GetZhengzhouContractsByClassifyNames(classifyNames)
+		if e != nil {
+			err = fmt.Errorf("获取郑商所实际合约失败, %v", e)
+			return
+		}
+		classifyNames = classifies
+	}
+
+	d, e := tradeAnalysis.GetClassifyNewestDataTime(exchange, classifyNames)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("获取品种最新数据日期失败, %v", e)
+		return
+	}
+	if !d.IsZero() {
+		dataTime = d
+	} else {
+		dataTime = time.Now().Local()
+	}
+	return
+}

+ 56 - 0
services/data/trade_analysis/trade_analysis_interface.go

@@ -13,6 +13,7 @@ import (
 type TradeAnalysisInterface interface {
 	GetTradeDataByContracts(exchange string, classifyNames, contracts, queryCompanies []string, startDate, endDate time.Time) (items []*tradeAnalysisModel.OriginTradeData, topItems, lastItems []*tradeAnalysisModel.OriginTradeData, err error)
 	GetContractTopRankData(exchange string, classifyNames []string, dataDate time.Time) (items []*tradeAnalysisModel.ContractTopRankData, err error)
+	GetClassifyNewestDataTime(exchange string, classifyNames []string) (dataTime time.Time, err error)
 }
 
 // BaseTradeAnalysis 通用交易所
@@ -154,10 +155,22 @@ func (b *BaseTradeAnalysis) GetContractTopRankData(exchange string, classifyName
 	return tradeAnalysisModel.GetContractTopRankData(exchange, classifyNames, dataDate)
 }
 
+func (b *BaseTradeAnalysis) GetClassifyNewestDataTime(exchange string, classifyNames []string) (dataTime time.Time, err error) {
+	return tradeAnalysisModel.GetClassifyNewestDataTime(exchange, classifyNames)
+}
+
 // ZhengzhouTradeAnalysis 郑商所
 type ZhengzhouTradeAnalysis struct{}
 
 func (z *ZhengzhouTradeAnalysis) GetTradeDataByContracts(exchange string, classifyNames, contracts, queryCompanies []string, startDate, endDate time.Time) (items, topItems, lastItems []*tradeAnalysisModel.OriginTradeData, err error) {
+	// 根据品种获取合约
+	//classifies, e := GetZhengzhouContractsByClassifyNames(classifyNames)
+	//if e != nil {
+	//	err = fmt.Errorf("获取郑商所实际合约失败, %v", e)
+	//	return
+	//}
+	//contracts = classifies
+
 	// 根据合约获取数据
 	originData, e := tradeAnalysisModel.GetZhengzhouTradeDataByContracts(contracts, startDate, endDate)
 	if e != nil {
@@ -172,6 +185,10 @@ func (z *ZhengzhouTradeAnalysis) GetContractTopRankData(exchange string, classif
 	return tradeAnalysisModel.GetZhengzhouContractTopRankData(classifyNames, dataDate)
 }
 
+func (z *ZhengzhouTradeAnalysis) GetClassifyNewestDataTime(exchange string, classifyNames []string) (dataTime time.Time, err error) {
+	return tradeAnalysisModel.GetClassifyNewestDataTime(exchange, classifyNames)
+}
+
 // GuangzhouTradeAnalysis 广期所
 type GuangzhouTradeAnalysis struct{}
 
@@ -389,3 +406,42 @@ func (g *GuangzhouTradeAnalysis) GetContractTopRankData(exchange string, classif
 	})
 	return
 }
+
+func (g *GuangzhouTradeAnalysis) GetClassifyNewestDataTime(exchange string, classifyNames []string) (dataTime time.Time, err error) {
+	// 取品种ID
+	classifyNameId := map[string]int{"si": 7, "lc": 8}
+	var classifyIds []int
+	for _, v := range classifyNames {
+		if classifyNameId[v] > 0 {
+			classifyIds = append(classifyIds, classifyNameId[v])
+		}
+	}
+
+	// 查询TOP20的最新日期
+	indexKeyword := fmt.Sprint("%", tradeAnalysisModel.GuangZhouTopCompanyAliasName, "%")
+	indexes, e := tradeAnalysisModel.GetBaseFromTradeGuangzhouIndex(classifyIds, []string{}, indexKeyword)
+	if e != nil {
+		err = fmt.Errorf("获取广期所指标失败, %v", e)
+		return
+	}
+	var indexIds []int
+	for _, v := range indexes {
+		// eg.永安期货_si2401_持买单量
+		nameArr := strings.Split(v.IndexName, "_")
+		if len(nameArr) != 3 {
+			continue
+		}
+		indexIds = append(indexIds, v.BaseFromTradeGuangzhouIndexId)
+	}
+	if len(indexIds) == 0 {
+		return
+	}
+
+	d, e := tradeAnalysisModel.GetGuangzhouClassifyNewestDataTime(indexIds)
+	if e != nil {
+		err = fmt.Errorf("获取广期品种最新数据失败, %v", e)
+		return
+	}
+	dataTime = d
+	return
+}

+ 84 - 38
services/data/trade_analysis/trade_analysis_table.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"math"
+	"strings"
 	"time"
 )
 
@@ -30,9 +31,9 @@ func CheckAnalysisTableExtraConfig(extraConfig tradeAnalysisModel.TableExtraConf
 		}
 		classifyTotal += len(v.ClassifyNames)
 	}
-	// TODO:品种选择上限
-	if classifyTotal > 5 {
-		tips = "选择品种不超过5个"
+	// 品种选择加个上限吧,过多SQL会很慢
+	if classifyTotal > 20 {
+		tips = "选择品种不超过20个"
 		return
 	}
 	typeArr := []int{tradeAnalysisModel.ContractQueryTypeTop, tradeAnalysisModel.ContractQueryTypeTop2, tradeAnalysisModel.ContractQueryTypeTop3, tradeAnalysisModel.ContractQueryTypeAll, tradeAnalysisModel.ContractQueryTypeTotal}
@@ -154,10 +155,9 @@ func GetTableRowsDataByConfig(tableConfig tradeAnalysisModel.TableExtraConfig) (
 		return
 	}
 
-	// 查询基准日期TOP20合约排名, 根据类型取出合约数
-	exchangeContracts := make(map[string][]*tradeAnalysisModel.ContractTopRankData) // 交易所最终取的合约
-	var contractMax int                                                             // 需要取出的品种对应的最大合约数
-	classifyMax := make(map[string]int)                                             // 品种对应的合约数
+	// 根据类型取出合约数
+	var contractMax int // 需要取出的品种对应的最大合约数
+	//classifyMax := make(map[string]int) // 品种对应的合约数
 	switch tableConfig.ContractType {
 	case tradeAnalysisModel.ContractQueryTypeTop:
 		contractMax = 1
@@ -168,46 +168,70 @@ func GetTableRowsDataByConfig(tableConfig tradeAnalysisModel.TableExtraConfig) (
 	case tradeAnalysisModel.ContractQueryTypeAll, tradeAnalysisModel.ContractQueryTypeTotal:
 		contractMax = 999
 	}
-	// 遍历交易所, 查询各品种下的合约排名情况及TOP当日的多空单数据
-	var sortContract []string
-	for _, v := range tableConfig.ClassifyList {
-		contractRanks, e := GetTopContractRank(v.Exchange, v.ClassifyNames, baseDate)
-		if e != nil {
-			err = fmt.Errorf("获取基准日期合约排名失败, %v", e)
-			return
-		}
-		if len(contractRanks) == 0 {
-			continue
-		}
 
-		// ps.正常来讲这里查出来的合约是唯一的, 根据品种分组, 取出ContractType所需的合约数
-		for _, rd := range contractRanks {
-			if classifyMax[rd.ClassifyName] >= contractMax {
+	var sortRules []string                                                              // 最终排序(合约加总以品种排序,其他以为合约排序)
+	classifyContractsData := make(map[string][]*tradeAnalysisModel.ContractTopRankData) // 根据品种分组的合约数据
+	classifyContracts := make(map[string][]string)                                      // 品种合约
+	for _, v := range tableConfig.ClassifyList {
+		for _, classify := range v.ClassifyNames {
+			// 获取合约排名及持仓数据
+			contractRanks, e := GetTopContractRank(v.Exchange, []string{classify}, baseDate)
+			if e != nil {
+				err = fmt.Errorf("获取基准日期合约排名失败, %v", e)
+				return
+			}
+			if len(contractRanks) == 0 {
 				continue
 			}
-			classifyMax[rd.ClassifyName] += 1
-			if exchangeContracts[rd.Exchange] == nil {
-				exchangeContracts[rd.Exchange] = make([]*tradeAnalysisModel.ContractTopRankData, 0)
+			flag := fmt.Sprintf("%s-%s", v.Exchange, classify)
+
+			// 取出指定数量的合约
+			var contractNum int
+			for _, rd := range contractRanks {
+				if contractNum >= contractMax {
+					continue
+				}
+				contractNum += 1
+				classifyContracts[classify] = append(classifyContracts[classify], rd.ClassifyType)
+
+				if classifyContractsData[flag] == nil {
+					classifyContractsData[flag] = make([]*tradeAnalysisModel.ContractTopRankData, 0)
+				}
+				classifyContractsData[flag] = append(classifyContractsData[flag], rd)
+
+				// 以合约排序
+				if tableConfig.ContractType != tradeAnalysisModel.ContractQueryTypeTotal {
+					sortRules = append(sortRules, rd.ClassifyType)
+				}
+			}
+
+			// 以品种排序
+			if tableConfig.ContractType == tradeAnalysisModel.ContractQueryTypeTotal {
+				sortRules = append(sortRules, classify)
 			}
-			exchangeContracts[rd.Exchange] = append(exchangeContracts[rd.Exchange], rd)
-			sortContract = append(sortContract, rd.ClassifyType)
 		}
 	}
 
-	// 查询对应品种与合约一周前后的多空单,填充预估值
 	mussyRows := make([]*tradeAnalysisModel.TableRowData, 0)
-	for exchange, contracts := range exchangeContracts {
-		var classifyNames, classifyTypes []string
-		for _, v := range contracts {
-			if !utils.InArrayByStr(classifyNames, v.ClassifyName) {
-				classifyNames = append(classifyNames, v.ClassifyName)
-			}
-			if !utils.InArrayByStr(classifyTypes, v.ClassifyType) {
-				classifyTypes = append(classifyTypes, v.ClassifyType)
-			}
+	for k, contracts := range classifyContractsData {
+		var exchange, classifyName string
+		keyArr := strings.Split(k, "-")
+		if len(keyArr) != 2 {
+			continue
+		}
+		exchange = keyArr[0]
+		classifyName = keyArr[1]
+		classifyTypes := classifyContracts[classifyName]
+		if len(classifyTypes) == 0 {
+			continue
+		}
+
+		// 合约加总时,contracts也需要加总,且ClassifyType为品种名
+		if tableConfig.ContractType == tradeAnalysisModel.ContractQueryTypeTotal {
+			contracts = MergeClassifyTypeTopRankData(contracts)
 		}
 
-		contractRowData, e := GetTableTradeData(exchange, classifyNames, classifyTypes, []string{tableConfig.CompanyName}, tableConfig.PredictRatio, baseDate.AddDate(0, 0, -5), baseDate.AddDate(0, 0, 5), tableConfig.ContractType)
+		contractRowData, e := GetTableTradeData(exchange, classifyName, classifyTypes, tableConfig.CompanyName, tableConfig.PredictRatio, baseDate.AddDate(0, 0, -5), baseDate.AddDate(0, 0, 5), tableConfig.ContractType)
 		if e != nil {
 			err = fmt.Errorf("获取公司合约多空单数据失败, %v", e)
 			return
@@ -231,7 +255,7 @@ func GetTableRowsDataByConfig(tableConfig tradeAnalysisModel.TableExtraConfig) (
 	for _, v := range mussyRows {
 		contractRow[v.ClassifyType] = v
 	}
-	for _, v := range sortContract {
+	for _, v := range sortRules {
 		t := contractRow[v]
 		if t != nil {
 			tableRows = append(tableRows, t)
@@ -239,3 +263,25 @@ func GetTableRowsDataByConfig(tableConfig tradeAnalysisModel.TableExtraConfig) (
 	}
 	return
 }
+
+// MergeClassifyTypeTopRankData 类型为合约加总时-合并当日的TOP数据
+func MergeClassifyTypeTopRankData(classifyTypesData []*tradeAnalysisModel.ContractTopRankData) (mergedData []*tradeAnalysisModel.ContractTopRankData) {
+	mergedData = make([]*tradeAnalysisModel.ContractTopRankData, 0)
+
+	mergeData := new(tradeAnalysisModel.ContractTopRankData)
+	for _, v := range classifyTypesData {
+		mergeData.Exchange = v.Exchange
+		mergeData.DealValue += v.DealValue
+		mergeData.BuyValue += v.BuyValue
+		mergeData.BuyChange += v.BuyChange
+		mergeData.SoldValue += v.SoldValue
+		mergeData.SoldChange += v.SoldChange
+		mergeData.PureBuyValue += v.PureBuyValue
+		mergeData.PureBuyChange += v.PureBuyChange
+		mergeData.ClassifyName = v.ClassifyName
+		mergeData.ClassifyType = v.ClassifyName // 合约合并后为品种名
+		mergeData.DataTime = v.DataTime
+	}
+	mergedData = append(mergedData, mergeData)
+	return
+}