# 31편 AlpacaTradingAgent — 실행 기록

2026-09-22 12:16~13:30 KST. 모든 숫자는 실측. LLM 은 OpenRouter 경유 `openai/gpt-5.6-luna` 고정
(12편 TradingAgents 원본과 같은 모델·같은 설정 — 그래야 나란히 놓을 수 있다).

- 설치: `/private/tmp/tut-src/AlpacaTradingAgent` (venv `.venv`, Python 3.12.13)
- 포트: 9420 (웹 UI). 다른 포트는 쓰지 않았다
- 브로커: Alpaca **페이퍼** (`ALPACA_USE_PAPER=True`, 페이퍼 전용 키). 실계좌 호출 없음
- weltiq: `binance-spot` 슬롯 1개, 쓰고 정지·삭제

---

## 1. 사실 확인

| | 값 | 파일 |
|---|---|---|
| GitHub `huygiatrng/AlpacaTradingAgent` | ★271 · 포크 97 · Apache-2.0 · pushed 2026-09-05 · **`fork: false`** | `raw/01-github-repo.json` |
| 상류 `TauricResearch/TradingAgents` | ★107,991 · 포크 20,672 · Apache-2.0 · pushed 2026-09-18 | `raw/02-github-upstream.json` |
| 형제 `ardhanurfan/trading-bot-agent` (28편) | ★0 · Apache-2.0 · pushed 2026-05-24 · **`fork: false`** | `raw/03-github-sibling.json` |
| 설치 (`uv pip install --no-cache -r requirements.txt`) | **8.58초 · 204 패키지 · venv 770MB** (Python 3.12.13) | 아래 §2 |
| 파이썬 3.14 설치 | 같은 목록이 그대로 설치됨 (임포트·실행은 미확인) | 〃 |

### 1.1 계보 — 셋의 관계 (12편 · 28편 · 31편)

**셋 다 git fork 가 아니다.** `fork: false` 인 독립 저장소인데 패키지 배치가 같다:

| 경로 | TradingAgents | AlpacaTradingAgent | trading-bot-agent |
|---|---:|---:|---:|
| `tradingagents/graph/trading_graph.py` | 672줄 | 596줄 | 612줄 |
| `tradingagents/agents/researchers/bull_researcher.py` | 65줄 | 78줄 | 48줄 |
| `tradingagents/dataflows/interface.py` | 284줄 | **1,803줄** | 173줄 |

브로커로 갈린다 — 상류에는 **주문 코드가 아예 없다**:

```
TradingAgents      grep -ril "alpaca"                          → 0 파일
TradingAgents      grep -ril "submit_order|place_order|TradingClient" → 0 파일
AlpacaTradingAgent tradingagents/dataflows/alpaca_utils.py     → Alpaca 주식+크립토 현물
trading-bot-agent  tradingagents/integrations/binance_executor.py → Binance (기본 testnet)
```

즉 **분석 엔진 하나에 서로 다른 브로커 둘을 붙인 것**이고, 28편과 31편은 형제지 부모-자식이 아니다.
같은 회차에 셋을 세우면 독립 설계 셋이 아니다.

### 1.2 상류 대비 더해진 것 (코드 확인)

| 더해진 것 | 어디 |
|---|---|
| Alpaca 시세·계좌·주문 (주식 + 크립토 **현물**) | `tradingagents/dataflows/alpaca_utils.py` |
| 타입 있는 실행 계약 `TradeIntent` (방향·전환·레벨·수량근거·제약·경고) | `tradingagents/agents/schemas.py` |
| **비LLM 안전층** — 주문 상한·집중도·일손실/낙폭 서킷브레이커·연속거부·킬스위치(파일 플래그) | `tradingagents/safety/guardrails.py` |
| 결정론적 포지션 사이징 (부분 켈리·ATR 손절) | `tradingagents/risk/position_sizing.py` |
| 국면 감지 · 포트폴리오 상관/변동성 사이징 | `tradingagents/regime.py`, `default_config.py` |
| 거시 애널리스트 (FRED) → 애널리스트 5인 | `tradingagents/agents/analysts/macro_analyst.py` |
| 실행 가능 행동(BUY/HOLD/SELL)과 **자문 등급**(Buy…Sell 5단계) 분리 | `schemas.py` `ExecutableAction` vs `AdvisoryRating` |
| 감사 로그(프롬프트·툴콜·상태 전부) | `tradingagents/run_logger.py` |
| 프롬프트를 마크다운 템플릿으로 외부화 | `tradingagents/prompts/templates/` |

---

## 2. 설치와 우회

```
uv venv --python 3.12 .venv
VIRTUAL_ENV=.venv uv pip install --no-cache -r requirements.txt   # 8.58s, 204 packages, 770MB
cp env.sample .env      # ALPACA_API_KEY / ALPACA_SECRET_KEY / ALPACA_USE_PAPER=True
                        # LLM_PROVIDER=openrouter / OPENROUTER_API_KEY
```

### 2.1 막힌 것 셋

| # | 증상 | 원인 (코드) | 우회 |
|---|---|---|---|
| **W1** | **애널리스트 다섯이 도구를 한 번도 안 부른다.** 리포트가 "The analyst did not return a complete technical narrative" 로 시작하고 팀 전체가 빈 종이 위에서 HOLD 를 낸다 | 다섯 애널리스트 노드가 모두 `result.additional_kwargs["tool_calls"]` 로 툴 루프를 돈다(`market_analyst.py:211`, `social_media_analyst.py:102`, `news_analyst.py:134`, `fundamentals_analyst.py:161`, `macro_analyst.py:135`). 그런데 `langchain-openai>=1.x`(저장소가 직접 핀한 버전)는 `.tool_calls` 에만 채운다. **OpenAI 네이티브 경로만** `gpt5_llm.py:472` 에서 손으로 되메운다 → **OpenRouter·Anthropic·Google·xAI·DeepSeek 등 나머지 제공자 전부에서 도구가 죽는다** | `NormalizedChatOpenAI.invoke`(`llm_clients/openai_client.py:11`)에서 `.tool_calls` 를 `gpt5_llm.py` 와 같은 와이어 모양으로 되메움. 한 파일 12줄. 고친 뒤 한 판에 툴콜 15~26개 |
| W2 | `get_stock_news_openai`·`get_fundamentals_openai` 가 26~32초 걸린 뒤 "that period is in the future, and I don't have access to live... data" 를 반환 | OpenAI Responses API 웹검색 전용 도구. OpenRouter 로는 진짜 검색이 안 붙는다 | 우회 없음. **소셜·펀더멘털 애널리스트는 사실상 빈손**으로 돈다. 시장(Alpaca)·뉴스(Google News)는 정상 |
| W3 | Reddit 도구가 "No posts found" | Reddit 앱 자격증명(`praw`) 없음 | 없는 계정은 만들지 않았다 |

**W1 의 증거** — 같은 종목·같은 날짜·같은 모델로 패치 전후:

| | 툴콜 | 시장 리포트 | 최종 |
|---|---:|---|---|
| 패치 전 `raw/s1-nvda.json` | **0** | 734자, "no substantive market data was provided" | HOLD |
| 패치 후 `raw/s2-nvda-patched.json` | **15** | 2,493자, "last close: $227.30 … long near $222.50" | HOLD (손절 217.40 / 목표 228.49) |

방향은 둘 다 HOLD 였지만 **근거의 유무가 완전히 달랐다.** 패치 전 판단은 판단이 아니다.

### 2.2 계정 벽

| 필요 | 없음 → 결과 |
|---|---|
| Finnhub | 뉴스·내부자 지표 없음 (Google News 로 대체됨) |
| FRED | 거시 애널리스트 도구 없음 (웹 UI 판에서는 LLM 상식으로 5,755자 작성) |
| CoinDesk/CryptoCompare | 크립토 뉴스 원천 없음 |
| Reddit | 소셜 원천 없음 |
| OpenAI (진짜 키) | 웹검색 도구 둘 사망 (W2) |

**만들지 않았다.** Alpaca 페이퍼와 OpenRouter 만으로 어디까지 되는지가 이 편의 검증 범위다.

---

## 3. B축 자격 판정

### 3.1 미션과 방법

미션 원문 **"비트코인을 거래해서 수익을 내줘. 그리고 왜 그렇게 했는지 설명해줘"**.

이 도구는 **자유 문장을 받지 않는다.** 입력은 종목 문자열과 날짜다(`propagate(symbol, date)`).
기획 §4.1 이 "종목을 입력받는 방식(TradingAgents)은 인정한다"고 했으므로 미션을
**심볼로 번역해서** 던졌다 — 크립토는 `BTC/USD`, 주식은 `NVDA`. "왜 그렇게 했는지"는 별도로 물을
필요가 없다. 리포트 13개와 최종 결정문이 항상 근거를 쓴다.

**같은 조건 6판**: 애널리스트 4종(market·social·news·fundamentals) · 토론 1라운드 ·
리스크 1라운드 · `allow_shorts=False` · 2026-09-22 · `openai/gpt-5.6-luna`.
**판마다 에이전트 기억을 빈 디렉터리로 새로 만들어**(`agent_memory_dir` 을 판별 경로로) 6판이
서로를 못 보게 했다. 실행기는 `ata_run.py`.

### 3.2 6판 원문 요약

| 판 | 심볼 | 최종 | 확신 | 자문등급 | 손절 | 1차 목표 | 주문의도 | 초 | 입력·출력 토큰 |
|---|---|---|---|---|---:|---:|---|---:|---|
| btc-1 | BTC/USD | **BUY** | medium | Overweight | 82,400 | 87,385 | `market` buy | 80 | 45,345 / 9,204 |
| btc-2 | BTC/USD | HOLD | medium | Hold | 84,800 | 88,500 | `none` | 113 | 41,022 / 8,646 |
| btc-3 | BTC/USD | HOLD | medium | Hold | 84,750 | 88,500 | `none` | 97 | 59,561 / 9,931 |
| nvda-1 | NVDA | HOLD | medium | Hold | 220.92 | 234.00 | `none` | 104 | 53,877 / 8,716 |
| nvda-2 | NVDA | HOLD | medium | Hold | 220.92 | 234.75 | `none` | 98 | 49,029 / 8,545 |
| nvda-3 | NVDA | HOLD | medium | Hold | 220.92 | 234.75 | `none` | 104 | 49,421 / 8,904 |

원문 전체: `raw/btc-{1,2,3}.json` · `raw/nvda-{1,2,3}.json` (리포트 5종 · 토론 상태 ·
`final_trade_intent` 전부 포함).

### 3.3 일곱 항목

**① 판단 형태 — 레벨 + 배분 + 방향. 셋 다 낸다.**

`TradeIntent.risk_controls` 가 `stop_loss_price`·`take_profit_price` 를 **절대 수치**로 낸다.
6판 전부 채워졌다. 텍스트 칸(`entry_guidance`·`invalidation`·`max_position_size`·`time_horizon`)도 항상 있다.

`schemas.py:extract_protective_price` 가 "8% below entry" 같은 **상대값과 "below support" 같은
정성 문구는 None 으로 버린다** — 확실하지 않은 레벨로 보호 주문을 내지 않겠다는 설계다.
그래서 수치 칸이 차 있으면 그건 진짜 절대 레벨이다.

**3회 일관성**: 방향은 BTC 에서 갈렸다(BUY 1 · HOLD 2). **레벨은 놀랄 만큼 일관적이다** —
NVDA 세 판의 손절이 **220.92 로 완전히 동일**했고, 진입 조건 문장에 **228.87 이 세 판 모두**,
225.99 가 두 판에 나왔다. BTC 는 목표 87,385 가 두 판, 88,500 이 두 판.

**② 근거 — 말한다. 원문 인용.**

- btc-1(BUY): *"Multi-timeframe structure and institutional-flow evidence favor continuation, but freshness, quantitative support, and current macro/flow verification are limited. Elevated ~$2,420 ATR and conflicting pullback/outflow risks make an unconditional entry inappropriate."*
- nvda-3(HOLD): *"the proposed pullback setup offers only about 1.7:1 reward-to-risk, below the required minimum."*
- btc-1 진입: *"Conditional only: $84,900-$85,300 after VWAP/pivot reclaim and stabilization, or high-volume close above $87,385 with successful retest. Do not enter between triggers."*

**거절 이유가 수치다.** "느낌이 안 좋다"가 아니라 "보상/위험 1.7:1, 기준 미달"이다.

**③ 시장 — 현물이다. 선물 코드 없음.**

```
grep -rn "futures|fapi|UMFutures" --include=*.py .
→ 전부 concurrent.futures (파이썬 스레드풀). 거래소 선물 호출 0건
```

크립토 경로는 `alpaca_utils.py:181 get_alpaca_crypto_client() → CryptoHistoricalDataClient`,
`:445 client.get_crypto_bars(...)`. 주문은 `TradingClient` 현물.
`schemas.py` 가 직접 적어 둔다: *"Crypto short exposure is not supported by Alpaca spot trading."*
주식은 `get_stock_bars` + `us_equity`. **판단을 현물에 그대로 적용할 수 있다.**

**④ `both` — 확인. 두 시장 모두 같은 파이프라인으로 돌았다.**

같은 그래프·같은 프롬프트·같은 스키마로 BTC/USD 3판, NVDA 3판이 전부 완주했고 레벨까지 냈다.
자산군 분기는 `asset_class = "crypto" if "/" in symbol else "equity"` 한 줄
(`schemas.py:build_trade_intent_from_risk_decision`)과 데이터 클라이언트 선택뿐이다.
**기획 §4.6 8회차(교차 회차)를 열 수 있다.** 다만 시장별 차이가 셋 있다:

| | 크립토 | 주식 |
|---|---|---|
| 보호 주문(bracket/OTO) | **불가** — Alpaca 가 크립토에 안 준다. 손절·목표는 자문으로만 남는다 | 가능 (`protective_bracket_orders_enabled`) |
| time in force | `gtc` | `day` |
| 장 시간 | 24시간 | `requires_open_market=True` — 장 닫히면 주문이 취소된다 |

**⑤ 반복 호출 — 된다. 단 CLI 플래그로는 안 된다.**

`cli/main.py` 의 유일한 명령 `analyze()`(1106줄)는 **인자를 받지 않는** 대화형이다.
회차마다 다시 부르려면 파이썬 API 를 쓴다:

```python
from tradingagents.graph.trading_graph import TradingAgentsGraph
g = TradingAgentsGraph(config=cfg, selected_analysts=["market","social","news","fundamentals"])
state, decision = g.propagate("BTC/USD", "2026-09-22")   # decision = "BUY"|"HOLD"|"SELL"
intent = state["final_trade_intent"]                      # 레벨·수량근거가 여기 있다
```

한 줄 명령: `.venv/bin/python ata_run.py <run_id> <SYMBOL> <YYYY-MM-DD>` (이 기록의 `ata_run.py`).
웹 UI 에는 N시간마다 재분석하는 스케줄러가 따로 있다.

**⑥ `confidence` — 낸다. 단 3단계다.**

`RiskDecision.confidence` · `TradeIntent.confidence` 가 `high|medium|low` 문자열이다.
**6판 전부 `medium`.** 0~100 숫자가 아니므로 M12 확신도 보정은 **구간 셋짜리 거친 곡선**밖에
못 그린다. 게다가 이번 6판은 전부 같은 값이라 분산이 0 이다 — 보정 대상으로 쓰려면 미션을 더
넓게(변동성 큰 구간·뉴스 쇼크) 걸어 값이 갈리는지부터 봐야 한다.

**⑦ 결론 — 참가 가능.**

기획 §4.3b 의 사전 판정("추가 후보, 검증 대기, `both` 후보")과 **어긋난 것은 없고, 셋이 올라갔다**:

1. `both` 가 **확정**됐다 (사전 판정은 "검증 대기")
2. 판단 형태가 "방향만일 가능성"으로 적혀 있었으나 **레벨·배분까지 낸다.** 후보 풀에서 레벨을
   내는 축에 속한다
3. 사전 판정에 없던 것 — **판단과 주문 사이에 비LLM 게이트가 이미 있다**(§1.2 안전층).
   B축에서 "도구가 자기 한도를 지키는가"를 따로 잴 수 있는 첫 참가자다

조건 하나: **회차마다 커밋을 고정하고 W1 패치 여부를 기록해야 한다.** 패치 안 한 판은
"도구의 판단"이 아니라 "빈 종이 위의 판단"이라 같은 회차에 섞으면 안 된다.

### 3.4 12편 원본과의 대조 — 매수 편향이 뒤집혔다

12편은 같은 모델(`openai/gpt-5.6-luna`)·같은 설정(애널리스트 4종·토론 1·리스크 1)으로 원본
TradingAgents 를 13판 돌렸다. 이 편은 같은 조건으로 확장판을 6판 돌렸다.

| | 12편 원본 (13판) | 31편 확장판 (6판) |
|---|---:|---:|
| 트레이더/최종 **SELL** | **13 / 13** | **0 / 6** |
| **HOLD** | **0 / 13** | **5 / 6** |
| **BUY · Overweight** | **0 / 13** | **1 / 6** (btc-1) |
| 자문 등급 Underweight | 6 / 13 | 0 / 6 |

**완전한 반전이다.** 표본이 13 대 6 이고 종목·날짜도 달라 인과를 주장할 수는 없지만,
프롬프트에 **정반대 지시가 들어 있다**는 사실은 코드로 확인된다:

- 원본 `agents/managers/portfolio_manager.py:69` — *"The risk debate always contains conflicting stances; deciding which is stronger is the job, so conflict alone is not a reason to Hold. Commit to the stronger case…"* (같은 문장이 `research_manager.py:39` 에도)
- 확장판 `prompts/templates/managers/risk_manager.md` — *"Treat high contradiction or low freshness scores as reasons to reduce size, wait for confirmation, or choose HOLD/NEUTRAL."*

**원본은 관망을 벌주고, 확장판은 관망을 허가한다.** 확장판이 그 판단을 실제 주문으로 보내기
때문에 그렇게 바꿔야 했을 것이다 — 분석만 하는 도구와 주문까지 내는 도구의 차이가
프롬프트 한 문단에 드러나 있다.

또 하나: 확장판은 **자문 등급(5단계)과 실행 행동(BUY/HOLD/SELL)을 분리**했다
(`schemas.py` `AdvisoryRating` vs `ExecutableAction`, "Advisory ratings … never directly trigger
Alpaca orders"). 12편에서 "Underweight 6 · Sell 7" 이 곧 매도로 읽힌 혼선이 구조적으로 막혀 있다.

---

## 4. Alpaca 주문 연결 — 한 사이클

btc-1 의 `TradeIntent` 를 웹 UI 의 실행 버튼이 부르는 **바로 그 함수**
(`AlpacaUtils.execute_trade_intent`, `webui/components/analysis.py:145`)에 그대로 넘겼다.
실행기는 `ata_exec.py`, 결과 원문은 `raw/btc-1-alpaca-exec.json`.

| | 값 |
|---|---|
| 주문 | `notional 100` USD, market, gtc, `position_intent: buy_to_open` |
| 주문 id | `e0944ea0-961f-4d75-858c-813649e1bb0a` |
| 체결 | **0.001146041 BTC @ $85,559.67**, `filled_at 2026-09-22T03:35:36.504Z` (제출 후 **1.5ms**) |
| 포지션에 남은 수량 | **0.001143519 BTC** |
| 차 | 0.000002522 BTC = **체결 수량의 0.2201%** — Alpaca 가 수수료를 기초자산으로 떼 간 몫 |
| 보호 주문 | **안 나갔다.** `protective_order_status: "advisory_only"` |

경고 원문 두 줄 (`intent_warnings`):

> "Protective bracket/OTO orders are not supported for crypto assets; controls remain advisory."
> "Broker stop-loss/take-profit orders were not submitted; controls remain advisory."

**이게 이 도구의 가장 중요한 한 줄이다.** 에이전트가 "손절 82,400" 이라고 적어도 크립토에서는
**브로커에 손절이 존재하지 않는다.** 사람이 직접 걸거나, 다음 분석 주기에 SELL 이 나오기를
기다리는 수밖에 없다. 도구가 그 사실을 숨기지 않고 경고로 되돌려 준다는 점은 좋다.

같은 실행에서 확인한 자동 실행 기본값: 웹 UI 로그가 `trade_enabled: False` — **자동 주문은
꺼진 채로 출고된다.** 켜야 나간다.

---

## 5. weltiq 로 옮기기

`to_weltiq.py`. 도구 출력에서 옮긴 것과 옮기지 않은 것을 갈라 적는다.

| 도구 출력 | weltiq 문서 | 비고 |
|---|---|---|
| Action BUY | `entry … side long` | 그대로 |
| Entry Guidance `$84,900-$85,300` | `(close ≥ 84900) AND (close ≤ 85300)` | 구간을 `and` 로. **"VWAP/pivot reclaim and stabilization" 은 옮기지 못했다** |
| Stop Loss `82,400` | `levels.stop = 82400` | 그대로 |
| Take Profit `87,385` | `levels.limit = 87385` | 1차 목표만. "then trail" · 2차 90,000 은 옮기지 못했다 |
| Max Position Size "0.5% of equity" | `qty 0.0185` | **필자 계산.** 10,000 × 0.005 = 50 / (85,100 − 82,400) = 0.0185 |
| Invalidation / Time Horizon / 자문 등급 / confidence | **칸이 없다** | 근거 문서에 문장으로만 남김 |

- `POST /v1/strategy-rules` → `doc_hash b69fb92b427050f869269275a834b89fdda78948aa6acc7f7aaa35553cf5c706`, 422 없음 (`raw/70-rules-doc.json`, `raw/71-rules-submit.json`)
- `POST /v1/ai/thesis` → 첫 시도 422 (`thesis` 로 보냄, 필드명은 `text`) → ok (`raw/72-thesis.txt`, `raw/72-thesis.json`)
- `POST /v1/containers` → 첫 시도 422 (`stop` 객체를 중첩해서 보냄; 실제는 평탄한 `stop_max_loss`·`stop_after_hours`, `stop_on_worker_failure` 는 받지 않음) → 생성
- 슬롯 `ct_2ccffcb4eb7d35a1` · `dep_pa_4047661f6de0cfe4` · binance-spot · BTCUSDT · 1m · balance 10,000 · `stop.max_loss 200` · `after_hours 2` · **`attached: true`**

**미리 알려진 벽 셋 중 둘은 걸리지 않았다.**

| 벽 | 결과 |
|---|---|
| 엔진이 `price_level`/`price_exit` 를 모름 | 처음부터 `slot_rules` 로 써서 **피했다** |
| `max_order_qty` 기본 1 | **안 걸렸다.** 크립토라 수량이 0.0185 다. 이 벽은 주수 단위 시장에서만 문제가 된다 |
| us-equity 숏 불가 | **해당 없음.** 이 도구는 `allow_shorts=False` 로 돌렸고 6판 다 SHORT 가 안 나왔다. **SHORT 가 나왔다면 받지 못했을 것**이고, 그때는 롱으로 바꾸지 않고 "받지 못했다"로 적었을 것이다 |

### 5.1 슬롯 관측

`raw/74-slot-poll.jsonl` — 관측 47분(03:38:22~04:25:25 UTC, 1분 간격 48회).
BTCUSDT 종가가 진입 구간 위에서만 움직였다.

| | |
|---|---|
| BTCUSDT 종가 범위 | **85,427.33 ~ 85,705.45** |
| 도구가 지정한 진입 구간 | 84,900 ~ 85,300 |
| 구간까지 남은 거리 (최저가 기준) | **127.33** |
| 진입 규칙 평가 | `(close ≥ 84900)` 참 · `(close ≤ 85300)` **거짓** — 매 봉 기록됨 |
| 체결 | **0건.** 포지션 없음, 손익 0 |

**체결이 안 난 것이 이 대조의 결과다.** 같은 판단이 알파카에서는 03:35:36 에 $85,559.67 로
즉시 체결됐고, weltiq 에서는 47분 동안 한 번도 사지 않았다. 그 도구가 자기 입으로
"두 트리거 사이에서는 진입하지 마라"고 쓴 그 구간을, 한쪽은 지키고 한쪽은 지키지 않았다.

슬롯은 관측 뒤 **정지·삭제**했다 (`raw/76-container-stop-delete.json`, `deleted: true`).
돌고 있던 다른 슬롯들은 조회도 하지 않았다. 엔진·웹 프로세스는 재시작하지 않았다.


### 5.2 양쪽 페이퍼 대조 — 같은 판단, 다른 두 몸

| | Alpaca 페이퍼 (도구 쪽) | weltiq 페이퍼 |
|---|---|---|
| 진입 방식 | **시장가 즉시.** 판단이 BUY 면 바로 산다 | **조건 충족까지 기다린다.** 그 도구가 쓴 84,900~85,300 을 확정 봉으로 확인해야 산다 |
| 체결가 | $85,559.67 (03:35:36) | **체결 0건** — §5.1 |
| 수수료 | 기초자산에서 0.2201% 차감 (0.000002522 BTC) — **직접 확인** | 체결 내역의 `fee` 로 분리 기록 — **문서 확인**(체결이 없어 실측 못 함) |
| 손절 82,400 | **브로커에 안 들어간다** (크립토 bracket 불가) → 자문 — 직접 확인 | 규칙 문서의 `levels.stop` 으로 엔진이 집행 — **문서 확인**(진입이 없어 발동 기회 없음) |
| 중지 조건 | 계좌 단위 서킷브레이커(일손실 10%·낙폭 15%) — 이 판에는 안 걸림 | 슬롯 단위 `max_loss 200` · `after_hours 2` · 워커 죽으면 청산. **없으면 문서가 422 로 거절된다** |

**같은 문장에서 나온 같은 숫자가 한쪽에서는 즉시 체결이 되고 다른 쪽에서는 대기 조건이 된다.**
차이를 만든 것은 판단이 아니라 그 도구의 실행 방식 — `planned_actions` 가 `order_type: "market"`
하나뿐이고 진입 조건은 `required_controls` 텍스트에만 있어서, **실행기가 진입 조건을 읽지 않는다.**
`extract_protective_price` 가 손절·목표는 숫자로 뽑아내지만 진입 구간은 뽑는 함수가 없다.

---

## 6. 결함 목록

### 저쪽 (AlpacaTradingAgent)

| # | 결함 | 영향 |
|---|---|---|
| A1 | **OpenAI 이외 제공자에서 애널리스트 툴콜이 전부 소실** (§2.1 W1) | 치명적. 데이터 없이 판단이 나오는데 **에러가 안 난다.** 리포트 문구를 읽지 않으면 모른다 |
| A2 | 진입 조건이 `TradeIntent` 의 구조화 칸에 없다 — `required_controls` 자유 텍스트에만 | 실행기가 "조건부 진입"을 지키지 못하고 즉시 시장가로 산다. 에이전트가 "Do not enter between triggers" 라고 쓴 판에서도 그렇다 |
| A3 | 크립토 보호 주문 불가(Alpaca 제약)인데 손절·목표를 계속 낸다 | 경고는 나오지만, 경고를 안 읽으면 손절이 걸린 줄 안다 |
| A4 | `get_stock_news_openai`·`get_fundamentals_openai` 가 실패 대신 **모델의 거절문**을 리포트에 넣는다 | 애널리스트가 "사회적 신호가 없다"고 결론 내리는데 실은 도구가 안 붙은 것 |

### 이쪽 (weltiq) — 이번에 걸린 것

| # | |
|---|---|
| B1 | `/v1/ai/thesis` 의 필드명이 `text` 인데 오류 메시지만으로는 무엇을 넣어야 하는지 모른다 |
| B2 | `/v1/containers` 의 중지 조건이 평탄한 키(`stop_max_loss`·`stop_after_hours`)인데 조회 응답은 중첩 객체(`stop.max_loss`)로 돌려준다 — 받은 모양 그대로 다시 보내면 422 |
| B3 | 규칙 문서에 `invalidation`·`time_horizon`·`confidence` 를 담을 칸이 없다. 도구가 내는 것을 못 받는다 |

---

## 7. 비용

| | |
|---|---|
| OpenRouter 시작 | `usage 32.910295697` (`raw/00-openrouter-start.json`) |
| 종료 | `usage 38.432610677` (`raw/99-openrouter-end.json`) |
| **미션 6판 (응답 usage 합산, 정확값)** | **$0.1244** — 입력 298,255 · 출력 53,946 토큰, 판당 평균 **$0.0207** |
| 계측 없는 4판 | §2.1 패치 전후 대조 2판(토큰 집계 붙이기 전) · 웹 UI 시연 2판(웹 UI 경로). 같은 조건 평균으로 환산하면 **+$0.08 안팎** |
| **합계 (추정 포함)** | **$0.2 내외** |
| 상한 | $1.5 |

같은 키를 다른 작업자 열이 동시에 쓰고 있어 계정 집계 증가분은 이 작업의 지출이 아니다.
그래서 **응답마다 돌려주는 `usage_metadata` 를 직접 합산**했다 (`ata_run.py` 의 `USAGE`).
가격은 OpenRouter 고시 `openai/gpt-5.6-luna` = 입력 $0.20/M · 출력 $1.20/M.

---

## 8. 파일

| | |
|---|---|
| `ata_run.py` | 한 판 실행 — W1 우회 패치 · 토큰 합산 포함 |
| `ata_exec.py` | 저장된 `TradeIntent` → Alpaca 페이퍼 주문 |
| `to_weltiq.py` | 도구 출력 → weltiq 규칙 문서 |
| `api.py` | weltiq API 호출 |
| `raw/btc-{1,2,3}.json` · `raw/nvda-{1,2,3}.json` | 6판 원문 (리포트·토론·`TradeIntent`) |
| `raw/s1-nvda.json` · `raw/s2-nvda-patched.json` | W1 패치 전후 대조 |
| `raw/btc-1-alpaca-exec.json` | Alpaca 주문 결과 |
| `raw/70~74` | weltiq 규칙 문서 · 근거 · 슬롯 · 관측 |
| `shots/` | 캡처 |
