Skip to content

Commit 7289562

Browse files
authored
target-weight 일일 운용 요약 추가
target-weight 후보의 일일 paper 운용 판단과 evidence 진행률 확인 흐름을 보강합니다.
2 parents db3a7a9 + 51c10fa commit 7289562

6 files changed

Lines changed: 466 additions & 2 deletions

File tree

.github/workflows/safety-regression.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
3737
- name: Compile safety modules
3838
run: |
39-
python -m py_compile core/order_executor.py core/paper_preflight.py core/scheduler.py core/live_gate.py
39+
python -m py_compile core/order_executor.py core/paper_preflight.py core/scheduler.py core/live_gate.py core/target_weight_rotation.py tools/target_weight_rotation_pilot.py tools/paper_pilot_control.py
4040
4141
- name: Run safety regression tests
4242
env:
@@ -50,6 +50,8 @@ jobs:
5050
tests/test_paper_runtime.py \
5151
tests/test_paper_preflight.py \
5252
tests/test_paper_pilot.py::TestPilotSchedulerIntegration \
53+
tests/test_target_weight_rotation.py \
54+
tests/test_target_weight_paper_adapter.py \
5355
tests/test_live_gate.py \
5456
tests/test_paper_evidence.py \
5557
tests/test_promotion_engine.py \

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ Paper Evidence 체계 — `core/paper_evidence.py` 일별 22개 지표 자동
223223
현재 scoring은 관찰용 paper_only로 강등되었습니다. 신규 우선 실험은 risk-adjusted 기준을 통과한 후보만 대상으로 하며, Paper Evidence 체계로 승격/강등 근거를 자동 수집합니다.
224224
Paper 운영 도구: `tools/run_paper_evidence_pipeline.py` (backfill/finalize/package), `tools/paper_preflight.py`, `tools/paper_launch_readiness.py`, `tools/paper_pilot_control.py`, `tools/research_candidate_sweep.py`, `tools/target_weight_rotation_pilot.py`. target-weight 후보는 capped pilot 승인 전 `tools/target_weight_rotation_pilot.py --record-shadow-evidence`, `--shadow-days 3`, 또는 `--shadow-start-date YYYY-MM-DD --shadow-end-date YYYY-MM-DD`로 dry-run plan artifact, shadow readiness evidence, launch readiness artifact, plan 기반 cap 추천과 enable 명령이 포함된 pilot runbook을 먼저 누적합니다. 이후 `--readiness-audit`로 주문 제출이나 shadow/pilot evidence 기록 없이 clean shadow, launch readiness, active pilot caps, 중복 세션, 실행 전 포지션 드리프트, 추천 cap 충족 여부, 최근 평균 거래대금 대비 주문 비율, 비용 반영 후 현금/투자비중 한도를 JSON artifact와 Markdown 운영 리포트로 점검합니다. 유동성 preflight는 기본적으로 주문별 20일 평균 거래대금의 5% 초과를 차단하며 `--max-order-adv-pct`로 조정할 수 있습니다. pre-trade risk 검증은 주문별 수수료/세금/동적 슬리피지를 반영한 예상 체결가로 현금 부족, 최소 현금비중, 총투자비중, 종목별 비중, 최대 보유 종목 수 위반을 실행 전에 차단합니다. Markdown 리포트에는 shadow 수집, audit 재실행, 추천 cap 승인, capped paper 실행 명령이 함께 기록됩니다. `--shadow-days N`은 휴장/데이터 공백으로 같은 거래일에 매핑되는 경우 과거 평일을 추가 스캔해 N개 고유 resolved trade_day 충족을 목표로 하며, 목표 미달이나 날짜별 실패가 있으면 CLI가 non-zero로 종료해 자동화가 불완전한 증거를 성공으로 처리하지 못하게 합니다. 실행형 pilot evidence는 같은 candidate/trade_day의 기존 pilot session artifact가 없고, 주문 제출 직전 실제 paper position이 계획 입력 장부 `position_quantities_before`와 일치하고, 유동성 preflight와 pre-trade risk 검증을 통과하고, 모든 계획 주문이 성공하며, 성공 주문 결과와 당일 `TradeHistory` fill 집계의 종목/방향/수량이 계획과 일치하고, 실행 후 실제 paper position 전체가 리밸런싱 후 `target_quantities_after` 장부와 일치하고 계획 밖 양수 포지션이 없을 때만 `pilot_paper`로 수집됩니다. 같은 날짜 evidence가 이미 있으면 기존 canonical record도 `pilot_paper`/authorized/target-weight complete/liquidity-complete/pre-trade-risk-complete/fill-complete 조건을 통과해야 재사용합니다. 중복 실행은 기본 차단하며 운영자가 명시적으로 재시도해야 할 때만 `--allow-rerun`을 사용합니다. 부분 실행/중단/중복 실행/주문 결과 불일치/체결 기록 불일치/기존 evidence 검증 실패/실행 전 포지션 드리프트/유동성 preflight 실패/pre-trade risk 실패/실행 후 포지션 불일치는 execution-backed 승격 증거에서 제외됩니다.
225225

226-
Target-weight 60영업일 pilot manifest: `--readiness-audit`는 운영 리포트와 함께 `target_weight_paper_experiment_manifest_*.json`을 생성해 후보 snapshot, 추천 cap, 차단 사유, 실행 명령, 승격 증거 조건을 고정합니다. 공식 `reports/paper_experiment_manifest.json`도 기존 scoring 60영업일 실험과 target-weight capped paper pilot을 분리해 기록합니다.
226+
Target-weight 60영업일 pilot manifest: `--readiness-audit`는 운영 리포트와 함께 `target_weight_paper_experiment_manifest_*.json`을 생성해 후보 snapshot, 추천 cap, 차단 사유, 실행 명령, 승격 증거 조건을 고정합니다. `--daily-ops-summary`는 readiness, risk check, verified pilot day 진행률, 다음 실행 명령을 `target_weight_daily_ops_summary_*.json/.md`로 묶어 매일 운영 판단을 한 번에 확인하게 합니다. 공식 `reports/paper_experiment_manifest.json`도 기존 scoring 60영업일 실험과 target-weight capped paper pilot을 분리해 기록합니다.
227227

228228
Generic paper entry도 동일한 실행 경계 원칙을 따른다. `main.py --mode paper`, scheduler auto-entry, fixed-quantity paper BUY 모두 preflight status와 runtime state를 주문 생성 전에 확인하고, 확인 실패는 fail-closed로 차단한다. blocked runtime에서 pilot authorization이 활성화되어도 `check_pilot_entry()`를 다시 통과해야 하며, SELL/exit 경로는 포지션 정리를 위해 계속 허용한다. pilot evidence freshness는 달력일이 아니라 주말과 한국장 휴장일을 제외한 영업일 기준으로 계산합니다.
229229

docs/PROJECT_GUIDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ full paper 신규 BUY는 preflight status artifact와 runtime state가 모두
713713
|**target-weight shadow proof 추가** | dry-run에서 `--record-shadow-evidence`를 켜면 `append_shadow_plan_evidence()`가 non-promotable `shadow_bootstrap` record를 남김. `execution_backed=False`, excess=null이라 promotion은 오염하지 않고, launch readiness의 clean final day만 채운다. `--shadow-days 3` 또는 `--shadow-start-date/--shadow-end-date`로 여러 날짜 shadow bootstrap을 한 번에 누적할 수 있고, `--shadow-days N`은 휴장/데이터 공백으로 같은 거래일에 매핑되면 과거 평일을 추가 스캔해 N개 고유 resolved trade_day 충족을 목표로 한다. 목표 미달 또는 날짜별 실패가 있으면 CLI는 non-zero로 종료해 불완전한 증거가 자동화에서 성공으로 처리되지 않게 한다. 같은 실행에서 launch readiness JSON/MD와 pilot runbook을 생성하며 session/batch artifact에는 기본 cap preview, plan 기반 최소/추천 cap, enable 명령, launch artifact 경로를 기록 |
714714
|**target-weight readiness audit 추가** | `tools/target_weight_rotation_pilot.py --readiness-audit`로 주문 제출, shadow evidence 기록, pilot session 저장 없이 capped pilot 직전 상태를 JSON artifact와 Markdown 운영 리포트로 점검. clean shadow/launch readiness, active pilot auth와 cap validation, 추천 cap, 중복 session idempotency, 실행 전 position drift, 유동성 preflight, 비용 반영 pre-trade risk, 다음 조치(`cap approval` 또는 `execute capped paper pilot`)를 함께 판정한다. Markdown 리포트는 shadow 수집/audit 재실행/추천 cap 승인/capped paper 실행 명령을 함께 남기며, cap 승인 준비가 안 된 blocker는 non-zero 종료로 자동화가 진행하지 못하게 한다 |
715715
|**target-weight 60영업일 pilot manifest 추가** | `tools/target_weight_rotation_pilot.py --readiness-audit`가 60영업일 `pilot_paper` 증거 누적 기준, 추천 cap, 차단 사유, 실행 명령을 담은 `target_weight_paper_experiment_manifest_*.json`을 함께 생성한다. `reports/paper_experiment_manifest.json`에도 기존 scoring 실험과 별도 target-weight capped paper pilot 기준을 기록해 운영 흐름을 분리한다 |
716+
|**target-weight daily ops summary 추가** | `tools/target_weight_rotation_pilot.py --daily-ops-summary`가 readiness audit, 60영업일 pilot evidence 진행률, 유동성/pre-trade risk 상태, 다음 운영 명령을 `target_weight_daily_ops_summary_*.json/.md`로 묶어 남긴다. 주문·증거·pilot session을 쓰지 않는 no-order summary라 장전/장마감 운영 확인에 안전하게 사용할 수 있다 |
716717
|**pilot freshness 영업일 기준 보정** | `check_pilot_entry()`와 launch readiness가 evidence stale 여부를 달력일이 아니라 한국장 영업일 기준으로 계산한다. 긴 주말·휴장일 이후 정상적인 paper/pilot 흐름이 불필요하게 차단되지 않도록 `evidence_stale_unit=business_days`를 함께 남긴다 |
717718
|**target-weight pilot enable guard 추가** | `tools/paper_pilot_control.py --enable`이 target-weight 후보를 승인할 때 pilot auth 기록 전에 readiness audit을 재실행하고, 운영자가 요청한 cap이 현재 plan/launch readiness/유동성 preflight/비용 반영 pre-trade risk를 만족할 때만 승인한다. stale plan이나 추천 cap 미충족 상태에서 auth만 먼저 열리는 경로를 차단한다 |
718719
|**target-weight cap validation artifact 추가** | `--execute`가 pilot cap validation에서 막혀도 예외로 조기 종료하지 않고 session JSON artifact에 차단 사유, skipped orders, evidence block reason을 남긴다. 주문/체결/증거 수집/runtime pilot session 저장은 하지 않아 cap 조정 후 재점검할 수 있다 |

reports/daily_ops_checklist.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@
4545
- [ ] MDD 확인: 누적 ___% (15% 접근 시 주의)
4646
- [ ] 손절/익절/트레일링 스탑 발동 건수: __
4747

48+
### Target-weight pilot 전용
49+
50+
- [ ] `python tools/target_weight_rotation_pilot.py --daily-ops-summary` 실행
51+
- [ ] Summary status 확인: BLOCKED / READY_TO_ENABLE_CAPS / READY_TO_EXECUTE
52+
- [ ] Verified pilot days 확인: ____ / 60
53+
- [ ] Invalid execution days가 0인지 확인
54+
- [ ] Liquidity / Pre-trade risk가 PASS인지 확인
55+
- [ ] 다음 명령이 cap 승인인지, capped paper 실행인지 기록
56+
4857
### 기록
4958

5059
```

tests/test_target_weight_paper_adapter.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,146 @@ def test_build_target_weight_experiment_manifest_freezes_pilot_flow():
10041004
assert manifest["no_order_safety"]["orders_submitted"] is False
10051005

10061006

1007+
def test_summarize_target_weight_evidence_progress_counts_verified_days(monkeypatch):
1008+
from tools.target_weight_rotation_pilot import summarize_target_weight_evidence_progress
1009+
1010+
strategy = "target_weight_candidate"
1011+
params_hash = "hash"
1012+
records = [
1013+
{
1014+
"date": "2026-04-08",
1015+
"strategy": strategy,
1016+
"execution_backed": False,
1017+
"evidence_mode": "shadow_bootstrap",
1018+
"session_mode": "shadow_bootstrap",
1019+
},
1020+
{
1021+
"date": "2026-04-09",
1022+
"strategy": strategy,
1023+
"execution_backed": True,
1024+
"evidence_mode": "pilot_paper",
1025+
"session_mode": "pilot_paper",
1026+
"pilot_authorized": True,
1027+
"pilot_caps_snapshot": {
1028+
"target_weight_plan": {
1029+
"candidate_id": strategy,
1030+
"trade_day": "2026-04-09",
1031+
"params_hash": params_hash,
1032+
},
1033+
"target_weight_execution": {
1034+
"complete": True,
1035+
"params_hash": params_hash,
1036+
"liquidity_complete": True,
1037+
"pre_trade_risk_complete": True,
1038+
"order_result_complete": True,
1039+
"fill_complete": True,
1040+
"position_reconciliation": {"complete": True},
1041+
},
1042+
},
1043+
},
1044+
{
1045+
"date": "2026-04-10",
1046+
"strategy": strategy,
1047+
"execution_backed": True,
1048+
"evidence_mode": "pilot_paper",
1049+
"session_mode": "pilot_paper",
1050+
"pilot_authorized": True,
1051+
"pilot_caps_snapshot": {
1052+
"target_weight_plan": {
1053+
"candidate_id": strategy,
1054+
"trade_day": "2026-04-10",
1055+
"params_hash": params_hash,
1056+
},
1057+
},
1058+
},
1059+
]
1060+
monkeypatch.setattr(
1061+
"core.paper_evidence.get_canonical_records",
1062+
lambda candidate_id: records if candidate_id == strategy else [],
1063+
)
1064+
1065+
progress = summarize_target_weight_evidence_progress(strategy)
1066+
1067+
assert progress["verified_pilot_days"] == 1
1068+
assert progress["remaining_pilot_days"] == 59
1069+
assert progress["shadow_days"] == 1
1070+
assert progress["invalid_execution_days"] == 1
1071+
assert progress["invalid_reasons"]["missing_target_weight_execution"] == 1
1072+
assert progress["latest_verified_pilot_date"] == "2026-04-09"
1073+
1074+
1075+
def test_build_target_weight_daily_ops_summary_writes_operator_view(tmp_path):
1076+
from tools.target_weight_rotation_pilot import (
1077+
build_target_weight_daily_ops_summary,
1078+
build_target_weight_experiment_manifest,
1079+
recommend_pilot_caps,
1080+
write_target_weight_daily_ops_summary,
1081+
)
1082+
1083+
plan = _adapter_plan()
1084+
cap_recommendation = recommend_pilot_caps(plan)
1085+
audit = {
1086+
"candidate_id": plan.candidate_id,
1087+
"trade_day": plan.trade_day,
1088+
"ready_for_cap_approval": True,
1089+
"ready_for_capped_pilot": False,
1090+
"next_action": "enable pilot with suggested caps, then rerun readiness audit",
1091+
"blocking_reasons": ["pilot_authorization: no active capped pilot authorization"],
1092+
"warning_reasons": ["preview_caps: current preview blocked"],
1093+
"operator_commands": {
1094+
"collect_shadow_days": "python tools/target_weight_rotation_pilot.py --shadow-days 3",
1095+
"rerun_readiness_audit": "python tools/target_weight_rotation_pilot.py --readiness-audit",
1096+
"enable_suggested_caps": cap_recommendation["enable_command"],
1097+
"execute_capped_paper": "python tools/target_weight_rotation_pilot.py --execute --collect-evidence",
1098+
},
1099+
"plan_summary": {
1100+
"order_count": 3,
1101+
"target_position_count": 3,
1102+
"max_order_notional": 1_200_000.0,
1103+
"gross_exposure_after": 3_200_000.0,
1104+
},
1105+
"liquidity_check": {"complete": True, "reason": "target_weight_liquidity_preflight_passed"},
1106+
"pre_trade_risk_check": {"complete": True, "reason": "target_weight_pre_trade_risk_passed"},
1107+
}
1108+
manifest = build_target_weight_experiment_manifest(
1109+
plan=plan,
1110+
cap_recommendation=cap_recommendation,
1111+
readiness_audit=audit,
1112+
)
1113+
progress = {
1114+
"candidate_id": plan.candidate_id,
1115+
"target_days": 60,
1116+
"verified_pilot_days": 12,
1117+
"remaining_pilot_days": 48,
1118+
"progress_ratio": 0.2,
1119+
"shadow_days": 3,
1120+
"invalid_execution_days": 0,
1121+
"invalid_reasons": {},
1122+
"non_promotable_days": 0,
1123+
"total_canonical_records": 15,
1124+
"latest_record_date": "2026-04-10",
1125+
"latest_verified_pilot_date": "2026-04-10",
1126+
"latest_shadow_date": "2026-04-03",
1127+
"ready_for_promotion_day_count": False,
1128+
}
1129+
1130+
summary = build_target_weight_daily_ops_summary(
1131+
audit=audit,
1132+
experiment_manifest=manifest,
1133+
evidence_progress=progress,
1134+
)
1135+
json_path, md_path = write_target_weight_daily_ops_summary(summary, output_dir=tmp_path)
1136+
payload = json.loads(json_path.read_text(encoding="utf-8"))
1137+
report = md_path.read_text(encoding="utf-8")
1138+
1139+
assert summary["status"] == "READY_TO_ENABLE_CAPS"
1140+
assert len(summary["summary_hash"]) == 64
1141+
assert payload["evidence_progress"]["remaining_pilot_days"] == 48
1142+
assert payload["no_order_safety"]["summary_only"] is True
1143+
assert "Verified pilot days: 12/60" in report
1144+
assert "READY_TO_ENABLE_CAPS" in report
1145+
1146+
10071147
def test_assess_plan_liquidity_blocks_large_adv_order():
10081148
from tools.target_weight_rotation_pilot import assess_plan_liquidity
10091149

0 commit comments

Comments
 (0)