feat(platform): 因子平台与因子↔策略双向联动 v0.2.3

- 因子平台: /factors 一级页(检验/因子库/编辑器/组合/挖掘), DSL 公式因子(25 算子点选、双语字段、我的因子模板、脏公式守卫), 版本与生命周期, 自动挖掘 L1 统计筛选
- 因子↔策略四条桥: 触发器 Zap 快建因子条件信号、因子一键生成排名策略、自定义信号 AI 提示词接入因子分组、策略回测因子归因(胜/败单入场信号日因子均值, 独立 tab, 双语因子名)
- 回测: 统计卡新增盈亏比(≥1 红/<1 绿), 蒙卡回撤合并为中位/95% 双值卡(自适应字号), 高级设置基础过滤与策略编辑器参数对齐(5 组区间)
- 信号库独立页 /signals(原设置 tab 迁出), 持仓提醒入导航; 挖掘并入因子页第 5 tab, /mining 旧链接重定向
- 研究线配套: 因子目录 61→77(评分/矩阵双内核), stats_v2(Newey-West/BH-FDR/DSR), enriched 管道与异动/报价服务配套调整
- 文档: README 导航与特性表、features.md 因子平台章节、操作说明书 9.2、factor-platform-plan 执行状态与 §5、二开文档桥接说明; 交流与支持节改版
- 版本 0.2.2 → 0.2.3; 后端全量 1625 passed(1 例环境性跳过), 前端 build 通过
This commit is contained in:
shy3130
2026-09-05 15:41:15 +08:00
parent bab609b2d4
commit e0cd625ef4
71 changed files with 9127 additions and 548 deletions
+45 -3
View File
@@ -11,7 +11,7 @@ from types import SimpleNamespace
import polars as pl
import pytest
from app.market_time import CN_TZ
from app.market_time import CN_TZ, cn_today
from app.services.data_integrity import (
AUTO_REPAIR_MAX_LAG_DAYS,
IntegrityIssue,
@@ -189,19 +189,61 @@ def test_realtime_daily_builder_drops_halted_rows_before_zero_fill():
"symbol": "600001.SH", "last_price": 10.0,
"open": 9.9, "high": 10.1, "low": 9.8,
"volume": 1000.0, "amount": 10000.0,
"timestamp": _ts_ms(TODAY, time(10, 0)),
"timestamp": _ts_ms(cn_today(), time(10, 0)),
},
{
"symbol": "600002.SH", "last_price": 20.0,
"open": 0.0, "high": 0.0, "low": 0.0,
"volume": 0.0, "amount": 0.0,
"timestamp": _ts_ms(TODAY, time(9, 15)),
"timestamp": _ts_ms(cn_today(), time(9, 15)),
},
])
assert result["symbol"].to_list() == ["600001.SH"]
def test_realtime_daily_builder_drops_stale_snapshot_rows():
"""回归: 停牌股快照停留旧日, 不得复制成当日假蜡烛 (301266.SZ 2026-09-04)。
实时源对停牌标的返回停牌前最后一份快照 — OHLCV 全为旧日真实值, 仅
timestamp 停在旧日。这种记录不属于当日, 必须在落盘前按 quote_ts 过滤。
"""
from app.services.quote_service import QuoteService
halted_since = cn_today() - timedelta(days=7)
result = QuoteService._build_daily([
{
"symbol": "600001.SH", "last_price": 10.0,
"open": 9.9, "high": 10.1, "low": 9.8,
"volume": 1000.0, "amount": 10000.0,
"timestamp": _ts_ms(cn_today(), time(15, 0)),
},
{
"symbol": "301266.SZ", "last_price": 24.97,
"open": 23.10, "high": 24.99, "low": 23.01,
"volume": 65038.0, "amount": 157796300.0,
"timestamp": _ts_ms(halted_since, time(15, 30)),
},
])
assert result["symbol"].to_list() == ["600001.SH"]
def test_realtime_daily_builder_keeps_rows_without_timestamp():
"""无时间戳的源无法判断快照新旧, 维持原行为保留 (不因缺列误删)。"""
from app.services.quote_service import QuoteService
result = QuoteService._build_daily([
{
"symbol": "600003.SH", "last_price": 8.0,
"open": 7.9, "high": 8.1, "low": 7.8,
"volume": 500.0, "amount": 4000.0,
},
])
assert result["symbol"].to_list() == ["600003.SH"]
def test_halt_filter_drops_legacy_zero_volume_row_after_ohlc_fill():
from app.indicators.pipeline import filter_halt_days