龙虎榜个股上涨成功率 (alpha_lhb_success_rate_data)

数据描述: 脚本:`eastmoney_tradedetail.py`(与本文件同目录) | 共 **527 行** | 依赖:`requests`(其余为标准库)       + 可选 `pandas` / `dai`(回退 `bigquant.dai`)/ `bigalpha`(仅 AIStudio 提供)

文档
数据简介

## 1. 定位 **东方财富「每日龙虎榜详情」采集 + 成功率因子提交工具**(`eastmoney_tradedetail.py`)。 - **做什么**:① 向东方财富数据中心接口按交易日拉取当日**全部**龙虎榜详情(榜单明细), 可读化后整份写入 `a_lhb_detail_<数据日期>.csv`(19 列,见「表结构」文档), 并从「解读」文本中解析出**成功率**写成 `success_rate_factor` 列(单位 **%**); ② 把明细聚合成 `date / instrument / name / success_rate` 因子面板 `a_lhb_success_rate.csv` (`success_rate` 落盘带 `%` 后缀),在 AIStudio 环境下落成平台数据源并提交因子。 - **不做什么**:**不做本地回测 / IC / 分层绩效**(因子分析由平台完成), 明细**不聚合去重**(同股多条上榜原样保留,聚合只发生在因子面板侧); 因子提交链路**不硬依赖** `dai` / `bigalpha` SDK——缺失时只打印提示并跳过, 采集与因子面板落盘照常完成(本地与 AIStudio 环境均可跑,只依赖 `requests` 与标准库)。 - **运行方式**:① 手工单次采集(`--date` 指定或取最近有数据的交易日); ② **常驻守护模式(主用)**:进程常驻内存,每天 **21:00** 自动采集一次**并顺带提交因子**(见第 6、7 节)。 > 与上级目录 `eastmoney_lhb_detail.py` 的关系:分页、重试、`元→万/亿` 换算、字段映射全部沿用, > 两处改动为 —— ① 抽出 `success_rate_factor`(成功率,单位 %);② 由**追加写**改为**整份重写** > (`open(..., "w")`),并新增 `--daemon / --at` 常驻守护。 --- ## 2. 数据来源与链路 **数据来源**:东方财富数据中心「每日龙虎榜详情」接口(页面 `https://data.eastmoney.com/stock/tradedetail.html` 的内部数据源,脚本直连接口、不解析 HTML)。 | 项 | 取值 | | --- | --- | | 接口 | `https://datacenter-web.eastmoney.com/api/data/v1/get` | | reportName | `RPT_DAILYBILLBOARD_DETAILSNEW` | | 过滤 | `filter=(TRADE_DATE='YYYY-MM-DD')`(不传则取最新交易日) | | 排序 | `sortColumns=SECURITY_CODE,TRADE_DATE`、`sortTypes=1,-1` | | 分页 | `pageSize=500`、`pageNumber=N`,按返回的 `result.pages` 逐页取 | | 请求头 | `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)`、`Referer: …/stock/tradedetail.html` | | 超时 / 重试 | 单次 `timeout=20`;第 2 页起每页最多重试 3 次(间隔 1.5s),页间 `sleep(0.3)` | ``` ① 确定数据日期 ├─ 单次模式:--date 指定 / 缺省用 latest_trade_date() 取接口最新交易日 └─ 守护模式:到点后调用 latest_trade_date()(当日榜单 18:00~20:00 陆续披露完毕) ▼ ② fetch_page(pageNumber=1, filter=TRADE_DATE) → 总条数 count / 总页数 pages / 首页数据 ▼ ③ 逐页 fetch_page(2..pages)(失败重试 3 次) → result.data[*] ▼ ④ to_row():18 列可读化(元→万、元→亿、比例截位) └─ EXPLAIN「解读」文本 ──正则「成功率…%」──▶ success_rate_factor(单位 %) ▼ ⑤ 整份写盘 a_lhb_detail_YYYY-MM-DD.csv(utf-8-sig,19 列,每次覆盖) ▼ ⑥ 聚合因子面板(build_factor_panel) instrument 直接取明细 instrument 列、name 取成功率最大值那一行;同 (date, instrument) 多条取成功率最大值 → save_factor_csv() 幂等合并写 a_lhb_success_rate.csv(date/instrument/name/success_rate,值带 % 后缀) ▼ ⑦ 提交链路(数据源落库走 dai,缺失时回退 bigquant.dai;bigalpha 仅 AIStudio 提供,缺失则打印提示后跳过) write_factor_datasource() dai.DataSource.write_bdb → 数据源 alpha_lhb_success_rate_data(因子列 success_rate) submit_factor() factors.submit_factor → 因子 alpha_lhb_success_rate ▼ ⑧ 控制台/日志打印 已写入 N 条 / 其中解析到成功率 H 条、未解析到 N-H 条 因子面板已写入 M 行(本次数据日期 …),文件累计 K 行 ``` **无未来函数**:只取「该交易日」的榜单数据,接口按 `TRADE_DATE` 过滤,不涉及时序外推。 --- ## 3. 字段口径 字段逐列说明见「表结构」文档,此处只列**换算与单位**要点: | 类别 | 口径 | | --- | --- | | 金额(`billboard_*_amt_wan` / `accum_amount_wan`) | 接口为**元**,脚本 `_w()` 除以 1e4 → **万元**,保留 2 位 | | 流通市值(`free_market_cap_yi`) | 接口为**元**,脚本 `_y()` 除以 1e8 → **亿元**,保留 2 位 | | 比例(`deal_net_ratio_pct` / `deal_amount_ratio_pct`) | 接口为**百分数数值**,保留 3 位,单位 %(列名后缀 `_pct`) | | 涨跌幅 / 换手率(`change_rate_pct` / `turnover_rate_pct`) | 接口原值,单位 %(列名后缀 `_pct`) | | 收盘价(`close_price`) | 接口原值,单位元 | | `success_rate_factor` | `explain` 文本中解析的成功率,单位 **%**,保留 2 位;解析不到为空串 | | `instrument`(明细第 3 列 / 因子面板第 2 列) | **必须带** `.SH/.SZ/.BJ` 后缀:由接口 `SECURITY_CODE` + `MARKET_SUFFIX[MARKET]` 拼接(如 `000592.SZ` / `600354.SH` / `920895.BJ`);`MARKET` 不在 `SH/SZ/BJ` 之列时只落 6 位代码、不带后缀(该行不进因子面板) | | `market` | 交易所 `SH` / `SZ` / `BJ`,与 `instrument` 后缀一致(冗余记录) | | 缺失值 | 统一落盘为空字符串(pandas 读入后为 `NaN`) | **表头命名**:明细 19 列**全部为英文小写**(`seq / date / instrument / name / explain / success_rate_factor / close_price / change_rate_pct / billboard_*_amt_wan / accum_amount_wan / deal_*_ratio_pct / turnover_rate_pct / free_market_cap_yi / explanation / market`),名称取接口字段名的小写形式,单位由后缀表达 (`_wan` = 万元、`_yi` = 亿元、`_pct` = %)。 成功率解析正则与命中/未命中实例见「表结构」文档第 4 节;因子面板三列与聚合口径见「表结构」文档第 2 节。 --- ## 4. 参数 命令行参数(`python eastmoney_tradedetail.py --help`): | 参数 | 默认 | 含义 | | --- | --- | --- | | `--date` | 最近有数据的交易日 | 数据日期 `YYYY-MM-DD`;**仅单次模式使用** | | `--out-dir` | 脚本所在目录 | 产物 CSV 输出目录 | | `--daemon` | 关 | **常驻内存**:进程常驻,每天 `--at` 时刻采集一次 | | `--at` | `21:00` | 守护模式触发时刻 `HH:MM`(配合 `--daemon`) | | `--no-submit` | 关 | 只采集 + 落因子面板,**不**落平台数据源、**不**提交因子 | | `--from-csv` | 关 | 不联网采集,直接用已落盘的 `a_lhb_success_rate.csv` 落库并提交(AIStudio 端回补用) | | `--group-num` | `10` | 因子分析分层组数(提交参数 `group_num`) | | `--instruments` | `全市场` | 股票池(提交参数 `instruments`) | | `--benchmark` | `中证500` | 基准(提交参数 `benchmark`) | 用法: ```powershell python eastmoney_tradedetail.py # 最近一个交易日,采集 + 构因子 + 提交后退出 python eastmoney_tradedetail.py --date 2026-09-15 # 指定交易日 python eastmoney_tradedetail.py --no-submit # 只采集 + 落因子面板,不落库不提交 python eastmoney_tradedetail.py --from-csv # 不联网,直接用已落盘的因子面板提交 python eastmoney_tradedetail.py --daemon # 常驻内存,每天 21:00 采集并顺带提交 python eastmoney_tradedetail.py --daemon --at 21:00 # 显式指定 21:00(等价写法) ``` 内部常量(脚本顶部): | 常量 | 值 | 含义 | | --- | --- | --- | | `PAGE_SIZE` | 500 | 每页条数 | | `DEFAULT_RUN_AT` | `"21:00"` | 守护模式默认触发时刻 | | `DAEMON_LOOP_GAP` | 60 | 单次采集后额外静睡秒数,避免同一分钟被立刻重复触发 | | `SUCCESS_RATE_RE` | `成功率\s*[::]?\s*([0-9]+(?:\.[0-9]+)?)\s*%` | 成功率解析正则 | | `FACTOR_CSV_NAME` | `"a_lhb_success_rate.csv"` | 因子面板文件名 | | `FACTOR_ID` / `FACTOR_NAME` | `alpha_lhb_success_rate` / 龙虎榜成功率因子 | 提交的因子 id 与名称 | | `FACTOR_DATASOURCE_ID` / `FACTOR_DATASOURCE_COLUMN` | `alpha_lhb_success_rate_data` / `success_rate` | 平台数据源 id 与因子列名 | | `FACTOR_SPACE_ID` | `""` | 空串 = 默认主站空间(「我的因子」) | 退出码:单次模式采集成功 `0`、当日无数据 `1`;守护模式正常退出(Ctrl+C)`0`; **因子落库 / 提交失败不改变退出码**(只打印 `[提示]`)。 --- ## 5. 产物清单 | 产物 | 说明 | | --- | --- | | `a_lhb_detail_YYYY-MM-DD.csv` | **主产物**,当日龙虎榜详情 19 列(见「表结构」文档),前两列为 `date` / `instrument`(与因子面板对齐,`instrument` 带 `.SH/.SZ/.BJ` 后缀);每个数据日期一份,重复执行整份覆盖 | | `a_lhb_success_rate.csv` | **因子面板**,`date / instrument / name / success_rate` 四列(见「表结构」文档第 2 节,`success_rate` 带 `%` 后缀、落平台时剥离);单文件跨日累积,同日期幂等重写 | | `tradedetail_daily.log` | 守护模式日志,由 `run_tradedetail_daemon.bat` 追加写入(启动/退出标记 + 脚本全部输出) | | `log\tradedetail_daily_.log` | 上一轮守护的**归档日志**:每次启动 `.bat` 时把旧日志改名归档,只保留最近 **30** 份 | 命名与编码约定: - CSV 文件名统一 `a_` 前缀:`a_lhb_detail_<日期>.csv`、`a_lhb_success_rate.csv`(全 ASCII,与项目内因子产物命名口径一致); - 落盘编码 `utf-8-sig`,Excel 直接双击不乱码; - 日志为**追加**写,单次守护运行内不清空历史;每次启动 `.bat` 会先把上一轮的日志归档进 `log\` 子目录(见第 6 节「日志轮转」),主日志路径始终是 `tradedetail_daily.log`。 --- ## 6. 定时运行(每天 21:00 · 内存常驻) **主用方案:常驻守护进程**——脚本自带 `--daemon`,进程启动后**一直驻留内存**, 到达每天 21:00 由自身 `sleep` 唤醒执行采集,**不依赖 Windows 计划任务**(无需注册任务、不重复拉起新进程)。 - **触发时刻**:每天 **21:00:00**(`--at 21:00`,即 `DEFAULT_RUN_AT`) - **启动脚本**:`run_tradedetail_daemon.bat`(与本脚本同目录,**ASCII-only**) - **启动方式**:双击 `run_tradedetail_daemon.bat`(或命令行执行),**保持该窗口常开**(可最小化) - **停止方式**:`Ctrl+C`,或直接关闭该窗口 - **日志**:`tradedetail_daily.log`(追加),内容与终端一致;启动时先把上一轮日志归档进 `log\`,只保留最近 30 份 `run_tradedetail_daemon.bat` 全文: ```bat @echo off rem ============================================================ rem EastMoney tradedetail (daily billboard detail) collector rem Resident daemon: stays in memory, collects once every day at 21:00 rem -> output CSV : a_lhb_detail_.csv rem -> log : tradedetail_daily.log (append) rem -> archives : log\tradedetail_daily_.log (newest 30 kept) rem Keep this window open (you may minimize it) while the daemon runs. rem Press Ctrl+C or close the window to stop it. rem NOTE: keep this file ASCII-only; cmd parses .bat with the ANSI codepage rem ============================================================ chcp 65001 >nul setlocal cd /d "%~dp0" set "PY=C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe" set "LOG=%~dp0tradedetail_daily.log" set "ARCHDIR=%~dp0log" set "KEEP=30" set "PYTHONIOENCODING=utf-8" rem ---- log rotation: archive the log left by the previous run ---- setlocal enabledelayedexpansion if exist "%LOG%" ( if not exist "%ARCHDIR%" mkdir "%ARCHDIR%" 2>nul set "STAMP=" for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd-HHmmss"') do set "STAMP=%%i" if not defined STAMP set "STAMP=nops-%RANDOM%" move /y "%LOG%" "%ARCHDIR%\tradedetail_daily_!STAMP!.log" >nul 2>&1 ) rem ---- prune: keep only the newest %KEEP% archives ---- set /a N=0 for /f "delims=" %%f in ('dir /b /o-d /a-d "%ARCHDIR%\tradedetail_daily_*.log" 2^>nul') do ( set /a N+=1 if !N! GTR %KEEP% del /q "%ARCHDIR%\%%f" >nul 2>&1 ) endlocal if not exist "%PY%" ( echo [%date% %time%] FAILED - python not found: %PY% >>"%LOG%" exit /b 1 ) echo [%date% %time%] ===== START resident daemon (daily 21:00) ===== >>"%LOG%" "%PY%" "%~dp0eastmoney_tradedetail.py" --daemon --at 21:00 >>"%LOG%" 2>&1 set "RC=%errorlevel%" echo [%date% %time%] ===== resident daemon exited (errorlevel=%RC%) ===== >>"%LOG%" exit /b %RC% ``` ### 日志轮转(在 `.bat` 内实现,不改 Python) 每次启动 `.bat` 时按以下顺序处理,因此 **主日志 `tradedetail_daily.log` 永远只含「本次守护运行」的内容**: 1. **归档**:若 `tradedetail_daily.log` 已存在(= 上一轮守护留下的),用 PowerShell 取 `yyyyMMdd-HHmmss` 时间戳,`move` 成 `log\tradedetail_daily_<时间戳>.log`(内容原样保留;时间戳取不到时退化为 `nops-<随机数>`); 2. **清理**:`dir /b /o-d` 按修改时间倒序枚举 `log\tradedetail_daily_*.log`,第 31 份起 `del` ——即**只保留最近 30 份**(`KEEP=30`); 3. 之后才写 `START` 标记并拉起守护,新日志从本轮开始累积。 要点与限制: - 归档/清理都发生在**新日志被写入之前**,`FAILED - python not found` 这类早期错误也会落在新日志里; - 若上一轮守护**仍在运行**(重复双击 `.bat`),旧日志文件被 cmd 的重定向句柄占用、`move` 失败, 此时不归档也不丢数据,仅继续往同一文件追加(`move ... >nul 2>&1` 已静默该错误); - 轮转发生在**启动时**:守护进程本身常驻不重启,所以同一次运行内日志不会切分 (按当前量级约每交易日新增 1~2 KB)。需要「每天一份」时可改为由计划任务每日拉起单次采集(见下方备选方案)。 守护循环要点(源码 `run_daemon` / `seconds_until`): ``` while True: wait = seconds_until(21, 0) # 距下一个 21:00 的秒数(已过则算次日) 打印「下次采集时间 …(等待 x.xx 小时)」 sleep(wait) # 常驻内存,此处只是阻塞,不占 CPU 取最新交易日 → collect() 整份落盘 若数据日期 == 今日:run_factor() # 构因子面板 + 落库 + 提交(见第 7 节) 否则:打印「不是今日(非交易日或接口尚未更新),跳过因子提交」 若本轮失败:traceback 记录后不退出,等下一个 21:00 重试 # 单次失败不影响常驻 sleep(60) # DAEMON_LOOP_GAP,避开同一分钟重复触发 ``` 另外两处为守护模式专门做的处理: 1. `sys.stdout.reconfigure(encoding="utf-8", line_buffering=True)`——**行缓冲**。 输出被重定向进日志文件时 Python 默认块缓冲,会导致启动提示与进度长时间不落盘; 改为行缓冲后日志实时可见。 2. `DAEMON_LOOP_GAP = 60`——采集完成后再静睡 60 秒,避免「21:00 刚跑完又命中同一分钟」重复采集。 **为什么选 21:00**:交易所龙虎榜明细在收盘后 18:00~20:00 陆续披露完毕,21:00 已能取到**当日完整榜单**, 且早于次日开盘,盘前即可使用;接口未更新时守护会取到上一交易日,不会写入空文件。 **备选方案(不希望常驻时)**:改用 Windows 计划任务每天 21:00 拉起一次性采集 (脚本单次模式缺省即取「最近有数据的交易日」,等价于当日采集): ```powershell $set = New-ScheduledTaskSettingsSet -StartWhenAvailable -MultipleInstances IgnoreNew ` -ExecutionTimeLimit (New-TimeSpan -Hours 1) $p = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest $a = New-ScheduledTaskAction -Execute "c:\Users\Administrator\Documents\trae_projects\dragon\run_tradedetail_daemon.bat" $t = New-ScheduledTaskTrigger -Daily -At 21:00 Register-ScheduledTask -TaskName "EastMoneyTradeDetail_Daily" -Action $a -Trigger $t ` -Settings $set -Principal $p -Force ``` > 注:上面的动作若走计划任务,请把 `.bat` 中的 `--daemon --at 21:00` 改为不带参数的一次性采集, > 否则常驻进程在 1 小时后会被 `ExecutionTimeLimit` 杀掉。两种方案**二选一**,不要同时启用。 --- ## 7. 因子提交链路 明细采集完成后,同一进程内顺带完成「聚合 → 落数据源 → 提交因子」三步(源码 `run_factor` → `build_factor_panel` / `save_factor_csv` → `commit_factor` → `write_factor_datasource` / `submit_factor`)。**本脚本不做本地因子分析**(不算 IC/分层/绩效), 提交时只带因子口径参数,因子分析由 AIStudio 平台完成。 | 步骤 | 代码 | 说明 | | --- | --- | --- | | ① 聚合 | `build_factor_panel(rows)` | `instrument` 直接取明细 `instrument` 列(采集时已带 `.SH/.SZ/.BJ` 后缀),`name` 取成功率最大值那一行的股票名称;同 `(date, instrument)` 多条上榜**取成功率最大值**;成功率空值或未带交易所后缀的行丢弃 | | ② 落盘 | `save_factor_csv(panel, out_dir)` | 写 `a_lhb_success_rate.csv`:`success_rate` 带 `%` 后缀落盘(`format_rate()`),读回时剥离(`parse_rate_cell()`);丢掉与本次数据日期相同的旧行、合并后整份重写(幂等 + 跨日累积) | | ③ 落数据源 | `write_factor_datasource(panel)` | `dai.DataSource.write_bdb(data=…, id="alpha_lhb_success_rate_data", unique_together=["date","instrument"], indexes=["date"])`;落库前 `date` 转 `datetime64[ns]`、`success_rate` 剥离 `%` 后转 `float64` 并 `dropna` | | ④ 提交 | `submit_factor(params, datasource)` | `factors.submit_factor(id="alpha_lhb_success_rate", performance_index=params, performance_report="", metadata={}, docs={…}, name="龙虎榜成功率因子", desc=…, factor_sql=FACTOR_SQL, datasource_id=…, datasource_column="success_rate", space_id="")` | 提交参数 `performance_index`(`--group-num` / `--instruments` / `--benchmark` 可覆盖): ``` {"group_num": 10, "factor_field": "success_rate", "instruments": "全市场", "factor_direction": 1, "benchmark": "中证500", "data_process": True} ``` `factor_sql` 为 **DuckDB 方言**的透传声明:成功率来自接口「解读」文本的本地正则解析,平台侧无法 复算原始 `EXPLAIN`,故 SQL 只声明「因子数值以 `datasource_id.datasource_column` 指向的 `success_rate` 列为准」, 不做任何转换(全文见脚本常量 `FACTOR_SQL`)。 **优雅降级(本机 = 非 AIStudio 环境)**:`write_factor_datasource()` 内 `import pandas`、`import dai` (失败回退 `from bigquant import dai`)、`submit_factor()` 内 `from bigalpha import factors` 均裹在 `try/except ImportError` 中,缺失时只打印提示并返回,**不抛异常、不影响采集产物与守护进程存活、退出码仍为 `0`**。实测输出: ``` 因子面板已写入 57 行(本次数据日期 2026-09-15),文件累计 57 行 -> …\a_lhb_success_rate.csv 未检测到 bigalpha SDK(仅 AIStudio 环境提供),跳过因子提交;本地结果见 a_lhb_success_rate.csv, 可在 AIStudio 端跑 --from-csv 完成提交 ``` **平台侧权限**:本机 `bigquant` SDK 已导出 `dai`(`from bigquant import dai` 实测可用), `write_bdb` 会被真实调用;若平台返回 `请先申请SDK使用权限`(本机实测即此状态), `commit_factor()` 会额外打印该提示并指明「请到 BigQuant 申请 SDK 使用权限,或把本脚本与 `a_lhb_success_rate.csv` 上传到 AIStudio 后执行 `--from-csv`」。 异常隔离:`commit_factor()` 分别 `try` 住「落库」与「提交」,失败只打 `[提示] 因子数据源落库失败:…` / `[提示] 因子提交失败:…`,采集与因子面板落盘已完成的产物不受影响。 **AIStudio 端回补**:若已在 AIStudio 环境,可用 `--from-csv` 直接读本地已落盘的 `a_lhb_success_rate.csv`(不联网、不重新采集)走完落库 + 提交: ```powershell python eastmoney_tradedetail.py --from-csv ``` **上传清单**:AIStudio 上只需上传本脚本 `eastmoney_tradedetail.py` 单文件即可跑通采集 + 提交 (因子面板落盘只用标准库 `csv`,`pandas` 仅在落库分支内惰性导入)。 --- ## 8. 已知限制 1. **同股多条上榜的聚合口径已定**:同一交易日同一只股票可能因多条上榜原因重复上榜(示例日 73 行仅 57 只证券),**明细按接口原样逐条保留**,因子面板侧统一取**成功率最大值**合成为一条 `(date, instrument)`(`build_factor_panel()`);如后续需要最小 / 均值 / 按上榜原因拆分,改这一处即可。 2. **成功率依赖文本解析**:`success_rate_factor` 完全依赖「解读」(`EXPLAIN`) 文本的措辞, 该字段为空(实测可转债如 111024)或文案改版(如改写成「胜率」)时该列为空, 该行也**不进因子面板**;正则一旦漏配需同步更新 `SUCCESS_RATE_RE`。 成功率本身是东方财富的口径,并非本地计算指标。 3. **非交易日 / 未更新**:接口对非交易日不返回数据,单次模式打印「无龙虎榜详情数据」并以退出码 `1` 结束、 **不生成 CSV**;守护模式取的是「最近有数据的交易日」,周末与节假日到点会重复采集上一交易日 (同名文件整份覆盖,不产生脏数据),且**只覆盖明细 CSV、不重复提交同一批因子**。 4. **因子面板跨日累积、明细按日分片**:明细文件按日期一份,历史回补需按日期逐个执行 `--date`; 因子面板为单文件跨日累积(幂等重写),多日样本便于平台做因子分析。 5. **平台侧 SDK 权限**:本机 `bigquant` SDK 已导出 `dai`(`import dai` 失败时脚本回退 `from bigquant import dai`),落库动作会**真实发出**;但本机账号当前被平台以 `请先申请SDK使用权限` 拒绝写入(读接口可用)。**这属于平台侧账号权限,脚本无法绕过**—— 需到 BigQuant 申请 SDK 使用权限,或把本脚本与 `a_lhb_success_rate.csv` 上传到 AIStudio 后执行 `--from-csv`(AIStudio 同时提供 `bigalpha`,可一并完成提交)。 6. **常驻进程的生存期**:守护模式依赖该进程/窗口存活——注销、重启机器、关闭窗口、RDP 会话被强制 结束都会终止它。**重启后需重新双击 `.bat`**(这是「内存常驻」方案的固有限制,若不接受请改用第 6 节备选方案)。 7. **接口为第三方非公开接口**:字段名(`EXPLAIN` / `BILLBOARD_*` 等)与限流策略可能随时变化, 报错会以异常堆栈形式落到日志(守护模式不退出,等下一个 21:00 重试)。 8. **无告警通道**:失败只写日志,不会主动通知(如需可在外层加日志关键字监控)。 9. **日志轮转发生在启动时**:归档与保留 30 份的清理只在 `.bat` 启动那一刻执行,常驻进程**运行期间不会切分** (守护本身不重启,所以日志仍会随每交易日缓慢增长,量级约 1~2 KB/交易日);需要在运行中就切分, 就得改用第 6 节备选方案(计划任务每日拉起单次采集,天然每天一份日志)或改由 Python 自持日志句柄按大小切分。

用例
脚本:`eastmoney_tradedetail.py`(与本文件同目录) | 定时:**每天 21:00,内存常驻**(`run_tradedetail_daemon.bat`), 采集完成后**顺带落因子面板并提交**(`a_lhb_success_rate.csv` → 平台数据源 → 因子) 前置约定: - 工作目录 `c:\Users\Administrator\Documents\trae_projects\dragon\`(产物 CSV 与日志均落在此目录); - 依赖仅 `requests`(其余为标准库);本机虽已装 `bigquant` SDK(`from bigquant import dai` 可用, 脚本会回退到它),但**平台侧未开通 SDK 写权限**,落库/提交会返回提示并按降级处理(见用例 6)。 - 本地 Python:`C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe`; - 网络可访问 `datacenter-web.eastmoney.com`。 > 本工具做两件事:① 采集明细(用例 1~5);② 把明细聚合成 `date / instrument / name / success_rate` 因子面板并提交平台(用例 6~7)。 > 下列用例**不做本地回测 / IC / 绩效校验**(因子分析由平台完成);因子落库与提交在本机**自动降级**, > 只核对降级文案与控制台输出,真正的落库/提交需在 AIStudio 环境执行(或先到 BigQuant 申请 SDK 使用权限)。 --- ## 用例 1:常驻守护启动(内存常驻) **前置**:`run_tradedetail_daemon.bat` 与本脚本同目录;`.bat` 中 `PY` 指向本机实际 Python 路径。 **执行**:双击 `run_tradedetail_daemon.bat`(或 ```powershell cd c:\Users\Administrator\Documents\trae_projects\dragon & "C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe" eastmoney_tradedetail.py --daemon --at 21:00 ``` )。 **预期输出**(实测,启动时刻 2026-09-16 21:01:58,当日 21:00 已过,故顺延到次日) ``` 守护模式已启动:进程常驻内存,每天 21:00 采集一次(Ctrl+C 退出) 下次采集时间 2026-09-17 21:00:00(等待 23.97 小时) ``` (若在当日 21:00 **之前**启动,第二行为 `下次采集时间 2026-09-16 21:00:00(等待 0.00 小时)`。) **检查点** 1. 进程不退出、CPU 占用接近 0(阻塞在 `time.sleep`,不是轮询); 2. 日志 `tradedetail_daily.log` **立即**出现内容(行缓冲生效,不必等进程退出)。实测日志: ``` [周三 2026/09/16 21:01:58.56] ===== START resident daemon (daily 21:00) ===== 守护模式已启动:进程常驻内存,每天 21:00 采集一次(Ctrl+C 退出) 下次采集时间 2026-09-17 21:00:00(等待 23.97 小时) ``` 3. 打印的「下次采集时间」为**当天或次日 21:00:00**(上例启动时 21:00 已过,故顺延到次日); 4. 退出时日志追加一行 `===== resident daemon exited (errorlevel=0) =====`(`Ctrl+C` 正常退出; 直接关窗口 / 强杀进程时该行不会写入); 5. **启动时日志轮转**:若启动前已存在 `tradedetail_daily.log`(= 上一轮守护留下的),它会先被改名归档为 `log\tradedetail_daily_.log`(内容原样保留),`log\` 内只保留最近 **30** 份, 主日志则从本轮 `START` 标记重新开始。实测(启动前手工造 1 份主日志 + 32 份假归档):启动后 `log\` 内共 **30** 份、最旧 3 份被删除,归档内容与原主日志逐字一致,主日志首行为本轮 `START` 标记。 **异常** - 日志出现 `FAILED - python not found` ⇒ `.bat` 里的 `PY` 路径在本机不存在(换机器常见),改为本机 `where python` 的绝对路径; - 双击后窗口一闪而过 ⇒ 改为在 PowerShell 里直接跑脚本本体观察报错(多为 `requests` 未安装): ```powershell cd c:\Users\Administrator\Documents\trae_projects\dragon & "C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe" eastmoney_tradedetail.py --daemon --at 21:00 ``` --- ## 用例 2:到点自动采集(21:00,无需人工干预) **前置**:用例 1 的守护进程处于运行状态,窗口保持开启。 **执行**:不干预,等待 21:00 自动唤醒。 **预期输出**(格式如下,逐日一行日志) ``` 下次采集时间 2026-09-16 21:00:00(等待 0.00 小时) ===== 2026-09-16 21:00:00 开始采集 数据日期=2026-09-16 ===== 共 73 条 / 1 页 已写入 73 条 -> C:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_detail_2026-09-16.csv 其中解析到成功率 71 条 / 未解析到 2 条 因子面板已写入 62 行(本次数据日期 2026-09-16),文件累计 119 行 -> C:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_success_rate.csv [提示] 因子数据源落库失败:请先申请SDK使用权限. Detail: Unavailable. gRPC client debug context: ... [提示] 该报错来自平台侧权限校验(不是脚本问题):请到 BigQuant 申请 SDK 使用权限,或把本脚本与 a_lhb_success_rate.csv 上传到 AIStudio 后执行 --from-csv 落库并提交 未检测到 bigalpha SDK(仅 AIStudio 环境提供),跳过因子提交;本地结果见 a_lhb_success_rate.csv,可在 AIStudio 端跑 --from-csv 完成提交 下次采集时间 2026-09-17 21:00:00(等待 23.99 小时) ``` **检查点** 1. 日志中「开始采集 数据日期=」等于期望的交易日(当日为交易日即为当日); 2. 生成 `a_lhb_detail_<数据日期>.csv`,且行数 = 日志中的「已写入 N 条」+ 1 行表头; 3. 「解析到成功率 + 未解析到 = 总条数」恒成立; 4. 采集完成后再次打印「下次采集时间 = **次日** 21:00:00」,两次采集至少间隔 60 秒 (`DAEMON_LOOP_GAP`,避免同一分钟内重复采集); 5. 「数据日期 == 当日」时顺带跑因子链路,日志紧跟「因子面板已写入 …」与提交 / 降级两行(见用例 6); 若为**非交易日**(数据日期 ≠ 当日),只打印「… 不是今日(非交易日或接口尚未更新),跳过因子提交」, **不重复提交**同一批因子。 **异常** - 日志出现 `采集失败,21:00(次日)重试:` + `Traceback` ⇒ 网络抖动 / 接口变更; **守护进程不退出**,下一个 21:00 自动重试,无需人工重启; - 非交易日(周末、节假日)到点执行时,`latest_trade_date()` 取到**上一交易日**, 输出仍为「已写入 N 条」,同名文件被整份覆盖,不会产生脏数据。 --- ## 用例 3:手工单次采集(指定交易日) **执行**(加 `--no-submit` 把采集链路与提交链路分开验证,提交链路见用例 6) ```powershell cd c:\Users\Administrator\Documents\trae_projects\dragon & "C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe" eastmoney_tradedetail.py --date 2026-09-15 --no-submit ``` **预期输出**(实测) ``` 数据日期=2026-09-15 共 73 条 / 1 页 已写入 73 条 -> C:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_detail_2026-09-15.csv 其中解析到成功率 73 条 / 未解析到 0 条 因子面板已写入 57 行(本次数据日期 2026-09-15),文件累计 57 行 -> C:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_success_rate.csv --no-submit:跳过因子数据源落库与提交 ``` **检查点** 1. 退出码 `0`; 2. `a_lhb_detail_2026-09-15.csv` 存在,表头 19 列,数据 73 行; 3. 「共 73 条 / 1 页」⇒ 单页即取完(`pageSize=500`),无翻页打印; 4. 「因子面板已写入 57 行」= 73 行明细按 `(date, instrument)` 聚合后的唯一证券数(去重 + 丢空,见用例 6); 5. 「文件累计」为 `a_lhb_success_rate.csv` **跨日累积**后的总行数,单日独跑时等于本次行数。 **重复执行**:再次运行同一条命令,明细 CSV 与因子面板**两处都是整份重写**,输出与文件内容完全一致 (不追加、不产生重复行);因子面板按「先丢掉本次数据日期的旧行再合并」实现,同日期重复执行结果不变。 **异常**:非交易日或接口尚未更新时输出 ``` 数据日期=… 共 0 条 ... # 或 2026-09-15 无龙虎榜详情数据(可能非交易日或尚未更新) ``` ⇒ 无数据时**不生成 CSV**、退出码 `1`(守护模式不受此影响,会在下一个 21:00 重试)。 --- ## 用例 4:读取产物 ```python import pandas as pd df = pd.read_csv(r"c:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_detail_2026-09-15.csv", encoding="utf-8-sig") print(df.shape) # (73, 19) print(list(df.columns)[:6]) # seq / date / instrument / name / explain / success_rate_factor print(df["success_rate_factor"].describe().round(3).to_dict()) print(df["market"].value_counts().to_dict()) print(df["instrument"].nunique()) # 57 ``` **实测输出** ``` (73, 19) ['seq', 'date', 'instrument', 'name', 'explain', 'success_rate_factor'] {'count': 73.0, 'mean': 36.949, 'std': 11.521, 'min': 1.67, '25%': 29.22, '50%': 39.9, '75%': 43.31, 'max': 62.85} {'SZ': 37, 'SH': 29, 'BJ': 7} 57 ``` **检查点** 1. 列数 19、列名与「表结构」文档第 1 节完全一致(第 6 列为 `success_rate_factor`); 2. `success_rate_factor` 为**百分数数值**(`40.14` 即 40.14%),不是 0.4014; 3. 行数 73 > `instrument` 去重数 57 ⇒ 存在**同股当日多条上榜**(属预期,见「表结构」第 1 节说明); 4. `market` 只出现 `SH / SZ / BJ` 三种取值。 **另一日对照(2026-09-16,含未解析样本)** ``` (73, 19) {'count': 71.0, 'mean': 37.46, 'std': 9.051, 'min': 3.59, '25%': 34.26, '50%': 39.24, '75%': 42.11, 'max': 61.75} {'SZ': 35, 'SH': 33, 'BJ': 5} 63 ``` 未解析到的 2 行: ``` seq instrument name explain 27 111024.SH 澳弘转债 NaN 28 111024.SH 澳弘转债 NaN ``` ⇒ 可转债上榜时接口 `EXPLAIN` 为空,`explain` 与 `success_rate_factor` 双空(对应「文档」第 8 节限制 2), 且该行**不进**因子面板 `a_lhb_success_rate.csv`(见用例 6)。 --- ## 用例 5:成功率解析口径核对(离线,不依赖接口) **目的**:确认 `success_rate_factor` 就是从同行「解读」文本里抽出来的数字,单位未被换算。 **执行** ```python import re, pandas as pd df = pd.read_csv(r"...\dragon\a_lhb_detail_2026-09-15.csv", encoding="utf-8-sig") pat = re.compile(r"成功率\s*[::]?\s*([0-9]+(?:\.[0-9]+)?)\s*%") # 逐行重算,与落盘值比对 bad = [r.seq for _, r in df.iterrows() if r.explain and (lambda m: (m is None) ^ pd.isna(r.success_rate_factor) or (m and round(float(m.group(1)), 2) != r.success_rate_factor))(pat.search(r.explain))] print("不一致行数:", len(bad)) print(df.loc[0, ["explain", "success_rate_factor"]].tolist()) ``` **实测输出** ``` 不一致行数: 0 ['主力做T,成功率40.14%', 40.14] ``` **检查点** 1. 不一致行数为 0(示例日 73/73 全部命中); 2. 解读原文里的 `40.14%` 在列中即 `40.14`(**带 % 语义、无 ×100**); 3. 2026-09-16 日核对时,未解析到的行正好是 `explain` 为空的那 2 行(111024 ×2), 即「空解读 ⇒ 空成功率」,不存在解析错位的行。 **异常**:若出现不一致行,检查「解读」文案是否被东方财富改版(例如「成功率: 40.14%」以外的写法), 按「表结构」第 4 节正则口径更新 `SUCCESS_RATE_RE`。 --- ## 用例 6:因子面板与提交链路(默认 → 本机权限受限 + 提交降级) **目的**:核对「明细 → 因子面板 → 数据源落库 → 提交因子」四步。本机有 `pandas` 与 `bigquant`(导出 `dai`, 回退可用),落库请求会**真实发出**、但被平台以 `请先申请SDK使用权限` 拒绝;`bigalpha` 仅 AIStudio 提供。 故本机只验证到**因子面板落盘 + 权限提示/降级文案**;真正的落库 / 提交需先开通 SDK 权限在本机执行, 或在 AIStudio 环境执行同一条命令(不改参数)。 **执行**(不加 `--no-submit` 即默认走提交链路) ```powershell cd c:\Users\Administrator\Documents\trae_projects\dragon & "C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe" eastmoney_tradedetail.py --date 2026-09-15 ``` **预期输出**(实测:落库被平台拒绝 ⇒ 权限提示两行 + 提交降级一行) ``` 数据日期=2026-09-15 共 73 条 / 1 页 已写入 73 条 -> C:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_detail_2026-09-15.csv 其中解析到成功率 73 条 / 未解析到 0 条 因子面板已写入 57 行(本次数据日期 2026-09-15),文件累计 57 行 -> C:\Users\Administrator\Documents\trae_projects\dragon\a_lhb_success_rate.csv [提示] 因子数据源落库失败:请先申请SDK使用权限. Detail: Unavailable. gRPC client debug context: ... [提示] 该报错来自平台侧权限校验(不是脚本问题):请到 BigQuant 申请 SDK 使用权限,或把本脚本与 a_lhb_success_rate.csv 上传到 AIStudio 后执行 --from-csv 落库并提交 未检测到 bigalpha SDK(仅 AIStudio 环境提供),跳过因子提交;本地结果见 a_lhb_success_rate.csv,可在 AIStudio 端跑 --from-csv 完成提交 ``` **检查点** 1. 退出码 `0`;**因子落库 / 提交失败不改变退出码**(只打印 `[提示] …`,不影响本地产物与守护进程存活); 2. 提示与缺失项逐条对应:`dai` 已可用但平台拒绝写(`请先申请SDK使用权限`)⇒ 落库失败提示两行; 缺 `bigalpha` ⇒ 跳过提交一行; 3. 因子面板表头为 `date,instrument,name,success_rate`,`instrument` **带** `.SH/.SZ/.BJ` 后缀, `success_rate` 带 `%` 后缀(如 `29.22%`),`name` 为成功率最大值那一行的股票简称; 4. 同一 `(date, instrument)` 只出现一次:同股多条上榜取成功率**最大值** (09-15 实测 `000523` 的 `22.2 / 29.22` → `29.22`、`002059` 的 `2.3 / 47.23` → `47.23`); 5. 未解析到成功率的行不进面板:09-15 明细 73 行 → 面板 57 行(对应 57 只唯一证券); 6. 跨日累积:再跑一次 `--date 2026-09-16`(62 行)后文件累计 **119 行**(`09-15: 57 / 09-16: 62`)。 **读取面板核对**(离线,仅需标准库) ```python import csv, collections rows = list(csv.DictReader(open(r"...\dragon\a_lhb_success_rate.csv", encoding="utf-8-sig"))) keys = [(r["date"], r["instrument"]) for r in rows] print(len(rows), list(rows[0].keys())) # 119 ['date', 'instrument', 'name', 'success_rate'] print(len(keys) - len(set(keys))) # 0 无重复 (date, instrument) print(collections.Counter(k[-3:] for _, k in keys)) # Counter({'.SZ': 60, '.SH': 49, '.BJ': 10}) print([r for r in rows if r["instrument"] == "000523.SZ"]) # [{'date': '2026-09-15', 'instrument': '000523.SZ', 'name': '红棉股份', 'success_rate': '29.22%'}] ``` **检查点**:`(date, instrument)` 重复键数恒为 `0`;`instrument` 后缀只出现 `.SH / .SZ / .BJ`; 同一代码在同一数据日期只有 1 行且为当日多条记录中的最大值;`success_rate` 文本形如 `<数值>%`。 **异常** - 打印 `[提示] 因子数据源落库失败:…` 或 `[提示] 因子提交失败:…` ⇒ 确已在 AIStudio 环境但落库/提交报错 (常见为 `id` 冲突、数据源列名不符、无权限);采集与本地 CSV 不受影响,修正后重跑即可 (`write_bdb` 按 `date + instrument` 去重更新,可重复执行); - 面板行数为 0 并打印「无可用成功率(全部未解析到或市场未知),跳过因子面板」⇒ 当日明细 `success_rate_factor` 全空(接口文案改版)或 `market` 列取值不在 `SH/SZ/BJ` 内,先按用例 5 核对解析口径。 --- ## 用例 7:`--from-csv` 回补提交(AIStudio 端) **目的**:本地已落好 `a_lhb_success_rate.csv`,而落库 / 提交必须在 AIStudio 环境执行; 不想再联网采集时,用 `--from-csv` 直接复用本地面板文件落库并提交。 **执行** ```powershell cd c:\Users\Administrator\Documents\trae_projects\dragon & "C:\Users\Administrator\AppData\Local\Programs\Python\Python314\python.exe" eastmoney_tradedetail.py --from-csv ``` **预期输出**(实测:落库被平台拒绝 ⇒ 权限提示两行 + 提交降级一行;首行括号内为文件当前累计行数) ``` === 复用因子面板 a_lhb_success_rate.csv(119 行)=== [提示] 因子数据源落库失败:请先申请SDK使用权限. Detail: Unavailable. gRPC client debug context: ... [提示] 该报错来自平台侧权限校验(不是脚本问题):请到 BigQuant 申请 SDK 使用权限,或把本脚本与 a_lhb_success_rate.csv 上传到 AIStudio 后执行 --from-csv 落库并提交 未检测到 bigalpha SDK(仅 AIStudio 环境提供),跳过因子提交;本地结果见 a_lhb_success_rate.csv,可在 AIStudio 端跑 --from-csv 完成提交 ``` **检查点** 1. 退出码 `0`,且**不发起网络请求**(无「数据日期=」「共 N 条 / N 页」等采集打印); 2. 首行括号内行数 == `a_lhb_success_rate.csv` 当前数据行数;该路径**只读文件、不改写**; 3. 开通 SDK 写权限或换到 AIStudio 环境后,同一命令应打印「因子数据源已写入 alpha_lhb_success_rate_data (… 行,因子列 success_rate)」与「已提交因子 alpha_lhb_success_rate(龙虎榜成功率因子),数据源 …」。 **异常**:面板文件不存在时直接报错退出 ``` FileNotFoundError: 找不到因子面板 ...\a_lhb_success_rate.csv,请先正常运行一次采集 ``` ⇒ 先按用例 3 跑一次采集生成面板文件,再执行 `--from-csv`;换了 `--out-dir` 时须保证该目录下有该文件。 --- ## 用例 8:常见问题排查 | 现象 | 原因 | 处理 | | --- | --- | --- | | `success_rate_factor` 整列空 | 「解读」为空或文案改版(实测可转债 `111024` 的 `EXPLAIN` 为空) | 属预期;核对 `explain` 列原文,若为文案改版则更新 `SUCCESS_RATE_RE` | | 非交易日运行打印「无龙虎榜详情数据」并退出码 `1` | 当日无榜单(周末 / 节假日 / 尚未更新) | 换交易日重跑;守护模式不会因此中断 | | 日志长时间空白(进程已启动但无输出) | 输出被重定向时 Python 默认块缓冲 | 已通过 `sys.stdout.reconfigure(..., line_buffering=True)` 修复;若手工改回,需恢复该行 | | 守护进程在第二天不再采集 | 窗口被关闭 / 注销 / 机器重启 / RDP 会话被结束 | 重新双击 `run_tradedetail_daemon.bat`(「内存常驻」方案的固有约束) | | 日志出现 `FAILED - python not found` | 换机器后 `.bat` 中 `PY` 路径失效 | 改成新机器 `python.exe` 的绝对路径 | | 目录里多出 `log\` 与 `log\tradedetail_daily_*.log` | 启动时轮转产生的归档日志(见用例 1 检查点 5) | 属预期;只保留最近 30 份,无需手工清理 | | 重复双击 `.bat` 后旧日志未被归档,仍往同一文件追加 | 上一轮守护仍在运行,日志文件被 cmd 重定向句柄占用,`move` 失败(该错误已静默) | 属预期,不丢数据;先 `Ctrl+C` 关掉上一轮再启动即可归档 | | 到点后反复采集同一数据日期 | 当日为非交易日,接口返回的最新交易日仍是上一交易日 | 属预期(同名文件整份覆盖);交易日恢复正常 | | `ModuleNotFoundError: No module named 'requests'` | 依赖缺失 | `& "" -m pip install requests` | | 采集条数与页面显示不一致 | 页面按当前筛选 / 分页展示,脚本按 `TRADE_DATE` 取**全量**(含多条上榜原因) | 以 CSV 行数与日志「已写入 N 条」为准 | | 打印「[提示] 因子数据源落库失败:请先申请SDK使用权限…」 | 本机 `bigquant.dai` 已可用、落库请求真实发出,但账号未开通 SDK 写权限(平台侧校验,脚本无法绕过) | 到 BigQuant 申请 SDK 使用权限;或把本脚本与 `a_lhb_success_rate.csv` 上传到 AIStudio 后跑 `--from-csv`(见用例 7) | | 打印「未检测到 bigalpha SDK(仅 AIStudio 环境提供)…跳过因子提交」 | `bigalpha` 只随 AIStudio 环境提供,本机装不上 | 属预期;真正提交在 AIStudio 端执行(见用例 6/7),本地结果直接看 `a_lhb_success_rate.csv` | | 打印「[提示] 因子数据源落库失败 / 因子提交失败」(非权限类) | 已在可写环境但落库或提交报错(`id` 冲突 / 数据源列名不符) | 采集与本地 CSV 不受影响;按提示修正后重跑,或用 `--from-csv` 回补(见用例 7) | | `--from-csv` 报 `找不到因子面板 …,请先正常运行一次采集` | 尚未跑过采集,或 `--out-dir` 指到了别的目录 | 先跑一次采集生成 `a_lhb_success_rate.csv`(见用例 7) | | 守护模式下某日打印「不是今日…跳过因子提交」 | 该日非交易日,`latest_trade_date()` 取到上一交易日 | 属预期:明细 CSV 仍整份覆盖,**不重复提交**同一批因子 | | 因子面板文件行数逐日变大 | 跨日累积(明细按日分片、面板单文件累积) | 属预期,便于平台按多日样本做因子分析;同一数据日期重复执行不会增加行数 | | 想停掉常驻守护 | — | 在该窗口按 `Ctrl+C`(或直接关闭窗口);日志会追加 `===== resident daemon exited (errorlevel=…) =====` |
表结构
字段 字段类型 字段描述
success_rate double -
instrument string -
date timestamp[ns] -
name string -

表名:alpha_lhb_success_rate_data

起始时间:

最近更新时间: