# 市场状态评分器（regime_check.py）

一个纯 stdlib、零 AI 的定时脚本，拉一组免费市场指标，每个指标按"逆向累积"
思路投一个整数票（正 = 低估/恐慌 = 适合做多，负 = 过热/贪婪 = 风险），
汇总成 regime 标签，写 `regime.json` 供 Freqtrade 开仓过滤或状态页读取。

部署：`/data/freqtrade/user_data/regime_check.py`，crontab `0 * * * *`（每小时整点），
日志 `user_data/logs/regime.log`。

## 指标与投票

| 指标 | 数据源 | 投票规则（简版） |
|---|---|---|
| Fear & Greed | api.alternative.me | <25 极恐 +2；<45 恐慌 +1；45-55 中性 0；<75 贪婪 -1；≥75 极贪 -2 |
| AHR999 | 币安 200 日线自算 | <0.45 抄底区 +2；<1.2 定投区 +1；<5 中性 0；≥5 顶部区 -2 |
| 200DMA 偏离 | 币安 200 日线自算 | <-15% 深跌破 +1；-15%~20% 附近 0；>20% 上方拉伸 -1 |
| BTC 资金费 | 币安永续 | <-0.005% 负费率（散户看空）+1；正常 0；>0.03% 高费率（多头拥挤）-1 |
| 稳定币 30 日 | DefiLlama | 缩水 -1；持平 0；增长（新流动性）+1 |
| MVRV Z-score | bitcoin-data.com | <0 深度低估 +2；<2 累积 +1；<5 中性 0；<7 偏高 -1；≥7 顶部 -2 |

分数区间约 -9..+9，映射：`≥5 RISK_ON_STRONG` / `≥2 RISK_ON` / `>-2 NEUTRAL` /
`>-5 RISK_OFF` / 其余 `RISK_OFF_STRONG`。投票权重全部写死在脚本注释里，可手调。

### AHR999 计算

无需第三方，用币安 200 根日线自算，与公开面板（如 Bewin Quant）数值吻合：

```
geomean200 = exp(mean(ln(每日收盘)))
币龄 = 今天 - 2009-01-03（创世）
拟合价 = 10^(5.84 * log10(币龄天数) - 17.01)   # 币龄估值模型
AHR999 = (现价 / geomean200) * (现价 / 拟合价)
```

## 输出

`regime.json` 示例字段：`regime`、`score`、`sources_ok/failed`、`indicators[]`（每个含
value/vote/note）、`allow_long`（布尔，供策略过滤：True = 允许新开多）。

## 容错（fail loud + fail closed）

任一数据源失败 → 从评分中剔除、输出标 `⚠ DEGRADED`、进程非零退出（cron 日志/邮件可捕获）。
写快照时先落临时文件再原子替换，避免策略读到半截 JSON。残缺评分仍会写入日志和历史供诊断，
但策略不会用它放行新多仓。

## 与 Freqtrade 的接入

**已接入（试点）：`VolatilityBreakout`**（2026-07-07 接入 V1，2026-07-08 起 bot1
换用 V5 逻辑，2026-07-09 合并回 `VolatilityBreakout`，2026-07-10 归档到策略族文件
`strategies/volatility_breakout/v5_d48.py`，闸门逻辑原样保留，
见 docs/strategy-iterations.md）。策略在
`confirm_trade_entry` 里读 `regime.json` 的 `allow_long`，作为「开多总闸」：

- `allow_long: false`（`RISK_OFF` / `RISK_OFF_STRONG`，即 score ≤ -2）→ 拒绝新开多单。
- 只闸开多；**做空不受影响**（froth/greed 对做空反而合理）。
- 已有持仓、出场逻辑（ROI/止损/移动止损）一律不受影响。
- **fail-closed 兜底**：`regime.json` 缺失、解析失败、时间在未来、超过
  `REGIME_MAX_AGE_H`（默认 3h）或 6 个数据源未全部成功，一律拒绝新开多仓并写 warning/error。
  现有仓位和止损不受影响；数据恢复后自动恢复放行。
- 拦截/放行均写 `freqtrade.log`（`grep regime` 可查）。

其余策略（`HighVolMeanRev`、`RisingVolTrend`）**暂未接入**，观察试点几周后再决定是否推广。
注意 `regime.json` 是实时快照、无历史序列，**回测无法体现该过滤**，只能在 dry-run/实盘生效。
为此 V5 另加了一个**可回测代理**：开多需 BTC 日线收盘 > 200DMA（informative pair 在策略内
计算，回测/实盘行为一致）。两道闸并存（AND 关系）：200DMA 代理管回测可验证性，
regime.json 管实时多因子。这里选择可用性让位于风险：过滤器故障最多错过多头机会，不能在
无法确认市场状态时扩大多头敞口。

策略显式检查 `self.config["runmode"]`：仅 `live` / `dry_run` 读取当前快照；
`backtest` / `hyperopt` 直接跳过实时闸门。这遵循 Freqtrade 对外部实时数据的官方用法，
也避免当前快照被历史时间误判为“未来数据”后拦掉全部历史多头。官方说明见
[Strategy callbacks](https://www.freqtrade.io/en/stable/strategy-callbacks/#bot-start)。

两道闸的哲学并不相同，会出现方向矛盾（这是设计内的，不是 bug）：评分器对
「深跌破 200DMA」投**逆向抄底票**（如 2026-07-08：偏离 -15.3% → +1，总分 +5 RISK_ON_STRONG），
而 V5 闸门把同一状态当**熊市趋势**拦掉所有新开多。AND 关系下慢速趋势闸优先，
评分器的增量价值只在「BTC 已站上 200DMA 但情绪/资金费过热（score ≤ -2）」时兑现。

## 历史落盘（2026-07-08 起）

每次运行除覆盖写 `regime.json` 外，追加一行到 `user_data/regime_history.jsonl`
（gitignore）：`ts / regime / score / allow_long / 各因子 {value, vote} / failed`。
目的：`regime.json` 无历史导致 6 因子闸门永远只能「信」不能「验」；攒够几个月序列后，
可在回测里按时间戳回放 `allow_long`，量化其相对纯 200DMA 代理的增量。在此之前，
评分器对策略的实际贡献视为**未验证**。

## 数据源可达性备忘

均从利雅得服务器验证可达（本地网络对部分接口有 SNI 拦截）：
api.alternative.me、api.binance.com / fapi.binance.com、stablecoins.llama.fi、
bitcoin-data.com。Upbit `api-manager` 有 Cloudflare 拦截，不可直接抓。
