15 KiB
Strategy Research Workflow And Handoff
이 문서는 return-max long 전략 연구를 여러 AI 에이전트가 이어서 하더라도
같은 실수를 반복하지 않도록 하기 위한 운영 기준이다.
가장 중요한 배경은 과거 v797_v793_epam_rklb_alk_combo 누락 사례와,
그 뒤에 드러난 disabled named micro contamination, repaired OOT snapshot 문제다.
이 문서는 그런 실수와 재오염을 다시 만들지 않기 위한 운영 규칙을 정의한다.
1. Source Of Truth
전략 연구의 source of truth는 아래 순서로 본다.
원칙:
runs/는 실험 산출물 저장소다. 단독으로는 정식 전략 정의가 아니다.journal은 평가 기록이다. 정식 전략 정의를 대신하지 않는다.- 실제로 다시 돌릴 수 있는 전략 정의는 반드시
configs/experiments/*.json에 있어야 한다. - run 비교가 안 맞으면
runs/<run_id>/snapshot_manifest.json,snapshot_fingerprint.json을 먼저 본다. snapshot_manifest.json의export_enrichments가 다르면 같은 base snapshot name이라도 다른 dataset으로 본다.
2. 용어 정의
Scratch Candidate
임시 탐색용 manifest.
/tmp/*.json또는 배치용 임시 경로에 둘 수 있다.- 빠른 조합 탐색과 train scan에는 허용한다.
- 이 상태로는 leaderboard/journal의 최종 후보가 될 수 없다.
Official Experiment
정식 manifest가 있고 full-split 결과까지 있는 실험.
필수 조건:
configs/experiments에.json파일이 있다.- 파일명 stem과
experiment_name이 같다. train / valid / testfull split 결과가 있다.- journal entry가 있다.
Deploy Candidate
실전 승격을 검토할 수 있는 실험.
Official Experiment 조건에 더해:
- walk-forward summary가 있다.
- robustness matrix summary가 있다.
- repaired out-of-time robustness summary가 있다.
- public
SQS가 계산돼 있다.
Archived Overfit Branch
다음 조건 중 하나라도 만족하면 기본 workflow에서 archive 대상으로 본다.
- manifest의
strategy_engines[].engine_id중 하나라도exact를 포함 - manifest에 named micro engine이 존재함
enabled: false여도 contamination으로 본다.
- symbol/date-specific pocket을 누적해 funded trade set을 직접 외우는 구조
이 계열은 연구 참고용으로만 남기고, 기본 leaderboard와 실전 후보군에서는 제외한다.
2.1 버전 규칙
- 기존
return_max_long_v326같은 historical 전략은 의미상v0.326으로 취급한다. - 과거 journal/manifest 파일명을 전부 물리적으로 바꾸지는 않는다. 재현성과 참조 무결성 때문이다.
- 새 clean lineage는
v1.1부터 시작한다. - lineage 시작점은
v1.1이지만, 문서 작성 시점의 active clean baseline은return_max_long_v1.51.json이다. - 이후 새 실험은
v1.52,v1.53식으로 올린다.
3. 정식 승격 기준
다음 중 하나에 해당하면 scratch를 official로 승격한다.
- full-split에서 현재 baseline을 이겼다.
- leaderboard에 올릴 가치가 있다.
- 다음 세션에서도 다시 이어서 연구할 가능성이 높다.
- WFV/robustness까지 붙일 대상이다.
즉, "의미 있는 winner"는 반드시 repo manifest로 옮긴다.
4. 금지 규칙
아래는 금지한다.
- scratch manifest 상태로 journal만 기록하는 것
experiment_name과 다른 파일명으로 공식 manifest를 저장하는 것- leaderboard 상위 전략이
configs/experiments에 없는 상태로 남는 것 - WFV/robustness가 붙은 전략의 summary 경로를 journal에 연결하지 않는 것
5. 표준 연구 흐름
현재 active clean baseline은 return_max_long_v1.51.json 이다.
이 전략은 old v1.12 계열에서 disabled named micro를 물리적으로 제거한 clean replacement다.
즉, 지금부터의 baseline 해석은 "계보의 시작"과 "현재 active baseline"을 구분해야 한다.
Step 1. Baseline 선택
- 기준 전략은 반드시
configs/experiments안의 manifest여야 한다. - baseline 이름은 journal entry와 동일하게 사용한다.
- paper backtest와 research backtester는 같은 snapshot을 써야 비교가 된다.
경로 해석 우선순위는
settings.parquet_dir다음data/datasets/snapshots다.
Step 2. Scratch 탐색
/tmp에 scratch manifest를 만들고 배치 탐색을 돌릴 수 있다.- 이 단계에서는 train-only scan이나 fast full-split batch를 허용한다.
- 아직 journal 최종 기록 대상은 아니다.
Step 3. Winner 확정
scratch 후보가 baseline을 이기면 먼저 repo에 정식 manifest를 만든다.
권장 순서:
configs/experiments/<experiment_name>.json생성- 해당 파일로 다시 full split 실행
- 그 뒤에 journal 기록
Step 4. Journal 기록
정식 manifest 기준으로만 기록한다.
필수 확인:
experiment_name == manifest filename stemtrain / valid / test결과가 모두 존재- hypothesis / config_delta / verdict가 비어 있지 않음
Step 5. WFV / Robustness
deploy 후보는 아래를 붙인다.
- walk-forward validation
- robustness matrix
- repaired out-of-time robustness
- 현재 기준 snapshot은
midlarge-liquid-long-v1-oot-2020-2021
- 현재 기준 snapshot은
그리고 tracker에 attach한다.
Step 6. Leaderboard 갱신
정식 manifest + journal + WFV/robustness attach까지 끝난 뒤에 leaderboard를 본다.
public SQS 계산 상세와 디버그 절차는
docs/public_sqs_calculation.md
를 본다.
6. 세션 종료 전 체크리스트
세션을 끝내기 전에 아래를 확인한다.
- 오늘 새 winner가 scratch만 있고 repo manifest가 없는가
- journal에 기록한 실험명이 실제
configs/experiments/<name>.json과 대응하는가 - deploy 후보인데 WFV/robustness attach가 빠진 것이 없는가
- deploy 후보인데 repaired OOT attach가 빠진 것이 없는가
- leaderboard 상단 전략 중 repo manifest가 없는 것이 없는가
- default leaderboard에 named micro manifest가 보이지 않는가
- 새로 만든 run의
snapshot_fingerprint.json에서 manifest mismatch가 없는가
하나라도 yes면 세션 종료 전에 정리한다.
7. 권장 명령 흐름
Full Split
python apps/backtester/run.py \
--manifest configs/experiments/<experiment>.json \
--snapshot-dir data/datasets/snapshots \
--output-root runs/<experiment>_fullsplit
Walk-Forward
python apps/backtester/run.py \
--manifest configs/experiments/<experiment>.json \
--snapshot-dir data/datasets/snapshots \
--walk-forward \
--wf-train-days 504 \
--wf-test-days 63 \
--wf-step-days 63 \
--output-root runs/<experiment>_wfv
Robustness Matrix
python apps/backtester/run.py \
--manifest configs/experiments/<experiment>.json \
--snapshot-dir data/datasets/snapshots \
--robustness-matrix \
--rm-horizons 21,63,126,252,504 \
--rm-step-days 21 \
--output-root runs/<experiment>_rm
Attach Summaries
python apps/tracker/cli.py attach-wfv \
--journal-dir journal \
<experiment_name> \
--summary runs/<experiment>_wfv/walk_forward/walk_forward_summary.json
python apps/tracker/cli.py attach-robustness \
--journal-dir journal \
<experiment_name> \
--summary runs/<experiment>_rm/robustness_matrix/robustness_matrix_summary.json
python apps/tracker/cli.py attach-oot-robustness \
--journal-dir journal \
<experiment_name> \
--summary runs/<experiment>_oot_rm/robustness_matrix/robustness_matrix_summary.json
Retired Overlay Path
book-of-books overlay 실험은 코드베이스에서 제거됐다.
- 공식 leaderboard와 paper backtest는 이제 single-book 전략만 지원한다.
- 과거 overlay journal entry는 재현성 기록으로만 남고, registry/leaderboard 재빌드에는 포함되지 않는다.
- 새 연구는
configs/experiments아래 single-book manifest 기준으로 진행한다.
8. 빠른 무결성 점검
journal entry와 manifest 대응을 확인하려면:
python - <<'PY'
import json
from pathlib import Path
cfg = Path("configs/experiments")
missing = []
for line in Path("journal/improvement_journal.jsonl").read_text().splitlines():
if not line.strip():
continue
obj = json.loads(line)
name = obj.get("experiment_name")
if name and not (cfg / f"{name}.json").exists():
missing.append(name)
print("missing", len(missing))
for name in missing[-20:]:
print(name)
PY
이 출력은 항상 0이어야 한다.
Snapshot Provenance 점검
run 결과가 journal/leaderboard 숫자와 다르면 아래 두 파일을 먼저 본다.
runs/<run_id>/snapshot_manifest.jsonruns/<run_id>/snapshot_fingerprint.json- 특히
snapshot_manifest.json의feature_version,export_enrichments,created_at_utc를 같이 본다.
핵심 필드:
dataset_snapshot_idsource_manifest_pathsnapshot_dir_namemanifest_snapshot_idsnapshot_id_matches_manifestoutput_dir_matches_manifest
snapshot_id_matches_manifest=false 또는 output_dir_matches_manifest=false면
같은 snapshot id 아래 다른 내용이 덮어써졌거나, 잘못 복사된 manifest일 수 있다.
이 상태에서 성능 비교를 계속하면 안 된다.
9. 다른 에이전트에게 넘길 때 남겨야 할 것
handoff에는 최소 아래를 포함한다.
- 현재 active baseline manifest 경로
- 현재 raw-return winner와 deploy winner
- 마지막으로 유효했던 개선 축
- 실패한 축 3~5개
- 아직 scratch 상태인 강한 후보가 있으면 그 manifest 경로
- 진행 중인 WFV / robustness 실행 경로
즉, "무엇이 최고였는가"보다 "무엇이 먹혔고 무엇이 죽었는가"를 남겨야 한다.
10. 운영 교훈
10.1 Disabled Named Micro도 오염이다
manifest에 named micro engine이 남아 있으면 enabled: false여도 clean 전략으로 보지 않는다.
실제로 old v1.10~v1.13은 disabled named micro가 남아 있었고,
이 때문에 현재 clean replacement로 return_max_long_v1.51.json,
return_max_long_v1.52.json 을 분리했다.
10.2 OOT는 최적화 목표가 아니라 품질 계층이다
2020~2021 repaired OOT는 점수를 올리기 위한 최적화 목표가 아니라,
현재 전략이 특정 시기 구조에만 맞는지 확인하는 추가 품질 계층이다.
OOT를 보고 규칙을 직접 맞추기 시작하면 그 순간 또 오염된다.
10.3 Snapshot 결함부터 의심한다
OOT가 전부 0 trade로 보이면 전략 탓만 하지 말고 snapshot 자체를 먼저 점검한다.
실제로 symbol 기반 export에서 market_cap_proxy, exchange_proxy가 비어 있던 버그가 있었고,
이를 고친 뒤에야 repaired OOT가 의미 있는 비교 지표가 됐다.
10.4 Paper Backtest stale 판정은 manifest 나이가 아니라 coverage로 본다
fithia2 paper backtest는 snapshot이 오래됐다는 이유만으로 refresh하면 안 된다.
실제로는 기존 snapshot이 충분한 날짜 범위를 이미 덮고 있을 수 있다.
현재 원칙:
- stale 여부는 parquet
event_datecoverage로 판단한다 - snapshot 경로는
settings.parquet_dir를 먼저 보고, 없으면data/datasets/snapshots를 본다 - refresh가 실패해도 기존 snapshot이 requested period를 덮으면 그대로 사용한다
즉 "manifest created_at이 오래됐다"는 이유만으로 refresh를 강제하면 안 된다.
10.5 Screener 장애 fallback은 local-only여야 한다
snapshot export 중 live screener가 500으로 죽을 수 있다.
이때 조용히 다른 remote endpoint로 넘어가면, 시점마다 다른 값이 섞여 재현성이 깨진다.
현재 원칙:
- universe screener가 실패하면 우선 기존 local snapshot metadata를 deterministic하게 재사용한다
- local fallback조차 없으면 export는 그대로 실패시킨다
- 임의의 remote fallback으로 조용히 성공시키지 않는다
즉 장애 대응보다 재현성을 우선한다.
11. 현재 상태
문서 작성 시점의 active clean 후보는 아래다.
현재 baseline은 v1.51이다.
- full split
+37.9 / +35.3 / +42.7 - WFV mean
+11.10%, gap29.67% - repaired OOT positive window rate
48.7%
최근 실패한 축:
return_max_long_v1.53.json- raw return은 좋아졌지만 WFV gap/OOT가 나빠져 탈락
return_max_long_v1.55.json- test는 좋아졌지만 repaired OOT가 크게 악화
return_max_long_v1.56.json,return_max_long_v1.57.json,return_max_long_v1.58.json- general-only 미세조정이었지만 no-op 또는 후퇴
12. 현재 예외
현재 문서 작성 시점의 active known exception은 없다.
과거 v797_v793_epam_rklb_alk_combo 누락은 이미 역사적 교훈으로만 남기고,
현재 workflow에선 journal ↔ manifest parity와 auto-sync 규칙으로 막는다.
13. 운영 원칙 요약
- scratch는 빠르게, official은 엄격하게 관리한다.
- journal에 적을 정도면 manifest도 repo에 있어야 한다.
- leaderboard 상단 전략은 반드시 재현 가능해야 한다.
- deploy 후보는 full split만으로 끝내지 않고 WFV/robustness/repaired OOT까지 붙인다.
- named micro는 disabled여도 clean 전략에 남겨두지 않는다.
- 세션 종료 전
journal ↔ manifestparity를 확인한다. - snapshot provenance가 틀리면 점수 논쟁보다 먼저 metadata부터 고친다.