// fundamentals 是正股基本面数据入库命令: // // go run ./cmd/fundamentals [-db 库路径] // // 从 convert_bond 表读取所有正股代码,通过东财 clist 接口批量拉取 // 基本面数据(ROE/资产负债率/净利润/ST状态),写入 stock_fundamental 表。 package main import ( "context" "flag" "fmt" "os" "path/filepath" "time" "stock-analysis/internal/datasource/eastmoney" "stock-analysis/internal/storage/sqlite" ) func main() { dbPath := flag.String("db", defaultDBPath(), "SQLite 数据库文件路径") flag.Parse() store, err := sqlite.Open(*dbPath) if err != nil { fatal("打开数据库失败: ", err) } defer store.Close() ctx := context.Background() // 1. 获取所有正股代码。 stock2bond, err := store.GetStockBondMap(ctx) if err != nil { fatal("获取正股映射失败: ", err) } if len(stock2bond) == 0 { fmt.Println("convert_bond 表为空,请先执行 go run ./cmd/ingest -all 入库转债数据") return } stockCodes := make([]string, 0, len(stock2bond)) for code := range stock2bond { stockCodes = append(stockCodes, code) } fmt.Printf("正股数量:%d\n", len(stockCodes)) // 2. 分批拉取基本面数据(每批最多 200 只)。 client := eastmoney.NewClient() const batchSize = 200 var totalSaved, totalFail int for i := 0; i < len(stockCodes); i += batchSize { end := i + batchSize if end > len(stockCodes) { end = len(stockCodes) } batch := stockCodes[i:end] funds, err := client.FetchStockFundamentals(ctx, batch) if err != nil { fmt.Printf("批次 %d-%d 拉取失败: %v\n", i, end-1, err) totalFail += len(batch) continue } for _, f := range funds { f.UpdateDate = time.Now().Format("2006-01-02") if err := store.SaveStockFundamental(ctx, f); err != nil { fmt.Printf(" %s %s: 保存失败 %v\n", f.Code, f.Name, err) totalFail++ continue } totalSaved++ } fmt.Printf("批次 %d-%d: 拉取 %d 只, 已保存 %d\n", i+1, end, len(batch), totalSaved) // 控制请求频率。 if end < len(stockCodes) { time.Sleep(2 * time.Second) } } fmt.Printf("\n基本面入库完成:成功 %d 只,失败 %d 只(库: %s)\n", totalSaved, totalFail, *dbPath) } // defaultDBPath 返回默认数据库路径。 func defaultDBPath() string { local := os.Getenv("LOCALAPPDATA") if local == "" { local = os.TempDir() } return filepath.Join(local, "stock-analysis", "stock.db") } // fatal 打印错误并退出。 func fatal(msg string, err error) { fmt.Fprintln(os.Stderr, msg, err) os.Exit(1) }