# StockPython **Repository Path**: yuejianli/StockPython ## Basic Information - **Project Name**: StockPython - **Description**: 股票量化, Python版本 - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-09-05 - **Last Updated**: 2026-09-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # StockPython 股票量化(Python 版)。数据统一以 **CSV** 形式保存在项目根目录的 `data/` 下,不使用数据库。 ## 目录结构 ``` StockPython/ ├── streamlit_app.py # Streamlit 主入口(菜单导航) ├── run_streamlit.bat # Windows 一键启动脚本 ├── views/ # Streamlit 页面(菜单都在这里) │ ├── components.py # 公共组件(实时行情 / K 线 / 分时图 / 日K图 / 提醒弹窗、涨跌配色) │ ├── stock_list.py # 【股票列表】菜单页 │ ├── index_board.py # 首页指数行情看板(非页面,被股票列表页 import) │ ├── realtime_snapshot.py # 【实时数据快照】菜单页 │ ├── selected_list.py # 【自选列表维护】菜单页 │ ├── price_alert.py # 【价格提醒】菜单页 │ ├── strategy_screen.py # 【初步策略】菜单页(基于每日快照的多因子选股) │ ├── week_top.py # 【周涨幅榜】菜单页 │ ├── month_top.py # 【月涨幅榜】菜单页 │ ├── top_list_view.py # 涨跌榜公共渲染(非页面,被上面两个页面共用) │ ├── open_position.py # 【建仓】菜单页(买入成本价 / 手续费 / 保本价) │ ├── add_position.py # 【补仓】菜单页(新平均成本 / 摊薄反推需补股数) │ ├── close_position.py # 【清仓】菜单页(盈亏 / 实收 / 费用明细) │ └── trade_calc_common.py # 交易计算共用 UI(非页面,被上面三个页面共用) ├── data/ # 数据目录,所有 CSV 落盘位置 │ ├── stock.csv # 全量股票列表(本次生成) │ ├── realtime/ # 每日实时行情快照 │ │ └── 20260904.csv # 按交易日命名 │ ├── history/ # 个股历史行情(日线) │ │ └── 000001.csv # 按股票代码命名 │ ├── selected.csv # 自选股清单(6 位代码,不重复) │ ├── alerts.csv # 价格提醒规则 │ └── rank/ # 周/月涨跌幅榜单存档 │ └── week_20260907.csv # 按 周期_数据日期 命名 │ ├── announcements/ # 深交所个股公告(按股票代码缓存,查询兜底用) │ │ └── 300750.csv # 按股票代码命名 │ └── holiday/ # 节假日(休市)日历存档 │ └── 2026.csv # 按年份命名 ├── scripts/ # 命令行入口 │ ├── fetch_stock_list.py # 拉取全量股票列表 -> data/stock.csv │ ├── fetch_market_snapshot.py # 抓取全市场实时行情 -> data/realtime/YYYYMMDD.csv │ ├── fetch_history.py # 抓取个股历史行情 -> data/history/{代码}.csv │ ├── fetch_top_list.py # 抓取周/月涨跌幅榜 -> data/rank/{周期}_{日期}.csv │ ├── fetch_market_overview.py # 抓取云端实时行情总览(yueshushu.top)并打印 JSON │ ├── fetch_holiday_calendar.py # 抓取指定年份节假日(休市)列表 -> data/holiday/{year}.csv │ ├── fetch_stock_announcement.py # 抓取深交所个股公告 -> data/announcements/{代码}.csv │ └── check_price_alerts.py # 检查价格提醒并发送邮件(定时任务用) ├── stockquant/ # 核心包(零第三方依赖) │ ├── config.py # 全局配置(API Key、SMTP、CSV 编码等) │ ├── client.py # HTTP 客户端(鉴权 / 超时 / 重试 / 分页) │ ├── trade_calc.py # 交易费用 / 持仓成本计算(建仓、补仓、清仓,纯计算无依赖) │ ├── fetchers/ # 各接口抓取逻辑 │ │ ├── stock_list.py # 股票列表(同花顺) │ │ ├── realtime_quote.py # 单只实时行情(腾讯 qt.gtimg.cn) │ │ ├── realtime_snapshot.py # 全市场快照(腾讯批量) │ │ ├── history_price.py # 个股历史行情(同花顺) │ │ ├── selected_stock.py # 自选股清单(本地维护) │ │ ├── price_alert.py # 价格提醒规则 + 触发判定(本地维护) │ │ ├── index_quote.py # 指数实时快照(同花顺 Fuyao,含常用指数名称表) │ │ ├── top_list.py # 周/月涨跌幅榜(yueshushu.top 接口) │ │ ├── market_overview.py # 实时行情总览(云端 yueshushu.top /ths/nowDb,同花顺被反爬后改用) │ │ ├── holiday_calendar.py # 节假日(休市)日历(云端 yueshushu.top /holidayCalendar/list,POST) │ │ └── stock_announcement.py # 深交所个股公告(szse.cn /api/disc/announcement/annList,POST)+ AI 利好/利空判断提示词生成 │ ├── notifier/ # 通知通道 │ │ └── email_sender.py # 邮件发送(SMTP SSL 465)+ 提醒邮件模板 │ └── storage/ # 存储层 │ └── csv_store.py # CSV 读写(原子写、utf-8-sig) ├── 文档目录/ # 课程 / 文档索引 ├── 课程文档/ # 各章节课程文档 └── requirements.txt # 抓取流程零依赖;streamlit / pandas 仅界面层需要 ``` ## 环境要求 - 抓取流程:Python 3.8+,零第三方依赖(标准库 `urllib` + `csv` 即可运行)。 - Streamlit 界面:需要 `streamlit>=1.34`、`pandas>=1.5`、`plotly>=5`(画 K 线图)。 本机 Python 3.10.9 已装 streamlit 1.58、pandas 2.0.3、plotly 6.7.0。 ## 启动界面(Streamlit) ```bash # 方式一:双击项目根目录下的 run_streamlit.bat # 方式二:命令行启动 cd Z:\AStockNew\StockPython D:\Python310\python.exe -m streamlit run streamlit_app.py ``` 启动后访问 http://localhost:8501 ,左侧为菜单导航。 > 开发时建议加上 `--server.runOnSave true`,改动代码后页面会自动刷新,不用手动重启服务(`run_streamlit.bat` 里已默认带上)。 | 页面 | 功能 | | --- | --- | | **📋 股票列表** | 顶部「📊 指数行情」看板(同花顺 Fuyao 实时指数,卡片含点位/涨跌额/振幅位置条/成交额,支持自定义代码);下方全量列表的同步、筛选、分页浏览,每行可查看实时行情、同步并查看历史 K 线 | | **📸 实时数据快照** | 批量抓取全市场实时行情并按交易日存档,支持涨跌统计、排序、筛选,每行可查看实时行情 | | **📡 实时行情数据** | 云端 yueshushu.top 大盘总览:大盘情绪评分与文案、昨日涨停今日收益、上涨/下跌家数对比、涨停/跌停家数、涨跌幅分布 10 档,每 30 秒自动刷新,可看 AI 盘面解读与原始 JSON | | **📆 节假日查询** | 云端 yueshushu.top 休市日历:选择年份(默认当前年),展示当年休市天数、各月分布柱状图与完整节假日列表(日期/星期/类型),数据落盘 `data/holiday/{year}.csv` | | **📢 股票公告** | 深交所(SZSE)个股公告:输入股票编码查近 N 个月上市公司公告(标题/日期/名称/原文 PDF 链接),每条公告自动生成「AI 利好/利空判断提示词」可一键复制,支持一键复制全部提示词,数据落盘 `data/announcements/{代码}.csv`(仅支持深交所 0/2/3 开头代码) | | **⭐ 自选列表维护** | 自选股的添加(搜索/直输/批量)、展示(含实时行情预览)、移除与清空,每行可设价格提醒 | | **🔔 价格提醒** | 邮件配置自检、提醒规则增删改查、手动检查与通知、距目标价进度 | | **🧭 初步策略** | 基于每日快照的多因子选股:预设策略一键套用 + 自定义区间筛选,结果可加入自选或导出 CSV | | **📅 周涨幅榜** | 本周区间涨跌幅 TOP N(涨 / 跌两榜),含周期信息、横向柱状图、代码名称过滤、导出与存档 | | **🟢 建仓** | 输入买入价格 / 数量 / 佣金,计算成交金额、手续费明细(佣金 + 过户费 + 规费)、**持仓成本价**(含费摊薄成本)与**保本卖出价**;选填当前价可看浮动盈亏 | | **➕ 补仓** | 输入原持仓与本次补仓的价格 / 数量,计算**补仓后新平均成本价**、所需资金、新保本价与成本摊薄幅度;并支持反推「想摊薄到目标成本价需补多少股 / 多少资金」(按 100 股/手取整) | | **📤 清仓** | 输入持仓成本与卖出价,计算手续费明细(含**印花税**)、实收金额、实收均价、**盈亏金额与盈亏比例**、保本卖出价;成本价可勾选是否已含买入费用 | ### 菜单:建仓 / 补仓 / 清仓(交易计算) 纯本地计算工具,不联网。按 A 股现行费用标准扣费,费率均可在页面上修改: | 费用项 | 收取方向 | 默认费率 | 说明 | | --- | --- | --- | --- | | 佣金 | 买卖双向 | 万 2.5(单笔最低 5 元) | 按券商实际费率填写;最低佣金填 0 可关闭 | | 印花税 | 仅卖出 | 0.05% | 2023-08-28 起由 0.1% 下调 | | 过户费 | 买卖双向 | 0.001% | 沪深两市统一(2022-04-29 起) | | 规费 | 买卖双向 | 0.00541% | 证管费 0.002% + 交易经手费 0.00341%;全佣券商可勾选「已含在佣金内」 | 关键口径: - **成本价(摊薄成本)** =(买入成交金额 + 买入费用)÷ 持股数量; - **保本价** = 卖出实收刚好等于持仓总成本时的卖出价(已计入卖出端全部费用,并处理了最低佣金的非线性); - **补仓后成本价** =(原持仓总成本 + 本次补仓实付)÷(原股数 + 补仓股数); - 摊薄反推的可行区间:`补仓价 ×(1+买入费率) < 目标成本价 < 原成本价`,超出区间会给出明确提示。 ### 菜单:周涨幅榜 / 月涨幅榜(涨幅榜单) 数据源为 yueshushu.top 自有接口,按周期统计区间涨跌幅: | 周期 | 接口 | | --- | --- | | 周 | `GET https://www.yueshushu.top/StockApi/stockHistory/thisWeekTopList?count=50&excludeSt=true&sign=hmh` | | 月 | `GET https://www.yueshushu.top/StockApi/stockHistory/thisMonthTopList?count=50&excludeSt=true&sign=hmh` | | 区域 | 能力 | | --- | --- | | 工具条 | 榜单条数(20/50/100/200)、是否排除 ST、🔄 刷新(清缓存重拉)、💾 保存为 `data/rank/{周期}_{数据日期}.csv` | | 周期信息 | 周期标签(如 2026年第37周)、区间起止日、数据日期、样本总量、是否进行中;接口回退到上一周期时给出提示 | | 榜单 Tab | 📈 涨幅榜 / 📉 跌幅榜,各自独立的排序、过滤与导出 | | 概览 | 榜单数量、平均涨跌幅、最大涨/跌幅、中位成交额、涨跌停级(±9.8%)家数 | | 图表 | 前 15 名区间涨跌幅横向柱状图,涨红跌绿,配色跟随主题 | | 列表 | 排名、代码、名称、板块、现价、涨跌幅、开高低、区间起始价、成交额;每行 📈 实时行情 / ⏱ 分时图 / 🕯 日K图 / ⭐ 加入自选 | 抓取逻辑在 `stockquant/fetchers/top_list.py`(标准库实现,可被脚本复用): - `fetch_top_list(period, count, exclude_st)` 拉数据,`save_top_list()` 落盘,`list_saved_files()` / `load_saved_file()` 读存档; - **业务成功码是 20000**,不是 0;`code=60000` 表示超过访问频率,页面会给出友好提示并列出本地存档; - `amplitudeProportion` 名为「振幅」,实为**区间涨跌幅**(相对区间起始前收盘价); - `tradingValue` 单位万元(落盘换算成亿元),`tradingVolume` 换算成万手(个别股票数量级有出入,重点看成交额); - 接口有访问频率限制,页面侧结果缓存 300 秒,避免频繁刷新触发限流。 命令行: ```bash python scripts/fetch_top_list.py --period week --count 20 --top 5 python scripts/fetch_top_list.py --period month --count 100 --both python scripts/fetch_top_list.py --period week --no-save # 只打印不落盘 ``` ### 菜单:初步策略(策略选股) 数据源为 `data/realtime/YYYYMMDD.csv`(每日全市场快照),先选交易日再筛选。 | 区域 | 能力 | | --- | --- | | 预设策略 | 🚀 放量上攻 / 🔥 强势领涨 / 💎 稳健蓝筹 / 🌱 小盘活跃 / 🧊 超跌反弹 / 🏦 破净低估 / 😴 底部横盘 / 🎯 尾盘走强,一键填好条件;「🧹 清空条件」重置 | | 筛选区 | 五个 Tab:价格与涨跌(现价、涨跌幅、振幅、开盘涨幅)、成交活跃(换手率、量比、成交额)、估值与市值(市盈率、市净率、流通市值、总市值)、盘口强度(均价偏离、收盘位置)、排除条件(板块、关键字、ST、停牌、亏损) | | 因子控件 | 每个因子「勾选启用 + 区间滑块」,未启用不参与过滤;滑块区间按当日数据 1%~99% 分位计算,拖到两端即代表「不限」 | | 结果区 | 命中数量 / 平均涨跌幅 / 涨停·跌停数 / 平均换手率 / 中位流通市值;13 种排序、分页,每行 📈 实时行情与 ⭐ 加入自选;支持前 N 只批量加入自选、导出 CSV | 筛选逻辑在 `stockquant/fetchers/stock_screener.py`(纯数据层,只依赖 pandas,可被脚本复用): - `RANGE_FACTORS` 用 `Factor` 数据类描述因子(标签、单位、分组、步长、提示、常用区间),新增因子只加一条即可; - `load_screen_frame()` 负责派生列:振幅、开盘涨幅、均价偏离、收盘位置、成交额(万元→亿元)、板块、是否 ST; - `apply_screen(df, criteria)` 按条件过滤,`screen_summary()` 统计,`to_export_frame()` 导出中文表头。 字段单位:涨跌幅与换手率为 %,成交额与市值为亿元,市盈率/市净率为倍;**振幅为派生字段** =(最高价 − 最低价)/ 昨收 × 100%。 ### 菜单:股票列表 | 区域 | 能力 | | --- | --- | | 顶部 | 「🔄 同步数据」按钮:按同花顺接口分页抓取全量列表,写入 `data/stock.csv`,并显示文件路径与最后更新时间 | | 筛选区 | 交易所多选(SH / SZ / BJ)、按代码或名称搜索、每页条数(20 / 50 / 100) | | 列表区 | 分页展示,支持上一页 / 下一页 | | 操作列 | 每行三个按钮:「📈 实时数据」看当前行情、「📥 同步」抓历史日线、「📊 K 线」看历史 K 线图 | | 实时行情弹窗 | 现价与涨跌(涨红跌绿)、价格 / 成交 / 估值三组指标、买卖五档、行情时间,支持「🔄 刷新」 | | K 线弹窗 | 蜡烛图 + MA5/MA10/MA20 + 成交量副图,支持区间切换(近 1 个月 / 全部)、均线开关、重新同步、明细数据表 | ### 菜单:实时数据快照 | 区域 | 能力 | | --- | --- | | 顶部 | 「🔄 同步今日快照」按钮:分批(每批 500 只)抓取全市场行情,带进度条,按**交易日**写入 `data/realtime/YYYYMMDD.csv` | | 日期选择 | 下拉切换已保存的历史快照 | | 统计卡片 | 总股票数 / 上涨 / 下跌 / 平盘 / 无行情 | | 筛选区 | 关键字搜索、涨跌筛选(上涨 / 下跌 / 平盘 / 无行情)、排序(涨跌幅、成交额、换手率、量比、现价)、每页条数 | | 列表区 | 分页展示,涨跌幅与现价按涨红跌绿着色;每行「📈 实时数据」查看当前实时行情 | 新增菜单的方法:在 `views/` 下新建页面文件(末尾调用自己的 `render()`),然后在 `streamlit_app.py` 的 `MENU` 中加一行 `st.Page(...)`;多个页面共用的 UI(如实时行情弹窗、K 线弹窗、新浪分时/日K图弹窗)放 `views/components.py`。 如果几个页面**整体结构相同、只有参数不同**(如周涨幅榜 / 月涨幅榜),把共用渲染逻辑抽到一个不带 `render()` 调用的模块(如 `views/top_list_view.py`),再由各页面 import 后传入参数调用——页面文件之间不能互相 import,否则会触发对方的 `render()`。 ### 菜单:自选列表维护 | 区域 | 能力 | | --- | --- | | 添加区 | 输入代码或名称 → 实时显示最多 8 条候选,点「➕ 添加」即加入;也支持直接输入 6 位代码;「📋 批量添加」可粘贴多个代码(逗号/空格/换行分隔) | | 统计卡片 | 自选总数、沪市 / 深市 / 北交所分布、文件最后更新时间 | | 工具条 | 按代码或名称筛选、排序(添加时间新→旧 / 旧→新 / 代码)、每页条数(10/20/50)、「🔄 行情」批量刷新实时价(带「刷新于 HH:MM:SS」提示,缓存 60 秒自动失效)、「🗑️ 清空」 | | 列表区 | 分页展示,现价与涨跌幅按涨红跌绿着色;每行六个操作:📈 实时行情、⏱ 分时图、🕯 日 K 图、📊 K 线(本地)、🔔 设置提醒、🗑️ 移出 | | 分时图 / 日 K 图 | 新浪财经静态图,地址模板 `https://image.sinajs.cn/newchart/min/n/{symbol}.gif`(分时)与 `.../daily/n/{symbol}.gif`(日 K),`symbol` 为市场前缀 + 代码(如 `sz002124`);弹窗内「🔄 刷新」会追加 `?t=时间戳` 绕过浏览器缓存 | 数据存 `data/selected.csv`,字段 `code / name / exchange / added_at`。 代码规则(在 `stockquant/fetchers/selected_stock.py` 中统一处理): - 存储格式统一为 **6 位纯数字**(`000001`),不是 `SZ.000001`; - 输入时兼容 `000001.SZ`、`SZ.000001`、`sz000001`、带空格等写法,会自动规范化; - **代码唯一**,重复添加会被拒绝并提示;批量添加时重复项与非法项自动跳过并汇总原因; - 添加时自动从 `data/stock.csv` 补全股票名称与交易所。 > 实现提示:`st.rerun()` 会中断当前脚本并重跑,**本轮已渲染的提示信息会丢失**。所以页面里的成功/失败提示统一走 `_flash()` 暂存到 `session_state`,下一轮再由 `_render_flash()` 取出渲染,否则用户看不到任何反馈。 ### 菜单:价格提醒 | 区域 | 能力 | | --- | --- | | 邮件配置区 | 展示发件邮箱 / SMTP / 默认收件人;「🔍 验证配置」只登录不发信,「📧 测试邮件」发一封测试信确认能收到 | | 新建提醒 | 输入 6 位代码(自动显示识别到的股票名称)+ 触发方向 + 目标价 + 备注 | | 统计卡片 | 规则总数、启用中、停用、累计触发次数 | | 工具条 | 「🔍 立即检查并通知」正式检查并发信、「🧪 试运行(不发信)」只判定不通知、「🔄 刷新行情」、「🗑️ 清空规则」 | | 列表区 | 代码 / 名称 / 触发条件 / 现价(涨红跌绿)/ 距目标价 / 上次触发 / 状态 / 操作(启用停用 · 编辑 · 删除) | | 检查结果 | 展示最近一次命中的条目,冷却跳过与无行情的股票分别提示 | **设置入口有两处**:自选列表每行「🔔」按钮(自动带入当前价作为目标价默认值),或本页「新建提醒」。 数据存 `data/alerts.csv`,规则字段: | 字段 | 说明 | | --- | --- | | `id` | `代码_方向_目标价`,如 `600519_below_1500.00`,天然唯一且可读 | | `code` / `name` / `exchange` | 股票代码(6 位)、名称、交易所 | | `direction` | `above` 涨破(现价 ≥ 目标价)/ `below` 跌破(现价 ≤ 目标价) | | `target_price` | 目标价,两位小数 | | `enabled` | `1` 启用 / `0` 停用 | | `created_at` / `last_triggered_at` / `last_price` / `trigger_count` | 创建时间、上次触发时间、上次检查到的价格、累计触发次数 | | `recipient` | 该条提醒单独的收件人;留空则用 `ALERT_RECIPIENT` | | `note` | 备注,会写进邮件正文 | **去重与冷却**: - 同一「代码 + 方向 + 目标价」只允许一条,重复添加会提示; - 触发后 **30 分钟**(`ALERT_COOLDOWN_MINUTES`)内不重复发信,避免行情在阈值附近反复穿越时狂轰滥炸; - **只有邮件真正发送成功后**才写入 `last_triggered_at` 和累加 `trigger_count`,发信失败会保留状态、下次检查重试,不会静默丢提醒。 **邮件模板**(`stockquant/notifier/email_sender.py`): - 主题:单条时带具体条件(如 `【StockPython 价格提醒】贵州茅台(600519) 跌破 1500.00,现价 1488.20`),多条时只报数量; - 正文:HTML 表格(股票 / 现价 / 涨跌 / 触发条件 / 行情时间 / 备注),涨红跌绿,同时附纯文本版本供不支持 HTML 的客户端降级; - 不同规则可指定不同收件人,发信时按收件人分组,每组单独一封。 > **AppTest 已知限制**:`streamlit.testing.v1.AppTest` **无法触发 `@st.dialog` 内按钮的回调**(只能验证弹窗渲染出的内容,点不动里面的按钮)。验证弹窗内的写操作需要直接调用弹窗内容函数(如 `_alert_fragment(...)`)或手动在浏览器点击。 ## 已实现功能 ### 1. 全量股票列表(同花顺) 接口:`GET https://fuyao.aicubes.cn/api/meta/tickers/list?asset_type=a-share&limit=1000&offset=0` 运行: ```bash python scripts/fetch_stock_list.py ``` 常用参数: | 参数 | 说明 | 默认值 | | --- | --- | --- | | `--asset-type` | 资产类型 | `a-share` | | `--page-size` | 每页条数(上限 1000) | `1000` | | `--output` | 输出文件名,相对 `data/` | `stock.csv` | | `--api-key` | 接口鉴权 key | 环境变量 `FUYAO_API_KEY` / config 默认值 | | `--no-sort` | 不按 `thscode` 排序,保持接口返回顺序 | 关闭 | | `--quiet` | 静默模式,只输出文件路径 | 关闭 | 示例: ```bash # 每页 500 条,输出到 data/stock_20260905.csv python scripts/fetch_stock_list.py --page-size 500 --output stock_20260905.csv # 通过环境变量注入 API Key(推荐,避免密钥入库) set FUYAO_API_KEY=sk-fuyao-xxxx && python scripts/fetch_stock_list.py ``` 输出字段(`data/stock.csv`): | 字段 | 含义 | 示例 | | --- | --- | --- | | `thscode` | 同花顺标准代码 | `600000.SH` | | `ticker` | 6 位股票代码 | `600000` | | `name` | 股票简称 | `浦发银行` | | `exchange` | 交易所 | `SH` / `SZ` / `BJ` | | `asset_type` | 资产类型 | `a-share` | | `currency` | 计价货币 | `CNY` | 最近一次运行结果(2026-09-05):共 **5567** 只,其中 SH 2320、SZ 2902、BJ 345,分 6 页取回。 ### 2. 实时行情(腾讯 qt.gtimg.cn) 接口:`GET http://qt.gtimg.cn/?q=sz002812` - `q` = 市场前缀 + 6 位代码,多个用英文逗号分隔:`q=sz002812,sh600000` - 市场前缀:深市 `sz`、沪市 `sh`、北交所 `bj` - 响应为 GBK 编码的文本:`v_sz002812="字段0~字段1~...~";`,字段以 `~` 分隔 调用示例: ```python from stockquant.fetchers import fetch_realtime_quotes, symbol_from_thscode quotes = fetch_realtime_quotes(["sz002812", "sh600000"]) symbol = symbol_from_thscode("002812.SZ") # -> sz002812 ``` 返回字段(部分):现价、涨跌额、涨跌幅、今开、昨收、最高、最低、均价、振幅、涨跌停价、成交量(手)、成交额(万元)、换手率、量比、市盈率(TTM)、市净率、流通市值(亿元)、总市值(亿元)、买卖五档、行情时间。 > 注意:请求时逗号不能做百分号编码,且 URL 结尾的 `/` 不能省略,否则服务端返回 502。 ### 3. 全市场实时行情快照 批量抓取 `data/stock.csv` 中全部股票的实时行情,按交易日写入 `data/realtime/YYYYMMDD.csv`。 ```bash python scripts/fetch_market_snapshot.py # --batch-size 每批数量(默认 500,实测 1200 会被服务端 414 拒绝) # --output 自定义输出文件名(相对 data/) # --verbose 显示详细请求日志 ``` 首次实测(2026-09-05,交易日 2026-09-04):5567 只,12 批约 4 秒完成,790 KB,仅 3 只因停牌未取到行情。 输出字段(`data/realtime/20260904.csv`): | 字段 | 含义 | | --- | --- | | `thscode` / `ticker` / `name` / `exchange` | 股票基础信息(来自 data/stock.csv) | | `price` / `change` / `change_pct` | 现价、涨跌额、涨跌幅 | | `open` / `prev_close` / `high` / `low` / `avg_price` | 今开、昨收、最高、最低、均价 | | `volume` / `amount` | 成交量(手)、成交额(万元) | | `turnover_rate` / `volume_ratio` | 换手率(%)、量比 | | `pe_ttm` / `pb` / `circulating_mktcap` / `total_mktcap` | 市盈率、市净率、流通市值(亿)、总市值(亿) | | `time` | 行情时间 | > 文件按**行情数据里的交易日**命名,不是运行当天日期。周末或收盘后同步,文件名仍是最近一个交易日。 ### 4. 历史行情与 K 线图(同花顺) 接口:`GET https://fuyao.aicubes.cn/api/a-share/prices/historical` | 参数 | 说明 | 默认值 | | --- | --- | --- | | `thscode` | 同花顺代码,如 `600519.SH` | 必填 | | `interval` | K 线周期,如 `1d`(日线) | `1d` | | `start` | 起始毫秒时间戳 | 今天往前推 3 个月 | | `end` | 结束毫秒时间戳 | 当前时刻 | | `adjust` | 复权方式:`forward` 前复权 / `backward` 后复权 / `none` 不复权 | `forward` | 命令行: ```bash # 单只(默认近三个月日线) python scripts/fetch_history.py --code 600519.SH # 多只 python scripts/fetch_history.py --code 600519.SH --code 000001.SZ # 指定区间 python scripts/fetch_history.py --code 600519.SH --start 2026-01-01 --end 2026-09-05 # 全市场(建议先 --limit 小批量试跑) python scripts/fetch_history.py --all --limit 20 ``` 输出:`data/history/{6 位代码}.csv`,例如 `data/history/000001.csv`。 | 字段 | 含义 | | --- | --- | | `date` | 交易日,YYYY-MM-DD | | `open` / `high` / `low` / `close` | 开 / 高 / 低 / 收(前复权,保留 3 位小数) | | `volume` | 成交量(股) | | `turnover` | 成交额(元) | 界面入口:`股票列表` 页每行的「📥 同步」与「📊 K 线」按钮。K 线图由 Plotly 绘制,未同步过数据的股票会先提示同步。 > **时间戳坑**:接口返回的 `date_ms` 是「北京时间当天 00:00」对应的毫秒时间戳(等价于 UTC 前一天 16:00)。必须用**本地时区**解析,用 UTC 会导致日期整体偏移一天。 ### 5. 自选股清单 纯本地维护(不请求接口),数据存 `data/selected.csv`,界面在【⭐ 自选列表维护】菜单。 ```python from stockquant.fetchers import ( add_selected, # 添加单只,重复或格式非法时抛 SelectedStockError add_selected_batch, # 批量添加,返回 {"added": [...], "skipped": [...]} remove_selected, # 移除,返回是否真的删掉了 list_selected, # 读取清单(按添加顺序) normalize_code, # 任意写法 -> 6 位纯数字 is_valid_code, # 校验 6 位数字 lookup_stock, # 从 data/stock.csv 查名称与交易所 ) ``` | 字段 | 含义 | 示例 | | --- | --- | --- | | `code` | 6 位股票代码 | `000001` | | `name` | 股票简称(自动补全) | `平安银行` | | `exchange` | 交易所(自动补全) | `SH` / `SZ` / `BJ` | | `added_at` | 添加时间 | `2026-09-05 17:20:31` | ### 6. 价格提醒与邮件通知 规则维护与触发判定在 `stockquant/fetchers/price_alert.py`,发信在 `stockquant/notifier/email_sender.py`(标准库 `smtplib`,无第三方依赖)。 ```python from stockquant.fetchers import ( add_alert, # 新增规则,重复/参数非法时抛 PriceAlertError list_alerts, # 读取规则,enabled_only=True 只取启用中的 update_alert, # 改目标价 / 备注 / 收件人 set_alert_enabled, # 启用 / 停用 remove_alert, # 删除 check_alerts, # 检查并发送邮件,dry_run=True 时只判定不通知 ) from stockquant.notifier import ( verify_smtp_config, # 校验配置(只登录不发信) send_test_email, # 发一封测试邮件 send_alert_email, # 直接发送提醒邮件 ) ``` `check_alerts()` 返回结构: ```python { "total": 启用中的规则总数, "triggered": [命中且已发信的记录...], "cooldown": [命中但在冷却期被跳过的记录...], "no_quote": [未取到行情的代码...], "sent": 是否发送成功(无命中时为 True), "error": 发信失败原因(无错误时为空串), } ``` **邮件配置**(`stockquant/config.py`,支持环境变量覆盖): | 配置项 | 默认值 | 说明 | | --- | --- | --- | | `SMTP_HOST` | `smtp.163.com` | SMTP 服务器 | | `SMTP_PORT` | `465` | SSL 直连端口,不需要 `starttls` | | `SMTP_USER` | `15734078926@163.com` | 发件邮箱 | | `SMTP_AUTH_CODE` | — | **客户端授权码**,不是登录密码 | | `SMTP_SENDER_NAME` | `StockPython 价格提醒` | 发件人显示名 | | `ALERT_RECIPIENT` | `1290513799@qq.com` | 默认收件人,多个用逗号分隔 | | `ALERT_COOLDOWN_MINUTES` | `30` | 同一规则触发后的冷却时间 | | `ALERT_MAX_PER_MAIL` | `20` | 单封邮件最多合并多少条 | > ⚠️ **授权码会随代码提交到 Git**。若仓库公开,请改用环境变量注入: > `set SMTP_AUTH_CODE=xxxx` 后重启服务,并把 `config.py` 里的默认值清空。 **命令行定时检查**: ```bash # 校验邮件配置(只登录不发信) python scripts/check_price_alerts.py --verify # 发一封测试邮件 python scripts/check_price_alerts.py --test-mail # 只检查不通知(排查规则是否命中时用) python scripts/check_price_alerts.py --dry-run # 正式检查并发送邮件(定时任务用这个) python scripts/check_price_alerts.py --quiet ``` Windows 任务计划程序示例(交易时段每 10 分钟一次): ``` 程序:D:\Python310\python.exe 参数:scripts\check_price_alerts.py --quiet 起始位置:Z:\AStockNew\StockPython 触发器:工作日 09:30-15:00,每 10 分钟重复 ``` **163 邮箱 535 认证失败排查**:① 邮箱网页端 设置 → POP3/SMTP/IMAP 是否已开启;② 用的必须是「客户端授权码」而非登录密码;③ 重新生成授权码会让旧的立即失效。 ## 开发约定 1. **数据落盘**:所有数据写 `data/`,CSV 编码 `utf-8-sig`(Excel 直接打开中文不乱码)。写入由 `storage/csv_store.py` 统一负责,三级策略: | 级别 | 行为 | 触发条件 | | --- | --- | --- | | ① 原子替换 | 写 `.tmp` 后 `os.replace`,失败重试 6 次(退避 0.12s×n) | 正常情况 | | ② 直接覆盖写 | 放弃原子性,直接写目标文件 | 目标被占用导致无法替换 | | ③ 抛出可读错误 | `PermissionError`,提示关闭占用程序 | ①②③ 全失败(文件被长期独占) | > **Windows 特有问题**:`os.replace()` 要求目标具备「删除」权限。Excel / 编辑器 / 杀毒软件 / Windows 索引服务只要握着句柄,就会抛 `PermissionError(WinError 5 拒绝访问)`。多数占用是瞬时的,重试即可;Excel 这类「允许读写共享但不给删除权限」的占用会走 ② 降级写入。 > > **界面层必须兜住 `OSError`**:数据模块的异常若直接冒泡会让整个 Streamlit 页面白屏。写盘点(添加 / 移除 / 清空 / 同步)一律 `try ... except OSError` 后转成友好提示,不要用裸调用。 2. **数据入库(提交)**:`data/` 下的 CSV 属于项目资产,**需要随代码一起提交到 Git**(`.gitignore` 中不得忽略 `data/` 与 `*.csv`)。每次重新抓取后应将数据变更与代码变更一起提交,便于回溯历史快照。 3. **新增菜单页**:在 `views/` 下新建页面文件(末尾调用 `render()`),并在 `streamlit_app.py` 的 `MENU` 中注册,不要在页面里单独调用 `st.set_page_config`。 4. **新增接口**:在 `stockquant/fetchers/` 下新建模块,暴露 `fetch_xxx()`(拉数据)与 `save_xxx()`(落盘),并在 `scripts/` 下配一个 CLI 入口。 5. **分页**:直接复用 `FuyaoClient.paginate()`,按 offset/limit 自动翻页,返回条数小于 `page_size` 即结束。 6. **密钥**:优先通过环境变量 `FUYAO_API_KEY` 注入,不要把 key 提交到公共仓库。 7. **涨红跌绿**:界面展示涨跌一律遵循中国市场习惯 —— 涨红(`#e03131`)、跌绿(`#2f9e44`)。