AlpacaTradingAgent 설치부터 모의 주문까지 — 에이전트 열셋이 판단하고 알파카가 체결합니다
TradingAgents 를 알파카 모의 계좌에 붙인 확장판입니다. 미국 주식과 비트코인을 같은 파이프라인으로 돌려 보고, 판단이 실제 모의 주문이 되는 한 사이클을 끝까지 따라갑니다.
31편. AlpacaTradingAgent — 에이전트 열셋이 판단하고, 알파카가 체결합니다독자는 에이전트 팀의 판단이 알파카 모의 계좌 주문으로 이어지는 한 사이클을 보면 성공입니다.
| 하는 일 | 애널리스트 5·리서처 3·트레이더 1·리스크 4가 한 종목을 놓고 토론해 BUY/HOLD/SELL 과 손절·목표 가격을 내고, 그 판단을 알파카 모의 계좌 주문으로 보냅니다 |
| 맞는 사람 | 판단과 체결을 한 몸으로 보고 싶은 사람 · 미국 주식과 비트코인을 같은 도구로 다루고 싶은 사람 · 알파카 계정이 이미 있는 사람 |
| 안 맞는 사람 | 초를 다투는 매매를 하려는 사람(한 판에 1분 40초) · LLM 키 없이 쓰려는 사람 · 브로커에 손절이 자동으로 걸리기를 바라는 사람(크립토는 안 걸립니다) |
| 이 글의 결과물 | 웹 대시보드에서 에이전트 열셋이 순서대로 일하는 화면, 그리고 그 결론으로 체결된 알파카 모의 주문 한 건 |
| 준비물·비용 | 코드 0원(Apache-2.0) · LLM 키 필요, 한 판 $0.021 · 시세는 알파카 무료 · 5절의 규칙 직접 작성은 weltiq 유료 기능입니다(무료는 템플릿 기반) |
| 시간·난도 | 설치 10분, 첫 판 2분 · 중 (가상환경과 .env 를 만질 수 있으면 됩니다) |
| weltiq 와의 관계 | 비교합니다. 같은 판단을 양쪽 모의 계좌에 각각 놓고 체결·수수료·중지 조건이 어떻게 다른지 봅니다 |
| 검증 환경·날짜 | macOS · Python 3.12.13 · 2026-09-22 · 모델은 OpenRouter 경유 openai/gpt-5.6-luna |
| 고지 | 필자는 weltiq 와 제휴를 맺고 기고합니다. 이 글의 모든 명령과 숫자는 위 환경에서 실제로 돌려 얻은 실측이고, 주문은 전부 모의입니다. 도구가 내는 BUY/HOLD/SELL·손절·목표는 전부 그 도구의 출력이며 필자의 의견이 아닙니다 |
원문 로그와 6판 전체 기록: reports/a/31-alpaca-trading-agent/
1. 이게 뭔가요
12편에서 다룬 TradingAgents 는 에이전트 여럿이 토론해 등급을 내는 프레임워크였습니다.
좋은 도구였지만 결정적으로 주문을 못 냅니다. 상류 저장소에는 submit_order 도
TradingClient 도 한 줄이 없습니다. 판단에서 체결까지 가려면 사람이 손으로 옮겨야 합니다.
AlpacaTradingAgent 는 그 빈칸을 알파카로 메운 확장판입니다. 같은 분석 엔진에 브로커를 붙였고, 그러면서 미국 주식과 비트코인 현물을 같은 파이프라인에 태웠습니다.
| 저장소 | huygiatrng/AlpacaTradingAgent · ★271 · 포크 97 · Apache-2.0 |
| 마지막 갱신 | 2026-09-05 |
| 상류 | TauricResearch/TradingAgents (★107,991). 깃 포크가 아니라 별도 저장소입니다 |
| 형제 | 28편의 trading-bot-agent 도 같은 뿌리에 바이낸스를 붙인 것입니다 |
셋의 관계는 이렇게 보면 쉽습니다. 분석 엔진은 하나고, 붙인 브로커가 다릅니다.
상류는 브로커가 없고 5단계 등급만 내며, 이 도구는 알파카(주식 + 크립토 현물)에
실행 행동과 손절·목표 가격을 함께 내고, trading-bot-agent 는 바이낸스에 등급을 냅니다.
에이전트는 열셋입니다. 애널리스트 다섯(시장·소셜·뉴스·펀더멘털·거시 — 상류의 넷에 거시가 더해졌습니다), 리서처 셋(강세론자·약세론자·리서치 매니저), 트레이더 하나, 리스크 넷(공격·보수· 중립이 논쟁하고 리스크 매니저가 최종 판정).
상류와 가장 크게 다른 점은 팀 구성이 아니라 판단과 주문 사이에 LLM 이 아닌 층이 하나 더 있다는 것입니다. 주문 금액 상한, 종목 집중도 상한, 일손실·낙폭 서킷브레이커, 연속 거부 감지, 그리고 파일 하나만 만들면 모든 주문을 멈추는 킬 스위치가 전부 산술식으로만 돌아갑니다. 모델이 무슨 말을 해도 이 층은 설득되지 않습니다.
2. 설치
git clone https://github.com/huygiatrng/AlpacaTradingAgent.git
cd AlpacaTradingAgent
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # 204개, 약 770MB
cp env.sample .env
.env 에서 최소 다섯 줄만 채우면 돕니다.
ALPACA_API_KEY=... # 알파카 페이퍼 키
ALPACA_SECRET_KEY=...
ALPACA_USE_PAPER=True # 이 줄은 건드리지 마세요
LLM_PROVIDER=openrouter # 또는 openai / anthropic / google ...
OPENROUTER_API_KEY=...
Finnhub·FRED·CoinDesk·Reddit 키는 선택입니다. 없으면 해당 애널리스트가 원천 없이 돌 뿐 에러는 나지 않습니다 — 이게 나중에 문제가 되는데, 바로 아래에서 다시 나옵니다.
막히는 자리
| 증상 | 왜 | 어떻게 |
|---|---|---|
| 리포트가 "The analyst did not return a complete technical narrative" 로 시작한다 | 애널리스트가 도구를 부르는 자리가 구버전 랭체인 형식을 읽습니다. OpenAI 직접 경로만 예외 처리가 돼 있어 다른 제공자에서는 도구 호출이 통째로 사라지는데 에러는 안 납니다 | llm_clients/openai_client.py 의 NormalizedChatOpenAI.invoke 에서 응답의 .tool_calls 를 additional_kwargs["tool_calls"] 로 되메우면 됩니다(열두 줄). OpenAI 키를 쓰면 해당 없습니다 |
| 소셜·펀더멘털 리포트가 "검증된 신호가 없다"로 끝난다 | 그 둘의 주 원천이 OpenAI 전용 웹검색 도구입니다. 다른 제공자로는 안 붙습니다 | 우회 없음. 시장(알파카)·뉴스(구글 뉴스)는 정상이라 판단은 나옵니다 |
| 크립토 종목을 못 찾는다 | 표기 문제 | BTCUSD 가 아니라 BTC/USD 로 씁니다 |
첫 번째 것이 제일 고약합니다. 데이터 없이도 결론이 나오기 때문에 리포트 본문을 읽지 않으면 정상으로 보입니다. 같은 종목·같은 날짜로 재 보면, 고치기 전은 도구 호출 0회에 시장 리포트 734자("실질적인 시장 데이터가 제공되지 않았다"), 고친 뒤는 15회에 2,493자("최근 종가 $227.30 … $222.50 근처 매수 고려")였습니다. 방향은 우연히 둘 다 HOLD 였지만, 앞의 것은 판단이 아닙니다.
3. 첫 결과
python run_webui_dash.py --port 9420
브라우저로 http://127.0.0.1:9420 을 엽니다. 로그인은 없습니다. 처음 열면 맨 위에 안전장치
상태가, 그 아래에 알파카 모의 계좌의 포지션과 최근 주문이 이미 떠 있습니다.
왼쪽 Analysis Configuration 에서 종목을 넣고(주식은 NVDA, 크립토는 BTC/USD),
애널리스트를 고르고, 연구 깊이를 정하고, LLM 제공자와 모델을 고른 뒤 Start Analysis 를 누릅니다.
OpenRouter 를 고르면 모델 칸이 자유 입력으로 바뀌므로 openai/gpt-5.6-luna 처럼 전체 ID 를 씁니다.
오른쪽 Analysis Status 표에 에이전트 열셋이 한 줄씩 나타나며 순서대로 완료됩니다.
에이전트 열셋이 순서대로 끝나는 화면
Analysis Status 표가 이 도구의 진행 화면입니다. 애널리스트 다섯 → 리서처 셋 → 트레이더 → 리스크 넷 순으로 COMPLETED 가 채워집니다. 이 열세 줄이 다 초록이 되면 한 판이 끝난 것입니다. 표 아래 Tool Calls: 26 | LLM Calls: 13 | Generated Reports: 13 이 이 판이 쓴 비용의 근거입니다 — 도구 호출이 0 이면 2절의 그 문제이니 바로 알 수 있습니다.
NVDA 한 판이 약 1분 40초, 비용은 $0.021 이었습니다. 끝나면 아래 Decision Summary 에 최종 판정이, Agent Reports & Audit Trail 에 리포트 열세 개 전문이 탭으로 붙습니다. 이 리포트가 이 도구의 진짜 산출물입니다.
시장 애널리스트 리포트 — 진입·손절·목표가 표로 정리된 부분
리포트 끝의 Summary Table 이 이 글에서 제일 중요한 그림입니다. 진입 $224–$226 되돌림, 또는 $228.87 돌파 확인, 목표 $234.75, 손절 $218.00, 보상/위험 약 1.3:1, 현재 행동 되돌림이나 돌파 확인까지 대기. 여기 칸이 숫자로 차 있으면 성공입니다 — "매수/관망" 한 단어만 주는 도구와 이 도구가 갈리는 자리입니다. 위쪽 Invalidation 에 "$220.92 아래로 닫히면 지속 논리가 약해진다"처럼 틀렸을 때의 조건까지 적혀 있습니다.
터미널을 선호하면 python -m cli.main 도 같은 파이프라인을 돕니다. 다만 인자를 받지 않는
대화형이라, 같은 질문을 여러 번 자동으로 던지려면 파이썬에서 직접 부르는 편이 낫습니다.
g = TradingAgentsGraph(config=cfg, selected_analysts=["market","news"])
state, decision = g.propagate("BTC/USD", "2026-09-22") # decision = "BUY"|"HOLD"|"SELL"
intent = state["final_trade_intent"] # 손절·목표·수량 근거가 여기
4. 제일 잘 쓰는 법 — 같은 질문을 여섯 번
한 판만 보고 "신중한 도구"라고 쓰면 안 됩니다. 같은 조건으로 비트코인 3판, NVDA 3판을 돌렸고, 판마다 에이전트 기억을 새로 만들어 서로를 못 보게 했습니다.
| 판 | 종목 | 최종 | 확신 | 손절 | 1차 목표 | 주문 |
|---|---|---|---|---|---|---|
| 1 | BTC/USD | BUY | medium | 82,400 | 87,385 | 시장가 매수 |
| 2 | BTC/USD | HOLD | medium | 84,800 | 88,500 | 없음 |
| 3 | BTC/USD | HOLD | medium | 84,750 | 88,500 | 없음 |
| 4 | NVDA | HOLD | medium | 220.92 | 234.00 | 없음 |
| 5 | NVDA | HOLD | medium | 220.92 | 234.75 | 없음 |
| 6 | NVDA | HOLD | medium | 220.92 | 234.75 | 없음 |
방향은 흔들리고 레벨은 안 흔들립니다. NVDA 세 판의 손절이 220.92 로 완전히 같았고, 진입 조건 문장에는 228.87 이 세 판 모두 등장했습니다. 비트코인도 목표 87,385 가 두 판에서 같았습니다. 판단이 갈린 건 "지금 들어갈 만한가"이지 "어디가 지지선인가"가 아니었습니다.
거절 이유가 숫자라는 점도 눈여겨볼 만합니다. 6판 중 HOLD 를 낸 어느 판은 이렇게 적었습니다.
"제안된 되돌림 진입은 보상/위험이 약 1.7:1 에 불과해 요구 최소치에 미달한다" (그 도구의 출력)
같은 판의 진입 지침은 이랬습니다.
"조건부로만: VWAP·피벗 탈환과 안정화 후 $84,900–$85,300, 또는 거래량을 동반한 $87,385 상향 종가와 되돌림 확인. 두 트리거 사이에서는 진입하지 마라" (그 도구의 출력)
12편의 원본과 나란히
12편은 같은 모델·같은 설정으로 상류 TradingAgents 를 13판 돌려 이렇게 나왔습니다. 같은 자로 잰 이 도구의 6판을 옆에 놓습니다.
| 최종 판정 | 상류 (13판) | 이 도구 (6판) |
|---|---|---|
| 매도 | 13 / 13 | 0 / 6 |
| 관망 | 0 / 13 | 5 / 6 |
| 매수 | 0 / 13 | 1 / 6 |
(상류 쪽 13/13 은 트레이더가 낸 최종 제안 기준입니다. 같은 판들의 5단계 등급도 매도 7·비중축소 6 으로 전부 매도 쪽이었고, 관망은 한 번도 없었습니다.)
표본이 13 대 6 이고 종목·날짜도 달라 인과를 주장할 수는 없습니다. 다만 프롬프트에 정반대 지시가 들어 있는 것은 소스로 확인됩니다. 상류의 판정자는 "논쟁에 상충이 있다는 것만으로는 관망의 이유가 되지 않는다. 더 강한 쪽에 서라" 고 지시받습니다. 이 도구의 리스크 매니저는 "상충이 크거나 신선도가 낮으면 크기를 줄이거나, 확인을 기다리거나, HOLD 를 고르라" 고 지시받습니다.
상류는 관망을 벌주고, 이 도구는 관망을 허가합니다. 판단이 곧바로 주문이 되니 그렇게 바꿀 이유가 있었을 겁니다.
판단이 모의 주문이 되기까지
BUY 가 나온 1판을 실제로 보냈습니다. 웹 UI 의 실행 버튼이 부르는 바로 그 함수에 판단 객체를 그대로 넘긴 것입니다.
| 주문 | $100 시장가 매수, BTC/USD |
| 체결 | 0.001146041 BTC @ $85,559.67 — 제출 1.5밀리초 뒤 |
| 포지션에 남은 것 | 0.001143519 BTC |
| 차이 | 0.000002522 BTC = 체결 수량의 0.2201%. 알파카가 수수료를 코인으로 떼 간 몫입니다 |
| 손절 82,400 | 브로커에 안 들어갔습니다 |
체결 직후의 대시보드 — 안전장치와 알파카 모의 계좌
위쪽 Safety Guardrails 여섯 칸이 LLM 이 아닌 층입니다 — 킬 스위치, 주문 상한 $25,000, 집중도 한도, 일손실 -1.09% today, 낙폭, 연속 거부 0 in a row. 아래 왼쪽 Open Positions 가 방금 생긴 포지션(0.001143519 BTC, 진입 $85,559.67)이고, 오른쪽 Recent Orders 맨 윗줄이 이 글에서 낸 주문입니다. 판단이 주문이 된 자리가 여기입니다. 두 번째 줄의 NVDA · canceled 는 미국장이 닫힌 시각에 낸 주식 주문이 어떻게 되는지를 보여 줍니다.
마지막 줄이 중요합니다. 도구는 경고를 두 줄 돌려줬습니다.
"크립토 자산에는 bracket/OTO 보호 주문이 지원되지 않습니다. 통제는 자문으로만 남습니다." "브로커 손절·익절 주문이 제출되지 않았습니다." (그 도구의 출력)
에이전트가 "손절 82,400" 이라고 아무리 또박또박 적어도, 크립토에서는 브로커에 손절이 존재하지 않습니다. 미국 주식이라면 진입과 함께 실제 보호 주문으로 나갑니다. 도구가 그 차이를 경고로 알려 준다는 점은 칭찬할 만합니다.
덧붙여, 자동 주문은 꺼진 채로 출고됩니다. 분석만 도는 게 기본이고, 실행은 따로 켜야 합니다.
5. weltiq 로 옮기기 — 통합 실험
같은 판단을 weltiq 모의 계좌에도 놓아 봤습니다. 목적은 하나입니다. 같은 문장에서 나온 같은 숫자가 두 몸에서 어떻게 다르게 움직이는가.
weltiq 준비 — 가입({{CTA_URL}}) → 마이 → 연결 관리에서 키 발급 → 아래 세 번. (도구 출력을 이렇게 직접 규칙으로 쓰는 것은 유료 기능입니다. 무료 계정은 템플릿을 고르는 방식입니다.)
- 규칙 문서로 옮깁니다. 도구가 낸 진입 구간·손절·목표 세 숫자를 그대로 씁니다.
(종가 ≥ 84,900) 그리고 (종가 ≤ 85,300)이면 롱 진입, 손절 82,400, 목표 87,385. - 근거를 씁니다. weltiq 는 근거 없는 규칙을 받지 않습니다. 이 규칙이 왜 돈을 벌려 하는지, 무엇이 깨지면 전제가 틀린 것인지를 사람 말로 적어야 통과합니다.
- 중지 조건과 함께 슬롯을 만듭니다. 최대 손실과 최대 가동 시간을 안 적으면 거절됩니다. 수량은 그 도구가 쓴 "초기 손절이 자본의 0.5% 를 위험에 두게" 를 그대로 적용해 0.0185 BTC 로 계산했습니다 — 이 한 줄은 그 도구의 출력이 아니라 필자의 산수입니다.
슬롯을 47분 동안 지켜봤습니다. 결과는 체결 0건입니다. 그 도구가 지정한 진입 구간은 84,900~85,300 인데 그 47분 동안 실제 가격은 85,427~85,705 에 머물렀습니다 — 최저가가 구간 상단보다 127 위입니다.
이게 이 절의 결과입니다. 같은 판단이 알파카에서는 즉시 $85,559.67 에 체결됐고, weltiq 에서는 47분 동안 한 번도 사지 않았습니다. 그 도구가 자기 입으로 "두 트리거 사이에서는 진입하지 마라"고 쓴 그 구간을, 한쪽은 지켰고 한쪽은 지키지 않았습니다. (슬롯은 확인 뒤 정지·삭제했습니다.)
옮겨진 것과 안 옮겨진 것
| 도구가 낸 것 | weltiq 규칙 |
|---|---|
| 방향 · 진입 구간 · 손절 · 1차 목표 | 그대로 들어갑니다 |
| "VWAP·피벗 탈환과 안정화 후" · "그다음 추격 청산" · 2차 목표 90,000 | 못 옮깁니다. 규칙으로 쓸 수 있는 조건이 아니거나, 칸이 하나뿐입니다 |
| 무효화 조건 · 보유 기간 · 확신도 | 담을 칸이 없어 근거 문서에 문장으로만 남습니다 |
그리고 이 편의 본론입니다.
| 알파카 쪽 (도구가 직접) | weltiq 쪽 | |
|---|---|---|
| 진입 | 시장가 즉시. BUY 면 바로 삽니다 | 조건 충족까지 기다립니다. 도구가 쓴 구간을 확정 봉으로 확인해야 삽니다 |
| 수수료 | 코인에서 0.2201% 차감 (직접 확인) | 체결 내역에 fee 로 분리 기록 (문서 확인 — 이번엔 체결이 없어 실측 못 함) |
| 손절 82,400 | 브로커에 안 들어감 → 자문 (직접 확인) | 규칙의 손절로 엔진이 집행 (문서 확인 — 진입이 없어 발동 기회 없음) |
| 중지 조건 | 계좌 단위 서킷브레이커 | 슬롯 단위 최대 손실·최대 가동 시간. 없으면 문서 자체가 거절 |
같은 숫자가 한쪽에서는 즉시 체결이 되고 다른 쪽에서는 대기 조건이 됩니다. 차이를 만든 것은
판단이 아니라 실행 방식입니다. 이 도구의 실행 계약에는 주문 종류가 market 하나뿐이고 진입
조건은 자유 텍스트 칸에만 있어서 실행기가 진입 조건을 읽지 않습니다.
6. 써 보니
좋은 것
- 판단에서 체결까지 한 몸입니다. 12편의 상류에서 사람이 손으로 옮겨야 했던 구간이 없습니다.
- 레벨을 냅니다. 손절·목표가 절대 가격으로 나오고, "지지선 아래" 같은 정성 문구나 "진입가 대비 8%" 같은 상대값은 일부러 버립니다.
- 판단과 주문 사이에 LLM 이 아닌 층이 있습니다. 주문 상한·집중도·서킷브레이커·킬 스위치가 전부 산술식입니다.
- 두 시장이 같은 파이프라인입니다. 주식과 비트코인 현물이 같은 프롬프트·스키마를 씁니다.
아쉬운 것
- OpenAI 이외 제공자에서 도구 호출이 통째로 사라집니다(2절). 에러 없이 그렇게 됩니다. 고쳐지면 이 문단을 정정하겠습니다.
- 진입 조건이 실행 계약에 없습니다. "조건부로만 진입하라"는 판단이 즉시 시장가 매수가 됩니다.
- 크립토는 브로커 손절이 안 걸립니다(알파카 제약). 경고는 나오지만 안 읽으면 모릅니다.
- 확신도가 high/medium/low 셋뿐이고, 이 6판은 전부 medium 이었습니다.
비용 (실측, 2026-09-22)
| 코드 · 시세 | 0원 (Apache-2.0) · 알파카 무료. Finnhub·FRED·CoinDesk·Reddit 키는 없어도 돕니다 |
| 한 판 | $0.021 · 80~113초 (6판 평균, openai/gpt-5.6-luna, 애널리스트 4종·토론 1라운드) |
7. 다음 행동
이 도구를 제대로 읽으려면 형제 둘을 같이 보는 게 좋습니다. 브로커가 없는 상류는
12편 TradingAgents, 같은 엔진에 바이낸스를 붙인 쪽은 28편
trading-bot-agent 입니다. 이 편의 6판은 브로커만 갈아 끼운 게 아니라 판단 쪽도
달라졌다고 말합니다.
해 볼 것 하나: LLM_PROVIDER 만 바꿔 같은 종목을 두 번 돌려, 손절 레벨이 그대로인지 보세요.
weltiq 준비 1. 가입: {{CTA_URL}} 2. 마이 → 연결 관리에서 키 발급 3.
claude mcp add weltiq --transport http https://weltiq.ai/mcp --header "Authorization: Bearer <키>"도구의 판단을 규칙으로 옮겨 모의로 돌린 기록이 남습니다.