Files
QuantEngineByItz/spec/60_quant_engine_wbs.yaml
T
kjh2064 49dc382a39
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 15s
Validators (Pushes and Pull Requests) / validate-core (push) Failing after 56s
fix: remove circular WBS evidence dependency
2026-07-12 12:33:46 +09:00

716 lines
34 KiB
YAML

# =============================================================================
# QuantEngine 데이터 실증 기반 퀀트 엔진 로드맵 + WBS (기계 판정)
# =============================================================================
# formula_id: QUANT_ENGINE_WBS_V1
# 원칙: 모든 작업(task)은 "완료 주장"이 아니라 게이트 실행으로만 DONE 판정된다.
# - BE 실증: pg_query(PostgreSQL 쿼리) + log_pattern(Serilog 로그) + json_gate(아티팩트)
# - FE 실증: playwright_report(스펙 PASS) + file_exists(스크린샷)
# 실행:
# 단일 작업 검증: python tools/verify_wbs_task_v1.py --task <TASK_ID>
# → Temp/evidence/<TASK_ID>/verdict.json + 원시 증거 보존
# 전체 WBS 게이트: python tools/validate_quant_engine_wbs_v1.py
# → Temp/quant_engine_wbs_v1.json (status=DONE 작업의 증거 재검증)
# 관례: spec/16_data_gaps_roadmap.yaml 의 success_criteria 구조
# (expected_success_value / evidence_artifacts / verification_commands) 준수.
# 검증 로직만 하드코딩 → evidence_checks 선언형으로 일반화.
# =============================================================================
meta:
formula_id: QUANT_ENGINE_WBS_V1
version: 1
created: "2026-07-12"
authority: "governance/authority_matrix.yaml"
validator: tools/validate_quant_engine_wbs_v1.py
task_verifier: tools/verify_wbs_task_v1.py
remote_evidence_collector: tools/collect_remote_wbs_evidence_v1.py
evidence_root: Temp/evidence
status_values: [PENDING, IN_PROGRESS, DONE] # DONE = 해당 verdict.json gate=PASS 필수
db_connection:
# 검증기의 PostgreSQL 접속 순서:
# 1) env QE_WBS_PG_DSN (psycopg DSN)
# 2) env ConnectionStrings__DefaultConnection (.NET 형식 → 자동 변환)
# 3) src/dotnet/QuantEngine.Web/appsettings.Development.json 의 ConnectionStrings.DefaultConnection
# (로컬은 SSH 터널 127.0.0.1:5432 전제 — CLAUDE.md "Local Development & Testing")
dotnet_appsettings: src/dotnet/QuantEngine.Web/appsettings.Development.json
remote_evidence:
collector: "python tools/collect_remote_wbs_evidence_v1.py --target <ssh-target>"
policy: "Collect journal and JSON artifacts only; never copy env files or passwords."
postgres: "Use QE_WBS_PG_DSN through an approved SSH tunnel; do not embed credentials in evidence."
# -----------------------------------------------------------------------------
# 검증 체크 타입 사전 (verify_wbs_task_v1.py 가 해석하는 선언형 vocabulary)
# -----------------------------------------------------------------------------
evidence_check_types:
pg_query: "PostgreSQL 쿼리 1개 실행, 단일 스칼라 결과를 expect{min,max,equals}와 비교. 원시 결과를 pg_query_<n>.json 으로 보존"
log_pattern: "file_glob 로그 파일들에서 정규식 매칭. expect{min_matches, max_age_hours(파일 mtime 기준)}. 매칭 라인을 log_excerpt.txt 로 보존"
json_gate: "path 의 JSON 아티팩트에서 expect 의 키-값 검사 (점 표기 경로 지원, 값 '>=N' 비교 지원)"
file_exists: "paths 의 모든 파일 존재 (expect.min_bytes 선택)"
playwright_report: "Playwright JSON 리포트(report)에서 spec_file 의 결과가 expect{passed_min, failed} 충족"
# =============================================================================
# 로드맵 (M0 → M5)
# =============================================================================
roadmap:
scope_note: >
전통 팩터(모멘텀/거래량/수급/실적/매크로/밸류/재무건전성 = spec/08_scoring_rules.yaml SS001)
+ ATR 리스크 관리 기본 포함. 최신 기법은 레짐 감지 + 워크포워드 캘리브레이션 + 거래비용
반영 평가로 한정(사용자 확정, 2026-07-12). 딥러닝/인트라데이/대체데이터/실거래 집행 제외
(은퇴자산 + read-only KIS 거버넌스: governance/rules/06_no_direct_api_trading.yaml 유지).
phases:
M0:
name: "실증 하네스 + 정직성 정리"
goal: "완료 주장이 불가능한 구조 확립 — 검증기/증거 규약/CI 편입 + 가짜 검증 제거"
exit_gate: "validate_quant_engine_wbs 가 release DAG/CI 노드로 PASS; dotnet test + Playwright evidence 스위트 CI 편입; 디버그 스펙 격리·가짜 PASS 제거"
tasks: [QE-M0-01, QE-M0-02, QE-M0-03, QE-M0-04, QE-M0-05, QE-M0-06]
M1:
name: "수집 파이프라인 배선"
goal: "운영 앱이 실제 KIS 데이터를 수집하도록 고아 오케스트레이터 배선 (첫 실데이터 실증)"
exit_gate: "Hangfire daily-collection + POST /api/collection/run 으로 kis_collection_* 에 실데이터 적재, Admin Collection 페이지 Playwright 실증"
tasks: [QE-M1-01, QE-M1-02, QE-M1-03, QE-M1-04, QE-M1-05]
M2:
name: "히스토리 시계열 저장소"
goal: "모멘텀 팩터·백테스트의 전제인 일봉/매크로 시계열 축적 (2년 백필)"
exit_gate: "price_history_daily/macro_history_daily 에 유니버스 2년치; (ticker,date) 중복 0; 거래일 캘린더 대비 gap 0"
tasks: [QE-M2-01, QE-M2-02, QE-M2-03, QE-M2-04, QE-M2-05]
M3:
name: "실데이터 팩터 계산"
goal: "SS001 전통 팩터를 PG 히스토리에서 계산해 engine_history 에 적재, 파일 개수 골든커버리지를 수치 패리티로 대체"
exit_gate: "factor_output_history 에 유니버스 전체 스코어(0-100); Python 참조 대비 패리티 ≥20 formula tol 1e-9 PASS"
tasks: [QE-M3-01, QE-M3-02, QE-M3-03, QE-M3-04, QE-M3-05]
M4:
name: "백테스팅 + 검증"
goal: "point-in-time 데이터만 사용하는 워크포워드 백테스터 + 거래비용 모델 + no-lookahead 게이트 실배선"
exit_gate: "Sharpe/MDD/턴오버 JSON 산출; no-lookahead 정상 PASS + 오염 픽스처 FAIL 양방향; T+5/T+20 원장 표본 ≥30"
tasks: [QE-M4-01, QE-M4-02, QE-M4-03, QE-M4-04, QE-M4-05]
M5:
name: "포트폴리오 구성 + 최신 기법"
goal: "레짐 감지 + SS001 가중치 워크포워드 캘리브레이션(제약+shrinkage) + 변동성 타게팅 사이징"
exit_gate: "백필 전 기간 레짐 라벨; 캘리브레이션 가중치 제약 준수 + OOS Sharpe 정직 보고; 최종 포트폴리오 패킷 캡 준수"
tasks: [QE-M5-01, QE-M5-02, QE-M5-03, QE-M5-04]
# =============================================================================
# WBS 작업 목록
# =============================================================================
tasks:
# ---------------------------------------------------------------------------
# M0 — 실증 하네스 + 정직성 정리
# ---------------------------------------------------------------------------
QE-M0-01:
title: "WBS 스펙(YAML) + 로드맵 작성, 레거시 로드맵 문서에 포인터 추가"
status: DONE
depends_on: []
owner_files:
- spec/60_quant_engine_wbs.yaml
- docs/ROADMAP_WBS.md
success_criteria:
expected_success_value: { spec_exists: true, legacy_pointer_appended: true }
evidence_artifacts: [Temp/evidence/QE-M0-01/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M0-01"]
evidence_checks:
- type: file_exists
paths: [spec/60_quant_engine_wbs.yaml]
expect: { min_bytes: 10000 }
- type: log_pattern
file_glob: docs/ROADMAP_WBS.md
pattern: "QUANT_ENGINE_WBS_V1"
expect: { min_matches: 1 }
QE-M0-02:
title: "증거 검증기 2종 구현 (단일 작업 verifier + 전체 WBS validator) + 유닛테스트"
status: DONE
depends_on: []
owner_files:
- tools/verify_wbs_task_v1.py
- tools/validate_quant_engine_wbs_v1.py
- tests/unit/test_validate_quant_engine_wbs_v1.py
success_criteria:
expected_success_value: { self_test: PASS, synthetic_pass_fail_bidirectional: true }
evidence_artifacts: [Temp/evidence/QE-M0-02/verdict.json, Temp/quant_engine_wbs_v1.json]
verification_commands:
- "python -m pytest tests/unit/test_validate_quant_engine_wbs_v1.py -q"
- "python tools/verify_wbs_task_v1.py --task QE-M0-02"
evidence_checks:
- type: file_exists
paths:
- tools/verify_wbs_task_v1.py
- tools/validate_quant_engine_wbs_v1.py
- tests/unit/test_validate_quant_engine_wbs_v1.py
- type: log_pattern
file_glob: tools/validate_quant_engine_wbs_v1.py
pattern: "def main"
expect: { min_matches: 1 }
QE-M0-03:
title: "Playwright 정직성 정리 + evidence 프로젝트 + npm 스크립트"
status: DONE
depends_on: [QE-M0-02]
owner_files:
- playwright.config.ts
- package.json
- tests/e2e/archive/
notes: >
디버그 스펙(~17개: debug-login, html-debug, wasm-test, framework-check, console-check,
screenshot-diagnosis, inspect-page, login* 변형 등)을 tests/e2e/archive/ 로 이동하고
testIgnore 로 제외. full-validation.spec.ts 의 assert 없는 가짜 "[PASS]" 배너 테스트
제거(파일째 archive). evidence 프로젝트: testDir tests/e2e/evidence, screenshot 'on',
trace 'on', JSON reporter → Temp/evidence/playwright-last-run.json.
npm 스크립트: verify:task / verify:wbs / test:e2e / test:evidence
success_criteria:
expected_success_value: { default_project_specs: ["admin-pages.spec.ts"], fake_pass_removed: true }
evidence_artifacts: [Temp/evidence/QE-M0-03/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M0-03"]
evidence_checks:
- type: file_exists
paths: [tests/e2e/archive]
- type: log_pattern
file_glob: playwright.config.ts
pattern: "evidence"
expect: { min_matches: 1 }
- type: log_pattern
file_glob: package.json
pattern: "verify:task"
expect: { min_matches: 1 }
- type: log_pattern
file_glob: tests/e2e/full-validation.spec.ts
pattern: ".*"
expect: { max_matches: 0 } # 파일이 기본 testDir 에 더 이상 존재하지 않아야 함
QE-M0-04:
title: "CI에 dotnet test 편입 + 고아 QuantEngine.Web.Tests 처리"
status: DONE
depends_on: []
owner_files:
- .gitea/workflows/ci.yml
- src/dotnet/QuantEngine.Web.Tests/
notes: >
QuantEngine.Web.Tests/DashboardComponentTests.cs 는 csproj 없는 고아(폐기된 Blazor 대상).
현 Razor Pages UI 에 맞지 않으면 삭제. ci.yml 에 dotnet test 스텝 추가.
success_criteria:
expected_success_value: { ci_has_dotnet_test: true, core_tests_green: true }
evidence_artifacts: [Temp/evidence/QE-M0-04/verdict.json]
verification_commands:
- "dotnet test src/dotnet/QuantEngine.Core.Tests/QuantEngine.Core.Tests.csproj -c Release --nologo"
- "python tools/verify_wbs_task_v1.py --task QE-M0-04"
evidence_checks:
- type: log_pattern
file_glob: .gitea/workflows/ci.yml
pattern: "dotnet test"
expect: { min_matches: 1 }
QE-M0-05:
title: "release DAG + CI 에 validate_quant_engine_wbs 게이트 노드 등록"
status: DONE
depends_on: [QE-M0-02]
owner_files:
- spec/41_release_dag.yaml
- .gitea/workflows/ci.yml
success_criteria:
expected_success_value: { dag_node: validate_quant_engine_wbs, ci_step: true }
evidence_artifacts: [Temp/evidence/QE-M0-05/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M0-05"]
evidence_checks:
- type: log_pattern
file_glob: spec/41_release_dag.yaml
pattern: "validate_quant_engine_wbs"
expect: { min_matches: 1 }
- type: log_pattern
file_glob: .gitea/workflows/ci.yml
pattern: "validate_quant_engine_wbs_v1"
expect: { min_matches: 1 }
QE-M0-06:
title: "골든커버리지 정직성 표기 (coverage_basis: FILE_COUNT_ONLY)"
status: DONE
depends_on: []
owner_files:
- tools/validate_golden_coverage_100.py
notes: >
골든 테스트 174개는 실행되지 않는 placeholder. 커버리지 판정 출력에
coverage_basis: FILE_COUNT_ONLY 필드를 추가해 실체를 명시(삭제는 M3 패리티 대체 후).
success_criteria:
expected_success_value: { honesty_field: FILE_COUNT_ONLY }
evidence_artifacts: [Temp/evidence/QE-M0-06/verdict.json]
verification_commands:
- "python tools/validate_golden_coverage_100.py"
- "python tools/verify_wbs_task_v1.py --task QE-M0-06"
evidence_checks:
- type: json_gate
path: Temp/golden_coverage_100_v1.json
expect: { coverage_basis: FILE_COUNT_ONLY }
# ---------------------------------------------------------------------------
# M1 — 수집 파이프라인 배선 (첫 실데이터 실증)
# ---------------------------------------------------------------------------
QE-M1-01:
title: "KisDataCollectionOrchestrator DI 등록 + daily-collection Hangfire 잡 실구현"
status: PENDING
depends_on: [QE-M0-02]
owner_files:
- src/dotnet/QuantEngine.Web/Program.cs
- src/dotnet/QuantEngine.Web/Services/SchedulerService.cs
notes: >
Program.cs 에 PriceDataNormalizer / SourcePriorityResolver / ICollectionOrchestrator →
KisDataCollectionOrchestrator 등록. RunDailyCollectionAsync 의 Task.Delay 시뮬레이션을
IServiceScopeFactory 스코프 → 오케스트레이터 호출로 교체 (runId "daily-yyyyMMdd-HHmmss").
완료 로그: "Collection run {RunId} completed: {Snapshots} snapshots, {Errors} errors".
상태값은 대문자 COMPLETED / COMPLETED_WITH_ERRORS (KisDataCollectionOrchestrator.cs:103).
success_criteria:
expected_success_value: { runs_completed_min: 1, snapshots_min: 5, hangfire_job: daily-collection }
evidence_artifacts: [Temp/evidence/QE-M1-01/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M1-01"]
evidence_checks:
- type: pg_query
sql: >
SELECT count(*) FROM quantengine.kis_collection_runs
WHERE status LIKE 'COMPLETED%' AND total_snapshots >= 5
AND started_at >= (now() - interval '24 hours')::text
expect: { min: 1 }
- type: pg_query
sql: >
SELECT count(DISTINCT s.ticker) FROM quantengine.kis_collection_snapshots s
JOIN quantengine.kis_collection_runs r ON r.run_id = s.run_id
WHERE r.started_at >= (now() - interval '24 hours')::text
expect: { min: 5 }
- type: log_pattern
file_glob: src/dotnet/QuantEngine.Web/logs/quantengine-*.log
pattern: 'Collection run .+ completed: \d+ snapshots'
expect: { min_matches: 1, max_age_hours: 24 }
- type: json_gate
path: Temp/kis_dotnet_collection_v1.json
expect: { gate: PASS }
QE-M1-02:
title: "Admin Collection 페이지 FE 실증 (실제 run 렌더링을 Playwright 로 증명)"
status: PENDING
depends_on: [QE-M1-01, QE-M0-03]
owner_files:
- tests/e2e/evidence/qe-m1-02-collection-run.spec.ts
notes: >
필수 3요소: (a) 기대값을 /api/collection/runs API 에서 조회(하드코딩 금지),
(b) /Admin/Collection DOM 에서 run_id·스냅샷 수·상태 배지를 기대값과 assert,
(c) assert 시점 스크린샷 → Temp/evidence/QE-M1-02/screenshots/{01-collection-page,02-run-detail}.png
success_criteria:
expected_success_value: { spec_passed: 1, screenshots: 2, dom_equals_api: true }
evidence_artifacts: [Temp/evidence/QE-M1-02/verdict.json]
verification_commands:
- "npx playwright test --project=evidence tests/e2e/evidence/qe-m1-02-collection-run.spec.ts"
- "python tools/verify_wbs_task_v1.py --task QE-M1-02"
evidence_checks:
- type: playwright_report
report: Temp/evidence/playwright-last-run.json
spec_file: qe-m1-02-collection-run.spec.ts
expect: { passed_min: 1, failed: 0 }
- type: file_exists
paths:
- Temp/evidence/QE-M1-02/screenshots/01-collection-page.png
- Temp/evidence/QE-M1-02/screenshots/02-run-detail.png
expect: { min_bytes: 10000 }
QE-M1-03:
title: "POST /api/collection/run 실구현 (BackgroundJob.Enqueue + 인증 필수화)"
status: PENDING
depends_on: [QE-M1-01]
owner_files:
- src/dotnet/QuantEngine.Web/Endpoints/CollectionEndpoints.cs
notes: >
202 no-op 스텁을 Hangfire BackgroundJob.Enqueue(오케스트레이터 실행)로 교체,
응답에 {runId} 포함. AllowAnonymous 제거(쿠키 인증).
로그: "Collection run {RunId} enqueued".
success_criteria:
expected_success_value: { returns_run_id: true, auth_required: true }
evidence_artifacts: [Temp/evidence/QE-M1-03/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M1-03"]
evidence_checks:
- type: log_pattern
file_glob: src/dotnet/QuantEngine.Web/logs/quantengine-*.log
pattern: 'Collection run .+ enqueued'
expect: { min_matches: 1, max_age_hours: 24 }
- type: pg_query
sql: >
SELECT count(*) FROM quantengine.kis_collection_runs
WHERE run_id LIKE 'api-%' AND started_at >= (now() - interval '24 hours')::text
expect: { min: 1 }
QE-M1-04:
title: "오케스트레이터 로깅 복원 + 출력 아티팩트 표준화 + 멀티소스 폴백 배선"
status: PENDING
depends_on: [QE-M1-01]
owner_files:
- src/dotnet/QuantEngine.Application/Services/KisDataCollectionOrchestrator.cs
notes: >
"// Log: skipped" → ILogger<KisDataCollectionOrchestrator> 복원.
출력 경로 Path.GetTempPath() → <repo>/Temp/kis_dotnet_collection_v1.json,
형식 {formula_id: KIS_DOTNET_COLLECTION_V1, gate, summary{success_count, error_count, source_counts}}.
SourcePriorityResolver 를 통해 Naver/Yahoo 폴백 경로 활성화.
success_criteria:
expected_success_value: { gate: PASS, source_counts_min: 1, error_rows_on_bad_ticker: true }
evidence_artifacts: [Temp/evidence/QE-M1-04/verdict.json, Temp/kis_dotnet_collection_v1.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M1-04"]
evidence_checks:
- type: json_gate
path: Temp/kis_dotnet_collection_v1.json
expect: { formula_id: KIS_DOTNET_COLLECTION_V1, gate: PASS }
- type: log_pattern
file_glob: src/dotnet/QuantEngine.Web/logs/quantengine-*.log
pattern: 'Collecting ticker'
expect: { min_matches: 1, max_age_hours: 24 }
QE-M1-05:
title: "티커 유니버스를 GatherTradingData 파서/DB 설정에서 로드 (하드코딩 제거)"
status: PENDING
depends_on: [QE-M1-01]
owner_files:
- src/dotnet/QuantEngine.Web/Services/SchedulerService.cs
success_criteria:
expected_success_value: { distinct_tickers_equals_universe: true }
evidence_artifacts: [Temp/evidence/QE-M1-05/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M1-05"]
evidence_checks:
- type: log_pattern
file_glob: src/dotnet/QuantEngine.Web/Services/SchedulerService.cs
pattern: '005930.+000660.+051910'
expect: { max_matches: 0 } # 하드코딩 티커 배열 부재
# ---------------------------------------------------------------------------
# M2 — 히스토리 시계열 저장소
# ---------------------------------------------------------------------------
QE-M2-01:
title: "V6 마이그레이션: price_history_daily + macro_history_daily"
status: PENDING
depends_on: [QE-M1-01]
owner_files:
- src/dotnet/QuantEngine.Infrastructure/Migrations/V6__Add_Market_Time_Series.sql
notes: >
price_history_daily(ticker, trade_date, open/high/low/close numeric, volume bigint,
source text, collected_at timestamptz, PK(ticker, trade_date));
macro_history_daily(symbol, trade_date, value numeric, source, PK(symbol, trade_date)).
DbUp 마이그레이션 추가 시 docs/db/quantengine.dbml 동기화 필수 (CLAUDE.md 규칙 — 아래 체크로 강제).
success_criteria:
expected_success_value: { tables_created: 2 }
evidence_artifacts: [Temp/evidence/QE-M2-01/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M2-01"]
evidence_checks:
- type: pg_query
sql: >
SELECT count(*) FROM information_schema.tables
WHERE table_schema='quantengine' AND table_name IN ('price_history_daily','macro_history_daily')
expect: { equals: 2 }
- type: log_pattern
file_glob: docs/db/quantengine.dbml
pattern: 'price_history_daily'
expect: { min_matches: 1 } # DBML 동기화 강제
QE-M2-02:
title: "일봉 OHLCV 시계열 적재 (daily run 마다 upsert, 재실행 중복 0)"
status: PENDING
depends_on: [QE-M2-01]
owner_files:
- src/dotnet/QuantEngine.Application/Services/KisDataCollectionOrchestrator.cs
success_criteria:
expected_success_value: { rows_per_ticker_min: 1, duplicate_on_rerun: 0 }
evidence_artifacts: [Temp/evidence/QE-M2-02/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M2-02"]
evidence_checks:
- type: pg_query
sql: "SELECT count(*) FROM quantengine.price_history_daily WHERE collected_at >= now() - interval '24 hours'"
expect: { min: 1 }
- type: pg_query
sql: >
SELECT count(*) FROM (SELECT ticker, trade_date, count(*) c
FROM quantengine.price_history_daily GROUP BY 1,2 HAVING count(*) > 1) d
expect: { equals: 0 }
QE-M2-03:
title: "2년치 백필 툴 (KIS chart API 페이지네이션 + rate-limit, 매크로는 yfinance→PG)"
status: PENDING
depends_on: [QE-M2-01]
owner_files:
- src/dotnet/QuantEngine.Tools/
- src/quant_engine/macro_index_collection_v1.py
success_criteria:
expected_success_value: { bars_per_ticker_min: 480, macro_bars_min: 480 }
evidence_artifacts: [Temp/evidence/QE-M2-03/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M2-03"]
evidence_checks:
- type: pg_query
sql: "SELECT coalesce(min(c),0) FROM (SELECT count(*) c FROM quantengine.price_history_daily GROUP BY ticker) t"
expect: { min: 480 }
- type: pg_query
sql: "SELECT count(*) FROM quantengine.macro_history_daily WHERE symbol IN ('KOSPI','KOSDAQ')"
expect: { min: 960 }
QE-M2-04:
title: "시계열 무결성 게이트 (거래일 캘린더 대비 gap 0, 가격 sanity)"
status: PENDING
depends_on: [QE-M2-03]
owner_files:
- tools/validate_price_history_integrity_v1.py
success_criteria:
expected_success_value: { gap_count: 0, invalid_price_rows: 0 }
evidence_artifacts: [Temp/evidence/QE-M2-04/verdict.json, Temp/price_history_integrity_v1.json]
verification_commands:
- "python tools/validate_price_history_integrity_v1.py"
- "python tools/verify_wbs_task_v1.py --task QE-M2-04"
evidence_checks:
- type: json_gate
path: Temp/price_history_integrity_v1.json
expect: { gate: PASS, gap_count: 0 }
QE-M2-05:
title: "히스토리 현황 FE (per-ticker bar 수/기간/gap — API 값과 DOM 대조)"
status: PENDING
depends_on: [QE-M2-03, QE-M0-03]
owner_files:
- src/dotnet/QuantEngine.Web/Pages/Admin/Collection/
- tests/e2e/evidence/qe-m2-05-history-tab.spec.ts
success_criteria:
expected_success_value: { spec_passed: 1, screenshots_min: 1 }
evidence_artifacts: [Temp/evidence/QE-M2-05/verdict.json]
verification_commands:
- "npx playwright test --project=evidence tests/e2e/evidence/qe-m2-05-history-tab.spec.ts"
- "python tools/verify_wbs_task_v1.py --task QE-M2-05"
evidence_checks:
- type: playwright_report
report: Temp/evidence/playwright-last-run.json
spec_file: qe-m2-05-history-tab.spec.ts
expect: { passed_min: 1, failed: 0 }
# ---------------------------------------------------------------------------
# M3 — 실데이터 팩터 계산
# ---------------------------------------------------------------------------
QE-M3-01:
title: "Point-in-time 리더 (GetBarsAsOf — lookahead 구조적 차단 + xUnit 증명)"
status: PENDING
depends_on: [QE-M2-03]
owner_files:
- src/dotnet/QuantEngine.Infrastructure/Repositories/
- src/dotnet/QuantEngine.Core.Tests/
success_criteria:
expected_success_value: { asof_leak_tests_green: true }
evidence_artifacts: [Temp/evidence/QE-M3-01/verdict.json]
verification_commands:
- "dotnet test src/dotnet/QuantEngine.Core.Tests/QuantEngine.Core.Tests.csproj -c Release --filter PriceHistoryReader"
- "python tools/verify_wbs_task_v1.py --task QE-M3-01"
evidence_checks:
- type: log_pattern
file_glob: src/dotnet/QuantEngine.Core.Tests/**/*.cs
pattern: 'GetBarsAsOf'
expect: { min_matches: 1 }
QE-M3-02:
title: "전통 팩터 계산기 (모멘텀 20/60/120d·RS, 저변동성 ATR%·stdev·beta, 밸류/퀄리티)"
status: PENDING
depends_on: [QE-M3-01]
owner_files:
- src/dotnet/QuantEngine.Core/Domain/
- tools/validate_factor_parity_v1.py
success_criteria:
expected_success_value: { parity_formulas_min: 20, tolerance: 1e-9 }
evidence_artifacts: [Temp/evidence/QE-M3-02/verdict.json, Temp/factor_parity_v1.json]
verification_commands:
- "python tools/validate_factor_parity_v1.py"
- "python tools/verify_wbs_task_v1.py --task QE-M3-02"
evidence_checks:
- type: json_gate
path: Temp/factor_parity_v1.json
expect: { gate: PASS, compared_count: ">=20" }
QE-M3-03:
title: "SS001 합성 스코어 + HF001-09 → engine_history.factor_output_history 적재"
status: PENDING
depends_on: [QE-M3-02]
owner_files:
- src/dotnet/QuantEngine.Application/Services/
success_criteria:
expected_success_value: { scored_universe_full: true, score_range_0_100: true }
evidence_artifacts: [Temp/evidence/QE-M3-03/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M3-03"]
evidence_checks:
- type: pg_query
sql: "SELECT count(*) FROM engine_history.factor_output_history WHERE created_at >= now() - interval '24 hours'"
expect: { min: 5 }
QE-M3-04:
title: "PipelineOrchestrator 정직화 (1-2단계 실구현, 나머지 STUBBED 표기 — mock PASS 금지)"
status: PENDING
depends_on: [QE-M3-03]
owner_files:
- src/dotnet/QuantEngine.Application/Services/PipelineOrchestrator.cs
success_criteria:
expected_success_value: { computed_steps_min: 2, stub_steps_marked: STUBBED }
evidence_artifacts: [Temp/evidence/QE-M3-04/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M3-04"]
evidence_checks:
- type: log_pattern
file_glob: src/dotnet/QuantEngine.Application/Services/PipelineOrchestrator.cs
pattern: 'STUBBED'
expect: { min_matches: 1 }
QE-M3-05:
title: "스코어 FE (SS001 테이블 — factor_output_history 값과 DOM 대조)"
status: PENDING
depends_on: [QE-M3-03, QE-M0-03]
owner_files:
- tests/e2e/evidence/qe-m3-05-scores.spec.ts
success_criteria:
expected_success_value: { spec_passed: 1 }
evidence_artifacts: [Temp/evidence/QE-M3-05/verdict.json]
verification_commands:
- "npx playwright test --project=evidence tests/e2e/evidence/qe-m3-05-scores.spec.ts"
- "python tools/verify_wbs_task_v1.py --task QE-M3-05"
evidence_checks:
- type: playwright_report
report: Temp/evidence/playwright-last-run.json
spec_file: qe-m3-05-scores.spec.ts
expect: { passed_min: 1, failed: 0 }
# ---------------------------------------------------------------------------
# M4 — 백테스팅 + 검증
# ---------------------------------------------------------------------------
QE-M4-01:
title: "백테스터 + 거래비용 모델 (Sharpe/MDD/턴오버/비용 드래그 JSON)"
status: PENDING
depends_on: [QE-M3-03]
owner_files:
- src/dotnet/QuantEngine.Core/Domain/Backtester.cs
- src/dotnet/QuantEngine.Tools/
success_criteria:
expected_success_value: { metrics_populated: [sharpe, mdd, turnover, cost_drag] }
evidence_artifacts: [Temp/evidence/QE-M4-01/verdict.json, Temp/backtest_result_v1.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M4-01"]
evidence_checks:
- type: json_gate
path: Temp/backtest_result_v1.json
expect: { gate: PASS }
QE-M4-02:
title: "no-lookahead 게이트 실배선 (정상 PASS + 오염 픽스처 FAIL 양방향 검증)"
status: PENDING
depends_on: [QE-M4-01]
owner_files:
- tools/validate_no_lookahead_bias_v1.py
success_criteria:
expected_success_value: { real_run: PASS, corrupted_fixture: FAIL }
evidence_artifacts: [Temp/evidence/QE-M4-02/verdict.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M4-02"]
evidence_checks:
- type: json_gate
path: Temp/no_lookahead_bias_v1.json
expect: { gate: PASS }
QE-M4-03:
title: "워크포워드 하네스 (24m train / 6m test 롤링, 윈도우 ≥4)"
status: PENDING
depends_on: [QE-M4-01]
owner_files:
- src/dotnet/QuantEngine.Tools/
success_criteria:
expected_success_value: { windows_min: 4, oos_metrics_nonnull: true }
evidence_artifacts: [Temp/evidence/QE-M4-03/verdict.json, Temp/walk_forward_v1.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M4-03"]
evidence_checks:
- type: json_gate
path: Temp/walk_forward_v1.json
expect: { gate: PASS, windows: ">=4" }
QE-M4-04:
title: "T+5/T+20 성과 원장 (prediction_accuracy 실표본 재계산, t5_sample≥30)"
status: PENDING
depends_on: [QE-M2-03]
owner_files:
- tools/
success_criteria:
expected_success_value: { t5_sample_min: 30 }
evidence_artifacts: [Temp/evidence/QE-M4-04/verdict.json, Temp/prediction_accuracy_harness_v2.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M4-04"]
evidence_checks:
- type: json_gate
path: Temp/prediction_accuracy_harness_v2.json
expect: { t5_sample: ">=30" }
QE-M4-05:
title: "백테스트 결과 FE (에쿼티커브/Sharpe/MDD — backtest_result_v1.json 값과 DOM 대조)"
status: PENDING
depends_on: [QE-M4-01, QE-M0-03]
owner_files:
- tests/e2e/evidence/qe-m4-05-backtest.spec.ts
success_criteria:
expected_success_value: { spec_passed: 1 }
evidence_artifacts: [Temp/evidence/QE-M4-05/verdict.json]
verification_commands:
- "npx playwright test --project=evidence tests/e2e/evidence/qe-m4-05-backtest.spec.ts"
- "python tools/verify_wbs_task_v1.py --task QE-M4-05"
evidence_checks:
- type: playwright_report
report: Temp/evidence/playwright-last-run.json
spec_file: qe-m4-05-backtest.spec.ts
expect: { passed_min: 1, failed: 0 }
# ---------------------------------------------------------------------------
# M5 — 포트폴리오 구성 + 최신 기법
# ---------------------------------------------------------------------------
QE-M5-01:
title: "레짐 감지기 (spec/11_market_regime.yaml — 실제 매크로 시계열, 전 거래일 라벨)"
status: PENDING
depends_on: [QE-M2-03]
owner_files:
- src/dotnet/QuantEngine.Core/Domain/
success_criteria:
expected_success_value: { regime_labels_full_window: true }
evidence_artifacts: [Temp/evidence/QE-M5-01/verdict.json, Temp/market_regime_v1.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M5-01"]
evidence_checks:
- type: json_gate
path: Temp/market_regime_v1.json
expect: { gate: PASS }
QE-M5-02:
title: "SS001 가중치 워크포워드 캘리브레이션 (±50% 제약 + shrinkage λ=0.5, 정직 보고)"
status: PENDING
depends_on: [QE-M4-03, QE-M5-01]
owner_files:
- src/dotnet/QuantEngine.Tools/
notes: "게이트는 방법론 필드(제약 준수, OOS 비교 존재)를 검증 — 캘리브레이션이 '이겨야' PASS 가 아님"
success_criteria:
expected_success_value: { weights_within_bounds: true, oos_comparison_reported: true }
evidence_artifacts: [Temp/evidence/QE-M5-02/verdict.json, Temp/weight_calibration_v1.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M5-02"]
evidence_checks:
- type: json_gate
path: Temp/weight_calibration_v1.json
expect: { gate: PASS }
QE-M5-03:
title: "변동성 타게팅 사이징 + heat/집중도 캡 합성 → 최종 목표 포트폴리오 패킷"
status: PENDING
depends_on: [QE-M5-02]
owner_files:
- src/dotnet/QuantEngine.Application/Services/
success_criteria:
expected_success_value: { weights_sum_lte_100: true, all_caps_satisfied: true }
evidence_artifacts: [Temp/evidence/QE-M5-03/verdict.json, Temp/target_portfolio_v1.json]
verification_commands: ["python tools/verify_wbs_task_v1.py --task QE-M5-03"]
evidence_checks:
- type: json_gate
path: Temp/target_portfolio_v1.json
expect: { gate: PASS }
QE-M5-04:
title: "포트폴리오·레짐 대시보드 FE (레짐 배지·목표 가중치 — API 값과 DOM 대조)"
status: PENDING
depends_on: [QE-M5-03, QE-M0-03]
owner_files:
- tests/e2e/evidence/qe-m5-04-portfolio.spec.ts
success_criteria:
expected_success_value: { spec_passed: 1 }
evidence_artifacts: [Temp/evidence/QE-M5-04/verdict.json]
verification_commands:
- "npx playwright test --project=evidence tests/e2e/evidence/qe-m5-04-portfolio.spec.ts"
- "python tools/verify_wbs_task_v1.py --task QE-M5-04"
evidence_checks:
- type: playwright_report
report: Temp/evidence/playwright-last-run.json
spec_file: qe-m5-04-portfolio.spec.ts
expect: { passed_min: 1, failed: 0 }