# 金融研究报告撰写技能 ## 概述 本技能用于协助用户撰写专业的A股上市公司投资研究报告,涵盖数据获取、分析和文档格式化等全流程。 ## 适用场景 - 撰写上市公司周报、月报或专项研究报告 - 需要使用Wind金融终端获取实时或历史数据 - 生成符合中国国家标准的Word格式文档 - 技术分析与基本面分析相结合的投资报告 ## 核心能力 ### 1. 数据获取(WindPy SDK) #### 连接Wind终端 ```python from WindPy import w w.start() data = w.wsd("300866.SZ", "close", "2025-11-01", "2026-02-06") w.stop() ``` #### 常用数据函数 - `w.wsd()` - 日线序列数据 - `w.wsi()` - 分钟线数据 - `w.wss()` - 截面数据(最新值) - `wsd` 常用字段: - `close` - 收盘价 - `open`/`high`/`low` - 开盘/最高/最低价 - `volume`/`amt` - 成交量/成交额 - `pct_chg` - 涨跌幅 - `ma5`, `ma10`, `ma20`, `ma60` - 均线 - `roe` - 净资产收益率 - `pe_ttm`, `pb_lf` - 市盈率/市净率 #### 技术指标计算 - 移动平均线:MA5, MA10, MA20, MA60 - 波动率:使用标准差计算年化波动率 - 支撑位/阻力位:基于高低点分析 ### 2. Word文档格式化(python-docx) #### 国家标准 GB/T 9704-2012 **页面设置** ```python from docx.shared import Mm section.top_margin = Mm(37) section.bottom_margin = Mm(35) section.left_margin = Mm(28) section.right_margin = Mm(26) ``` **字体规范** ```python def set_font(run, font_name, size=None, bold=None): run.font.name = font_name run.font.size = Pt(size) if size else None if bold is not None: run.font.bold = bold # 设置中文字体 run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) # 三号标题:16pt # 四号正文:14pt # 小四表格:12pt ``` **字号对照表** - 三号:16pt - 四号:14pt - 小四:12pt - 五号:10.5pt **行距设置** ```python paragraph.paragraph_format.line_spacing = Pt(28) # 正文 paragraph.paragraph_format.line_spacing = Pt(25) # 小标题 paragraph.paragraph_format.line_spacing_rule = 2 # 固定值 ``` **段落格式** ```python # 首行缩进2字符(28pt) paragraph.paragraph_format.first_line_indent = Pt(28) # 两端对齐 paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY # 段前段后距 paragraph.paragraph_format.space_before = Pt(0) paragraph.paragraph_format.space_after = Pt(0) ``` #### 三线表格式 ```python def set_table_borders(table): tblPr = table._tbl.tblPr tblBorders = OxmlElement('w:tblBorders') # 顶底边框 1.5pt (sz=18) for pos in ['top', 'bottom']: border = OxmlElement(f'w:{pos}') border.set(qn('w:val'), 'single') border.set(qn('w:sz'), '18') border.set(qn('w:color'), 'auto') tblBorders.append(border) # 内部横线 0.75pt (sz=9) for pos in ['insideH']: border = OxmlElement(f'w:{pos}') border.set(qn('w:val'), 'single') border.set(qn('w:sz'), '9') border.set(qn('w:color'), 'auto') tblBorders.append(border) # 无左右和竖线 for pos in ['left', 'right', 'insideV']: border = OxmlElement(f'w:{pos}') border.set(qn('w:val'), 'none') tblBorders.append(border) tblPr.append(tblBorders) ``` #### 页脚页码 ```python from docx.enum.text import WD_ALIGN_PARAGRAPH footer = section.footer p = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER r1 = p.add_run('—') set_font(r1, '仿宋', 12) r2 = p.add_run() # 页码占位 set_font(r2, '仿宋', 12) r3 = p.add_run(' ') set_font(r3, '仿宋', 12) r4 = p.add_run() # 页码占位 set_font(r4, '仿宋', 12) r5 = p.add_run('—') set_font(r5, '仿宋', 12) ``` ### 3. 报告结构标准 #### 完整报告结构(可选封面版) 1. **封面页** - 标题:黑体三号(16pt),居中 - 报告机构和日期:黑体三号(16pt),居中 2. **免责声明**(可选) - 标题:黑体三号(16pt),居中 - 内容:仿宋四号(14pt),两端对齐 3. **核心提要** - 标题:黑体三号(16pt),居中 - 内容:3-4个要点,仿宋四号(14pt),首行缩进2字符 4. **一、投资评级** - 标题:黑体三号(16pt) - 评级表格:小四(12pt),三线表 - 内容:投资建议、目标价格、有效期、风险等级 5. **二、核心观点** - 标题:黑体三号(16pt) - 内容:基本面+技术面分析+投资建议 6. **三、市场表现回顾** - 标题:黑体三号(16pt) - (一)价格走势分析:黑体三号(16pt) - (二)K线数据表:标题黑体小四(12pt) #### 简化报告结构(无封面) - 核心提要 → 一、投资评级 → 二、核心观点 → 三、市场表现回顾 - 不使用分页符,保持内容连贯 ### 4. 内容撰写要点 #### 核心提要 - 突出关键数据(ROE、涨跌幅等) - 简明扼要,3-4个要点 - 包含明确的投资建议 #### 核心观点 - 基本面:盈利能力(ROE)、估值水平(PE/PB) - 技术面:价格趋势、均线、支撑阻力位 - 投资建议:具体操作策略 #### 市场表现回顾 - 时间周期:近13周(周线数据) - 价格区间:最高价、最低价、累计涨跌幅 - 数据表格:日期、开盘、最高、最低、收盘、涨跌幅、成交量 ### 5. 数据处理技巧 #### 时间序列对齐 ```python import pandas as pd df = pd.DataFrame(data, columns=['date', 'open', 'high', 'low', 'close']) df['date'] = pd.to_datetime(df['date']) df.sort_values('date', ascending=True) ``` #### 周线数据汇总 ```python df_weekly = df.resample('W').agg({ 'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum' }) ``` #### 波动率计算 ```python import numpy as np returns = df['close'].pct_change().dropna() volatility = returns.std() * np.sqrt(52) * 100 # 年化百分比 ``` ### 6. 常见问题解决 #### 字体显示问题 - 必须同时设置中文字体:`run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)` - 不要只设置 `run.font.name` #### 表格单元格格式 - Cell对象没有 `paragraph_format` 属性 - 需要通过 `cell.paragraphs[0].paragraph_format` 访问 #### Run对象访问 - Run对象没有 `runs` 属性 - 段落的runs通过 `paragraph.runs` 访问 #### 行距单位 - 使用 `Pt()` 设置固定行距(点) - `line_spacing_rule = 2` 表示固定值 ## 使用示例 ### 基本流程 1. 使用WindPy SDK获取股票数据 2. 计算技术指标(MA、波动率等) 3. 分析基本面指标(ROE、PE等) 4. 整理分析结论 5. 使用python-docx生成Word文档 6. 应用GB/T 9704-2012格式规范 7. 保存文档 ### 快速检查清单(Word文档) - [ ] 页面边距:37/35/28/26mm - [ ] 字体:标题黑体三号、正文仿宋四号、表格小四 - [ ] 行距:正文28pt、小标题25pt - [ ] 首行缩进:正文2字符(28pt) - [ ] 三线表:顶底1.5pt、内部0.75pt、无边框 - [ ] 数据来源注脚:右对齐,小四(12pt) ### Excel图表检查清单 详见「图表设置检查清单」表格(第7节末尾) ## 注意事项 1. **字体重要性**:中文文本必须设置 `w:eastAsia` 属性,否则可能显示为默认字体 2. **表格边框**:使用三线表格式,无左右和竖线 3. **段落缩进**:正文段落需要首行缩进,标题不需要 4. **分页控制**:根据需要决定是否分页,简化版通常不分页 5. **数据时效**:注意数据更新日期,避免使用过期数据 6. **投资建议**:提供明确的操作策略,但需提醒风险 ### 7. Excel 图表绘制(openpyxl) 本技能支持生成包含原生 Excel 图表的 .xlsx 文件,图表可在 Excel 中编辑和交互。 #### 基础图表创建 ```python from openpyxl import Workbook from openpyxl.chart import LineChart, BarChart, Reference from openpyxl.chart.axis import DateAxis # 创建工作簿 wb = Workbook() ws = wb.active ws.title = "股价走势" # 写入数据 ws.append(["日期", "收盘价", "成交量"]) for date, close, vol in data: ws.append([date, close, vol]) # 创建折线图(股价走势) chart = LineChart() chart.title = "股价走势" chart.y_axis.title = "价格(元)" chart.x_axis.title = "日期" # 设置数据区域 data_ref = Reference(ws, min_col=2, min_row=1, max_row=len(data)+1) cats_ref = Reference(ws, min_col=1, min_row=2, max_row=len(data)+1) chart.add_data(data_ref, titles_from_data=True) chart.set_categories(cats_ref) # 设置样式 chart.style = 10 chart.height = 10 # 高度(厘米) chart.width = 20 # 宽度(厘米) # 添加到工作表 ws.add_chart(chart, "D2") wb.save("股价走势.xlsx") ``` #### 常用图表类型 **折线图(LineChart)** - 适用:股价走势、均线趋势 - 特点:展示时间序列变化 ```python from openpyxl.chart import LineChart chart = LineChart() chart.add_data(data_ref) chart.set_categories(cats_ref) ``` **柱状图(BarChart)** - 适用:成交量对比、财务数据对比 - 特点:横向展示数据大小 ```python from openpyxl.chart import BarChart chart = BarChart() chart.type = "col" # 垂直柱状图 chart.add_data(data_ref) ``` **组合图表(股价+成交量)** - 适用:K线+成交量组合展示 - 特点:双Y轴,价格+量能 ```python from openpyxl.chart import LineChart, BarChart from openpyxl.chart.series import DataPoint # 主图:股价线 price_chart = LineChart() price_chart.add_data(price_ref, titles_from_data=True) price_chart.y_axis.title = "价格(元)" # 副图:成交量柱状 vol_chart = BarChart() vol_chart.add_data(vol_ref, titles_from_data=True) vol_chart.y_axis.axId = 200 # 设置次要Y轴 vol_chart.y_axis.title = "成交量(手)" # 组合图表 price_chart += vol_chart price_chart.y_axis.crosses = "max" # 成交量轴在右侧 ``` #### 图表样式设置 **线条与颜色规范** ```python # 线条样式 from openpyxl.chart.series import DataPoint from openpyxl.drawing.fill import SolidColorFillProperties from openpyxl.drawing.line import LineProperties series = chart.series[0] series.graphicalProperties.line.solidFill = "4472C4" # 线条颜色 series.graphicalProperties.line.width = 25000 # 线条粗细(EMUs) # 标记点样式 series.marker.symbol = "circle" series.marker.size = 5 ``` **A股颜色规范(涨跌自动着色)** ```python # 柱状图根据涨跌自动设置颜色(红涨绿跌) from openpyxl.drawing.fill import PatternFillProperties, ColorChoice from openpyxl.chart.series import DataPoint # 为每个数据点设置颜色 for i, val in enumerate(values): pt = DataPoint(idx=i) # A股规范:上涨红色,下跌绿色 fill_color = "FF0000" if val >= 0 else "00B050" pt.graphicalProperties = GraphicalProperties( solidFill=fill_color ) series.data_points.append(pt) ``` **网格线与图例** ```python # 网格线设置(研报风格:仅水平主网格线) chart.x_axis.majorGridlines = None # 隐藏X轴网格 chart.y_axis.majorGridlines = None # 或设置为浅灰色虚线 # 图例位置 chart.legend.position = "b" # 底部:b, 右侧:r, 顶部:t, 左侧:l ``` #### 双轴图表(价格+涨跌幅) ```python from openpyxl.chart import LineChart from openpyxl.chart.axis import DateAxis # 主图:股价 price_chart = LineChart() price_chart.add_data(price_ref, titles_from_data=True) price_chart.y_axis.title = "价格(元)" # 副图:涨跌幅(柱状) pct_chart = LineChart() pct_chart.add_data(pct_ref, titles_from_data=True) pct_chart.y_axis.axId = 200 pct_chart.y_axis.title = "涨跌幅(%)" # 组合 price_chart += pct_chart price_chart.y_axis.crosses = "max" ``` #### 坐标轴精修(研报级设置) **数值轴(Y轴)刻度设置** ```python # 设置Y轴最大最小值(避免自动刻度导致的空白过多) chart.y_axis.scaling.min = 10 # 根据实际数据调整 chart.y_axis.scaling.max = 50 # 设置主要刻度单位 chart.y_axis.majorUnit = 5 # 对于大数值(如成交量),建议数据预处理转换为"亿"或"万" # 并在轴标题中注明单位 chart.y_axis.title = "成交量(亿元)" ``` **日期轴(X轴)标签优化** ```python from openpyxl.chart.axis import DateAxis from openpyxl.drawing.text import Paragraph, ParagraphProperties, CharacterProperties # 设置X轴为日期轴 chart.x_axis = DateAxis() chart.x_axis.title = "日期" # 标签间隔(避免数据点过多导致重叠) chart.x_axis.tickLblSkip = 5 # 每5个数据点显示一个标签 # 标签旋转(防止重叠) from copy import deepcopy from openpyxl.drawing.text import RichText rt = RichText(p=[Paragraph( pPr=ParagraphProperties(defRPr=CharacterProperties(sz=900)), endParaRPr=CharacterProperties(sz=900) )]) chart.x_axis.txPr = rt ``` **坐标轴数字格式** ```python # Y轴数字格式(保留2位小数) chart.y_axis.numFmt = '0.00' # 百分比格式 chart.y_axis.numFmt = '0.00%' ``` #### 数据标签与参考线(视觉增强) **数据标签设置** ```python from openpyxl.chart.label import DataLabelList # 显示所有数据点的值 chart.dataLabels = DataLabelList() chart.dataLabels.showVal = True # 仅显示最后一个点(标注最新价)- 需要手动设置 from openpyxl.chart.series import DataPoint last_pt = DataPoint(idx=len(values)-1) last_pt.graphicalProperties = GraphicalProperties(solidFill="FF0000") series.data_points = [last_pt] ``` **参考线(均值/目标价)** ```python from openpyxl.chart.series import SeriesLabel # 添加均值参考线(作为单独的数据系列) mean_value = sum(values) / len(values) ws.append([""] * (len(headers)-1) + ["均值"]) for i in range(len(values)): ws.append([""] * (len(headers)-1) + [mean_value]) mean_ref = Reference(ws, min_col=len(headers), min_row=len(values)+2, max_row=len(values)*2+1) mean_series = Series(mean_ref, title="均值线") chart.series.append(mean_series) ``` #### 财务数据可视化 **营收利润趋势图** ```python # 数据准备 ws.append(["报告期", "营业收入", "净利润"]) for period, revenue, profit in financial_data: ws.append([period, revenue/1e8, profit/1e8]) # 转换为亿元 # 组合图表 chart = BarChart() chart.type = "col" chart.grouping = "clustered" chart.title = "营业收入与净利润" chart.y_axis.title = "金额(亿元)" data = Reference(ws, min_col=2, max_col=3, min_row=1, max_row=len(financial_data)+1) cats = Reference(ws, min_col=1, min_row=2, max_row=len(financial_data)+1) chart.add_data(data, titles_from_data=True) chart.set_categories(cats) # 添加数据标签 chart.dataLabels = DataLabelList() chart.dataLabels.showVal = True ``` **ROE 柱状图** ```python chart = BarChart() chart.type = "col" data = Reference(ws, min_col=2, min_row=1, max_row=5) cats = Reference(ws, min_col=1, min_row=2, max_row=5) chart.add_data(data, titles_from_data=True) chart.set_categories(cats) chart.title = "年度 ROE 对比" ``` #### Excel 文件与 Word 报告配合 **方案A:Excel 作为附件** ```python # 生成带图表的 Excel 文件 wb.save(f"{stock_name}_数据分析.xlsx") # 在 Word 中引用 paragraph = doc.add_paragraph() run = paragraph.add_run(f"详细数据及图表请参见附件《{stock_name}_数据分析.xlsx》") set_font(run, "仿宋", 12) ``` **方案B:图表图片直接插入 Word(推荐)** ```python import matplotlib.pyplot as plt import matplotlib.dates as mdates from docx.shared import Inches # 使用 Matplotlib 生成符合 GB/T 9704 字体要求的图表 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei'] plt.rcParams['axes.unicode_minus'] = False fig, ax = plt.subplots(figsize=(8, 4.5)) ax.plot(dates, prices, color='#4472C4', linewidth=1.5) ax.set_title('股价走势', fontsize=12, fontweight='bold') ax.set_xlabel('日期', fontsize=10) ax.set_ylabel('价格(元)', fontsize=10) ax.grid(True, alpha=0.3) # 保存为图片 fig.savefig('chart.png', dpi=150, bbox_inches='tight') plt.close() # 插入 Word doc.add_picture('chart.png', width=Inches(6)) ``` **方案C:Word 原生图表(功能有限)** ```python from docx.chart import Chart from docx.enum.chart import XL_CHART_TYPE # 注意:docx.chart 功能较弱,仅支持基础图表类型 # 如需复杂图表,建议使用方案A或B ``` #### 图表最佳实践 1. **数据准备**:使用 pandas 预处理数据,确保格式正确 2. **图表选择**: - 时间序列 → 折线图 - 对比分析 → 柱状图 - 价格+成交量 → 组合图 3. **颜色规范**(A股标准): - 上涨:红色(FF0000) - 下跌:绿色(00B050,推荐)或(00AA00) - 中性:蓝色(4472C4) 4. **数据单位**:图表标题或轴标题中注明单位(元、%、亿元等) 5. **数据来源**:图表下方注明数据来源,如"数据来源:Wind" #### 图表设置检查清单 | 检查项 | 专业要求 | 常见错误 | |--------|----------|----------| | **坐标轴标题** | 必须包含单位,如"价格(元)"、"比例(%)" | 漏写单位或单位不统一 | | **色彩方案** | 涨红跌绿(A股规范),多序列使用高对比度商务色 | 使用 Excel 默认全家桶配色 | | **网格线** | 仅保留水平主网格线,且设为浅灰色/虚线,或直接隐藏 | 粗黑实线网格,遮盖趋势线 | | **字体统一** | 图表内字体需与 Word 正文匹配(建议微软雅黑或等线,9pt) | 默认小号宋体,缩放后模糊 | | **数据源标注** | 图表下方须注明:数据来源:Wind,XX证券研究所 | 来源不明 | | **标签间隔** | X轴数据点密集时需设置 `tickLblSkip` 防止重叠 | 标签重叠难以辨认 | | **数值精度** | Y轴根据数据类型设置合适的小数位(价格2位,百分比1-2位) | 小数位过多或过少 | #### 高度封装工具函数 工具函数已封装在 `scripts/chart_utils.py` 中,使用时直接导入: ```python import sys sys.path.insert(0, ".claude/skills/financial-report-writing/scripts") from chart_utils import create_research_chart, create_price_volume_chart import pandas as pd # 准备数据 df = pd.DataFrame({ "date": ["2024-01", "2024-02", "2024-03"], "close": [10.5, 11.2, 10.8] }) # 生成股价走势图 wb = create_research_chart( df, chart_type="line", title="股价走势", x_col="date", y_cols="close", y_axis_title="价格(元)", show_last_label_only=True, add_mean_line=True, output_path="股价走势.xlsx" ) ``` **可用函数** | 函数 | 用途 | |------|------| | `create_research_chart()` | 通用图表(折线/柱状/多序列) | | `create_price_volume_chart()` | 股价+成交量双Y轴组合图 | **`create_research_chart()` 主要参数** | 参数 | 说明 | 默认值 | |------|------|--------| | `df` | DataFrame数据源 | 必填 | | `chart_type` | 图表类型:"line"/"bar" | "line" | | `x_col` | X轴列名 | 必填 | | `y_cols` | Y轴列名(支持列表) | 必填 | | `y_axis_title` | Y轴标题(建议带单位) | "" | | `y_format` | 数字格式 | "0.00" | | `show_last_label_only` | 仅显示最后一个标签 | False | | `add_mean_line` | 添加均值参考线 | False | | `color_up/color_down` | 涨跌颜色 | FF0000/00B050 | | `tick_skip` | X轴标签间隔 | 5 | 完整函数定义和源码见:`scripts/chart_utils.py` ## 相关工具 - **WindPy**:Wind金融终端Python API - **python-docx**:Word文档操作库 - **openpyxl**:Excel文件操作,支持原生图表 - **pandas**:数据处理和分析 - **numpy**:数值计算 - **GB/T 9704-2012**:党政机关公文格式标准 ## 参考资源 - WindPy SDK官方文档 - python-docx API文档 - GB/T 9704-2012国家公文格式标准 - 券商研究报告格式规范