meta: title: "은퇴자산포트폴리오 — 비기계적 매도전략(가치보존) 명세" parent_file: "RetirementAssetPortfolio.yaml" version: "2026-06-21-PHASE8_qualitative_sell" language: "ko-KR" timezone: "Asia/Seoul" role: "canonical" has_code_implementation: true code_path: "src/quant_engine/qualitative_sell_strategy_v1.py" purpose: > 익절/손절을 고정 % 임계값으로 기계적으로 트리거하지 않고, 매크로·실적·펀더멘털· 공매도수급·호가 미시구조·대내외 변수(대형 IPO·섹터 로테이션) 5개 독립 팩터군의 합의(confluence)로 매도/보유/추가 확신도를 산출해 주식가치를 최대치로 보존한다. 현금부족 사유는 입력에서 의도적으로 배제한다. qualitative_sell_strategy: policy: execution: "보유 포지션 검토 시 항상 실행. STOP_PRICE_CORE_V1/PROFIT_RATCHET_TIERED_V2 등 기존 기계적 손절/래칫 라인과 병행 — 이 명세가 그것들을 대체하지 않으며, '서두르지 않는 재량적 정리' 판단을 보강한다." confluence_rule: "5개 팩터군 중 최소 3개가 동일 방향(+/-)으로 합의해야 행동 생성. 단일 팩터의 임계값 돌파만으로 매도 트리거 금지." cash_shortfall_exclusion: "현금부족·리밸런싱 강제매도 사유는 이 명세의 입력에서 제외한다. 해당 사유의 매도는 spec/exit/value_preserving_cash_raise_optimizer_v7.yaml 책임." date_basis: "review_window는 실제 실적발표일·고영향 매크로 이벤트일(spec/strategy/ macro_event_synchronizer_v2.yaml:event_hold_gate)에서 역산한다. 임의 고정일 금지." factor_families: macro_pressure: id: "F1" formula_ref: "spec/strategy/macro_event_synchronizer_v2.yaml:position_size_scale_formula" sources: ["macro_risk_score", "FX", "금리", "산업통상부 수출입동향(섹터별)"] note: "수출입 동향으로 섹터별 실적 선행지표를 추정해 가중." fundamental_trajectory: id: "F2" formula_ref: "spec/strategy/fundamental_quality_v3.yaml" sources: ["EPS 추정치 변화", "영업이익률 추세", "실적발표 컨센서스 서프라이즈"] short_interest_pressure: id: "F3" formula_ref: "spec/13b_harness_formulas.yaml:formula_registry.formulas.SHORT_INTEREST_RISK_GAUGE_V1" sources: ["공매도잔고율 추세", "공매도거래비중", "상대수익률", "거래량 이상", "실적전망"] note: > 잔고율은 '매수/매도 버튼'이 아니라 위험계기판. 잔고율이 낮은 종목(예: 현대로템형, <1%)은 잔고율 자체보다 거래비중·상대수익률을 더 중요하게 본다. microstructure_pressure: id: "F4" sources: ["호가 10단계 매수/매도 잔량 불균형", "체결강도", "스프레드"] note: "전략적 방향 결정에는 쓰지 않고 confluence가 SELL/ADD로 합의된 이후의 '집행 타이밍'에만 사용 — execution_window 산정 보조." liquidity_rotation_risk: id: "F5" sources: ["대형 IPO 청약/상장에 따른 섹터 자금 이탈", "동일 섹터 로테이션", "외국인/기관 섹터 비중 변화"] output: formula_ref: "spec/13b_harness_formulas.yaml:formula_registry.formulas.QUALITATIVE_SELL_STRATEGY_V1" python_tool: "src/quant_engine/qualitative_sell_strategy_v1.py:compute_qualitative_sell_strategy" actions: EXIT_REVIEW_FULL: "4-5개 팩터군 매도방향 합의 + composite_score>=0.6 — 전량 정리 검토" TRIM_REVIEW_PARTIAL: "3개 이상 팩터군 매도방향 합의, composite_score<0.6 — 부분 정리 검토" HOLD_ADD_CONVICTION: "3개 이상 팩터군 지지방향 합의 — 보유/추가 확신" HOLD_NO_CONFLUENCE: "합의 미달 — 보유, 관찰 지속" INSUFFICIENT_DATA_NO_ACTION: "confluence 판정에 필요한 최소 데이터 부족 — 추정 금지" market_regime: formula_ref: "spec/13b_harness_formulas.yaml:formula_registry.formulas.MARKET_REGIME_CLASSIFIER_V1" rule: "금리 상승기(RISING)=PERFORMANCE_MARKET(실적장세) — fundamental_trajectory 가중 상향. 금리 보합/하락기(FLAT/FALLING)=TECHNICAL_MARKET(기술장세) — short_interest_pressure/ microstructure_pressure 가중 상향. confluence 합의건수 판정 자체는 가중치와 무관 — composite_score(행동 강도)에만 영향." satellite_candidate_score: formula_ref: "spec/13b_harness_formulas.yaml:formula_registry.formulas.SATELLITE_CANDIDATE_SCORE_V1" purpose: "미보유 위성 유니버스 종목의 BUY_CANDIDATE/WATCH/AVOID 사전 평가. sector_export_trend (관세청/산업통상부 수출입동향)·fundamental_trajectory·relative_return_20d를 market_regime별 가중치로 종합." data_sources: note: > 2026-06-21 세션 실측 결과. investing.com 직접 스크래핑은 403(Cloudflare) 차단 확인 — 자동 수집 경로로 채택하지 않는다. WBS-7.9(2026-06-22): Naver 도메인(finance.naver.com)은 현재 무인증 접근 가능(sise_day, frgn 엔드포인트). 다만 향후 Cloudflare 차단 가능성에 대비해 fetch_naver_market_data_v1.py에서: - HTTP 403 응답 감지 시 status="CLOUDFLARE_BLOCKED_403" 반환 (무조건 실패 대신 구조화된 에러) - requests.RequestException 캐치로 네트워크 오류 처리 - 호출부(build_qualitative_sell_inputs_v1.py)에서 상태 확인 후 DATA_MISSING_SAFE 처리 실제 차단 발생 시 대체 경로 없음(KRX는 OTP 필수, investing.com은 차단됨). 운영: Cloudflare_BLOCKED_403 상태 발생 시 slack/로그 경고 + 수동 실행. relative_return_20d: tool: "tools/fetch_naver_market_data_v1.py:compute_relative_return_20d" source: "finance.naver.com/item/sise_day.naver (무인증, 동작 확인)" status: "WORKING" volume_ratio_5d: tool: "tools/fetch_naver_market_data_v1.py:compute_volume_ratio_5d" source: "finance.naver.com/item/sise_day.naver" status: "WORKING" foreign_institution_flow: tool: "tools/fetch_naver_market_data_v1.py:fetch_foreign_institution_flow" source: "finance.naver.com/item/frgn.naver (GAS gdc_01_fetch_fundamentals.gs와 동일 소스 — 보유종목은 기존 GAS 수집 결과 재사용 권장, 위성 후보군만 직접 호출)" status: "WORKING" sector_export_trend: tool: "tools/fetch_trade_statistics_motie_v1.py:compute_sector_export_trend" source: "관세청/산업통상부 수출입통계 — 1차: --csv 수동 다운로드 경로(안정적, 권장). 2차: data.go.kr OpenAPI(CUSTOMS_API_KEY 필요, 미설정 시 DATA_MISSING)." status: "CSV_PATH_WORKING / API_PATH_NEEDS_KEY" short_balance_ratio: source: "KRX 공매도종합포털(open.krx.co.kr/contents/SRT) — 직접 API 호출은 OTP 세션 필요, LOGOUT 응답으로 차단 확인. KIS Open API도 잔고율(보유 포지션 개념)은 제공하지 않음 (실측 확인, 2026-06-21). 수동 다운로드 CSV(--short-csv)로만 안정 확보 — 자동화 재시도 불필요(차단 확정)." status: "MANUAL_CSV_ONLY" short_turnover_share: source: "[2026-06-21 해결] KIS Open API daily-short-sale(FHPST04830000, /uapi/domestic-stock/v1/quotations/daily-short-sale) output2.ssts_vol_rlim — 실전계좌 도메인(--kis-account real)에서 실측 동작 확인. 모의계좌 도메인은 500 에러(미지원). Naver는 KRX iframe 위임으로 값 없음(폐기)." tool: "tools/build_qualitative_sell_inputs_v1.py:fetch_kis_supplement" status: "KIS_API_WORKING (real account only)" microstructure_pressure_10_level_orderbook: source: "[2026-06-21 해결] KIS Open API inquire-asking-price-exp-ccn(FHKST01010200, /uapi/domestic-stock/v1/quotations/inquire-asking-price-exp-ccn) output1 — 실전+모의계좌 도메인 모두 실측 동작 확인. 필드명: askp1~10/bidp1~10/ askp_rsqn1~10/bidp_rsqn1~10/total_askp_rsqn/total_bidp_rsqn(전부 소문자, 실측 확인). 전략 방향 결정에는 쓰지 않고 confluence 성립 후 집행 타이밍 보조로만 사용(factor_families.microstructure_pressure 참조)." tool: "src/quant_engine/qualitative_sell_strategy_v1.py:compute_microstructure_pressure_from_orderbook" status: "KIS_API_WORKING" investor_trend_official: source: "[참고, 미연동] KIS Open API inquire-investor(FHKST01010900) — 개인/외국인/기관 순매수수량(prsn_ntby_qty/frgn_ntby_qty/orgn_ntby_qty) 등 실측 확인. Naver frgn.naver 스크래핑을 대체할 수 있는 공식 소스이나 아직 미연동 (기존 GAS 수급 피드와 중복 — 필요 시 후속 작업)." status: "VERIFIED_NOT_WIRED" kis_open_api_constraints: note: "[CRITICAL] governance/rules/06_no_direct_api_trading.yaml(주문 미실행), governance/rules/07_no_kis_account_balance_query.yaml(계좌 보유종목 조회 금지) — KIS API는 시장 전체 공개 데이터(시세/호가/공매도/투자자동향) 조회에만 사용. CI 강제 게이트: tools/validate_no_direct_api_trading_v1.py(strict, warn_only 불가)." macro_pressure / rate_trend / next_earnings_date / next_macro_event_date / macro_event_impact: source: "기존 GAS 하네스(macro_event_synchronizer_v2, gas_event_calendar.gs)가 이미 산출/수집 — 중복 수집 금지, --context-json으로 그 결과를 주입." status: "REUSE_EXISTING_HARNESS" orchestrator: tool: "tools/build_qualitative_sell_inputs_v1.py" purpose: "위 출처들을 종목별 ctx로 조립해 QUALITATIVE_SELL_STRATEGY_V1을 호출하고 outputs/qualitative_sell_strategy/.json에 기록한다. --batch --workbook으로 account_snapshot 실보유 종목 전체 일괄 처리." satellite_orchestrator: tool: "tools/build_satellite_candidate_recommendations_v1.py" purpose: "universe 시트(미보유 위성 유니버스)에서 보유종목을 제외한 후보 전체를 SATELLITE_CANDIDATE_SCORE_V1로 평가해 outputs/qualitative_sell_strategy/ satellite_recommendations.json에 기록한다. universe.Sector 한글 라벨은 부분 문자열 매칭으로 SECTOR_HS_MAP에 연결 — 매칭 실패 시 sector_export_trend를 추정하지 않고 None 유지(추정 금지 원칙)."