From 84e3dbf4018d0bc2225b4ea0faf0a355f1351079 Mon Sep 17 00:00:00 2001 From: yanweidong Date: Mon, 2 Feb 2026 12:17:37 +0800 Subject: [PATCH] deving --- internal/cron/boot.go | 11 +- internal/logic/a/markdown.go | 467 +++++++++++++++++++++++++++ internal/logic/a/markdown_builder.go | 79 +++++ 3 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 internal/logic/a/markdown.go create mode 100644 internal/logic/a/markdown_builder.go diff --git a/internal/cron/boot.go b/internal/cron/boot.go index a9c28fa..ecb5233 100644 --- a/internal/cron/boot.go +++ b/internal/cron/boot.go @@ -1,14 +1,21 @@ package cron import ( + "os" + + "git.apinb.com/bsm-sdk/core/utils" "git.apinb.com/dataset/stock/internal/logic/a" "github.com/robfig/cron/v3" ) func Boot() { + if !utils.PathExists("./markdata/") { + os.MkdirAll("./markdata/", 0755) + } + scheduler := cron.New() // 每天运行3次来更新数据 - scheduler.AddFunc("* 1,20,22 * * *", func() { + scheduler.AddFunc("30 21 * * *", func() { updateTask() }) scheduler.Start() @@ -24,4 +31,6 @@ func updateTask() { a.GetStockIndicator() // 获取股票财务指标数据 a.GetFinaIndicator() + // 生成Markdown文件 + a.GenMarkData() } diff --git a/internal/logic/a/markdown.go b/internal/logic/a/markdown.go new file mode 100644 index 0000000..b2ebf9a --- /dev/null +++ b/internal/logic/a/markdown.go @@ -0,0 +1,467 @@ +package a + +import ( + "fmt" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" +) + +func GenMarkData() { + for _, code := range GetStockCodes() { + md := NewMarkdownBuilder() + + var basic models.StockBasic + impl.DBService.Where("ts_code = ?", code).First(&basic) + + md.Title(basic.TsCode + " " + basic.Name + " 股票详情") + md.Catalog("公司基础情况") + md.KvText("股票代码", basic.TsCode) + md.KvText("股票名称", basic.Name) + md.KvText("股票行业", basic.Industry) + md.KvText("股票地域", basic.Area) + md.KvText("上市日期", basic.ListDate) + md.KvText("股票市场", basic.Market) + md.KvText("股票类型", basic.Market) + md.KvText("股票是否沪深港通", basic.IsHS) + md.KvText("股票中文全称", basic.FullName) + md.KvText("股票实控人", basic.ActName) + md.KvText("股票实控人企业性质", basic.ActEntType) + md.BR() + + var daily []models.StockDaily + impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(200).Find(&daily) + var data [][]string + for _, row := range daily { + data = append(data, []string{ + utils.Int2String(row.TradeDate), + fmt.Sprintf("%.2f", row.Open), + fmt.Sprintf("%.2f", row.Close), + fmt.Sprintf("%.2f", row.High), + fmt.Sprintf("%.2f", row.Low), + fmt.Sprintf("%.2f", row.Vol), + fmt.Sprintf("%.2f", row.Amount), + }) + } + md.Table("最近200天股票行情", []string{"日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"}, data) + md.BR() + + var indicator []models.StockIndicator + impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(200).Find(&indicator) + var indiData [][]string + for _, row := range indicator { + indiData = append(indiData, []string{ + utils.Int2String(row.TradeDate), + fmt.Sprintf("%.2f", row.Close), + fmt.Sprintf("%.2f%%", row.TurnoverRate), + fmt.Sprintf("%.2f", row.VolumeRatio), + fmt.Sprintf("%.2f", row.Pe), + fmt.Sprintf("%.2f", row.PeTtm), + fmt.Sprintf("%.2f", row.Pb), + fmt.Sprintf("%.2f", row.Ps), + fmt.Sprintf("%.2f", row.PsTtm), + fmt.Sprintf("%.2f", row.DvRatio), + fmt.Sprintf("%.2f", row.DvTtm), + fmt.Sprintf("%.2f", row.TotalShare), + fmt.Sprintf("%.2f", row.FloatShare), + fmt.Sprintf("%.2f", row.FreeShare), + fmt.Sprintf("%.2f", row.TotalMv), + fmt.Sprintf("%.2f", row.CircMv), + }) + } + md.Table("最近200天股票指标", []string{"日期", "当日收盘价", "换手率", "量比", "市盈率", "市盈率 TTM", "市净率", "市销率", "市销率 TTM", "股息率", "股息率 TTM", "总股本", "流通股本", "自由流通股本", "总市值", "流通市值"}, indiData) + md.BR() + + var finaIndicator []models.StockFinaIndicator + impl.DBService.Where("ts_code = ?", code).Order("period desc").Find(&finaIndicator) + var fiHeaders []string = []string{ + "报告期数", + "公告日期", + "报告期", + + // 每股指标 + "基本每股收益", + "稀释每股收益", + "每股营业总收入", + "每股营业收入", + "每股资本公积", + "每股盈余公积", + "每股未分配利润", + "期末摊薄每股收益", + "每股净资产", + "每股经营活动产生的现金流量净额", + "每股留存收益", + "每股现金流量净额", + "每股息税前利润", + "每股企业自由现金流量", + "每股股东自由现金流量", + + // 利润表相关 + "非经常性损益", + "扣除非经常性损益后的净利润", + "毛利", + "经营活动净收益", + "价值变动净收益", + "利息费用", + "折旧与摊销", + "息税前利润", + "息税折旧摊销前利润", + "企业自由现金流量", + "股权自由现金流量", + "研发费用", + "固定资产合计", + "扣除财务费用前营业利润", + "非营业利润", + + // 资产负债表相关 + "无息流动负债", + "无息非流动负债", + "带息债务", + "净债务", + "有形资产", + "营运资金", + "营运流动资本", + "全部投入资本", + "留存收益", + + // 偿债能力指标 + "流动比率", + "速动比率", + "保守速动比率", + "资产负债率", + "权益乘数", + "权益乘数(杜邦分析)", + "产权比率", + "归属于母公司的股东权益/负债合计", + "经营活动产生的现金流量净额/流动负债", + "已获利息倍数", + + // 运营能力指标 + "存货周转天数", + "应收账款周转天数", + "存货周转率", + "应收账款周转率", + "流动资产周转率", + "固定资产周转率", + "总资产周转率", + "营业周期", + + // 盈利能力指标 + "销售净利率", + "销售毛利率", + "销售成本率", + "销售期间费用率", + "净资产收益率", + "加权平均净资产收益率", + "净资产收益率(扣除非经常损益)", + "总资产报酬率", + "总资产净利润", + "投入资本回报率", + "总资产净利率(杜邦分析)", + + // 结构指标 + "流动资产/总资产", + "非流动资产/总资产", + "有形资产/总资产", + "带息债务/全部投入资本", + "归属于母公司的股东权益/全部投入资本", + "流动负债/负债合计", + "非流动负债/负债合计", + "有形资产/负债合计", + + // 单季度指标 + "经营活动单季度净收益", + "价值变动单季度净收益", + "扣除非经常损益后的单季度净利润", + "每股收益(单季度)", + "销售净利率(单季度)", + "销售毛利率(单季度)", + "销售期间费用率(单季度)", + "净资产收益率(单季度)", + "净资产单季度收益率(扣除非经常损益)", + "总资产净利润(单季度)", + + // 同比增长率 + "基本每股收益同比增长率(%)", + "稀释每股收益同比增长率(%)", + "每股经营活动产生的现金流量净额同比增长率(%)", + "营业利润同比增长率(%)", + "利润总额同比增长率(%)", + "归属母公司股东的净利润同比增长率(%)", + "归属母公司股东的净利润-扣除非经常损益同比增长率(%)", + "经营活动产生的现金流量净额同比增长率(%)", + "净资产收益率(摊薄)同比增长率(%)", + "每股净资产相对年初增长率(%)", + "资产总计相对年初增长率(%)", + "归属母公司的股东权益相对年初增长率(%)", + "营业总收入同比增长率(%)", + "营业收入同比增长率(%)", + "净资产同比增长率", + + // 其他比率指标 + "净利润/营业总收入", + "销售费用/营业总收入", + "管理费用/营业总收入", + "财务费用/营业总收入", + "资产减值损失/营业总收入", + "营业总成本/营业总收入", + "营业利润/营业总收入", + "息税前利润/营业总收入", + "经营活动净收益/利润总额", + "价值变动净收益/利润总额", + "营业外收支净额/利润总额", + "所得税/利润总额", + "扣除非经常损益后的净利润/净利润", + "销售商品提供劳务收到的现金/营业收入", + "经营活动产生的现金流量净额/营业收入", + "经营活动产生的现金流量净额/经营活动净收益", + "资本支出/折旧和摊销", + "经营活动产生的现金流量净额/负债合计", + "经营活动产生的现金流量净额/带息债务", + "经营活动产生的现金流量净额/净债务", + "长期债务与营运资金比率", + "息税折旧摊销前利润/负债合计", + "营业利润/利润总额", + "非营业利润/利润总额", + "经营活动产生的现金流量净额/营业利润", + "货币资金/流动负债", + "货币资金/带息流动负债", + "营业利润/流动负债", + "营业利润/负债合计", + "利润总额/营业收入", + + // 年度化指标 + "年化净资产收益率", + "年化总资产报酬率", + "年化总资产净利率", + "年化投入资本回报率", + "平均净资产收益率(增发条件)", + + // 单季度增长比率 + "营业总收入同比增长率(%)(单季度)", + "营业总收入环比增长率(%)(单季度)", + "营业收入同比增长率(%)(单季度)", + "营业收入环比增长率(%)(单季度)", + "营业利润同比增长率(%)(单季度)", + "营业利润环比增长率(%)(单季度)", + "净利润同比增长率(%)(单季度)", + "净利润环比增长率(%)(单季度)", + "归属母公司股东的净利润同比增长率(%)(单季度)", + "归属母公司股东的净利润环比增长率(%)(单季度)", + + // 单季度比率指标 + "净利润/营业总收入(单季度)", + "销售费用/营业总收入 (单季度)", + "管理费用/营业总收入 (单季度)", + "财务费用/营业总收入 (单季度)", + "资产减值损失/营业总收入(单季度)", + "营业总成本/营业总收入 (单季度)", + "营业利润/营业总收入(单季度)", + "经营活动净收益/利润总额(单季度)", + "价值变动净收益/利润总额(单季度)", + "扣除非经常损益后的净利润/净利润(单季度)", + "销售商品提供劳务收到的现金/营业收入(单季度)", + "经营活动产生的现金流量净额/营业收入(单季度)", + "经营活动产生的现金流量净额/经营活动净收益(单季度)", + } + var fiData [][]string + for _, row := range finaIndicator { + fiData = append(fiData, []string{ + fmt.Sprintf("%d", row.Period), + row.AnnDate, + row.EndDate, + + // 每股指标 + fmt.Sprintf("%.2f", row.Eps), + fmt.Sprintf("%.2f", row.DtEps), + fmt.Sprintf("%.2f", row.TotalRevenuePs), + fmt.Sprintf("%.2f", row.RevenuePs), + fmt.Sprintf("%.2f", row.CapitalResePs), + fmt.Sprintf("%.2f", row.SurplusResePs), + fmt.Sprintf("%.2f", row.UndistProfitPs), + fmt.Sprintf("%.2f", row.Diluted2Eps), + fmt.Sprintf("%.2f", row.Bps), + fmt.Sprintf("%.2f", row.Ocfps), + fmt.Sprintf("%.2f", row.Retainedps), + fmt.Sprintf("%.2f", row.Cfps), + fmt.Sprintf("%.2f", row.EbitPs), + fmt.Sprintf("%.2f", row.FcffPs), + fmt.Sprintf("%.2f", row.FcfePs), + + // 利润表相关 + fmt.Sprintf("%.2f", row.ExtraItem), + fmt.Sprintf("%.2f", row.ProfitDedt), + fmt.Sprintf("%.2f", row.GrossMargin), + fmt.Sprintf("%.2f", row.OpIncome), + fmt.Sprintf("%.2f", row.ValuechangeIncome), + fmt.Sprintf("%.2f", row.InterstIncome), + fmt.Sprintf("%.2f", row.Daa), + fmt.Sprintf("%.2f", row.Ebit), + fmt.Sprintf("%.2f", row.Ebitda), + fmt.Sprintf("%.2f", row.Fcff), + fmt.Sprintf("%.2f", row.Fcfe), + fmt.Sprintf("%.2f", row.RdExp), + fmt.Sprintf("%.2f", row.FixedAssets), + fmt.Sprintf("%.2f", row.ProfitPrefinExp), + fmt.Sprintf("%.2f", row.NonOpProfit), + + // 资产负债表相关 + fmt.Sprintf("%.2f", row.CurrentExint), + fmt.Sprintf("%.2f", row.NoncurrentExint), + fmt.Sprintf("%.2f", row.Interestdebt), + fmt.Sprintf("%.2f", row.Netdebt), + fmt.Sprintf("%.2f", row.TangibleAsset), + fmt.Sprintf("%.2f", row.WorkingCapital), + fmt.Sprintf("%.2f", row.NetworkingCapital), + fmt.Sprintf("%.2f", row.InvestCapital), + fmt.Sprintf("%.2f", row.RetainedEarnings), + + // 偿债能力指标 + fmt.Sprintf("%.2f", row.CurrentRatio), + fmt.Sprintf("%.2f", row.QuickRatio), + fmt.Sprintf("%.2f", row.CashRatio), + fmt.Sprintf("%.2f", row.DebtToAssets), + fmt.Sprintf("%.2f", row.AssetsToEqt), + fmt.Sprintf("%.2f", row.DpAssetsToEqt), + fmt.Sprintf("%.2f", row.DebtToEqt), + fmt.Sprintf("%.2f", row.EqtToDebt), + fmt.Sprintf("%.2f", row.OcfToShortdebt), + fmt.Sprintf("%.2f", row.EbitToInterest), + + // 运营能力指标 + fmt.Sprintf("%.2f", row.InvturnDays), + fmt.Sprintf("%.2f", row.ArturnDays), + fmt.Sprintf("%.2f", row.InvTurn), + fmt.Sprintf("%.2f", row.ArTurn), + fmt.Sprintf("%.2f", row.CaTurn), + fmt.Sprintf("%.2f", row.FaTurn), + fmt.Sprintf("%.2f", row.AssetsTurn), + fmt.Sprintf("%.2f", row.TurnDays), + fmt.Sprintf("%.2f", row.TotalFaTrun), + + // 盈利能力指标 + fmt.Sprintf("%.2f", row.NetprofitMargin), + fmt.Sprintf("%.2f", row.GrossprofitMargin), + fmt.Sprintf("%.2f", row.CogsOfSales), + fmt.Sprintf("%.2f", row.ExpenseOfSales), + fmt.Sprintf("%.2f", row.Roe), + fmt.Sprintf("%.2f", row.RoeWaa), + fmt.Sprintf("%.2f", row.RoeDt), + fmt.Sprintf("%.2f", row.Roa), + fmt.Sprintf("%.2f", row.Npta), + fmt.Sprintf("%.2f", row.Roic), + fmt.Sprintf("%.2f", row.RoaDp), + + // 结构指标 + fmt.Sprintf("%.2f", row.CaToAssets), + fmt.Sprintf("%.2f", row.NcaToAssets), + fmt.Sprintf("%.2f", row.TbassetsToTotalassets), + fmt.Sprintf("%.2f", row.IntToTalcap), + fmt.Sprintf("%.2f", row.EqtToTalcapital), + fmt.Sprintf("%.2f", row.CurrentdebtToDebt), + fmt.Sprintf("%.2f", row.LongdebToDebt), + fmt.Sprintf("%.2f", row.TangibleassetToDebt), + + // 单季度指标 + fmt.Sprintf("%.2f", row.QOpincome), + fmt.Sprintf("%.2f", row.QInvestincome), + fmt.Sprintf("%.2f", row.QDtprofit), + fmt.Sprintf("%.2f", row.QEps), + fmt.Sprintf("%.2f", row.QNetprofitMargin), + fmt.Sprintf("%.2f", row.QGscaleprofitMargin), + fmt.Sprintf("%.2f", row.QExpToSales), + fmt.Sprintf("%.2f", row.QRoe), + fmt.Sprintf("%.2f", row.QDtRoe), + fmt.Sprintf("%.2f", row.QNpta), + + // 同比增长率 + fmt.Sprintf("%.2f%%", row.BasicEpsYoy), + fmt.Sprintf("%.2f%%", row.DtEpsYoy), + fmt.Sprintf("%.2f%%", row.CfpsYoy), + fmt.Sprintf("%.2f%%", row.OpYoy), + fmt.Sprintf("%.2f%%", row.EbtYoy), + fmt.Sprintf("%.2f%%", row.NetprofitYoy), + fmt.Sprintf("%.2f%%", row.DtNetprofitYoy), + fmt.Sprintf("%.2f%%", row.OcfYoy), + fmt.Sprintf("%.2f%%", row.RoeYoy), + fmt.Sprintf("%.2f%%", row.BpsYoy), + fmt.Sprintf("%.2f%%", row.AssetsYoy), + fmt.Sprintf("%.2f%%", row.EqtYoy), + fmt.Sprintf("%.2f%%", row.TrYoy), + fmt.Sprintf("%.2f%%", row.OrYoy), + fmt.Sprintf("%.2f%%", row.EquityYoy), + + // 其他比率指标 + fmt.Sprintf("%.2f", row.ProfitToGr), + fmt.Sprintf("%.2f", row.SaleexpToGr), + fmt.Sprintf("%.2f", row.AdminexpOfGr), + fmt.Sprintf("%.2f", row.FinaexpOfGr), + fmt.Sprintf("%.2f", row.ImpaiTtm), + fmt.Sprintf("%.2f", row.GcOfGr), + fmt.Sprintf("%.2f", row.OpOfGr), + fmt.Sprintf("%.2f", row.EbitOfGr), + fmt.Sprintf("%.2f", row.OpincomeOfEbt), + fmt.Sprintf("%.2f", row.InvestincomeOfEbt), + fmt.Sprintf("%.2f", row.NOpProfitOfEbt), + fmt.Sprintf("%.2f", row.TaxToEbt), + fmt.Sprintf("%.2f", row.DtprofitToProfit), + fmt.Sprintf("%.2f", row.SalescashToOr), + fmt.Sprintf("%.2f", row.OcfToOr), + fmt.Sprintf("%.2f", row.OcfToOpincome), + fmt.Sprintf("%.2f", row.CapitalizedToDa), + fmt.Sprintf("%.2f", row.OcfToDebt), + fmt.Sprintf("%.2f", row.OcfToInterestdebt), + fmt.Sprintf("%.2f", row.OcfToNetdebt), + fmt.Sprintf("%.2f", row.LongdebtToWorkingcapital), + fmt.Sprintf("%.2f", row.EbitdaToDebt), + fmt.Sprintf("%.2f", row.OpToEbt), + fmt.Sprintf("%.2f", row.NopToEbt), + fmt.Sprintf("%.2f", row.OcfToProfit), + fmt.Sprintf("%.2f", row.CashToLiqdebt), + fmt.Sprintf("%.2f", row.CashToLiqdebtWithinterest), + fmt.Sprintf("%.2f", row.OpToLiqdebt), + fmt.Sprintf("%.2f", row.OpToDebt), + fmt.Sprintf("%.2f", row.ProfitToOp), + + // 年度化指标 + fmt.Sprintf("%.2f", row.RoeYearly), + fmt.Sprintf("%.2f", row.Roa2Yearly), + fmt.Sprintf("%.2f", row.RoaYearly), + fmt.Sprintf("%.2f", row.RoicYearly), + fmt.Sprintf("%.2f", row.RoeAvg), + + // 单季度增长比率 + fmt.Sprintf("%.2f%%", row.QGrYoy), + fmt.Sprintf("%.2f%%", row.QGrQoq), + fmt.Sprintf("%.2f%%", row.QSalesYoy), + fmt.Sprintf("%.2f%%", row.QSalesQoq), + fmt.Sprintf("%.2f%%", row.QOpYoy), + fmt.Sprintf("%.2f%%", row.QOpQoq), + fmt.Sprintf("%.2f%%", row.QProfitYoy), + fmt.Sprintf("%.2f%%", row.QProfitQoq), + fmt.Sprintf("%.2f%%", row.QNetprofitYoy), + fmt.Sprintf("%.2f%%", row.QNetprofitQoq), + + // 单季度比率指标 + fmt.Sprintf("%.2f", row.QProfitToGr), + fmt.Sprintf("%.2f", row.QSaleexpToGr), + fmt.Sprintf("%.2f", row.QAdminexpToGr), + fmt.Sprintf("%.2f", row.QFinaexpToGr), + fmt.Sprintf("%.2f", row.QImpairToGrTtm), + fmt.Sprintf("%.2f", row.QGcToGr), + fmt.Sprintf("%.2f", row.QOpToGr), + fmt.Sprintf("%.2f", row.QOpincomeToEbt), + fmt.Sprintf("%.2f", row.QInvestincomeToEbt), + fmt.Sprintf("%.2f", row.QDtprofitToProfit), + fmt.Sprintf("%.2f", row.QSalescashToOr), + fmt.Sprintf("%.2f", row.QOcfToSales), + fmt.Sprintf("%.2f", row.QOcfToOr), + }) + } + md.Table("最近3年的股票财务指标", fiHeaders, fiData) + md.BR() + + md.SaveToFile("./markdata/" + code + ".md") + } +} diff --git a/internal/logic/a/markdown_builder.go b/internal/logic/a/markdown_builder.go new file mode 100644 index 0000000..cb2b178 --- /dev/null +++ b/internal/logic/a/markdown_builder.go @@ -0,0 +1,79 @@ +package a + +import ( + "bytes" + "io" + "os" + "strings" +) + +type MarkdownBuilder struct { + buf bytes.Buffer +} + +func NewMarkdownBuilder() *MarkdownBuilder { + return &MarkdownBuilder{} +} +func (m *MarkdownBuilder) Title(title string) { + m.buf.WriteString("# " + title + "\n\n") +} + +func (m *MarkdownBuilder) Catalog(text string) { + m.buf.WriteString("## " + text + "\n\n") +} +func (m *MarkdownBuilder) Text(text string) { + m.buf.WriteString(text + "\n\n") +} + +func (m *MarkdownBuilder) KvText(key, val string) { + m.buf.WriteString(key + " :" + val + " \n") +} + +func (m *MarkdownBuilder) BR() { + m.buf.WriteString("\n") +} + +func (m *MarkdownBuilder) Code(lang, code string) { + m.buf.WriteString("```" + lang + "\n") + m.buf.WriteString(code + "\n") + m.buf.WriteString("```\n\n") +} + +// 生成Markdown表格 +// | Header1 | Header2 | +// |---------|---------| +// | Cell1 | Cell2 | +func (m *MarkdownBuilder) 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 *MarkdownBuilder) Build() string { + return m.buf.String() +} + +func (m *MarkdownBuilder) 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() + + // 将buffer内容复制到文件 + _, err = io.Copy(rf, &m.buf) + if err != nil { + return err + } + return nil +}