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>
This commit is contained in:
2026-06-07 15:59:05 +08:00
co-authored by Claude Opus 4.7
commit 271a9343a5
293 changed files with 59598 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
"""
信号生成工具函数。
因子值 → 交易信号的桥梁,纯函数无副作用。
"""
import pandas as pd
def factor_to_threshold_signal(
factor_series: pd.Series,
buy_threshold: float,
sell_threshold: float | None = None,
cross_direction: str = "up",
) -> pd.Series:
"""
因子阈值交叉信号。
参数:
factor_series: 因子值 Series
buy_threshold: 买入阈值(如 RSI < 30 则买)
sell_threshold: 卖出阈值(如 RSI > 70 则卖),None 表示平所有仓
cross_direction: 'up'=因子向上穿越阈值时触发, 'down'=向下穿越
返回:
信号 Series1=买入, 0=平仓
"""
signals = pd.Series(0, index=factor_series.index)
if cross_direction == "down":
buys = factor_series < buy_threshold
else:
buys = factor_series > buy_threshold
signals[buys] = 1
if sell_threshold is not None:
if cross_direction == "down":
sells = factor_series > sell_threshold
else:
sells = factor_series < sell_threshold
signals[sells] = 0
# 过滤连续信号
signals = _filter_consecutive(signals)
return signals
def factor_to_quantile_signal(
factor_series: pd.Series,
top_quantile: float = 0.8,
bottom_quantile: float = 0.2,
) -> pd.Series:
"""
因子分位数信号 — 按滚动分位数判断。
参数:
factor_series: 因子值
top_quantile: 高于此分位买入
bottom_quantile: 低于此分位平仓
返回:
信号 Series
"""
top = factor_series.quantile(top_quantile)
bottom = factor_series.quantile(bottom_quantile)
signals = pd.Series(0, index=factor_series.index)
signals[factor_series > top] = 1
signals[factor_series < bottom] = 0
return _filter_consecutive(signals)
def cross_signal(
fast: pd.Series,
slow: pd.Series,
) -> pd.Series:
"""
金叉/死叉信号。
fast 上穿 slow → 买入(1)
fast 下穿 slow → 平仓(0)
"""
fast = fast.dropna()
slow = slow.dropna()
common_idx = fast.index.intersection(slow.index)
fast, slow = fast[common_idx], slow[common_idx]
signals = pd.Series(-1, index=common_idx)
above = (fast > slow).fillna(False)
above = above.infer_objects(copy=False)
# 交叉点:今天 above=True 且昨天 above=False → 金叉
prev = above.shift(1).fillna(False)
prev = prev.infer_objects(copy=False)
cross_up = above & ~prev
cross_down = ~above & prev
signals[cross_up] = 1
signals[cross_down] = 0
return _filter_consecutive(signals)
def _filter_consecutive(signals: pd.Series) -> pd.Series:
"""过滤连续相同信号,只保留首次出现的信号。"""
result = signals.copy()
prev = None
for i in range(len(result)):
if result.iloc[i] == prev:
result.iloc[i] = -1 # 标记为不操作
else:
prev = result.iloc[i]
return result[result != -1].reindex(signals.index).fillna(-1)