1、新增二板炸板情况统计;
2、更新项目结构;
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
func (h *LimitBoardHandler) DownloadBreakNextPerformanceCSV(c *gin.Context) {
|
||||
req, err := parseLimitBreakNextPerformanceRequest(c, false)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
rows, _, err := h.Service.ListLimitBreakNextPerformanceStocks(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("limit_break_next_performance_%s_%s_daily_detail.csv",
|
||||
strings.ReplaceAll(req.TradeStartDate, "-", ""),
|
||||
strings.ReplaceAll(req.TradeEndDate, "-", ""),
|
||||
)
|
||||
c.Header("Content-Type", "text/csv; charset=utf-8")
|
||||
c.Header("Content-Disposition", `attachment; filename="`+filename+`"`)
|
||||
c.Status(http.StatusOK)
|
||||
|
||||
if _, err := c.Writer.Write([]byte{0xEF, 0xBB, 0xBF}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
writer := csv.NewWriter(c.Writer)
|
||||
defer writer.Flush()
|
||||
|
||||
_ = writer.Write(breakNextPerformanceCSVHeaders())
|
||||
for _, row := range rows {
|
||||
if err := writer.Write(breakNextPerformanceCSVRecord(row)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func breakNextPerformanceCSVHeaders() []string {
|
||||
return []string{
|
||||
"T日",
|
||||
"股票代码",
|
||||
"股票简称",
|
||||
"T涨跌幅",
|
||||
"T开盘<1%",
|
||||
"tushare炸板时间",
|
||||
"11点前炸板",
|
||||
"T+1盘中可盈利",
|
||||
"T+1最高获利",
|
||||
"T+1开盘涨幅",
|
||||
"T+1收盘涨跌",
|
||||
"市场",
|
||||
"行业",
|
||||
"T开盘",
|
||||
"T开盘涨幅",
|
||||
"T最高",
|
||||
"T收盘",
|
||||
"tushare是否炸板",
|
||||
"T+1开盘",
|
||||
"T+1最高",
|
||||
"T+1最低",
|
||||
"T+1收盘",
|
||||
"T+1最大亏损",
|
||||
}
|
||||
}
|
||||
|
||||
func breakNextPerformanceCSVRecord(row model.LimitBreakNextPerformanceStock) []string {
|
||||
return []string{
|
||||
row.TradeDate,
|
||||
row.TsCode,
|
||||
row.Name,
|
||||
breakNextPerformanceFormatFloatPtr(row.TPctChg),
|
||||
breakNextPerformanceBoolLabel(row.TOpenGainLt101),
|
||||
row.FirstTime,
|
||||
breakNextPerformanceBoolLabel(row.BreakTimeBefore1100),
|
||||
breakNextPerformanceBoolPtrLabel(row.NextIntradayProfitable),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextHighProfitPct),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextOpenPctChg),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextClosePctChg),
|
||||
row.Market,
|
||||
row.Industry,
|
||||
breakNextPerformanceFormatFloatPtr(row.TOpen),
|
||||
breakNextPerformanceFormatFloatPtr(row.TOpenPctChg),
|
||||
breakNextPerformanceFormatFloatPtr(row.THigh),
|
||||
breakNextPerformanceFormatFloatPtr(row.TClose),
|
||||
breakNextPerformanceBoolLabel(row.HasTushareBreakData),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextOpen),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextHigh),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextLow),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextClose),
|
||||
breakNextPerformanceFormatFloatPtr(row.NextLowLossPct),
|
||||
}
|
||||
}
|
||||
|
||||
func breakNextPerformanceFormatFloatPtr(value *float64) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
return strconv.FormatFloat(*value, 'f', -1, 64)
|
||||
}
|
||||
|
||||
func breakNextPerformanceBoolLabel(value bool) string {
|
||||
if value {
|
||||
return "是"
|
||||
}
|
||||
return "否"
|
||||
}
|
||||
|
||||
func breakNextPerformanceBoolPtrLabel(value *bool) string {
|
||||
if value == nil {
|
||||
return "无次日数据"
|
||||
}
|
||||
return breakNextPerformanceBoolLabel(*value)
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/model"
|
||||
@@ -48,6 +51,90 @@ func (h *LimitBoardHandler) GetOpenLowerGroups(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LimitBoardHandler) GetBreakNextPerformance(c *gin.Context) {
|
||||
req, err := parseLimitBreakNextPerformanceRequest(c, true)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.Service.GetLimitBreakNextPerformance(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func parseLimitBreakNextPerformanceRequest(c *gin.Context, includePagination bool) (model.LimitBreakNextPerformanceRequest, error) {
|
||||
req := model.LimitBreakNextPerformanceRequest{
|
||||
TradeStartDate: strings.TrimSpace(c.DefaultQuery("tradeStartDate", c.Query("start_date"))),
|
||||
TradeEndDate: strings.TrimSpace(c.DefaultQuery("tradeEndDate", c.Query("end_date"))),
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
}
|
||||
|
||||
if req.TradeStartDate == "" {
|
||||
return req, errors.New("tradeStartDate 参数不能为空")
|
||||
}
|
||||
if req.TradeEndDate == "" {
|
||||
return req, errors.New("tradeEndDate 参数不能为空")
|
||||
}
|
||||
if err := validateSecondProfitDateRange(req.TradeStartDate, req.TradeEndDate); err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
if includePagination {
|
||||
pageText := strings.TrimSpace(c.DefaultQuery("page", "1"))
|
||||
page, err := strconv.Atoi(pageText)
|
||||
if err != nil || page <= 0 {
|
||||
return req, errors.New("page 必须为正整数")
|
||||
}
|
||||
req.Page = page
|
||||
|
||||
pageSizeText := strings.TrimSpace(c.DefaultQuery("pageSize", "20"))
|
||||
pageSize, err := strconv.Atoi(pageSizeText)
|
||||
if err != nil || pageSize <= 0 {
|
||||
return req, errors.New("pageSize 必须为正整数")
|
||||
}
|
||||
req.PageSize = pageSize
|
||||
}
|
||||
|
||||
tOpenGainLt101, err := parseOptionalBoolQuery(c, "tOpenGainLt101", "tOpenLessThan101", "t_open_gain_lt_101")
|
||||
if err != nil {
|
||||
return req, errors.New("tOpenGainLt101 必须为布尔值")
|
||||
}
|
||||
req.TOpenGainLt101 = tOpenGainLt101
|
||||
|
||||
breakTimeBefore1100, err := parseOptionalBoolQuery(c, "breakTimeBefore1100", "tBreakTimeBefore1100", "break_time_before_1100")
|
||||
if err != nil {
|
||||
return req, errors.New("breakTimeBefore1100 必须为布尔值")
|
||||
}
|
||||
req.BreakTimeBefore1100 = breakTimeBefore1100
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func parseOptionalBoolQuery(c *gin.Context, keys ...string) (*bool, error) {
|
||||
for _, key := range keys {
|
||||
valueText, exists := c.GetQuery(key)
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
valueText = strings.TrimSpace(valueText)
|
||||
if valueText == "" {
|
||||
continue
|
||||
}
|
||||
value, err := strconv.ParseBool(valueText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (h *LimitBoardHandler) GetOpenThresholdGroups(c *gin.Context) {
|
||||
var req model.LimitBoardRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil || req.TradeDate == "" {
|
||||
|
||||
Reference in New Issue
Block a user