feat(onechartlab-research): 添加技术调研设计与执行计划,包含报告结构与证据收集流程

This commit is contained in:
yuxuanhui
2026-08-28 11:13:09 +08:00
parent 71dfd22fb7
commit 3da992700b
12 changed files with 1121 additions and 0 deletions
+279
View File
@@ -0,0 +1,279 @@
# OneChartLab「宏观择时」技术调研报告
> 调研日期:2026-08-27;视频补充:2026-08-28;页面:<https://onechartlab.com/macro/>;当前公开数据日期:2026-08-26。
> 目标:还原能够由公开证据支持的指标、状态和贡献逻辑,为后续复现提供技术边界。
## 结论摘要
OneChartLab 的“宏观择时”公开页面、脚本和数据协议中**没有披露**利率、信用、通胀、PMI 等传统宏观输入;已公开部分更准确地说是一个 **A 股行业横截面离散度与拥挤结构指标**。它以 124 个申万二级行业的相对收益分布生成精确离散度 `D`,使用 3 日均值作为默认平滑序列,再把当前值映射到“低位趋同—正常—潜在高切低—极致化抱团”四个区间。由于构建端源码未公开,不能排除其内部还使用了未披露变量。
页面公开了状态阈值、平滑方式、十年百分位、行业贡献恒等关系、方向/结构四象限和数据发布协议,但没有公开最核心的 `D` 构造公式。作者在视频 `04:42—05:13` 进一步说明其设计输入是申万一百多个二级行业近 90 天涨跌幅的横截面差异;这确认了指标语义,仍不能确定它使用标准差、分位距、极差、熵还是其他统计量。作者也把“高切低”限定为资金趋势和风险环境观察,不能把页面区间直接解释成仓位或资产切换指令。所谓“择时”目前是风险环境解释层,而不是一套可直接交易的完整状态机。
本报告把证据分为四类:“已确认”表示页面、公开脚本或响应可直接验证;“作者自述”表示视频中可定位的设计与使用说明;“合理推断”表示有清楚推断链但没有构建端源码证明;“无法确认”表示公开证据不足。视频观点只用于解释设计意图和使用边界,不覆盖页面代码和公开数据已经确认的公式。
## 产品逻辑
页面的目标文案是“观察市场趋势与拥挤程度,判断中长期多空方向”,唯一指标页签叫“高切低指标”,并明确注明“参考国投证券同名指标,非原版算法”。公开方法说明是:使用申万二级行业的相对收益分布衡量行情分化程度,再通过平滑形成离散度序列。作者视频 `04:27—04:42` 同样只称“参考”国投证券同名指标,没有声称复刻原版;`04:42—05:13` 则把观察窗口解释为行业近 90 天涨跌幅差异,未说明“90 天”是自然日还是交易日。
这一产品逻辑可以拆成三层:
1. 主指标层用离散度判断市场处于趋同还是抱团;
2. 对照层把离散度与上证、创业、科创、50、300、1000、2000 七条指数累计表现叠加;
3. 贡献层把离散度变化拆给 124 个行业,解释哪些行业正在推动分化或收敛,以及行业相对方向是向上还是向下。
它没有公开“买入什么、卖出什么、仓位多少”的规则。因此复现时应把输出定义为市场结构状态和行业贡献,不应擅自增加交易信号。
## 公开数据架构
```mermaid
flowchart LR
A[macro/state.json] --> B[current / last_good]
B --> C[series.json]
C --> D[D=precise / MA3=smooth / 7个指数]
B --> E[dispersion manifest]
E --> F[latest180 + 年度数据块]
F --> G[124行业 C/O/normalized + D/MA3/K/K_MA5]
H[sector_rankings/manifest.json] --> I[行业成分排行分片]
I --> J[1/3/5/10/90日个股解释]
```
主入口 [`macro/state.json`](https://onechartlab.com/macro/state.json) 使用 `onechart_macro_daily_bundle_v3`,同时提供 `current` 和 `last_good`。2026-08-27 本轮复核时,两者都指向同一份 2026-08-26 快照:发布状态为 `production`,`revision_watermark=21`,批次是 `batch-20260826-r21-c8768898f3`。主序列位于 [`series.json`](https://onechartlab.com/macro/batches/batch-20260826-r21-c8768898f3/series.json),schema 为 `onechart_macro_v1`。这是动态入口,后续复现必须记录实际响应和观测时间,不能假设 `current` 永远存在或永远与 `last_good` 相同。
主序列含 6365 个日期,从 2000-06-01 到 2026-08-26;前端会裁掉 2002-11-12 以前的数据,默认显示最近 7 年。字段包括 `dates`、`precise`、`smooth` 和七个指数的收盘序列。当前摘要是:
| 指标 | 2026-08-26 值 |
| --- | ---: |
| `precise` | 42.1781 |
| `smooth` | 42.5510 |
| 1 日变化 | -1.7363 |
| 5 日变化 | -15.5529 |
| 精确值十年百分位 | 38.4% |
| 平滑值十年百分位 | 38.6% |
贡献模块使用 `dispersion-public-v2`。公开 manifest 是 [`macro/dispersion/builds/dispersion-20260826-6186c256680b/manifest.json`](https://onechartlab.com/macro/dispersion/builds/dispersion-20260826-6186c256680b/manifest.json),包含 2017-01-25 至 2026-08-26 共 2326 个交易日、124 个申万二级行业和 7 个指数。前端先加载 `latest180.json`,拖动到更早日期时再按年份懒加载数据块。
## 主指标的可确认公式
### 1. 精确值与平滑值
贡献数据把主指标写成 `D`,把平滑值写成 `MA3`。公开 180 日数据逐日验证:
```text
precise_t = D_t
smooth_t = MA3_t
= mean(D_t, D_{t-1}, D_{t-2})
```
180 日窗口内 `MA3` 与 3 日简单移动平均的最大误差约为 `6.67e-11`,可以视为同一公式。前端默认 `mode="smooth"`,用户可以切换到 `precise`。
### 2. 十年百分位
对当前模式的最后一个有效值,前端取当前日期向前十年的所有有限样本,并计算:
```text
percentile_10y = 100 × count(sample <= current) / len(sample)
```
样本包含当前观测,因此百分位不是严格的历史外样本百分位。页面显示的 1 日变化是 `value_t - value_{t-1}`,5 日变化是 `value_t - value_{t-5}`。
### 3. 区间状态
`dispersionZone(value)` 是无记忆的阈值映射,没有滞回、连续多日确认或跨区间冷却:
| 条件 | 页面状态 | 技术含义 |
| --- | --- | --- |
| `value < 30` | 低位趋同 | 行业相对收益分化较低 |
| `30 <= value < 70` | 正常区间范围内 | 常态分化 |
| `70 <= value < 120` | 中高位 · 潜在高切低动力 | 结构抱团较强,存在切换风险 |
| `value >= 120` | 极致化抱团 · 谨慎 | 极端分化环境 |
状态使用当前选择的 `smooth` 或 `precise` 序列。默认平滑值降低了日噪声,但这只是 3 日均值,不是另一个有持续性约束的状态机。
视频 `05:23—05:39` 对区间的口头解释与页面文案一致:30—70 为正常,70—120 为结构性抱团,120 以上是极致化抱团、拥挤或过热。边界实现仍以代码为准:页面在 `value >= 120` 时进入极致区间,而视频口头说的是“超过 120”,不能用口述改变恰好等于 120 的实现行为。
## 指数对照不是择时信号
七个对照指数是 `000001.SH`、`399006.SZ`、`000688.SH`、`000016.SH`、`000300.SH`、`000852.SH`、`932000.CSI`。图中曲线以当前可视时间范围起点为基准,绘制:
```text
chart_value_t = 100 × log(close_t / close_base)
```
tooltip 则显示普通区间收益 `100 × (close_t / close_base - 1)`。这两者是可视化变换,不参与离散度和阈值计算。默认只选择上证指数;选择、缩放或隐藏指数不会改变主指标状态。
## 行业贡献的可确认公式
贡献数据每个交易日包含:
- 主指标 `D`、`MA3`、`K`、`K_MA5`;
- 每个行业的 `normalized`、`return90`、`C`、`O`;
- 七个指数的 `normalized`。
公开 180 日、22320 个行业观测可以验证以下恒等关系:
```text
D_t - D_{t-1} = Σ_i C_{i,t}
K_t = Σ_i O_{i,t}
K_MA5_t = mean(K_t ... K_{t-4})
abs(O_{i,t}) = abs(C_{i,t})
```
其中第一条的最大误差约为 `1.1e-9`,后两条移动平均/求和关系误差约为 `1e-10`。因此:
- `C` 是每个行业对离散度日变化的结构贡献,正数扩大分化,负数缩小分化;
- `O` 保留 `C` 的绝对贡献大小,但把符号重新定向为行业相对向上或向下迁移;
- `K` 是所有定向贡献的合计,`K_MA5` 是其 5 日均值。
公开数据没有说明 `O` 的定向符号如何从行业相对收益生成。`return90`、`normalized` 与方向之间也不存在可以从字段名直接确定的单一规则,所以只能复现消费逻辑,不能复现 `O` 的上游构造。
### 复算说明
上述关系使用 manifest 指向的完整 `latest180.json` 复算。响应 SHA-256 是 `b0f2821c9f72f9d7b583f748b0759b5c0a1cc72f92edc66162418d2bc9b3add3`,与 manifest 一致。以下最小脚本从标准输入检查四个恒等关系:
```python
import json
import sys
from statistics import fmean
rows = json.load(sys.stdin)["rows"]
ma3_error = max(
abs(rows[t]["MA3"] - fmean(row["D"] for row in rows[t - 2 : t + 1]))
for t in range(2, len(rows))
)
delta_error = max(
abs(
(rows[t]["D"] - rows[t - 1]["D"])
- sum(item["C"] for item in rows[t]["industries"])
)
for t in range(1, len(rows))
)
k_error = max(
abs(row["K"] - sum(item["O"] for item in row["industries"]))
for row in rows
)
k_ma5_error = max(
abs(rows[t]["K_MA5"] - fmean(row["K"] for row in rows[t - 4 : t + 1]))
for t in range(4, len(rows))
)
print(ma3_error, delta_error, k_error, k_ma5_error)
```
## 当前 V31 贡献视图
页面允许选择 1、3、5、10、90 个交易日,或在固定 91 点窗口内自定义框选区间。对行业 `i` 和窗口 `(a,b]`,当前脚本使用:
```text
direction_i = Σ O_i,t
structure_i = Σ C_i,t
relative_return_i = normalized_i,b / normalized_i,a - 1
```
贡献榜按 `direction_i` 排序,正向和负向各取 10 个行业。行业状态是方向与结构的四象限:
| `direction` | `structure` | 状态 |
| ---: | ---: | --- |
| `>= 0` | `>= 0` | 进攻 |
| `< 0` | `>= 0` | 走弱 |
| `< 0` | `< 0` | 回落 |
| `>= 0` | `< 0` | 修复 |
主曲线累计 `O`,虚线是累计曲线的 5 日均值;每日柱子的方向由 `O` 决定,颜色由 `C` 决定。因此“向上/向下迁移”和“扩大/缩小分化”是两个不同维度,不能只看柱子正负或颜色之一。
详情层可以读取 1/3/5/10/90 日窗口对应的行业成分股排行。输入契约来自 Tushare:行业成员 `index_member_all`、涨跌幅 `daily.pct_chg`、主力净额 `moneyflow_dc.net_amount`、主买净额 `moneyflow.net_mf_amount`,并使用停牌和上市生命周期数据筛选有效样本。
## 核心离散度 `D` 为什么仍不能复现
页面公开“申万二级行业的相对收益分布”和平滑说明,视频又确认作者意图是衡量一百多个二级行业近 90 天涨跌幅的差异;公开贡献数据同时含有 `return90`。这些证据仍不能把 `D` 推导成某个标准统计量。`D` 并不等于公开 `normalized` 的横截面标准差:2026-08-26 的 `D=42.1781`,而 124 个 `normalized` 的总体标准差约为 104.8181。公开 payload 也没有给出生成 `D` 所用的原始相对收益向量、相对基准、截尾、标准化和历史校准参数。
以下候选都无法由现有证据确定:标准差、分位距、分位数间距组合、极差、熵、MAD、横截面分布拟合或多步归一化。因此只能确认 `D` 的**下游代数关系**,不能确认它的**上游统计定义**。
`state.json` 中的 `accepted_input_hash`、`bundle_id`、`revision_watermark` 和 `last_good`,以及 dispersion manifest 的版本化 `daily_contract`,说明构建链会接受版本化输入并可能修订历史结果。公开页面没有发布修订差分或 point-in-time 输入快照。
## 推荐的复现设计
建议把系统分成两个兼容层:外层完全复现已确认的消费逻辑,内层把核心离散度公式作为明确的可替换接口。
```python
def derive_public_state(dates, precise, industry_contrib):
smooth = sma(precise, window=3)
c = industry_contrib.structure_contribution
o = industry_contrib.oriented_contribution
assert_close(diff(precise), sum_by_day(c))
k = sum_by_day(o)
k_ma5 = sma(k, window=5)
current = smooth[-1] # 页面默认模式
zone = (
"low" if current < 30 else
"normal" if current < 70 else
"elevated" if current < 120 else
"extreme"
)
return smooth, k, k_ma5, zone
```
核心接口可以定义为:
```python
def dispersion_formula(
trade_date,
point_in_time_sw2_membership,
industry_relative_returns,
versioned_parameters,
) -> tuple[float, list[IndustryContribution]]:
"""返回 D_t,以及能够严格加总到 ΔD_t 的 C/O 行业贡献。"""
```
如果目标是先复刻页面而不是复刻专有算法,可以直接消费公开 `series.json` 和 dispersion 分片;如果目标是独立重算,就必须先获得 `D`、`C`、`O` 的构建定义或建立与公开历史序列的校准实验。不能只通过调整标准差倍率宣称等价。
## 发布、缓存与异常边界
贡献数据加载器执行以下保护:所有请求使用 `cache: no-store`;manifest 带 hash 时执行 SHA-256 校验;`current` 超过 30 天视为 stale 并尝试 `last_good`;年度块必须满足 schema、日期范围、严格递增、124 行业、7 指数和固定字段集合。贡献模块失败时只显示“离散度贡献暂不可用”,主高切低指标继续工作。
这些保护说明主指标和解释层是松耦合的。复现时应保持同样边界:贡献数据失败不能把主指标错误清空,使用 `last_good` 时必须显式标注,而不是伪装为最新数据。
## 回测和使用风险
公开数据只有 `as_of_date`,没有 release timestamp、输入实际到达时间或“次日可用”契约。指标明显依赖当日行业收盘及资金数据,因此历史验证应保守地把 `T` 日结果视为 `T+1` 可用。用 `T` 日指标指导 `T` 日盘中或收盘前交易存在潜在前视偏差。
申万二级行业当前 universe 是 124 个,但公开协议没有提供完整的历史成分版本。若用当前行业成员回填历史,会引入成员变更和幸存者偏差。主序列也可能随输入修订;不保存批次和 hash 就无法重建某次页面发布时的历史曲线。
阈值只是数值分区,不包含收益保证。`70—120` 的“潜在高切低动力”不能直接推导为卖出高位行业或买入低位行业;贡献四象限也只是结构解释,缺少交易成本、持有期、风控和退出规则。作者在视频 `07:27—07:58` 也明确提醒:低位板块出现小行情不等于它会成为新主线,只能说明资金存在高切低趋势。因此“主线识别”与“资金留存、行情延续”都应保留启发式和概率性限定,不能写成经过验证的预测能力。
## 复现置信度
| 能力 | 置信度 | 说明 |
| --- | --- | --- |
| 页面目标、输入行业层级和数据协议 | 高 | HTML、JS、state、manifest 可重复核验 |
| `smooth=MA3(D)`、十年百分位和 30/70/120 阈值 | 高 | 代码与公开序列交叉验证 |
| `ΔD=ΣC`、`K=ΣO`、`K_MA5=MA5(K)` | 高 | 180 日公开数据逐日验证 |
| 贡献窗口、正负各 10、方向/结构四象限 | 高 | 当前 V31 前端代码明确 |
| `D` 的核心统计公式 | 低 | 没有构建端源码或足够原始输入 |
| `O` 的符号定向规则 | 低 | 只能确认 `abs(O)=abs(C)` |
| 仓位、资产切换和交易状态机 | 无 | 页面没有公开这类输出 |
| 历史 point-in-time 可复现性 | 低 | 没有发布日期、完整历史成员和修订快照 |
## 视频与本轮限制
浏览器交互连接在本轮核验时被目标站点关闭,因此没有把截图作为证据;页面 DOM、公开脚本、state、manifest 和 JSON 响应均通过只读 HTTP 重复核对。图表拖拽和响应式视觉行为没有做完整端到端测试,但公式、阈值和数据契约不依赖截图判断。
作者视频现已完成 `04:13—08:00` 的音轨转写和画面抽帧核对。视频没有公开字幕,自动转写可能存在同音字误差,因此本报告只引用语义清楚且能与画面模块位置对齐的片段。视频补强了指标输入、阈值解释和三类使用场景:宏观风险识别、普涨/结构行情判断,以及高离散度回落时的高切低趋势观察;它没有披露 `D/C/O` 的精确公式、滞回或连续确认、仓位与买卖规则。
本报告只用于技术与产品逻辑研究,不构成投资建议,也不代表能够复制原作者未公开的专有算法。
## 主要证据
- [宏观择时首页](https://onechartlab.com/macro/):产品目标、非原版声明、方法摘要、阈值文案和贡献模块说明。
- [作者视频《我把自己的交易系统,做成了4个网页工具》](https://www.bilibili.com/video/BV16Hur6rEq2/):`04:13—07:58` 的指标来源、近 90 天行业涨跌幅语义、阈值解释和使用边界。
- [`assets/app.js`](https://onechartlab.com/macro/assets/app.js?v=a4a0a7155f44):默认模式、MA 序列消费、十年百分位、阈值、指数归一化和 state 加载。
- [`macro/state.json`](https://onechartlab.com/macro/state.json):当前/最近有效构建、日期、批次、hash、水位和摘要。
- [当前 `series.json`](https://onechartlab.com/macro/batches/batch-20260826-r21-c8768898f3/series.json):`precise`、`smooth`、日期和七个指数序列。
- [`dispersion-store.js`](https://onechartlab.com/macro/assets/dispersion-store.js):schema、hash、current/last_good、30 日过期、91 点窗口和数据块校验。
- [dispersion manifest](https://onechartlab.com/macro/dispersion/builds/dispersion-20260826-6186c256680b/manifest.json):124 行业、7 指数、2326 个日期、构建身份和数据块。
- [当前 `latest180.json`](https://onechartlab.com/macro/dispersion/builds/dispersion-20260826-6186c256680b/latest180.json):`D/MA3/K/K_MA5` 与行业 `C/O/normalized/return90`。
- [`dispersion-contribution.js`](https://onechartlab.com/macro/assets/dispersion-contribution.js?v=05aeb6c74aaf):贡献窗口、O/C 消费、四象限、图形与成分详情逻辑。
- [成分排行 manifest](https://onechartlab.com/sector_rankings/manifest.json):Tushare 输入字段、聚合单位、124 行业 universe 和窗口契约。