시리즈: 토스증권 Open API로 나만의 포트폴리오 리포트 만들기
1편: 매일 쌓고, 과거는 복원하고
2편: Claude Code·Codex CLI로 종목 AI 분석 붙이기 (이 글)
1편에서 만든 종목 상세 화면에는 숫자와 차트가 많다. Trend Score, 밸류에이션, 수급, 공시, 캔들 차트. 그런데 이걸 종합해서 읽는 건 여전히 내 몫이었다.
여기에 LLM 분석을 붙이기로 했다. 다만 API 키를 따로 결제하고 싶지는 않았다. 이미 Claude Code와 Codex를 구독으로 쓰고 있었기 때문이다. 그래서 서버에서 두 CLI를 직접 실행하는 방식을 택했다.
claude -p "프롬프트"codex exec "프롬프트"둘 다 한 번 질의하고 끝나는 비대화형 모드가 있다. 컨테이너에 Node.js와 두 CLI를 설치하고, 자격증명은 기존 data/ 볼륨 아래에 두도록 CLAUDE_CONFIG_DIR, CODEX_HOME을 지정했다.

투자의견: Buy 같은 한 줄을 파싱해서 저장한다. 종목 목록에 배지로 보인다.
차트 캡처에는 새 의존성이 필요 없었다. KLineChart에 이미 getConvertPictureUrl()이라는 스크린샷 API가 들어 있다. 일·주·월 전환도 이미 받은 일봉으로 다시 계산하는 구조라 네트워크 요청 없이 세 장을 금방 뽑는다.
다만 처음엔 주봉과 월봉이 똑같은 이미지로 나왔다. 데이터를 바꾼 뒤 requestAnimationFrame을 한 번만 기다렸더니, 차트가 실제로 다시 그려지기 전에 캡처해 버린 것이다. 두 프레임을 기다리도록 바꿔서 해결했다.
시스템 프롬프트에는 몇 가지 규칙을 넣었다.
투자의견: <Strong Buy|Buy|Hold|Sell|Strong Sell>을 정확히 한 줄 쓴다.마지막 규칙 덕분에 정규식 하나로 의견을 뽑을 수 있다.
서버에서 CLI를 돌리려면 서버의 CLI가 로그인돼 있어야 한다. 매번 컨테이너에 들어가 로그인하기는 싫어서, LNB의 "LLM 설정" 메뉴에서 인증까지 끝나게 만들었다.
두 CLI의 인증 방식은 다르다.
| CLI | 방식 | 화면에서 하는 일 |
| Codex | codex login --device-auth (디바이스 코드) |
URL과 코드를 보여주고, 사용자가 다른 기기에서 입력하면 자동 완료 |
| Claude Code | claude auth login (코드 붙여넣기) |
URL을 보여주고, 사용자가 받은 코드를 모달에 입력하면 CLI 표준입력으로 전달 |

말로는 간단한데, 실측해 보니 문서에 없는 것들이 계속 나왔다.
94m 같은)과 코드가 하나의 단어로 붙어서 \b 정규식이 실패했다. 파싱 전에 ANSI 시퀀스를 걷어내서 해결했다.claude auth login은 URL만 찍고 "Paste code here if prompted >"에서 기다린다. 코드는 사용자가 브라우저 페이지에서 보고 직접 넣는 값이다.--bare를 못 쓴다: claude -p에 --bare를 붙이면 API 키만 읽고 OAuth 자격증명을 무시한다. 이 앱은 OAuth 로그인을 쓰므로 빼야 했다.CLAUDE_CONFIG_DIR/CODEX_HOME 디렉터리가 없으면 CLI가 바로 죽는다. 호출 전에 항상 만들어 두도록 했다.select()로 데드라인을 걸어 90초 안에 반드시 끝나게 했다.
시스템 프롬프트와 이미지를 넘기는 방식도 CLI마다 달랐다.
--append-system-prompt로 시스템 프롬프트를 붙인다. 이미지는 프롬프트에 파일 경로를 적고 --allowedTools "Read"로 읽기만 허용해서 스스로 읽게 한다.AGENTS.md를 써서 -C로 넘기는 식으로 우회했다. 이미지는 -i로 첨부한다.Codex에서 "No prompt provided via stdin" 에러가 계속 난 적이 있다. 원인은 인자 순서였다. -i <FILE>...은 여러 값을 받는 옵션이라, 그 뒤에 둔 프롬프트까지 이미지 목록으로 먹어 버렸다. 프롬프트를 맨 앞으로 옮겨서 해결했다. 여러 값을 받는 옵션 뒤에는 위치 인자를 두지 않는 게 안전하다.
provider는 처음엔 최근 7일 호출 수를 비교해 덜 쓴 쪽을 고르게 했다. 결국 규칙을 단순하게 바꿨다. 인증된 쪽만 쓰고, 둘 다 되어 있으면 Codex를 먼저 쓴다.
하나씩 누르는 게 귀찮아서 보유·관심종목 전체를 평일 밤에 자동 분석하는 배치를 만들었다.
문제는 차트 이미지였다. 캡처가 100% 브라우저에서 일어나니 배치에선 이미지를 만들 수 없다. 그래서 배치 안에서 Playwright로 헤드리스 브라우저를 띄우고, 종목 상세 페이지를 열어 기존 캡처 함수(window.captureCandleCharts())를 그대로 호출했다. 캡처 로직이 함수 하나로 묶여 있어서 생각보다 간단했다.
구독 쿼터도 신경 써야 했다. 남은 사용량을 미리 조회하는 공식 방법이 두 CLI 모두 없다. 대신 쿼터를 다 쓰면 에러 메시지에 "usage limit"이 들어간다. 이걸 감지하면 다른 provider로 넘기고, 대안이 없으면 남은 종목을 건너뛰고 텔레그램으로 알린다.
그런데 몇 주 돌려 보니 토큰 사용량이 예측이 안 될 만큼 커졌다. 매일 수십 종목을 이미지 세 장씩 붙여 분석하니 당연한 결과였다. 결국 cron 자동 실행은 없앴다. 파이프라인은 그대로 두고, 홈 화면의 "AI 분석" 버튼으로 필요할 때만 전체를 돌리게 바꿨다.

수동 실행으로 바꾼 뒤 쓰면서 불편했던 점을 손봤다.
--model로 넘긴다. Codex는 gpt-6-luna, Claude는 haiku 별칭이다. .env로 바꿀 수 있다. 진행 버튼에 실제 provider와 모델명을 표시한다.Buy 또는 Strong Buy인 종목만 모아 보여준다. 예전엔 Buy였어도 최신 분석이 Hold면 빠진다. 관심종목 표 형식을 재사용했다.
종목이 많아지니 순차 실행으로는 전체 분석이 꽤 오래 걸렸다. 시간 대부분은 CLI 응답을 기다리는 시간이다. 그래서 여러 종목의 CLI 호출을 겹쳐 돌리도록 바꿨다.
실제 CLI 대신 응답을 150ms 늦추는 가짜 CLI로 12종목을 측정해 보니, 동시 2개일 때 걸린 시간이 순차 대비 약 절반이었다. 실제 provider 속도나 쿼터까지 반영한 숫자는 아니라서 참고용이다.
홈의 "AI 분석" 버튼 아래에는 실행 현황 카드도 붙였다. 지금 동시에 몇 개가 호출 중인지, 최대 몇 개까지 겹쳤는지, 종목별로 어떤 provider·모델이 어느 단계에 있는지, 호출 타임라인이 보인다. 새로고침해도 서버 상태 파일에서 복원된다. 통신이 15초 넘게 끊기면 실패라고 단정하지 않고 "마지막 확인값"이라고 표시한다.
여기서 신경 쓴 건 "병렬 모드"와 "실제로 병렬로 돌고 있음"을 구분하는 것이다. 설정이 2개여도 실제 호출이 하나뿐이면 "병렬 모드 · 현재 1개 호출"이라고만 보여준다. 숫자는 실제 CLI 호출 함수에 들어가고 나오는 지점에서만 센다.

API 키 없이 구독 중인 CLI를 서버에서 돌리는 방식은 꽤 쓸 만했다. 대신 CLI는 사람이 터미널에서 쓰라고 만든 도구라서, 프로그램이 다루려면 출력 형식, 인자 순서, 인증 저장 위치 같은 걸 하나하나 실측해야 했다. 문서만 믿고 짠 부분은 거의 다 한 번씩 깨졌다.
그리고 자동화가 항상 답은 아니었다. 매일 자동 분석은 편했지만 사용량을 감당할 수 없었고, 필요할 때 누르는 버튼과 "이미 한 건 건너뛰기"가 더 맞았다.
마지막으로, 이 분석은 투자 자문이 아니다. 앱이 가진 데이터를 LLM이 해석한 참고 의견일 뿐이고, 판단은 직접 한다.
이전 글: 1편 — 매일 쌓고, 과거는 복원하고
다음 글: 없음
※ 이 글은 AI의 도움을 받아 작성했고, 작성자가 내용을 직접 검토한 뒤 올렸다.
| 토스증권 Open API로 나만의 포트폴리오 리포트 만들기 (1) — 매일 쌓고, 과거는 복원하고 (0) | 2026.10.28 |
|---|---|
| 맥북 한 대로 AI 개발팀 굴리기 (3) — Agent Office와 삽질 모음 (0) | 2026.10.28 |
| 맥북 한 대로 AI 개발팀 굴리기 (2) — 사용량 관리와 모델 배치 (0) | 2026.10.28 |
| 맥북 한 대로 AI 개발팀 굴리기 (1) — 설계와 작업 흐름 (0) | 2026.10.28 |
댓글 영역