diff --git a/cmd/money/main.go b/cmd/money/main.go new file mode 100644 index 0000000..57730a7 --- /dev/null +++ b/cmd/money/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "git.apinb.com/dataset/stock/internal/config" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/logic/a" +) + +var ( + ServiceKey = "stock" +) + +func main() { + // 配置初始化 + config.New(ServiceKey) + + // 创建实现层 + impl.NewImpl() + a.NewApiClient() + a.GetTushareMoneyFlow() +} diff --git a/internal/logic/a/moneyflow.go b/internal/logic/a/moneyflow.go new file mode 100644 index 0000000..9eb672c --- /dev/null +++ b/internal/logic/a/moneyflow.go @@ -0,0 +1,133 @@ +package a + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" +) + +// 请求结构(根据Tushare API格式定义) +type TushareReq struct { + APIName string `json:"api_name"` + Token string `json:"token"` + Params MParams `json:"params"` + Fields []string `json:"fields"` +} +type MParams map[string]interface{} + +// 响应结构 +type TushareResp struct { + RequestID string `json:"request_id"` + Code int `json:"code"` + Data struct { + Fields []string `json:"fields"` + Items [][]any `json:"items"` + HasMore bool `json:"has_more"` + Count int `json:"count"` + } `json:"data"` + Msg string `json:"msg"` +} + +// 资金流数据模型 +type Moneyflow struct { + TsCode string `json:"ts_code"` // 股票代码 + TradeDate string `json:"trade_date"` // 交易日期 + BuySmAmount float64 `json:"buy_sm_amount"` // 小单买入 + BuyMdAmount float64 `json:"buy_md_amount"` // 中单买入 + BuyLgAmount float64 `json:"buy_lg_amount"` // 大单买入 + BuyElgAmount float64 `json:"buy_elg_amount"` // 超大单买入 + // 可根据需要补充净流入字段 + NetMfAmount float64 `json:"net_mf_amount"` // 净流入额(万元) +} + +func GetTushareMoneyFlow() { + dates := GetTradeDateBy(3, true) + allData := make(map[string][]Moneyflow) + for _, date := range dates { + list, _ := fetchMoneyFlowByDate(TushareToken, date) + for _, mf := range list { + allData[mf.TsCode] = append(allData[mf.TsCode], mf) + } + } + for code, dailyList := range allData { + if len(dailyList) < 3 { + continue + } + + // 判断资金流向是否正在正增长。 + isGrenter := false + if dailyList[2].NetMfAmount > dailyList[1].NetMfAmount && dailyList[1].NetMfAmount > dailyList[0].NetMfAmount { + isGrenter = true + } + data := models.MoneyTotal{ + Code: code, + Last1DayMfAmount: dailyList[2].NetMfAmount, + Last1DayTotalAmount: dailyList[2].BuySmAmount + dailyList[2].BuyMdAmount + dailyList[2].BuyLgAmount + dailyList[2].BuyElgAmount, + IsGreaterPervious: isGrenter, + } + for _, item := range dailyList { + data.Last3DayMfAmount += item.NetMfAmount + data.Last3DayTotalAmount += item.BuySmAmount + item.BuyMdAmount + item.BuyLgAmount + item.BuyElgAmount + } + + data.Last1DayMfAmount = utils.FloatRound(data.Last1DayMfAmount, 2) + data.Last1DayTotalAmount = utils.FloatRound(data.Last1DayTotalAmount, 2) + data.Last3DayMfAmount = utils.FloatRound(data.Last3DayMfAmount, 2) + data.Last3DayTotalAmount = utils.FloatRound(data.Last3DayTotalAmount, 2) + + var cnt int64 + impl.DBService.Model(&models.MoneyTotal{}).Where("code=?", code).Count(&cnt) + if cnt == 0 { + impl.DBService.Create(&data) + } else { + impl.DBService.Model(&models.MoneyTotal{}).Where("code=?", code).Updates(&data) + } + } +} + +// 1. 获取指定日期所有股票的资金流向 +func fetchMoneyFlowByDate(token, date string) ([]Moneyflow, error) { + url := "http://api.tushare.pro" + payload := TushareReq{ + APIName: "moneyflow", + Token: token, + Params: MParams{"trade_date": date}, + Fields: []string{"ts_code", "trade_date", "buy_sm_amount", "buy_md_amount", "buy_lg_amount", "buy_elg_amount", "net_mf_amount"}, + } + reqBytes, _ := json.Marshal(payload) + resp, err := http.Post(url, "application/json", strings.NewReader(string(reqBytes))) + if err != nil { + return nil, fmt.Errorf("#100 %v", err) + } + defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + + var tushareResp TushareResp + if err := json.Unmarshal(body, &tushareResp); err != nil { + fmt.Println(string(body)) + return nil, fmt.Errorf("#101 %v", err) + } + + // 解析二维数组 + var result []Moneyflow + for _, item := range tushareResp.Data.Items { + // 此处简化了类型转换,生产环境需严格处理err + mf := Moneyflow{ + TsCode: item[0].(string), + TradeDate: item[1].(string), + BuySmAmount: item[2].(float64), + BuyMdAmount: item[3].(float64), + BuyLgAmount: item[4].(float64), + BuyElgAmount: item[5].(float64), + NetMfAmount: item[6].(float64), + } + result = append(result, mf) + } + return result, nil +} diff --git a/internal/logic/a/new.go b/internal/logic/a/new.go index 5f16a3c..ea0d91f 100644 --- a/internal/logic/a/new.go +++ b/internal/logic/a/new.go @@ -16,6 +16,30 @@ func NewApiClient() { TushareClient = tushare.New(TushareToken) } +// 获取最近N个交易日,asc=true 顺序:asc=false 倒序 +func GetTradeDateBy(max int, asc bool) []string { + reply, _ := TushareClient.TradeCal(map[string]string{ + "is_open": "1", + "end_date": time.Now().Format("20060102"), + }, []string{"exchange", "cal_date"}) + + var days []string + for i := 0; i < max; i++ { + days = append(days, reply.Data.Items[i][1].(string)) + } + + // 将days的数据倒序 + if asc { + var newDays []string + for i := len(days) - 1; i >= 0; i-- { + newDays = append(newDays, days[i]) + } + return newDays + } + + return days +} + func ReturnLastDay() (string, string) { now := time.Now() end := now.Format("20060102") diff --git a/internal/models/money_total.go b/internal/models/money_total.go new file mode 100644 index 0000000..6a6a8bd --- /dev/null +++ b/internal/models/money_total.go @@ -0,0 +1,23 @@ +package models + +import "git.apinb.com/bsm-sdk/core/database" + +// 资金流数据模型 +type MoneyTotal struct { + ID uint `gorm:"primarykey"` + Code string // 股票代码 + Last1DayMfAmount float64 // 最近1天净流入额(万元) + Last3DayMfAmount float64 // 最近3天净流入额(万元) + Last1DayTotalAmount float64 // 最近1天中大单总流入额(万元) + Last3DayTotalAmount float64 // 最近3天中大单总流入额(万元) + IsGreaterPervious bool // 是否比上一日大单流入额多 +} + +func init() { + database.AppendMigrate(&MoneyTotal{}) +} + +// TableName 设置表名 +func (MoneyTotal) TableName() string { + return "money_total" +}