review code.
This commit is contained in:
26
conv/conv.go
26
conv/conv.go
@@ -99,14 +99,36 @@ func AnyToFloat64(v any) float64 {
|
|||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
return val
|
return val
|
||||||
case string:
|
|
||||||
return String2Float64(val)
|
|
||||||
case float32:
|
case float32:
|
||||||
return float64(val)
|
return float64(val)
|
||||||
case int:
|
case int:
|
||||||
return float64(val)
|
return float64(val)
|
||||||
|
case int8:
|
||||||
|
return float64(val)
|
||||||
|
case int16:
|
||||||
|
return float64(val)
|
||||||
|
case int32:
|
||||||
|
return float64(val)
|
||||||
|
case int64:
|
||||||
|
return float64(val)
|
||||||
case uint:
|
case uint:
|
||||||
return float64(val)
|
return float64(val)
|
||||||
|
case uint8:
|
||||||
|
return float64(val)
|
||||||
|
case uint16:
|
||||||
|
return float64(val)
|
||||||
|
case uint32:
|
||||||
|
return float64(val)
|
||||||
|
case uint64:
|
||||||
|
return float64(val)
|
||||||
|
case string:
|
||||||
|
return String2Float64(val)
|
||||||
|
case json.Number:
|
||||||
|
f, err := val.Float64()
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return f
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func (m *Builder) Build() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Builder) SaveToFile(filePath string) error {
|
func (m *Builder) SaveToFile(filePath string) error {
|
||||||
rf, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
|
rf, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,8 @@ package schema
|
|||||||
|
|
||||||
import "gorm.io/gorm"
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
// StockBasic 股票基本信息表(合并 dataset/stock 与 gostock 字段;gostock 独有 Level、Desc)。
|
// DatasetStockBasic 股票基本信息表(合并 dataset/stock 与 gostock 字段;gostock 独有 Level、Desc)。
|
||||||
type StockBasic struct {
|
type DatasetStockBasic struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
TsCode string `gorm:"type:varchar(50);not null;index;comment:TS代码"`
|
TsCode string `gorm:"type:varchar(50);not null;index;comment:TS代码"`
|
||||||
Symbol string `gorm:"type:varchar(50);not null;comment:股票代码"`
|
Symbol string `gorm:"type:varchar(50);not null;comment:股票代码"`
|
||||||
@@ -21,17 +21,17 @@ type StockBasic struct {
|
|||||||
ActEntType string `gorm:"type:varchar(50);not null;default:'';comment:实控人企业性质"`
|
ActEntType string `gorm:"type:varchar(50);not null;default:'';comment:实控人企业性质"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (StockBasic) TableName() string {
|
func (DatasetStockBasic) TableName() string {
|
||||||
return "stock_basic"
|
return "dataset_stock_basic"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key 业务主键:TS 代码。
|
// Key 业务主键:TS 代码。
|
||||||
func (s *StockBasic) Key() string {
|
func (s *DatasetStockBasic) Key() string {
|
||||||
return s.TsCode
|
return s.TsCode
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisplaySymbol 展示用代码:有 symbol 用 symbol,否则用 ts_code。
|
// DisplaySymbol 展示用代码:有 symbol 用 symbol,否则用 ts_code。
|
||||||
func (s *StockBasic) DisplaySymbol() string {
|
func (s *DatasetStockBasic) DisplaySymbol() string {
|
||||||
if s.Symbol != "" {
|
if s.Symbol != "" {
|
||||||
return s.Symbol
|
return s.Symbol
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,6 @@ func (s *StockBasic) DisplaySymbol() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsNorthbound 是否沪深港通标的(H 沪股通 / S 深股通)。
|
// IsNorthbound 是否沪深港通标的(H 沪股通 / S 深股通)。
|
||||||
func (s *StockBasic) IsNorthbound() bool {
|
func (s *DatasetStockBasic) IsNorthbound() bool {
|
||||||
return s.IsHS == "H" || s.IsHS == "S"
|
return s.IsHS == "H" || s.IsHS == "S"
|
||||||
}
|
}
|
||||||
28
schema/dataset_blocks_index.go
Normal file
28
schema/dataset_blocks_index.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
// DatasetBlocksIndex 板块索引(采用 dataset/stock 的 code 唯一约束)。
|
||||||
|
type DatasetBlocksIndex struct {
|
||||||
|
gorm.Model
|
||||||
|
TiCode string `gorm:"type:varchar(50);not null;default:'';comment:指数代码;uniqueIndex:uq_blocks_index_code" json:"ti_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 (DatasetBlocksIndex) TableName() string {
|
||||||
|
return "dataset_blocks_index"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key 板块业务主键:code。
|
||||||
|
func (b *DatasetBlocksIndex) Key() string {
|
||||||
|
return b.TiCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayName 展示名称:非空 name,否则回退 code。
|
||||||
|
func (b *DatasetBlocksIndex) DisplayName() string {
|
||||||
|
if b.Name != "" {
|
||||||
|
return b.Name
|
||||||
|
}
|
||||||
|
return b.TiCode
|
||||||
|
}
|
||||||
28
schema/dataset_blocks_member.go
Normal file
28
schema/dataset_blocks_member.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
// DatasetBlocksMember 板块成分股。
|
||||||
|
type DatasetBlocksMember struct {
|
||||||
|
gorm.Model
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DatasetBlocksMember) TableName() string {
|
||||||
|
return "dataset_blocks_member"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key 成分在板块内的键:ti_code + ts_code。
|
||||||
|
func (b *DatasetBlocksMember) Key() string {
|
||||||
|
if b.TiCode == "" && b.TsCode == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return b.TiCode + "/" + b.TsCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasWeight 是否带权重量化成分。
|
||||||
|
func (b *DatasetBlocksMember) HasWeight() bool {
|
||||||
|
return b.Weight > 0
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@ package schema
|
|||||||
|
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
// StockDaily 股票日线数据(两仓库结构一致)。
|
// DatasetStockDaily 股票日线数据(两仓库结构一致)。
|
||||||
type StockDaily struct {
|
type DatasetStockDaily struct {
|
||||||
ID uint `gorm:"primarykey;autoIncrement" json:"id"`
|
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"`
|
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"`
|
TradeDate int `gorm:"index:idx_trade_date;uniqueIndex:un_code_date;comment:交易日期" json:"trade_date"`
|
||||||
@@ -19,12 +19,12 @@ type StockDaily struct {
|
|||||||
Amount float64 `gorm:"type:decimal(20,2);comment:成交额(千元)" json:"amount"`
|
Amount float64 `gorm:"type:decimal(20,2);comment:成交额(千元)" json:"amount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (StockDaily) TableName() string {
|
func (DatasetStockDaily) TableName() string {
|
||||||
return "stock_daily"
|
return "dataset_stock_daily"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key 业务主键:ts_code + 交易日。
|
// Key 业务主键:ts_code + 交易日。
|
||||||
func (d *StockDaily) Key() string {
|
func (d *DatasetStockDaily) Key() string {
|
||||||
if d.TsCode == "" && d.TradeDate == 0 {
|
if d.TsCode == "" && d.TradeDate == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -32,17 +32,17 @@ func (d *StockDaily) Key() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsRising 是否收涨(昨收有效且收盘高于昨收)。
|
// IsRising 是否收涨(昨收有效且收盘高于昨收)。
|
||||||
func (d *StockDaily) IsRising() bool {
|
func (d *DatasetStockDaily) IsRising() bool {
|
||||||
return d.PreClose > 0 && d.Close > d.PreClose
|
return d.PreClose > 0 && d.Close > d.PreClose
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFalling 是否收跌。
|
// IsFalling 是否收跌。
|
||||||
func (d *StockDaily) IsFalling() bool {
|
func (d *DatasetStockDaily) IsFalling() bool {
|
||||||
return d.PreClose > 0 && d.Close < d.PreClose
|
return d.PreClose > 0 && d.Close < d.PreClose
|
||||||
}
|
}
|
||||||
|
|
||||||
// PctChangeFromPre 由昨收计算的涨跌幅(%);昨收无效时返回 0。
|
// PctChangeFromPre 由昨收计算的涨跌幅(%);昨收无效时返回 0。
|
||||||
func (d *StockDaily) PctChangeFromPre() float64 {
|
func (d *DatasetStockDaily) PctChangeFromPre() float64 {
|
||||||
if d.PreClose <= 0 {
|
if d.PreClose <= 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ func (d *StockDaily) PctChangeFromPre() float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AmplitudePct 振幅(相对昨收,%):(最高-最低)/昨收*100。
|
// AmplitudePct 振幅(相对昨收,%):(最高-最低)/昨收*100。
|
||||||
func (d *StockDaily) AmplitudePct() float64 {
|
func (d *DatasetStockDaily) AmplitudePct() float64 {
|
||||||
if d.PreClose <= 0 {
|
if d.PreClose <= 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StockFinaIndicator 财务指标模型
|
// DatasetStockFinaIndicator 财务指标模型
|
||||||
type StockFinaIndicator struct {
|
type DatasetStockFinaIndicator struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
TsCode string `gorm:"type:varchar(20);not null;index:fi_ts_code;uniqueIndex:un_fi_code_date;comment:TS代码"`
|
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:报告期数"`
|
Period int `gorm:"index:idx_period;uniqueIndex:un_fi_code_date;comment:报告期数"`
|
||||||
@@ -205,12 +205,12 @@ type StockFinaIndicator struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TableName 设置表名
|
// TableName 设置表名
|
||||||
func (StockFinaIndicator) TableName() string {
|
func (DatasetStockFinaIndicator) TableName() string {
|
||||||
return "fina_indicator"
|
return "dataset_fina_indicator"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key 与表 uniqueIndex un_fi_code_date 一致:ts_code + period。
|
// Key 与表 uniqueIndex un_fi_code_date 一致:ts_code + period。
|
||||||
func (f *StockFinaIndicator) Key() string {
|
func (f *DatasetStockFinaIndicator) Key() string {
|
||||||
if f.TsCode == "" && f.Period == 0 {
|
if f.TsCode == "" && f.Period == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ func (f *StockFinaIndicator) Key() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RowLabel 便于日志/调试:ts_code + 报告期 end_date + 公告 ann_date。
|
// RowLabel 便于日志/调试:ts_code + 报告期 end_date + 公告 ann_date。
|
||||||
func (f *StockFinaIndicator) RowLabel() string {
|
func (f *DatasetStockFinaIndicator) RowLabel() string {
|
||||||
if f.EndDate != "" || f.AnnDate != "" {
|
if f.EndDate != "" || f.AnnDate != "" {
|
||||||
return f.TsCode + " end=" + f.EndDate + " ann=" + f.AnnDate
|
return f.TsCode + " end=" + f.EndDate + " ann=" + f.AnnDate
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@ package schema
|
|||||||
|
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
// StockIndicator 每日基本面指标(采用 dataset/stock 的 decimal 定义,与采集端迁移一致)。
|
// DatasetStockIndicator 每日基本面指标(采用 dataset/stock 的 decimal 定义,与采集端迁移一致)。
|
||||||
type StockIndicator struct {
|
type DatasetStockIndicator struct {
|
||||||
ID uint `gorm:"primarykey;autoIncrement"`
|
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"`
|
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"`
|
TradeDate int `gorm:"index:si_trade_date;uniqueIndex:un_si_code_date;comment:交易日期" json:"trade_date"`
|
||||||
@@ -26,12 +26,12 @@ type StockIndicator struct {
|
|||||||
Roe float64 `gorm:"type:decimal(20,4);comment:ROE(%) 净利润/股本"`
|
Roe float64 `gorm:"type:decimal(20,4);comment:ROE(%) 净利润/股本"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (StockIndicator) TableName() string {
|
func (DatasetStockIndicator) TableName() string {
|
||||||
return "stock_indicator"
|
return "dataset_stock_indicator"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key 业务主键:ts_code + 交易日。
|
// Key 业务主键:ts_code + 交易日。
|
||||||
func (s *StockIndicator) Key() string {
|
func (s *DatasetStockIndicator) Key() string {
|
||||||
if s.TsCode == "" && s.TradeDate == 0 {
|
if s.TsCode == "" && s.TradeDate == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -39,11 +39,11 @@ func (s *StockIndicator) Key() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasTotalMV 总市值是否已填充(大于 0)。
|
// HasTotalMV 总市值是否已填充(大于 0)。
|
||||||
func (s *StockIndicator) HasTotalMV() bool {
|
func (s *DatasetStockIndicator) HasTotalMV() bool {
|
||||||
return s.TotalMv > 0
|
return s.TotalMv > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasCircMV 流通市值是否已填充。
|
// HasCircMV 流通市值是否已填充。
|
||||||
func (s *StockIndicator) HasCircMV() bool {
|
func (s *DatasetStockIndicator) HasCircMV() bool {
|
||||||
return s.CircMv > 0
|
return s.CircMv > 0
|
||||||
}
|
}
|
||||||
66
schema/dataset_indicator_pro.go
Normal file
66
schema/dataset_indicator_pro.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// DatasetIndicatorPro 对应 Tushare stk_factor_pro 当前请求字段集(见 tushare/indicator.go 的 fields 列表)。
|
||||||
|
type DatasetIndicatorPro struct {
|
||||||
|
ID uint `gorm:"primarykey;autoIncrement"`
|
||||||
|
TsCode string `gorm:"type:varchar(20);not null;index:dip_ts_code;uniqueIndex:un_dip_code_date;comment:股票代码" json:"ts_code"`
|
||||||
|
TradeDate int `gorm:"index:dip_trade_date;uniqueIndex:un_dip_code_date;comment:交易日期" json:"trade_date"`
|
||||||
|
Open float64 `gorm:"type:decimal(20,4);comment:开盘价"`
|
||||||
|
High float64 `gorm:"type:decimal(20,4);comment:最高价"`
|
||||||
|
Low float64 `gorm:"type:decimal(20,4);comment:最低价"`
|
||||||
|
Close float64 `gorm:"type:decimal(20,4);comment:收盘价"`
|
||||||
|
PreClose float64 `gorm:"type:decimal(20,4);comment:昨收价"`
|
||||||
|
Change float64 `gorm:"type:decimal(20,4);comment:涨跌额"`
|
||||||
|
PctChg float64 `gorm:"type:decimal(20,6);comment:涨跌幅%"`
|
||||||
|
Vol float64 `gorm:"type:decimal(20,2);comment:成交量(手)"`
|
||||||
|
Amount float64 `gorm:"type:decimal(20,2);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:流通市值(万元)"`
|
||||||
|
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下轨不复权"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DatasetIndicatorPro) TableName() string {
|
||||||
|
return "dataset_indicator_pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key 业务主键:ts_code + 交易日。
|
||||||
|
func (s *DatasetIndicatorPro) Key() string {
|
||||||
|
if s.TsCode == "" && s.TradeDate == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s.TsCode + "#" + strconv.Itoa(s.TradeDate)
|
||||||
|
}
|
||||||
31
schema/dataset_money_total.go
Normal file
31
schema/dataset_money_total.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package schema
|
package schema
|
||||||
|
|
||||||
// PledgeStat 股权质押统计(表 pledge_stat;dataset/stock 中原为 PledgeStatModel,业务层 Save 请留在各应用内)。
|
// DatasetPledgeStat 股权质押统计(表 dataset_pledge_stat;dataset/stock 中原为 PledgeStatModel,业务层 Save 请留在各应用内)。
|
||||||
type PledgeStat struct {
|
type DatasetPledgeStat struct {
|
||||||
ID uint `gorm:"primarykey" json:"id"`
|
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"`
|
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"`
|
EndDate int `gorm:"type:int;not null;default:0;comment:截止日期" json:"end_date"`
|
||||||
@@ -12,19 +12,19 @@ type PledgeStat struct {
|
|||||||
PledgeRatio float64 `json:"pledge_ratio"`
|
PledgeRatio float64 `json:"pledge_ratio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (PledgeStat) TableName() string {
|
func (DatasetPledgeStat) TableName() string {
|
||||||
return "pledge_stat"
|
return "dataset_pledge_stat"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key 与唯一约束 uq_pledge_stat_ts_code 一致:ts_code。
|
// Key 与唯一约束 uq_pledge_stat_ts_code 一致:ts_code。
|
||||||
func (p *PledgeStat) Key() string {
|
func (p *DatasetPledgeStat) Key() string {
|
||||||
return p.TsCode
|
return p.TsCode
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPledgeFacts 是否具备任一质押统计字段(比例、次数或股本)。
|
// HasPledgeFacts 是否具备任一质押统计字段(比例、次数或股本)。
|
||||||
func (p *PledgeStat) HasPledgeFacts() bool {
|
func (p *DatasetPledgeStat) HasPledgeFacts() bool {
|
||||||
return p.PledgeRatio > 0 || p.PledgeCount > 0 || p.TotalShare > 0
|
return p.PledgeRatio > 0 || p.PledgeCount > 0 || p.TotalShare > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// PledgeStatModel 兼容 dataset/stock 中的类型名。
|
// DatasetPledgeStatModel 兼容 dataset/stock 中的类型名。
|
||||||
type PledgeStatModel = PledgeStat
|
type DatasetPledgeStatModel = DatasetPledgeStat
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -5,14 +5,15 @@ import "git.apinb.com/bsm-sdk/core/database"
|
|||||||
// RegisterAutoMigrate 将本包内与 stock/gostock 共用的表注册到 bsm-sdk 的迁移列表(可选;也可在各应用 init 中自行 AppendMigrate)。
|
// RegisterAutoMigrate 将本包内与 stock/gostock 共用的表注册到 bsm-sdk 的迁移列表(可选;也可在各应用 init 中自行 AppendMigrate)。
|
||||||
func RegisterAutoMigrate() {
|
func RegisterAutoMigrate() {
|
||||||
for _, t := range []any{
|
for _, t := range []any{
|
||||||
&StockBasic{},
|
&DatasetStockBasic{},
|
||||||
&StockDaily{},
|
&DatasetStockDaily{},
|
||||||
&BlocksIndex{},
|
&DatasetBlocksIndex{},
|
||||||
&BlocksMember{},
|
&DatasetBlocksMember{},
|
||||||
&MoneyTotal{},
|
&DatasetMoneyTotal{},
|
||||||
&PledgeStat{},
|
&DatasetPledgeStat{},
|
||||||
&StockIndicator{},
|
&DatasetStockIndicator{},
|
||||||
&StockFinaIndicator{},
|
&DatasetIndicatorPro{},
|
||||||
|
&DatasetStockFinaIndicator{},
|
||||||
} {
|
} {
|
||||||
database.AppendMigrate(t)
|
database.AppendMigrate(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,26 @@
|
|||||||
package tushare
|
package tushare
|
||||||
|
|
||||||
func (cli *TushareClient) StkFactorPro(ts_code string, tradeDate string) (*TushareRespData, error) {
|
/*
|
||||||
|
StkFactorPro 获取股票技术面因子(专业版)
|
||||||
|
|
||||||
|
ts_code: 股票代码,支持多个,逗号分隔
|
||||||
|
trade_date: 交易日期,格式:YYYYMMDD
|
||||||
|
start_date: 开始日期,格式:YYYYMMDD
|
||||||
|
end_date: 结束日期,格式:YYYYMMDD
|
||||||
|
*/
|
||||||
|
func (cli *TushareClient) StkFactorPro(ts_code, trade_date, start_date, end_date string) (*TushareRespData, error) {
|
||||||
params := map[string]any{}
|
params := map[string]any{}
|
||||||
if ts_code != "" {
|
if ts_code != "" {
|
||||||
params["ts_code"] = ts_code
|
params["ts_code"] = ts_code
|
||||||
}
|
}
|
||||||
if tradeDate != "" {
|
if trade_date != "" {
|
||||||
params["trade_date"] = tradeDate
|
params["trade_date"] = trade_date
|
||||||
|
}
|
||||||
|
if start_date != "" {
|
||||||
|
params["start_date"] = start_date
|
||||||
|
}
|
||||||
|
if end_date != "" {
|
||||||
|
params["end_date"] = end_date
|
||||||
}
|
}
|
||||||
|
|
||||||
req := TushareReq{
|
req := TushareReq{
|
||||||
@@ -14,10 +28,52 @@ func (cli *TushareClient) StkFactorPro(ts_code string, tradeDate string) (*Tusha
|
|||||||
Params: params,
|
Params: params,
|
||||||
}
|
}
|
||||||
fields := []map[string]string{
|
fields := []map[string]string{
|
||||||
{"trade_date": "trade_date"},
|
{"ts_code": "股票代码"},
|
||||||
{"rsi_bfq_24": "rsi_bfq_24"},
|
{"trade_date": "交易日期"},
|
||||||
{"rsi_hfq_24": "rsi_hfq_24"},
|
{"open": "开盘价"},
|
||||||
{"rsi_qfq_24": "rsi_qfq_24"},
|
{"high": "最高价"},
|
||||||
|
{"low": "最低价"},
|
||||||
|
{"close": "收盘价"},
|
||||||
|
{"pre_close": "昨收价"},
|
||||||
|
{"change": "涨跌额"},
|
||||||
|
{"pct_chg": "涨跌幅%"},
|
||||||
|
{"vol": "成交量(手)"},
|
||||||
|
{"amount": "成交额(千元)"},
|
||||||
|
{"turnover_rate": "换手率(%)"},
|
||||||
|
{"turnover_rate_f": "换手率(自由流通股)"},
|
||||||
|
{"volume_ratio": "量比"},
|
||||||
|
{"pe": "市盈率"},
|
||||||
|
{"pe_ttm": "市盈率TTM"},
|
||||||
|
{"pb": "市净率"},
|
||||||
|
{"ps": "市销率"},
|
||||||
|
{"ps_ttm": "市销率TTM"},
|
||||||
|
{"dv_ratio": "股息率(%)"},
|
||||||
|
{"dv_ttm": "股息率TTM(%)"},
|
||||||
|
{"total_share": "总股本(万股)"},
|
||||||
|
{"float_share": "流通股本(万股)"},
|
||||||
|
{"free_share": "自由流通股本(万股)"},
|
||||||
|
{"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下轨不复权"},
|
||||||
}
|
}
|
||||||
|
|
||||||
return cli.Do(req, fields)
|
return cli.Do(req, fields)
|
||||||
|
|||||||
131
tushare/new.go
131
tushare/new.go
@@ -17,6 +17,7 @@ import (
|
|||||||
type TushareClient struct {
|
type TushareClient struct {
|
||||||
Token string `json:"token"` // API 访问令牌
|
Token string `json:"token"` // API 访问令牌
|
||||||
BaseUrl string `json:"base_url"` // API 基础 URL
|
BaseUrl string `json:"base_url"` // API 基础 URL
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// TushareReq Tushare API 请求结构
|
// TushareReq Tushare API 请求结构
|
||||||
@@ -53,9 +54,40 @@ func NewClient(token string) *TushareClient {
|
|||||||
return &TushareClient{
|
return &TushareClient{
|
||||||
Token: token,
|
Token: token,
|
||||||
BaseUrl: "http://api.tushare.pro",
|
BaseUrl: "http://api.tushare.pro",
|
||||||
|
client: &http.Client{Timeout: 30 * time.Second},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TushareClient) httpDo() *http.Client {
|
||||||
|
if c.client != nil {
|
||||||
|
return c.client
|
||||||
|
}
|
||||||
|
return &http.Client{Timeout: 30 * time.Second}
|
||||||
|
}
|
||||||
|
|
||||||
|
// postTushare 发送 JSON 请求并解析为 TushareResp(不含业务码以外的处理)。
|
||||||
|
func (c *TushareClient) postTushare(reqBytes []byte) (*TushareResp, error) {
|
||||||
|
resp, err := c.httpDo().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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return &tushareResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Do 执行 Tushare API 请求
|
// Do 执行 Tushare API 请求
|
||||||
// req: 请求参数,包含 API 名称、参数等
|
// req: 请求参数,包含 API 名称、参数等
|
||||||
// fieldsVals: 字段配置列表,每个元素是一个 map,key 为字段名,value 为字段中文描述
|
// fieldsVals: 字段配置列表,每个元素是一个 map,key 为字段名,value 为字段中文描述
|
||||||
@@ -69,8 +101,12 @@ func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*Tus
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 提取字段名和对应的中文表头
|
// 提取字段名和对应的中文表头
|
||||||
var fields []string
|
n := 0
|
||||||
var headers []string
|
for _, setting := range fieldsVals {
|
||||||
|
n += len(setting)
|
||||||
|
}
|
||||||
|
fields := make([]string, 0, n)
|
||||||
|
headers := make([]string, 0, n)
|
||||||
for _, setting := range fieldsVals {
|
for _, setting := range fieldsVals {
|
||||||
for key, value := range setting {
|
for key, value := range setting {
|
||||||
fields = append(fields, key)
|
fields = append(fields, key)
|
||||||
@@ -79,40 +115,15 @@ func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*Tus
|
|||||||
}
|
}
|
||||||
payload.Fields = fields
|
payload.Fields = fields
|
||||||
|
|
||||||
// 序列化请求体为 JSON
|
|
||||||
reqBytes, err := json.Marshal(payload)
|
reqBytes, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
|
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 HTTP 请求
|
tushareResp, err := c.postTushare(reqBytes)
|
||||||
client := &http.Client{
|
|
||||||
Timeout: 30 * time.Second, // 设置 30 秒超时
|
|
||||||
}
|
|
||||||
resp, err := client.Post(c.BaseUrl, "application/json", bytes.NewReader(reqBytes))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("#100 发送请求失败:%w", err)
|
return nil, 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 {
|
if tushareResp.Data != nil {
|
||||||
tushareResp.Data.Headers = headers
|
tushareResp.Data.Headers = headers
|
||||||
}
|
}
|
||||||
@@ -125,12 +136,22 @@ func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*Tus
|
|||||||
// Params: 请求参数
|
// Params: 请求参数
|
||||||
// 返回:响应数据和错误信息
|
// 返回:响应数据和错误信息
|
||||||
func (c *TushareClient) Exec(ApiName string, fields string, Params map[string]any) (*TushareRespData, error) {
|
func (c *TushareClient) Exec(ApiName string, fields string, Params map[string]any) (*TushareRespData, error) {
|
||||||
fieldsList := strings.Split(fields, ",")
|
fields = strings.TrimSpace(fields)
|
||||||
|
if fields == "" {
|
||||||
|
return nil, fmt.Errorf("#100 字段列表不能为空")
|
||||||
|
}
|
||||||
|
raw := strings.Split(fields, ",")
|
||||||
|
fieldsList := make([]string, 0, len(raw))
|
||||||
|
for _, f := range raw {
|
||||||
|
f = strings.TrimSpace(f)
|
||||||
|
if f != "" {
|
||||||
|
fieldsList = append(fieldsList, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(fieldsList) == 0 {
|
if len(fieldsList) == 0 {
|
||||||
return nil, fmt.Errorf("#100 字段列表不能为空")
|
return nil, fmt.Errorf("#100 字段列表不能为空")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建请求体
|
|
||||||
payload := TushareReq{
|
payload := TushareReq{
|
||||||
APIName: ApiName,
|
APIName: ApiName,
|
||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
@@ -138,51 +159,25 @@ func (c *TushareClient) Exec(ApiName string, fields string, Params map[string]an
|
|||||||
Fields: fieldsList,
|
Fields: fieldsList,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 序列化请求体为 JSON
|
|
||||||
reqBytes, err := json.Marshal(payload)
|
reqBytes, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
|
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 HTTP 请求
|
tushareResp, err := c.postTushare(reqBytes)
|
||||||
client := &http.Client{
|
|
||||||
Timeout: 30 * time.Second, // 设置 30 秒超时
|
|
||||||
}
|
|
||||||
resp, err := client.Post(c.BaseUrl, "application/json", bytes.NewReader(reqBytes))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("#100 发送请求失败:%w", err)
|
return nil, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置表头信息
|
|
||||||
return tushareResp.Data, nil
|
return tushareResp.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map 将响应数据转换为 map 切片
|
// Map 将响应数据转换为 map 切片
|
||||||
// 每个 map 代表一行数据,key 为字段名,value 为对应的值
|
// 每个 map 代表一行数据,key 为字段名,value 为对应的值
|
||||||
func (c *TushareRespData) Map() []map[string]any {
|
func (c *TushareRespData) Map() []map[string]any {
|
||||||
result := make([]map[string]any, 0)
|
|
||||||
if c == nil || len(c.Items) == 0 || len(c.Fields) == 0 {
|
if c == nil || len(c.Items) == 0 || len(c.Fields) == 0 {
|
||||||
return result
|
return make([]map[string]any, 0)
|
||||||
}
|
}
|
||||||
|
result := make([]map[string]any, 0, len(c.Items))
|
||||||
for _, item := range c.Items {
|
for _, item := range c.Items {
|
||||||
rowMap := make(map[string]any, len(c.Fields))
|
rowMap := make(map[string]any, len(c.Fields))
|
||||||
for idx, fn := range c.Fields {
|
for idx, fn := range c.Fields {
|
||||||
@@ -209,11 +204,17 @@ func (c *TushareRespData) Output(title string) table.Writer {
|
|||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(table.StyleLight)
|
tw.SetStyle(table.StyleLight)
|
||||||
tw.SetTitle(title)
|
tw.SetTitle(title)
|
||||||
|
if c == nil {
|
||||||
|
return tw
|
||||||
|
}
|
||||||
|
|
||||||
// 构建表头行
|
n := len(c.Fields)
|
||||||
headerRow := make(table.Row, 0, len(c.Headers))
|
if nh := len(c.Headers); nh < n {
|
||||||
for idx, header := range c.Headers {
|
n = nh
|
||||||
headerRow = append(headerRow, header+"("+c.Fields[idx]+")")
|
}
|
||||||
|
headerRow := make(table.Row, 0, n)
|
||||||
|
for idx := 0; idx < n; idx++ {
|
||||||
|
headerRow = append(headerRow, c.Headers[idx]+"("+c.Fields[idx]+")")
|
||||||
}
|
}
|
||||||
tw.AppendHeader(headerRow)
|
tw.AppendHeader(headerRow)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package tushare
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.apinb.com/quant/qsdk/conv"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -49,7 +51,10 @@ func (cli *TushareClient) ReturnLastTradeDay() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
cal := result.Map()
|
cal := result.Map()
|
||||||
return cal[0]["cal_date"].(string)
|
if len(cal) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return conv.AnyToString(cal[0]["cal_date"])
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user