# Public SQS Calculation 이 문서는 tracker가 최종 public `SQS`를 어떻게 계산하는지, 그리고 왜 split-only 점수와 final leaderboard 점수가 다를 수 있는지를 정리한다. 2026-04-07부터 기본 leaderboard는 **public `SQS v9`**을 쓴다. - `SQS v9`: **3-pillar additive core** + regime adaptability (scenario-test RRS) (기본 rank) - `SQS v8`: `SQS v7` backbone + reset common-window blend (이전 기본; 하위호환 유지) - `SQS v7`: continuous OOT factor + 3-criterion deployment gate (v8 backbone) - `SQS v6`: `SQS v3` + reset common-window - `SQS v4`: `SQS v3` + comparable `10k common-window` - `SQS v5`: optional diagnostic. `SQS v3` + multi-capital common-window capital-growth blend - `sqs_v3_score`: deployment/WFV-first primary rank - `stress_sqs_score`: 예전 public score. stress OOT quality를 추가 패널티로 곱한 legacy score 핵심 결론부터 말하면: - `test split`만으로 나오는 `sqs_v2_score`는 final public `SQS`가 아니다. - final public `SQS v9`의 backbone은 아래다. - `train` / `valid` / `test` split (RQS 35%) - walk-forward summary (WFQS_v2 40%) - regime adaptability: scenario-test RRS → OOT quality → 50.0 neutral (25%) - main robustness summary (multiplied gate) - 여기에 comparable reset common-window summary가 있으면 CW blend 적용. - reset summary 없이 comparable `10k common-window summary`만 있으면 common-window blend. - `multi-capital common-window`는 diagnostics로는 저장하지만, 기본 rank에는 쓰지 않는다. - optional comparable capital buckets은 현재 `10,000 / 25,000 / 100,000`이다. - official comparable common-window date range는 현재 `2022-03-02 ~ 2026-03-24`다. - 기본 leaderboard active window는 현재 `IMP-0606` / `return_max_long_v6new.29` 이후다. 그 전 실험은 retired research로 남고, `--include-retired`에서만 본다. - 다만 `stress_sqs_score`도 같이 계산해 registry에 남긴다. ## v9 핵심 변경사항 (2026-04-07) ### 배경: OOT 편향 문제 v8의 OOT factor는 2020-2021 COVID 특수 구간 성과에 의존한다: - `compute_oot_factor_v2` = 0.75 + 0.25 × (oot_quality / 100) - COVID-era에서 잘 된 전략은 최대 +25% 보너스, 저조한 전략은 최대 -25% 패널티 실증 문제 (v8 기준): - v7.70: OOT quality 100 → factor 1.000 (+0pp 패널티 없음) - v7.119: OOT quality 50.2 → factor 0.876 (-8.7점 감점) - v7.119의 수익률/WFQS가 우수함에도 COVID 특이성으로 인한 의미 없는 감점 → OOT를 multiplier에서 제거하고, 12개 합성 시장 환경(scenario-test) 점수로 대체. ### v9 공식 ``` core = RQS × 0.35 + WFQS_v2 × 0.40 + regime_score × 0.25 v9_base = core × deployment_gate × rb_gate × activity_factor ``` #### 3 Pillars | Pillar | 가중치 | 출처 | 측정 대상 | |--------|--------|------|----------| | **RQS** | 35% | `compute_rqs` | test split 성과: 수익률, Sharpe, DD, PF | | **WFQS_v2** | 40% | `compute_wfqs_v2` | WFV 교차검증 일관성 + overfitting_penalty | | **Regime Adaptability** | 25% | scenario-test RRS | 12개 가상 시장 환경 적응력 | #### regime_score 결정 (fallback chain) ```python if scenario_robustness_score is not None: regime_score = scenario_robustness_score # scenario-test RRS (0-100) elif oot_quality is not None: regime_score = oot_quality # 기존 OOT quality (하위호환) else: regime_score = 50.0 # 중립 fallback ``` #### CW blend (v8과 동일) reset common-window 또는 common-window가 comparable하면 15% weight blend. ### v9 시뮬레이션 (regime=50 중립 기준) | 전략 | v8 SQS | v9 SQS (est.) | 변동 | 근거 | |------|--------|---------------|------|------| | v7.119 | 57.3 | ~61.0 | **+3.7** | OOT 패널티(-8.7점) 해소 | | v17.72 | 53.6 | ~57.5 | +3.9 | OOT 패널티 해소 | | v7.70 | 67.9 | ~68.4 | +0.5 | OOT 보너스(100) 제거되지만 높은 RQS 보상 | | v19.1 | 69.9 | ~71.3 | +1.4 | RQS+WFQS 모두 우수 | | v7.123 | 70.1 | ~68.6 | -1.5 | OOT 보너스 제거 | ### scenario-test 자동 실행 `fithia2 rescore-public`이 실행될 때, `scenario_robustness_score`가 없는 전략은 자동으로 12개 시나리오를 실행하여 RRS를 계산한다 (~10초/전략). ```bash fithia2 rescore-public # scenario-test 자동 실행 포함 fithia2 scenario-test --config v7.70 # 수동으로 단일 전략 테스트 ``` ### overfit-check 분리 `fithia2 overfit-check` (7-30분 소요)는 SQS에 포함하지 않고 별도 필드로 관리: ```bash fithia2 aoc --report overfit_report.json # 기존 결과 attach fithia2 aoc --run [--quick] # 직접 실행 ``` `JournalEntry.overfit_check_score` (0-100) + `overfit_check_breakdown`으로 저장. leaderboard에서 SQS 옆 참고 정보로 표시. --- ## v7/v8 핵심 변경사항 (2026-04-07) ### 변경 1: OOT binary gate → continuous factor 기존 v3 backbone은 4개 기준의 pass/fail step function: ``` 4/4 → 1.00, 3/4 → 0.85, 2/4 → 0.65, 1/4 → 0.40, 0/4 → 0.20 ``` v7부터 `compute_oot_factor_v2` (continuous, [0.75, 1.00]): ``` factor = 0.75 + 0.25 * (oot_quality / 100.0) ``` - OOT quality score는 기존 `compute_oot_robustness_quality` 재활용 (COVID 조정 정규화 범위) - 252d median 범위: [-15%, +10%] → 비례 점수 (기존 pass/fail 임계값 3.0% 폐기) - 최대 페널티: 25% (기존 80%) **이유**: COVID(2020-2021)는 단일 극단 이벤트. "orderly" 이벤트 전략이 COVID에서 저조한 건 설계 의도대로. binary gate는 역 인센티브 유발 (v7.123 사례: COVID 엔진 추가 → 수익률 하락). ### 변경 2: deployment gate 이중 페널티 제거 기존 deployment gate는 4개 기준 (positive_fold_rate, median, worst, **WFV gap**). WFV gap은 이미 `_gap_penalty`에서 연속적으로 WFQS_v2에 반영됨. v7부터 3개 기준: ``` pass_positive = positive_fold_rate_pct >= 70% pass_median = median_return_pct >= 5% pass_worst = worst_return_pct >= -5% factor: {3: 1.00, 2: 0.85, 1: 0.65, 0: 0.40} ``` ## 1. 어디서 계산되나 최종 public `SQS v9` 계산 진입점은 [`compute_public_sqs`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L568) 이고, 실제 계산은 [`compute_public_sqs_v9`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1762)가 한다. `v8` backbone인 `v7` 계산은 [`compute_public_sqs_v7`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1312) 에 있다. `v7` backbone인 `v3` 계산은 [`compute_public_sqs_v3`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1258) 에 남아 있다. 이전 reset CW 기본 표준 `v6` 계산은 [`compute_public_sqs_v6`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1552) 에 남아 있다 (하위호환). multi-capital experimental score는 [`compute_public_sqs_v5`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1467) 에 남아 있다. legacy stress-adjusted score는 [`compute_public_sqs_v2`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1199) 가 그대로 유지한다. public leaderboard / registry rebuild도 같은 함수를 쓴다: - [`rebuild_registry`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L2929) 즉, 수동 계산과 leaderboard 계산은 같은 코드 경로를 따라야 한다. ## 1.5 Overlay 경로는 retired 상태다 overlay/book-of-books 평가는 코드베이스에서 제거됐다. - 기본 leaderboard는 single-book 전략만 포함한다. - journal에 남아 있는 과거 overlay entry는 historical record로만 취급한다. - registry rebuild와 `fithia2 lb`는 overlay entry를 건너뛴다. ## 2. 흔한 오해 ### `sqs_v2_score`는 final public `SQS`가 아니다 `journal/improvement_journal.jsonl`에 들어가는 `sqs_v2_score`는 `test split` 하나만 보고 계산한 split-level quality 점수다. 이 값은 아래 함수에서 나온다: - [`compute_sqs_v2`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L178) 반면 final public `SQS v8`의 backbone인 `SQS v7/v3`는: - `RQS` - `WFQS v2` - deployment gate - main robustness gate - OOT robustness factor (v7부터 continuous [0.75, 1.00]) - activity factor 를 모두 반영한 값이다. 그래서 `sqs_v2_score=90.7`인데 public `SQS=54.7` 같은 일이 정상적으로 발생할 수 있다. ## 2.5 왜 `v3`로 바뀌었나 기존 public score는: ```text base_score * main_rb_gate * oot_gate * oot_quality_factor * activity_factor ``` 였다. 이 구조는 stress OOT를 - eligibility gate로 한 번 쓰고 - quality penalty로 한 번 더 써서 최근/실전 적합도가 강한 전략을 과하게 깎을 수 있었다. `SQS v3`는 다음처럼 바꿨다. ```text base_score * main_rb_gate * oot_gate * activity_factor ``` 즉 stress OOT는 **통과 여부를 확인하는 safety layer**로 남기고, 연속 quality 패널티는 primary ranking에서 제거했다. 대신 예전 점수는 `stress_sqs_score`로 registry에 그대로 남긴다. ## 2.6 왜 `v8`이 default인가 ### v3 → v4/v5/v6 변천사 한동안 `v5`로 `10k/25k/100k` multi-capital common-window를 기본 점수에 섞었다. 하지만 이 방식은 연구 속도를 떨어뜨렸고, historical baseline까지 전부 같은 three-bucket으로 다시 채워 넣어야 공정 비교가 가능했다. 그래서 기본 rank는 `v4`(10k reset CW)로 돌렸다가, 이후 `v6`(reset CW + v3 backbone)로 올랐다. ### v6 → v7/v8 (2026-04-07) `v6`는 OOT binary gate(5-step)와 deployment gate의 WFV gap 이중 페널티 문제가 있었다. `v7`는 두 가지를 동시에 고쳤다: 1. OOT binary gate → continuous factor `[0.75, 1.00]` 2. deployment gate에서 WFV gap 기준 제거 (이미 `_gap_penalty`에서 반영) `v8`은 `v7` backbone + reset common-window blend: ```text final_v8 = v7_score * 0.80 + reset_common_window_score * 0.20 # reset CW 있을 때 final_v8 = v7_score * 0.80 + common_window_score * 0.20 # fallback: 10k CW final_v8 = v7_score # fallback: CW 없음 ``` multi-capital common-window summary는 계속 저장한다. 다만 이건 `default rank`가 아니라 `capital scalability` 진단용으로 본다. ### validation stack 중 하나라도 없으면 public `SQS`는 `None` 아래 세 개 중 하나라도 빠지면 public `SQS`는 계산되지 않는다. - walk-forward - main robustness - out-of-time robustness 이 경우 함수는 숫자 대신 `None`을 주고, breakdown에 missing reason을 넣는다. 예: ```python score, breakdown, source = compute_public_sqs(...) # score == None # source == "pending_validation" # breakdown == {"requires_out_of_time_robustness": 1.0} ``` ## 3. 입력 산출물 경로 표준 경로는 아래다. ### Walk-Forward [`runs/_wfv/walk_forward/walk_forward_summary.json`](/Users/yirugi/mycloud/personal/workspace/fithia2/runs) ### Main Robustness [`runs/_rm/robustness_matrix/robustness_matrix_summary.json`](/Users/yirugi/mycloud/personal/workspace/fithia2/runs) ### OOT Robustness [`runs/_oot_rm/robustness_matrix/robustness_matrix_summary.json`](/Users/yirugi/mycloud/personal/workspace/fithia2/runs) journal에 attach된 경우엔 file path가 없어도 journal entry만으로 rebuild가 가능하다. 다만 디버그와 재현을 위해서는 run file도 남아 있는 쪽이 낫다. ## 4. 실제 계산 순서 public `SQS v8`은 아래 순서로 계산된다. ### 4.1 RQS 계산 split 3개에서 [`compute_rqs`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L691) 를 계산한다. ### 4.2 WFV 기반 `WFQS v2` walk-forward summary에서 [`compute_wfqs_v2`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L979) 를 계산한다. WFQS v2는 test fold 성과를 기반으로 한 base score에 4개의 multiplicative penalty를 곱한다: - **overfitting_penalty** (`_overfitting_penalty`): win rate gap 기반. train/test 간 per-trade 정확도 차이 탐지. - 0~3pp: 1.0 (정상 노이즈), 3~10pp: 1.0→0.85, 10~20pp: 0.85→0.60, >20pp: 0.5 - raw return gap 비교는 train/test 기간 차이(e.g. 504d vs 63d) 때문에 과적합이 없어도 큰 gap이 생기므로 사용하지 않는다 - 구 journal 데이터에 win rate 없으면 기존 `_gap_penalty`로 fallback - **fold_variance_penalty**: test fold 수익률 CV (변동계수) 기반. 특정 시기에만 작동하는 전략 탐지. - **trade_credibility**: fold당 거래 수 + win rate 이상값 탐지. - **engine_reliability**: engine 신뢰도 비율. ### 4.3 Deployment gate를 반영한 base score 코드 그대로 쓰면 (v7/v8 기준): ```text base_score = (RQS * 0.45 + WFQS_v2 * 0.55) * deployment_gate ``` deployment gate는 아래 **3개** 통과 개수로 정해진다 (v7부터 WFV gap 기준 제거). - WFV positive fold rate >= 70% - WFV median return >= 5% - WFV worst return >= -5% 통과 개수별 factor: - 3개: `1.00` - 2개: `0.85` - 1개: `0.65` - 0개: `0.40` > **v6 이하**에서는 4번째 기준(`WFV mean train-test gap <= 35%`)이 있어 5-step > `{4:1.00, 3:0.85, 2:0.65, 1:0.40, 0:0.20}`이었다. v7부터 gap은 `_gap_penalty`에서 > WFQS_v2에 이미 연속 반영되므로 deployment gate에서 제거. 구현: [`_compute_public_sqs_components_v2`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1128) ### 4.4 Main robustness gate [`compute_robustness_gate`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1860) 를 곱한다. 기준: - overall positive window rate >= 65% - 63d median >= 3% - 252d median >= 8% - overall worst return >= -12% factor는 `1.00 / 0.85 / 0.65 / 0.40 / 0.20` 구조 (4-criterion 그대로). ### 4.5 OOT robustness factor (v7부터 continuous) [`compute_oot_factor_v2`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L2084) 를 곱한다. v7/v8에서는 binary gate(5-step) 대신 continuous factor `[0.75, 1.00]`: ```text oot_factor = 0.75 + 0.25 * (oot_quality / 100.0) ``` - `oot_quality`는 기존 `compute_oot_robustness_quality` 재활용 (COVID 조정 정규화 범위) - 최소 0.75 (최악 OOT에서도 25% 감점) ← 기존 최대 80% 감점 대비 OOT quality 정규화 범위: | 지표 | 낮음 (0점) | 높음 (100점) | |------|-----------|-------------| | 252d median | -15% | +10% | | 63d median | -8% | +3% | | positive_rate 63d+ | 20% | 70% | | worst_return | -30% | -5% | 예외: OOT summary 전 horizon에서 `all-zero sparse no-trade` 패턴이면 `factor = 1.0` 중립 처리 (stress에서 거래하지 않은 것 = 전략 설계 의도). > **v6 이하**에서는 `compute_oot_robustness_gate` > (L[1945](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L1945))의 > binary 4-criterion gate를 사용했다. v7부터 이 함수는 하위호환 유지용으로만 남음. ### 4.6 OOT quality — v7/v8에서 factor로 사용 [`compute_oot_robustness_quality`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L2033) 로 `0~100` quality를 계산한다. v7/v8에서는 이 값이 **OOT factor**에 비례 반영된다 (위 4.5 참조). `SQS v3`/`v4`/`v5`/`v6`에서는 랭킹 점수에 곱하지 않고 registry breakdown과 `stress_sqs_score`에서만 사용했었다. ### 4.7 Activity factor 마지막으로 [`_public_activity_factor`](/Users/yirugi/mycloud/personal/workspace/fithia2/libs/backtest/tracker.py#L940) 를 곱한다. 이 단계 때문에: - replacement 전략 - 지나치게 trade 수가 적은 rotation 전략 은 split/WFV가 좋아도 public 점수가 낮아질 수 있다. ### 4.8 Common-window blend (v8 우선순위) v8는 아래 우선순위로 CW blend를 적용한다: **1순위: reset common-window (`v8_continuous_oot+reset_common_window`)** comparable reset CW summary가 있으면: ```text final_v8 = v7_score * 0.80 + reset_common_window_score * 0.20 ``` **2순위: 10k common-window (`v8_continuous_oot+common_window`)** reset CW 없고 comparable 10k CW summary가 있으면: ```text final_v8 = v7_score * 0.80 + common_window_score * 0.20 ``` **fallback: bare v7 (`v8_fallback_v7`)** CW summary 없으면 `v7_score` 그대로. 개별 `common_window_score`는 같은 연속 기간, 같은 초기 자본으로 돌린 run의: - total return - profit factor - Sharpe - max drawdown - return on gross exposure - capital velocity 를 합성한 값이다. ### 4.9 Multi-capital common-window diagnostics `10k/25k/100k` common-window summary가 모두 comparable이면 multi-capital score도 계산해 registry에 저장한다. multi-capital score는: - `10k` 점수 `60%` - `25k` 점수 `25%` - `100k` 점수 `15%` - 세 bucket 중 최저 점수를 floor로 `10%` blend 구조다. 이 값은 현재 **기본 leaderboard rank**가 아니라, 자본 규모 민감도와 scalability를 보는 보조 진단이다. ## 5. `return_max_long_v6new.29` / `v6new.28` 같은 사례를 어떻게 읽어야 하나 `v6new.29`는 split/WFV/deployment도 강하고, `10k common-window` capital-growth가 특히 강한 전형적인 케이스다. - `SQS v4`에서는 이 전략이 더 높은 순위를 받는다. - `stress_sqs_score`는 여전히 낮아서 stress 취약성은 숨겨지지 않는다. `v6new.28`은 다른 방향의 예시다. ### 5.1 왜 헷갈렸나 journal에는 아래 값이 이미 있었다. - `sqs_v2_score = 90.7` - `rqs_score = 83.5` - `wfqs_v2_score = 48.6` 그런데 final public `SQS`는 비어 있었다. 이유는 간단했다. - walk-forward 있음 - main robustness 있음 - OOT robustness 없음 즉 tracker 기준으로는 아직 `pending_validation`이었다. 실제로: ```python score, breakdown, source = compute_public_sqs(...) ``` 결과는: - `score = None` - `source = "pending_validation"` - `breakdown = {"requires_out_of_time_robustness": 1.0}` 였다. ### 5.2 OOT를 붙인 뒤 최종 계산 OOT run: - [robustness_matrix_summary.json](/Users/yirugi/mycloud/personal/workspace/fithia2/runs/return_max_long_v6new.28_oot_rm/robustness_matrix/robustness_matrix_summary.json) OOT 주요 수치: - overall positive `90.0%` - overall worst `-4.59%` - `63d median 11.8%` - `252d median 72.07%` 최종 계산 결과: - `RQS = 83.5` - `WFQS v2 = 48.6` - public `SQS = 54.7` - source = `v2_deployment+robustness+oot` 이 케이스에서는: - deployment gate = `0.85` - main robustness gate = `1.0` - OOT gate = `1.0` - OOT quality factor = `1.0` - activity factor = `1.0` 라서 실질적으로: ```text base_score = (83.5 * 0.45 + 48.6 * 0.55) * 0.85 = 54.7 final_public_sqs = 54.7 ``` 즉 `90.7`이 틀린 값이 아니라, **그 값은 public `SQS`가 아니라 split-only `sqs_v2_score`였다**가 정확한 설명이다. ## 6. 권장 계산 절차 ### 6.1 validation run 만들기 ```bash python apps/backtester/run.py \ --manifest configs/experiments/.json \ --snapshot-dir data/datasets/snapshots \ --walk-forward \ --wf-train-days 252 \ --wf-test-days 63 \ --wf-step-days 63 \ --output-root runs/_wfv python apps/backtester/run.py \ --manifest configs/experiments/.json \ --snapshot-dir data/datasets/snapshots \ --robustness-matrix \ --rm-horizons 21,63,126,252,504 \ --rm-step-days 21 \ --output-root runs/_rm python apps/backtester/run.py \ --manifest configs/experiments/.json \ --snapshot-dir data/datasets/snapshots \ --snapshot-id \ --robustness-matrix \ --rm-horizons 21,63,126,252 \ --rm-step-days 21 \ --output-root runs/_oot_rm ``` 기본 ``는 [`midlarge-liquid-long-v1-oot-2020-2021`](/Users/yirugi/mycloud/personal/workspace/fithia2/data/datasets/snapshots/midlarge-liquid-long-v1-oot-2020-2021/manifest.json)이다. 다만 `pre_event_hurst_60d`, `pre_event_entropy_60d`, `pre_event_market_temperature` 같은 `tier2/tier3` feature를 실제 선택 규칙에 쓰는 전략은 [`midlarge-liquid-long-v1-oot-2020-2021_tier3`](/Users/yirugi/mycloud/personal/workspace/fithia2/data/parquet/midlarge-liquid-long-v1-oot-2020-2021_tier3/manifest.json) 처럼 feature-matched OOT snapshot을 써야 한다. ### 6.2 journal에 attach ```bash python apps/tracker/cli.py attach-wfv \ --journal-dir journal \ \ --summary runs/_wfv/walk_forward/walk_forward_summary.json python apps/tracker/cli.py attach-robustness \ --journal-dir journal \ \ --summary runs/_rm/robustness_matrix/robustness_matrix_summary.json python apps/tracker/cli.py attach-oot-robustness \ --journal-dir journal \ \ --summary runs/_oot_rm/robustness_matrix/robustness_matrix_summary.json ``` ### 6.3 Python으로 직접 계산 ```python import json from pathlib import Path from libs.backtest.domain import SplitResult, WalkForwardSummary, RobustnessMatrixSummary from libs.backtest.tracker import compute_public_sqs entry = ... # journal entry dict train = SplitResult(**entry["results"]["train"]) valid = SplitResult(**entry["results"]["valid"]) test = SplitResult(**entry["results"]["test"]) wfv = WalkForwardSummary(**entry["walk_forward_summary"]) rm = RobustnessMatrixSummary(**entry["robustness_matrix_summary"]) oot = RobustnessMatrixSummary(**entry["out_of_time_robustness_summary"]) score, breakdown, source = compute_public_sqs( train, valid, test, walk_forward_summary=wfv, robustness_matrix_summary=rm, out_of_time_robustness_summary=oot, ) print(score, source, breakdown) ``` ### 6.4 Common-window attach common-window는 같은 기간과 같은 초기 자본으로 맞춰서 별도로 붙인다. 권장 원칙: - contenders끼리는 같은 `start_date / end_date`를 쓴다 - initial equity도 같게 맞춘다 - 현재 official `v8` 기준값은 `10,000`이다 - partial backfill 상태에선 `v8`와 `v7 fallback`이 섞일 수 있음을 인지한다 attach는 tracker CLI 또는 Python helper로 한다. 요약만 붙어 있으면 registry rebuild가 가능하다. ## 7. 디버그 체크리스트 ### score가 `None`이면 아래를 먼저 본다. - `walk_forward_summary` 존재 여부 - `robustness_matrix_summary` 존재 여부 - `out_of_time_robustness_summary` 존재 여부 ### `sqs_v2_score`는 큰데 public `SQS`가 낮으면 아래를 본다. - `wfqs_v2_score` (WFV gap이 크면 `_gap_penalty`로 크게 감점) - deployment gate (3-criterion: positive_rate, median, worst) - robustness gate - OOT factor (breakdown의 `oot_quality` 값 확인; v7/v8에서 0.75~1.00) - activity factor - common-window summary 존재 여부와 comparable window 설정 특히 rotation / low-activity 전략은 activity factor 때문에 public 점수가 크게 깎일 수 있다. 반대로 연속 구간 복리 수익이 강한 전략은 reset common-window가 붙으면 더 올라갈 수 있다. ### registry와 수동 계산이 다르면 아래를 본다. - journal entry에 attach가 실제로 되었는가 - registry가 rebuild 되었는가 (`rsp` 또는 `python -m apps.tracker.cli leaderboard`) - 같은 experiment 이름의 중복 journal entry 중 최신 exact match가 선택되었는가 - common-window가 일부 전략에만 backfill된 partial 상태가 아닌가 - `sqs_score` 필드가 최신 v8 기준인지 확인 (이전 v6 기준 registry 캐시가 남아 있을 수 있음) ### run 숫자가 leaderboard/journal과 다르면 점수 계산식보다 먼저 snapshot provenance를 본다. - run dir의 [`snapshot_manifest.json`](/Users/yirugi/mycloud/personal/workspace/fithia2/runs) - run dir의 [`snapshot_fingerprint.json`](/Users/yirugi/mycloud/personal/workspace/fithia2/runs) - `snapshot_manifest.json`의 `export_enrichments`가 다르면 같은 snapshot id 계열이라도 직접 비교 대상으로 섞지 않는다. 여기서 특히 아래를 본다. - `dataset_snapshot_id` - `source_manifest_path` - `snapshot_dir_name` - `manifest_snapshot_id` - `snapshot_id_matches_manifest` - `output_dir_matches_manifest` 둘 중 하나라도 `false`면, 같은 snapshot id 아래 다른 내용이 덮어써졌거나 잘못 복사된 manifest일 수 있다. 이 경우 점수 비교 전에 snapshot provenance부터 정리해야 한다. ## 8. 운영 원칙 - split-only `sqs_v2_score`를 final public `SQS`로 말하지 않는다. - leaderboard 숫자는 반드시 `compute_public_sqs()` 경로로만 확인한다. - WFV/robustness/OOT 또는 scenario-test가 없는 후보는 “점수 미계산”이지 “점수 0”이 아니다. - journal과 registry가 다르면 journal + tracker 함수가 source of truth다. - `v9` 비교는 comparable common-window가 붙은 후보끼리 우선 본다. - scenario-test RRS가 없으면 OOT quality로 fallback, 없으면 50.0 중립. - deployment gate는 WFV gap을 포함하지 않는다 (이미 WFQS_v2 `_gap_penalty`에서 반영). - **SQS 점수는 날짜 간 비교 불가** — Yahoo Finance adjusted close 비결정성으로 ±15pp 변동. 같은 날 계산된 점수끼리만 상대 비교 신뢰. - `overfit-check` 점수는 SQS에 포함되지 않으며, 별도 필드(`overfit_check_score`)로 관리한다.