彻底解决 sql 兼容性问题 #6
@@ -19,7 +19,7 @@
|
||||
| `tOpenGainLt101` | `bool` | 否 | 无 | 筛选 T 日开盘价是否小于 T-1 收盘价的 101%,支持别名 `tOpenLessThan101`、`t_open_gain_lt_101` |
|
||||
| `breakTimeBefore1100` | `bool` | 否 | 无 | 筛选 T 日炸板时间是否在 `11:00:00` 前,空炸板时间按 `true` 处理,支持别名 `tBreakTimeBefore1100`、`break_time_before_1100` |
|
||||
|
||||
兼容 `start_date`、`end_date` 作为日期参数别名。
|
||||
兼容 `start_date`、`end_date`、`trade_start_date`、`trade_end_date` 作为日期参数别名。
|
||||
|
||||
示例:
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
ALTER DATABASE `level` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE `limit_list_d` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`trade_date` date NOT NULL COMMENT '交易日期',
|
||||
`ts_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '股票代码',
|
||||
`industry` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属行业',
|
||||
`name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '股票名称',
|
||||
`close` decimal(16,4) DEFAULT NULL COMMENT '收盘价',
|
||||
`pct_chg` decimal(10,4) DEFAULT NULL COMMENT '涨跌幅',
|
||||
`amount` decimal(24,4) DEFAULT NULL COMMENT '成交额',
|
||||
`limit_amount` decimal(24,4) DEFAULT NULL COMMENT '板上成交金额',
|
||||
`float_mv` decimal(24,4) DEFAULT NULL COMMENT '流通市值',
|
||||
`total_mv` decimal(24,4) DEFAULT NULL COMMENT '总市值',
|
||||
`turnover_ratio` decimal(10,4) DEFAULT NULL COMMENT '换手率',
|
||||
`fd_amount` decimal(24,4) DEFAULT NULL COMMENT '封单金额',
|
||||
`first_time` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '首次封板时间',
|
||||
`last_time` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最后封板时间',
|
||||
`open_times` int DEFAULT NULL COMMENT '打开次数',
|
||||
`up_stat` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '涨停统计',
|
||||
`limit_times` int DEFAULT NULL COMMENT '连板数',
|
||||
`limit` varchar(1) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'D跌停 U涨停 Z炸板',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_trade_code_limit` (`trade_date`,`ts_code`,`limit`),
|
||||
KEY `idx_trade_date` (`trade_date`),
|
||||
KEY `idx_ts_code` (`ts_code`),
|
||||
KEY `idx_limit` (`limit`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `stock_basic` (
|
||||
`ts_code` text COLLATE utf8mb4_unicode_ci,
|
||||
`symbol` text COLLATE utf8mb4_unicode_ci,
|
||||
`name` text COLLATE utf8mb4_unicode_ci,
|
||||
`area` text COLLATE utf8mb4_unicode_ci,
|
||||
`industry` text COLLATE utf8mb4_unicode_ci,
|
||||
`fullname` text COLLATE utf8mb4_unicode_ci,
|
||||
`enname` text COLLATE utf8mb4_unicode_ci,
|
||||
`cnspell` text COLLATE utf8mb4_unicode_ci,
|
||||
`market` text COLLATE utf8mb4_unicode_ci,
|
||||
`exchange` text COLLATE utf8mb4_unicode_ci,
|
||||
`curr_type` text COLLATE utf8mb4_unicode_ci,
|
||||
`list_status` text COLLATE utf8mb4_unicode_ci,
|
||||
`list_date` text COLLATE utf8mb4_unicode_ci,
|
||||
`delist_date` text COLLATE utf8mb4_unicode_ci,
|
||||
`is_hs` text COLLATE utf8mb4_unicode_ci,
|
||||
`act_name` text COLLATE utf8mb4_unicode_ci,
|
||||
`act_ent_type` text COLLATE utf8mb4_unicode_ci
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `stock_daily` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`ts_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`symbol` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`trade_date` date NOT NULL,
|
||||
`open` decimal(16,4) DEFAULT NULL,
|
||||
`high` decimal(16,4) DEFAULT NULL,
|
||||
`low` decimal(16,4) DEFAULT NULL,
|
||||
`close` decimal(16,4) DEFAULT NULL,
|
||||
`pre_close` decimal(16,4) DEFAULT NULL,
|
||||
`change_val` decimal(16,4) DEFAULT NULL,
|
||||
`pct_chg` decimal(10,4) DEFAULT NULL,
|
||||
`vol` bigint DEFAULT NULL,
|
||||
`amount` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_code_date` (`ts_code`,`trade_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `stock_limit_combined_data` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`trade_date` date NOT NULL COMMENT '交易日期',
|
||||
`symbol` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '股票代码,以stock_daily为准',
|
||||
`ts_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'TS股票代码,以stock_daily为准',
|
||||
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '股票名称,以stock_daily关联基础表为准',
|
||||
`final_status` tinyint NOT NULL COMMENT '融合状态:1-涨停,2-炸板',
|
||||
`source_count` int NOT NULL DEFAULT '0' COMMENT '命中的数据源数量',
|
||||
`source_flags` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '命中的数据源:calc,tushare',
|
||||
`calc_status` tinyint DEFAULT NULL COMMENT '本地行情计算状态:1-涨停,2-炸板',
|
||||
`calc_limit_price` decimal(16,4) DEFAULT NULL COMMENT '本地行情计算涨停价',
|
||||
`calc_high` decimal(16,4) DEFAULT NULL COMMENT 'stock_daily最高价',
|
||||
`calc_close` decimal(16,4) DEFAULT NULL COMMENT 'stock_daily收盘价',
|
||||
`calc_pre_close` decimal(16,4) DEFAULT NULL COMMENT 'stock_daily前收盘价',
|
||||
`calc_vol` bigint DEFAULT NULL COMMENT 'stock_daily成交量',
|
||||
`market` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属板块',
|
||||
`tushare_limit_type` varchar(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tushare-D跌停U涨停Z炸板',
|
||||
`tushare_industry` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tushare-所属行业',
|
||||
`tushare_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tushare-名称',
|
||||
`tushare_close` decimal(16,4) DEFAULT NULL COMMENT 'Tushare-收盘价',
|
||||
`tushare_pct_chg` decimal(10,4) DEFAULT NULL COMMENT 'Tushare-涨跌幅',
|
||||
`tushare_amount` decimal(24,4) DEFAULT NULL COMMENT 'Tushare-成交额',
|
||||
`tushare_limit_amount` decimal(24,4) DEFAULT NULL COMMENT 'Tushare-板上成交金额',
|
||||
`tushare_float_mv` decimal(24,4) DEFAULT NULL COMMENT 'Tushare-流通市值',
|
||||
`tushare_total_mv` decimal(24,4) DEFAULT NULL COMMENT 'Tushare-总市值',
|
||||
`tushare_turnover_ratio` decimal(10,4) DEFAULT NULL COMMENT 'Tushare-换手率',
|
||||
`tushare_fd_amount` decimal(24,4) DEFAULT NULL COMMENT 'Tushare-封单金额',
|
||||
`tushare_first_time` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tushare-首次封板时间',
|
||||
`tushare_last_time` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tushare-最后封板时间',
|
||||
`tushare_open_times` int DEFAULT NULL COMMENT 'Tushare-炸板/开板次数',
|
||||
`tushare_up_stat` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Tushare-涨停统计',
|
||||
`tushare_limit_times` int DEFAULT NULL COMMENT 'Tushare-连板数',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_date_code` (`trade_date`,`ts_code`),
|
||||
KEY `idx_trade_date` (`trade_date`),
|
||||
KEY `idx_symbol` (`symbol`),
|
||||
KEY `idx_trade_date_status` (`trade_date`,`final_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `trade_calendar` (
|
||||
`cal_date` date NOT NULL,
|
||||
`exchange` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`is_open` tinyint DEFAULT NULL,
|
||||
`pretrade_date` date DEFAULT NULL,
|
||||
PRIMARY KEY (`cal_date`,`exchange`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `stock_limit_data` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`trade_date` date NOT NULL COMMENT '交易日期',
|
||||
`symbol` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '股票代码',
|
||||
`ts_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'TS股票代码',
|
||||
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '股票名称',
|
||||
`status` tinyint NOT NULL COMMENT '状态:1-涨停,2-炸板',
|
||||
`limit_price` decimal(16,4) DEFAULT NULL COMMENT '计算出的理论涨停价',
|
||||
`close` decimal(16,4) DEFAULT NULL COMMENT '实际收盘价',
|
||||
`vol` bigint DEFAULT NULL COMMENT '成交量',
|
||||
`market` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属板块',
|
||||
`ak_code` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'AKShare-代码',
|
||||
`ak_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'AKShare-名称',
|
||||
`ak_pct_chg` decimal(10,4) DEFAULT NULL COMMENT 'AKShare-涨跌幅',
|
||||
`ak_latest_price` decimal(16,4) DEFAULT NULL COMMENT 'AKShare-最新价',
|
||||
`ak_amount` decimal(24,4) DEFAULT NULL COMMENT 'AKShare-成交额',
|
||||
`ak_float_market_value` decimal(24,4) DEFAULT NULL COMMENT 'AKShare-流通市值',
|
||||
`ak_total_market_value` decimal(24,4) DEFAULT NULL COMMENT 'AKShare-总市值',
|
||||
`ak_turnover_rate` decimal(10,4) DEFAULT NULL COMMENT 'AKShare-换手率',
|
||||
`ak_seal_amount` decimal(24,4) DEFAULT NULL COMMENT 'AKShare-封板资金',
|
||||
`ak_first_limit_time` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'AKShare-首次封板时间',
|
||||
`ak_last_limit_time` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'AKShare-最后封板时间',
|
||||
`ak_break_limit_count` int DEFAULT NULL COMMENT 'AKShare-炸板次数',
|
||||
`ak_limit_up_stat` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'AKShare-涨停统计',
|
||||
`ak_limit_up_days` int DEFAULT NULL COMMENT 'AKShare-连板数',
|
||||
`ak_industry` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'AKShare-所属行业',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_date_code` (`trade_date`,`ts_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
ALTER DATABASE `level` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `limit_list_d`
|
||||
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `stock_basic`
|
||||
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `stock_daily`
|
||||
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `stock_limit_combined_data`
|
||||
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `trade_calendar`
|
||||
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `stock_limit_data`
|
||||
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -112,8 +112,8 @@ func parseSecondCalRequest(c *gin.Context) (model.SecondCalRequest, error) {
|
||||
// includePagination 为 false 时跳过分页参数,供 CSV 导出复用同一套日期和过滤条件解析逻辑。
|
||||
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"))),
|
||||
TradeStartDate: firstNonEmptyQuery(c, "tradeStartDate", "trade_start_date", "start_date"),
|
||||
TradeEndDate: firstNonEmptyQuery(c, "tradeEndDate", "trade_end_date", "end_date"),
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
}
|
||||
@@ -159,6 +159,16 @@ func parseLimitBreakNextPerformanceRequest(c *gin.Context, includePagination boo
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// firstNonEmptyQuery 按优先级读取第一个非空查询参数。
|
||||
func firstNonEmptyQuery(c *gin.Context, keys ...string) string {
|
||||
for _, key := range keys {
|
||||
if value := strings.TrimSpace(c.Query(key)); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// parseOptionalBoolQuery 从多个兼容参数名中读取可选布尔值。
|
||||
//
|
||||
// 返回 nil 表示用户没有传入该过滤项;返回 *bool 表示需要按 true/false 过滤。
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/dbcompat"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -337,16 +336,21 @@ func validateSecondProfitDateRange(startDate, endDate string) error {
|
||||
// SQL 先找出“前一日涨停、前两日未涨停、当日继续涨停”的股票,再补齐 T+1 行情;
|
||||
// Go 代码负责应用动态过滤条件并计算开盘、最高、最低、收盘相对 T 日收盘的收益率。
|
||||
func (h *LimitBoardHandler) loadSecondProfitRows(req secondProfitQuery) ([]secondProfitCSVRow, error) {
|
||||
seeds, err := dbcompat.LoadSSEOpenCalendarSeeds(h.Service.DB, req.TradeStartDate, req.TradeEndDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(seeds) == 0 {
|
||||
return []secondProfitCSVRow{}, nil
|
||||
}
|
||||
calendarTable, calendarArgs := dbcompat.CalendarSeedTable(seeds)
|
||||
|
||||
query := `
|
||||
WITH calendar AS (
|
||||
SELECT
|
||||
cal_date AS trade_date,
|
||||
LAG(cal_date) OVER (ORDER BY cal_date) AS previous_trade_date,
|
||||
LAG(cal_date, 2) OVER (ORDER BY cal_date) AS previous_two_trade_date,
|
||||
LEAD(cal_date) OVER (ORDER BY cal_date) AS next_trade_date
|
||||
FROM trade_calendar
|
||||
WHERE exchange = 'SSE' AND is_open = 1
|
||||
),
|
||||
calendar_window AS (
|
||||
SELECT *
|
||||
FROM calendar
|
||||
WHERE trade_date BETWEEN ? AND ?
|
||||
)
|
||||
SELECT
|
||||
DATE_FORMAT(c.trade_date, '%Y-%m-%d') AS trade_date,
|
||||
DATE_FORMAT(c.previous_trade_date, '%Y-%m-%d') AS previous_trade_date,
|
||||
@@ -374,28 +378,28 @@ SELECT
|
||||
nd.high AS next_high,
|
||||
nd.low AS next_low,
|
||||
nd.close AS next_close
|
||||
FROM (` + calendarTable + `) c
|
||||
FROM calendar_window c
|
||||
INNER JOIN stock_limit_combined_data l
|
||||
ON l.trade_date = c.trade_date AND l.final_status = 1
|
||||
INNER JOIN stock_limit_combined_data pl
|
||||
ON ` + dbcompat.TsCodeEqual("pl.ts_code", "l.ts_code") + ` AND pl.trade_date = c.previous_trade_date AND pl.final_status = 1
|
||||
ON pl.ts_code = l.ts_code AND pl.trade_date = c.previous_trade_date AND pl.final_status = 1
|
||||
LEFT JOIN stock_limit_combined_data ppl
|
||||
ON ` + dbcompat.TsCodeEqual("ppl.ts_code", "l.ts_code") + ` AND ppl.trade_date = c.previous_two_trade_date AND ppl.final_status = 1
|
||||
ON ppl.ts_code = l.ts_code AND ppl.trade_date = c.previous_two_trade_date AND ppl.final_status = 1
|
||||
LEFT JOIN stock_daily d
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "d.ts_code") + ` AND d.trade_date = l.trade_date
|
||||
ON l.ts_code = d.ts_code AND d.trade_date = l.trade_date
|
||||
LEFT JOIN stock_daily pd
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "pd.ts_code") + ` AND pd.trade_date = c.previous_trade_date
|
||||
ON l.ts_code = pd.ts_code AND pd.trade_date = c.previous_trade_date
|
||||
LEFT JOIN stock_daily ppd
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "ppd.ts_code") + ` AND ppd.trade_date = c.previous_two_trade_date
|
||||
ON l.ts_code = ppd.ts_code AND ppd.trade_date = c.previous_two_trade_date
|
||||
LEFT JOIN stock_daily nd
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "nd.ts_code") + ` AND nd.trade_date = c.next_trade_date
|
||||
ON l.ts_code = nd.ts_code AND nd.trade_date = c.next_trade_date
|
||||
WHERE c.previous_trade_date IS NOT NULL
|
||||
AND c.previous_two_trade_date IS NOT NULL
|
||||
AND ppl.ts_code IS NULL
|
||||
ORDER BY c.trade_date ASC, l.ts_code ASC`
|
||||
|
||||
var rawRows []secondProfitCSVRow
|
||||
if err := h.Service.DB.Raw(query, calendarArgs...).Scan(&rawRows).Error; err != nil {
|
||||
if err := h.Service.DB.Raw(query, req.TradeStartDate, req.TradeEndDate).Scan(&rawRows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
package dbcompat
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CalendarSeed is a MySQL-version-neutral replacement for LAG/LEAD over the
|
||||
// trade calendar. Keeping the adjacent dates in Go avoids depending on MySQL 8.
|
||||
type CalendarSeed struct {
|
||||
TradeDate string
|
||||
PreviousTradeDate string
|
||||
PreviousTwoTradeDate string
|
||||
NextTradeDate string
|
||||
}
|
||||
|
||||
type calendarDateRow struct {
|
||||
TradeDate string `gorm:"column:trade_date"`
|
||||
}
|
||||
|
||||
type columnCollationRow struct {
|
||||
CollationName string `gorm:"column:collation_name"`
|
||||
}
|
||||
|
||||
// LoadSSEOpenCalendarSeeds loads all SSE open dates and builds the date window
|
||||
// needed by range queries. The returned seeds are limited to [startDate, endDate].
|
||||
func LoadSSEOpenCalendarSeeds(db *gorm.DB, startDate, endDate string) ([]CalendarSeed, error) {
|
||||
var rows []calendarDateRow
|
||||
if err := db.Table("trade_calendar").
|
||||
Select("DATE_FORMAT(cal_date, '%Y-%m-%d') AS trade_date").
|
||||
Where("exchange = ? AND is_open = ?", "SSE", 1).
|
||||
Order("cal_date ASC").
|
||||
Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seeds := make([]CalendarSeed, 0)
|
||||
for i, row := range rows {
|
||||
if row.TradeDate < startDate || row.TradeDate > endDate {
|
||||
continue
|
||||
}
|
||||
|
||||
seed := CalendarSeed{TradeDate: row.TradeDate}
|
||||
if i > 0 {
|
||||
seed.PreviousTradeDate = rows[i-1].TradeDate
|
||||
}
|
||||
if i > 1 {
|
||||
seed.PreviousTwoTradeDate = rows[i-2].TradeDate
|
||||
}
|
||||
if i+1 < len(rows) {
|
||||
seed.NextTradeDate = rows[i+1].TradeDate
|
||||
}
|
||||
seeds = append(seeds, seed)
|
||||
}
|
||||
|
||||
return seeds, nil
|
||||
}
|
||||
|
||||
// CalendarSeedTable converts calendar seeds into a derived table that works on
|
||||
// MySQL 5.7, MySQL 8, and MariaDB without CTE/window-function support.
|
||||
func CalendarSeedTable(seeds []CalendarSeed) (string, []any) {
|
||||
if len(seeds) == 0 {
|
||||
return `SELECT
|
||||
CAST(NULL AS DATE) AS trade_date,
|
||||
CAST(NULL AS DATE) AS previous_trade_date,
|
||||
CAST(NULL AS DATE) AS previous_two_trade_date,
|
||||
CAST(NULL AS DATE) AS next_trade_date
|
||||
WHERE 1 = 0`, nil
|
||||
}
|
||||
|
||||
parts := make([]string, 0, len(seeds))
|
||||
args := make([]any, 0, len(seeds)*4)
|
||||
for _, seed := range seeds {
|
||||
parts = append(parts, `SELECT
|
||||
CAST(? AS DATE) AS trade_date,
|
||||
CAST(? AS DATE) AS previous_trade_date,
|
||||
CAST(? AS DATE) AS previous_two_trade_date,
|
||||
CAST(? AS DATE) AS next_trade_date`)
|
||||
args = append(args,
|
||||
nullableDate(seed.TradeDate),
|
||||
nullableDate(seed.PreviousTradeDate),
|
||||
nullableDate(seed.PreviousTwoTradeDate),
|
||||
nullableDate(seed.NextTradeDate),
|
||||
)
|
||||
}
|
||||
|
||||
return strings.Join(parts, "\nUNION ALL\n"), args
|
||||
}
|
||||
|
||||
// TsCodeEqual keeps joins free of hard-coded COLLATE clauses. This avoids
|
||||
// binding SQL to MySQL-8-only collations while preserving index-friendly joins.
|
||||
func TsCodeEqual(left, right string) string {
|
||||
return left + " = " + right
|
||||
}
|
||||
|
||||
// ColumnCollation returns the current database collation for one column.
|
||||
func ColumnCollation(db *gorm.DB, tableName, columnName string) (string, error) {
|
||||
var row columnCollationRow
|
||||
err := db.Raw(`
|
||||
SELECT COLLATION_NAME AS collation_name
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = ?
|
||||
AND COLUMN_NAME = ?
|
||||
LIMIT 1`, tableName, columnName).Scan(&row).Error
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return row.CollationName, nil
|
||||
}
|
||||
|
||||
// TsCodeEqualUnicode compares a utf8mb4_unicode_ci column with a column that
|
||||
// may be utf8mb4_0900_ai_ci. utf8mb4_unicode_ci exists on both MySQL 5.7 and 8.
|
||||
func TsCodeEqualUnicode(left, right string) string {
|
||||
return left + " = " + right + " COLLATE utf8mb4_unicode_ci"
|
||||
}
|
||||
|
||||
// TsCodeEqualRightCollation compares two stock-code columns using a collation
|
||||
// known to exist on the connected database. This avoids MySQL-version-specific
|
||||
// literals while preserving the fast side of joins such as stock_daily.ts_code.
|
||||
func TsCodeEqualRightCollation(left, right, collation string) string {
|
||||
if !safeCollationName(collation) {
|
||||
return TsCodeEqual(left, right)
|
||||
}
|
||||
return left + " = " + right + " COLLATE " + collation
|
||||
}
|
||||
|
||||
func safeCollationName(collation string) bool {
|
||||
if collation == "" {
|
||||
return false
|
||||
}
|
||||
for _, r := range collation {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func nullableDate(date string) any {
|
||||
if strings.TrimSpace(date) == "" {
|
||||
return nil
|
||||
}
|
||||
return date
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"level_go/internal/dbcompat"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
@@ -105,16 +104,21 @@ func (s *LimitBoardService) ListLimitBreakNextPerformanceStocks(req model.LimitB
|
||||
//
|
||||
// 口径:前一交易日涨停、前两交易日未涨停、当前交易日 final_status = 2。
|
||||
func (s *LimitBoardService) loadLimitBreakNextPerformanceRows(startDate, endDate string) ([]limitBreakNextPerformanceRow, error) {
|
||||
seeds, err := dbcompat.LoadSSEOpenCalendarSeeds(s.DB, startDate, endDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(seeds) == 0 {
|
||||
return []limitBreakNextPerformanceRow{}, nil
|
||||
}
|
||||
calendarTable, calendarArgs := dbcompat.CalendarSeedTable(seeds)
|
||||
|
||||
query := `
|
||||
WITH calendar AS (
|
||||
SELECT
|
||||
cal_date AS trade_date,
|
||||
LAG(cal_date) OVER (ORDER BY cal_date) AS previous_trade_date,
|
||||
LAG(cal_date, 2) OVER (ORDER BY cal_date) AS previous_two_trade_date,
|
||||
LEAD(cal_date) OVER (ORDER BY cal_date) AS next_trade_date
|
||||
FROM trade_calendar
|
||||
WHERE exchange = 'SSE' AND is_open = 1
|
||||
),
|
||||
calendar_window AS (
|
||||
SELECT *
|
||||
FROM calendar
|
||||
WHERE trade_date BETWEEN ? AND ?
|
||||
)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT
|
||||
@@ -143,21 +147,21 @@ FROM (
|
||||
nd.high AS next_high,
|
||||
nd.low AS next_low,
|
||||
nd.close AS next_close
|
||||
FROM (` + calendarTable + `) c
|
||||
FROM calendar_window c
|
||||
INNER JOIN stock_limit_combined_data l
|
||||
ON l.trade_date = c.trade_date AND l.final_status = 2
|
||||
INNER JOIN stock_limit_combined_data pl
|
||||
ON ` + dbcompat.TsCodeEqual("pl.ts_code", "l.ts_code") + ` AND pl.trade_date = c.previous_trade_date AND pl.final_status = 1
|
||||
ON pl.ts_code = l.ts_code AND pl.trade_date = c.previous_trade_date AND pl.final_status = 1
|
||||
LEFT JOIN stock_limit_combined_data ppl
|
||||
ON ` + dbcompat.TsCodeEqual("ppl.ts_code", "l.ts_code") + ` AND ppl.trade_date = c.previous_two_trade_date AND ppl.final_status = 1
|
||||
ON ppl.ts_code = l.ts_code AND ppl.trade_date = c.previous_two_trade_date AND ppl.final_status = 1
|
||||
LEFT JOIN stock_daily d
|
||||
ON ` + dbcompat.TsCodeEqual("d.ts_code", "l.ts_code") + ` AND d.trade_date = c.trade_date
|
||||
ON d.ts_code = l.ts_code AND d.trade_date = c.trade_date
|
||||
LEFT JOIN stock_daily pd
|
||||
ON ` + dbcompat.TsCodeEqual("pd.ts_code", "l.ts_code") + ` AND pd.trade_date = c.previous_trade_date
|
||||
ON pd.ts_code = l.ts_code AND pd.trade_date = c.previous_trade_date
|
||||
LEFT JOIN stock_daily nd
|
||||
ON ` + dbcompat.TsCodeEqual("nd.ts_code", "l.ts_code") + ` AND nd.trade_date = c.next_trade_date
|
||||
ON nd.ts_code = l.ts_code AND nd.trade_date = c.next_trade_date
|
||||
LEFT JOIN stock_basic sb
|
||||
ON ` + dbcompat.TsCodeEqualUnicode("sb.ts_code", "l.ts_code") + `
|
||||
ON sb.ts_code = l.ts_code
|
||||
WHERE c.previous_trade_date IS NOT NULL
|
||||
AND c.previous_two_trade_date IS NOT NULL
|
||||
AND ppl.ts_code IS NULL
|
||||
@@ -167,7 +171,7 @@ WHERE previous_close IS NOT NULL
|
||||
ORDER BY trade_date ASC, ts_code ASC`
|
||||
|
||||
var rows []limitBreakNextPerformanceRow
|
||||
err = s.DB.Raw(query, calendarArgs...).Scan(&rows).Error
|
||||
err := s.DB.Raw(query, startDate, endDate).Scan(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"level_go/internal/dbcompat"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
@@ -147,7 +146,7 @@ func (s *LimitBoardService) loadLimitOpenCompareStocks(tradeDate, previousTradeD
|
||||
(? <> '' AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM stock_limit_combined_data ppl
|
||||
WHERE ` + dbcompat.TsCodeEqual("ppl.ts_code", "l.ts_code") + ` AND ppl.trade_date = ? AND ppl.final_status = 1
|
||||
WHERE ppl.ts_code = l.ts_code AND ppl.trade_date = ? AND ppl.final_status = 1
|
||||
)) AS is_first_to_second_board,
|
||||
COALESCE(l.calc_limit_price, 0) AS limit_price,
|
||||
COALESCE(l.tushare_pct_chg, d.pct_chg, 0) AS pct_chg,
|
||||
@@ -165,11 +164,11 @@ func (s *LimitBoardService) loadLimitOpenCompareStocks(tradeDate, previousTradeD
|
||||
l.calc_vol AS vol
|
||||
FROM stock_limit_combined_data l
|
||||
LEFT JOIN stock_daily d
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "d.ts_code") + ` AND d.trade_date = l.trade_date
|
||||
ON l.ts_code = d.ts_code AND d.trade_date = l.trade_date
|
||||
LEFT JOIN stock_daily pd
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "pd.ts_code") + ` AND pd.trade_date = ?
|
||||
ON l.ts_code = pd.ts_code AND pd.trade_date = ?
|
||||
INNER JOIN stock_limit_combined_data pl
|
||||
ON ` + dbcompat.TsCodeEqual("pl.ts_code", "l.ts_code") + ` AND pl.trade_date = ? AND pl.final_status = 1
|
||||
ON pl.ts_code = l.ts_code AND pl.trade_date = ? AND pl.final_status = 1
|
||||
WHERE l.trade_date = ? AND l.final_status = 1
|
||||
ORDER BY d.pct_chg DESC, l.ts_code ASC`
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"level_go/internal/dbcompat"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
@@ -94,11 +93,11 @@ func (s *LimitBoardService) loadLimitOpenThresholdStocks(tradeDate, previousTrad
|
||||
l.calc_vol AS vol
|
||||
FROM stock_limit_combined_data l
|
||||
LEFT JOIN stock_daily d
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "d.ts_code") + ` AND d.trade_date = l.trade_date
|
||||
ON l.ts_code = d.ts_code AND d.trade_date = l.trade_date
|
||||
LEFT JOIN stock_daily pd
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "pd.ts_code") + ` AND pd.trade_date = ?
|
||||
ON l.ts_code = pd.ts_code AND pd.trade_date = ?
|
||||
INNER JOIN stock_limit_combined_data pl
|
||||
ON ` + dbcompat.TsCodeEqual("pl.ts_code", "l.ts_code") + ` AND pl.trade_date = ? AND pl.final_status = 1
|
||||
ON pl.ts_code = l.ts_code AND pl.trade_date = ? AND pl.final_status = 1
|
||||
WHERE l.trade_date = ? AND l.final_status = 1
|
||||
ORDER BY d.pct_chg DESC, l.ts_code ASC`
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"level_go/internal/dbcompat"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
@@ -147,7 +146,7 @@ func (s *LimitBoardService) loadCurrentLimitStocks(tradeDate string) ([]model.Li
|
||||
l.calc_vol AS vol
|
||||
FROM stock_limit_combined_data l
|
||||
LEFT JOIN stock_daily d
|
||||
ON ` + dbcompat.TsCodeEqual("l.ts_code", "d.ts_code") + ` AND d.trade_date = l.trade_date
|
||||
ON l.ts_code = d.ts_code AND d.trade_date = l.trade_date
|
||||
WHERE l.trade_date = ? AND l.final_status = 1
|
||||
ORDER BY d.pct_chg DESC, l.ts_code ASC`
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"math"
|
||||
|
||||
"level_go/internal/dbcompat"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
@@ -88,19 +87,29 @@ func (s *LimitBoardService) GetSecondCal(req model.SecondCalRequest) (model.Seco
|
||||
|
||||
// loadSecondCalDays 查询日期范围内的开市交易日和相邻交易日。
|
||||
func (s *LimitBoardService) loadSecondCalDays(startDate, endDate string) ([]secondCalDaySeed, map[string]int, error) {
|
||||
seeds, err := dbcompat.LoadSSEOpenCalendarSeeds(s.DB, startDate, endDate)
|
||||
if err != nil {
|
||||
query := `
|
||||
WITH calendar AS (
|
||||
SELECT
|
||||
cal_date AS trade_date,
|
||||
LAG(cal_date) OVER (ORDER BY cal_date) AS previous_trade_date,
|
||||
LAG(cal_date, 2) OVER (ORDER BY cal_date) AS previous_two_trade_date,
|
||||
LEAD(cal_date) OVER (ORDER BY cal_date) AS next_trade_date
|
||||
FROM trade_calendar
|
||||
WHERE exchange = 'SSE' AND is_open = 1
|
||||
)
|
||||
SELECT
|
||||
DATE_FORMAT(trade_date, '%Y-%m-%d') AS trade_date,
|
||||
COALESCE(DATE_FORMAT(previous_trade_date, '%Y-%m-%d'), '') AS previous_trade_date,
|
||||
COALESCE(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
|
||||
FROM calendar
|
||||
WHERE trade_date BETWEEN ? AND ?
|
||||
ORDER BY trade_date ASC`
|
||||
|
||||
var days []secondCalDaySeed
|
||||
if err := s.DB.Raw(query, startDate, endDate).Scan(&days).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
days := make([]secondCalDaySeed, 0, len(seeds))
|
||||
for _, seed := range seeds {
|
||||
days = append(days, secondCalDaySeed{
|
||||
TradeDate: seed.TradeDate,
|
||||
PreviousTradeDate: seed.PreviousTradeDate,
|
||||
PreviousTwoTradeDate: seed.PreviousTwoTradeDate,
|
||||
NextTradeDate: seed.NextTradeDate,
|
||||
})
|
||||
}
|
||||
|
||||
// 构建交易日到响应数组下标的映射,后续聚合明细时可以 O(1) 定位。
|
||||
dayIndex := make(map[string]int, len(days))
|
||||
@@ -112,20 +121,21 @@ func (s *LimitBoardService) loadSecondCalDays(startDate, endDate string) ([]seco
|
||||
|
||||
// loadSecondCalRows 查询 T-1 涨停、T-2 未涨停、T 日再次触及涨停价的二板股票。
|
||||
func (s *LimitBoardService) loadSecondCalRows(startDate, endDate string) ([]secondCalRow, error) {
|
||||
seeds, err := dbcompat.LoadSSEOpenCalendarSeeds(s.DB, startDate, endDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(seeds) == 0 {
|
||||
return []secondCalRow{}, nil
|
||||
}
|
||||
calendarTable, calendarArgs := dbcompat.CalendarSeedTable(seeds)
|
||||
stockDailyTsCodeCollation, err := dbcompat.ColumnCollation(s.DB, "stock_daily", "ts_code")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := `
|
||||
WITH calendar AS (
|
||||
SELECT
|
||||
cal_date AS trade_date,
|
||||
LAG(cal_date) OVER (ORDER BY cal_date) AS previous_trade_date,
|
||||
LAG(cal_date, 2) OVER (ORDER BY cal_date) AS previous_two_trade_date,
|
||||
LEAD(cal_date) OVER (ORDER BY cal_date) AS next_trade_date
|
||||
FROM trade_calendar
|
||||
WHERE exchange = 'SSE' AND is_open = 1
|
||||
),
|
||||
calendar_window AS (
|
||||
SELECT *
|
||||
FROM calendar
|
||||
WHERE trade_date BETWEEN ? AND ?
|
||||
)
|
||||
SELECT
|
||||
DATE_FORMAT(trade_date, '%Y-%m-%d') AS trade_date,
|
||||
DATE_FORMAT(previous_trade_date, '%Y-%m-%d') AS previous_trade_date,
|
||||
@@ -190,21 +200,21 @@ FROM (
|
||||
END AS open_times,
|
||||
nd.high AS next_high,
|
||||
nd.close AS next_close
|
||||
FROM (` + calendarTable + `) c
|
||||
FROM calendar_window c
|
||||
INNER JOIN limit_list_d pl
|
||||
ON pl.trade_date = c.previous_trade_date AND pl.` + "`limit`" + ` = 'U'
|
||||
LEFT JOIN limit_list_d ppl
|
||||
ON ` + dbcompat.TsCodeEqual("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
|
||||
ON ` + dbcompat.TsCodeEqualRightCollation("d.ts_code", "pl.ts_code", stockDailyTsCodeCollation) + ` AND d.trade_date = c.trade_date
|
||||
ON d.ts_code = pl.ts_code AND d.trade_date = c.trade_date
|
||||
LEFT JOIN stock_daily nd
|
||||
ON ` + dbcompat.TsCodeEqualRightCollation("nd.ts_code", "pl.ts_code", stockDailyTsCodeCollation) + ` AND nd.trade_date = c.next_trade_date
|
||||
ON nd.ts_code = pl.ts_code AND nd.trade_date = c.next_trade_date
|
||||
LEFT JOIN limit_list_d tlu
|
||||
ON ` + dbcompat.TsCodeEqual("tlu.ts_code", "pl.ts_code") + ` AND tlu.trade_date = c.trade_date AND tlu.` + "`limit`" + ` = 'U'
|
||||
ON tlu.ts_code = pl.ts_code AND tlu.trade_date = c.trade_date AND tlu.` + "`limit`" + ` = 'U'
|
||||
LEFT JOIN limit_list_d tlz
|
||||
ON ` + dbcompat.TsCodeEqual("tlz.ts_code", "pl.ts_code") + ` AND tlz.trade_date = c.trade_date AND tlz.` + "`limit`" + ` = 'Z'
|
||||
ON tlz.ts_code = pl.ts_code AND tlz.trade_date = c.trade_date AND tlz.` + "`limit`" + ` = 'Z'
|
||||
LEFT JOIN stock_basic sb
|
||||
ON ` + dbcompat.TsCodeEqual("sb.ts_code", "pl.ts_code") + `
|
||||
ON sb.ts_code = pl.ts_code
|
||||
WHERE c.previous_trade_date IS NOT NULL
|
||||
AND c.previous_two_trade_date IS NOT NULL
|
||||
AND ppl.ts_code IS NULL
|
||||
@@ -219,7 +229,7 @@ WHERE previous_close IS NOT NULL
|
||||
ORDER BY trade_date ASC, ts_code ASC`
|
||||
|
||||
var rows []secondCalRow
|
||||
err = s.DB.Raw(query, calendarArgs...).Scan(&rows).Error
|
||||
err := s.DB.Raw(query, startDate, endDate).Scan(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"sort"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"level_go/internal/dbcompat"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
@@ -46,7 +45,7 @@ func (s *StockService) AnalyzePerformance(startDate, endDate string) ([]model.An
|
||||
|
||||
// 查询 startDate 当日涨停股,并优先使用涨停合并表中的计算最高价。
|
||||
query := `SELECT l.ts_code, l.name, d.open, COALESCE(l.calc_high, d.high, 0) AS high FROM stock_limit_combined_data l
|
||||
LEFT JOIN stock_daily d ON ` + dbcompat.TsCodeEqual("l.ts_code", "d.ts_code") + ` AND l.trade_date = d.trade_date
|
||||
LEFT JOIN stock_daily d ON l.ts_code = d.ts_code AND l.trade_date = d.trade_date
|
||||
WHERE l.trade_date = ? AND l.final_status = 1`
|
||||
|
||||
if err := s.DB.Raw(query, startDate).Scan(&limits).Error; err != nil {
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
macbook pro 本地
|
||||
-- auto-generated definition
|
||||
create table stock_basic
|
||||
(
|
||||
ts_code text null,
|
||||
symbol text null,
|
||||
name text null,
|
||||
area text null,
|
||||
industry text null,
|
||||
fullname text null,
|
||||
enname text null,
|
||||
cnspell text null,
|
||||
market text null,
|
||||
exchange text null,
|
||||
curr_type text null,
|
||||
list_status text null,
|
||||
list_date text null,
|
||||
delist_date text null,
|
||||
is_hs text null,
|
||||
act_name text null,
|
||||
act_ent_type text null
|
||||
);
|
||||
|
||||
-- auto-generated definition
|
||||
create table stock_daily
|
||||
(
|
||||
id bigint auto_increment
|
||||
primary key,
|
||||
ts_code varchar(20) not null,
|
||||
symbol varchar(10) not null,
|
||||
trade_date date not null,
|
||||
open decimal(16, 4) null,
|
||||
high decimal(16, 4) null,
|
||||
low decimal(16, 4) null,
|
||||
close decimal(16, 4) null,
|
||||
pre_close decimal(16, 4) null,
|
||||
change_val decimal(16, 4) null,
|
||||
pct_chg decimal(10, 4) null,
|
||||
vol bigint null,
|
||||
amount bigint null,
|
||||
constraint uniq_code_date
|
||||
unique (ts_code, trade_date)
|
||||
)
|
||||
charset = utf8mb4;
|
||||
|
||||
-- auto-generated definition
|
||||
create table stock_limit_combined_data
|
||||
(
|
||||
id bigint auto_increment
|
||||
primary key,
|
||||
trade_date date not null comment '交易日期',
|
||||
symbol varchar(10) not null comment '股票代码,以stock_daily为准',
|
||||
ts_code varchar(20) not null comment 'TS股票代码,以stock_daily为准',
|
||||
name varchar(50) not null comment '股票名称,以stock_daily关联基础表为准',
|
||||
final_status tinyint not null comment '融合状态:1-涨停,2-炸板',
|
||||
source_count int default 0 not null comment '命中的数据源数量',
|
||||
source_flags varchar(50) null comment '命中的数据源:calc,tushare',
|
||||
calc_status tinyint null comment '本地行情计算状态:1-涨停,2-炸板',
|
||||
calc_limit_price decimal(16, 4) null comment '本地行情计算涨停价',
|
||||
calc_high decimal(16, 4) null comment 'stock_daily最高价',
|
||||
calc_close decimal(16, 4) null comment 'stock_daily收盘价',
|
||||
calc_pre_close decimal(16, 4) null comment 'stock_daily前收盘价',
|
||||
calc_vol bigint null comment 'stock_daily成交量',
|
||||
market varchar(20) null comment '所属板块',
|
||||
tushare_limit_type varchar(1) null comment 'Tushare-D跌停U涨停Z炸板',
|
||||
tushare_industry varchar(100) null comment 'Tushare-所属行业',
|
||||
tushare_name varchar(50) null comment 'Tushare-名称',
|
||||
tushare_close decimal(16, 4) null comment 'Tushare-收盘价',
|
||||
tushare_pct_chg decimal(10, 4) null comment 'Tushare-涨跌幅',
|
||||
tushare_amount decimal(24, 4) null comment 'Tushare-成交额',
|
||||
tushare_limit_amount decimal(24, 4) null comment 'Tushare-板上成交金额',
|
||||
tushare_float_mv decimal(24, 4) null comment 'Tushare-流通市值',
|
||||
tushare_total_mv decimal(24, 4) null comment 'Tushare-总市值',
|
||||
tushare_turnover_ratio decimal(10, 4) null comment 'Tushare-换手率',
|
||||
tushare_fd_amount decimal(24, 4) null comment 'Tushare-封单金额',
|
||||
tushare_first_time varchar(20) null comment 'Tushare-首次封板时间',
|
||||
tushare_last_time varchar(20) null comment 'Tushare-最后封板时间',
|
||||
tushare_open_times int null comment 'Tushare-炸板/开板次数',
|
||||
tushare_up_stat varchar(50) null comment 'Tushare-涨停统计',
|
||||
tushare_limit_times int null comment 'Tushare-连板数',
|
||||
constraint uniq_date_code
|
||||
unique (trade_date, ts_code)
|
||||
);
|
||||
|
||||
create index idx_symbol
|
||||
on stock_limit_combined_data (symbol);
|
||||
|
||||
create index idx_trade_date
|
||||
on stock_limit_combined_data (trade_date);
|
||||
|
||||
create index idx_trade_date_status
|
||||
on stock_limit_combined_data (trade_date, final_status);
|
||||
|
||||
-- auto-generated definition
|
||||
create table trade_calendar
|
||||
(
|
||||
cal_date date not null,
|
||||
exchange varchar(10) not null,
|
||||
is_open tinyint null,
|
||||
pretrade_date date null,
|
||||
primary key (cal_date, exchange)
|
||||
)
|
||||
charset = utf8mb4;
|
||||
|
||||
阿里云
|
||||
-- auto-generated definition
|
||||
create table stock_basic
|
||||
(
|
||||
ts_code text null,
|
||||
symbol text null,
|
||||
name text null,
|
||||
area text null,
|
||||
industry text null,
|
||||
fullname text null,
|
||||
enname text null,
|
||||
cnspell text null,
|
||||
market text null,
|
||||
exchange text null,
|
||||
curr_type text null,
|
||||
list_status text null,
|
||||
list_date text null,
|
||||
delist_date text null,
|
||||
is_hs text null,
|
||||
act_name text null,
|
||||
act_ent_type text null
|
||||
);
|
||||
|
||||
-- auto-generated definition
|
||||
create table stock_daily
|
||||
(
|
||||
id bigint auto_increment
|
||||
primary key,
|
||||
ts_code varchar(20) not null,
|
||||
symbol varchar(10) not null,
|
||||
trade_date date not null,
|
||||
open decimal(16, 4) null,
|
||||
high decimal(16, 4) null,
|
||||
low decimal(16, 4) null,
|
||||
close decimal(16, 4) null,
|
||||
pre_close decimal(16, 4) null,
|
||||
change_val decimal(16, 4) null,
|
||||
pct_chg decimal(10, 4) null,
|
||||
vol bigint null,
|
||||
amount bigint null,
|
||||
constraint uniq_code_date
|
||||
unique (ts_code, trade_date)
|
||||
);
|
||||
|
||||
-- auto-generated definition
|
||||
create table trade_calendar
|
||||
(
|
||||
cal_date date not null,
|
||||
exchange varchar(10) not null,
|
||||
is_open tinyint null,
|
||||
pretrade_date date null,
|
||||
primary key (cal_date, exchange)
|
||||
)
|
||||
charset = utf8mb4;
|
||||
|
||||
Reference in New Issue
Block a user