Files
simonandClaude Opus 4.7 271a9343a5 Initial commit: cc-cursor 全链路量化研究平台
7 Sprints 全部完成:
  Sprint 0: 基础设施 (DataManager + MariaDB)
  Sprint 1: 因子引擎 (34因子/12分类)
  Sprint 2: VectorBT 回测 (5策略+截面)
  Sprint 3: Optuna 优化 (+Walk-Forward)
  Sprint 4: ML 模型 (LightGBM+CatBoost)
  Sprint 5: Qwen 情绪因子 (三源新闻+日期对齐)
  Sprint 6: Agent 系统 (4Agent+日报.md/.html)

生产加固 (15项): Tushare双源fallback, SSH自动恢复, pool_pre_ping,
  save_daily先删后插, load_dotenv绝对路径, 日报5d/20d修复,
  RiskAgent改上证指数, 昨日对比+数据截止, mac_report utf8mb4,
  CLAUDE-*.md 9条已知Bug, demo全参数化, djapi数据源归一化,
  indexDatas API修正

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:59:05 +08:00

1161 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# cc-cursor 使用指南
Mac Mini 单机量化研究平台,覆盖数据获取 → 因子计算 → 回测 → 参数优化 → ML 模型 → 情绪因子 → Agent 系统 全链路。
---
## 目录
1. [环境准备](#1-环境准备)
2. [数据库连接](#2-数据库连接)
3. [数据层 — DataManager](#3-数据层--datamanager)
4. [因子引擎 — FactorEngine](#4-因子引擎--factorengine)
5. [回测引擎 — VectorBTEngine](#5-回测引擎--vectorbtengine)
6. [参数优化 — OptunaEngine](#6-参数优化--optunaengine)
7. [ML 模型 — LightGBM / CatBoost](#7-ml-模型--lightgbm--catboost)
8. [情绪因子 — SentimentEngine](#8-情绪因子--sentimentengine)
9. [Agent 系统 — 命令行入口](#9-agent-系统--命令行入口)
10. [配置说明](#10-配置说明)
11. [完整示例](#11-完整示例)
12. [常见问题](#12-常见问题)
---
## 1. 环境准备
### 硬件要求
- macOS / Linux(本系统开发于 Mac Mini
- 内存 ≥ 16GB(ML 模型训练推荐)
- 网络:可访问东方财富 / 同花顺 API
### Python 环境
```bash
# 激活 conda 环境
conda activate quant
# 确认 Python 版本
python --version # → 3.11.13
```
### 核心依赖
| 包 | 版本 | 用途 |
|---|------|------|
| pandas | 3.0 | 数据处理 |
| numpy | 2.4 | 数值计算 |
| akshare | 1.18 | A 股数据获取 |
| vectorbt | 1.0 | 回测引擎 |
| optuna | 4.9 | 参数优化 |
| lightgbm | 4.6 | 梯度提升模型 |
| catboost | 1.2 | 梯度提升模型 |
| scikit-learn | 1.9 | 特征工程 |
| sqlalchemy | 2.0 | 数据库 ORM |
| pymysql | 1.2 | MySQL 连接 |
### 项目路径设置
所有代码从 `finance/` 目录运行。Python 脚本开头加入:
```python
import sys
sys.path.insert(0, "/path/to/cc-cursor/finance")
```
---
## 2. 数据库连接
### 建立 SSH 隧道
系统通过 SSH 隧道连接远程 MariaDB
```bash
bash shared/script/autossh.sh
```
验证隧道:
```bash
lsof -i :13306 | grep LISTEN
# → ssh ... localhost:13306 (LISTEN) ...
```
### 连接信息
```
Host: 127.0.0.1
Port: 13306
User: myquant
Password: <your-db-password>
Database: myquant
```
### 数据库表
所有表使用 `mac_` 前缀,与已有表隔离:
| 表名 | 内容 | 说明 |
|------|------|------|
| `mac_stock_basic` | A 股列表 | 5,524 只股票 |
| `mac_stock_daily` | 日线行情 | 按需同步 |
| `mac_stock_financial` | 财务指标 | 同花顺核心指标 |
### 测试连接
```python
from database.connection import test_connection
if test_connection():
print("数据库连接成功")
else:
print("请先建立 SSH 隧道: bash shared/script/autossh.sh")
```
---
## 3. 数据层 — DataManager
### 初始化
```python
from data.data_manager import DataManager
dm = DataManager()
dm.init_db() # 首次使用创建表(幂等操作)
```
### 获取股票列表
```python
# 从 DB 缓存读取(已缓存的 5,524 只 A 股)
stocks = dm.get_stock_list()
# → DataFrame: index=ts_code, columns=[name, area, industry, ...]
# 强制从 AkShare 刷新
stocks = dm.get_stock_list(force_refresh=True)
```
### 获取日线数据
```python
# 获取单只股票日线(DB 缓存优先,缺失自动补拉)
daily = dm.get_daily("000001.SZ")
# → DataFrame: trade_date, open, high, low, close, vol, amount, ...
# 指定日期范围
daily = dm.get_daily("000001.SZ", start="20240101", end="20241231")
# 直接用索引
price = daily.set_index("trade_date").sort_index()
close = price["close"]
```
### 获取财务数据
```python
fina = dm.get_financial("000001.SZ")
# → DataFrame: end_date, eps, bvps, roe, net_profit_margin, debt_to_assets, ...
# 数据源: stock_financial_abstract_ths(同花顺)
# 覆盖: 主板/创业板/科创板
```
### 数据同步
```python
# 增量同步:从 DB 最新日期到今天的缺失数据
n = dm.sync_daily("000001.SZ")
# 批量同步全部股票(谨慎使用,耗时长)
total = dm.sync_all_daily()
```
---
## 4. 因子引擎 — FactorEngine
### 因子注册表
```python
from factors.registry import get_factor, list_factors, list_categories
# 查看所有因子分类
print(list_categories())
# → ['动量', 'RSI', 'MACD', '量价', '布林', 'ATR', '均线', '波动率', '换手率', '振幅', '基本面', '情绪']
# 查看某个分类下的因子
print(list_factors("RSI"))
# → ['rsi_7', 'rsi_14']
# 查看全部因子
all_factors = list_factors()
print(len(all_factors))
# → 34
```
### 创建因子实例
```python
# 按名称获取(使用默认参数)
factor = get_factor("momentum_20") # 20 日动量
factor = get_factor("rsi_14") # 14 日 RSI
factor = get_factor("roe") # ROE 基本面因子
factor = get_factor("news_sent_5") # 5 日新闻情绪因子
# 自定义参数
from factors.technical.momentum import MomentumFactor
factor = MomentumFactor(period=60)
```
### 计算因子
```python
from factors.engine import FactorEngine
engine_fe = FactorEngine(dm)
# 单股票多因子
factors = [
get_factor("momentum_20"),
get_factor("rsi_14"),
get_factor("volatility_20"),
get_factor("ma_dev_20"),
]
factor_df = engine_fe.compute("000001.SZ", factors)
# → DataFrame: index=trade_date, columns=[momentum_20, rsi_14, volatility_20, ma_dev_20]
# 查看因子值
print(factor_df.tail())
print(factor_df.describe())
```
### 截面因子
```python
# 计算多只股票在某一天的因子值
cross = engine_fe.compute_universe(
factors=[get_factor("momentum_20"), get_factor("rsi_14")],
date="20250630",
ts_codes=["000001.SZ", "600519.SH", "300750.SZ"],
)
# → DataFrame: index=ts_code, columns=[momentum_20, rsi_14]
```
### 因子质量检查
```python
# 查看 NaN 率
total = len(factor_df)
for col in factor_df.columns:
nan_pct = factor_df[col].isna().sum() / total * 100
print(f"{col}: NaN {nan_pct:.1f}%")
# 正常范围: 技术因子 0.3%-2.1%, 基本面因子 0%
```
---
## 5. 回测引擎 — VectorBTEngine
### 参数
- 初始资金:100,000 元
- 手续费:0.03%(万三)
- 方向:只做多
### 创建引擎
```python
from backtest.vectorbt.engine import VectorBTEngine
engine_bt = VectorBTEngine(
initial_capital=100_000,
commission=0.0003,
)
```
### 使用内置策略
```python
from backtest.strategies.rsi_mean_revert import RSIMeanRevertStrategy
# 创建策略
strategy = RSIMeanRevertStrategy(oversold=30, overbought=70)
# 运行回测
report = engine_bt.run(strategy, price_df, factor_df)
```
### 读取回测报告
```python
# 一行摘要
print(report.summary())
# → 收益=29.4% 年化=4.3% 回撤=-19.1% 夏普=0.37 胜率=77.1% 交易=70笔
# 字典格式
metrics = report.to_dict()
# → {'total_return': 29.4, 'cagr': 4.3, 'sharpe_ratio': 0.37, ...}
# 获取净值曲线
equity = report.equity_curve # pd.Series
drawdown = report.drawdown_curve # pd.Series
# 逐笔交易
trades = report.trades_df # pd.DataFrame
```
### 内置策略清单
| 策略 | 类名 | 适用场景 |
|------|------|----------|
| 均线交叉 | `SMACrossStrategy(fast=5, slow=20)` | 趋势跟踪 |
| RSI 反转 | `RSIMeanRevertStrategy(oversold=30, overbought=70)` | 均值回归 |
| 动量突破 | `MomentumBreakoutStrategy(lookback=20, exit_period=10)` | 动量策略 |
| 因子阈值 | `FactorCrossStrategy(factor_column, buy_threshold, sell_threshold)` | 通用因子 |
| 因子轮动 | `FactorRotationStrategy(factor_name, top_n=5)` | 截面选股 |
### 自定义策略
```python
from backtest.base import BaseStrategy
class MyStrategy(BaseStrategy):
name = "my_strategy"
category = "custom"
def __init__(self, param_a=10):
self.param_a = param_a
def generate_signals(self, factor_df):
# factor_df 包含因子值和 close 列
# 返回: 1=买入, 0=卖出, -1=持有
signals = pd.Series(-1, index=factor_df.index)
signals[factor_df["rsi_14"] < 30] = 1 # RSI 超卖买入
signals[factor_df["rsi_14"] > 70] = 0 # RSI 超买卖出
return signals
report = engine_bt.run(MyStrategy(param_a=20), price_df, factor_df)
```
### 截面回测(多股票)
```python
report_xs = engine_bt.run_cross_section(
strategy,
price_universe={"000001.SZ": df1, "600519.SH": df2},
factor_universe={"000001.SZ": f1, "600519.SH": f2},
)
# → 等权组合回测报告
```
---
## 6. 参数优化 — OptunaEngine
### 使用预置搜索空间
```python
from optimizer.engine import OptunaEngine
from optimizer.space import rsi_revert_space, sma_cross_space
from backtest.strategies.rsi_mean_revert import RSIMeanRevertStrategy
opt_engine = OptunaEngine(engine_bt)
# 优化 RSI 反转策略参数
result = opt_engine.optimize(
strategy_class=RSIMeanRevertStrategy,
search_space=rsi_revert_space,
price_df=price_df,
factor_df=factor_df,
metric="sharpe", # 优化目标: sharpe/cagr/calmar/total_return
n_trials=200, # 试验次数
)
```
### 读取优化结果
```python
print(result.summary())
# → 最优参数: oversold=13, overbought=66
# → 最优目标 (sharpe): 0.5985
# 最优参数的回测报告
best_report = result.best_report
# 参数重要性
for k, v in sorted(result.param_importance.items(), key=lambda x: -x[1]):
print(f" {k}: {v:.4f}")
# 试验记录
trials = result.trials_df # pd.DataFrame
```
### Walk-Forward 验证
```python
wf_result = opt_engine.optimize_walk_forward(
strategy_class=RSIMeanRevertStrategy,
search_space=rsi_revert_space,
price_df=price_df,
factor_df=factor_df,
metric="sharpe",
n_trials=80,
train_window=252 * 3, # 3 年训练
test_window=252, # 1 年测试
)
print(wf_result.summary())
# → 各窗口参数变化 + 整体收益
```
### 快捷函数
```python
from optimizer.presets import (
optimize_sma_cross,
optimize_rsi_revert,
optimize_momentum_breakout,
)
result = optimize_rsi_revert(price_df, factor_df, engine_bt, n_trials=100)
```
### 自定义搜索空间
```python
from optimizer.space import SearchSpace
my_space = SearchSpace(params=[
{"name": "fast", "type": "int", "low": 2, "high": 30, "step": 1},
{"name": "slow", "type": "int", "low": 15, "high": 120, "step": 5},
])
result = opt_engine.optimize(MyStrategy, my_space, price_df, factor_df)
```
---
## 7. ML 模型 — LightGBM / CatBoost
### 特征工程
```python
from models.features import FeatureEngine
# lookahead=5: 预测未来 5 个交易日收益
fe = FeatureEngine(lookahead=5, label_type="regression")
# 构建特征矩阵和标签
X, y = fe.build(factor_df, price_df, fit=True)
# → X: 标准特征矩阵(去极值 → 缺失填充 → RobustScaler
# → y: 未来 5 日收益率(%
print(f"特征: {X.shape[1]} 列, 样本: {X.shape[0]} 行")
print(f"标签: mean={y.mean():.2f}%, std={y.std():.2f}%")
```
### 数据划分
```python
# 时间序列划分(前 70% 训练,后 30% 测试)
n = len(X)
split = int(n * 0.7)
X_train, X_test = X.iloc[:split], X.iloc[split:]
y_train, y_test = y.iloc[:split], y.iloc[split:]
print(f"训练集: {len(X_train)} 行")
print(f"测试集: {len(X_test)} 行")
```
### LightGBM 训练
```python
from models.lightgbm.model import LightGBMModel
model = LightGBMModel(
params={
"n_estimators": 200,
"learning_rate": 0.03,
"num_leaves": 15,
},
early_stopping=100,
eval_ratio=0.2, # 20% 做验证集
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
# 评估
ic = pred.corr(y_test)
print(f"测试集 IC: {ic:.4f}")
```
### CatBoost 训练
```python
from models.catboost.model import CatBoostModel
model = CatBoostModel(
params={"iterations": 200, "learning_rate": 0.03, "depth": 5},
eval_ratio=0.2,
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
```
### 特征重要性
```python
# LightGBM
imp = model.get_feature_importance(importance_type="gain")
print(imp.head(10))
# → feature, importance, importance_pct
# CatBoost
imp = model.get_feature_importance()
print(imp.head(5))
```
### 交叉验证
```python
# 5 折时间序列 CV(不 shuffle
cv_df = model.cv_evaluate(X_train, y_train, n_folds=5)
print(cv_df)
# → 各折 IC + MSE, 均值
```
### 模型持久化
```python
# 保存
model.save("models/lightgbm_000001.pkl")
# 加载
model = LightGBMModel.load("models/lightgbm_000001.pkl")
```
### ML 策略回测
```python
from models.backtest_integration import MLStrategy, MLBenchmark
# 预测值分位 → 交易信号
strategy = MLStrategy(
model=model,
feature_engine=fe,
buy_quantile=0.7, # 预测值最高的 30% 买入
sell_quantile=0.3, # 预测值最低的 30% 卖出
rebalance_freq=5, # 每 5 日调仓
)
report = engine_bt.run(strategy, price_df, factor_df)
# 多模型对比
benchmark = MLBenchmark(
models=[lgb_model, cb_model],
feature_engine=fe,
price_df=test_price,
factor_df=test_factor,
)
df = benchmark.run()
print(df)
# → model × (IC, total_return, sharpe, win_rate, trades)
```
---
## 8. 情绪因子 — SentimentEngine
### 配置 API Key
编辑 `finance/.env`
```bash
# DashScope API(推荐)
QWEN_API_KEY=sk-your-key-here
QWEN_MODEL=qwen-turbo
# 或本地 Ollama
# QWEN_LOCAL_BASE_URL=http://localhost:11434/v1
# QWEN_LOCAL_MODEL=qwen2.5:7b
```
### 配置分析范围
```bash
# 按指数成分股分析(沪深300 + 中证500)
SENTIMENT_SCOPE_TYPE=index
SENTIMENT_SCOPE_INDEXES=000300,000905
# 按板块分析
# SENTIMENT_SCOPE_TYPE=sector
# SENTIMENT_SCOPE_SECTORS=银行,电力设备,医药生物
# 按自定义列表
# SENTIMENT_SCOPE_TYPE=custom
# SENTIMENT_SCOPE_CUSTOM=000001.SZ,600519.SH,300750.SZ
```
### 使用情绪引擎
```python
from factors.sentiment.sentiment_engine import SentimentEngine
from factors.sentiment.news_source import NewsSource
from factors.sentiment.qwen_client import QwenClient
sent = SentimentEngine(dm, qwen_client=QwenClient(), news_source=NewsSource())
# 单股票情绪因子
sent_df = sent.compute("000001.SZ", max_news=20)
# → DataFrame: (trade_date, news_sent_5, news_conf_5, sent_delta_5)
# 批量计算
results = sent.compute_batch(
ts_codes=["000001.SZ", "600519.SH", "300750.SZ"],
max_news=10,
)
```
### 新闻数据源
系统聚合三个数据源:
| 数据源 | 说明 | 配置 |
|--------|------|------|
| AkShare `stock_news_em` | 东方财富个股新闻 | `use_akshare=True` |
| MariaDB `xwlb_daily_ext` | 新闻联播分割数据 | `use_xwlb=True` |
| MCP `trendradar-news` | 外部新闻聚合服务 | `use_mcp=True` |
```python
news = NewsSource(
use_akshare=True, # 启用东方财富
use_xwlb=True, # 启用新闻联播
use_mcp=False, # 关闭 MCP
)
news_df = news.fetch("000001.SZ", start="20260501", end="20260603")
# → DataFrame: date, title, content, source, url
```
### 日期对齐机制
- **AkShare 新闻**`发布时间` 直接保留 → `align_news_to_trading_days` 对齐到最近交易日
- **新闻联播**`news_date + 1 day`(晚间播出 → 次日市场影响)→ 对齐到交易日
```
周五新闻联播 → +1 = 周六 → align → 下周一交易日
```
---
## 9. Agent 系统 — 命令行入口
### 注册 Agent
```python
from agents.orchestrator import AgentOrchestrator
engines = {
"dm": dm,
"fe": engine_fe,
"bt": engine_bt,
"opt": opt_engine,
"sent": sent,
}
orch = AgentOrchestrator(**engines)
orch.setup()
# → [Orchestrator] 已注册 4 个 Agent: ['research', 'selection', 'risk', 'report']
```
### CLI 命令
```bash
# 完整每日流程(同步行情 → 风险评估 → 选股打分 → 生成日报)
python finance/cli/agent_cli.py daily
# 今日选股 Top 15
python finance/cli/agent_cli.py picks 15
# 风险评估
python finance/cli/agent_cli.py risk
# 因子研究(IC 评估)
python finance/cli/agent_cli.py research
# 生成指定日期日报
python finance/cli/agent_cli.py report 20260603
```
### 每日流程输出
```
============================================================
[Orchestrator] 每日流程 — 20260603
============================================================
[Step 1/4] 同步行情... 0 条(已是最新)
[Step 2/4] 风险评估... high, 仓位 30%
[Step 3/4] 股票打分... 1 只
[Step 4/4] 生成日报... reports/daily_20260603.md
```
### 日报输出
日报保存到 `finance/reports/daily_YYYYMMDD.md`,内容包含:
- **市场概览**:上证/深证/创业板 收盘价、涨跌幅、5日/20日趋势
- **今日推荐**:TOP 15 股票打分排名
- **风险评估**:风险等级、建议仓位、止损线、预警
### 编程调用
```python
# 各 Agent 独立调用
selection_result = orch.picks(date="20260603", top_n=15)
risk_result = orch.risk_check()
research_result = orch.run_research_cycle()
report_result = orch.generate_report(date="20260603")
```
---
## 10. 配置说明
### 环境变量(`finance/.env`
```bash
# ── Qwen API ──────────────────────
QWEN_API_KEY=sk-xxx # DashScope API Key
QWEN_MODEL=qwen-turbo # 模型选择: qwen-turbo/plus/max
# ── 本地 Ollama(可选) ──────────
# QWEN_LOCAL_BASE_URL=http://localhost:11434/v1
# QWEN_LOCAL_MODEL=qwen2.5:7b
# ── 数据库 ───────────────────────
MAC_DB_HOST=127.0.0.1
MAC_DB_PORT=13306
MAC_DB_USER=myquant
MAC_DB_PASSWORD=<your-db-password>
MAC_DB_NAME=myquant
# ── 情绪分析范围 ─────────────────
SENTIMENT_SCOPE_TYPE=index
SENTIMENT_SCOPE_INDEXES=000300,000905
SENTIMENT_MAX_NEWS_PER_STOCK=20
# ── MCP 新闻服务(可选) ─────────
NEWS_MCP_URL=http://192.168.1.160:3333/mcp
```
### 回测参数
```python
VectorBTEngine(
initial_capital=100_000, # 初始资金(元)
commission=0.0003, # 手续费(万三)
)
```
### Optuna 参数
```python
opt_engine.optimize(
n_trials=200, # 试验次数
metric="sharpe", # 优化目标
# 可选: cagr, calmar, total_return, return_over_dd, win_rate, profit_factor
)
```
### ML 模型参数
```python
# LightGBM 推荐参数
LightGBMModel(params={
"n_estimators": 200,
"learning_rate": 0.03,
"num_leaves": 15,
"min_data_in_leaf": 20,
"feature_fraction": 0.7,
"bagging_fraction": 0.7,
})
# CatBoost 推荐参数
CatBoostModel(params={
"iterations": 200,
"learning_rate": 0.03,
"depth": 5,
"min_data_in_leaf": 20,
})
```
---
## 11. 完整示例
### 示例 1:快速回测
```python
import sys; sys.path.insert(0, "finance")
from data.data_manager import DataManager
from factors.registry import get_factor
from factors.engine import FactorEngine
from backtest.vectorbt.engine import VectorBTEngine
from backtest.strategies.rsi_mean_revert import RSIMeanRevertStrategy
# 数据
dm = DataManager(); dm.init_db()
price = dm.get_daily("000001.SZ").set_index("trade_date")
# 因子
engine_fe = FactorEngine(dm)
factor_df = engine_fe.compute("000001.SZ", [get_factor("rsi_14")])
# 回测
engine_bt = VectorBTEngine()
report = engine_bt.run(
RSIMeanRevertStrategy(oversold=30, overbought=70),
price, factor_df,
)
print(report.summary())
```
### 示例 2:策略寻优 + Walk-Forward
```python
from optimizer.engine import OptunaEngine
from optimizer.space import rsi_revert_space
opt_engine = OptunaEngine(engine_bt)
# 寻优
result = opt_engine.optimize(
RSIMeanRevertStrategy, rsi_revert_space,
price, factor_df, metric="sharpe", n_trials=200,
)
print(result.summary())
# Walk-Forward 验证
wf = opt_engine.optimize_walk_forward(
RSIMeanRevertStrategy, rsi_revert_space,
price, factor_df, n_trials=80,
train_window=756, test_window=252,
)
print(wf.summary())
```
### 示例 3ML 训练 + 回测
```python
from models.features import FeatureEngine
from models.lightgbm.model import LightGBMModel
from models.backtest_integration import MLStrategy
# 特征工程
fe = FeatureEngine(lookahead=5)
X, y = fe.build(factor_df, price, fit=True)
split = int(len(X) * 0.7)
# 训练
model = LightGBMModel(params={"n_estimators": 200, "learning_rate": 0.03})
model.fit(X.iloc[:split], y.iloc[:split])
# 回测
strategy = MLStrategy(model, fe)
report = engine_bt.run(strategy, price, factor_df)
print(report.summary())
print(model.get_feature_importance().head(5))
```
### 示例 4:每日 Agent 运行
```python
from agents.orchestrator import AgentOrchestrator
orch = AgentOrchestrator(
dm=dm, fe=engine_fe, bt=engine_bt, opt=opt_engine, sent=sent,
)
orch.setup()
results = orch.run_daily()
# 获取结果
sel = results["selection"]
risk = results["risk"]
report_path = results["report"]["report_path"]
print(f"日报: {report_path}")
```
---
## 12. 常见问题
### Q: SSH 隧道连接失败?
```bash
# 检查端口
lsof -i :13306 | grep LISTEN
# 重新建立
bash shared/script/autossh.sh
```
### Q: AkShare 返回 RemoteDisconnected
这是 AkShare 的 curl_cffi 在连续请求时偶发的连接问题。系统已内置 3 次递增间隔重试 + fallback 机制,通常第 2-3 次重试会成功。如果持续失败:
- 等待 30 秒后重试
- 减少并发请求频率
- 检查网络是否能访问 eastmoney.com
### Q: 因子计算结果全是 NaN?
- 技术因子:前 N 个周期内 NaN 是正常的(如 20 日动量前 19 天为 NaN)
- 基本面因子:检查财务数据是否已同步(`dm.get_financial(ts_code)`
- 情绪因子:检查是否配置了 `QWEN_API_KEY`
### Q: 回测结果为 0 笔交易?
- 检查策略参数是否过于严格(如 RSI oversold=10 过少触发)
- 使用 `OptunaEngine.optimize()` 寻找更优参数
- 检查因子值是否合理(`factor_df.describe()`
### Q: 模型训练只有 2 棵树?
当验证集损失不下降时,早停会在很少的迭代后触发。这是单股票预测的正常现象(信号噪声比低)。建议:
- 设置 `eval_ratio=0.0` 禁用早停
- 降低 `learning_rate` 到 0.01
- 增加 `min_data_in_leaf` 防止过拟合
### Q: 情绪因子返回空?
- 确认 `.env``QWEN_API_KEY` 已配置
- 检查网络是否能访问 `dashscope.aliyuncs.com`
- 如果使用本地 Ollama,确认服务运行中:`curl http://localhost:11434/api/tags`
### Q: 日报中选股为空?
日报只对 DB 中有日线缓存的股票打分。需要先同步目标股票池的数据:
```python
# 同步单只
dm.sync_daily("000001.SZ")
# 按范围批量同步(需先配置 SENTIMENT_SCOPE
codes = sent.get_scope_stocks()
for code in codes[:10]:
dm.sync_daily(code)
```
---
## 13. CLI 脚本参考
所有脚本位于 `finance/cli/`,需在项目根目录或 `finance/` 下运行。
---
### 13.1 Agent 系统入口 — `agent_cli.py`
```bash
cd finance && python cli/agent_cli.py <命令> [参数]
```
| 命令 | 说明 | 示例 |
|------|------|------|
| `daily [DATE]` | 完整每日流程(增量同步已缓存→评估风险→选股→日报) | `agent_cli.py daily` |
| `picks [N] [DATE]` | 多因子选股 Top N(需已缓存) | `agent_cli.py picks 15` |
| `risk` | 市场风险评估(等级、仓位、止损) | `agent_cli.py risk` |
| `research` | 因子发现:遍历因子计算 IC/IC_IR 排名 | `agent_cli.py research` |
| `report [DATE]` | 生成日报(含三指数行情+选股+情绪+风险评估) | `agent_cli.py report` |
| `warmup [N]` | 首次批量预热范围股票到 DB 缓存(每批 N 只,默认 50) | `agent_cli.py warmup 50` |
**`daily` 流程**
```
[Step 1/4] 增量同步 → 只更新已缓存股票(最新则 0.04s 跳过)
→ 未缓存提示:运行 'agent_cli.py warmup' 首次预热
[Step 2/4] 风险评估 → high/medium/low + 仓位建议 + 预警
[Step 3/4] 股票打分 → DB 缓存命中率 + 多因子等权打分 → Top 15
[Step 4/4] 日报生成 → 三指数行情 (Tushare) + 情绪摘要 + 风险预警
→ reports/daily_YYYYMMDD.md
```
**数据源优先级**Tushare → AkShare`.env` 配置 `TUSHARE_TOKEN`
---
### 13.2 数据层验证 — `demo_data_manager.py`
```bash
python cli/demo_data_manager.py [--ts_code CODE] [--start YYYYMMDD]
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--ts_code` | `000001.SZ` | 测试股票代码 |
| `--start` | `20250101` | 起始日期 YYYYMMDD |
5 步验证:数据库连接 → 建表 → 股票列表 → 日线获取(双源fallback) → 增量同步。
---
### 13.3 因子引擎验证 — `demo_factor_engine.py`
```bash
python cli/demo_factor_engine.py [--ts_code CODE] [--ts_code2 CODE]
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--ts_code` | `000001.SZ` | 测试股票代码 |
| `--ts_code2` | `600519.SH` | 截面测试第二只股票 |
验证:因子注册表(12分类/34因子)→ 技术因子计算(describe统计) → 基本面因子(ROE/PE/PB/EP) → NaN 覆盖率检查 → 双股票截面因子。
---
### 13.4 回测引擎验证 — `demo_backtest.py`
```bash
python cli/demo_backtest.py [--ts_code CODE]
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--ts_code` | `000001.SZ` | 回测股票代码 |
测试 5 个内置策略:
| 策略 | 参数 |
|------|------|
| SMACrossStrategy | (5,20) / (10,60) |
| RSIMeanRevertStrategy | (30,70) / (20,80) |
| MomentumBreakoutStrategy | lookback=20 |
| FactorCrossStrategy | momentum_20 > 0 |
| FactorRotationStrategy | momentum top 20% |
---
### 13.5 参数优化验证 — `demo_optimizer.py`
```bash
python cli/demo_optimizer.py [--ts_code CODE] [--trials N]
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--ts_code` | `000001.SZ` | 回测股票代码 |
| `--trials` | `200` | Optuna 试验次数 |
对 RSI 反转策略执行参数寻优 + Walk-Forward 验证。输出最优 vs 默认对比表 + 参数重要性排序。
---
### 13.6 ML 模型验证 — `demo_ml.py`
```bash
python cli/demo_ml.py [--ts_code CODE] [--lookahead N]
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--ts_code` | `000001.SZ` | 训练股票代码 |
| `--lookahead` | `5` | 预测未来 N 日收益 |
完整 ML pipeline:特征工程(25因子→Winsorize→RobustScaler) → LightGBM训练(IC/CV) → CatBoost训练 → MLBenchmark对比(IC/收益/夏普/胜率)。
---
### 13.7 情绪因子快速验证 — `demo_sentiment.py`
```bash
python cli/demo_sentiment.py [--ts_code CODE] [--no-qwen]
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--ts_code` | `000001.SZ` | 测试股票代码 |
| `--no-qwen` | flag | 跳过 Qwen API 调用 |
6 步验证:新闻数据源(三源聚合) → 日期对齐 → Qwen 客户端状态 → SentimentEngine全链路 → 分析范围解析。
适合快速检查情绪因子系统是否就绪。
---
### 13.8 情绪因子详细演示 — `demo_sentiment_detail.py`
```bash
python cli/demo_sentiment_detail.py [选项]
```
最详细的情绪因子脚本,支持完整命令行参数和逐步输出。
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `--ts_code` | str | `000001.SZ` | 股票代码,多个用逗号分隔 |
| `--date` | str | 今天 | 目标日期 YYYYMMDD |
| `--start` | str | date-30天 | 起始日期 YYYYMMDD |
| `--end` | str | date | 结束日期 YYYYMMDD |
| `--scope-type` | str | - | 分析范围:`index`/`sector`/`custom`/`all` |
| `--scope-indexes` | str | `000300` | 指数代码(逗号分隔) |
| `--scope-sectors` | str | - | 板块名称(逗号分隔) |
| `--max-news` | int | .env 配置 | 最大新闻条数 |
| `--max-analyze` | int | `50` | Qwen API 分析最大条数(控制成本) |
| `--no-xwlb` | flag | - | 禁用新闻联播数据源 |
| `--no-akshare` | flag | - | 禁用东方财富数据源 |
| `--no-mcp` | flag | - | 禁用 MCP 数据源 |
| `--source` | str | - | 仅用指定数据源:`xwlb`/`akshare`/`mcp` |
| `--no-qwen` | flag | - | 跳过 Qwen API 调用(仅演示数据流) |
使用示例:
```bash
# 默认演示(000001.SZ,最近30天,全数据源)
python cli/demo_sentiment_detail.py
# 指定股票和日期
python cli/demo_sentiment_detail.py --ts_code 600519.SH --date 20260603
# 多股票 + 日期范围
python cli/demo_sentiment_detail.py --ts_code 000001.SZ,300316.SZ --start 20260501 --end 20260603
# 按指数成分股分析
python cli/demo_sentiment_detail.py --scope-type index --scope-indexes 000300
# 按板块分析
python cli/demo_sentiment_detail.py --scope-type sector --scope-sectors 银行,电力设备
# 只看东方财富新闻,不调用 Qwen
python cli/demo_sentiment_detail.py --source akshare --no-qwen --max-news 20
```
输出 6 步详情:
```
Step 0: 初始化引擎(显示数据源、API状态、范围、Tushare可用性)
Step 1: 按数据源分别拉取新闻(xwlb/AkShare/MCP 各自数量 + 双源fallback
Step 2: 新闻详情(按来源分开展示标题/内容/链接)
Step 3: 日期对齐(xwlb +1day偏移 + 非交易日对齐 + DB缓存检查→sync补齐)
Step 4: Qwen 情绪分析(每条新闻的分数/置信度/主题/来源标签)
Step 5: 因子计算(weighted sent / confidence-weighted / momentum + 公式说明)
Step 6: 结果输出(因子值表 + 历史统计 + 每新闻情绪贡献明细)
```
---
### 13.9 脚本一览
| 脚本 | 参数 | 用途 | 数据源 fallback |
|------|------|------|:---:|
| `agent_cli.py` | 子命令 + 参数 | 日常操作入口 | ✅ |
| `demo_data_manager.py` | `--ts_code` `--start` | Sprint 0 验证 | ✅ |
| `demo_factor_engine.py` | `--ts_code` `--ts_code2` | Sprint 1 验证 | ✅ |
| `demo_backtest.py` | `--ts_code` | Sprint 2 验证 | ✅ |
| `demo_optimizer.py` | `--ts_code` `--trials` | Sprint 3 验证 | ✅ |
| `demo_ml.py` | `--ts_code` `--lookahead` | Sprint 4 验证 | ✅ |
| `demo_sentiment.py` | `--ts_code` `--no-qwen` | Sprint 5 快速验证 | ✅ |
| `demo_sentiment_detail.py` | 14 个 argparse 参数 | Sprint 5 详细演示 | ✅ |