Initial commit

This commit is contained in:
freedakgmail
2026-07-17 18:49:07 +08:00
commit 9c7d7abdd4
100 changed files with 41337 additions and 0 deletions
@@ -0,0 +1,131 @@
# 推荐算法回测方案 — 约定与设计
## 一、回测规则约定(已确认)
| 项目 | 约定 | 说明 |
|------|------|------|
| **10:00 买入依据** | 用**前一交易日收盘后**的全景扫描推荐 | 与当前应用一致,无未来数据 |
| **15:00 加仓/清仓依据** | 用**当天中午**的全景扫描数据 | 即当日 11:50 左右的扫描结果(与现有定时任务一致) |
| **成交价格** | 用**当时的实时价格** | 10:00 买入用 10:00 附近价,15:00 操作用 15:00 附近价;若无分钟数据则需约定近似方式 |
| **选股** | **每天最多买 2 只** | 全市场从「买入」中按推荐分取前 2 只,每只 1000 股(可配置 `MAX_BUYS_PER_DAY` |
## 二、时间线小结
- **T 日 10:00**:根据 **T-1 日收盘后** 的扫描结果,若出现「买入」则按约定选**最多 2 只**(推荐分从高到低),每只以 **10:00 实时价** 买入 1000 股。
- **T 日 15:00**:根据 **T 日中午**(约 11:50)的扫描结果,对当前持仓做 `compute_recommend(..., is_holding=True)`,若为「加仓」则以 **15:00 实时价** 加仓 1000 股,若为「卖出」则以 **15:00 实时价** 清仓。
- 价格:优先使用「当时实时价格」;回测若无分钟/实时数据,需在实现中明确近似规则(见下)。
## 三、实现要点与数据需求
### 3.1 10:00 推荐与价格
- **推荐**:对每个交易日 T,用 **T-1 收盘** 的日 K 跑 `detect_all_signals`,再 `compute_recommend(..., is_holding=False)`,筛出「买入」,按约定选单只(如推荐分最高或信号数最多)。
- **价格**:理想为 T 日 10:00 实时价;若无分钟数据,可用 **T 日开盘价** 作为近似,并在文档/结果中注明。
### 3.2 15:00 推荐与价格(当天中午扫描)
- **推荐**:使用 **T 日中午全景扫描** 结果。
- 若回测期内有**历史中午扫描落库**(如 `stock_signal_scan` 带 scan_time 或 scan_date=当日且标记为午扫),可直接用。
- 若没有,则需在回测中**模拟「当日中午」的扫描**:用 T 日 11:50 前可得的数据(例如 T-1 日 K + T 日 open,或若有分钟数据则用 T 日 11:30 前数据)跑一次 `detect_all_signals` + `compute_recommend`,作为当日 15:00 决策依据,且不引入 T 日 15:00 之后的数据。
- **价格**:理想为 T 日 15:00 实时价;若无分钟数据,可用 **T 日收盘价** 作为近似,并注明。
### 3.3 实时价格的回测实现
- **有实时/分钟数据**:按 10:00 / 15:00 时刻取价。
- **仅日 K**:在方案中明确写「回测采用:10:00 用当日 open15:00 用当日 close」,并视为对「当时实时价格」的近似,在结果与文档中统一说明。
## 四、选股规则(每天最多买 2 只)
- 在「买入」列表中按推荐分从高到低排序,**最多选 2 只**(`MAX_BUYS_PER_DAY=2`):
-`recommend_rate` 从高到低,同分再按 `triggered_count` 或信号强度排序;
- 每只买入 1000 股(开盘价)。
- 若当日无「买入」,则不新开仓;仅对已有持仓做 15:00 的加仓/清仓判断。
## 五、回测程序与运行
1. **脚本位置**`stock-html/backtest_recommend.py`
2. **依赖**:需存在表 **`stock_kline_daily`** 且含 2026 年及以后的日 K 数据(可先运行 `sync_kline.py` 等同步脚本)。
3. **运行**:在项目根目录下执行
```bash
cd stock-html && ./venv/bin/python backtest_recommend.py
```
4. **输出**:控制台打印交易记录 + 核心统计(胜率/回撤/持仓天数/盈亏比/个股明细);结果写入 `docs/backtest_result.json`。
5. **约定**10:00 用 T-1 扫描 + 开盘价,15:00 用当日中午扫描(T-1 + T 日 open 模拟)+ 收盘价,每天最多买 2 只(推荐分从高到低取前 2)。
### v2 优化(2026-02-25
| 优化项 | 说明 |
|--------|------|
| **股价区间过滤** | 仅扫描 2~100 元股票,避免仙股和高价股导致金额失衡 |
| **单只最大投入** | ¥30,000 上限(含加仓),防止单只占比过高 |
| **最大同时持仓** | 8 只,分散风险 |
| **卖出冷却期** | 卖出后 3 天内不再买入同只,防止反复买卖 |
| **批量 K 线加载** | 用窗口函数一次查全市场 K 线,减少 DB 往返约 10 倍 |
| **丰富统计指标** | 胜率、最大回撤、平均持仓天数、盈亏比、个股盈亏明细 |
| **对比表增强** | 15 场景对比增加胜率/回撤/盈亏比列,标注最优场景 |
### v3 优化(2026-02-25
| 优化项 | 说明 |
|--------|------|
| **基于预扫描表** | 直接从 `stock_scan_history` 读取扫描结果,无需现场计算,秒级完成回测 |
| **修复最大回撤计算** | 改为基于每日组合权益(持仓市值 + 累计现金流)的回撤,修复之前用现金流计算导致数值虚高的问题 |
| **新增回撤百分比** | 输出 `max_drawdown_pct`(最大回撤 / 总投入资金 × 100%) |
| **运算符优先级修复** | 修复 `action_taken` 判断条件的括号缺失问题 |
以上约定已写入本文档,作为实现推荐回测的统一依据。
---
## 六、清仓与止盈推荐(目的:挣钱而非单纯持有)
当前回测里**清仓**只做一件事:**推荐为「卖出」时按收盘价清仓**(即 MACD 死叉且无主升浪)。
目的是「按信号纪律出场」,但**没有**针对「高点落袋」的规则,可能拿很久才等到卖出信号,回吐利润。
### 6.1 建议增加的清仓/止盈方式
在保留「信号卖出」的前提下,可增加**以挣钱为导向**的止盈类规则,例如:
| 类型 | 说明 | 目的 |
|------|------|------|
| **固定止盈** | 持仓浮盈 ≥ X%(如 10%、15%)时,当日 15:00 清仓 | 到点落袋,不贪最后一笔 |
| **高点回撤止盈** | 从持仓期间最高价回撤 ≥ Y%(如 8%)时清仓 | 近似「高点跑」,锁定大部分利润 |
| **信号卖出(现有)** | 推荐为「卖出」时清仓 | 趋势走弱时离场 |
可只选一种,或**组合**:先看是否触发止盈,若未触发再看是否触发「卖出」;若都未触发则继续持有或按原规则加仓。
### 6.2 推荐用法(兼顾挣钱与纪律)
- **优先**:在回测中增加 **固定止盈**(如 10%):
- 每个交易日 15:00 前,若 **持仓收益率 ≥ 10%**,则当日 15:00 按收盘价清仓,视为「高点跑」一次。
- **可选**:再增加 **高点回撤止盈**(如 8%):
- 若持仓期间最高价到当前价的回撤 ≥ 8%,则当日 15:00 清仓。
- **保留**:若未触发上述止盈,仍按当前逻辑:推荐「卖出」则清仓,「加仓」则加仓,「持有」则不动。
这样既保留「按推荐买卖」的纪律,又明确加入「高点跑 / 落袋为安」的推荐,更贴近「目的是挣钱」。
---
## 七、如何提供/提升收益率
### 7.1 当前收益率指标
回测脚本已输出并写入 `backtest_result.json`
- **收益率** = (总收回 - 总投入) / 总投入 × 100%,即整段回测的累计收益。
- **年化收益率** = (1 + 收益率)^(365/回测天数) − 1,折算为「若按同样节奏跑满一年」的大致水平,便于和理财/指数对比。
v2 已内置更多指标:胜率、最大回撤、平均持仓天数、盈亏比、个股盈亏明细,均在控制台和 JSON 中输出。
### 7.2 可操作的提升方向
| 方向 | 做法 | 说明 |
|------|------|------|
| **止盈** | 使用 `--take-profit 10` 等 | 浮盈到点即走,减少回吐;可多试 8%、12% 等找合适区间。 |
| **止损** | 在回测中增加「浮亏达 X% 清仓」 | 控制单笔最大亏损,避免深套。 |
| **高点回撤止盈** | 从持仓最高价回撤 Y% 时清仓 | 贴近「高点跑」,锁住大部分利润。 |
| **选股过滤** | 在「买入」列表中提高门槛 | 如仅选 recommend_rate≥90 或 triggered_count≥2,减少弱信号。 |
| **仓位/频率** | 每日买一只 vs 仅无仓时买 | 当前为每日买一只;若改为仅无仓时买,收益曲线会不同,可对比。 |
| **参数扫描** | 对止盈比例、止损比例等做网格 | 批量回测不同参数,看哪组年化/回撤更优。 |
建议先固定一套规则(如 止盈 10% + 信号卖出),跑出基准年化与回撤,再逐项加「止损」「高点回撤止盈」或选股过滤,对比同一区间下收益率与回撤的变化,再决定是否采用。