11点涨停逻辑变更为10点
This commit is contained in:
@@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"level_go/internal/model"
|
"level_go/internal/model"
|
||||||
@@ -193,7 +194,7 @@ func buildLimitBreakNextPerformanceStock(row limitBreakNextPerformanceRow) model
|
|||||||
if row.FirstTime != nil {
|
if row.FirstTime != nil {
|
||||||
item.FirstTime = formatLimitTime(*row.FirstTime)
|
item.FirstTime = formatLimitTime(*row.FirstTime)
|
||||||
}
|
}
|
||||||
item.BreakTimeBefore1100 = isLimitBreakTimeBeforeEleven(item.FirstTime)
|
item.BreakTimeBefore1100 = isLimitBreakTimeBeforeTen(item.FirstTime)
|
||||||
if row.TPreClose != nil && *row.TPreClose > 0 {
|
if row.TPreClose != nil && *row.TPreClose > 0 {
|
||||||
// T 日开盘涨幅以 T 日前收为基准,低于 101% 用于筛选低开/小幅高开炸板。
|
// T 日开盘涨幅以 T 日前收为基准,低于 101% 用于筛选低开/小幅高开炸板。
|
||||||
item.TOpenPctChg = pctChangePtr(row.TOpen, *row.TPreClose)
|
item.TOpenPctChg = pctChangePtr(row.TOpen, *row.TPreClose)
|
||||||
@@ -251,12 +252,38 @@ func formatLimitTime(value string) string {
|
|||||||
return timeText[0:2] + ":" + timeText[2:4] + ":" + timeText[4:6]
|
return timeText[0:2] + ":" + timeText[2:4] + ":" + timeText[4:6]
|
||||||
}
|
}
|
||||||
|
|
||||||
// isLimitBreakTimeBeforeEleven 判断炸板时间是否早于 11:00。
|
// isLimitBreakTimeBeforeTen 判断炸板时间是否早于 10:00。
|
||||||
//
|
//
|
||||||
// 空时间在当前业务中视为满足条件,保持历史接口行为。
|
// 空时间在当前业务中视为满足条件,保持历史接口行为。
|
||||||
func isLimitBreakTimeBeforeEleven(limitTime string) bool {
|
func isLimitBreakTimeBeforeTen(limitTime string) bool {
|
||||||
if strings.TrimSpace(limitTime) == "" {
|
if strings.TrimSpace(limitTime) == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return isBeforeEleven(&limitTime)
|
|
||||||
|
timeText := formatLimitTime(limitTime)
|
||||||
|
parts := strings.Split(timeText, ":")
|
||||||
|
if len(parts) < 2 || len(parts) > 3 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hour, err := strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
minute, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
second := 0
|
||||||
|
if len(parts) == 3 {
|
||||||
|
second, err = strconv.Atoi(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return hour < 10
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
// TestBuildLimitBreakNextPerformanceStockIntradayProfitableUsesTHigh 验证 T+1 盘中可盈利口径。
|
|
||||||
//
|
|
||||||
// 业务要求用 T+1 最高价覆盖 T 日最高价来判断是否可解套,而不是只和 T 日收盘价比较。
|
|
||||||
func TestBuildLimitBreakNextPerformanceStockIntradayProfitableUsesTHigh(t *testing.T) {
|
|
||||||
tClose := 9.50
|
|
||||||
tHigh := 10.00
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
nextHigh *float64
|
|
||||||
want *bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "next high below T high cannot cover T high buy loss",
|
|
||||||
nextHigh: float64Ptr(9.99),
|
|
||||||
want: boolPtr(false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "next high equal T high covers T high buy loss",
|
|
||||||
nextHigh: float64Ptr(10.00),
|
|
||||||
want: boolPtr(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing next high returns nil",
|
|
||||||
nextHigh: nil,
|
|
||||||
want: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
item := buildLimitBreakNextPerformanceStock(limitBreakNextPerformanceRow{
|
|
||||||
TClose: &tClose,
|
|
||||||
THigh: &tHigh,
|
|
||||||
NextHigh: tt.nextHigh,
|
|
||||||
})
|
|
||||||
|
|
||||||
if tt.want == nil {
|
|
||||||
if item.NextIntradayProfitable != nil {
|
|
||||||
t.Fatalf("NextIntradayProfitable = %v, want nil", *item.NextIntradayProfitable)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if item.NextIntradayProfitable == nil {
|
|
||||||
t.Fatalf("NextIntradayProfitable = nil, want %v", *tt.want)
|
|
||||||
}
|
|
||||||
if *item.NextIntradayProfitable != *tt.want {
|
|
||||||
t.Fatalf("NextIntradayProfitable = %v, want %v", *item.NextIntradayProfitable, *tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// float64Ptr 是测试用指针辅助函数。
|
|
||||||
func float64Ptr(value float64) *float64 {
|
|
||||||
return &value
|
|
||||||
}
|
|
||||||
|
|
||||||
// boolPtr 是测试用指针辅助函数。
|
|
||||||
func boolPtr(value bool) *bool {
|
|
||||||
return &value
|
|
||||||
}
|
|
||||||
@@ -167,9 +167,9 @@ candidate AS (
|
|||||||
LEFT JOIN limit_list_d ppl
|
LEFT JOIN limit_list_d ppl
|
||||||
ON ppl.ts_code = pl.ts_code AND ppl.trade_date = c.previous_two_trade_date AND ppl.` + "`limit`" + ` = 'U'
|
ON ppl.ts_code = pl.ts_code AND ppl.trade_date = c.previous_two_trade_date AND ppl.` + "`limit`" + ` = 'U'
|
||||||
INNER JOIN stock_daily d
|
INNER JOIN stock_daily d
|
||||||
ON d.ts_code = pl.ts_code AND d.trade_date = c.trade_date
|
ON d.ts_code = pl.ts_code COLLATE utf8mb4_0900_ai_ci AND d.trade_date = c.trade_date
|
||||||
LEFT JOIN stock_daily nd
|
LEFT JOIN stock_daily nd
|
||||||
ON nd.ts_code = pl.ts_code AND nd.trade_date = c.next_trade_date
|
ON nd.ts_code = pl.ts_code COLLATE utf8mb4_0900_ai_ci AND nd.trade_date = c.next_trade_date
|
||||||
LEFT JOIN stock_basic sb
|
LEFT JOIN stock_basic sb
|
||||||
ON sb.ts_code = pl.ts_code COLLATE utf8mb4_unicode_ci
|
ON sb.ts_code = pl.ts_code COLLATE utf8mb4_unicode_ci
|
||||||
WHERE c.trade_date BETWEEN ? AND ?
|
WHERE c.trade_date BETWEEN ? AND ?
|
||||||
|
|||||||
Reference in New Issue
Block a user