Files

139 lines
7.9 KiB
Markdown
Raw Permalink Normal View History

# research/ —— 研究管线骨架(你的代码)
> ⚠️ 这是**你的研究管线骨架**,只用于离线研究。它包装 `mtdata-cli` 作为子
> 进程拉数据/跑回测,**不 import 任何 mtdata 内部模块**,因此不会被上游同步覆盖。
> 研究结果(模型文件 + 报告)最终通过 `trading/` 的 `signal(ctx)` 接口进入执行壳。
## 管线一览
```
┌──────────┬──────────┬──────────────┬──────────────┬─────────────┬───────────┐
│ 取数据 │ 存储 │ 特征/标签 │ 前向验证 │ 训练 │ 评估 │
│ fetch │ store │ features │ walk-forward │ train │ evaluate │
├──────────┼──────────┼──────────────┼──────────────┼─────────────┼───────────┤
│ mtdata-cli│ parquet │ pandas_ta / │ mtdata-cli │ sklearn / │ sklearn │
│ data_ │ (CSV 兜底)│ 手写指标兜底 │ strategy_ │ lightgbm │ 指标 + │
│ fetch_ │ manifest │ 三触线标签 │ validate │ (可选) │ markdown │
│ candles │ │ │ 或本地 stub │ │ 报告 │
└──────────┴──────────┴──────────────┴──────────────┴─────────────┴───────────┘
```
## 快速上手(离线、无需下单)
```bash
# 一键端到端:取数 → 数据集 → 前向验证 → 训练 → OOS 评估 → 模型落盘
py -3.14 -m research --symbol EURUSD --timeframe H1 --limit 3000
# 可选:最后再对同一份数据回测一个 trading/ 注册表策略(与实盘同一套代码)
py -3.14 -m research --symbol EURUSD --timeframe H1 --limit 3000 \
--backtest --strategy ma_cross
# 也可只手动调用某个阶段(见下方"手动"示例)
py -3.14 -c "
from research.config import ResearchConfig
from research import data_pipeline as dp
cfg = ResearchConfig()
ds = dp.fetch_and_store(cfg, 'EURUSD', limit=3000)
print('stored:', ds.to_dict())
"
```
`py -3.14 -m research` 的六个阶段:
1. **fetch**:`fetch_and_store` 拉 K 线并落盘(parquet 优先,无 pyarrow 时 CSV)。
2. **dataset**:`make_dataset` 产出特征 + 二分类标签(`1`=止盈先到)。
3. **walk-forward**:`local_walk_forward` 按时序分 5 折,报告每折标签命中率。
4. **split**:按 `RESEARCH_TRAIN_RATIO`(默认 `0.8`)做时序切分,留出 OOS 尾部。
5. **train + evaluate**:训练指定模型,输出 OOS 指标 + 与"永远做多"基线的对比报告。
6. **save**:写到 `research/models/latest.joblib` —— 即
`trading/strategies/model_threshold.py` 的默认 `model_path`。
7. **backtest(可选,`--backtest`)**:从 `trading/` 策略注册表加载一个策略
(`--strategy`,默认 `ma_cross`),对同一份历史数据跑离线回测 —— 与实盘
同一个 `signal(ctx)` + 可选的 `manage_position`,输出统计并落 markdown 报告。
入口参数:`--symbol` `--timeframe` `--limit` `--kind`(model 类型)`--model-name`
(默认 `latest`)`--seed`,以及回测组的 `--backtest` `--strategy`(策略名或
模块路径)`--strategy-param key=value`(可重复)。训练跳出或样本不足时返回码
`2`,成功为 `0`。
研究管线不接触订单:任何会触达 `trade_*` 的调用都留在 `trading/`。
## 文件地图
| 文件 | 职责 |
|------|------|
| `main.py` / `__main__.py` | 端到端编排 CLI(`py -3.14 -m research`) |
| `config.py` | 环境变量 → `ResearchConfig`(frozen dataclass) |
| `data_pipeline.py` | 取 K 线 → 存储(parquet/CSV)→ manifest 记录 |
| `feature_engineering.py` | 指标特征 + 前向三触线标签(`make_dataset`) |
| `backtest.py` | `strategy_validate` 包装 + 本地前向验证 + 离线回测(含持仓管理模拟) |
| `model_trainer.py` | sklearn / lightgbm 训练、`ModelWrapper` 存取(含 baseline 命中率) |
| `evaluate.py` | 分类指标 + OOS 对比 markdown 报告 |
## 环境变量(默认值)
| 变量 | 默认 | 说明 |
|------|------|------|
| `RESEARCH_DATA_DIR` | `research/data` | 数据与 manifest 目录 |
| `RESEARCH_STORAGE_FORMAT` | `parquet` | `parquet` / `csv`(parquet 需 pyarrow) |
| `RESEARCH_SYMBOLS` | `EURUSD,GBPUSD` | 默认研究品种 |
| `RESEARCH_TIMEFRAME` | `H1` | K 线周期 |
| `RESEARCH_LOOKBACK_BARS` | `3000` | 默认取数根数 |
| `RESEARCH_INDICATORS` | `rsi,atr,ema,bbands` | 取数时附带指标 |
| `RESEARCH_LABEL_TP_PCT` | `0.4` | 标签止盈距离(%) |
| `RESEARCH_LABEL_SL_PCT` | `0.6` | 标签止损距离(%) |
| `RESEARCH_LABEL_HORIZON` | `12` | 标签前视 K 线数 |
| `RESEARCH_WF_N_SPLITS` | `5` | `strategy_validate` 折数 |
| `RESEARCH_WF_MIN_TRAIN_BARS` | `1000` | 前向验证最小训练根数 |
| `RESEARCH_TRAIN_RATIO` | `0.8` | 时序切分:前 80% 训练,尾部留 OOS |
| `RESEARCH_SPREAD_BPS` / `RESEARCH_COMMISSION_BPS` / `RESEARCH_SLIPPAGE_BPS` | `1.0` / `0.0` / `1.0` | 回测成本(bps) |
| `RESEARCH_MODEL_DIR` | `research/models` | 模型输出目录 |
| `RESEARCH_MODEL` | `logistic` | `logistic` / `rf` / `gboost` / `lightgbm` |
| `RESEARCH_REPORT_DIR` | `research/reports` | 报告输出目录 |
| `MTDATA_CLI` | `mtdata-cli` | CLI 路径 |
## 模型如何接入 trading/ 执行壳
研究侧只产出两类东西,供 `trading/` 使用:
1. **特征/标签约定**:`feature_engineering.make_dataset(frame, cfg)` 的输出
直接作为训练输入;在线推理时对最新一根 K 线同样调用 `build_features`。
目标已折叠为二分类:`1` = 止盈先到(+1),`0` = 止损先到或超时;
模型输出的单一概率即 `P(+1)`,可直接喂给 `signal()` 的阈值。
2. **模型文件**:`model_trainer.ModelWrapper.save(path)` 落盘,`trading/` 侧
的策略模块用 `ModelWrapper.load` 读取,然后在 `signal(ctx)` 里:
```python
def signal(ctx):
X = research.feature_engineering.build_features(ctx.candles).tail(1)
p = wrapper.predict_proba_positive(X)[0]
if p >= BUY_THRESHOLD:
return TradeSignal(side="BUY", stop_loss=..., take_profit=..., reason=f"p={p:.3f}")
return None
```
训练模型**永远不直接下单**——它只是 `signal()` 的一个输入;是否真的下单仍由
`trading/` 的 dry-run 分层和护栏把关。任何模型上线实盘前,请先完成
[docs/LIVE_TRADING_CHECKLIST.md](../docs/LIVE_TRADING_CHECKLIST.md)。
## 前向验证(walk-forward)
- 优先:`research.backtest.run_strategy_validate(cfg, symbol, candidates)` 直接
调用 `mtdata-cli strategy_validate`(含 purge/embargo 防泄漏、固定候选、
horizon-safe 结果)。
- 兜底:`local_walk_forward(frame, cfg)` 在本地做时序切分的标签分布摘要,
用于无 broker 环境下的骨架验证。
## 离线回测(可选,任意 `signal(ctx)` 策略)
`research.backtest.backtest_strategy(strategy, frame, cfg)` 用与实盘**同一个**
策略对象逐 bar 回测:`signal(ctx)` 开仓后,每根 K 线先结算服务器端 SL/TP
(价格先行动),未命中再调用策略的 `manage_position(ctx, position)` 做移损 /
保本 / 分批平仓 / 提前离场 —— 与实盘循环的持仓管理 pass 调用的是同一个方法,
动作语义也完全一致(`apply_position_action` 模拟 `Executor.modify/close`)。
成本按 `RESEARCH_SPREAD_BPS` / `RESEARCH_COMMISSION_BPS` /
`RESEARCH_SLIPPAGE_BPS` 结算。`backtest_registered(spec, frame, cfg)` 接受
`trading/` 注册表名字(`ma_cross`、`model_threshold` 或任意模块路径),
`py -3.14 -m research --backtest` 即其一键入口。