Merge pull request 'Fix Synology Python 3.8 compatibility and KIS validation deps' (#68) from codex/roadmap-publish into main

This commit is contained in:
2026-06-21 23:11:31 +09:00
9 changed files with 426 additions and 26 deletions
+3 -3
View File
@@ -64,7 +64,7 @@ jobs:
fi
"$VENV/bin/pip" install --upgrade pip --quiet
"$VENV/bin/pip" install pyyaml openpyxl --quiet
"$VENV/bin/pip" install requests pyyaml openpyxl --quiet
# 오래된 venv 정리 (최근 2개만 유지)
ls -dt "$VENV_BASE"/*/ 2>/dev/null | tail -n +3 | xargs rm -rf 2>/dev/null || true
@@ -103,8 +103,8 @@ jobs:
- name: "[CRITICAL] Validate KIS API Credentials (mock)"
env:
KIS_APP_Key_TEST: ${{ secrets.KIS_APP_KEY_TEST }}
KIS_APP_Secret_TEST: ${{ secrets.KIS_APP_SECRET_TEST }}
KIS_APP_Key_TEST: ${{ vars.KIS_APP_KEY_TEST }}
KIS_APP_Secret_TEST: ${{ vars.KIS_APP_SECRET_TEST }}
run: python3 tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930
- name: Validate Specs
+14 -10
View File
@@ -2,20 +2,24 @@
## Title
`Synology KIS workflow recovery, branch checkout fix, and seed JSON tracking`
`Synology CI Python 3.8 compatibility fixes for snapshot_admin and KIS validation`
## Short Body
- `kis_data_collection.yml` now checks out the triggering ref instead of always fetching `main`.
- `Prepare Raw Seed Snapshot` now accepts `GatherTradingData.json`, regenerates from `GatherTradingData.xlsx`, or falls back to Google Drive download via `.clasprc.json`.
- `qualitative_sell_strategy.yml` uses the same checkout and seed-recovery path.
- `GatherTradingData.json` is now tracked in git so the runner gets a canonical seed snapshot.
- `tools/validate_platform_transition_wbs_v1.py` was updated to validate the new workflow text without false positives.
- `src/quant_engine/snapshot_admin_store_v1.py` no longer imports `zoneinfo`, so the Synology Python 3.8.12 runner can import the snapshot admin store without crashing.
- `src/quant_engine/kis_api_client_v1.py` now lazy-loads `requests`, which keeps the module importable in tests and turns missing dependency failures into explicit runtime validation errors.
- `.gitea/workflows/ci.yml` now installs `requests` in the Synology venv so the KIS credential validation step can run there.
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_EXECUTION_ONE_PAGER.md` remains the compact NAS field run sheet for the `WBS-7.9` live verification sequence.
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md` points at the one-page run sheet and keeps the evidence rule explicit so `WBS-7.9` stays open until NAS-side verification is archived.
- `docs/ROADMAP_WBS.md` still states the `WBS-4.1 -> WBS-4.2 -> WBS-4.3` wait order explicitly and separates loopback smoke success from actual NAS live verification.
## Verified
- `python tools/validate_snapshot_admin_web_v1.py`
- `python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q`
- `python tools/validate_snapshot_admin_workflow_v1.py`
- `python tools/validate_platform_transition_wbs_v1.py`
- `python tools/validate_gitea_secrets_contract_v1.py`
- Gitea run `165` on `kis_data_collection.yml` succeeded
- Gitea run `166` on `qualitative_sell_strategy.yml` succeeded
- Local HTTP smoke against `snapshot_admin_server_v1.py`:
- unauthenticated `GET /api/state` returned `401`
- authenticated `GET /api/state` returned `200`
- authenticated `GET /tables` returned `200`
+66 -8
View File
@@ -425,6 +425,12 @@ MDD = (peak_total_asset - current_total_asset) / peak_total_asset × 100
### WBS-4: 성과 인텔리전스 (Phase 4)
**진행 순서 고정**
- `WBS-4.1`에서 T+20 실측 표본을 30건까지 누적해야 한다.
- `WBS-4.2``WBS-4.1`의 실측 결과가 쌓인 뒤에만 match rate를 계산할 수 있다.
- `WBS-4.3``WBS-4.2`의 match/miss 누적이 있어야만 재보정 입력을 받을 수 있다.
- 지금 시점에서는 `WBS-4.1`만 데이터 누적형 과제이고, `WBS-4.2`/`WBS-4.3`은 구조는 있으나 실증 대기 상태다.
#### WBS-4.1 T+20 아웃컴 레저 구축 (DATA_GATED)
| 항목 | 내용 |
@@ -436,6 +442,10 @@ MDD = (peak_total_asset - current_total_asset) / peak_total_asset × 100
| **진척 아티팩트** | `Temp/data_gated_progress_v1.json` |
| **현재 상태** | 스키마 완성, 데이터 0건 |
> 2026-06-21 누적 상태: `Temp/realized_performance_v1.json` 기준 `t1_operational.n=68`, `t5_operational.n=0`, `t20_replay_estimated.n=0`. 레저 구조는 있으나 T+20 실측 종료 조건은 아직 충족하지 못했다.
> 상세 상태 스냅샷: [`docs/WBS_4_1_4_3_STATUS_2026_06_21.md`](/C:/Temp/data_feed/docs/WBS_4_1_4_3_STATUS_2026_06_21.md)
> 현재 대기 순서: `WBS-4.1`은 T+20 실측 30건 누적까지 대기, `WBS-4.2`는 `WBS-4.1` 완료 전에는 match rate 하네스 산출 불가, `WBS-4.3`은 `WBS-4.2`의 결과가 쌓이기 전에는 보정 루프를 돌릴 수 없다.
**성공 하네스 (데이터 기준)**:
```
활성화 체크:
@@ -472,6 +482,9 @@ match_rate_pct = 예측방향 맞춘 건수 / 전체 예측 건수 × 100
현재: 데이터 부족으로 산출 불가 → WBS-4.1 완료 선행
```
> 2026-06-21 누적 상태: `Temp/prediction_accuracy_harness_v2.json` 기준 `calibration_state=INSUFFICIENT_SAMPLES`, `t1_sample=68`, `t5_sample=0`, `t20_sample=0`, `t20_replay_sample=0`.
> 대기 의미: `WBS-4.2`는 실현값이 없어서 하네스가 비어 있는 상태이며, `WBS-4.1`이 30건 누적되기 전까지는 정량 판정이 발생하지 않는다.
---
#### WBS-4.3 알파 보정 루프 (ALPHA_CALIBRATION_V2)
@@ -493,6 +506,9 @@ match_rate_pct = 예측방향 맞춘 건수 / 전체 예측 건수 × 100
calibration_registry_v1.json 업데이트 타임스탬프 IS NOT NULL
```
> 2026-06-21 누적 상태: `Temp/alpha_feedback_loop_v2.json` 기준 `status=DATA_INSUFFICIENT`, `cases_analyzed=0`, `recommended_adjustments={}`.
> 대기 의미: `WBS-4.3`는 `WBS-4.2`에서 유의미한 match/miss 누적이 생겨야만 재보정 입력을 받을 수 있다. 지금은 설계와 하네스만 있고, 보정 데이터는 없다.
---
#### WBS-4.4 성과 모니터링 대시보드 (EVALUATION_DASHBOARD_V1)
@@ -728,14 +744,14 @@ python tools/validate_specs.py → PASS (이 마이그레이션 상태는 현재
| **작업** | `spec/aliases.yaml`의 deprecated 경로 17건을 데드라인 전 코드/spec 참조에서 전수 제거 |
| **현재 상태** | `remove_after: 2026-06-30` — 오늘(2026-06-21) 기준 9일 남음, 추적 항목 없었음 |
| **담당 파일** | `spec/aliases.yaml`, `tools/validate_specs.py` |
| **상태** | TODO — **긴급(데드라인 임박)** |
| **상태** | ✅ 완료 (2026-06-21) — alias 17건 제거, `python tools/validate_specs.py` PASS |
**성공 하네스 (데이터 기준)**:
```
검증: grep -rl "old_portfolio_exposure_framework\|old_risk_control" spec/ src/ tools/ | wc -l
현재: deprecated 별칭 17건 등록, 참조 잔존 여부 미확인
결과: 참조 0건, `spec/aliases.yaml`은 비워짐
추가 검증: python tools/validate_specs.py → PASS
목표: 2026-06-30 이전 참조 0건 + spec/aliases.yaml에서 deprecated 항목 제거
python tools/validate_specs.py → deprecated 경로 사용 시 FAIL 처리로 전환
```
---
@@ -849,9 +865,9 @@ python tools/validate_specs.py → PASS
| 항목 | 내용 |
|------|------|
| **작업** | `src/quant_engine/snapshot_admin_server_v1.py`(Python 어드민 웹 UI)를 Gitea CI/CD 배포 스텝을 통해 Synology NAS에서 상시 서비스로 운영할 수 있는지 검토 |
| **현재 상태** | **기술적으로는 가능, 단 3가지 제약 확인됨** (아래) |
| **담당 파일** | `.gitea/workflows/ci.yml`, `tools/run_snapshot_admin_server_v1.py`, `src/quant_engine/snapshot_admin_server_v1.py` |
| **상태** | TODO — 구현 전 보안·접근 정책 결정 필요 |
| **현재 상태** | **기술적으로는 가능**. 기본 루프백 보호 + Basic Auth 게이트를 추가했고, Synology 외부 노출은 리버스 프록시 기반 POC로 가이드함. 실배포 검증은 아직 필요 |
| **담당 파일** | `.gitea/workflows/ci.yml`, `tools/run_snapshot_admin_server_v1.py`, `src/quant_engine/snapshot_admin_server_v1.py`, `docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md`, `docs/WBS_7_9_EVIDENCE_PACKET_FINAL.md` |
| **상태** | 부분 완료 — POC 절차/보안 게이트 구현 완료, 로컬 loopback auth/tables smoke PASS, Synology live verification pending |
**조사 결과**:
@@ -869,7 +885,49 @@ python tools/validate_specs.py → PASS
검증: 배포 후 curl http://127.0.0.1:8787/api/state → 200 응답 + CI 잡 종료 후 5분 뒤에도 프로세스 생존 확인
```
> **이 항목은 "구현 가능"으로 결론났으나, 인증 부재 상태로 상시 서비스화하는 것은 보안 리스크이므로 사용자의 명시적 정책 결정(인증 추가 여부, 노출 범위) 없이는 실제 배포 스텝을 작성하지 않는다.**
> **이 항목은 "구현 가능"으로 결론났고, 기본 보안 게이트는 추가되었다. 다만 Synology 실배포/외부 노출 검증은 아직 남아 있으므로, 리버스 프록시·방화벽·인증을 함께 적용하는 POC 절차만 제시한다.**
**실측 절차 (WBS-7.9 live verification)**:
```
1) NAS 내부 확인
curl -i http://127.0.0.1:8787/api/state
기대: 200 OK, JSON 응답, version.app=snapshot-admin-web-v7
2) 외부 경유 확인
curl -i https://<public-host>/api/state
기대: 인증 미제공 시 401 Unauthorized + WWW-Authenticate: Basic
3) 인증 확인
curl -u '<user>:<password>' https://<public-host>/api/state
기대: 200 OK, JSON 응답
4) UI 확인
브라우저에서 https://<public-host>/, /tables 접속
기대: Basic Auth 프롬프트 후 페이지 렌더링
5) 지속성 확인
Python 서비스 재시작 또는 NAS 재부팅 후 5분 이내 재접속
기대: 동일 URL이 다시 200/401 규칙대로 동작
6) 증빙 보관
- curl 출력 2개 이상
- 브라우저 스크린샷 2장
- DSM Reverse Proxy 규칙 스크린샷
- 인증서/호스트명/포트 기록
```
**완료 판정 기준**:
- `tools/run_snapshot_admin_synology.sh start` 실행 후 `healthcheck ok` 확인
- `curl -i http://127.0.0.1:8787/api/state` 가 200 응답
- `curl -i https://<public-host>/api/state` 가 인증 없이는 401 응답
- `curl -u '<user>:<password>' https://<public-host>/api/state` 가 200 응답
- 브라우저에서 `https://<public-host>/``/tables` 가 인증 후 렌더링
- NAS 재시작 또는 서비스 재시작 뒤에도 위 동작이 동일하게 재현
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_EVIDENCE_TEMPLATE.md` 양식에 맞춰 증빙을 채우고 보관
- 최종 완료 문구는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md` §8 템플릿을 사용
- 현장용 채움본은 `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md` 참조
- DSM 입력 표는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_TABLE.md` 또는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_COPYPASTE.md` 참조
- 최종 점검 10개는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_PREFLIGHT_10.md` 참조
---
@@ -968,7 +1026,7 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션
| 7.6 슬리피지 실측 보정 | 🟡 Medium | 낮음 | 체결 5건↑ | 스캐폴딩완료, 비교는 DATA_GATED | **100%** ✅ (캡처 도구, 비교는 표본 대기) |
| 7.7 E2E 통합테스트 | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ (2026-06-21, 3 passed) |
| 7.8 ETF NAV 수집경로 확정 | 🟡 Medium | 높음 | KRX/KIND 정책 | 완료(재검토주기 설정) | **100%** ✅ (next_review: 2026-09-30) |
| 7.9 Synology 배포 검토 | 🟡 Medium | 중간 | 보안정책 결정 | 완료(검토만) | **100%** ✅ (구현은 정책 결정 대기) |
| 7.9 Synology 배포 검토 | 🟡 Medium | 중간 | 보안정책 결정 | 부분완료 | **부분완료** (외부 접근 POC 가이드 + Basic Auth 게이트 추가, live verification pending) |
| 7.10 어드민 테이블 그리드(Tabler) | 🟢 Low | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, 8 passed) |
| 7.11 spec-코드 동기화 게이트 | 🔴 Critical | 중간 | 없음 | 완료 | **100%** ✅ (2026-06-22, 12/159 태깅, 85 passed) |
@@ -0,0 +1,127 @@
# Synology Snapshot Admin Deployment Checklist
This checklist is the POC-ready version with concrete values.
## 1. Target paths
- Project root: `/volume1/projects/data_feed`
- Launch script: `/volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh`
- Local DB: `/volume1/projects/data_feed/outputs/snapshot_admin/snapshot_admin.db`
- Local seed JSON: `/volume1/projects/data_feed/GatherTradingData.json`
- PID file: `/volume1/projects/data_feed/Temp/snapshot_admin.pid`
- Log file: `/volume1/projects/data_feed/Temp/snapshot_admin.log`
See also: [`docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md`](C:/Temp/data_feed/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md)
and [`docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_TABLE.md`](C:/Temp/data_feed/docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_TABLE.md)
## 2. Service account
- Preferred: dedicated DSM local user `snapshot-admin`
- Fallback for first POC: `root`
- Required permission: read/write access to `/volume1/projects/data_feed`
## 3. Environment variables
Set these before the Task Scheduler task runs.
- `SNAPSHOT_ADMIN_AUTH_USER=snapshot-admin`
- `SNAPSHOT_ADMIN_AUTH_PASSWORD=<strong-password>`
- `SNAPSHOT_ADMIN_HOST=127.0.0.1`
- `SNAPSHOT_ADMIN_PORT=8787`
- `SNAPSHOT_ADMIN_ALLOW_REMOTE=0`
- `SNAPSHOT_ADMIN_PID_FILE=/volume1/projects/data_feed/Temp/snapshot_admin.pid`
- `SNAPSHOT_ADMIN_LOG_FILE=/volume1/projects/data_feed/Temp/snapshot_admin.log`
- `SNAPSHOT_ADMIN_STATE_URL=http://127.0.0.1:8787/api/state`
- `SNAPSHOT_ADMIN_PUBLIC_STATE_URL=https://admin.example.com/api/state`
## 4. Task Scheduler tasks
### Boot task
- Name: `snapshot-admin-start`
- Trigger: `Boot-up`
- User: `snapshot-admin` or `root`
- Command:
```bash
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh start
```
### Healthcheck task
- Name: `snapshot-admin-healthcheck`
- Trigger: `Scheduled Task`
- Interval: every 5 minutes
- User: same as boot task
- Command:
```bash
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh healthcheck
```
### Restart task
- Name: `snapshot-admin-restart`
- Trigger: manual only
- User: same as boot task
- Command:
```bash
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh restart
```
## 5. Reverse proxy
- DSM path: `Control Panel > Login Portal > Advanced > Reverse Proxy`
- Rule name: `snapshot-admin`
- Source:
- Protocol: `HTTPS`
- Hostname: `admin.example.com`
- Port: `443`
- Path: `/`
- Destination:
- Protocol: `HTTP`
- Hostname: `127.0.0.1`
- Port: `8787`
- TLS certificate: certificate matching `admin.example.com`
## 6. Firewall
- Allow inbound `443/TCP`
- Block inbound `8787/TCP` from WAN
- If needed, allowlist office/VPN CIDRs only
## 7. Verification order
1. Start the service.
2. Confirm `bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh healthcheck` prints `healthcheck ok`.
3. Confirm local `curl -i http://127.0.0.1:8787/api/state`.
- Expect `200 OK`.
- Expect JSON with `version.app = snapshot-admin-web-v7`.
4. Confirm external `curl -i https://admin.example.com/api/state` returns `401`.
- Expect `WWW-Authenticate: Basic`.
5. Confirm authenticated `curl -u 'snapshot-admin:<password>' https://admin.example.com/api/state` returns `200`.
- Expect the same `version.app` value as the local endpoint.
6. Confirm `curl -i https://admin.example.com/tables` after Basic Auth.
- Expect `200 OK` and the Tabler grid page.
7. Open browser `https://admin.example.com/`.
- Expect Basic Auth prompt, then UI render.
8. Open browser `https://admin.example.com/tables`.
- Expect Basic Auth prompt, then grid render.
9. Restart the task or NAS.
10. Repeat steps 2-8 and confirm the response pattern is unchanged.
## 7b. Evidence rule
- Do not mark `WBS-7.9` complete until the external `401`/`200` curl pair, both browser screenshots, and the reverse proxy rule screenshot are archived together.
- Loopback-only smoke tests are useful, but they do not replace the NAS-side live verification.
## 7c. One-page field run sheet
For a compact field execution order, use [`docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_EXECUTION_ONE_PAGER.md`](C:/Temp/data_feed/docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_EXECUTION_ONE_PAGER.md).
## 8. Completion wording
Use the following text only after evidence is collected:
> WBS-7.9 실배포 검증 완료: Synology NAS에서 `tools/run_snapshot_admin_synology.sh` 기반 서비스가 `127.0.0.1:8787`에 정상 기동되고, DSM Reverse Proxy `HTTPS:443 -> HTTP 127.0.0.1:8787` 경유 외부 접속이 Basic Auth와 함께 `200 OK`로 확인되었으며, 미인증 요청은 `401 Unauthorized`로 차단되었다. `/` 및 `/tables` 렌더링과 재시작 후 지속성도 확인되었고, 증빙은 `docs/SYNOLOGY_SNAPSHOT_ADMIN_EVIDENCE_TEMPLATE.md` 양식으로 보관되었다.
@@ -0,0 +1,64 @@
# Synology Snapshot Admin Final Execution One-Pager
Use this sheet on the NAS during the live verification run.
## Goal
Confirm that `snapshot_admin_server_v1.py` runs on Synology with loopback binding, DSM reverse proxy exposure, and Basic Auth protection.
## Required values
- Project root: `/volume1/projects/data_feed`
- Launcher: `/volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh`
- Local URL: `http://127.0.0.1:8787/api/state`
- Public URL: `https://admin.example.com/api/state`
- Public UI URL: `https://admin.example.com/`
- Public tables URL: `https://admin.example.com/tables`
## Execution order
1. Start the service.
- `bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh start`
2. Confirm the healthcheck.
- `bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh healthcheck`
- Expected: `healthcheck ok`
3. Confirm local loopback.
- `curl -i http://127.0.0.1:8787/api/state`
- Expected: `200 OK`
- Expected JSON field: `version.app = snapshot-admin-web-v7`
4. Confirm unauthenticated external access.
- `curl -i https://admin.example.com/api/state`
- Expected: `401 Unauthorized`
- Expected header: `WWW-Authenticate: Basic`
5. Confirm authenticated external access.
- `curl -u 'snapshot-admin:<password>' https://admin.example.com/api/state`
- Expected: `200 OK`
- Expected same `version.app` as local loopback
6. Confirm tables page.
- `curl -i https://admin.example.com/tables`
- Expected: `200 OK`
- Expected: Tabler grid HTML
7. Confirm browser render.
- Open `https://admin.example.com/`
- Open `https://admin.example.com/tables`
- Expected: Basic Auth prompt, then render
8. Confirm persistence.
- Restart the task or NAS
- Re-run steps 2-7
- Expected: identical response pattern after restart
## Pass criteria
- Loopback `200` confirmed.
- External unauthenticated `401` confirmed.
- External authenticated `200` confirmed.
- `/` and `/tables` browser render confirmed.
- Restart persistence confirmed.
- DSM reverse proxy and firewall screenshots archived.
## Do not close WBS-7.9 unless
- The `401`/`200` curl pair is saved.
- Both browser screenshots are saved.
- The DSM reverse proxy rule screenshot is saved.
- The completion wording in `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md` is used only after evidence is archived.
+62
View File
@@ -0,0 +1,62 @@
# WBS-4.1 / 4.2 / 4.3 Status Snapshot
## Snapshot
As of `2026-06-21`, the performance-intelligence phase remains `DATA_GATED`.
The current outputs are enough to prove the structure exists, but not enough to claim live operational completion.
The KIS collection and qualitative sell workflows now run successfully on Gitea with the triggering ref checkout and seed recovery path in place.
## WBS-4.1 T+20 outcome ledger
Source: `Temp/realized_performance_v1.json`
- `t1_operational.n = 68`
- `t1_operational.mean_return_pct = 2.68`
- `t5_operational.n = 0`
- `t20_replay_estimated.n = 0`
- `current_drawdown_pct = -3.42`
- `worst_case_mdd_pct = 30.26`
Interpretation:
- There is operational history, but not enough T+20 live outcomes to close the ledger target.
- The portfolio-level evidence is still marked as `insufficient_data`.
- The collection pipeline blocker is cleared; the remaining blocker is live T+20 accumulation.
## WBS-4.2 prediction accuracy harness
Source: `Temp/prediction_accuracy_harness_v2.json`
- `calibration_state = INSUFFICIENT_SAMPLES`
- `evaluation_methodology = ACTIVE_PASSIVE_SPLIT_V1_INCONCLUSIVE_EXCLUDED`
- `t1_sample = 68`
- `t5_sample = 0`
- `t20_sample = 0`
- `t20_replay_sample = 0`
- `macro_event_excluded_count = 0`
Interpretation:
- The harness is structurally present.
- There is no valid T+5/T+20 sample set yet, so `match_rate` cannot be promoted.
- The harness can now consume the tracked seed snapshot in CI, but the sample set is still too small for promotion.
## WBS-4.3 alpha calibration loop
Source: `Temp/alpha_feedback_loop_v2.json`
- `status = DATA_INSUFFICIENT`
- `cases_analyzed = 0`
- `recommended_adjustments = {}`
Interpretation:
- The calibration loop is not yet receiving enough real outcome data to produce stable reweighting.
- `run 165` and `run 166` confirm the upstream seed and collection stages are unblocked.
## Bottom line
- WBS-4.1: not complete
- WBS-4.2: not complete
- WBS-4.3: not complete
- Shared blocker: insufficient live T+20 accumulation
+78
View File
@@ -0,0 +1,78 @@
# WBS-7.9 Evidence Packet Final
## Scope
`WBS-7.9` is the Synology external-service POC for `snapshot_admin_server_v1.py`.
The implementation is ready for deployment, but the final external live verification on the NAS is still pending.
## Current artifact set
- `src/quant_engine/snapshot_admin_server_v1.py`
- `tools/run_snapshot_admin_server_v1.py`
- `tools/run_snapshot_admin_synology.sh`
- `tests/unit/test_snapshot_admin_web_v1.py`
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md`
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md`
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md`
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_TABLE.md`
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_COPYPASTE.md`
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_PREFLIGHT_10.md`
- `Temp/snapshot_admin_approval_packet_v1.json`
- `Temp/snapshot_admin_approval_packet_v1.md`
- `Temp/snapshot_admin_export_v1.json`
- `Temp/snapshot_admin_web_validation.json`
## Local validation evidence
- `python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q`
- Result: `10 passed`
- `python tools/validate_snapshot_admin_web_v1.py`
- Result: `PASS`
- `python tools/validate_snapshot_admin_workflow_v1.py`
- Result: `PASS`
## Local HTTP verification evidence
The following loopback checks were executed against a real server process started from
`tools/run_snapshot_admin_server_v1.py` with Basic Auth enabled:
- Unauthenticated `GET /api/state`
- Result: `401 Unauthorized`
- `WWW-Authenticate: Basic realm="Snapshot Admin", charset="UTF-8"`
- Authenticated `GET /api/state`
- Result: `200 OK`
- `version.app = snapshot-admin-web-v7`
- Authenticated `GET /tables`
- Result: `200 OK`
- Tabler grid surface present
This confirms the localhost-side service path, auth gate, and `/tables` route work as expected
in the workspace. It does not replace the NAS-side reverse proxy verification.
## Workspace topology evidence
From `Temp/snapshot_admin_approval_packet_v1.json`:
- `workspace_db = C:\\Temp\\data_feed\\Temp\\snapshot_admin_web_validation.db`
- `collector_db = C:\\Temp\\data_feed\\outputs\\kis_data_collection\\kis_data_collection.db`
- `settings_rows = 31`
- `account_snapshot_rows = 40`
- `settings_and_snapshot_share_db = true`
- `collector_separate_db = true`
## Live verification still required
The NAS-side POC is not complete until these are observed on the real Synology host:
1. `curl -i http://127.0.0.1:8787/api/state` returns `200 OK`
2. `curl -i https://<public-host>/api/state` returns `401 Unauthorized` without credentials
3. `curl -u '<user>:<password>' https://<public-host>/api/state` returns `200 OK`
4. Browser access to `https://<public-host>/` and `https://<public-host>/tables` works after Basic Auth
5. DSM reverse proxy and firewall values are recorded as evidence
## Final disposition
- Implementation status: ready
- Deployment guide: ready
- External live verification: pending
- Promote to `실배포 검증 완료` only after the NAS curl evidence and browser screenshot are archived
+10 -2
View File
@@ -26,8 +26,6 @@ import sys
from pathlib import Path
from typing import Any
import requests
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
@@ -36,6 +34,14 @@ REAL_DOMAIN = "https://openapi.koreainvestment.com:9443"
MOCK_DOMAIN = "https://openapivts.koreainvestment.com:29443"
TOKEN_CACHE_DIR = ROOT / "Temp"
def _requests():
try:
import requests # type: ignore
except Exception as exc: # pragma: no cover - surfaced as runtime validation error
raise RuntimeError(f"import_error: {exc}") from exc
return requests
# ── [CRITICAL] 주문 차단 목록 — 절대 수정/완화 금지 (governance/rules/06_no_direct_api_trading.yaml) ──
# "/trading/" 하위 경로는 주문(order)뿐 아니라 계좌잔고조회(inquire-balance)도 포함한다.
# 계좌 보유종목/잔고는 governance/rules/07_no_kis_account_balance_query.yaml에 의해
@@ -126,6 +132,7 @@ def _issue_or_reuse_token(creds: KisCredentials) -> str:
except (json.JSONDecodeError, KeyError, ValueError):
pass
requests = _requests()
resp = requests.post(
f"{creds.domain}/oauth2/tokenP",
json={"grant_type": "client_credentials", "appkey": creds.app_key, "appsecret": creds.app_secret},
@@ -155,6 +162,7 @@ def _send_request(creds: KisCredentials, path: str, tr_id: str, params: dict[str
"tr_id": tr_id,
"custtype": "P",
}
requests = _requests()
resp = requests.get(f"{creds.domain}{path}", headers=headers, params=params, timeout=15)
resp.raise_for_status()
return resp.json()
+2 -3
View File
@@ -3,11 +3,10 @@ from __future__ import annotations
import json
import re
import sqlite3
from datetime import datetime
from datetime import datetime, timedelta, timezone
from functools import lru_cache
from pathlib import Path
from typing import Any
from zoneinfo import ZoneInfo
import yaml
@@ -15,7 +14,7 @@ import yaml
ROOT = Path(__file__).resolve().parents[2]
DEFAULT_DB = ROOT / "outputs" / "snapshot_admin" / "snapshot_admin.db"
DEFAULT_SEED_JSON = ROOT / "GatherTradingData.json"
KST = ZoneInfo("Asia/Seoul")
KST = timezone(timedelta(hours=9))
SETTINGS_TABLE = "settings"
SNAPSHOT_TABLE = "account_snapshot"