# 23편 검증 기록 — ryan-yuuu/crypto-trading-arena

- 검증일 2026-09-22 · macOS 15 (Darwin 25.6) · Apple Silicon · Python 3.13.12 (uv) · 도커 없음
- 저장소 `ryan-yuuu/crypto-trading-arena` · 128★ · fork 40 · Apache-2.0 · 마지막 푸시 **2026-07-08** · 이슈 0 (`01-github-repo.json`)
- 의존 SDK `calfkit>=0.12.6,<0.13`
- 설치 경로 `/private/tmp/tut-src/crypto-trading-arena` · 포트 9242(Kafka) · 9243(Kafka 컨트롤러) · 9245(캡처용 정적 서버)
- 독자판 `docs/tutorials/a/23-crypto-trading-arena.md`

---

## 1. 무엇을 돌렸나 — 전체표

| # | 항목 | 돌렸나 | 결과 | 원문 |
|---|---|---|---|---|
| 1 | 설치 (`uv sync`) | ✅ | 의존 78개, 실패 없음 | — |
| 2 | 브로커 기동 (도커 대체) | ✅ | Homebrew Kafka 4.3.1 KRaft 단독 노드, 9242 | `server.properties` (증적 디렉터리) |
| 3 | 저장소 자체 테스트 | ✅ | **139 passed · 7 xfailed · 10 deselected** (1.09s) | `raw/05-pytest.txt` |
| 4 | Binance 커넥터 기동 | ✅ | BTCUSDT·ETHUSDT·SOLUSDT, 1m/5m/15m 봉 | `logs/connector-*.log` |
| 5 | 도구·대시보드 기동 | ✅ | Rich TUI, 페이퍼 지갑 4개 | `shots/01`, `shots/03` |
| 6 | 에이전트 4종 동시 기동 | ✅ | gpt-5-nano · gemini-3.1-flash-lite · deepseek-v3.2 · claude-haiku-4.5 | `raw/73-agent-output-all.jsonl` |
| 7 | 수수료 60bp 실시간 판 | ✅ | 11틱 · 체결 12 · 2/4 에이전트만 거래 | `raw/70-live-fee60-trades.csv` |
| 8 | 수수료 0bp 실시간 판 | ✅ | 11틱 · 체결 43 · 4/4 에이전트 거래 | `raw/72-live-fee0-trades.csv` |
| 9 | 수수료 A/B 오프라인 재생 (같은 프롬프트 60회) | ✅ | 3모델 × 2조건 × 10회 | `raw/40-ab-fee-runs.json` · `41-ab-summary.txt` |
| 10 | 미션 3회 | ✅ | claude-haiku-4.5, 같은 조건 | `raw/30-mission-3runs.json` |
| 11 | 수량 소수 1자리 제한 확인 | ✅ | 0.01·0.05·0.09·0.15 거절 / 0.1·0.2 통과 | `raw/20-qty-probe.txt` |
| 12 | 부동소수 누적(B6) 재현 | ✅ | 0.1+0.1+0.7 → 0.8999999999999999 → 0.9 매도 거절 | `raw/20-qty-probe.txt` |
| 13 | weltiq 슬롯 1개 (생성→체결→정지→삭제) | ✅ | `ct_0698aeed14efa7ea` · 체결 2 | `60`~`69-*.json` |
| 14 | Coinbase 커넥터 | ❌ | 안 돌렸다 — 저장소 자신이 B1(봉 없음)을 미해결로 적어 놓았고, 이 편의 완료 조건이 "같은 봉"이라 Binance 만 썼다 | — |
| 15 | 클라우드 브로커 | ❌ | 계정 발급이 필요하고 가입하지 않는다 | — |
| 16 | 저장소 내부 수익률을 weltiq 성적으로 환산 | ❌ | 의도적으로 안 했다 — §7 참조 | — |

프로세스는 전부 이 작업이 띄운 것만 죽였다. 엔진 pid 55526 · 웹 pid 48127·67039·71349 · 감시 pid 는 건드리지 않았고,
돌고 있던 슬롯 12개는 목록 조회만 했다.

---

## 2. 설치 — 도커 없이 세우기

README 는 Kafka 브로커를 도커로 띄우라고 한다(`calfkit-broker` · `make dev-up`). 이 기계에 도커가 없다.

```
docker not found · docker-compose not found · java not found
```

대체 경로: Homebrew 의 Kafka 4.3.1(KRaft, 주키퍼 불필요)을 쓴다. openjdk 27 이 의존으로 함께 깔린다(약 530MB).

```bash
brew install kafka           # openjdk 27 포함, 4분
# server.properties: process.roles=broker,controller / node.id=1
#   controller.quorum.bootstrap.servers=localhost:9243
#   listeners=PLAINTEXT://localhost:9242,CONTROLLER://localhost:9243
kafka-storage format -t $(kafka-storage random-uuid) -c server.properties --standalone
kafka-server-start server.properties
```

막힌 곳 둘:

1. `controller.quorum.voters` 와 `--standalone` 을 같이 쓰면 포맷이 거절된다(Kafka 4.x 동적 쿼럼).
   `controller.quorum.bootstrap.servers` 로 바꿔야 한다.
2. **커넥터가 `.env` 를 안 읽는다.** `deploy/agent.py`·`deploy/tools_and_dashboard.py`·`deploy/response_viewer.py`
   는 `load_dotenv()` 를 부르는데 **`exchanges/binance.py`·`exchanges/coinbase.py` 는 안 부른다.**
   커넥터는 LLM 키가 전혀 필요 없는데도 `config.json` 의 `"${OPENROUTER_API_KEY}"` 를 해석하려다 죽는다.

```
ValueError: Environment variable 'OPENROUTER_API_KEY' is not set
  (referenced at root.llm_providers.openrouter.api_key)
  exchanges/binance.py:538 → config.load_config_strict → resolve_env_vars
```

저장소의 `docs/REVIEW_FINDINGS.md` **R3** 이 인접한 문제(`get_default_symbols` 가 키 없으면 죽는다)를 적어 놓았지만,
**커넥터 기동 전체가 같은 이유로 죽는 것은 R3 에 없다.** 우회: 실행 전에 `export OPENROUTER_API_KEY=…`.

---

## 3. 자료가 현물인가 — 코드 확인

```
exchanges/binance.py:41  wss://stream.binance.com:9443
exchanges/binance.py:44  https://api.binance.com
exchanges/binance.py:147 GET /api/v3/klines
exchanges/binance.py:152 GET /api/v3/ticker/24hr
```

`grep -rn 'fapi|UMFutures|futures_|dapi' --include='*.py' .` → **0건.** 선물 경로가 없다.
페이퍼 지갑도 롱 전용이다 — 보유하지 않은 수량 매도는 `Insufficient holdings` 로 거절된다(실측, §5 미션 1회차).

**판정: Binance 현물. 자격 ③ 충족.**

Coinbase 쪽은 저장소 자신이 **B1(High·open)** 으로 "커넥터가 `CandleBook` 을 안 붙여서 봉이 아예 안 간다"고 적어 놓았고
`tests/test_exchanges.py` 에 strict xfail 로 고정해 뒀다. 그래서 "같은 봉을 본다"가 성립하는 것은 **Binance 커넥터뿐**이다.

---

## 4. 수수료가 판단에 들어가는가 — 이 편의 본론

### 4.1 어디에 들어가는가 (기획 §4.3 의 서술 정정)

기획 표는 "`arena/strategies.py` 의 프롬프트에 수수료를 판단 입력으로 넣는다"고 적었다. **소스는 다르다.**

- `arena/strategies.py` 의 전략 프롬프트 4종(default·momentum·brainrot·scalper)에는 **수수료라는 단어가 없다.**
- 수수료 문장은 `arena/fees.py::FeeModel.disclosure_prompt()` 가 만들고,
  `exchanges/binance.py::_publish_latest()` 가 **매 틱 사용자 메시지 맨 앞에** 끼워 넣는다.

실제로 나간 문장(`raw/10-captured-prompt.json`, Kafka `agent_router.input` 에서 그대로 떠 온 것):

```
A taker fee of 60 bps (0.60%) is charged on every fill (both buys and sells).
Factor this into your sizing and P&L targets — a round-trip costs 120 bps.
```

0bp 로 바꾸면 같은 자리에 `Trading is fee-free in this deployment.` 가 들어간다.

곁가지 하나: `arena/tools.py::execute_trade` 의 독스트링은 **요율과 무관하게 항상** "A taker fee (set by the
deployment) is charged on every fill" 이라고 말한다. 즉 0bp 판에서도 도구 설명서에는 수수료가 있다고 적혀 있다.
두 경로가 어긋나며, 아래 A/B 에서 "0bp 인데도 조심하는" 응답이 나오는 이유일 수 있다.

### 4.2 오프라인 A/B — 같은 봉, 같은 시각, 수수료 한 줄만 다르게

실시간 두 판은 **시장 분(分)이 다르므로** 그것만으로는 수수료 효과라고 말할 수 없다. 그래서 커넥터가 실제로 보낸
프롬프트 1개(10,160바이트, 12:15 틱)를 떠서, **수수료 문장만 바꿔** 같은 시스템 프롬프트·같은 도구 스키마로 재생했다.
계좌는 매 회차 새 `AccountStore`(저장소 자신의 클래스) 로 $100,000 에서 시작한다. 하네스는 `raw/ab_fee.py`.

| 모델 | 60bp 거래한 회차 | 0bp 거래한 회차 | 60bp 체결액 | 0bp 체결액 |
|---|---:|---:|---:|---:|
| deepseek-v3.2 | **1 / 10** | **5 / 10** | $8,553 | $39,314 |
| gemini-3.1-flash-lite | 8 / 10 | 6 / 10 | $147,839 | $413,915 |
| gpt-5-nano | **0 / 10** | **2 / 10** | $0 | $9,251 |
| **합계** | **9 / 30 (30%)** | **13 / 30 (43%)** | **$156,392** | **$462,480 (3.0×)** |

**결론이 달라진다 — 단, 모델마다 다르다.** deepseek 와 gpt-5-nano 는 수수료를 없애면 거래 빈도가 오르고,
gemini-flash-lite 는 **반대로 움직였다**(8→6). 즉 이 도구에서 "수수료가 판단을 바꾼다"는 **모델 속성**이지
도구 속성이 아니다. 체결액 합계(3.0×)는 세 모델 모두 같은 방향이다.

근거 인용 — deepseek-v3.2 가 수수료를 명시적으로 계산에 넣은 대목(실시간 판, `shots/02-viewer.png`):

> All three major cryptocurrencies are showing bearish momentum across timeframes. **With a 60 bps taker fee
> (0.6%), I need to be cautious about entering new positions** unless I see a clear reversal signal or oversold
> conditions.

> Given that I already have a SOL position at a slight loss ($117.19 avg cost vs current $116.40) … **The 0.6%
> fee makes holding a losing position more expensive if the trend continues downward.**

### 4.3 실시간 A/B — 같은 에이전트 4종, 각각 11틱

| | 수수료 60bp | 수수료 0bp |
|---|---|---|
| 구간 | 12:14~12:25 | 12:25~12:37 |
| 시장 데이터 틱 | 11 | 11 |
| 체결 | **12** | **43** |
| 거래한 에이전트 | **2 / 4** (gemini 10 · deepseek 2) | **4 / 4** (gemini 24 · deepseek 9 · haiku 7 · gpt-5-nano 3) |
| 체결 명목액 | $169,332 | **$1,439,744 (8.5×)** |
| 낸 수수료 | $1,015.99 | $0.00 |

60bp 판에서 **한 번도 주문하지 않았던 haiku-4.5 와 gpt-5-nano 가 0bp 판에서는 둘 다 주문했다.**
같은 모델·같은 전략 프롬프트·같은 심볼·같은 11틱이고 바뀐 것은 요율 한 줄이다.
다만 두 판의 시장 구간이 다르므로 이 표 단독으로는 인과가 아니고, §4.2 의 오프라인 재생이 인과 쪽 근거다.

또 하나: 60bp 판에서 gemini-flash-lite 는 10회 체결로 **$1,004 를 수수료로 냈고** 총자산이 $100,000 → $99,065 가 됐다.
수수료를 못 이긴 것이 아니라 수수료를 안 본 것에 가깝다.

### 4.4 요율 기본값이 스트리밍하는 거래소와 안 맞는다

`config.json` 기본 `taker_bps: 60` 은 Coinbase Advanced Trade 기본 티어다. 그런데 이 편에서 봉을 준 곳은
**Binance 현물(VIP0 = 10bp)** 이다. 즉 기본값 그대로 Binance 커넥터를 쓰면 **실제 요율의 6배**를 문다.
README 가 `10 ≈ Binance global VIP 0` 이라고 적어 놓았지만 `config.example.json` 기본값은 60 이다.

같은 왕복 거래(0.1 BTC, $8,548 명목)를 두 원장이 각각 얼마로 계산했는지는 §6 에 있다.

---

## 5. B축 자격 판정

### 5.1 미션 원문과 회수

미션(원문 그대로, 3회 동일):

> 비트코인을 거래해서 수익을 내줘. 그리고 왜 그렇게 했는지 설명해줘

- 모델 `anthropic/claude-haiku-4.5`, 수수료 60bp, 같은 시장 스냅샷, 회차마다 **새 $100,000 계좌**
- 던지는 자리: 이 도구는 `--strategy {default|momentum|brainrot|scalper}` 넷 중 하나만 고를 수 있어
  **임의 미션을 넣는 자리가 CLI 에 없다.** 미션은 시스템 프롬프트 자리에 넣었고(= `arena/strategies.py` 에 키를
  하나 더 넣는 것과 같다), 사용자 메시지는 커넥터가 실제로 보낸 것을 그대로 썼다. 원문 `raw/30-mission-3runs.json`.
- 크립토가 되므로 NVDA 로 바꾸지 않았다.

### 5.2 3회 판단

| 회차 | 방향·종목 | 수량 | 레벨 | 결과 |
|---:|---|---:|---|---|
| 1 | **BTC 매수** | 0.5 BTC ($43,023) | 목표 **85,845** · 무효화 **85,400** | 보유 종료 |
| 2 | **BTC 매수** | 1.1 BTC ($94,650) | 목표 **87,290** · 손절 −2% | 보유 종료 |
| 3 | **BTC 매수** | 1.1 BTC (1.2 는 현금 부족으로 거절 후 축소) | 목표 86,800~87,000 | **같은 가격에 즉시 전량 매도 → −$1,129 (전액 수수료)** |

**판단 형태: 레벨.** 3회 모두 진입(시장가)·목표가·무효화(또는 손절%)를 함께 냈다. 배분도 함께 나온다(현금 대비 43%·95%·95%).

**일관성**: 방향은 3/3 일치(BTC 매수). 수량은 0.5~1.1 로 **2.2배** 벌어졌다. 목표가는 85,845 / 87,290 / 86,800~87,000 —
회차 1만 "가까운 5분봉 고점", 나머지 둘은 "수수료 120bp 를 넘길 만큼"으로 논리가 달랐다.

**3회차의 실패가 이 도구의 성질을 가장 잘 보여 준다.** 자기 글로 "1.5~2% 반등을 기다렸다가 판다"고 써 놓고
같은 호출 안에서 즉시 매도했다. 한 번의 호출이 하나의 에피소드라서 **"기다린다"를 실행할 자리가 도구 안에 없다.**
상주 실행은 "60초마다 처음부터 다시 판단"이지 "계획을 이어서 집행"이 아니다.

### 5.3 근거를 말하는가 — 원문 인용 (1회차)

> 1. **저점 매수 기회** — BTC가 3시간 전 86,478에서 현재 85,532로 하락 / 하지만 최근 13분 동안
>    85,480~85,750 범위에서 안정화 / 이는 **바닥 형성** 신호 → 반등 가능성 높음
> 3. **수수료 최적화** — 왕복 수수료: 120 bps (1.2%) / 0.5 BTC로 적정 포지션 (과도한 레버리지 회피) /
>    현금 50%만 사용 (리스크 관리)

(2회차) > 이유: 왕복 거래 비용(120bps)을 감당하면서도 1-2% 수익이 나도록 계산 … 매도 목표가: $85,532 × 1.021 = **$87,290**

미션을 한국어로 던지자 3회 모두 한국어로 답했다. 근거 항목은 회차마다 3~4개.

### 5.4 반복 호출 진입점

- 있다: `uv run python -m deploy.agent --from-config <이름> --strategy <전략> --bootstrap-servers localhost:9242`
- **단, 일회성 질의 진입점이 아니다.** 프로세스가 뜨면 스트림 구독자가 되어 틱마다 계속 판단한다.
  독립 판단 N개를 받으려면 (a) 프로세스를 N번 다시 띄우거나 (b) 이번 검증처럼 프롬프트를 떠서 재생해야 한다.
  (a) 는 매번 계좌가 초기화되지 않는다는 문제가 있다(계좌는 도구 노드 쪽에 산다).
- 기획 §4.4 D1("N = 같은 미션을 N번 던져 N개 판단")을 이 도구로 지키려면 **재생 하네스가 사실상 필수**다.
  이번에 쓴 것을 `raw/ab_fee.py` 로 남겨 둔다.

### 5.5 confidence

**안 낸다.** 3회 판단 어디에도 0~100 확신도 수치가 없고, 전략 프롬프트에도 요구가 없다.
M12(확신도 보정)를 이 도구에 걸려면 미션 문장에 "판단마다 확신도를 붙여 줘"를 더해야 한다 —
그 순간 시스템 프롬프트를 고치는 것이므로 **참가자 구성이 바뀐다**는 점을 회차 설계에서 정해야 한다.

### 5.6 결론

**참가 가능.** 자격 3조건을 전부 충족한다.

1. 자연어 미션 → 종목·방향: ✅ (3/3 BTC 매수, 수량·레벨 포함)
2. 근거: ✅ (미션의 "왜"에 회차마다 3~4항목으로 답함)
3. 우리 시장: ✅ (Binance **현물**, 선물 경로 없음)

부가 점수: 레벨·조건 ✅ / 유료 자료원 불필요 ✅(Binance 공개 API, LLM 키만) /
CLI 반복 호출 △(상주형, 일회성 진입점 없음) / 최근 3개월 커밋 ❌(마지막 푸시 2026-07-08).

**기획 §4.3b 사전 판정("두 조사가 모두 통과")과 다른 점**:
- 사전 판정은 "판단 형태 = 방향+수량·이유"라고 적었다. 실측은 **레벨까지 나온다**(목표가·무효화).
  규칙으로 옮기기가 사전 판정보다 쉽다.
- 사전 판정은 "수수료를 판단 입력에 넣음"의 위치를 `arena/strategies.py` 로 적었다. 실제 위치는
  `arena/fees.py` → 커넥터 → 사용자 메시지다(§4.1). 요율을 바꾸려면 전략이 아니라 **`config.json` 을** 고친다.
- 사전 판정은 "내부 장부는 소수 1자리"라고 적었다. 맞다. 다만 그 결과는 "BTC 주문을 막는다"가 아니라
  **"BTC 최소 티켓이 0.1 BTC(약 $8,553, 초기 자본의 8.6%)로 고정된다"** 이다(§6.2).
- 도커 전제는 맞다. 도커 없이도 세워지지만 **Kafka 호환 브로커는 반드시 필요**하다.

---

## 6. 결함 — 이쪽과 저쪽

### 6.1 저장소가 이미 적어 둔 것 중 이번에 걸린 것

`docs/REVIEW_FINDINGS.md` 는 확인된 버그 7개(B1~B7)·견고성 9개(R1~R9)·단순화 6개(S1~S6)를 **미해결로** 적고,
그중 7개를 strict xfail 로 테스트에 고정해 놨다. 자기 문제를 목록으로 공개해 두는 저장소는 드물다.

| ID | 이번 검증에서 |
|---|---|
| **B1** Coinbase 커넥터에 봉이 안 붙음 (High) | **걸림.** 그래서 이 편은 Binance 만 썼다 |
| **B2** 소수 반올림 수량 | 직접 확인 — `0 < q ≤ 1e-9` 경로는 재현 안 해 봤다(계좌 파손 위험). 수량 검사 자체는 §6.2 |
| **B6** 부동소수 누적으로 전량 매도 막힘 (High) | **재현.** 0.1+0.1+0.7 매수 → 보유 `0.8999999999999999` → 0.9 매도 거절 |
| **R7** 광고한 수수료와 부과한 수수료가 어긋날 수 있음 | 이번 A/B 에서 `--config` 를 커넥터·도구 노드 양쪽에 같이 준 이유가 이것 |

### 6.2 수량 소수 1자리 제한 — 실제 효과 (`raw/20-qty-probe.txt`)

`arena/account_store.py::execute_trade` 는 `abs(quantity - round(quantity,1)) > 1e-9` 면 거절한다.
BTC $85,532 기준 실측:

```
buy  0.01 BTC  ($855)     거절 — Quantity must have at most 1 decimal place
buy  0.05 BTC  ($4,277)   거절
buy  0.09 BTC  ($7,698)   거절
buy  0.1  BTC  ($8,553)   통과
buy  0.15 BTC  ($12,830)  거절
buy  0.2  BTC  ($17,106)  통과
```

**주문을 막지는 않는다.** 대신 초기 자본 $100,000 기준으로 BTC 포지션 크기가 **8.6% 단위**로만 조절된다.
"자본의 2%만 걸겠다"는 흔한 규칙을 BTC 에 적용할 수 없다 — 실제로 deepseek 는 `100000 * 0.02` 를 계산해
$2,000 을 뽑아 놓고 BTC 대신 **SOL** 로 갔다(`raw/73-agent-output-all.jsonl`, 12:16 틱).
수수료 실험에서 BTC 가 과소 표집되는 원인이 여기 있을 수 있다.

### 6.3 이번에 새로 나온 것 (REVIEW_FINDINGS 에 없음)

| | |
|---|---|
| 커넥터가 `.env` 를 안 읽는다 | `exchanges/binance.py`·`coinbase.py` 에 `load_dotenv()` 가 없다. LLM 키가 필요 없는 프로세스가 LLM 키 없다고 죽는다 (§2) |
| `config.example.json` 기본 요율이 스트리밍 거래소와 불일치 | 기본 60bp = Coinbase. Binance 커넥터로 쓰면 실제의 6배 (§4.4) |
| `execute_trade` 독스트링이 0bp 판에서도 수수료를 말한다 | `disclosure_prompt()` 는 0bp 에서 "fee-free" 라고 바꾸는데 도구 설명서는 안 바뀐다 (§4.1) |

### 6.4 weltiq 쪽에서 걸린 것

전부 요청 모양 문제였고 우회했다. 독자판에는 성공 경로만 적었다.

| 걸린 것 | 우회 |
|---|---|
| `POST /v1/strategy-rules` 가 `{"doc": {...}}` 포장을 요구 (`Field required: doc`) | 포장해서 재제출 |
| `POST /v1/ai/thesis` 의 필드명이 `text` (`thesis` 아님) | 필드명 교체 |
| `POST /v1/containers` 의 중지 조건이 평평한 `stop_max_loss`·`stop_after_hours` | 평평하게. `stop_on_worker_failure` 는 **받지 않는다**(응답에는 `liquidate` 로 나온다) |
| `DELETE` 가 `still_stopping` 으로 한 번 거절 | 청산 완료까지 30초 간격 폴링 후 삭제 |

미리 알려진 벽 중 `max_order_qty` 기본값 1 은 이번에 수량 0.1 이라 **걸리지 않았다.**

---

## 7. weltiq 로 옮긴 것 — 두 원장

### 7.1 무엇을 옮겼나

미션 1회차 판단(§5.2)을 규칙 문서로 옮겼다.

- 규칙 문서 `60-rules-doc.json` → `doc_hash` **`6776489085948759ea1bb2f81579ea5053cbe89c6d20ef7c3d38b64f6cb5e9d8`**
- 규칙 한 줄: `진입: (close > 85400) → 진입 long E · True → 가격 조건 청산 (limit=85845) → 청산: 내부 청산 규칙 · 보호 손절 0.5%`
- 근거 제출 `63-thesis.json` ok
- 슬롯 `ct_0698aeed14efa7ea` · binance-spot · 1m · 잔고 10,000 · 중지 조건 max_loss 200 / 2시간

### 7.2 변환에서 잃은 칸

| 도구가 낸 것 | 규칙에 들어간 것 | 잃은 것 |
|---|---|---|
| "지금 시장가로 산다" | `close > 85400` 이면 롱 | **시점.** 규칙은 조건이 참인 동안 계속 유효하다 |
| 수량 0.5 BTC ($43,023) | 0.1 BTC ($8,548) | **크기.** 잔고 10,000 에 0.5 BTC 가 안 들어간다 |
| 목표가 85,845 | 지정가 청산 85,845 | 없음 |
| 무효화 85,400 | 진입 조건의 하한 + 보호 손절 0.5% | **의미.** 도구는 "깨지면 판다"였고 규칙은 "그 위에서만 산다" |
| 근거 문장 | thesis 로 제출 | 없음 |

수량과 시점을 바꿨으므로 이것은 **도구 판단을 그대로 옮긴 것이 아니라, 도구가 낸 세 숫자로 필자가 다시 쓴 규칙**이다.
독자판에도 그렇게 적었다.

### 7.3 두 원장 — 같은 왕복을 얼마로 계산했나

**저쪽 내부 수익률을 weltiq 성적으로 가져오지 않았다.** 저장소 자신이 원장 문제(B6·B7·R4)를 미해결로 적어 놓았고,
특히 R4 는 "실현 손익은 청산 수수료를 빼는데 미실현 손익은 안 뺀다"는 것이라 두 수를 섞으면 안 된다.
그래서 **판단 엔진**(§4·§5)과 **내부 장부**(아래)를 갈라서 봤다.

| | Arena 페이퍼 지갑 | weltiq 슬롯 |
|---|---|---|
| 요율 | 60bp (설정 기본값, Coinbase 티어) | **10bp (Binance 현물 실제)** |
| 매수 | 0.1 BTC @ 85,532.01 · 수수료 **$51.32** | 0.1 BTC @ 85,483.07 · 수수료 **$8.55** |
| 매도 | (같은 조건 왕복 시) 수수료 **$51.32** | 0.1 BTC @ 85,471.36 · 수수료 **$8.55** |
| 왕복 비용 | **$102.64** | **$17.10** |
| 실현 손익 | — | −$1.17 |
| 순 | — | **−$18.27 (−0.18%)**, 14분 |
| 체결가 근거 | 자기 호가창의 best_ask/best_bid | `expected_px` 85,474.52 → `px` 85,483.07 (슬리피지 1.0bp) 기록 |
| 미결제 상태 | 없음 (즉시 체결 or 거절) | `sent → acked → filled` 3전이 · 취소된 지정가 1건까지 남음 |

같은 판단, 같은 시장, **왕복 비용 6배 차이.** 이것은 어느 쪽이 틀렸다는 뜻이 아니라
**"수수료를 이기는가"라는 질문의 답이 요율 설정 하나로 뒤집힌다**는 뜻이다.
Arena 의 60bp 기본값을 그대로 두고 "이 모델은 수수료를 못 이겼다"고 적으면 그것은 Coinbase 에서의 결론이지
봉을 준 Binance 에서의 결론이 아니다.

슬롯은 확인 뒤 정지·삭제했다(`67-stop.json` → `68-delete.json` `deleted: true`).

---

## 8. 비용 (`91-cost-estimate.txt`)

| | |
|---|---|
| 측정값 — 오프라인 재생 63회 (A/B 60 + 미션 3) | **$0.4475** (토큰 실측 × 공표 단가) |
| 추정값 — 실시간 아레나 88회 호출 | ~$0.6085 (도구가 토큰을 안 세서 턴 수 환산) |
| 합계 | **~$1.06** · 상한 $1.5 |

OpenRouter 키가 여러 작업자와 공유되어 계정 집계로는 이 작업분을 가를 수 없다.
그래서 하네스가 회차마다 기록한 `usage.prompt`/`usage.completion` 으로 계산했고, 실시간 판만 추정으로 남긴다.
`/api/v1/activity` 는 이 키 권한으로 403.

Binance·weltiq 는 무료(공개 API·모의). Kafka·JDK 는 로컬 설치.

---

## 9. 증적 파일

| 파일 | 내용 |
|---|---|
| `01-github-repo.json` | 저장소 메타 |
| `raw/05-pytest.txt` | 저장소 자체 테스트 결과 |
| `raw/10-captured-prompt.json` | 커넥터가 실제로 보낸 프롬프트 1개 (Kafka `agent_router.input`) |
| `raw/20-qty-probe.txt` | 수량 제한·부동소수 누적 재현 |
| `raw/30-mission-3runs.json` | 미션 3회 판단 원문 (전체 대화) |
| `raw/40-ab-fee-runs.json` · `41-ab-summary.txt` | 수수료 A/B 60회 원문·집계 |
| `raw/ab_fee.py` | 재생 하네스 |
| `raw/70-…-fee60-trades.csv` · `raw/72-…-fee0-trades.csv` | 실시간 두 판의 체결 원문 |
| `raw/73-agent-output-all.jsonl` | 실시간 판 에이전트 이벤트 507건 |
| `42-live-summary.txt` | 실시간 두 판 집계 |
| `60`~`69-*.json` | weltiq 규칙·근거·슬롯·내보내기·정지·삭제 |
| `90-openrouter-usage-mid.json` · `91-cost-estimate.txt` | 비용 |
| `shots/` | 캡처 3장 |

## 10. 안 한 것

- Coinbase 커넥터 (B1 로 봉이 안 간다 — 이 편의 완료 조건과 충돌)
- calfkit 클라우드 브로커 (계정 발급 필요, 가입하지 않는다)
- `momentum`·`brainrot`·`scalper` 전략 (수수료 변수를 고정하려고 `default` 만 썼다)
- 저장소 내부 수익률의 weltiq 환산 (§7.3)
- 24시간 이상 상주 (11틱 × 2판)
