新增注释
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
// limitBreakNextPerformanceRow 是炸板股次日表现查询的数据库扫描结构。
|
||||
type limitBreakNextPerformanceRow struct {
|
||||
TradeDate string `gorm:"column:trade_date"`
|
||||
PreviousTradeDate string `gorm:"column:previous_trade_date"`
|
||||
@@ -31,6 +32,9 @@ type limitBreakNextPerformanceRow struct {
|
||||
NextClose *float64 `gorm:"column:next_close"`
|
||||
}
|
||||
|
||||
// GetLimitBreakNextPerformance 返回炸板股次日表现的分页响应。
|
||||
//
|
||||
// 该方法负责分页和汇总统计,实际查询与单股指标计算由 ListLimitBreakNextPerformanceStocks 完成。
|
||||
func (s *LimitBoardService) GetLimitBreakNextPerformance(req model.LimitBreakNextPerformanceRequest) (model.LimitBreakNextPerformanceResponse, error) {
|
||||
resp := model.LimitBreakNextPerformanceResponse{
|
||||
TradeStartDate: req.TradeStartDate,
|
||||
@@ -67,6 +71,9 @@ func (s *LimitBoardService) GetLimitBreakNextPerformance(req model.LimitBreakNex
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ListLimitBreakNextPerformanceStocks 查询并计算全部命中的炸板股次日表现。
|
||||
//
|
||||
// 返回值中的 int 表示拥有任意 T+1 行情字段的记录数量。
|
||||
func (s *LimitBoardService) ListLimitBreakNextPerformanceStocks(req model.LimitBreakNextPerformanceRequest) ([]model.LimitBreakNextPerformanceStock, int, error) {
|
||||
rows, err := s.loadLimitBreakNextPerformanceRows(req.TradeStartDate, req.TradeEndDate)
|
||||
if err != nil {
|
||||
@@ -77,6 +84,7 @@ func (s *LimitBoardService) ListLimitBreakNextPerformanceStocks(req model.LimitB
|
||||
nextTradeDataCount := 0
|
||||
for _, row := range rows {
|
||||
item := buildLimitBreakNextPerformanceStock(row)
|
||||
// 可选布尔过滤在指标计算后执行,便于复用同一批基础查询结果。
|
||||
if req.TOpenGainLt101 != nil && item.TOpenGainLt101 != *req.TOpenGainLt101 {
|
||||
continue
|
||||
}
|
||||
@@ -91,6 +99,9 @@ func (s *LimitBoardService) ListLimitBreakNextPerformanceStocks(req model.LimitB
|
||||
return items, nextTradeDataCount, nil
|
||||
}
|
||||
|
||||
// loadLimitBreakNextPerformanceRows 查询“首板后次日炸板”的候选股票和 T+1 行情。
|
||||
//
|
||||
// 口径:前一交易日涨停、前两交易日未涨停、当前交易日 final_status = 2。
|
||||
func (s *LimitBoardService) loadLimitBreakNextPerformanceRows(startDate, endDate string) ([]limitBreakNextPerformanceRow, error) {
|
||||
query := `
|
||||
WITH open_calendar AS (
|
||||
@@ -160,6 +171,7 @@ ORDER BY trade_date ASC, ts_code ASC`
|
||||
return rows, err
|
||||
}
|
||||
|
||||
// buildLimitBreakNextPerformanceStock 根据数据库行计算前端需要的派生指标。
|
||||
func buildLimitBreakNextPerformanceStock(row limitBreakNextPerformanceRow) model.LimitBreakNextPerformanceStock {
|
||||
item := model.LimitBreakNextPerformanceStock{
|
||||
TradeDate: row.TradeDate,
|
||||
@@ -183,17 +195,20 @@ func buildLimitBreakNextPerformanceStock(row limitBreakNextPerformanceRow) model
|
||||
}
|
||||
item.BreakTimeBefore1100 = isLimitBreakTimeBeforeEleven(item.FirstTime)
|
||||
if row.TPreClose != nil && *row.TPreClose > 0 {
|
||||
// T 日开盘涨幅以 T 日前收为基准,低于 101% 用于筛选低开/小幅高开炸板。
|
||||
item.TOpenPctChg = pctChangePtr(row.TOpen, *row.TPreClose)
|
||||
if row.TOpen != nil {
|
||||
item.TOpenGainLt101 = *row.TOpen < *row.TPreClose*1.01
|
||||
}
|
||||
}
|
||||
if row.TClose != nil && *row.TClose > 0 {
|
||||
// T+1 的开高低收收益均以 T 日收盘价作为基准。
|
||||
item.NextOpenPctChg = pctChangePtr(row.NextOpen, *row.TClose)
|
||||
item.NextHighProfitPct = pctChangePtr(row.NextHigh, *row.TClose)
|
||||
item.NextLowLossPct = pctChangePtr(row.NextLow, *row.TClose)
|
||||
item.NextClosePctChg = pctChangePtr(row.NextClose, *row.TClose)
|
||||
if row.NextHigh != nil && row.THigh != nil {
|
||||
// 是否盘中可盈利按 T+1 最高价能否覆盖 T 日最高买入成本判断。
|
||||
nextIntradayProfitable := *row.NextHigh >= *row.THigh
|
||||
item.NextIntradayProfitable = &nextIntradayProfitable
|
||||
}
|
||||
@@ -201,6 +216,7 @@ func buildLimitBreakNextPerformanceStock(row limitBreakNextPerformanceRow) model
|
||||
return item
|
||||
}
|
||||
|
||||
// pctChangePtr 计算相对基准价的百分比变化,输入缺失时返回 nil。
|
||||
func pctChangePtr(value *float64, base float64) *float64 {
|
||||
if value == nil || base == 0 {
|
||||
return nil
|
||||
@@ -209,10 +225,12 @@ func pctChangePtr(value *float64, base float64) *float64 {
|
||||
return &result
|
||||
}
|
||||
|
||||
// roundLimitBreakPct 保留两位小数。
|
||||
func roundLimitBreakPct(value float64) float64 {
|
||||
return math.Round(value*100) / 100
|
||||
}
|
||||
|
||||
// formatLimitTime 将 TuShare 紧凑时间格式转换为 HH:MM:SS。
|
||||
func formatLimitTime(value string) string {
|
||||
timeText := strings.TrimSpace(value)
|
||||
if timeText == "" || strings.EqualFold(timeText, "none") || strings.EqualFold(timeText, "null") {
|
||||
@@ -233,6 +251,9 @@ func formatLimitTime(value string) string {
|
||||
return timeText[0:2] + ":" + timeText[2:4] + ":" + timeText[4:6]
|
||||
}
|
||||
|
||||
// isLimitBreakTimeBeforeEleven 判断炸板时间是否早于 11:00。
|
||||
//
|
||||
// 空时间在当前业务中视为满足条件,保持历史接口行为。
|
||||
func isLimitBreakTimeBeforeEleven(limitTime string) bool {
|
||||
if strings.TrimSpace(limitTime) == "" {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user