AI 판단은 주문 직전에 새로 호출되는 요청이 아니다. 스케줄러가 분석을 만들어 판단 로그로 저장해두면 주문 실행 단계가 그 로그를 다시 읽어 안전장치를 통과한 경우에만 처리한다. 둘을 분리한 이유는 하나다. LLM 응답을 주문 명령으로 쓰면 실패했을 때 무엇이 잘못됐는지 나눌 수 없으니까.

| 작업 | 주기 | 운영 의미 |
|---|---|---|
| 뉴스/RAG 수집 | 설정값 기반, 코드 기본값 12시간 | 수집, 번역, 청크 분할, 임베딩, OpenSearch 저장 |
| 시장 심리 갱신 | 5분 | Alternative.me 기반 시장 심리 상태 갱신 |
| AI 판단 생성 | 60분 | 관심 종목별 BUY / SELL / HOLD 분석 생성 |
| 정확도 검증 | 30분 | 과거 판단과 이후 가격 흐름을 비교해 실패 피드백 후보 생성 |
| 포트폴리오 스냅샷 | 1시간 | 보유 자산, 손익, 상태를 시계열로 저장 |
| 실주문 대사 | 15초 | 미종결 실주문을 거래소 상태와 대조해 체결을 exactly-once 반영 |
| AI 브리핑 | 08:30 | 포트폴리오와 시장 상태 기반 일일 브리핑 |
포트폴리오와 기술 지표, RAG 뉴스, 시장 심리에 최근 실패한 판단 피드백까지 넣는다. 기술 지표는 15m나 60m 캔들을 기반으로 계산한 SMA·EMA·Bollinger·RSI와 현재가의 상대 위치다.
조회 오류도 값으로 함께 넘긴다. 데이터가 없는 것과 조회에 실패한 것을 프롬프트 단계에서부터 구분해야 AI가 빈 값을 자기 방식대로 해석하지 않는다.
페르소나는 설정 화면에서 사용자가 바꿀 수 있지만 안전 규칙보다 우선할 수 없다고 프롬프트에 명시했다. 공격적인 페르소나를 넣어도 BUY 조건 자체는 완화되지 않아야 했기 때문이다.
출력은 decision과 confidence, recommended_weight, reasoning 네 필드로 제한된다. 이 응답은 주문 요청이 아니라 판단 로그고 executor가 별도로 아래 조건을 다시 검사한다.
| 차단 조건 | 처리 |
|---|---|
| 분석 로그 없음 또는 만료 | 주문 실행 스킵 |
| decision = HOLD | 주문 실행 없음 |
| confidence 부족 (기본 85 미만) | 확신도 미달로 스킵 |
| recommended_weight ≤ 0 | 포지션 비중 제안 없음으로 스킵 |
| 포트폴리오 조회 오류 | 현재 잔고를 신뢰할 수 없어 스킵 |
| entry gate 실패 (기본 70 미만) | score, calibrated confidence, 제외 조건에 따라 BUY 차단 |
| shadow mode | 주문 후보만 기록하고 실제 주문 없음 |
| live BUY lock | 실전 신규 매수는 명시적으로 잠금 해제한 경우만 허용 |
1차 분석과 BUY 직전 2차 검증이 같은 형태로 저장돼서 정확도 통계에 둘이 섞였다. 성과가 좋게 나와도 무엇 덕분인지 설명할 수 없어서 stage와 계보를 나눴다.