# A-2 1편 — ai-hedge-fund + LangAlpha · 검증 기록

- 검증일 2026-09-22 (KST) · macOS 26.6 (Apple Silicon)
- 설계서: [`docs/design/A2_ORCHESTRATION_2026-09-22.md`](../../../docs/design/A2_ORCHESTRATION_2026-09-22.md) §3 조합 1
- 독자판: [`docs/tutorials/a2/01-ai-hedge-fund-langalpha.md`](../../../docs/tutorials/a2/01-ai-hedge-fund-langalpha.md)
- 도구 A: `virattt/ai-hedge-fund` 커밋 `154a8b2f46dca0f40764d814e4e747b0ad71f4c4` (2026-09-18) — 배분형
- 도구 B: `ginlix-ai/LangAlpha` 커밋 `d95c4ce5ebb9d8730b6e66fab2241697bf296796` (2026-09-21) — 레벨형
- 모델: 양쪽 다 OpenRouter `openai/gpt-6-astra`, `temperature` 는 각 도구 기본값
- 등급 **L2**(빈 칸을 다른 도구에게 물어서 채움). 종목·방향·크기는 도구 A 가 낸 것을 그대로 썼다
- 작업 디렉터리 `/private/tmp/weltiq-a2-01` · LangAlpha 백엔드 포트 9440 · Redis 9441

---

## 0. 이 편이 확인하려던 것

설계서 §1 정정판의 명제다.

> A-2 가 하는 일은 `derived`(우리가 만든 무효화)를 `thesis`(도구가 지목한 무효화)로 바꾸는 것이다.

따라서 대조는 **같은 종목·같은 방향·같은 크기에서 무효화의 출처만 다른 것**이다.
수익률 비교가 아니다.

세 갈래:

| 갈래 | 등급 | 무엇 |
|---|---|---|
| **단독** | L0 | ai-hedge-fund 만. 무효화는 이 시스템이 자동 생성 (`derived`) |
| **조합** | L2 | ai-hedge-fund 방향·크기 + LangAlpha 무효화·진입·목표 (`thesis`) |
| **Claude 단독** | L4 | 도구 없이 직접 판단 — **대조군**, 조합 성적에 섞지 않는다 |

---

## 1. 회차 고정 입력

### 1.1 참가 단위는 도구가 아니라 만다테다

`reports/b-track-verify-20260922/ai-hedge-fund.md` 의 실행 판정이 그렇게 적었다.
이 회차의 만다테를 실행 전에 박고 3회 내내 바꾸지 않았다 (`mandate-a2-01.yaml`).

```yaml
name: a2-01-desk
strategies:
  - name: discretionary
    models: [buffett, lynch, druckenmiller]     # LLM 투자자 셋만. 계량 모델 pead 는 뺐다
risk:
  max_position_pct: 0.10
  max_gross_exposure: 1.0
capital: 2500
```

**장부 $2,500 · 종목당 상한 10%.** 이 둘은 필자가 회차 전에 정한 값이고,
**주수는 도구가 그 둘에서 계산한다.** 설계서 §2.3 의 예시 표가 상정한 형태
("비중 0.10 × 자본 ÷ 종가")와 같다.

장부 규모를 이렇게 잡은 이유는 검증 기록에 적을 만한 것이다 —
이 계정의 NVDA 주문 한도 승인이 **주문당 $700 · 일일 $2,000** 이고(`ev_3fc4362ed10c7400`,
18편에서 만든 것), 슬롯 경로의 주문당 수량 한도가 1 이다(§6 W1). 장부를 $2,500 로 두면
도구가 계산한 주수가 그 두 한도 안에 들어온다. **이것은 크기를 필자가 고른 것이 아니라,
집행처의 승인 한도를 만다테에 반영한 것이다.** 한도를 밝히지 않고 주수만 맞췄다면 그것은 L4 다.

### 1.2 LangAlpha 위임 문서 — 네 칸 + 분업 조항

18편 §2.5 가 밝혀낸 것: LangAlpha 는 위임 문서 네 칸을 주지 않으면
`wait for proof` 로 전부 보류하고 레벨을 내지 않는다. 그 네 칸을 그대로 쓰되
**A-2 용으로 분업 조항 한 덩이를 더했다.** 전문 `mandate-a2-langalpha.txt`.

네 칸:

```
- Risk posture: the loss budget is 150 basis points of the book, read as an absolute cap
  that must hold in any state. The book is USD 2,500, so the cap is USD 37.50.
- Horizon: ONE US regular session (09:30-16:00 ET) starting at the next open. ...
  A multi-quarter horizon is outside the mandate and must not be substituted for it.
- The analysis this pitch rests on: none is finished. ... Deliver a screen-grade pitch
  that says the underwriting is still owed rather than declining to state an expression.
- Execution: the order goes to a paper broker that requires, before it will accept
  anything, a written reason, a price level below which the idea is wrong, and a quantity.
```

A-2 가 더한 분업 조항 — **이것이 이 편에서 새로 시험한 문장이다**:

```
DIVISION OF LABOUR FOR THIS PITCH — read this before Step 1:
- The instrument, the direction and the quantity are ALREADY DECIDED by a separate
  allocation desk whose record is quoted below. They are inputs, not questions.
  Do not change them, do not re-argue them, and do not withhold the expression
  because you would have sized it differently.
- What is NOT decided ... is exactly three fields: (1) the invalidation price ...
  (2) the entry condition for the next open; (3) the target.
- The loss budget above constrains the DISTANCE of the stop, not the purchase outlay,
  because the outlay is already fixed by the allocation desk.
```

**결과: 3/3 모두 배분을 바꾸지 않았고 3/3 모두 세 칸을 냈다.** run 1 원문:

> **Depth: first pass.** I retain the desk's direction, instrument and quantity.

run 2 는 한 발 더 나가 표에 한 행을 따로 뒀다:

> | **Allocation input—unchanged** | **BUY 1 share of NVDA common stock**, as specified by the separate desk. | Recorded as an inherited instruction, not a sizing recommendation from this pitch. |

### 1.3 묻는 문장

Claude 가 만든 것은 **문장뿐이고 숫자는 하나도 없다.** `a2_mission.py` 가 회차마다 조립한다.

```
Another desk has already decided to buy NVDA. Give me the price level below which
that idea is wrong, the entry condition for the next open, and the target.
And explain why you set them where you did.
```

뒤에 붙는 것: 위임 문서(§1.2) + **그 회차 ai-hedge-fund 출력의 기계 요약**
(만다테 이름·자본·비중 상한·원시 목표비중·클램프 후 비중·주문 한 줄 + 투자자 셋의 `reasoning` 원문·
`signal`·`value`·`confidence`) + LangAlpha 백엔드 무료 Yahoo 경로에서 받은 시세 JSON.
회차별 프롬프트 전문은 `runs/langalpha-run{1,2,3}.prompt.txt`.

---

## 2. 고리를 3번 돌렸다

설계서 §4.3 이 요구하는 형태다 — **도구 A 를 3번 부르고 그 각각에 도구 B 를 붙였다.**
A 를 한 번 부르고 B 만 3번 부른 것이 아니다.

### 2.1 ai-hedge-fund 3회

회차마다 `~/.hedge-fund/cache/llm` 을 옮겨 두고 돌렸다. 9콜 전부 `cached: false`.

```
FD_COUNT_OUT=fd-runN.json python drive.py ~/.hedge-fund/mandates/a2-01.yaml \
  --tickers NVDA --date 2026-09-19 --model openai/gpt-6-astra --out runs/aihf-runN.json
```

| 회차 | buffett | lynch | druckenmiller | 목표비중 | 클램프 | 주문 |
|---|---|---|---|---|---|---|
| 1 | bullish 0.76 (conf 76) | bullish 0.88 (88) | bullish 0.89 (89) | 1.0 | → 0.10 | **buy 1 @ 222.27** |
| 2 | bullish 0.76 (76) | bullish 0.86 (86) | bullish 0.88 (88) | 1.0 | → 0.10 | buy 1 @ 222.27 |
| 3 | bullish 0.77 (77) | bullish 0.82 (82) | bullish 0.89 (89) | 1.0 | → 0.10 | buy 1 @ 222.27 |

**3/3 일관.** 방향 3/3 매수, 주수 3/3 동일, 클램프 기록도 3/3 동일
(`clamps: [{limit: max_position_pct, before: 1.0, after: 0.1}]`).

**진입가·손절·목표는 3회 모두 없다.** 가격 칸에는 그날 마크 $222.27 이 그대로 들어간다.

### 2.2 오늘 안에서 같은 프롬프트가 다른 판단을 냈다 — 기록해 둘 것

같은 기계에서 오늘 오전(B축 자격 판정, `reports/b-track-verify-20260922/`)에 돌린 3회와
이번 3회는 **프롬프트 해시가 완전히 같다.**

```
prompt_key   4526afd90d3a469b6fa20a25 (buffett) · 9e3971c42afb0dda3c6a4165 (lynch)
             123ed50baeae0cca3439c6f3 (druckenmiller)
snapshot_hash 1c0c4bc31d1615b36bdb95ab   — 오전·오후 6회 전부 동일
```

그런데 buffett 의 판단이 달라졌다.

| | 오전 3회 | 오후 3회 (이 편) |
|---|---|---|
| buffett | **neutral 0.0** · conf 68 / 68 / 68 | **bullish 0.76 / 0.76 / 0.77** · conf 76 / 76 / 77 |
| lynch | bullish 0.83 / 0.85 / 0.86 | bullish 0.88 / 0.86 / 0.82 |
| druckenmiller | bullish 0.88 / 0.89 / 0.87 | bullish 0.89 / 0.88 / 0.89 |

거절 사유의 문장은 양쪽 다 같다 — "네 개의 겹치는 후행 구간으로는 십 년짜리 해자를 세울 수 없다".
**오전에는 그 이유로 중립을 냈고, 오후에는 같은 이유를 적으면서 매수를 냈다.** 오후 run 3 원문:

> I would consider ownership at this price, but four overlapping trailing periods are not
> enough to establish pricing power or give me exceptional confidence in a ten-year holding.

같은 입력·같은 모델에서 **행동 동사가 뒤집히고 확신도가 68 → 76 으로 옮겨 갔다.**
만다테의 자본·비중 상한은 프롬프트에 들어가지 않으므로(해시가 같다) 만다테 차이가 원인이 아니다.
남는 설명은 표본 추출의 비결정성이다.

**영향**: 설계서 §4.3 의 "도구 커밋과 모델을 고정한다"는 충분조건이 아니다.
**같은 하루 안에서도 6회 중 3회가 중립, 3회가 매수였다.** 확신도 보정 곡선(M12)을 그릴 때
이 폭을 모르면 보정이 아니라 잡음을 그리게 된다. 후속 회차에서 같은 프롬프트를 N≥10 으로 반복해
분포를 재는 항목을 제안한다.

### 2.3 LangAlpha 3회

```
python a2_mission.py {1,2,3}
```

**3/3 완전히 같은 세 숫자를 냈다.**

| 칸 | run 1 | run 2 | run 3 |
|---|---|---|---|
| 무효화 | **$221.56** | $221.56 | $221.56 |
| 진입 하한 | $222.94 | $222.94 | $222.94 |
| 진입 상한 | **$223.87** | $223.87 | $223.87 |
| 목표 | $228.50 | $228.50 | $228.50 |
| 시간 청산 | 16:00 ET 9/22 | 동일 | 동일 |
| 행동 동사 | `wait for proof` | `wait for proof` | `wait for proof` |
| 준비도 | screen-grade | screen-grade | screen-grade |
| 배분 변경 | 없음 | 없음 | 없음 |

세 숫자의 출처를 도구가 스스로 라벨했다 — `Reported`(자료에 있는 값)와
`Our judgement`(그 값을 그 용도로 쓴 것)를 갈라 적는다.

- `$221.56` = 9월 21일 **저가**. *"losing the entire latest session's trading range defeats this particular support-and-recovery thesis. It does not prove the business is impaired."*
- `$222.94` = 9월 21일 **시가**
- `$228.50` = 9월 21일 **고가**. *"an observable retest objective... It is not a fundamental fair-value estimate."*
- `$223.87` = **도구가 계산한 값.** 목표까지의 보상이 손절까지의 위험의 2배가 되는 진입 상한.
  run 2 가 산수를 적었다: 목표 거리 $4.63, 무효화 거리 $2.31

**행동 동사는 3/3 `wait for proof` 로 고정됐다.** 18편이 찾은 성질이 A-2 에서도 그대로다 —
이 도구는 "지금 사라"를 끝까지 말하지 않는다. 다만 **세 칸은 전부 찼다.**
회차 집계를 "매수/관망 중 무엇을 골랐나"로 하면 이 도구는 늘 관망이 된다.

### 2.4 도구가 스스로 단 단서 — 이것이 이 편의 두 번째 발견

세 회차 모두, 요청하지 않았는데 **"당신이 준 손실 예산은 내가 지킬 수 있는 종류가 아니다"** 를
별도 절로 적었다. run 3 원문:

> **The absolute cap is the binding constraint, not the allocation decision.** At the ceiling
> entry, the unchanged allocation's ideal stop loss consumes approximately **6.2% of the stated
> loss budget**, before costs. But a sufficiently large discontinuous decline can exceed that
> budget; a stop-limit can instead fail to execute altogether.

run 1 은 수치까지 갈랐다 — 손절에서 정확히 체결되면 예산의 **0.062배**를 쓰지만,
주식이 전액 손실이면 **예산의 6.0배**를 쓴다. 즉 "어떤 상태에서도 지켜야 하는 절대 상한"은
보통주 롱으로는 애초에 보장되지 않는다.

**이것은 무효화의 출처를 옮긴 것과는 다른 종류의 이득이다.** 배분형 도구는
"장부의 150bp" 같은 문장을 받지도 않고 해석하지도 않는다. 레벨형 도구를 붙이니
그 문장이 지켜질 수 없다는 진단이 따라왔다.

---

## 3. 출처 표 — 최종 주문의 모든 숫자

설계서 §2.3 이 요구하는 표다. **Claude 발 숫자가 하나라도 있으면 그 판은 L4 이고 조합 성적이 아니다.**

### 3.1 조합 (L2)

| 칸 | 값 | 출처 | 도구 원문 근거 |
|---|---|---|---|
| 종목 | NVDA | 미션 입력 (`--tickers NVDA`) | 이 도구는 종목을 스스로 고르지 않는다 |
| 방향 | 매수(롱) | **ai-hedge-fund** run 1·2·3 | `signal: bullish` ×9 |
| 목표비중 | 1.00 | **ai-hedge-fund** run 1·2·3 | `target_weights {NVDA: 1.0}` |
| 비중 상한 적용 | 0.10 | **ai-hedge-fund** (만다테 `max_position_pct`) | `clamps [{before: 1.0, after: 0.1}]` |
| 크기 | **1주** | **ai-hedge-fund** ($2,500 × 0.10 ÷ $222.27) | `orders [{side: buy, quantity: 1}]` |
| 마크 | $222.27 | **ai-hedge-fund** | `marks {NVDA: 222.27}` |
| 진입 하한 | **$222.94** | **LangAlpha** run 1·2·3 | "$222.94 is September 21's open" |
| 진입 상한 | **$223.87** | **LangAlpha** run 1·2·3 | "preserves at least 2:1 gross reward/risk" |
| **무효화** | **$221.56** | **LangAlpha** run 1·2·3 | "this is September 21's low" |
| 목표 | **$228.50** | **LangAlpha** run 1·2·3 | "this is September 21's high" |
| 보호 손절 % | **1.0%** | **LangAlpha** run 2 | "−1.0% at that fill" (진입 상한 → 무효화) |
| 시간 청산 | 16:00 ET | **LangAlpha** (위임 문서의 기간을 되돌려 준 값) | |
| 근거 문장 | 원문 | 양쪽 합본 | |

**Claude 발 숫자: 0.**

Claude 가 한 일은 넷이고 전부 숫자가 아니다 — ① 만다테 두 파일을 회차 전에 고정,
② 묻는 문장 작성, ③ 도구 A 출력을 도구 B 프롬프트로 기계 조립(`a2_mission.py`),
④ 도구 B 의 네 숫자를 규칙 문서의 네 칸에 옮기기.

**회색 지대 하나를 미리 밝힌다.** 보호 손절 1.0% 는 처음에 필자가
$(223.87−221.56)/223.87 = 1.03\%$ 로 계산해 넣었다가, **도구가 원문에 쓴 값
"−1.0%" 로 바꿨다.** 산수 자체는 도구 두 숫자의 나눗셈이지만 소수점 둘째 자리는
필자가 만든 정밀도였다. 이 기록에 남긴다 — 첫 문서 `98f67546…` 가 1.03%,
쓴 문서 `fe9aaeec…` 가 1.0% 다.

### 3.2 단독 (L0) — 같은 표, 무효화 칸만 다르다

| 칸 | 값 | 출처 |
|---|---|---|
| 종목·방향·크기·마크 | NVDA / 매수 / 1주 / $222.27 | ai-hedge-fund (조합과 **완전히 같다**) |
| 진입 조건 | 없음 → "포지션이 없으면 산다" | **도구에 칸이 없다** |
| **무효화** | **없음 → 이 시스템의 기본 보호 손절 2%** | **weltiq 기본값** |
| 목표 | 없음 | **도구에 칸이 없다** |

### 3.3 Claude 단독 (L4) — 대조군

| 칸 | 1회차 | 2회차 | 3회차 | 출처 |
|---|---|---|---|---|
| 방향 | 롱 | 롱 | 롱 | Claude |
| 크기 | 1주 | 1주 | 1주 | Claude |
| 진입 | 개장 시장가 | 개장 시장가 | 개장가 ≤ $229.00 | Claude |
| **무효화** | **$224.60** | **$225.50** | **$226.00** | **Claude — 셋 다 자료에 없는 숫자** |
| 목표 | $230.40 | $234.76 | $231.00 | Claude (앞의 둘은 자료 안 값을 골라 쓴 것) |

**Claude 발 숫자: 13/13.** 턴 원문 전부 `claude-turns/L4-claude-alone-run{1,2,3}.md`.
3회차 원문에 필자가 직접 적어 둔 문장이 이 대조군의 결론이다:

> 셋 다 그럴듯하고, 셋 중 어느 것이 옳은지 나는 근거를 대지 못한다.

---

## 4. 지표 넷 (설계서 §4.2)

### 4.1 칸 충족률

weltiq 가 주문·규칙 문서에 받을 수 있는 칸 기준.

| 칸 | 단독 | 조합 | Claude 단독 |
|---|---|---|---|
| 종목 | ✅ | ✅ | ✅ |
| 방향 | ✅ | ✅ | ✅ |
| 수량 | ✅ | ✅ | ✅ |
| 진입 조건 | ❌ | ✅ | ✅ (1·2회차는 시장가) |
| **무효화 가격** | ❌ (→ 기본값 2%) | ✅ | ✅ |
| 목표 | ❌ | ✅ | ✅ |
| 근거 문장 | ✅ | ✅ | ✅ |
| **충족률** | **4/7 = 57%** | **7/7 = 100%** | **7/7 = 100%** |

**조합이 단독보다 3칸 더 채웠다.** 다만 **Claude 단독도 7/7 이다** —
칸 충족률만으로는 조합과 대조군이 갈리지 않는다. 갈리는 것은 출처 분포다.

### 4.2 출처 분포

| | ai-hedge-fund | LangAlpha | **Claude** | 합 |
|---|---:|---:|---:|---:|
| 단독 | 4 | 0 | **0** (+ 시스템 기본값 1) | 5 |
| **조합** | **4** | **6** | **0** | **10** |
| Claude 단독 | 0 | 0 | **13** | 13 |

조합의 목표 — **Claude 발 0 — 을 충족했다.**

### 4.3 비용 배수

| | LLM 호출 | 비용 |
|---|---:|---:|
| ai-hedge-fund 3회 | 9 (에이전트 3 × 3회) | **약 $0.146** |
| LangAlpha 3회 | 3 | **$0.983** (0.4463 + 0.2747 + 0.2621) |
| **조합 3회 합** | 12 | **약 $1.13** |
| Claude 단독 3회 | — | 이 계량기에 안 잡힌다 (§4.5) |

**비용 배수 = $1.13 ÷ $0.983 = 1.15배.** 설계서 §5 가 상정한 "조합은 곱해진다"보다 훨씬 작다.
이유는 이 조합에서 비싼 쪽(LangAlpha)이 한 번씩만 불리고, 싼 쪽(ai-hedge-fund)이
회당 $0.05 이기 때문이다. **배분형 + 레벨형 조합은 비용 면에서 유리한 짝이다.**

ai-hedge-fund 비용은 캐시 파일에서 산정했다 (`llm-cache/`, 4자/토큰,
프롬프트 6,579 · 출력 1,602 토큰, gpt-6-astra prompt $10/M · completion $50/M).
OpenRouter 키 usage 는 38.432610677 → 39.643179677 로 +1.2106 움직였으나
**이 키는 여러 작업자가 동시에 쓰고 있어 전부가 이 작업 몫이 아니다.** 위 산정치가 이 작업의 몫이다.

### 4.4 모순 발생률

**해당 없음.** L2 에는 규칙 조정이 없다. 두 도구가 겹치는 칸을 내지 않았으므로
모순이 생길 자리가 없었다 — 이것이 조합 1을 첫 편으로 고른 이유이기도 하다.

### 4.5 이 비용 표의 구멍

Claude 단독(L4)의 비용이 이 표에 없다. 대조군을 Claude Code 안에서 돌렸기 때문에
같은 계량기에 안 잡힌다. **"조합이 Claude 단독보다 나은가"를 비용까지 포함해 재려면
대조군도 같은 키로 API 호출해야 한다.** 다음 편에서 고친다.

### 4.6 Financial Datasets

**이 작업이 쓴 요청: 0개.** (`fd-run{1,2,3}.json` 전부 `{"total": 0}`)
`--date 2026-09-19` 로 고정해 오전 판정이 받아 둔 자료 캐시를 그대로 썼다. 잔여 917개 유지.

---

## 5. weltiq 로 옮기기

### 5.1 재량 주문 경로 — 배분형이 낸 그대로 보내면 거절된다

```
POST /v1/orders/oneoff  {venue: us-equity, symbol: NVDA, side: buy, type: limit,
                         qty: "1", price: "222.27", thesis: {why: "…"}}
→ 422 {"code":"invalidation_required","detail":"청산 조건(px_below/px_above)이 필요합니다"}
```

원문 `weltiq/A-oneoff-422.txt`. **ai-hedge-fund 출력에는 이 칸에 넣을 값이 없다.**

### 5.2 규칙 문서 둘 — 같은 주문, 다른 무효화 출처

us-equity 엔진이 `price_level`·`price_exit` 블록을 모르므로 둘 다 `slot_rules` 로 짰다.

**A (단독)** — 진입 조건은 "포지션이 없으면", 청산 레벨은 없다.

```
doc_hash 4633a2bffa5fe73d6600aa3305d090c9e77af91308ccb4600d3c98ea91eff7af
설명     "진입: (position.size = 0) → 진입 long L → 청산: 내부 청산 규칙 · 보호 손절 2%"
```

**보호 손절 2% 는 `rule_exit` 블록의 기본값이다** (`weltiq/pine.py:1319` `def __init__(self, stop_pct="2")`).
도구가 낸 값이 아니다. 그리고 러너는 진입 의도에 무효 조건이 없으면
이 값을 밴드로 삼아 `invalidation.px_below` 를 **스스로 만들어 붙인다**
(`weltiq/runner/slots.py:217-223`). 이 편의 `derived` 가 바로 이 숫자다.

**B (조합)** — LangAlpha 의 네 숫자가 네 칸에 그대로 들어갔다.

```
doc_hash fe9aaeec37283ae16868c04ece8df12da13a043862a7a97727efec4920de4d5c
설명     "진입: (((position.size = 0) AND (개장 후 분 ≤ 1)) AND ((open ≥ 222.94) AND (open ≤ 223.87)))
          → 진입 long L · (close ≥ close) → 가격 조건 청산 L (stop=221.56, limit=228.50)
          → 청산: 내부 청산 규칙 · 보호 손절 1.0%"
```

`clock('minutes_from_open') ≤ 1` 로 **"정규장 개장 첫 봉에서만"** 을 표현했다.
18편에서는 이 조건을 못 옮겨 `low` 밴드만 썼는데, 그러면 장중에 값이 구간으로 내려와도
진입해 버려 도구가 말한 "no chasing or later substitute entry" 와 어긋난다.
이번에는 시각 연산자를 써서 그 뜻을 지켰다.

두 문서 모두 `POST /v1/ai/thesis` 를 통과했다 (`weltiq/{A,B}-thesis.resp.txt`).
단독 문서의 근거 문장에는 **"보호 손절 2% 는 이 시스템의 기본값이며 도구의 판단이 아니다"** 를
그대로 적어 두었다 — 나중에 채점할 때 이 판의 무효화가 도구 것이 아님을 저널에서 읽을 수 있게.

### 5.3 옮기면서 잃은 칸

| 도구가 낸 것 | weltiq 칸 | 결과 |
|---|---|---|
| 무효화 $221.56 | `exit.levels.stop` | **그대로** |
| 진입 구간 $222.94~$223.87 | 규칙 문서 `open` 비교 두 개 | **그대로** |
| "정규장 공식 시가" | `clock('minutes_from_open') ≤ 1` | **근사.** 개장 첫 1분봉의 시가지 공식 시가가 아니다 |
| 목표 $228.50 | `exit.levels.limit` | **그대로** |
| 시간 청산 16:00 ET | **없다** | `stop_after_hours` 는 슬롯 수명이지 포지션 청산 시각이 아니다 (18편과 같은 벽) |
| 손실 예산 150bp 절대 상한 | **없다** | 개념이 다르다 (18편 §2.6) |
| "이 예산은 보통주로는 못 지킨다"는 진단 | **없다** | 근거 문장에 말로만 남는다 |
| 준비도 등급 `screen-grade` | **없다** | |

### 5.4 상주 실행 — 문서는 붙었고, 봉이 없었다

단독 문서 A 로 슬롯을 하나 세웠다(한 번에 하나만, 쓰고 바로 정지·삭제).

```
POST /v1/containers {doc_hash: 4633a2bf…, venue: us-equity, tf: 1m, symbols: [NVDA],
                     balance: "2500", stop_max_loss: "37.5", stop_after_hours: 1}
→ 200 ct_9988f45ed82b170e / dep_pa_77f0186e8261fcb7 / status running
   line "진입: (position.size = 0) → 진입 long L → 청산: 내부 청산 규칙 · 보호 손절 2%"
```

2분 뒤 상태(`weltiq/A-container-status.json`):

```
diagnostics {"stage": "waiting_data", "reason": "no_bar_received"}
fills 0 · intents 0
```

원인은 시각이다. 이 작업 시각은 **2026-09-22 17:33 KST = 04:33 ET** 로 정규장 전이고,
저장소의 NVDA 1분봉은 직전 정규장 마감(2026-09-22 05:00 KST = 09-21 16:00 ET)에서 끊겨 있다.
확인:

```
GET /v1/bars?venue=us-equity&symbol=NVDA&tf=1m&limit=500
→ n 404 · 마지막 봉 2026-09-22T05:00:00+09:00 (delay_s 0)
```

**즉 프리마켓 구간에는 이 경로로 새 봉이 오지 않는다.** 개장(22:30 KST)까지 5시간을 기다릴 수
없어 슬롯은 정지·삭제했다(`ct_9988f45ed82b170e` deleted). **상주 실행에서의 체결은 이 편에서 미확인이다.**

### 5.5 국소 리플레이 — 무효화의 출처를 화면으로 갈랐다

상주 경로가 봉을 못 받으므로, **같은 엔진 코어를 직전 정규장 위에서 국소로 돌려**
두 문서가 만드는 진입 의도를 직접 봤다. 호스팅 백테스트 API 가 아니라
`weltiq.runner.backtest.run_backtest` 를 프로세스 안에서 부른 것이고(크레딧 불필요),
DB 는 테스트 DSN(`dbname=weltiq_dev`)이다. 봉은 위 `/v1/bars` 응답 404개를 그대로 주입했다.
스크립트 `replay/replay_probe.py`, 결과 `replay/{A-solo,B-combo,B-combo-diag-1cent}.json`.

```
bars 404  2026-09-21 12:15 UTC .. 2026-09-21 20:00 UTC
        (엔진이 프리·애프터 14봉을 버리고 정규장 390봉만 판단 — session_filtered: 14)
```

**A — 단독**

```
INTENT OPEN buy market qty 1
       invalidation = {"px_below": "217.6090"}
FILL   buy 1 @ 222.082205  2026-09-21T13:31:00+00:00
```

`217.6090` = 첫 정규장 봉 종가 `222.05` × `(1 − 0.01 × 2)`.
`0.01` 은 `PineExitV2("2")` 가 만든 `default_sl`(`pine.py:1326`, `pct / 200`)이고
`× 2` 는 `paper.py:626-630` 의 밴드다. **어느 도구도 이 숫자를 말한 적이 없다.**

**B — 조합**

```
intents 0 · fills 0
```

**진입 조건이 한 번도 맞지 않았다.** 원인은 판단이 아니라 **자료원 차이**다 —
도구가 읽은 일봉(yfinance)의 9월 21일 시가는 `$222.94` 인데,
이 저장소의 **첫 정규장 1분봉 시가는 `$222.93`** 이다. `open ≥ 222.94` 가 **1센트 차이로** 거짓이다.

```
2026-09-21T22:31:00+09:00  o 222.93  h 223.39  l 221.82  c 222.05
```

**B-diag — 진단 전용 변형 (도구 숫자가 아니다)**

진입 하한만 `222.94 → 222.93` 으로 1센트 내렸다. **이 한 숫자는 필자가 바꾼 것이고
조합 성적에 넣지 않는다.** 바꾼 뒤:

```
INTENT OPEN buy market qty 1
       invalidation = {"px_below": "221.5600000000000000000000000"}
FILL   buy 1 @ 222.082205  2026-09-21T13:31:00+00:00
```

**같은 종목·같은 방향·같은 수량·같은 분·같은 체결가에서 무효화만 `217.6090` 대 `221.5600` 으로 갈렸다.**
설계서 §3 조합 1 이 "재는 것"으로 적은 대조가 그대로 화면에 나왔다.

| | 단독 | 조합(진단 변형) |
|---|---|---|
| 체결 | buy 1 @ 222.082205 · 13:31 UTC | **같음** |
| **무효화** | **217.6090** | **221.5600** |
| 그 숫자를 말한 주체 | 없음 (`sl_pct` 밴드 × 2) | **LangAlpha run 1·2·3** |

### 5.6 세 레벨이 전부 1~2센트로 빗나갔다 — 이 편의 세 번째 발견

같은 리플레이에서 진입 뒤 **청산이 한 번도 안 걸렸다**(`pnl 0`, `n_trades 0`).
도구가 낸 세 레벨과 이 저장소 1분봉의 실제 범위를 맞대면 이유가 보인다.

| 도구가 낸 값 | 무엇이라고 했나 | 1분봉 실측 | 차이 |
|---|---|---|---|
| `$222.94` | 9/21 시가 | 첫 정규장 봉 시가 **222.93** | **−$0.01** |
| `$228.50` | 9/21 고가 | 정규장 최고 **228.49** | **−$0.01** |
| `$221.56` | 9/21 저가 | 정규장 최저 **221.58** | **+$0.02** |

**세 레벨이 전부 실측 범위의 바깥쪽 한 틱에 서 있다.** 도구는 일봉(yfinance)의 O/H/L 을 읽었고,
일봉은 프리·애프터를 포함하거나 공식 개장·종가 체결을 쓰므로 정규장 1분봉의 범위와 **끝에서 어긋난다.**
그 결과 이 리플레이에서는 진입도, 목표도, 손절도 전부 한 틱 차이로 안 걸렸다.

**이것은 도구의 판단이 틀린 것이 아니라 옮기는 과정에서 생긴 손실이다.**
그리고 A-2 가 아니면 드러나지 않는 종류다 — 배분형 단독은 레벨이 없으니 어긋날 것도 없다.
**조합은 도구가 지목한 레벨을 그대로 쓰는 대신, 그 레벨이 어느 계열에서 읽힌 값인지까지 같이 옮겨야 한다**
(후속 과제로 올린다: 도구에 넘기는 시세를 집행에 쓸 계열과 같은 것으로 맞추거나, 레벨에 허용 오차를 붙이거나).

---

## 6. 결함 목록

**우리(weltiq)**

| # | 내용 | 영향 |
|---|---|---|
| **N1** | `rule_exit` 블록에서 `stop_pct` 를 빼면 문서 검증은 통과하는데 응답을 만들다가 **500** 이 난다. `weltiq/describe.py:80` 이 `p['stop_pct']` 를 직접 읽어 `KeyError`. 블록 자체는 기본값 `"2"` 를 갖고 있으므로 검증은 통과하는 것이 맞다 — 설명 문구 쪽만 기본값을 모른다 | **새로 찾음.** 500 이라 원인을 알 수 없다 |
| W1 | API 키로 올린 규칙 문서는 주문당 수량 한도가 **1** 로 고정된다(`weltiq/runner/slot_contract.py:9` 가 `doc['_q']['policy']` 를 읽는데 API 키 경로는 `_q` 를 붙이지 않는다). 배분형 도구가 낸 주수가 2 이상이면 전부 거절된다 | 9·11·12·30편에 이어 다섯 번째 |
| W2 | `POST /v1/envelopes` 가 세션 전용 — API 키로 붙은 에이전트는 주문 한도 승인을 스스로 못 만든다 | 18편과 같음 |
| W3 | 무효화의 **출처 라벨이 없다.** `derived`(시스템이 만든 것)와 `thesis`(도구가 지목한 것)가 같은 칸에 구별 없이 들어간다 | 설계서 §1 이 제품 과제로 올린 것. **이 편이 그 필요를 실물로 보여 준다** |
| W4 | 재량 주문에 목표가 칸이 없다 | 18편과 같음 |
| W5 | 시간 청산(포지션을 몇 시에 닫을지) 칸이 없다 | 18편과 같음 |
| **N2** | 외부 도구가 읽은 일봉 O/H/L 과 집행 계열(정규장 1분봉)의 범위가 **끝에서 1~2센트 어긋난다.** 도구가 지목한 레벨을 그대로 옮기면 진입·목표·손절이 전부 한 틱 차이로 안 걸릴 수 있다 (§5.6) | **새로 찾음.** 레벨형 도구를 붙이는 모든 조합에 걸린다. 규칙 문서에 레벨 허용 오차 칸이 없다 |
| N3 | 프리마켓 구간에 us-equity 1분봉이 저장소에 들어오지 않아, 장 전에 세운 슬롯은 `waiting_data / no_bar_received` 로 대기만 한다. 화면에는 정상 기동으로 보인다 | 장 전 준비 회차에서 "붙었는지"를 봉으로 확인할 수 없다 |

**저쪽**

| # | 도구 | 내용 |
|---|---|---|
| L1 | LangAlpha | 샌드박스 공급자가 `daytona`·`docker` 둘뿐 — 도커 없는 기계에서 에이전트 하니스가 한 턴도 안 돈다. **이 편도 18편과 같은 우회를 썼다** (§8) |
| A1 | ai-hedge-fund | `--tickers` 없이는 실행 자체가 안 된다. 종목을 스스로 고르지 않는다 |
| A2 | ai-hedge-fund | 같은 프롬프트 해시에서 오전 3회와 오후 3회의 행동 동사가 갈렸다 (§2.2) |

---

## 7. 체결 — 무엇을 봤고 무엇을 못 봤나

| 무엇 | 봤나 | 근거 |
|---|---|---|
| 재량 주문 접수(조합) | — | 이 편은 규칙 문서 경로로만 갔다. 단독 문서는 `422 invalidation_required` 로 거절되는 것까지 확인(§5.1) |
| 상주 슬롯 기동(단독) | **직접 확인** | `ct_9988f45ed82b170e` status `running` |
| **상주 실행 체결** | **미확인** | 프리마켓 구간에 새 봉이 오지 않는다 (§5.4). 개장 22:30 KST 까지 못 기다렸다 |
| **리플레이 체결** | **직접 확인** | 단독·조합(진단 변형) 둘 다 `buy 1 @ 222.082205` (§5.5) |
| 리플레이 청산 | **직접 확인 — 안 걸렸다** | 세 레벨이 실측 범위 밖 한 틱 (§5.6) |

**따라서 이 편이 "체결을 봤다"고 말할 수 있는 범위는 직전 정규장 리플레이까지다.**
독자판 5절 제목을 "— 통합 실험" 으로 둔 이유가 이것이다.

그리고 리플레이 자체가 **같은 세션 안에서 읽은 숫자를 같은 세션에 다시 적용한 것**이다.
도구는 "다음 개장에 들어가라"고 했고 그 다음 개장(9/22)은 아직 오지 않았다.
**그러므로 §5.5 의 리플레이는 성과 측정이 아니라 배선 확인이다** — 네 숫자가
네 칸에 들어가 엔진이 그대로 쓰는지만 봤다. 손익(`pnl 0`)을 성적으로 읽으면 안 된다.

---

## 8. 이 검증의 한계

1. **LangAlpha 하니스가 아니라 스킬 묶음 8장을 시스템 프롬프트로 얹어 돌렸다.**
   이 기계에 도커·Daytona 가 없어 에이전트 경로가 돌지 않는다(18편 §1.2 와 같은 벽).
   빠진 것: 샌드박스 파이썬 실행(PTC)·서브에이전트 스웜·누적 워크스페이스 기억·웹 검색.
   **즉 "LangAlpha 가 $221.56 을 냈다"는 이 실행 조건에서의 관찰이다.**
   전체 하니스라면 직전 세션 저가 대신 다른 자리를 짚었을 수 있다.
2. **세 숫자가 전부 직전 세션의 OHLC 다.** $221.56=저가, $222.94=시가, $228.50=고가.
   자료로 준 것이 일봉 90개와 스냅샷뿐이라, 도구가 쓸 수 있는 "관측된 레벨"이 그것뿐이었다.
   자료를 더 주면 다른 자리를 짚을지는 **미확인**이다.
3. **N=3 은 통계가 아니다.** 일관성은 "3/3 같은 숫자"라는 관찰일 뿐이다.
   그리고 §2.2 가 보여 주듯 **3회가 같다고 6회가 같은 것도 아니다.**
4. **Claude 단독 대조군은 Claude 가 스스로 평가한 대조군이다.** 원문을 전부 공개한 것이
   할 수 있는 최선이고, 편향이 없다는 보장은 아니다.
5. **시장이 닫혀 있었다.** 두 도구 모두 장중 호가·분봉 없이 판단했다.
   상주 실행 체결도 못 봤다(§5.4). 본 것은 직전 정규장 국소 리플레이까지다.
5b. **그 리플레이는 도구가 숫자를 읽은 바로 그 세션이다.** 도구는 "다음 개장"을 말했는데
   다음 개장은 아직 오지 않았다. 그러므로 §5.5 는 배선 확인이지 성과 측정이 아니고,
   `pnl 0` 은 성적이 아니다. 그리고 조합이 진입하려면 필자가 진입 하한을 1센트 내려야 했다 —
   **그 한 숫자는 도구 것이 아니므로 그 판은 조합 성적에서 제외한다.**
6. **`invalidation.source` 라벨이 없어 "이 판의 무효화가 도구 것이었다"를
   기계가 아니라 이 문서가 증언한다.** 라벨이 생기기 전까지 A-2 의 성적은 사람이 보증하는 것이다.
7. **라벨은 검증이 아니다.** 이 편이 잰 것은 "도구가 그 칸을 냈다"이지
   "그 칸이 옳다"가 아니다.

---

## 9. ID·해시

```
ai-hedge-fund 커밋   154a8b2f46dca0f40764d814e4e747b0ad71f4c4
LangAlpha 커밋       d95c4ce5ebb9d8730b6e66fab2241697bf296796
만다테 A             mandate-a2-01.yaml (a2-01-desk)
위임 문서 B          mandate-a2-langalpha.txt
doc_hash 단독        4633a2bffa5fe73d6600aa3305d090c9e77af91308ccb4600d3c98ea91eff7af
doc_hash 조합(폐기)  98f675469d93c55c68752b4e4cbf0724d33e92a24b56d7349a02058223cdecf8  (보호 손절 1.03%)
doc_hash 조합(사용)  fe9aaeec37283ae16868c04ece8df12da13a043862a7a97727efec4920de4d5c  (보호 손절 1.0%)
주문 한도 승인       ev_3fc4362ed10c7400 (NVDA, $700/$2,000, 2026-09-23 12:25 만료)
슬롯(단독)           ct_9988f45ed82b170e / dep_pa_77f0186e8261fcb7 → stopped, deleted
리플레이             replay/{A-solo,B-combo,B-combo-diag-1cent}.json · 봉 404개 replay/nvda-1m-bars.json
                     (호스팅 백테스트 아님 — 프로세스 내 run_backtest, 크레딧 0, DSN dbname=weltiq_dev)
LLM 비용            약 $1.13 (조합 3회, ai-hedge-fund 9콜 + LangAlpha 3콜)
Financial Datasets  0 요청 (잔여 917 유지)
```

**캡처 없음 — 사유.** 이 편의 화면은 전부 터미널 출력(두 도구의 CLI·판단 원문)과 JSON 이다.
`ai-hedge-fund` 는 CLI 만 있고, `LangAlpha` 는 이 기계에서 에이전트 화면이 돌지 않는다(§8-1).
weltiq 화면은 체결이 있는 것만 싣는 규칙인데 상주 실행 체결을 못 봤다(§5.4).
텍스트를 이미지로 꾸며 싣는 것은 포맷 규칙이 금지하므로, 원문을 코드 블록으로 실었다.

파일: `runs/`(양쪽 판단 원문·프롬프트 원문·시세 스냅샷) · `weltiq/`(요청·응답 원문, 규칙 문서 둘) ·
`claude-turns/`(대조군 원문 3장) · `llm-cache/`(ai-hedge-fund 9콜 전문) ·
`replay/`(국소 리플레이 스크립트·결과·입력 봉) · `start_slot.sh`·`watch_slot.sh` ·
`mandate-a2-01.yaml` · `mandate-a2-langalpha.txt` · `a2_mission.py` · `drive.py`
