9 Commits
v0.0.4 ... main

Author SHA1 Message Date
62c420c8bf fix bug 2026-05-03 21:48:42 +08:00
0d008bb3ab fix cal 2026-05-03 18:53:48 +08:00
165423e668 fix bug 2026-05-02 22:47:06 +08:00
ea69c515d2 fix bug 2026-05-02 13:00:06 +08:00
a2b101621b fix bug 2026-05-02 12:58:16 +08:00
7f3117fd88 fix bug 2026-05-02 12:03:47 +08:00
ba41a6af76 add money flow. 2026-05-02 10:01:33 +08:00
94de0de8c7 fix bug 2026-05-01 23:36:45 +08:00
4f4781aad3 fix schema 2026-05-01 17:47:58 +08:00
15 changed files with 535 additions and 176 deletions

View File

@@ -2,7 +2,6 @@ package markdown
import (
"bytes"
"io"
"os"
"strings"
)
@@ -78,6 +77,10 @@ func (m *Builder) SaveToFile(filePath string) error {
}
defer rf.Close()
_, err = io.Copy(rf, &m.buf)
return err
_, err = rf.WriteString(m.Build())
if err != nil {
return err
}
return nil
}

View File

@@ -2,8 +2,8 @@ package schema
import "gorm.io/gorm"
// DatasetStockBasic 股票基本信息表(合并 dataset/stock 与 gostock 字段gostock 独有 Level、Desc
type DatasetStockBasic struct {
// DatasetBasic 股票基本信息表(合并 dataset/stock 与 gostock 字段gostock 独有 Level、Desc
type DatasetBasic struct {
gorm.Model
TsCode string `gorm:"type:varchar(50);not null;index;comment:TS代码"`
Symbol string `gorm:"type:varchar(50);not null;comment:股票代码"`
@@ -21,17 +21,17 @@ type DatasetStockBasic struct {
ActEntType string `gorm:"type:varchar(50);not null;default:'';comment:实控人企业性质"`
}
func (DatasetStockBasic) TableName() string {
return "dataset_stock_basic"
func (DatasetBasic) TableName() string {
return "dataset_basic"
}
// Key 业务主键TS 代码。
func (s *DatasetStockBasic) Key() string {
func (s *DatasetBasic) Key() string {
return s.TsCode
}
// DisplaySymbol 展示用代码:有 symbol 用 symbol否则用 ts_code。
func (s *DatasetStockBasic) DisplaySymbol() string {
func (s *DatasetBasic) DisplaySymbol() string {
if s.Symbol != "" {
return s.Symbol
}
@@ -39,6 +39,6 @@ func (s *DatasetStockBasic) DisplaySymbol() string {
}
// IsNorthbound 是否沪深港通标的H 沪股通 / S 深股通)。
func (s *DatasetStockBasic) IsNorthbound() bool {
func (s *DatasetBasic) IsNorthbound() bool {
return s.IsHS == "H" || s.IsHS == "S"
}

View File

@@ -1,10 +1,8 @@
package schema
import "gorm.io/gorm"
// DatasetBlocksMember 板块成分股。
type DatasetBlocksMember struct {
gorm.Model
ID uint `gorm:"primarykey"`
TiCode string `gorm:"type:varchar(50);not null;default:'';comment:板块代码;index" json:"ti_code"`
TsCode string `gorm:"type:varchar(50);not null;default:'';comment:股票代码;index" json:"ts_code"`
Weight float64 `gorm:"type:float;not null;default:0;comment:权重" json:"weight"`

View File

@@ -2,8 +2,8 @@ package schema
import "strconv"
// DatasetStockDaily 股票日线数据(两仓库结构一致)。
type DatasetStockDaily struct {
// DatasetDaily 股票日线数据(两仓库结构一致)。
type DatasetDaily struct {
ID uint `gorm:"primarykey;autoIncrement" json:"id"`
TsCode string `gorm:"type:varchar(20);not null;index:idx_ts_code;uniqueIndex:un_code_date;comment:股票代码" json:"ts_code"`
TradeDate int `gorm:"index:idx_trade_date;uniqueIndex:un_code_date;comment:交易日期" json:"trade_date"`
@@ -19,12 +19,12 @@ type DatasetStockDaily struct {
Amount float64 `gorm:"type:decimal(20,2);comment:成交额(千元)" json:"amount"`
}
func (DatasetStockDaily) TableName() string {
return "dataset_stock_daily"
func (DatasetDaily) TableName() string {
return "dataset_daily"
}
// Key 业务主键ts_code + 交易日。
func (d *DatasetStockDaily) Key() string {
func (d *DatasetDaily) Key() string {
if d.TsCode == "" && d.TradeDate == 0 {
return ""
}
@@ -32,17 +32,17 @@ func (d *DatasetStockDaily) Key() string {
}
// IsRising 是否收涨(昨收有效且收盘高于昨收)。
func (d *DatasetStockDaily) IsRising() bool {
func (d *DatasetDaily) IsRising() bool {
return d.PreClose > 0 && d.Close > d.PreClose
}
// IsFalling 是否收跌。
func (d *DatasetStockDaily) IsFalling() bool {
func (d *DatasetDaily) IsFalling() bool {
return d.PreClose > 0 && d.Close < d.PreClose
}
// PctChangeFromPre 由昨收计算的涨跌幅(%);昨收无效时返回 0。
func (d *DatasetStockDaily) PctChangeFromPre() float64 {
func (d *DatasetDaily) PctChangeFromPre() float64 {
if d.PreClose <= 0 {
return 0
}
@@ -50,7 +50,7 @@ func (d *DatasetStockDaily) PctChangeFromPre() float64 {
}
// AmplitudePct 振幅(相对昨收,%(最高-最低)/昨收*100。
func (d *DatasetStockDaily) AmplitudePct() float64 {
func (d *DatasetDaily) AmplitudePct() float64 {
if d.PreClose <= 0 {
return 0
}

View File

@@ -6,8 +6,8 @@ import (
"gorm.io/gorm"
)
// DatasetStockFinaIndicator 财务指标模型
type DatasetStockFinaIndicator struct {
// DatasetFinaIndicator 财务指标模型
type DatasetFinaIndicator struct {
gorm.Model
TsCode string `gorm:"type:varchar(20);not null;index:fi_ts_code;uniqueIndex:un_fi_code_date;comment:TS代码"`
Period int `gorm:"index:idx_period;uniqueIndex:un_fi_code_date;comment:报告期数"`
@@ -205,12 +205,12 @@ type DatasetStockFinaIndicator struct {
}
// TableName 设置表名
func (DatasetStockFinaIndicator) TableName() string {
func (DatasetFinaIndicator) TableName() string {
return "dataset_fina_indicator"
}
// Key 与表 uniqueIndex un_fi_code_date 一致ts_code + period。
func (f *DatasetStockFinaIndicator) Key() string {
func (f *DatasetFinaIndicator) Key() string {
if f.TsCode == "" && f.Period == 0 {
return ""
}
@@ -218,7 +218,7 @@ func (f *DatasetStockFinaIndicator) Key() string {
}
// RowLabel 便于日志/调试ts_code + 报告期 end_date + 公告 ann_date。
func (f *DatasetStockFinaIndicator) RowLabel() string {
func (f *DatasetFinaIndicator) RowLabel() string {
if f.EndDate != "" || f.AnnDate != "" {
return f.TsCode + " end=" + f.EndDate + " ann=" + f.AnnDate
}

View File

@@ -2,8 +2,8 @@ package schema
import "strconv"
// DatasetStockIndicator 每日基本面指标(采用 dataset/stock 的 decimal 定义,与采集端迁移一致)。
type DatasetStockIndicator struct {
// DatasetIndicator 每日基本面指标(采用 dataset/stock 的 decimal 定义,与采集端迁移一致)。
type DatasetIndicator struct {
ID uint `gorm:"primarykey;autoIncrement"`
TsCode string `gorm:"type:varchar(20);not null;index:si_ts_code;uniqueIndex:un_si_code_date;comment:股票代码" json:"ts_code"`
TradeDate int `gorm:"index:si_trade_date;uniqueIndex:un_si_code_date;comment:交易日期" json:"trade_date"`
@@ -23,15 +23,14 @@ type DatasetStockIndicator struct {
FreeShare float64 `gorm:"type:decimal(20,4);comment:自由流通股本(万)"`
TotalMv float64 `gorm:"type:decimal(20,4);comment:总市值(万元)"`
CircMv float64 `gorm:"type:decimal(20,4);comment:流通市值(万元)"`
Roe float64 `gorm:"type:decimal(20,4);comment:ROE(%) 净利润/股本"`
}
func (DatasetStockIndicator) TableName() string {
return "dataset_stock_indicator"
func (DatasetIndicator) TableName() string {
return "dataset_indicator"
}
// Key 业务主键ts_code + 交易日。
func (s *DatasetStockIndicator) Key() string {
func (s *DatasetIndicator) Key() string {
if s.TsCode == "" && s.TradeDate == 0 {
return ""
}
@@ -39,11 +38,11 @@ func (s *DatasetStockIndicator) Key() string {
}
// HasTotalMV 总市值是否已填充(大于 0
func (s *DatasetStockIndicator) HasTotalMV() bool {
func (s *DatasetIndicator) HasTotalMV() bool {
return s.TotalMv > 0
}
// HasCircMV 流通市值是否已填充。
func (s *DatasetStockIndicator) HasCircMV() bool {
func (s *DatasetIndicator) HasCircMV() bool {
return s.CircMv > 0
}

View File

@@ -32,25 +32,25 @@ type DatasetIndicatorPro struct {
TotalMv float64 `gorm:"type:decimal(20,4);comment:总市值(万元)"`
CircMv float64 `gorm:"type:decimal(20,4);comment:流通市值(万元)"`
AdjFactor float64 `gorm:"type:decimal(20,6);comment:复权因子"`
MaBfq5 float64 `gorm:"type:decimal(20,6);comment:MA5复权"`
MaBfq10 float64 `gorm:"type:decimal(20,6);comment:MA10复权"`
MaBfq20 float64 `gorm:"type:decimal(20,6);comment:MA20复权"`
MaBfq60 float64 `gorm:"type:decimal(20,6);comment:MA60复权"`
EmaBfq5 float64 `gorm:"type:decimal(20,6);comment:EMA5复权"`
EmaBfq10 float64 `gorm:"type:decimal(20,6);comment:EMA10复权"`
EmaBfq20 float64 `gorm:"type:decimal(20,6);comment:EMA20复权"`
MacdBfq float64 `gorm:"type:decimal(20,6);comment:MACD复权"`
MacdDifBfq float64 `gorm:"type:decimal(20,6);comment:MACD DIF复权"`
MacdDeaBfq float64 `gorm:"type:decimal(20,6);comment:MACD DEA复权"`
RsiBfq6 float64 `gorm:"type:decimal(20,6);comment:RSI6复权"`
RsiBfq12 float64 `gorm:"type:decimal(20,6);comment:RSI12复权"`
RsiBfq24 float64 `gorm:"type:decimal(20,6);comment:RSI24复权"`
KdjKBfq float64 `gorm:"type:decimal(20,6);comment:KDJ-K复权"`
KdjDBfq float64 `gorm:"type:decimal(20,6);comment:KDJ-D复权"`
KdjBfq float64 `gorm:"type:decimal(20,6);comment:KDJ-J复权"`
BollUpperBfq float64 `gorm:"type:decimal(20,6);comment:BOLL上轨复权"`
BollMidBfq float64 `gorm:"type:decimal(20,6);comment:BOLL中轨复权"`
BollLowerBfq float64 `gorm:"type:decimal(20,6);comment:BOLL下轨复权"`
MaQfq5 float64 `gorm:"type:decimal(20,6);comment:MA5复权"`
MaQfq10 float64 `gorm:"type:decimal(20,6);comment:MA10复权"`
MaQfq20 float64 `gorm:"type:decimal(20,6);comment:MA20复权"`
MaQfq60 float64 `gorm:"type:decimal(20,6);comment:MA60复权"`
EmaQfq5 float64 `gorm:"type:decimal(20,6);comment:EMA5复权"`
EmaQfq10 float64 `gorm:"type:decimal(20,6);comment:EMA10复权"`
EmaQfq20 float64 `gorm:"type:decimal(20,6);comment:EMA20复权"`
MacdQfq float64 `gorm:"type:decimal(20,6);comment:MACD复权"`
MacdDifQfq float64 `gorm:"type:decimal(20,6);comment:MACD DIF复权"`
MacdDeaQfq float64 `gorm:"type:decimal(20,6);comment:MACD DEA复权"`
RsiQfq6 float64 `gorm:"type:decimal(20,6);comment:RSI6复权"`
RsiQfq12 float64 `gorm:"type:decimal(20,6);comment:RSI12复权"`
RsiQfq24 float64 `gorm:"type:decimal(20,6);comment:RSI24复权"`
KdjKQfq float64 `gorm:"type:decimal(20,6);comment:KDJ-K复权"`
KdjDQfq float64 `gorm:"type:decimal(20,6);comment:KDJ-D复权"`
KdjQfq float64 `gorm:"type:decimal(20,6);comment:KDJ-J复权"`
BollUpperQfq float64 `gorm:"type:decimal(20,6);comment:BOLL上轨复权"`
BollMidQfq float64 `gorm:"type:decimal(20,6);comment:BOLL中轨复权"`
BollLowerQfq float64 `gorm:"type:decimal(20,6);comment:BOLL下轨复权"`
}
func (DatasetIndicatorPro) TableName() string {

33
schema/dataset_money.go Normal file
View File

@@ -0,0 +1,33 @@
package schema
// DatasetMoney个股资金流向与 Tushare moneyflow 接口字段一致,每行 ts_code+trade_date
// 金额单位为万元,量单位为手;分类阈值见 Tushare 文档(小单 5 万以下、中单 520 万等)。
type DatasetMoney struct {
ID uint `gorm:"primarykey;autoIncrement"`
TsCode string `gorm:"type:varchar(20);not null;uniqueIndex:un_money_ts_date;index;comment:TS代码" json:"ts_code"`
UpdateYmd int `gorm:"uniqueIndex:un_money_ts_date;index;comment:更新日期" json:"update_ymd"`
BuySmVol int64 `gorm:"comment:小单买入量(手)" json:"buy_sm_vol"`
BuySmAmount float64 `gorm:"type:decimal(20,4);comment:小单买入金额(万元)" json:"buy_sm_amount"`
SellSmVol int64 `gorm:"comment:小单卖出量(手)" json:"sell_sm_vol"`
SellSmAmount float64 `gorm:"type:decimal(20,4);comment:小单卖出金额(万元)" json:"sell_sm_amount"`
BuyMdVol int64 `gorm:"comment:中单买入量(手)" json:"buy_md_vol"`
BuyMdAmount float64 `gorm:"type:decimal(20,4);comment:中单买入金额(万元)" json:"buy_md_amount"`
SellMdVol int64 `gorm:"comment:中单卖出量(手)" json:"sell_md_vol"`
SellMdAmount float64 `gorm:"type:decimal(20,4);comment:中单卖出金额(万元)" json:"sell_md_amount"`
BuyLgVol int64 `gorm:"comment:大单买入量(手)" json:"buy_lg_vol"`
BuyLgAmount float64 `gorm:"type:decimal(20,4);comment:大单买入金额(万元)" json:"buy_lg_amount"`
SellLgVol int64 `gorm:"comment:大单卖出量(手)" json:"sell_lg_vol"`
SellLgAmount float64 `gorm:"type:decimal(20,4);comment:大单卖出金额(万元)" json:"sell_lg_amount"`
BuyElgVol int64 `gorm:"comment:特大单买入量(手)" json:"buy_elg_vol"`
BuyElgAmount float64 `gorm:"type:decimal(20,4);comment:特大单买入金额(万元)" json:"buy_elg_amount"`
SellElgVol int64 `gorm:"comment:特大单卖出量(手)" json:"sell_elg_vol"`
SellElgAmount float64 `gorm:"type:decimal(20,4);comment:特大单卖出金额(万元)" json:"sell_elg_amount"`
NetMfVol int64 `gorm:"comment:净流入量(手)" json:"net_mf_vol"`
NetMfAmount float64 `gorm:"type:decimal(20,4);comment:净流入额(万元)" json:"net_mf_amount"`
MainForceNetWan float64 `gorm:"type:decimal(20,4);comment:主力净流入(万元)" json:"main_force_net_wan"`
}
func (DatasetMoney) TableName() string {
return "dataset_money"
}

View File

@@ -1,31 +0,0 @@
package schema
// DatasetMoneyTotal 资金流汇总(采用 dataset/stock 的索引定义)。
type DatasetMoneyTotal struct {
ID uint `gorm:"primarykey"`
TsCode string `gorm:"type:varchar(20);not null;uniqueIndex:uq_money_total_ts_code"`
Last1DayMfAmount float64
Last3DayMfAmount float64
Last1DayTotalAmount float64
Last3DayTotalAmount float64
IsGreaterPervious bool
}
func (DatasetMoneyTotal) TableName() string {
return "dataset_money_total"
}
// Key 与唯一索引 uq_money_total_ts_code 一致。
func (m *DatasetMoneyTotal) Key() string {
return m.TsCode
}
// NetFlow1Day 最近 1 日主力净流入(万元),与字段语义一致。
func (m *DatasetMoneyTotal) NetFlow1Day() float64 {
return m.Last1DayMfAmount
}
// NetFlow3Day 最近 3 日主力净流入(万元)。
func (m *DatasetMoneyTotal) NetFlow3Day() float64 {
return m.Last3DayMfAmount
}

325
schema/dataset_summary.go Normal file
View File

@@ -0,0 +1,325 @@
package schema
import (
"errors"
"strconv"
"strings"
"time"
"gorm.io/gorm"
)
// DatasetSummary 个股重要指标总览(每股一行,由 Basic / Daily / Indicator / IndicatorPro /
// FinaIndicator / MoneyTotal / PledgeStat / BlocksMember 等表聚合写入)。
type DatasetSummary struct {
ID uint `gorm:"primarykey"`
TsCode string `gorm:"type:varchar(20);not null;uniqueIndex:uq_dataset_summary_ts_code;index;comment:TS代码" json:"ts_code"`
UpdateYmd int `gorm:"uniqueIndex:uq_dataset_summary_ts_code;index;comment:更新日期" json:"update_ymd"`
// --- DatasetBasic身份与分类 ---
Name string `gorm:"type:varchar(50);not null;default:'';comment:股票名称" json:"name"`
Industry string `gorm:"type:varchar(50);not null;default:'';comment:所属行业" json:"industry"`
Area string `gorm:"type:varchar(50);not null;default:'';comment:地域" json:"area"`
Market string `gorm:"type:varchar(50);not null;default:'';comment:市场类型" json:"market"`
ListDate string `gorm:"type:varchar(50);not null;default:'';comment:上市日期" json:"list_date"`
IsHS string `gorm:"type:varchar(2);default:'N';comment:沪深港通 N/H/S" json:"is_hs"`
IsSt string `gorm:"type:varchar(2);default:'N';comment:是否是ST" json:"is_st"`
// --- DatasetDaily / DatasetIndicator / DatasetIndicatorPro最近交易日行情与估值 ---
Close float64 `gorm:"type:decimal(20,4);comment:收盘价" json:"close"`
PctChg float64 `gorm:"type:decimal(20,6);comment:涨跌幅(%)" json:"pct_chg"`
Vol float64 `gorm:"type:decimal(20,2);comment:成交量(手)" json:"vol"`
Amount float64 `gorm:"type:decimal(20,2);comment:成交额(千元)" json:"amount"`
TurnoverRate float64 `gorm:"type:decimal(20,4);comment:换手率(%)" json:"turnover_rate"`
TurnoverRateF float64 `gorm:"type:decimal(20,4);comment:换手率(自由流通股%)" json:"turnover_rate_f"`
VolumeRatio float64 `gorm:"type:decimal(20,4);comment:量比" json:"volume_ratio"`
Pe float64 `gorm:"type:decimal(20,4);comment:市盈率" json:"pe"`
PeTtm float64 `gorm:"type:decimal(20,4);comment:市盈率TTM" json:"pe_ttm"`
Pb float64 `gorm:"type:decimal(20,4);comment:市净率" json:"pb"`
PsTtm float64 `gorm:"type:decimal(20,4);comment:市销率TTM" json:"ps_ttm"`
DvTtm float64 `gorm:"type:decimal(20,4);comment:股息率TTM(%)" json:"dv_ttm"`
TotalShare float64 `gorm:"type:decimal(20,4);comment:总股本(万股)" json:"total_share"`
FloatShare float64 `gorm:"type:decimal(20,4);comment:流通股本(万股)" json:"float_share"`
FreeShare float64 `gorm:"type:decimal(20,4);comment:自由流通股本(万)" json:"free_share"`
TotalMv float64 `gorm:"type:decimal(20,4);comment:总市值(万元)" json:"total_mv"`
CircMv float64 `gorm:"type:decimal(20,4);comment:流通市值(万元)" json:"circ_mv"`
AdjFactor float64 `gorm:"type:decimal(20,6);comment:复权因子(来自因子表)" json:"adj_factor"`
MaQfq5 float64 `gorm:"type:decimal(20,6);comment:MA5前复权" json:"ma_bfq5"`
MaQfq20 float64 `gorm:"type:decimal(20,6);comment:MA20前复权" json:"ma_bfq20"`
MacdDifQfq float64 `gorm:"type:decimal(20,6);comment:MACD DIF前复权" json:"macd_dif_bfq"`
RsiQfq12 float64 `gorm:"type:decimal(20,6);comment:RSI12前复权" json:"rsi_bfq12"`
// --- DatasetFinaIndicator最近一期财务核心 ---
FinaPeriod int `gorm:"index;comment:财务报告期数(与 fina 表 period 一致)" json:"fina_period"`
FinaAnnDate string `gorm:"type:varchar(32);default:'';comment:财报公告日" json:"fina_ann_date"`
FinaEndDate string `gorm:"type:varchar(32);default:'';comment:财报报告期" json:"fina_end_date"`
Eps float64 `gorm:"type:decimal(20,4);comment:基本每股收益" json:"eps"`
Bps float64 `gorm:"type:decimal(20,4);comment:每股净资产" json:"bps"`
Ocfps float64 `gorm:"type:decimal(20,4);comment:每股经营活动现金流净额" json:"ocfps"`
Roe float64 `gorm:"type:decimal(20,4);comment:净资产收益率" json:"roe"`
RoeWaa float64 `gorm:"type:decimal(20,4);comment:加权平均净资产收益率" json:"roe_waa"`
Roa float64 `gorm:"type:decimal(20,4);comment:总资产报酬率" json:"roa"`
DebtToAssets float64 `gorm:"type:decimal(20,4);comment:资产负债率" json:"debt_to_assets"`
GrossprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售毛利率" json:"grossprofit_margin"`
NetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归母净利润同比(%)" json:"netprofit_yoy"`
OrYoy float64 `gorm:"type:decimal(20,4);comment:营业收入同比(%)" json:"or_yoy"`
TrYoy float64 `gorm:"type:decimal(20,4);comment:营业总收入同比(%)" json:"tr_yoy"`
// --- DatasetMoneyTotalmoneyflow近 1/3 交易日主力净流入(万元) 与成交额,由源表按 update_ymd 倒序聚合 ---
Last1DayMfAmount float64 `gorm:"type:decimal(20,4);comment:最近1个交易日主力净流入(万元)" json:"last_1day_mf_amount"`
Last3DayMfAmount float64 `gorm:"type:decimal(20,4);comment:最近3个交易日主力净流入合计(万元)" json:"last_3day_mf_amount"`
Last1DayTotalAmount float64 `gorm:"type:decimal(20,4);comment:最近1个交易日成交额(万元,来自日线千元换算)" json:"last_1day_total_amount"`
Last3DayTotalAmount float64 `gorm:"type:decimal(20,4);comment:最近3个交易日成交额合计(万元)" json:"last_3day_total_amount"`
IsGreaterPervious bool `gorm:"comment:主力净流入是否大于上一交易日" json:"is_greater_pervious"`
// --- DatasetPledgeStat股权质押 ---
PledgeEndDate int `gorm:"comment:质押统计截止日期" json:"pledge_end_date"`
PledgeRatio float64 `gorm:"type:decimal(20,4);comment:质押比例" json:"pledge_ratio"`
PledgeCount float64 `gorm:"type:decimal(20,4);comment:质押次数" json:"pledge_count"`
// --- DatasetBlocksMember板块覆盖度(成分条数) ---
BlockMemberCount int `gorm:"not null;default:0;comment:所属板块/指数成分条数" json:"block_member_count"`
}
func (DatasetSummary) TableName() string {
return "dataset_summary"
}
// UpdateByTsCode 根据 ts_code 从各源表聚合并写入或更新本表对应行。
func (DatasetSummary) UpdateByTsCode(db *gorm.DB, tsCode string) error {
sum, err := buildDatasetSummary(db, tsCode)
if err != nil {
return err
}
return upsertDatasetSummary(db, sum)
}
// UpdateAll 遍历 dataset_basic 中全部 ts_code逐只聚合并写入或更新本表。
func (DatasetSummary) UpdateAll(db *gorm.DB) error {
var codes []string
if err := db.Model(&DatasetBasic{}).Pluck("ts_code", &codes).Error; err != nil {
return err
}
var z DatasetSummary
for _, c := range codes {
if c == "" {
continue
}
if err := z.UpdateByTsCode(db, c); err != nil {
return err
}
}
return nil
}
func upsertDatasetSummary(db *gorm.DB, sum *DatasetSummary) error {
if sum == nil || sum.TsCode == "" {
return errors.New("schema: invalid dataset_summary row")
}
var cur DatasetSummary
err := db.Where("ts_code = ?", sum.TsCode).Take(&cur).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return db.Create(sum).Error
}
if err != nil {
return err
}
sum.ID = cur.ID
return db.Save(sum).Error
}
func buildDatasetSummary(db *gorm.DB, tsCode string) (*DatasetSummary, error) {
if tsCode == "" {
return nil, errors.New("schema: empty ts_code")
}
var basic DatasetBasic
if err := db.Where("ts_code = ?", tsCode).Take(&basic).Error; err != nil {
return nil, err
}
var dip DatasetIndicatorPro
hasDip := db.Where("ts_code = ?", tsCode).Order("trade_date DESC").Take(&dip).Error == nil
var day DatasetDaily
hasDay := db.Where("ts_code = ?", tsCode).Order("trade_date DESC").Take(&day).Error == nil
var ind DatasetIndicator
hasInd := db.Where("ts_code = ?", tsCode).Order("trade_date DESC").Take(&ind).Error == nil
var fina DatasetFinaIndicator
hasFina := db.Where("ts_code = ?", tsCode).Order("period DESC").Take(&fina).Error == nil
last1Mf, last3Mf, last1Tot, last3Tot, mfGreater := aggregateMoneyForSummary(db, tsCode)
var pledge DatasetPledgeStat
hasPledge := db.Where("ts_code = ?", tsCode).Take(&pledge).Error == nil
var blockCnt int64
if err := db.Model(&DatasetBlocksMember{}).Where("ts_code = ?", tsCode).Count(&blockCnt).Error; err != nil {
return nil, err
}
updateYmd := todayYmd()
switch {
case hasDip && dip.TradeDate > 0:
updateYmd = dip.TradeDate
case hasDay && day.TradeDate > 0:
updateYmd = day.TradeDate
case hasInd && ind.TradeDate > 0:
updateYmd = ind.TradeDate
}
s := &DatasetSummary{
TsCode: tsCode,
UpdateYmd: updateYmd,
Name: basic.Name,
Industry: basic.Industry,
Area: basic.Area,
Market: basic.Market,
ListDate: basic.ListDate,
IsHS: basic.IsHS,
IsSt: isStFlag(basic.Name),
BlockMemberCount: int(blockCnt),
Last1DayMfAmount: last1Mf,
Last3DayMfAmount: last3Mf,
Last1DayTotalAmount: last1Tot,
Last3DayTotalAmount: last3Tot,
IsGreaterPervious: mfGreater,
}
if hasPledge {
s.PledgeEndDate = pledge.EndDate
s.PledgeRatio = pledge.PledgeRatio
s.PledgeCount = pledge.PledgeCount
}
if hasFina {
s.FinaPeriod = fina.Period
s.FinaAnnDate = fina.AnnDate
s.FinaEndDate = fina.EndDate
s.Eps = fina.Eps
s.Bps = fina.Bps
s.Ocfps = fina.Ocfps
s.Roe = fina.Roe
s.RoeWaa = fina.RoeWaa
s.Roa = fina.Roa
s.DebtToAssets = fina.DebtToAssets
s.GrossprofitMargin = fina.GrossprofitMargin
s.NetprofitYoy = fina.NetprofitYoy
s.OrYoy = fina.OrYoy
s.TrYoy = fina.TrYoy
}
switch {
case hasDip:
s.Close = dip.Close
s.PctChg = dip.PctChg
s.Vol = dip.Vol
s.Amount = dip.Amount
s.TurnoverRate = dip.TurnoverRate
s.TurnoverRateF = dip.TurnoverRateF
s.VolumeRatio = dip.VolumeRatio
s.Pe = dip.Pe
s.PeTtm = dip.PeTtm
s.Pb = dip.Pb
s.PsTtm = dip.PsTtm
s.DvTtm = dip.DvTtm
s.TotalShare = dip.TotalShare
s.FloatShare = dip.FloatShare
s.FreeShare = dip.FreeShare
s.TotalMv = dip.TotalMv
s.CircMv = dip.CircMv
s.AdjFactor = dip.AdjFactor
s.MaQfq5 = dip.MaQfq5
s.MaQfq20 = dip.MaQfq20
s.MacdDifQfq = dip.MacdDifQfq
s.RsiQfq12 = dip.RsiQfq12
case hasDay:
s.Close = day.Close
s.PctChg = day.PctChg
s.Vol = day.Vol
s.Amount = day.Amount
if hasInd {
s.TurnoverRate = ind.TurnoverRate
s.TurnoverRateF = ind.TurnoverRateF
s.VolumeRatio = ind.VolumeRatio
s.Pe = ind.Pe
s.PeTtm = ind.PeTtm
s.Pb = ind.Pb
s.PsTtm = ind.PsTtm
s.DvTtm = ind.DvTtm
s.TotalShare = ind.TotalShare
s.FloatShare = ind.FloatShare
s.FreeShare = ind.FreeShare
s.TotalMv = ind.TotalMv
s.CircMv = ind.CircMv
}
case hasInd:
s.Close = ind.Close
s.TurnoverRate = ind.TurnoverRate
s.TurnoverRateF = ind.TurnoverRateF
s.VolumeRatio = ind.VolumeRatio
s.Pe = ind.Pe
s.PeTtm = ind.PeTtm
s.Pb = ind.Pb
s.PsTtm = ind.PsTtm
s.DvTtm = ind.DvTtm
s.TotalShare = ind.TotalShare
s.FloatShare = ind.FloatShare
s.FreeShare = ind.FreeShare
s.TotalMv = ind.TotalMv
s.CircMv = ind.CircMv
}
return s, nil
}
// aggregateMoneyForSummary 由 dataset_money_totalmoneyflow与 dataset_daily 聚合近 1/3 日指标。
// 主力净流入取源表 MainForceNetWan万元按 update_ymd 从新到旧取至多 3 行。成交额取日线 amount千元换算为万元。
func aggregateMoneyForSummary(db *gorm.DB, tsCode string) (last1Mf, last3Mf, last1Tot, last3Tot float64, greater bool) {
var mfRows []DatasetMoney
_ = db.Where("ts_code = ?", tsCode).Order("update_ymd DESC, id DESC").Limit(3).Find(&mfRows).Error
for i, r := range mfRows {
v := r.MainForceNetWan
if i == 0 {
last1Mf = v
}
if i < 3 {
last3Mf += v
}
}
if len(mfRows) >= 2 {
greater = mfRows[0].MainForceNetWan > mfRows[1].MainForceNetWan
}
var dayRows []DatasetDaily
_ = db.Where("ts_code = ?", tsCode).Order("trade_date DESC").Limit(3).Find(&dayRows).Error
for i, d := range dayRows {
wan := d.Amount / 10 // 千元 -> 万元
if i == 0 {
last1Tot = wan
}
if i < 3 {
last3Tot += wan
}
}
return
}
func todayYmd() int {
n, err := strconv.Atoi(time.Now().Format("20060102"))
if err != nil {
return 0
}
return n
}
func isStFlag(name string) string {
s := strings.TrimSpace(name)
if len(s) >= 3 && strings.HasPrefix(s, "*ST") {
return "Y"
}
if len(s) >= 2 && strings.HasPrefix(s, "ST") {
return "Y"
}
return "N"
}

View File

@@ -2,18 +2,19 @@ package schema
import "git.apinb.com/bsm-sdk/core/database"
// RegisterAutoMigrate 将本包内与 stock/gostock 共用的表注册到 bsm-sdk 的迁移列表(可选;也可在各应用 init 中自行 AppendMigrate
// RegisterAutoMigrate 共用的表注册到 bsm-sdk 的迁移列表(可选;也可在各应用 init 中自行 AppendMigrate
func RegisterAutoMigrate() {
for _, t := range []any{
&DatasetStockBasic{},
&DatasetStockDaily{},
&DatasetBasic{},
&DatasetDaily{},
&DatasetBlocksIndex{},
&DatasetBlocksMember{},
&DatasetMoneyTotal{},
&DatasetMoney{},
&DatasetPledgeStat{},
&DatasetStockIndicator{},
&DatasetIndicator{},
&DatasetIndicatorPro{},
&DatasetStockFinaIndicator{},
&DatasetFinaIndicator{},
&DatasetSummary{},
} {
database.AppendMigrate(t)
}

78
tushare/cal.go Normal file
View File

@@ -0,0 +1,78 @@
package tushare
import (
"time"
"git.apinb.com/quant/qsdk/conv"
)
/*
TradeCal 获取交易日历
exchange: 交易所代码SSE 上交所SZSE 深交所BSE 北交所,空字符串代表所有
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
is_open: 是否开盘,'0'休市,'1'交易,空字符串代表所有
*/
func (cli *TushareClient) TradeCal(exchange, start_date, end_date, is_open string) (*TushareRespData, error) {
params := map[string]any{}
if exchange != "" {
params["exchange"] = exchange
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if is_open != "" {
params["is_open"] = is_open
}
req := TushareReq{
APIName: "trade_cal",
Params: params,
}
fields := []map[string]string{
{"exchange": "交易所代码"},
{"cal_date": "日历日期"},
{"is_open": "是否开盘"},
{"pretrade_date": "上一交易日"},
}
return cli.Do(req, fields)
}
func (cli *TushareClient) LastTradeDay() string {
result, err := cli.TradeCal("SSE", time.Now().AddDate(0, 0, -15).Format("20060102"), time.Now().Format("20060102"), "1")
if err != nil {
return ""
}
cal := result.Map()
if len(cal) == 0 {
return ""
}
return conv.AnyToString(cal[0]["cal_date"])
}
func (cli *TushareClient) LimitTradeDay(limit int) []string {
if limit <= 0 {
return []string{}
}
if limit > 100 {
limit = 100
}
result, err := cli.TradeCal("SSE", time.Now().AddDate(0, 0, -100).Format("20060102"), time.Now().Format("20060102"), "1")
if err != nil {
return []string{}
}
cal := result.Map()
days := []string{}
for i := 0; i < limit; i++ {
item := cal[i]
days = append(days, conv.AnyToString(item["cal_date"]))
}
return days
}

View File

@@ -117,17 +117,26 @@ func (cli *TushareClient) Moneyflow(ts_code, trade_date, start_date, end_date st
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"ts_code": "TS代码"},
{"trade_date": "交易日期"},
{"buy_sm_amount": "小单买入金额 (千元)"},
{"sell_sm_amount": "小单卖出金额 (元)"},
{"buy_md_amount": "中单买入金额 (千元)"},
{"sell_md_amount": "单卖出金额 (元)"},
{"buy_lg_amount": "单买入金额 (千元)"},
{"sell_lg_amount": "大单卖出金额 (元)"},
{"buy_elg_amount": "特大单买入金额 (千元)"},
{"sell_elg_amount": "特大单卖出金额 (元)"},
{"net_mf_amount": "净流入金额 (千元)"},
{"buy_sm_vol": "小单买入量 (手)"},
{"buy_sm_amount": "小单买入金额 (元)"},
{"sell_sm_vol": "小单卖出量 (手)"},
{"sell_sm_amount": "单卖出金额 (元)"},
{"buy_md_vol": "单买入量 (手)"},
{"buy_md_amount": "中单买入金额 (元)"},
{"sell_md_vol": "中单卖出量 (手)"},
{"sell_md_amount": "单卖出金额 (元)"},
{"buy_lg_vol": "大单买入量 (手)"},
{"buy_lg_amount": "大单买入金额 (万元)"},
{"sell_lg_vol": "大单卖出量 (手)"},
{"sell_lg_amount": "大单卖出金额 (万元)"},
{"buy_elg_vol": "特大单买入量 (手)"},
{"buy_elg_amount": "特大单买入金额 (万元)"},
{"sell_elg_vol": "特大单卖出量 (手)"},
{"sell_elg_amount": "特大单卖出金额 (万元)"},
{"net_mf_vol": "净流入量 (手)"},
{"net_mf_amount": "净流入额 (万元)"},
}
return cli.Do(req, fields)

View File

@@ -55,25 +55,25 @@ func (cli *TushareClient) StkFactorPro(ts_code, trade_date, start_date, end_date
{"total_mv": "总市值(万元)"},
{"circ_mv": "流通市值(万元)"},
{"adj_factor": "复权因子"},
{"ma_bfq_5": "MA5复权"},
{"ma_bfq_10": "MA10复权"},
{"ma_bfq_20": "MA20复权"},
{"ma_bfq_60": "MA60复权"},
{"ema_bfq_5": "EMA5复权"},
{"ema_bfq_10": "EMA10复权"},
{"ema_bfq_20": "EMA20复权"},
{"macd_bfq": "MACD复权"},
{"macd_dif_bfq": "MACD DIF复权"},
{"macd_dea_bfq": "MACD DEA复权"},
{"rsi_bfq_6": "RSI6复权"},
{"rsi_bfq_12": "RSI12复权"},
{"rsi_bfq_24": "RSI24复权"},
{"kdj_k_bfq": "KDJ-K复权"},
{"kdj_d_bfq": "KDJ-D复权"},
{"kdj_bfq": "KDJ-J复权"},
{"boll_upper_bfq": "BOLL上轨复权"},
{"boll_mid_bfq": "BOLL中轨复权"},
{"boll_lower_bfq": "BOLL下轨复权"},
{"ma_qfq_5": "MA5复权"},
{"ma_qfq_10": "MA10复权"},
{"ma_qfq_20": "MA20复权"},
{"ma_qfq_60": "MA60复权"},
{"ema_qfq_5": "EMA5复权"},
{"ema_qfq_10": "EMA10复权"},
{"ema_qfq_20": "EMA20复权"},
{"macd_qfq": "MACD复权"},
{"macd_dif_qfq": "MACD DIF复权"},
{"macd_dea_qfq": "MACD DEA复权"},
{"rsi_qfq_6": "RSI6复权"},
{"rsi_qfq_12": "RSI12复权"},
{"rsi_qfq_24": "RSI24复权"},
{"kdj_k_qfq": "KDJ-K复权"},
{"kdj_d_qfq": "KDJ-D复权"},
{"kdj_qfq": "KDJ-J复权"},
{"boll_upper_qfq": "BOLL上轨复权"},
{"boll_mid_qfq": "BOLL中轨复权"},
{"boll_lower_qfq": "BOLL下轨复权"},
}
return cli.Do(req, fields)

View File

@@ -1,61 +1,5 @@
package tushare
import (
"time"
"git.apinb.com/quant/qsdk/conv"
)
/*
TradeCal 获取交易日历
exchange: 交易所代码SSE 上交所SZSE 深交所BSE 北交所,空字符串代表所有
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
is_open: 是否开盘,'0'休市,'1'交易,空字符串代表所有
*/
func (cli *TushareClient) TradeCal(exchange, start_date, end_date, is_open string) (*TushareRespData, error) {
params := map[string]any{}
if exchange != "" {
params["exchange"] = exchange
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if is_open != "" {
params["is_open"] = is_open
}
req := TushareReq{
APIName: "trade_cal",
Params: params,
}
fields := []map[string]string{
{"exchange": "交易所代码"},
{"cal_date": "日历日期"},
{"is_open": "是否开盘"},
{"pretrade_date": "上一交易日"},
}
return cli.Do(req, fields)
}
func (cli *TushareClient) ReturnLastTradeDay() string {
result, err := cli.TradeCal("SSE", time.Now().AddDate(0, 0, -15).Format("20060102"), time.Now().Format("20060102"), "1")
if err != nil {
return ""
}
cal := result.Map()
if len(cal) == 0 {
return ""
}
return conv.AnyToString(cal[0]["cal_date"])
}
/*
StockBasic 获取股票列表