feat(kis-collection): finalize sqlite migration, add fallback resilience, and update WBS documentation
This commit is contained in:
@@ -28,6 +28,7 @@ from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
# 입력 파일
|
||||
PREDICTION_ACCURACY = ROOT / "Temp" / "prediction_accuracy_harness_v2.json"
|
||||
REBOUND_EFF = ROOT / "Temp" / "rebound_sell_efficiency_v1.json"
|
||||
LATE_CHASE = ROOT / "Temp" / "late_chase_attribution_v1.json"
|
||||
PROPOSAL_HIS = ROOT / "Temp" / "proposal_evaluation_history.json"
|
||||
@@ -46,6 +47,30 @@ def load_json(p: Path) -> dict | list:
|
||||
return json.loads(p.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def load_prediction_accuracy() -> dict:
|
||||
data = load_json(PREDICTION_ACCURACY)
|
||||
return data if isinstance(data, dict) else {}
|
||||
|
||||
|
||||
def current_t5_status() -> tuple[float | None, str]:
|
||||
"""WBS-7.2 source-of-truth shim.
|
||||
|
||||
Prefer the latest prediction accuracy harness when present. Do not fall back to
|
||||
stale hardcoded percentages when the harness explicitly says sample=0.
|
||||
"""
|
||||
data = load_prediction_accuracy()
|
||||
if not data:
|
||||
return None, "ARTIFACT_MISSING"
|
||||
|
||||
t5_sample = int(data.get("t5_sample") or 0)
|
||||
t5_rate = data.get("t5_op_rate")
|
||||
if t5_sample == 0:
|
||||
return None, "DATA_GATED"
|
||||
if isinstance(t5_rate, (int, float)):
|
||||
return float(t5_rate), "OK"
|
||||
return None, "DATA_MISSING"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
rebound = load_json(REBOUND_EFF)
|
||||
chase = load_json(LATE_CHASE)
|
||||
@@ -90,7 +115,8 @@ def main() -> int:
|
||||
})
|
||||
|
||||
# ── (3) T+1 / T+5 KPI 추적 ─────────────────────────────────────────
|
||||
# operational_report 에서 일치율 추출
|
||||
# operational_report는 보고서 텍스트용 보조 원장이고,
|
||||
# T+5 현재값은 prediction_accuracy_harness_v2.json을 우선한다.
|
||||
t1_rate = None
|
||||
t5_rate = None
|
||||
sections = op.get("sections", []) if isinstance(op, dict) else []
|
||||
@@ -109,7 +135,11 @@ def main() -> int:
|
||||
|
||||
# 직접 알려진 값 사용 (operational_report 에서 확인된 수치)
|
||||
if t1_rate is None: t1_rate = 47.28
|
||||
if t5_rate is None: t5_rate = 35.86
|
||||
live_t5_rate, live_t5_status = current_t5_status()
|
||||
if live_t5_rate is not None:
|
||||
t5_rate = live_t5_rate
|
||||
elif t5_rate is None:
|
||||
t5_rate = None
|
||||
|
||||
kpi_tracker.append({
|
||||
"metric": "T+1_match_rate_pct",
|
||||
@@ -119,14 +149,24 @@ def main() -> int:
|
||||
"status": "BELOW_TARGET" if t1_rate < 55.0 else "ON_TARGET",
|
||||
"note": "동전던지기(50%) 이하 — 신호 품질 개선 필요",
|
||||
})
|
||||
kpi_tracker.append({
|
||||
"metric": "T+5_match_rate_pct",
|
||||
"current": t5_rate,
|
||||
"target_min": 55.0,
|
||||
"gap": round(55.0 - t5_rate, 2),
|
||||
"status": "BELOW_TARGET" if t5_rate < 55.0 else "ON_TARGET",
|
||||
"note": "T+5 35.86% — ANTI_LATE_ENTRY_GATE_V2 임계값 보정 시 개선 목표",
|
||||
})
|
||||
if t5_rate is None:
|
||||
kpi_tracker.append({
|
||||
"metric": "T+5_match_rate_pct",
|
||||
"current": None,
|
||||
"target_min": 55.0,
|
||||
"gap": None,
|
||||
"status": "DATA_GATED",
|
||||
"note": f"T+5 current source={live_t5_status} — sample=0 or artifact missing; do not cite stale 35.86%",
|
||||
})
|
||||
else:
|
||||
kpi_tracker.append({
|
||||
"metric": "T+5_match_rate_pct",
|
||||
"current": t5_rate,
|
||||
"target_min": 55.0,
|
||||
"gap": round(55.0 - t5_rate, 2),
|
||||
"status": "BELOW_TARGET" if t5_rate < 55.0 else "ON_TARGET",
|
||||
"note": "T+5 current source-of-truth read from prediction_accuracy_harness_v2.json",
|
||||
})
|
||||
|
||||
# ── (4) OUTCOME_TRUST_GAP ───────────────────────────────────────────
|
||||
# design_score 97.12 vs 실측 T+5 35.86% 간 신뢰도 괴리
|
||||
@@ -134,7 +174,8 @@ def main() -> int:
|
||||
"design_score": rb_score,
|
||||
"actual_t5_pct": t5_rate,
|
||||
"gap_note": (
|
||||
f"설계점수 rebound_efficiency={rb_score:.2f} vs 실측 T+5 일치율 {t5_rate}% — "
|
||||
f"설계점수 rebound_efficiency={rb_score:.2f} vs 실측 T+5 일치율 "
|
||||
f"{('DATA_GATED' if t5_rate is None else f'{t5_rate}%')} — "
|
||||
f"설계점수가 높아도 실제 수익성 지표(T+5)는 낮을 수 있음. "
|
||||
f"두 지표를 항상 물리적으로 분리해 표시해야 한다."
|
||||
),
|
||||
@@ -153,11 +194,14 @@ def main() -> int:
|
||||
print(f"\n [T+1/T+5 KPI 현황]")
|
||||
for k in kpi_tracker:
|
||||
status_icon = "✗" if k["status"] == "BELOW_TARGET" else "✓"
|
||||
print(f" {k['metric']}: {k['current']}% (목표 ≥{k['target_min']}%) {status_icon}")
|
||||
if k["current"] is None:
|
||||
print(f" {k['metric']}: DATA_GATED (목표 ≥{k['target_min']}%) {status_icon}")
|
||||
else:
|
||||
print(f" {k['metric']}: {k['current']}% (목표 ≥{k['target_min']}%) {status_icon}")
|
||||
print(f" → {k['note']}")
|
||||
|
||||
print(f"\n [보정루프 개선 경로]")
|
||||
print(f" T+5 35.86% → 50%+ 목표:")
|
||||
print(f" T+5 {'DATA_GATED' if t5_rate is None else f'{t5_rate}%'} → 50%+ 목표:")
|
||||
print(f" Step 1. ALEG_V2_GATE1_BLOCK_PCT(3%) → 표본 누적 후 최적값 보정")
|
||||
print(f" Step 2. DSD_V1 가중치 → logistic regression 최적화")
|
||||
print(f" Step 3. K2 분할비율 0.5 → 30/70/40/60/50/50 backtest 비교")
|
||||
@@ -191,7 +235,7 @@ def main() -> int:
|
||||
"correction_steps": [
|
||||
f"rebound_efficiency_score={rb_score:.2f} → 보고서 표시 시 [UNVALIDATED_DESIGN_SCORE: n={rb_combo}] 주석 필수",
|
||||
f"late_chase_attribution: samples=0 → 최소 {SAMPLE_MIN}건 표본 누적 후 chase_entry_rate 검증",
|
||||
f"T+5 {t5_rate}% → 보정루프(calibration_registry.yaml) 기반 임계값 최적화로 50%+ 목표",
|
||||
f"T+5 {'DATA_GATED' if t5_rate is None else f'{t5_rate}%'} → 보정루프(calibration_registry.yaml) 기반 임계값 최적화로 50%+ 목표",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user