This commit is contained in:
2026-05-01 11:03:19 +08:00
parent b6a199887e
commit aa8a1190f0
24 changed files with 2552 additions and 0 deletions

76
conv/conv.go Normal file
View File

@@ -0,0 +1,76 @@
package conv
import (
"reflect"
"strconv"
)
// AnyToString 任意类型转字符串
// in: 输入值
// 返回: 转换后的字符串
func AnyToString(in any) (s string) {
if in == nil {
return ""
}
return in.(string)
}
// AnyToInt 将动态类型转为 int两仓库 internal 中逻辑一致,此处合并分支)。
func AnyToInt(v any) int {
switch val := v.(type) {
case int:
return val
case int8:
return int(val)
case int16:
return int(val)
case int32:
return int(val)
case int64:
return int(val)
case uint, uint8, uint16, uint32, uint64:
return int(reflect.ValueOf(val).Uint())
case float32:
return int(val)
case float64:
return int(val)
case string:
i, err := strconv.Atoi(val)
if err != nil {
return 0
}
return i
default:
return 0
}
}
// AnyToFloat64 将动态类型转为 float64合并 stock 对 int/uint 等分支与 gostock 的 string 分支)。
func AnyToFloat64(v any) float64 {
switch val := v.(type) {
case float64:
return val
case string:
return String2Float64(val)
case float32:
return float64(val)
case int:
return float64(val)
case uint:
return float64(val)
default:
return 0
}
}
// String2Float64 字符串转Float64
// floatStr: 小数点数字的字符串
// 返回: 转换后的64位浮点数转换失败返回0
func String2Float64(floatStr string) (floatNum float64) {
floatNum, err := strconv.ParseFloat(floatStr, 64)
if err != nil {
return 0
}
return
}

13
day/ymd.go Normal file
View File

@@ -0,0 +1,13 @@
package day
import (
"time"
"git.apinb.com/bsm-sdk/core/utils"
)
// TodayYmd 返回本地日期的 YYYYMMDD 整数gostock internal/models/query.GetYmd
func TodayYmd() int {
ymd := time.Now().Format("20060102")
return utils.String2Int(ymd)
}

31
go.mod Normal file
View File

@@ -0,0 +1,31 @@
module git.apinb.com/quant/qsdk
go 1.26.1
require (
git.apinb.com/bsm-sdk/core v0.1.9
github.com/jedib0t/go-pretty/v6 v6.7.9
gorm.io/gorm v1.31.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/oklog/ulid/v2 v2.1.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
gorm.io/driver/mysql v1.6.0 // indirect
gorm.io/driver/postgres v1.6.0 // indirect
)

60
go.sum Normal file
View File

@@ -0,0 +1,60 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.apinb.com/bsm-sdk/core v0.1.9 h1:Pp0zpSeX7OnFb3JQdXdJHR74EvU02HnMauk4wI0/gVg=
git.apinb.com/bsm-sdk/core v0.1.9/go.mod h1:R5Rm/Ep4D3mJ97dL6sLRi3jtfQxi2ftekp2E3frts/8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jedib0t/go-pretty/v6 v6.7.9 h1:frarzQWmkZd97syT81+TH8INKPpzoxQnk+Mk5EIHSrM=
github.com/jedib0t/go-pretty/v6 v6.7.9/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=

83
markdown/builder.go Normal file
View File

@@ -0,0 +1,83 @@
package markdown
import (
"bytes"
"io"
"os"
"strings"
)
// Builder 用于拼接 Markdown 文本(原 stock internal/logic/a、gostock internal/logic/libs 中重复实现)。
type Builder struct {
buf bytes.Buffer
}
// MarkdownBuilder 兼容旧项目中的类型名。
type MarkdownBuilder = Builder
func NewBuilder() *Builder {
return &Builder{}
}
// NewMarkdownBuilder 兼容旧项目中的构造函数名。
func NewMarkdownBuilder() *Builder {
return NewBuilder()
}
func (m *Builder) Title(title string) {
m.buf.WriteString("# " + title + "\n\n")
}
func (m *Builder) Catalog(text string) {
m.buf.WriteString("## " + text + "\n\n")
}
func (m *Builder) Text(text string) {
m.buf.WriteString(text + "\n\n")
}
func (m *Builder) KvText(key, val string) {
m.buf.WriteString(key + " " + val + " \n")
}
func (m *Builder) BR() {
m.buf.WriteString("\n")
}
func (m *Builder) Code(lang, code string) {
m.buf.WriteString("```" + lang + "\n")
m.buf.WriteString(code + "\n")
m.buf.WriteString("```\n\n")
}
// Table 生成 Markdown 表格。
func (m *Builder) Table(catalog string, headers []string, rows [][]string) {
if catalog != "" {
m.buf.WriteString("## " + catalog + "\n")
}
m.buf.WriteString("| " + strings.Join(headers, " | ") + " |\n")
m.buf.WriteString("| ")
for _, header := range headers {
m.buf.WriteString(strings.Repeat("-", len(header)) + "|")
}
m.buf.WriteString("\n")
for _, row := range rows {
m.buf.WriteString("| " + strings.Join(row, " | ") + " |\n")
}
}
func (m *Builder) Build() string {
return m.buf.String()
}
func (m *Builder) SaveToFile(filePath string) error {
rf, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
defer rf.Close()
_, err = io.Copy(rf, &m.buf)
return err
}

28
schema/blocks_index.go Normal file
View File

@@ -0,0 +1,28 @@
package schema
import "gorm.io/gorm"
// BlocksIndex 板块索引(采用 dataset/stock 的 code 唯一约束)。
type BlocksIndex struct {
gorm.Model
Code string `gorm:"type:varchar(50);not null;default:'';comment:板块代码;uniqueIndex:uq_blocks_index_code" json:"code"`
Name string `gorm:"type:varchar(50);not null;default:'';comment:板块名称" json:"name"`
IsRecommend bool `gorm:"type:bool;not null;default:false;comment:是否推荐" json:"is_recommend"`
}
func (BlocksIndex) TableName() string {
return "blocks_index"
}
// Key 板块业务主键code。
func (b *BlocksIndex) Key() string {
return b.Code
}
// DisplayName 展示名称:非空 name否则回退 code。
func (b *BlocksIndex) DisplayName() string {
if b.Name != "" {
return b.Name
}
return b.Code
}

28
schema/blocks_member.go Normal file
View File

@@ -0,0 +1,28 @@
package schema
import "gorm.io/gorm"
// BlocksMember 板块成分股。
type BlocksMember struct {
gorm.Model
TiCode string `gorm:"type:varchar(50);not null;default:'';comment:板块代码;index" json:"ti_code"`
StockCode string `gorm:"type:varchar(50);not null;default:'';comment:股票代码;index" json:"stock_code"`
Weight float64 `gorm:"type:float;not null;default:0;comment:权重" json:"weight"`
}
func (BlocksMember) TableName() string {
return "blocks_member"
}
// Key 成分在板块内的键ti_code + stock_code。
func (b *BlocksMember) Key() string {
if b.TiCode == "" && b.StockCode == "" {
return ""
}
return b.TiCode + "/" + b.StockCode
}
// HasWeight 是否带权重量化成分。
func (b *BlocksMember) HasWeight() bool {
return b.Weight > 0
}

31
schema/money_total.go Normal file
View File

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

30
schema/pledge_stat.go Normal file
View File

@@ -0,0 +1,30 @@
package schema
// PledgeStat 股权质押统计(表 pledge_statdataset/stock 中原为 PledgeStatModel业务层 Save 请留在各应用内)。
type PledgeStat struct {
ID uint `gorm:"primarykey" json:"id"`
TsCode string `gorm:"type:varchar(50);not null;default:'';comment:股票代码;uniqueIndex:uq_pledge_stat_ts_code" json:"ts_code"`
EndDate int `gorm:"type:int;not null;default:0;comment:截止日期" json:"end_date"`
PledgeCount float64 `json:"pledge_count"`
UnrestPledge float64 `json:"unrest_pledge"`
RestPledge float64 `json:"rest_pledge"`
TotalShare float64 `json:"total_share"`
PledgeRatio float64 `json:"pledge_ratio"`
}
func (PledgeStat) TableName() string {
return "pledge_stat"
}
// Key 与唯一约束 uq_pledge_stat_ts_code 一致ts_code。
func (p *PledgeStat) Key() string {
return p.TsCode
}
// HasPledgeFacts 是否具备任一质押统计字段(比例、次数或股本)。
func (p *PledgeStat) HasPledgeFacts() bool {
return p.PledgeRatio > 0 || p.PledgeCount > 0 || p.TotalShare > 0
}
// PledgeStatModel 兼容 dataset/stock 中的类型名。
type PledgeStatModel = PledgeStat

19
schema/register.go Normal file
View File

@@ -0,0 +1,19 @@
package schema
import "git.apinb.com/bsm-sdk/core/database"
// RegisterAutoMigrate 将本包内与 stock/gostock 共用的表注册到 bsm-sdk 的迁移列表(可选;也可在各应用 init 中自行 AppendMigrate
func RegisterAutoMigrate() {
for _, t := range []any{
&StockBasic{},
&StockDaily{},
&BlocksIndex{},
&BlocksMember{},
&MoneyTotal{},
&PledgeStat{},
&StockIndicator{},
&StockFinaIndicator{},
} {
database.AppendMigrate(t)
}
}

107
schema/scopes.go Normal file
View File

@@ -0,0 +1,107 @@
package schema
import "gorm.io/gorm"
// ScopeTsCode 按 ts_code 精确过滤;空字符串则不加条件。
func ScopeTsCode(tsCode string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if tsCode == "" {
return db
}
return db.Where("ts_code = ?", tsCode)
}
}
// ScopeTsCodes 按 ts_code IN (...)nil 或空切片不加条件。
func ScopeTsCodes(tsCodes []string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if len(tsCodes) == 0 {
return db
}
return db.Where("ts_code IN ?", tsCodes)
}
}
// ScopeTradeDateEQ 按 trade_date 等于0 表示不加条件。
func ScopeTradeDateEQ(tradeDate int) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if tradeDate == 0 {
return db
}
return db.Where("trade_date = ?", tradeDate)
}
}
// ScopeTradeDateBetween trade_date 区间 [start,end];仅传一侧时做单边约束;均为 0 不加条件。
func ScopeTradeDateBetween(start, end int) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
switch {
case start > 0 && end > 0:
return db.Where("trade_date BETWEEN ? AND ?", start, end)
case start > 0:
return db.Where("trade_date >= ?", start)
case end > 0:
return db.Where("trade_date <= ?", end)
default:
return db
}
}
}
// ScopeStockDailyTsDate 日线ts_code + 交易日(任一为空则该项不限制)。
func ScopeStockDailyTsDate(tsCode string, tradeDate int) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Scopes(ScopeTsCode(tsCode), ScopeTradeDateEQ(tradeDate))
}
}
// ScopeStockIndicatorTsDate 指标表ts_code + trade_date。
func ScopeStockIndicatorTsDate(tsCode string, tradeDate int) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Scopes(ScopeTsCode(tsCode), ScopeTradeDateEQ(tradeDate))
}
}
// ScopeFinaTsPeriod 财务指标ts_code + period与 uniqueIndex un_fi_code_date 一致period 为 0 时不限制 period。
func ScopeFinaTsPeriod(tsCode string, period int) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
db = db.Scopes(ScopeTsCode(tsCode))
if period != 0 {
db = db.Where("period = ?", period)
}
return db
}
}
// ScopeBlocksIndexCode 板块 code。
func ScopeBlocksIndexCode(code string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if code == "" {
return db
}
return db.Where("code = ?", code)
}
}
// ScopeBlocksMemberPair 板块 ti_code + 成分 stock_code。
func ScopeBlocksMemberPair(tiCode, stockCode string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if tiCode != "" {
db = db.Where("ti_code = ?", tiCode)
}
if stockCode != "" {
db = db.Where("stock_code = ?", stockCode)
}
return db
}
}
// ScopeMoneyTotalCode 资金流 code。
func ScopeMoneyTotalCode(code string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if code == "" {
return db
}
return db.Where("code = ?", code)
}
}

44
schema/stock_basic.go Normal file
View File

@@ -0,0 +1,44 @@
package schema
import "gorm.io/gorm"
// StockBasic 股票基本信息表(合并 dataset/stock 与 gostock 字段gostock 独有 Level、Desc
type StockBasic struct {
gorm.Model
TsCode string `gorm:"type:varchar(50);not null;index;comment:TS代码"`
Symbol string `gorm:"type:varchar(50);not null;comment:股票代码"`
Name string `gorm:"type:varchar(50);not null;comment:股票名称"`
Area string `gorm:"type:varchar(50);not null;default:'';comment:地域"`
Industry string `gorm:"type:varchar(50);not null;default:'';comment:所属行业"`
FullName string `gorm:"type:varchar(500);comment:股票全称"`
EnName string `gorm:"type:varchar(200);comment:英文全称"`
CnSpell string `gorm:"type:varchar(50);not null;default:'';comment:拼音缩写"`
Market string `gorm:"type:varchar(50);not null;comment:市场类型(主板/创业板/科创板/CDR"`
Exchange string `gorm:"type:varchar(50);comment:交易所代码"`
ListDate string `gorm:"type:varchar(50);not null;comment:上市日期"`
IsHS string `gorm:"type:varchar(2);default:'N';comment:是否沪深港通标的N否 H沪股通 S深股通"`
ActName string `gorm:"type:varchar(500);not null;default:'';comment:实控人名称"`
ActEntType string `gorm:"type:varchar(50);not null;default:'';comment:实控人企业性质"`
}
func (StockBasic) TableName() string {
return "stock_basic"
}
// Key 业务主键TS 代码。
func (s *StockBasic) Key() string {
return s.TsCode
}
// DisplaySymbol 展示用代码:有 symbol 用 symbol否则用 ts_code。
func (s *StockBasic) DisplaySymbol() string {
if s.Symbol != "" {
return s.Symbol
}
return s.TsCode
}
// IsNorthbound 是否沪深港通标的H 沪股通 / S 深股通)。
func (s *StockBasic) IsNorthbound() bool {
return s.IsHS == "H" || s.IsHS == "S"
}

58
schema/stock_daily.go Normal file
View File

@@ -0,0 +1,58 @@
package schema
import "strconv"
// StockDaily 股票日线数据(两仓库结构一致)。
type StockDaily 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"`
Open float64 `gorm:"type:decimal(10,4);comment:开盘价" json:"open"`
High float64 `gorm:"type:decimal(10,4);comment:最高价" json:"high"`
Low float64 `gorm:"type:decimal(10,4);comment:最低价" json:"low"`
Close float64 `gorm:"type:decimal(10,4);comment:收盘价" json:"close"`
PreClose float64 `gorm:"type:decimal(10,4);comment:昨收价(除权价)" json:"pre_close"`
Change float64 `gorm:"type:decimal(10,4);comment:涨跌额" json:"change"`
PctChg float64 `gorm:"type:decimal(10,6);comment:涨跌幅(%)" json:"pct_chg"`
DayChg float64 `gorm:"type:decimal(6,2);comment:日均振幅(%)" json:"day_chg"`
Vol float64 `gorm:"type:decimal(15,2);comment:成交量(手)" json:"vol"`
Amount float64 `gorm:"type:decimal(20,2);comment:成交额(千元)" json:"amount"`
}
func (StockDaily) TableName() string {
return "stock_daily"
}
// Key 业务主键ts_code + 交易日。
func (d *StockDaily) Key() string {
if d.TsCode == "" && d.TradeDate == 0 {
return ""
}
return d.TsCode + "#" + strconv.Itoa(d.TradeDate)
}
// IsRising 是否收涨(昨收有效且收盘高于昨收)。
func (d *StockDaily) IsRising() bool {
return d.PreClose > 0 && d.Close > d.PreClose
}
// IsFalling 是否收跌。
func (d *StockDaily) IsFalling() bool {
return d.PreClose > 0 && d.Close < d.PreClose
}
// PctChangeFromPre 由昨收计算的涨跌幅(%);昨收无效时返回 0。
func (d *StockDaily) PctChangeFromPre() float64 {
if d.PreClose <= 0 {
return 0
}
return (d.Close - d.PreClose) / d.PreClose * 100
}
// AmplitudePct 振幅(相对昨收,%(最高-最低)/昨收*100。
func (d *StockDaily) AmplitudePct() float64 {
if d.PreClose <= 0 {
return 0
}
return (d.High - d.Low) / d.PreClose * 100
}

View File

@@ -0,0 +1,226 @@
package schema
import (
"strconv"
"gorm.io/gorm"
)
// StockFinaIndicator 财务指标模型
type StockFinaIndicator 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:报告期数"`
AnnDate string `gorm:"index:idx_ann_date;comment:公告日期"`
EndDate string `gorm:"index:idx_end_date;comment:报告期"`
// 每股指标
Eps float64 `gorm:"type:decimal(20,4);comment:基本每股收益"`
DtEps float64 `gorm:"type:decimal(20,4);comment:稀释每股收益"`
TotalRevenuePs float64 `gorm:"type:decimal(20,4);comment:每股营业总收入"`
RevenuePs float64 `gorm:"type:decimal(20,4);comment:每股营业收入"`
CapitalResePs float64 `gorm:"type:decimal(20,4);comment:每股资本公积"`
SurplusResePs float64 `gorm:"type:decimal(20,4);comment:每股盈余公积"`
UndistProfitPs float64 `gorm:"type:decimal(20,4);comment:每股未分配利润"`
Diluted2Eps float64 `gorm:"type:decimal(20,4);comment:期末摊薄每股收益"`
Bps float64 `gorm:"type:decimal(20,4);comment:每股净资产"`
Ocfps float64 `gorm:"type:decimal(20,4);comment:每股经营活动产生的现金流量净额"`
Retainedps float64 `gorm:"type:decimal(20,4);comment:每股留存收益"`
Cfps float64 `gorm:"type:decimal(20,4);comment:每股现金流量净额"`
EbitPs float64 `gorm:"type:decimal(20,4);comment:每股息税前利润"`
FcffPs float64 `gorm:"type:decimal(20,4);comment:每股企业自由现金流量"`
FcfePs float64 `gorm:"type:decimal(20,4);comment:每股股东自由现金流量"`
// 利润表相关
ExtraItem float64 `gorm:"type:decimal(20,4);comment:非经常性损益"`
ProfitDedt float64 `gorm:"type:decimal(20,4);comment:扣除非经常性损益后的净利润"`
GrossMargin float64 `gorm:"type:decimal(20,4);comment:毛利"`
OpIncome float64 `gorm:"type:decimal(20,4);comment:经营活动净收益"`
ValuechangeIncome float64 `gorm:"type:decimal(20,4);comment:价值变动净收益"`
InterstIncome float64 `gorm:"type:decimal(20,4);comment:利息费用"`
Daa float64 `gorm:"type:decimal(20,4);comment:折旧与摊销"`
Ebit float64 `gorm:"type:decimal(20,4);comment:息税前利润"`
Ebitda float64 `gorm:"type:decimal(20,4);comment:息税折旧摊销前利润"`
Fcff float64 `gorm:"type:decimal(20,4);comment:企业自由现金流量"`
Fcfe float64 `gorm:"type:decimal(20,4);comment:股权自由现金流量"`
RdExp float64 `gorm:"type:decimal(20,4);comment:研发费用"`
FixedAssets float64 `gorm:"type:decimal(20,4);comment:固定资产合计"`
ProfitPrefinExp float64 `gorm:"type:decimal(20,4);comment:扣除财务费用前营业利润"`
NonOpProfit float64 `gorm:"type:decimal(20,4);comment:非营业利润"`
// 资产负债表相关
CurrentExint float64 `gorm:"type:decimal(20,4);comment:无息流动负债"`
NoncurrentExint float64 `gorm:"type:decimal(20,4);comment:无息非流动负债"`
Interestdebt float64 `gorm:"type:decimal(20,4);comment:带息债务"`
Netdebt float64 `gorm:"type:decimal(20,4);comment:净债务"`
TangibleAsset float64 `gorm:"type:decimal(20,4);comment:有形资产"`
WorkingCapital float64 `gorm:"type:decimal(20,4);comment:营运资金"`
NetworkingCapital float64 `gorm:"type:decimal(20,4);comment:营运流动资本"`
InvestCapital float64 `gorm:"type:decimal(20,4);comment:全部投入资本"`
RetainedEarnings float64 `gorm:"type:decimal(20,4);comment:留存收益"`
// 偿债能力指标
CurrentRatio float64 `gorm:"type:decimal(20,4);comment:流动比率"`
QuickRatio float64 `gorm:"type:decimal(20,4);comment:速动比率"`
CashRatio float64 `gorm:"type:decimal(20,4);comment:保守速动比率"`
DebtToAssets float64 `gorm:"type:decimal(20,4);comment:资产负债率"`
AssetsToEqt float64 `gorm:"type:decimal(20,4);comment:权益乘数"`
DpAssetsToEqt float64 `gorm:"type:decimal(20,4);comment:权益乘数(杜邦分析)"`
DebtToEqt float64 `gorm:"type:decimal(20,4);comment:产权比率"`
EqtToDebt float64 `gorm:"type:decimal(20,4);comment:归属于母公司的股东权益/负债合计"`
OcfToShortdebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/流动负债"`
EbitToInterest float64 `gorm:"type:decimal(20,4);comment:已获利息倍数"`
// 运营能力指标
InvturnDays float64 `gorm:"type:decimal(20,4);comment:存货周转天数"`
ArturnDays float64 `gorm:"type:decimal(20,4);comment:应收账款周转天数"`
InvTurn float64 `gorm:"type:decimal(20,4);comment:存货周转率"`
ArTurn float64 `gorm:"type:decimal(20,4);comment:应收账款周转率"`
CaTurn float64 `gorm:"type:decimal(20,4);comment:流动资产周转率"`
FaTurn float64 `gorm:"type:decimal(20,4);comment:固定资产周转率"`
AssetsTurn float64 `gorm:"type:decimal(20,4);comment:总资产周转率"`
TurnDays float64 `gorm:"type:decimal(20,4);comment:营业周期"`
TotalFaTrun float64 `gorm:"type:decimal(20,4);comment:固定资产合计周转率"`
// 盈利能力指标
NetprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售净利率"`
GrossprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售毛利率"`
CogsOfSales float64 `gorm:"type:decimal(20,4);comment:销售成本率"`
ExpenseOfSales float64 `gorm:"type:decimal(20,4);comment:销售期间费用率"`
Roe float64 `gorm:"type:decimal(20,4);comment:净资产收益率"`
RoeWaa float64 `gorm:"type:decimal(20,4);comment:加权平均净资产收益率"`
RoeDt float64 `gorm:"type:decimal(20,4);comment:净资产收益率(扣除非经常损益)"`
Roa float64 `gorm:"type:decimal(20,4);comment:总资产报酬率"`
Npta float64 `gorm:"type:decimal(20,4);comment:总资产净利润"`
Roic float64 `gorm:"type:decimal(20,4);comment:投入资本回报率"`
RoaDp float64 `gorm:"type:decimal(20,4);comment:总资产净利率(杜邦分析)"`
// 结构指标
CaToAssets float64 `gorm:"type:decimal(20,4);comment:流动资产/总资产"`
NcaToAssets float64 `gorm:"type:decimal(20,4);comment:非流动资产/总资产"`
TbassetsToTotalassets float64 `gorm:"type:decimal(20,4);comment:有形资产/总资产"`
IntToTalcap float64 `gorm:"type:decimal(20,4);comment:带息债务/全部投入资本"`
EqtToTalcapital float64 `gorm:"type:decimal(20,4);comment:归属于母公司的股东权益/全部投入资本"`
CurrentdebtToDebt float64 `gorm:"type:decimal(20,4);comment:流动负债/负债合计"`
LongdebToDebt float64 `gorm:"type:decimal(20,4);comment:非流动负债/负债合计"`
TangibleassetToDebt float64 `gorm:"type:decimal(20,4);comment:有形资产/负债合计"`
// 单季度指标
QOpincome float64 `gorm:"type:decimal(20,4);comment:经营活动单季度净收益"`
QInvestincome float64 `gorm:"type:decimal(20,4);comment:价值变动单季度净收益"`
QDtprofit float64 `gorm:"type:decimal(20,4);comment:扣除非经常损益后的单季度净利润"`
QEps float64 `gorm:"type:decimal(20,4);comment:每股收益(单季度)"`
QNetprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售净利率(单季度)"`
QGscaleprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售毛利率(单季度)"`
QExpToSales float64 `gorm:"type:decimal(20,4);comment:销售期间费用率(单季度)"`
QRoe float64 `gorm:"type:decimal(20,4);comment:净资产收益率(单季度)"`
QDtRoe float64 `gorm:"type:decimal(20,4);comment:净资产单季度收益率(扣除非经常损益)"`
QNpta float64 `gorm:"type:decimal(20,4);comment:总资产净利润(单季度)"`
// 同比增长率
BasicEpsYoy float64 `gorm:"type:decimal(20,4);comment:基本每股收益同比增长率(%)"`
DtEpsYoy float64 `gorm:"type:decimal(20,4);comment:稀释每股收益同比增长率(%)"`
CfpsYoy float64 `gorm:"type:decimal(20,4);comment:每股经营活动产生的现金流量净额同比增长率(%)"`
OpYoy float64 `gorm:"type:decimal(20,4);comment:营业利润同比增长率(%)"`
EbtYoy float64 `gorm:"type:decimal(20,4);comment:利润总额同比增长率(%)"`
NetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润同比增长率(%)"`
DtNetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润-扣除非经常损益同比增长率(%)"`
OcfYoy float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额同比增长率(%)"`
RoeYoy float64 `gorm:"type:decimal(20,4);comment:净资产收益率(摊薄)同比增长率(%)"`
BpsYoy float64 `gorm:"type:decimal(20,4);comment:每股净资产相对年初增长率(%)"`
AssetsYoy float64 `gorm:"type:decimal(20,4);comment:资产总计相对年初增长率(%)"`
EqtYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司的股东权益相对年初增长率(%)"`
TrYoy float64 `gorm:"type:decimal(20,4);comment:营业总收入同比增长率(%)"`
OrYoy float64 `gorm:"type:decimal(20,4);comment:营业收入同比增长率(%)"`
EquityYoy float64 `gorm:"type:decimal(20,4);comment:净资产同比增长率"`
// 其他比率指标
ProfitToGr float64 `gorm:"type:decimal(20,4);comment:净利润/营业总收入"`
SaleexpToGr float64 `gorm:"type:decimal(20,4);comment:销售费用/营业总收入"`
AdminexpOfGr float64 `gorm:"type:decimal(20,4);comment:管理费用/营业总收入"`
FinaexpOfGr float64 `gorm:"type:decimal(20,4);comment:财务费用/营业总收入"`
ImpaiTtm float64 `gorm:"type:decimal(20,4);comment:资产减值损失/营业总收入"`
GcOfGr float64 `gorm:"type:decimal(20,4);comment:营业总成本/营业总收入"`
OpOfGr float64 `gorm:"type:decimal(20,4);comment:营业利润/营业总收入"`
EbitOfGr float64 `gorm:"type:decimal(20,4);comment:息税前利润/营业总收入"`
OpincomeOfEbt float64 `gorm:"type:decimal(20,4);comment:经营活动净收益/利润总额"`
InvestincomeOfEbt float64 `gorm:"type:decimal(20,4);comment:价值变动净收益/利润总额"`
NOpProfitOfEbt float64 `gorm:"type:decimal(20,4);comment:营业外收支净额/利润总额"`
TaxToEbt float64 `gorm:"type:decimal(20,4);comment:所得税/利润总额"`
DtprofitToProfit float64 `gorm:"type:decimal(20,4);comment:扣除非经常损益后的净利润/净利润"`
SalescashToOr float64 `gorm:"type:decimal(20,4);comment:销售商品提供劳务收到的现金/营业收入"`
OcfToOr float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/营业收入"`
OcfToOpincome float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/经营活动净收益"`
CapitalizedToDa float64 `gorm:"type:decimal(20,4);comment:资本支出/折旧和摊销"`
OcfToDebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/负债合计"`
OcfToInterestdebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/带息债务"`
OcfToNetdebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/净债务"`
LongdebtToWorkingcapital float64 `gorm:"type:decimal(20,4);comment:长期债务与营运资金比率"`
EbitdaToDebt float64 `gorm:"type:decimal(20,4);comment:息税折旧摊销前利润/负债合计"`
OpToEbt float64 `gorm:"type:decimal(20,4);comment:营业利润/利润总额"`
NopToEbt float64 `gorm:"type:decimal(20,4);comment:非营业利润/利润总额"`
OcfToProfit float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/营业利润"`
CashToLiqdebt float64 `gorm:"type:decimal(20,4);comment:货币资金/流动负债"`
CashToLiqdebtWithinterest float64 `gorm:"type:decimal(20,4);comment:货币资金/带息流动负债"`
OpToLiqdebt float64 `gorm:"type:decimal(20,4);comment:营业利润/流动负债"`
OpToDebt float64 `gorm:"type:decimal(20,4);comment:营业利润/负债合计"`
ProfitToOp float64 `gorm:"type:decimal(20,4);comment:利润总额/营业收入"`
// 年度化指标
RoeYearly float64 `gorm:"type:decimal(20,4);comment:年化净资产收益率"`
Roa2Yearly float64 `gorm:"type:decimal(20,4);comment:年化总资产报酬率"`
RoaYearly float64 `gorm:"type:decimal(20,4);comment:年化总资产净利率"`
RoicYearly float64 `gorm:"type:decimal(20,4);comment:年化投入资本回报率"`
RoeAvg float64 `gorm:"type:decimal(20,4);comment:平均净资产收益率(增发条件)"`
// 单季度增长比率
QGrYoy float64 `gorm:"type:decimal(20,4);comment:营业总收入同比增长率(%)(单季度)"`
QGrQoq float64 `gorm:"type:decimal(20,4);comment:营业总收入环比增长率(%)(单季度)"`
QSalesYoy float64 `gorm:"type:decimal(20,4);comment:营业收入同比增长率(%)(单季度)"`
QSalesQoq float64 `gorm:"type:decimal(20,4);comment:营业收入环比增长率(%)(单季度)"`
QOpYoy float64 `gorm:"type:decimal(20,4);comment:营业利润同比增长率(%)(单季度)"`
QOpQoq float64 `gorm:"type:decimal(20,4);comment:营业利润环比增长率(%)(单季度)"`
QProfitYoy float64 `gorm:"type:decimal(20,4);comment:净利润同比增长率(%)(单季度)"`
QProfitQoq float64 `gorm:"type:decimal(20,4);comment:净利润环比增长率(%)(单季度)"`
QNetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润同比增长率(%)(单季度)"`
QNetprofitQoq float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润环比增长率(%)(单季度)"`
// 单季度比率指标
QProfitToGr float64 `gorm:"type:decimal(20,4);comment:净利润/营业总收入(单季度)"`
QSaleexpToGr float64 `gorm:"type:decimal(20,4);comment:销售费用/营业总收入 (单季度)"`
QAdminexpToGr float64 `gorm:"type:decimal(20,4);comment:管理费用/营业总收入 (单季度)"`
QFinaexpToGr float64 `gorm:"type:decimal(20,4);comment:财务费用/营业总收入 (单季度)"`
QImpairToGrTtm float64 `gorm:"type:decimal(20,4);comment:资产减值损失/营业总收入(单季度)"`
QGcToGr float64 `gorm:"type:decimal(20,4);comment:营业总成本/营业总收入 (单季度)"`
QOpToGr float64 `gorm:"type:decimal(20,4);comment:营业利润/营业总收入(单季度)"`
QOpincomeToEbt float64 `gorm:"type:decimal(20,4);comment:经营活动净收益/利润总额(单季度)"`
QInvestincomeToEbt float64 `gorm:"type:decimal(20,4);comment:价值变动净收益/利润总额(单季度)"`
QDtprofitToProfit float64 `gorm:"type:decimal(20,4);comment:扣除非经常损益后的净利润/净利润(单季度)"`
QSalescashToOr float64 `gorm:"type:decimal(20,4);comment:销售商品提供劳务收到的现金/营业收入(单季度)"`
QOcfToSales float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/营业收入(单季度)"`
QOcfToOr float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/经营活动净收益(单季度)"`
// 其他
UpdateFlag string `gorm:"type:varchar(1);comment:更新标识"`
}
// TableName 设置表名
func (StockFinaIndicator) TableName() string {
return "fina_indicator"
}
// Key 与表 uniqueIndex un_fi_code_date 一致ts_code + period。
func (f *StockFinaIndicator) Key() string {
if f.TsCode == "" && f.Period == 0 {
return ""
}
return f.TsCode + "#" + strconv.Itoa(f.Period)
}
// RowLabel 便于日志/调试ts_code + 报告期 end_date + 公告 ann_date。
func (f *StockFinaIndicator) RowLabel() string {
if f.EndDate != "" || f.AnnDate != "" {
return f.TsCode + " end=" + f.EndDate + " ann=" + f.AnnDate
}
return f.Key()
}

49
schema/stock_indicator.go Normal file
View File

@@ -0,0 +1,49 @@
package schema
import "strconv"
// StockIndicator 每日基本面指标(采用 dataset/stock 的 decimal 定义,与采集端迁移一致)。
type StockIndicator 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"`
Close float64 `gorm:"type:decimal(20,4);comment:当日收盘价"`
TurnoverRate float64 `gorm:"type:decimal(20,4);comment:换手率(%)"`
TurnoverRateF float64 `gorm:"type:decimal(20,4);comment:换手率(自由流通股)"`
VolumeRatio float64 `gorm:"type:decimal(20,4);comment:量比"`
Pe float64 `gorm:"type:decimal(20,4);comment:市盈率(总市值/净利润)"`
PeTtm float64 `gorm:"type:decimal(20,4);comment:市盈率(TTM)"`
Pb float64 `gorm:"type:decimal(20,4);comment:市净率"`
Ps float64 `gorm:"type:decimal(20,4);comment:市销率"`
PsTtm float64 `gorm:"type:decimal(20,4);comment:市销率(TTM)"`
DvRatio float64 `gorm:"type:decimal(20,4);comment:股息率(%)"`
DvTtm float64 `gorm:"type:decimal(20,4);comment:股息率(TTM)(%)"`
TotalShare float64 `gorm:"type:decimal(20,4);comment:总股本(万股)"`
FloatShare float64 `gorm:"type:decimal(20,4);comment:流通股本(万股)"`
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 (StockIndicator) TableName() string {
return "stock_indicator"
}
// Key 业务主键ts_code + 交易日。
func (s *StockIndicator) Key() string {
if s.TsCode == "" && s.TradeDate == 0 {
return ""
}
return s.TsCode + "#" + strconv.Itoa(s.TradeDate)
}
// HasTotalMV 总市值是否已填充(大于 0
func (s *StockIndicator) HasTotalMV() bool {
return s.TotalMv > 0
}
// HasCircMV 流通市值是否已填充。
func (s *StockIndicator) HasCircMV() bool {
return s.CircMv > 0
}

141
tushare/board.go Normal file
View File

@@ -0,0 +1,141 @@
package tushare
/*
LimitCptList 获取限板板块列表
trade_date: 交易日,格式:YYYYMMDD
ts_code: 板块代码
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) LimitCptList(trade_date, ts_code, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if ts_code != "" {
params["ts_code"] = ts_code
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "limit_cpt_list",
Params: params,
}
fields := []map[string]string{
{"rank": "排名"},
{"ts_code": "板块代码"},
{"name": "板块名称"},
{"pct_chg": "涨跌幅%"},
{"up_stat": "连板高度"},
{"days": "上榜天数"},
{"up_nums": "涨停标的数"},
{"cons_nums": "连板标的数"},
}
return cli.Do(req, fields)
}
/*
LimitListD 获取涨跌停列表(新)
trade_date: 交易日期,格式:YYYYMMDD
ts_code: 股票代码
limit_type: 涨跌停类型U 涨停 D 跌停 Z 炸板
exchange: 交易所SH 上交所 SZ 深交所 BJ 北交所
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) LimitListD(trade_date, ts_code, limit_type, exchange, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if ts_code != "" {
params["ts_code"] = ts_code
}
if limit_type != "" {
params["limit_type"] = limit_type
}
if exchange != "" {
params["exchange"] = exchange
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "limit_list_d",
Params: params,
}
fields := []map[string]string{
{"trade_date": "交易日期"},
{"ts_code": "股票代码"},
{"industry": "所属行业"},
{"name": "股票名称"},
{"close": "收盘价"},
{"pct_chg": "涨跌幅%"},
{"amount": "成交额"},
{"limit_amount": "板上成交金额"},
{"float_mv": "流通市值"},
{"total_mv": "总市值"},
{"turnover_ratio": "换手率%"},
{"fd_amount": "封单金额"},
{"first_time": "首次封板时间"},
{"last_time": "最后封板时间"},
{"open_times": "炸板次数"},
{"up_stat": "涨停统计"},
{"limit_times": "连板数"},
{"limit": "涨跌停类型"},
}
return cli.Do(req, fields)
}
/*
LimitU 获取涨停股票列表
trade_date: 交易日期,格式:YYYYMMDD
ts_code: 股票代码
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) LimitU(trade_date, ts_code, start_date, end_date string) (*TushareRespData, error) {
return cli.LimitListD(trade_date, ts_code, "U", "", start_date, end_date)
}
/*
LimitD 获取跌停股票列表
trade_date: 交易日期,格式:YYYYMMDD
ts_code: 股票代码
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) LimitD(trade_date, ts_code, start_date, end_date string) (*TushareRespData, error) {
return cli.LimitListD(trade_date, ts_code, "D", "", start_date, end_date)
}
/*
LimitZ 获取炸板股票列表
trade_date: 交易日期,格式:YYYYMMDD
ts_code: 股票代码
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) LimitZ(trade_date, ts_code, start_date, end_date string) (*TushareRespData, error) {
return cli.LimitListD(trade_date, ts_code, "Z", "", start_date, end_date)
}

254
tushare/fina.go Normal file
View File

@@ -0,0 +1,254 @@
package tushare
/*
Income 获取利润表
ts_code: 股票代码,支持多个,逗号分隔
ann_date: 公告日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
report_type: 报表类型1:合并报表2:母公司报表
*/
func (cli *TushareClient) Income(ts_code, ann_date, start_date, end_date, report_type string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if ann_date != "" {
params["ann_date"] = ann_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if report_type != "" {
params["report_type"] = report_type
}
req := TushareReq{
APIName: "income",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"ann_date": "公告日期"},
{"f_ann_date": "实际公告日期"},
{"end_date": "报告期"},
{"basic_eps": "基本每股收益"},
{"diluted_eps": "稀释每股收益"},
{"total_revenue": "营业总收入"},
{"revenue": "营业收入"},
{"total_profit": "利润总额"},
{"net_profit": "净利润"},
{"net_profit_attr_sh": "归属于母公司所有者的净利润"},
{"operating_expense": "营业总成本"},
{"operating_cost": "营业成本"},
}
return cli.Do(req, fields)
}
/*
Balancesheet 获取资产负债表
ts_code: 股票代码,支持多个,逗号分隔
ann_date: 公告日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
report_type: 报表类型1:合并报表2:母公司报表
*/
func (cli *TushareClient) Balancesheet(ts_code, ann_date, start_date, end_date, report_type string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if ann_date != "" {
params["ann_date"] = ann_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if report_type != "" {
params["report_type"] = report_type
}
req := TushareReq{
APIName: "balancesheet",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"ann_date": "公告日期"},
{"f_ann_date": "实际公告日期"},
{"end_date": "报告期"},
{"total_assets": "资产总计"},
{"total_liab": "负债合计"},
{"total_hldr_eqy_exc_min_int": "股东权益合计"},
{"total_hldr_eqy_inc_min_int": "股东权益合计 (含少数股东权益)"},
{"total_share_capital": "股本"},
{"cap_rsrv": "资本公积"},
{"surplus_rsrv": "盈余公积"},
{"undist_prft": "未分配利润"},
{"monetary_cap": "货币资金"},
{"total_current_assets": "流动资产合计"},
{"total_non_current_assets": "非流动资产合计"},
}
return cli.Do(req, fields)
}
/*
Cashflow 获取现金流量表
ts_code: 股票代码,支持多个,逗号分隔
ann_date: 公告日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
report_type: 报表类型1:合并报表2:母公司报表
*/
func (cli *TushareClient) Cashflow(ts_code, ann_date, start_date, end_date, report_type string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if ann_date != "" {
params["ann_date"] = ann_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if report_type != "" {
params["report_type"] = report_type
}
req := TushareReq{
APIName: "cashflow",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"ann_date": "公告日期"},
{"f_ann_date": "实际公告日期"},
{"end_date": "报告期"},
{"net_cash_invest_act": "投资活动产生的现金流量净额"},
{"net_cash_financing_act": "筹资活动产生的现金流量净额"},
{"net_cash_oper_act": "经营活动产生的现金流量净额"},
{"cash_equivalents_end": "现金及现金等价物期末余额"},
{"cash_equivalents_begin": "现金及现金等价物期初余额"},
{"net_increase_cash": "现金及现金等价物净增加额"},
}
return cli.Do(req, fields)
}
/*
FinaIndicator 获取财务指标
ts_code: 股票代码,支持多个,逗号分隔
ann_date: 公告日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) FinaIndicator(ts_code, ann_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if ann_date != "" {
params["ann_date"] = ann_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "fina_indicator",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"ann_date": "公告日期"},
{"end_date": "报告期"},
{"eps_basic": "基本每股收益"},
{"eps_diluted": "稀释每股收益"},
{"roe": "净资产收益率%"},
{"roe_wa": "净资产收益率 (加权)%"},
{"roa": "总资产净利率%"},
{"gross_margin": "销售毛利率%"},
{"net_profit_margin": "销售净利率%"},
{"current_ratio": "流动比率"},
{"quick_ratio": "速动比率"},
{"debt_to_assets": "资产负债率%"},
{"turnover_days": "存货周转天数"},
{"receivables_turnover": "应收账款周转率"},
}
return cli.Do(req, fields)
}
/*
Forecast 获取业绩预告
ts_code: 股票代码,支持多个,逗号分隔
ann_date: 公告日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
type: 预告类型,预增/预减/扭亏/续盈/首亏/略增/略减
*/
func (cli *TushareClient) Forecast(ts_code, ann_date, start_date, end_date, forecast_type string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if ann_date != "" {
params["ann_date"] = ann_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if forecast_type != "" {
params["type"] = forecast_type
}
req := TushareReq{
APIName: "forecast",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"ann_date": "公告日期"},
{"end_date": "报告期"},
{"type": "预告类型"},
{"net_profit_min": "净利润下限 (万元)"},
{"net_profit_max": "净利润上限 (万元)"},
{"parent_netprofit_min": "归母净利润下限 (万元)"},
{"parent_netprofit_max": "归母净利润上限 (万元)"},
{"summary": "业绩预告摘要"},
}
return cli.Do(req, fields)
}

206
tushare/finance.go Normal file
View File

@@ -0,0 +1,206 @@
package tushare
/*
DailyBasic 获取股票日线指标
ts_code: 股票代码,支持多个,逗号分隔
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) DailyBasic(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "daily_basic",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"trade_date": "交易日期"},
{"close": "收盘价"},
{"turnover_rate": "换手率%"},
{"turnover_rate_f": "换手率% (自由流通股本)"},
{"volume_ratio": "量比"},
{"pe": "市盈率"},
{"pe_ttm": "市盈率 TTM"},
{"pb": "市净率"},
{"ps": "市销率"},
{"ps_ttm": "市销率 TTM"},
{"dv_ratio": "股息率%"},
{"total_mv": "总市值"},
{"circ_mv": "流通市值"},
}
return cli.Do(req, fields)
}
/*
AdjFactor 获取复权因子
ts_code: 股票代码,支持多个,逗号分隔
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) AdjFactor(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "adj_factor",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"trade_date": "交易日期"},
{"adj_factor": "复权因子"},
}
return cli.Do(req, fields)
}
/*
Moneyflow 获取个股资金流向
ts_code: 股票代码,支持多个,逗号分隔
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) Moneyflow(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "moneyflow",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"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": "净流入金额 (千元)"},
}
return cli.Do(req, fields)
}
/*
SuspendList 获取停牌股票列表
suspend_type: 停牌类型1:盘中停牌2:盘中临时停牌3:全天停牌
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) SuspendList(suspend_type, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if suspend_type != "" {
params["suspend_type"] = suspend_type
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "suspend_list",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"name": "股票名称"},
{"suspend_type": "停牌类型"},
{"suspend_start": "停牌起始日"},
{"suspend_end": "停牌结束日"},
{"reason": "停牌原因"},
}
return cli.Do(req, fields)
}
/*
RealtimeQuote 获取实时行情
ts_code: 股票代码,支持多个,逗号分隔
*/
func (cli *TushareClient) RealtimeQuote(ts_code string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
req := TushareReq{
APIName: "realtime_quote",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"price": "最新价"},
{"open": "开盘价"},
{"high": "最高价"},
{"low": "最低价"},
{"pre_close": "昨收价"},
{"vol": "成交量 (手)"},
{"amount": "成交额 (千元)"},
{"buy_vol": "买总量 (手)"},
{"sell_vol": "卖总量 (手)"},
{"buy_amount": "买总额 (千元)"},
{"sell_amount": "卖总额 (千元)"},
}
return cli.Do(req, fields)
}

213
tushare/index.go Normal file
View File

@@ -0,0 +1,213 @@
package tushare
/*
IndexBasic 获取大盘指数基本信息
ts_code: 指数代码,支持多个,逗号分隔
exchange: 交易所代码SSE 上交所SZSE 深交所
*/
func (cli *TushareClient) IndexBasic(ts_code, exchange string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if exchange != "" {
params["exchange"] = exchange
}
req := TushareReq{
APIName: "index_basic",
Params: params,
}
fields := []map[string]string{
{"ts_code": "指数代码"},
{"name": "指数名称"},
{"market": "市场类别"},
{"publisher": "发布方"},
{"category": "指数类别"},
{"base_date": "基日"},
{"base_point": "基点"},
{"list_date": "发布日期"},
}
return cli.Do(req, fields)
}
/*
IndexWeight 获取指数成分和权重
index_code: 指数代码,如 000300.SH(沪深 300)
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) IndexWeight(index_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if index_code != "" {
params["index_code"] = index_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "index_weight",
Params: params,
}
fields := []map[string]string{
{"index_code": "指数代码"},
{"trade_date": "交易日期"},
{"ts_code": "成分股票代码"},
{"weight": "权重%"},
}
return cli.Do(req, fields)
}
/*
IndexClassify 获取指数分类信息
level: 指数级别L1/L2/L3/L4
src: 指数来源SW 申万ZJW 证监会ICS iFinD
parent_code: 父级代码
*/
func (cli *TushareClient) IndexClassify(level, src, parent_code string) (*TushareRespData, error) {
params := map[string]any{}
if level != "" {
params["level"] = level
}
if src != "" {
params["src"] = src
}
if parent_code != "" {
params["parent_code"] = parent_code
}
req := TushareReq{
APIName: "index_classify",
Params: params,
}
fields := []map[string]string{
{"index_code": "指数代码"},
{"industry_name": "行业名称"},
{"industry_code": "行业代码"},
{"parent_code": "父级代码"},
{"level": "级别"},
{"src": "来源"},
}
return cli.Do(req, fields)
}
/*
IndexMember 获取指数成分股
index_code: 指数代码,如 000300.SH(沪深 300)
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) IndexMember(index_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if index_code != "" {
params["index_code"] = index_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "index_member",
Params: params,
}
fields := []map[string]string{
{"index_code": "指数代码"},
{"trade_date": "交易日期"},
{"ts_code": "成分股票代码"},
{"symbol": "成分股票代码"},
{"name": "成分股票名称"},
{"is_new": "是否新增"},
{"in_date": "纳入日期"},
{"out_date": "剔除日期"},
}
return cli.Do(req, fields)
}
/*
Concept 获取概念板块信息
ts_code: 板块代码,支持多个,逗号分隔
name: 板块名称,支持模糊匹配
*/
func (cli *TushareClient) Concept(ts_code, name string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if name != "" {
params["name"] = name
}
req := TushareReq{
APIName: "concept",
Params: params,
}
fields := []map[string]string{
{"ts_code": "板块代码"},
{"name": "板块名称"},
{"src": "来源"},
{"pub_date": "发布日期"},
}
return cli.Do(req, fields)
}
/*
ConceptDetail 获取概念板块详情
ts_code: 板块代码
*/
func (cli *TushareClient) ConceptDetail(ts_code string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
req := TushareReq{
APIName: "concept_detail",
Params: params,
}
fields := []map[string]string{
{"ts_code": "板块代码"},
{"name": "板块名称"},
{"concept_name": "概念名称"},
{"stock_count": "成分股票数量"},
}
return cli.Do(req, fields)
}

24
tushare/indicator.go Normal file
View File

@@ -0,0 +1,24 @@
package tushare
func (cli *TushareClient) StkFactorPro(ts_code string, tradeDate string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if tradeDate != "" {
params["trade_date"] = tradeDate
}
req := TushareReq{
APIName: "stk_factor_pro",
Params: params,
}
fields := []map[string]string{
{"trade_date": "trade_date"},
{"rsi_bfq_24": "rsi_bfq_24"},
{"rsi_hfq_24": "rsi_hfq_24"},
{"rsi_qfq_24": "rsi_qfq_24"},
}
return cli.Do(req, fields)
}

209
tushare/margin.go Normal file
View File

@@ -0,0 +1,209 @@
package tushare
/*
HkHold 获取沪深股通持股明细
ts_code: 股票代码
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) HkHold(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "hk_hold",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"trade_date": "交易日期"},
{"holding_sh": "沪股通持股数量 (股)"},
{"holding_sz": "深股通持股数量 (股)"},
{"holding_total": "沪深股通持股总量 (股)"},
{"ratio_sh": "沪股通持股比例%"},
{"ratio_sz": "深股通持股比例%"},
{"ratio_total": "沪深股通持股比例合计%"},
}
return cli.Do(req, fields)
}
/*
MarginDetail 获取融资融券明细
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
exchange: 交易所代码SSE 上交所SZSE 深交所
*/
func (cli *TushareClient) MarginDetail(trade_date, start_date, end_date, exchange string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
if exchange != "" {
params["exchange"] = exchange
}
req := TushareReq{
APIName: "margin_detail",
Params: params,
}
fields := []map[string]string{
{"trade_date": "交易日期"},
{"ts_code": "股票代码"},
{"buy_value": "融资买入额 (元)"},
{"buy_repay_value": "融资偿还额 (元)"},
{"buy_bal": "融资余额 (元)"},
{"sell_value": "融券卖出量 (股)"},
{"sell_repay_value": "融券偿还量 (股)"},
{"sell_bal": "融券余量 (股)"},
{"sell_amount": "融券余量金额 (元)"},
}
return cli.Do(req, fields)
}
/*
TopList 获取龙虎榜数据
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) TopList(trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "top_list",
Params: params,
}
fields := []map[string]string{
{"trade_date": "交易日期"},
{"ts_code": "股票代码"},
{"name": "股票名称"},
{"close": "收盘价"},
{"pct_chg": "涨跌幅%"},
{"turnover_rate": "换手率%"},
{"total_value": "成交总额 (万元)"},
{"net_value": "净额 (万元)"},
{"buy_value": "买入总额 (万元)"},
{"sell_value": "卖出总额 (万元)"},
{"reason": "上榜原因"},
}
return cli.Do(req, fields)
}
/*
TopInst 获取龙虎榜机构席位数据
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) TopInst(trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "top_inst",
Params: params,
}
fields := []map[string]string{
{"trade_date": "交易日期"},
{"ts_code": "股票代码"},
{"name": "股票名称"},
{"buy_value": "机构买入总额 (万元)"},
{"buy_count": "机构买入次数"},
{"sell_value": "机构卖出总额 (万元)"},
{"sell_count": "机构卖出次数"},
{"net_value": "机构净买入额 (万元)"},
}
return cli.Do(req, fields)
}
/*
BlockTrade 获取大宗交易数据
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) BlockTrade(trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "block_trade",
Params: params,
}
fields := []map[string]string{
{"trade_date": "交易日期"},
{"ts_code": "股票代码"},
{"name": "证券简称"},
{"price": "成交价"},
{"vol": "成交量 (万股)"},
{"amount": "成交额 (万元)"},
{"buyer": "买方营业部"},
{"seller": "卖方营业部"},
{"premium_rate": "溢价率%"},
}
return cli.Do(req, fields)
}

168
tushare/new.go Normal file
View File

@@ -0,0 +1,168 @@
package tushare
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/jedib0t/go-pretty/v6/table"
)
// TushareClient Tushare API 客户端
type TushareClient struct {
Token string `json:"token"` // API 访问令牌
BaseUrl string `json:"base_url"` // API 基础 URL
}
// TushareReq Tushare API 请求结构
type TushareReq struct {
APIName string `json:"api_name"` // API 接口名称
Token string `json:"token"` // API 访问令牌
Params map[string]any `json:"params"` // 请求参数
Fields []string `json:"fields"` // 返回字段列表
}
// TushareResp Tushare API 响应结构
type TushareResp struct {
RequestID string `json:"request_id"` // 请求 ID
Code int `json:"code"` // 响应码 (0 表示成功)
Data *TushareRespData `json:"data"` // 响应数据
Msg string `json:"msg"` // 响应消息
}
// TushareRespData Tushare API 响应数据结构
type TushareRespData struct {
Fields []string `json:"fields"` // 字段列表
Headers []string `json:"headers"` // 表头(中文描述)
Items [][]any `json:"items"` // 数据项(二维数组)
HasMore bool `json:"has_more"` // 是否有更多数据
Count int `json:"count"` // 返回数据条数
}
// NewClient 创建并初始化 Tushare 客户端
// 返回配置好 Token 和基础 URL 的客户端实例
func NewClient(token string) *TushareClient {
if token == "" {
token = os.Getenv("TUSHARE_TOKEN")
}
return &TushareClient{
Token: token,
BaseUrl: "http://api.tushare.pro",
}
}
// Do 执行 Tushare API 请求
// req: 请求参数,包含 API 名称、参数等
// fieldsVals: 字段配置列表,每个元素是一个 mapkey 为字段名value 为字段中文描述
// 返回:响应数据和错误信息
func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*TushareRespData, error) {
// 构建请求体
payload := TushareReq{
APIName: req.APIName,
Token: c.Token,
Params: req.Params,
}
// 提取字段名和对应的中文表头
var fields []string
var headers []string
for _, setting := range fieldsVals {
for key, value := range setting {
fields = append(fields, key)
headers = append(headers, value)
}
}
payload.Fields = fields
// 序列化请求体为 JSON
reqBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
}
// 创建 HTTP 请求
client := &http.Client{
Timeout: 30 * time.Second, // 设置 30 秒超时
}
resp, err := client.Post(c.BaseUrl, "application/json", bytes.NewReader(reqBytes))
if err != nil {
return nil, fmt.Errorf("#100 发送请求失败:%w", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("#101 读取响应失败:%w", err)
}
// 解析响应 JSON
var tushareResp TushareResp
if err := json.Unmarshal(body, &tushareResp); err != nil {
return nil, fmt.Errorf("#101 响应解析失败:%v - %s", err, string(body))
}
// 检查响应码
if tushareResp.Code != 0 {
return nil, fmt.Errorf("#102 API 返回错误:%s", tushareResp.Msg)
}
// 设置表头信息
if tushareResp.Data != nil {
tushareResp.Data.Headers = headers
}
return tushareResp.Data, nil
}
// Map 将响应数据转换为 map 切片
// 每个 map 代表一行数据key 为字段名value 为对应的值
func (c *TushareRespData) Map() []map[string]any {
result := make([]map[string]any, 0)
if c == nil || len(c.Items) == 0 || len(c.Fields) == 0 {
return result
}
for _, item := range c.Items {
rowMap := make(map[string]any, len(c.Fields))
for idx, fn := range c.Fields {
if idx < len(item) {
rowMap[fn] = item[idx]
}
}
result = append(result, rowMap)
}
return result
}
// Json 将响应数据转换为 JSON 字节数组
// 返回JSON 格式的字节数据和错误信息
func (c *TushareRespData) Json() ([]byte, error) {
data := c.Map()
return json.MarshalIndent(data, "", " ")
}
// Output 将响应数据格式化为表格输出
// title: 表格标题
// 返回:格式化的表格写入器
func (c *TushareRespData) Output(title string) table.Writer {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.SetTitle(title)
// 构建表头行
headerRow := make(table.Row, 0, len(c.Headers))
for idx, header := range c.Headers {
headerRow = append(headerRow, header+"("+c.Fields[idx]+")")
}
tw.AppendHeader(headerRow)
// 添加数据行
for _, item := range c.Items {
tw.AppendRow(item)
}
return tw
}

241
tushare/stock.go Normal file
View File

@@ -0,0 +1,241 @@
package tushare
import (
"time"
)
/*
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()
return cal[0]["cal_date"].(string)
}
/*
StockBasic 获取股票列表
ts_code: 股票代码,支持多个,如"000002.SZ,000001.SZ"
exchange: 交易所代码SSE 上交所SZSE 深交所
is_shsc: 是否在沪股通或深港通范围内0:否;1:沪股通;2:深股通
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) StockBasic(ts_code, exchange, is_shsc, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if exchange != "" {
params["exchange"] = exchange
}
if is_shsc != "" {
params["is_shsc"] = is_shsc
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "stock_basic",
Params: params,
}
fields := []map[string]string{
{"ts_code": "TS 代码"},
{"symbol": "股票交易代码"},
{"name": "证券简称"},
{"area": "地区"},
{"industry": "所属行业"},
{"market": "市场类别"},
{"list_status": "上市状态"},
{"list_date": "上市日期"},
{"delist_date": "退市日期"},
{"is_shsc": "是否沪股通或深股通"},
}
return cli.Do(req, fields)
}
/*
Daily 获取日线行情
ts_code: 股票代码格式000001.SZ 或 600000.SH
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) Daily(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "daily",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"trade_date": "交易日期"},
{"open": "开盘价"},
{"high": "最高价"},
{"low": "最低价"},
{"close": "收盘价"},
{"pre_close": "昨收价"},
{"change": "涨跌额"},
{"pct_chg": "涨跌幅%"},
{"vol": "成交量 (手)"},
{"amount": "成交额 (千元)"},
}
return cli.Do(req, fields)
}
/*
Min 获取分钟线行情
ts_code: 股票代码格式000001.SZ 或 600000.SH
trade_date: 交易日期,格式:YYYYMMDD
minute: 分钟类型1/5/15/30/60 分钟
start_time: 开始时间,格式:HHmmss
end_time: 结束时间,格式:HHmmss
*/
func (cli *TushareClient) Min(ts_code, trade_date, minute, start_time, end_time string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if minute != "" {
params["minute"] = minute
}
if start_time != "" {
params["start_time"] = start_time
}
if end_time != "" {
params["end_time"] = end_time
}
req := TushareReq{
APIName: "min",
Params: params,
}
fields := []map[string]string{
{"ts_code": "股票代码"},
{"trade_time": "交易时间"},
{"open": "开盘价"},
{"high": "最高价"},
{"low": "最低价"},
{"close": "收盘价"},
{"vol": "成交量 (手)"},
{"amount": "成交额 (千元)"},
}
return cli.Do(req, fields)
}
/*
IndexDaily 获取大盘指数日线行情
ts_code: 指数代码,如 000001.SH(上证指数),399001.SZ(深证成指)
trade_date: 交易日期,格式:YYYYMMDD
start_date: 开始日期,格式:YYYYMMDD
end_date: 结束日期,格式:YYYYMMDD
*/
func (cli *TushareClient) IndexDaily(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "index_daily",
Params: params,
}
fields := []map[string]string{
{"ts_code": "指数代码"},
{"trade_date": "交易日期"},
{"close": "收盘点位"},
{"open": "开盘点位"},
{"high": "最高点位"},
{"low": "最低点位"},
{"pre_close": "昨收点位"},
{"change": "涨跌额"},
{"pct_chg": "涨跌幅%"},
{"vol": "成交量 (手)"},
{"amount": "成交额 (千元)"},
}
return cli.Do(req, fields)
}

213
tushare/ths.go Normal file
View File

@@ -0,0 +1,213 @@
package tushare
/*
ThsHot 同花顺热榜
trade_date str N 交易日期
ts_code str N TS代码
market str N 热榜类型(热股、ETF、可转债、行业板块、概念板块、期货、港股、热基、美股)
is_new str N 是否最新默认Y如果为N则为盘中和盘后阶段采集具体时间可参考rank_time字段状态N每小时更新一次状态Y更新时间为2230
*/
func (cli *TushareClient) ThsHot(trade_date, ts_code, market, is_new string) (*TushareRespData, error) {
params := map[string]any{}
if trade_date != "" {
params["trade_date"] = trade_date
}
if ts_code != "" {
params["ts_code"] = ts_code
}
if market != "" {
params["market"] = market
}
if is_new != "" {
params["is_new"] = is_new
}
req := TushareReq{
APIName: "ths_hot",
Params: params,
}
/*
trade_date str Y 交易日期
data_type str Y 数据类型
ts_code str Y 股票代码
ts_name str Y 股票名称
rank int Y 排行
pct_change float Y 涨跌幅%
current_price float Y 当前价格
concept str Y 标签
rank_reason str Y 上榜解读
hot float Y 热度值
rank_time str Y 排行榜获取时间
*/
fields := []map[string]string{
{"trade_date": "交易日期"},
{"data_type": "数据类型"},
{"ts_code": "TS代码"},
{"ts_name": "股票名称"},
{"rank": "排行"},
{"pct_change": "涨跌幅%"},
{"current_price": "当前价格"},
{"concept": "标签"},
{"rank_reason": "上榜解读"},
{"hot": "热度值"},
{"rank_time": "排行榜获取时间"},
}
return cli.Do(req, fields)
}
/*
ThsIndex 同花顺概念和行业指数
接口说明:
- 描述:获取同花顺板块指数,包括概念、行业、特色指数
- 权限本接口需有6000积分单次最大返回5000行数据一次可提取全部数据请勿循环提取
- 注意事项:数据版权归属同花顺,如做商业用途,请主动联系同花顺
输入参数:
- ts_code: 指数代码(可选)
- exchange: 市场类型A-a股 HK-港股 US-美股(可选)
- type: 指数类型N-概念指数 I-行业指数 R-地域指数 S-同花顺特色指数 ST-同花顺风格指数 TH-同花顺主题指数 BB-同花顺宽基指数(可选)
输出参数:
- ts_code: 代码
- name: 名称
- count: 成分个数
- exchange: 交易所
- list_date: 上市日期
- type: N概念指数S特色指数
*/
func (cli *TushareClient) ThsIndex(ts_code, exchange, typ string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if exchange != "" {
params["exchange"] = exchange
}
if typ != "" {
params["type"] = typ
}
req := TushareReq{
APIName: "ths_index",
Params: params,
}
fields := []map[string]string{
{"ts_code": "代码"},
{"name": "名称"},
{"count": "成分个数"},
{"exchange": "交易所"},
{"list_date": "上市日期"},
{"type": "类型"},
}
return cli.Do(req, fields)
}
/*
ThsDaily 同花顺板块指数行情
接口说明:
- 描述:获取同花顺板块指数行情
- 限量单次最大3000行数据需6000积分可根据指数代码、日期参数循环提取
- 注意事项:数据版权归属同花顺,如做商业用途,请主动联系同花顺
输入参数:
- ts_code: 指数代码(可选)
- trade_date: 交易日期YYYYMMDD格式可选
- start_date: 开始日期YYYYMMDD格式可选
- end_date: 结束日期YYYYMMDD格式可选
输出参数:
- ts_code: TS指数代码
- trade_date: 交易日
- close: 收盘点位
- open: 开盘点位
- high: 最高点位
- low: 最低点位
- pre_close: 昨日收盘点
- avg_price: 平均价
- change: 涨跌点位
- pct_change: 涨跌幅
- vol: 成交量(手)
- turnover_rate: 换手率(%
- total_mv: 总市值(元)
- float_mv: 流通市值(元)
*/
func (cli *TushareClient) ThsDaily(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if trade_date != "" {
params["trade_date"] = trade_date
}
if start_date != "" {
params["start_date"] = start_date
}
if end_date != "" {
params["end_date"] = end_date
}
req := TushareReq{
APIName: "ths_daily",
Params: params,
}
fields := []map[string]string{
{"ts_code": "TS指数代码"},
{"trade_date": "交易日"},
{"close": "收盘点位"},
{"open": "开盘点位"},
{"high": "最高点位"},
{"low": "最低点位"},
{"pre_close": "昨日收盘点"},
{"avg_price": "平均价"},
{"change": "涨跌点位"},
{"pct_change": "涨跌幅"},
{"vol": "成交量(手)"},
{"turnover_rate": "换手率(%"},
{"total_mv": "总市值(元)"},
{"float_mv": "流通市值(元)"},
}
return cli.Do(req, fields)
}
/*
ThsMember 获取同花顺概念板块成分
ts_code: 板块指数代码
con_code: 股票代码
*/
func (cli *TushareClient) ThsMember(ts_code, con_code string) (*TushareRespData, error) {
params := map[string]any{}
if ts_code != "" {
params["ts_code"] = ts_code
}
if con_code != "" {
params["con_code"] = con_code
}
req := TushareReq{
APIName: "ths_member",
Params: params,
}
fields := []map[string]string{
{"ts_code": "指数代码"},
{"con_code": "股票代码"},
{"con_name": "股票名称"},
{"weight": "权重"},
{"in_date": "纳入日期"},
{"out_date": "剔除日期"},
{"is_new": "是否最新"},
}
return cli.Do(req, fields)
}