新增二板历史角度
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
// GetSecondBoardProfitSummary 返回 2020 年至今二板早盘股票的 T+1 最大获利汇总。
|
||||
func (h *LimitBoardHandler) GetSecondBoardProfitSummary(c *gin.Context) {
|
||||
req, err := parseSecondBoardProfitRequest(c, false)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.Service.GetSecondBoardProfitSummary(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetSecondBoardProfitDetail 返回单只股票二板早盘后的 T+1 最大获利详情。
|
||||
func (h *LimitBoardHandler) GetSecondBoardProfitDetail(c *gin.Context) {
|
||||
req, err := parseSecondBoardProfitRequest(c, true)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.Service.GetSecondBoardProfitDetail(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// parseSecondBoardProfitRequest 解析二板早盘获利综合和详情接口参数。
|
||||
func parseSecondBoardProfitRequest(c *gin.Context, requireCode bool) (model.SecondBoardProfitRequest, error) {
|
||||
req := model.SecondBoardProfitRequest{
|
||||
TradeStartDate: firstNonEmptyQuery(c, "tradeStartDate", "trade_start_date", "start_date"),
|
||||
TradeEndDate: firstNonEmptyQuery(c, "tradeEndDate", "trade_end_date", "end_date"),
|
||||
TsCode: firstNonEmptyQuery(c, "tsCode", "ts_code"),
|
||||
Symbol: firstNonEmptyQuery(c, "symbol", "code"),
|
||||
}
|
||||
if req.TradeStartDate == "" {
|
||||
req.TradeStartDate = defaultSecondProfitStartDate
|
||||
}
|
||||
if req.TradeEndDate == "" {
|
||||
req.TradeEndDate = time.Now().Format("2006-01-02")
|
||||
}
|
||||
if err := validateSecondProfitDateRange(req.TradeStartDate, req.TradeEndDate); err != nil {
|
||||
return req, err
|
||||
}
|
||||
if requireCode && req.TsCode == "" && req.Symbol == "" {
|
||||
return req, errors.New("tsCode 或 symbol 参数不能为空")
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
@@ -21,6 +21,14 @@ type SecondCalRequest struct {
|
||||
TradeEndDate string `form:"tradeEndDate" json:"trade_end_date"` // 查询结束 T 日。
|
||||
}
|
||||
|
||||
// SecondBoardProfitRequest 是二板获利综合和详情接口的日期范围请求。
|
||||
type SecondBoardProfitRequest struct {
|
||||
TradeStartDate string `form:"tradeStartDate" json:"trade_start_date"` // 查询起始 T 日,默认 2020-01-01。
|
||||
TradeEndDate string `form:"tradeEndDate" json:"trade_end_date"` // 查询结束 T 日,默认当天。
|
||||
TsCode string `form:"tsCode" json:"ts_code"` // 可选 TuShare 股票代码,详情接口必填。
|
||||
Symbol string `form:"symbol" json:"symbol"` // 可选市场股票代码,详情接口可替代 tsCode。
|
||||
}
|
||||
|
||||
// LimitBoardStock 表示单只涨停股在涨停概览中的展示数据。
|
||||
type LimitBoardStock struct {
|
||||
Symbol string `json:"symbol"` // 市场展示用股票代码。
|
||||
@@ -165,6 +173,51 @@ type SecondCalStock struct {
|
||||
NextHighProfitPct *float64 `json:"next_high_profit_pct"` // T+1 最高价相对 T 日最高价的收益率。
|
||||
}
|
||||
|
||||
// SecondBoardProfitSummaryItem 表示单只股票二板后的次日最大获利汇总。
|
||||
type SecondBoardProfitSummaryItem struct {
|
||||
Symbol string `json:"symbol"` // 股票代码。
|
||||
TsCode string `json:"ts_code"` // TuShare 股票代码。
|
||||
Name string `json:"name"` // 股票简称。
|
||||
Market string `json:"market"` // 所属板块。
|
||||
Industry string `json:"industry"` // 所属行业。
|
||||
SecondBoardCount int `json:"second_board_count"` // 09:31:00-09:59:59 期间命中的二板次数。
|
||||
LimitCount int `json:"limit_count"` // 二板封板次数。
|
||||
BreakCount int `json:"break_count"` // 二板炸板次数。
|
||||
ProfitDataCount int `json:"profit_data_count"` // 具备 T+1 最大获利百分比的次数。
|
||||
TotalNextHighProfitPct float64 `json:"total_next_high_profit_pct"` // T+1 最大获利百分比累计值。
|
||||
AverageNextHighProfitPct float64 `json:"average_next_high_profit_pct"` // T+1 最大获利百分比平均值。
|
||||
FirstTradeDate string `json:"first_trade_date"` // 首次命中交易日。
|
||||
LastTradeDate string `json:"last_trade_date"` // 最近命中交易日。
|
||||
}
|
||||
|
||||
// SecondBoardProfitSummaryResponse 是二板获利综合接口响应。
|
||||
type SecondBoardProfitSummaryResponse struct {
|
||||
TradeStartDate string `json:"trade_start_date"` // 查询起始 T 日。
|
||||
TradeEndDate string `json:"trade_end_date"` // 查询结束 T 日。
|
||||
StartTime string `json:"start_time"` // 二板首次涨停时间筛选起点。
|
||||
EndTime string `json:"end_time"` // 二板首次涨停时间筛选终点。
|
||||
Total int `json:"total"` // 返回的股票数量。
|
||||
List []SecondBoardProfitSummaryItem `json:"list"` // 按累计获利从高到低排序。
|
||||
}
|
||||
|
||||
// SecondBoardProfitDetailResponse 是单只股票二板获利详情接口响应。
|
||||
type SecondBoardProfitDetailResponse struct {
|
||||
TradeStartDate string `json:"trade_start_date"` // 查询起始 T 日。
|
||||
TradeEndDate string `json:"trade_end_date"` // 查询结束 T 日。
|
||||
StartTime string `json:"start_time"` // 二板首次涨停时间筛选起点。
|
||||
EndTime string `json:"end_time"` // 二板首次涨停时间筛选终点。
|
||||
Symbol string `json:"symbol"` // 股票代码。
|
||||
TsCode string `json:"ts_code"` // TuShare 股票代码。
|
||||
Name string `json:"name"` // 股票简称。
|
||||
Market string `json:"market"` // 所属板块。
|
||||
Industry string `json:"industry"` // 所属行业。
|
||||
SecondBoardCount int `json:"second_board_count"` // 命中二板次数。
|
||||
ProfitDataCount int `json:"profit_data_count"` // 具备 T+1 最大获利百分比的次数。
|
||||
TotalNextHighProfitPct float64 `json:"total_next_high_profit_pct"` // T+1 最大获利百分比累计值。
|
||||
AverageNextHighProfitPct float64 `json:"average_next_high_profit_pct"` // T+1 最大获利百分比平均值。
|
||||
List []SecondCalStock `json:"list"` // 每次二板获利详情。
|
||||
}
|
||||
|
||||
// LimitOpenCompareGroup 是开盘价低于前收接口的分组结果。
|
||||
type LimitOpenCompareGroup struct {
|
||||
Key string `json:"key"` // 分组唯一标识。
|
||||
|
||||
@@ -39,6 +39,8 @@ func NewRouter(commonApi *api.CommonHandler, stockApi *api.StockHandler, limitBo
|
||||
limitBoard.GET("/open-threshold", limitBoardApi.GetOpenThresholdGroups)
|
||||
limitBoard.GET("/open-threshold-groups", limitBoardApi.GetOpenThresholdGroups)
|
||||
limitBoard.GET("/second-board-profit", limitBoardApi.ListSecondBoardProfit)
|
||||
limitBoard.GET("/second-board-profit-summary", limitBoardApi.GetSecondBoardProfitSummary)
|
||||
limitBoard.GET("/second-board-profit-detail", limitBoardApi.GetSecondBoardProfitDetail)
|
||||
limitBoard.GET("/second-cal", limitBoardApi.GetSecondCal)
|
||||
limitBoard.GET("/break-next-performance", limitBoardApi.GetBreakNextPerformance)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
package service // 声明 service 包,保持与涨停板服务层一致。
|
||||
|
||||
import ( // 引入本文件需要的标准库和项目包。
|
||||
"strings" // 用于清理请求中的股票代码空格。
|
||||
|
||||
"level_go/internal/model" // 引入接口请求和响应模型。
|
||||
)
|
||||
|
||||
const ( // 定义二板获利缓存接口使用的常量。
|
||||
secondBoardProfitStartTime = "09:31:00" // 固定的二板首次涨停时间筛选起点。
|
||||
secondBoardProfitEndTime = "09:59:59" // 固定的二板首次涨停时间筛选终点。
|
||||
)
|
||||
|
||||
type secondBoardProfitCacheRange struct { // 表示明细缓存表当前覆盖的交易日期范围。
|
||||
StartDate string `gorm:"column:start_date"` // 缓存明细最早交易日,格式 YYYY-MM-DD。
|
||||
EndDate string `gorm:"column:end_date"` // 缓存明细最晚交易日,格式 YYYY-MM-DD。
|
||||
}
|
||||
|
||||
type secondBoardProfitDetailCacheRow struct { // 表示从二板获利详情缓存表扫描出的单行数据。
|
||||
TradeDate string `gorm:"column:trade_date"` // T 日交易日。
|
||||
PreviousTradeDate string `gorm:"column:previous_trade_date"` // T-1 交易日。
|
||||
PreviousTwoTradeDate string `gorm:"column:previous_two_trade_date"` // T-2 交易日。
|
||||
NextTradeDate string `gorm:"column:next_trade_date"` // T+1 交易日。
|
||||
Symbol string `gorm:"column:symbol"` // 股票代码。
|
||||
TsCode string `gorm:"column:ts_code"` // TuShare 股票代码。
|
||||
Name string `gorm:"column:name"` // 股票简称。
|
||||
Market string `gorm:"column:market"` // 所属板块。
|
||||
Industry string `gorm:"column:industry"` // 所属行业。
|
||||
PreviousClose *float64 `gorm:"column:previous_close"` // T-1 收盘价。
|
||||
LimitPrice *float64 `gorm:"column:limit_price"` // T 日涨停价。
|
||||
THigh *float64 `gorm:"column:t_high"` // T 日最高价。
|
||||
TClose *float64 `gorm:"column:t_close"` // T 日收盘价。
|
||||
TPctChg *float64 `gorm:"column:t_pct_chg"` // T 日涨跌幅。
|
||||
Status string `gorm:"column:status"` // 二板状态,limit 表示封板,break 表示炸板。
|
||||
FirstLimitTime string `gorm:"column:first_limit_time"` // T 日二板首次涨停时间。
|
||||
OpenTimes *int `gorm:"column:open_times"` // T 日开板次数。
|
||||
NextHigh *float64 `gorm:"column:next_high"` // T+1 最高价。
|
||||
NextClose *float64 `gorm:"column:next_close"` // T+1 收盘价。
|
||||
NextIntradayProfitable *int `gorm:"column:next_intraday_profitable"` // T+1 盘中是否盈利,数据库中为 0/1/null。
|
||||
NextHighProfitPct *float64 `gorm:"column:next_high_profit_pct"` // T+1 最大获利百分比。
|
||||
}
|
||||
|
||||
// GetSecondBoardProfitSummary 返回二板获利综合数据。
|
||||
func (s *LimitBoardService) GetSecondBoardProfitSummary(req model.SecondBoardProfitRequest) (model.SecondBoardProfitSummaryResponse, error) { // 定义二板获利综合服务方法。
|
||||
resp := model.SecondBoardProfitSummaryResponse{ // 初始化响应结构。
|
||||
TradeStartDate: req.TradeStartDate, // 回显查询起始交易日。
|
||||
TradeEndDate: req.TradeEndDate, // 回显查询结束交易日。
|
||||
StartTime: secondBoardProfitStartTime, // 回显固定时间筛选起点。
|
||||
EndTime: secondBoardProfitEndTime, // 回显固定时间筛选终点。
|
||||
List: []model.SecondBoardProfitSummaryItem{}, // 初始化空列表,避免 JSON 返回 null。
|
||||
}
|
||||
|
||||
useSummaryCache, err := s.shouldUseSecondBoardProfitSummaryCache(req) // 判断当前日期范围是否可以直接使用汇总缓存表。
|
||||
if err != nil { // 判断缓存范围查询是否失败。
|
||||
return resp, err // 返回已初始化响应和数据库错误。
|
||||
}
|
||||
if useSummaryCache { // 请求范围覆盖整张明细缓存表时,直接读取预聚合汇总缓存表。
|
||||
return s.loadSecondBoardProfitSummaryCache(resp) // 从 summary cache 加载结果并返回。
|
||||
}
|
||||
return s.loadSecondBoardProfitSummaryFromDetail(req, resp) // 窄日期范围从 detail cache 动态聚合,保持日期筛选语义。
|
||||
}
|
||||
|
||||
// GetSecondBoardProfitDetail 返回单只股票二板获利详情数据。
|
||||
func (s *LimitBoardService) GetSecondBoardProfitDetail(req model.SecondBoardProfitRequest) (model.SecondBoardProfitDetailResponse, error) { // 定义个股二板详情服务方法。
|
||||
resp := model.SecondBoardProfitDetailResponse{ // 初始化响应结构。
|
||||
TradeStartDate: req.TradeStartDate, // 回显查询起始交易日。
|
||||
TradeEndDate: req.TradeEndDate, // 回显查询结束交易日。
|
||||
StartTime: secondBoardProfitStartTime, // 回显固定时间筛选起点。
|
||||
EndTime: secondBoardProfitEndTime, // 回显固定时间筛选终点。
|
||||
Symbol: strings.TrimSpace(req.Symbol), // 回显请求中的股票代码。
|
||||
TsCode: strings.TrimSpace(req.TsCode), // 回显请求中的 TuShare 股票代码。
|
||||
List: []model.SecondCalStock{}, // 初始化空列表,避免 JSON 返回 null。
|
||||
}
|
||||
|
||||
rows, err := s.loadSecondBoardProfitDetailRows(req) // 从详情缓存表读取个股明细行。
|
||||
if err != nil { // 判断数据库查询是否失败。
|
||||
return resp, err // 返回已初始化响应和数据库错误。
|
||||
}
|
||||
|
||||
for _, row := range rows { // 遍历缓存明细行并构建响应。
|
||||
stock := buildSecondBoardProfitStockFromCache(row) // 将缓存行转换为原接口复用的股票明细结构。
|
||||
if resp.SecondBoardCount == 0 { // 第一条命中记录用于补齐个股基础信息。
|
||||
resp.Symbol = stock.Symbol // 设置响应股票代码。
|
||||
resp.TsCode = stock.TsCode // 设置响应 TuShare 股票代码。
|
||||
resp.Name = stock.Name // 设置响应股票简称。
|
||||
resp.Market = stock.Market // 设置响应所属板块。
|
||||
resp.Industry = stock.Industry // 设置响应所属行业。
|
||||
}
|
||||
resp.SecondBoardCount++ // 累加命中的二板次数。
|
||||
if stock.NextHighProfitPct != nil { // 只统计具备 T+1 最大获利百分比的数据。
|
||||
resp.ProfitDataCount++ // 累加具备收益数据的次数。
|
||||
resp.TotalNextHighProfitPct = roundSecondCalPct(resp.TotalNextHighProfitPct + *stock.NextHighProfitPct) // 累加最大获利百分比。
|
||||
}
|
||||
resp.List = append(resp.List, stock) // 追加单次二板详情。
|
||||
}
|
||||
if resp.ProfitDataCount > 0 { // 判断是否存在可计算平均值的数据。
|
||||
resp.AverageNextHighProfitPct = roundSecondCalPct(resp.TotalNextHighProfitPct / float64(resp.ProfitDataCount)) // 计算平均最大获利百分比。
|
||||
}
|
||||
|
||||
return resp, nil // 返回详情响应。
|
||||
}
|
||||
|
||||
// shouldUseSecondBoardProfitSummaryCache 判断是否可以使用预聚合汇总缓存表。
|
||||
func (s *LimitBoardService) shouldUseSecondBoardProfitSummaryCache(req model.SecondBoardProfitRequest) (bool, error) { // 定义汇总缓存使用判断函数。
|
||||
cacheRange, err := s.loadSecondBoardProfitCacheRange() // 查询明细缓存表当前覆盖日期范围。
|
||||
if err != nil { // 判断缓存范围查询是否失败。
|
||||
return false, err // 查询失败时返回错误。
|
||||
}
|
||||
if cacheRange.StartDate == "" || cacheRange.EndDate == "" { // 判断缓存表是否为空。
|
||||
return false, nil // 缓存为空时不能使用汇总缓存。
|
||||
}
|
||||
return req.TradeStartDate <= cacheRange.StartDate && req.TradeEndDate >= cacheRange.EndDate, nil // 请求覆盖缓存全范围时使用汇总缓存。
|
||||
}
|
||||
|
||||
// loadSecondBoardProfitCacheRange 查询二板获利详情缓存表的日期覆盖范围。
|
||||
func (s *LimitBoardService) loadSecondBoardProfitCacheRange() (secondBoardProfitCacheRange, error) { // 定义缓存日期范围查询函数。
|
||||
// 定义查询缓存范围 SQL。
|
||||
query := `
|
||||
SELECT
|
||||
COALESCE(DATE_FORMAT(MIN(trade_date), '%Y-%m-%d'), '') AS start_date,
|
||||
COALESCE(DATE_FORMAT(MAX(trade_date), '%Y-%m-%d'), '') AS end_date
|
||||
FROM second_board_profit_detail_cache` // 从明细缓存表读取最小和最大交易日。
|
||||
var cacheRange secondBoardProfitCacheRange // 声明缓存范围结果变量。
|
||||
err := s.DB.Raw(query).Scan(&cacheRange).Error // 执行 SQL 并扫描结果。
|
||||
return cacheRange, err // 返回缓存范围和可能的错误。
|
||||
}
|
||||
|
||||
// loadSecondBoardProfitSummaryCache 从预聚合汇总缓存表读取完整汇总数据。
|
||||
func (s *LimitBoardService) loadSecondBoardProfitSummaryCache(resp model.SecondBoardProfitSummaryResponse) (model.SecondBoardProfitSummaryResponse, error) { // 定义汇总缓存读取函数。
|
||||
// 定义汇总缓存查询 SQL。
|
||||
query := `
|
||||
SELECT
|
||||
symbol,
|
||||
ts_code,
|
||||
name,
|
||||
market,
|
||||
industry,
|
||||
second_board_count,
|
||||
limit_count,
|
||||
break_count,
|
||||
profit_data_count,
|
||||
total_next_high_profit_pct,
|
||||
average_next_high_profit_pct,
|
||||
COALESCE(DATE_FORMAT(first_trade_date, '%Y-%m-%d'), '') AS first_trade_date,
|
||||
COALESCE(DATE_FORMAT(last_trade_date, '%Y-%m-%d'), '') AS last_trade_date
|
||||
FROM second_board_profit_summary_cache
|
||||
ORDER BY total_next_high_profit_pct DESC, profit_data_count DESC, ts_code ASC` // 按累计获利、数据次数和股票代码稳定排序。
|
||||
if err := s.DB.Raw(query).Scan(&resp.List).Error; err != nil { // 执行汇总缓存查询。
|
||||
return resp, err // 查询失败时返回错误。
|
||||
}
|
||||
resp.Total = len(resp.List) // 设置返回股票数量。
|
||||
return resp, nil // 返回汇总响应。
|
||||
}
|
||||
|
||||
// loadSecondBoardProfitSummaryFromDetail 从明细缓存表按日期范围动态聚合汇总数据。
|
||||
func (s *LimitBoardService) loadSecondBoardProfitSummaryFromDetail(req model.SecondBoardProfitRequest, resp model.SecondBoardProfitSummaryResponse) (model.SecondBoardProfitSummaryResponse, error) { // 定义明细聚合函数。
|
||||
// 定义日期范围聚合 SQL。
|
||||
query := `
|
||||
SELECT
|
||||
MAX(symbol) AS symbol,
|
||||
ts_code,
|
||||
MAX(name) AS name,
|
||||
MAX(market) AS market,
|
||||
MAX(industry) AS industry,
|
||||
COUNT(*) AS second_board_count,
|
||||
SUM(CASE WHEN status = 'limit' THEN 1 ELSE 0 END) AS limit_count,
|
||||
SUM(CASE WHEN status = 'break' THEN 1 ELSE 0 END) AS break_count,
|
||||
COUNT(next_high_profit_pct) AS profit_data_count,
|
||||
ROUND(SUM(COALESCE(next_high_profit_pct, 0)), 2) AS total_next_high_profit_pct,
|
||||
COALESCE(ROUND(SUM(COALESCE(next_high_profit_pct, 0)) / NULLIF(COUNT(next_high_profit_pct), 0), 2), 0) AS average_next_high_profit_pct,
|
||||
COALESCE(DATE_FORMAT(MIN(trade_date), '%Y-%m-%d'), '') AS first_trade_date,
|
||||
COALESCE(DATE_FORMAT(MAX(trade_date), '%Y-%m-%d'), '') AS last_trade_date
|
||||
FROM second_board_profit_detail_cache
|
||||
WHERE trade_date BETWEEN ? AND ?
|
||||
GROUP BY ts_code
|
||||
ORDER BY total_next_high_profit_pct DESC, profit_data_count DESC, ts_code ASC` // 在明细缓存表内按请求日期范围聚合。
|
||||
if err := s.DB.Raw(query, req.TradeStartDate, req.TradeEndDate).Scan(&resp.List).Error; err != nil { // 执行聚合查询。
|
||||
return resp, err // 查询失败时返回错误。
|
||||
}
|
||||
resp.Total = len(resp.List) // 设置返回股票数量。
|
||||
return resp, nil // 返回汇总响应。
|
||||
}
|
||||
|
||||
// loadSecondBoardProfitDetailRows 从明细缓存表读取单只股票详情。
|
||||
func (s *LimitBoardService) loadSecondBoardProfitDetailRows(req model.SecondBoardProfitRequest) ([]secondBoardProfitDetailCacheRow, error) { // 定义明细缓存查询函数。
|
||||
tsCode := strings.TrimSpace(req.TsCode) // 清理 TuShare 股票代码参数。
|
||||
symbol := strings.TrimSpace(req.Symbol) // 清理市场股票代码参数。
|
||||
// 定义明细缓存查询 SQL。
|
||||
query := `
|
||||
SELECT
|
||||
DATE_FORMAT(trade_date, '%Y-%m-%d') AS trade_date,
|
||||
DATE_FORMAT(previous_trade_date, '%Y-%m-%d') AS previous_trade_date,
|
||||
DATE_FORMAT(previous_two_trade_date, '%Y-%m-%d') AS previous_two_trade_date,
|
||||
COALESCE(DATE_FORMAT(next_trade_date, '%Y-%m-%d'), '') AS next_trade_date,
|
||||
symbol,
|
||||
ts_code,
|
||||
name,
|
||||
market,
|
||||
industry,
|
||||
previous_close,
|
||||
limit_price,
|
||||
t_high,
|
||||
t_close,
|
||||
t_pct_chg,
|
||||
status,
|
||||
first_limit_time,
|
||||
open_times,
|
||||
next_high,
|
||||
next_close,
|
||||
next_intraday_profitable,
|
||||
next_high_profit_pct
|
||||
FROM second_board_profit_detail_cache
|
||||
WHERE trade_date BETWEEN ? AND ?` // 先按日期范围过滤明细缓存。
|
||||
args := []interface{}{req.TradeStartDate, req.TradeEndDate} // 初始化 SQL 参数为日期范围。
|
||||
if tsCode != "" { // 优先按 TuShare 股票代码查询。
|
||||
query += " AND ts_code = ?" // 追加 TuShare 股票代码条件。
|
||||
args = append(args, tsCode) // 追加 TuShare 股票代码参数。
|
||||
} else { // 未传 TuShare 股票代码时使用市场股票代码。
|
||||
query += " AND symbol = ?" // 追加市场股票代码条件。
|
||||
args = append(args, symbol) // 追加市场股票代码参数。
|
||||
}
|
||||
query += " ORDER BY trade_date ASC, ts_code ASC" // 追加稳定排序条件。
|
||||
var rows []secondBoardProfitDetailCacheRow // 声明明细缓存扫描结果。
|
||||
err := s.DB.Raw(query, args...).Scan(&rows).Error // 执行 SQL 并扫描结果。
|
||||
return rows, err // 返回明细行和可能的错误。
|
||||
}
|
||||
|
||||
// buildSecondBoardProfitStockFromCache 将缓存行转换为接口复用的二板股票明细模型。
|
||||
func buildSecondBoardProfitStockFromCache(row secondBoardProfitDetailCacheRow) model.SecondCalStock { // 定义缓存行到响应模型的转换函数。
|
||||
item := model.SecondCalStock{ // 初始化二板股票明细。
|
||||
TradeDate: row.TradeDate, // 设置 T 日交易日。
|
||||
PreviousTradeDate: row.PreviousTradeDate, // 设置 T-1 交易日。
|
||||
PreviousTwoTradeDate: row.PreviousTwoTradeDate, // 设置 T-2 交易日。
|
||||
NextTradeDate: row.NextTradeDate, // 设置 T+1 交易日。
|
||||
Symbol: row.Symbol, // 设置股票代码。
|
||||
TsCode: row.TsCode, // 设置 TuShare 股票代码。
|
||||
Name: row.Name, // 设置股票简称。
|
||||
Market: row.Market, // 设置所属板块。
|
||||
Industry: row.Industry, // 设置所属行业。
|
||||
PreviousClose: row.PreviousClose, // 设置 T-1 收盘价。
|
||||
LimitPrice: row.LimitPrice, // 设置 T 日涨停价。
|
||||
THigh: row.THigh, // 设置 T 日最高价。
|
||||
TClose: row.TClose, // 设置 T 日收盘价。
|
||||
TPctChg: row.TPctChg, // 设置 T 日涨跌幅。
|
||||
Status: row.Status, // 设置二板状态。
|
||||
FirstLimitTime: row.FirstLimitTime, // 设置首次涨停时间。
|
||||
OpenTimes: row.OpenTimes, // 设置开板次数。
|
||||
NextHigh: row.NextHigh, // 设置 T+1 最高价。
|
||||
NextClose: row.NextClose, // 设置 T+1 收盘价。
|
||||
NextHighProfitPct: row.NextHighProfitPct, // 设置 T+1 最大获利百分比。
|
||||
}
|
||||
if row.NextIntradayProfitable != nil { // 判断缓存中是否存在盈利布尔数据。
|
||||
nextIntradayProfitable := *row.NextIntradayProfitable != 0 // 将 0/1 转换为 bool。
|
||||
item.NextIntradayProfitable = &nextIntradayProfitable // 设置 T+1 盘中是否盈利。
|
||||
}
|
||||
return item // 返回转换后的股票明细。
|
||||
}
|
||||
Reference in New Issue
Block a user