# A축 8편 → 시험 매수 API 실측 이관 (17·18·22·23·25·26·27·28)

2026-09-28. `docs/design/A_TOOLS_TRIAL_ORDER_2026-09-27.md` 가 등급을 매긴 근거는 「이 도구의 기록된 출력이
이 필드에 맞는다」는 **주장**이었고 도구를 한 개도 돌리지 않았다. 이 문서는 그 주장을 **실제 POST 로 바꾼 것**이다.
도구를 새로 설치하지 않았다 — 검증 대상은 이관(interface)이고 입력은 이미 기록돼 있다.

## 0. 환경 고지

| | |
|---|---|
| 하네스 | `reports/a2/03-tool-transfer/run_part3.py` (`02-trial-order/run_evidence.py` 를 베낀 것) |
| 증거 원문 | `reports/a2/03-tool-transfer/part3-evidence.json` — 요청 본문·응답 원문·추적 표본 전부 |
| DB | **격리 임시 PostgreSQL** (소켓 prefix `weltiq-xfer3-`, 포트 55543, DB `weltiq_xfer3`). `db/schema.sql` + `tools/migrate.py` |
| API | `http://127.0.0.1:8903` (돌고 있던 로컬 엔진 8722~8724 와 DB `weltiq_r1_claude` 는 건드리지 않았다 — 실행 후 확인) |
| 계정 | `xfer3@example.invalid` · plan `pro` · 키 스코프 `read-data`·`run-forward-tests` · `WELTIQ_FORWARD_TEST_MONTHLY_LIMIT=200` |
| 브로커·프로필 | `binance-spot` v1 · `binance-usdm` v1 · `us-equity` **v4**(shortable=True) · `krx` v1 |
| 라이선스 | `market.license` 에 `krx`=`open` · `us-equity`=`open` (운영 실측과 같게). **`cme` 는 기본값 `license_pending` 그대로** |
| `market.listings` | `BTCUSDT`(crypto, price_venue `binance-spot`, alt `binance-usdm`) · `NVDA`(us_stock, `us-equity`) · `069500`(kr_stock, `krx`) — 전부 `tradable=true` |
| 합성 호가 | `binance-spot BTCUSDT` 85795.14/85795.15 · `binance-usdm BTCUSDT` 85548.0/85548.1 · `us-equity NVDA` 227.38/227.40 · `krx 069500` 11500/11505 — **각 편 기록의 가격대**에서 잡았다(17편 진입가 85795.148…, 28편 종가 85,548, 25·27편 기준가 227.38) |
| 실전 여부 | 운영 배포 아님 · 실계좌 아님 · 외부 거래소 호출 없음. 계좌 원장은 **시작 전과 끝난 뒤가 완전히 동일**(`account_unchanged_vs_U0: true`) |
| 시각 | 2026-09-27 16:2x UTC = **일요일**. 그래서 `us-equity`·`krx` 는 폐장이고, 접수(201)된 뒤 배치에서 `rejected:market_closed` 다(한도는 환급). 크립토는 24시간이라 체결·청산까지 봤다 |
| 한도 실측 | 승인 14건 → 폐장 거절 7건 환급(`delta=-1`, `placement_rejected:market_closed`) → **used 7**. `forward.usage_events` 21행 |

## 1. 판정 표

| 편 | 판정 | 근거 (실측 코드·수치) |
|---|---|---|
| **17 Vibe-Trading** | **PASS-보강**(+반올림) | 절대 레벨도 시한도 없다 → `17-A/B` **422 `exit_plan_deadline_required`**. %를 기록된 진입가 85795.14865650 에 곱한 값(90942.8575758900)은 틱 0.01 배수가 아니라 `17-C` **422 `invalid_take_profit`** (tick=0.01). 반올림 뒤 `17-D` **201**, 0.00008 BTC @ 85803.7345145 체결·`armed` |
| **18 LangAlpha** | **PASS-보강** | 여덟 편 중 **시한을 도구가 낸 유일한 편**인데 그 시한이 과거다 → `18-A` **422 `invalid_close_at`** (`2026-09-22T16:00:00-04:00`). 날짜만 굴려 `18-B` **201**, `effective_close_basis: close_at`. 레벨 228.50/221.56 은 손 안 댔다 |
| **22 crypto-claude-desk** | **PASS** | `22-A` **201** 한 번에. 레벨 88000/83000 과 시한 48h 가 **전부 도구 기록에서 나왔고 한 자리도 고치지 않았다**. 0.00008 BTC 체결·`armed` |
| **23 crypto-trading-arena** | **PASS-보강** | `23-A` **422 `exit_plan_deadline_required`** · `23-B`(max_duration_hours=24 만) **여전히 422 같은 코드** · `23-C` **201**. 목표가 85,845 가 실제로 발동 — `close_reason: take_profit`, `exit_trigger_px 85850.00`, 체결 85841.4099995 |
| **25 daily_stock_analysis** | **PASS-보강** | 방향과 시한을 둘 다 필자가 넣어야 했다(판정은 `관망`) → `25-A/B` **422 `exit_plan_deadline_required`**, `25-C` **201**. 가격 4칸 중 **들어갈 자리가 있는 것은 2칸**(`ideal_buy`·`secondary_buy` 는 칸이 없다) |
| **26 ClawdTrader** | **FAIL** | 도구가 낸 유일한 수치를 넣으면 `26-A` **422 `extra_forbidden`**(pydantic, 감사 원장에는 `invalid_request`). 그 외 전부를 넣어도 `26-B/C` **422 `exit_plan_deadline_required`**. `26-D` 의 201 은 **필자가 넣은 시한 하나로 만든 것**이고 수량은 서버가 정한 1주다 |
| **27 AI Trading Analyst** | **PASS-보강** | 레벨 235.75/211.00 은 틱 그대로 통과하는데 **지평이 계약 상한을 넘는다** → `27-A` **422 `invalid_close_after_seconds`**(3개월=7,776,000s > 604,800) · `27-B` **422 `invalid_max_duration_hours`**(2160h > 168). `27-C/D` **201** — 단 7일로 자른 거래다 |
| **28 trading-bot-agent** | **PASS**(단, 방향 해석은 계약이 못 가린다) | `28-A` binance-spot 숏 **422 `unsupported_direction`**(현물 `shortable=False`). `28-B` binance-usdm 숏 **201**, 서버 수량 **0.001 BTC**(lot 0.001 · min_notional 50 · ref 85548.05), 0.001 @ 85539.395195 체결·`armed`·`effective_close_basis: close_after`. `28-C/D/E` 도 201 |

보조 확인 (이 8편의 산출물이 아니다 — 지시된 최소 시장이 실제로 올라와 있는지):
`K1` krx 숏 **422 `unsupported_direction`** · `K2` krx 롱 **201**(라이선스 open 이 먹는다, 배치는 폐장 거절) ·
`C1` cme **`market_not_licensed`** — 다만 **HTTP 403 이 아니라 422** 다(`api-contract` §4.2 표와 일치, `server.py` 의 403 게이트와는 다른 경로).

## 2. 편별

### 17 Vibe-Trading — %는 필드가 없고, 환산하면 틱에 걸린다
이 도구의 기록은 백테스트다: `SMA20/60 골든크로스 롱 · 손절 3% · 익절 6% · 0.01 BTC 고정`. **절대 가격이 하나도 없다.**
`exit_plan` 은 상대(%) 레벨을 받는 칸이 없어서 그대로는 `422 exit_plan_deadline_required` 부터 만난다(`17-A`).
`max_duration_hours=1` 만 붙여도 같은 코드다(`17-B`) — **`max_duration_hours` 는 시한이 아니다.**
절대가를 만들려면 기준가가 필요하고, 그 글에 있는 유일한 절대 BTC 가격은 weltiq 쪽 진입 체결가 `85795.14865650` 이다(보강임을 명시).
×1.06 = `90942.8575758900` 은 틱 0.01 의 배수가 아니라 **422 `invalid_take_profit`**. 반올림해 `90942.86`/`83221.29` 로 넣으니 201.
**이것이 54편 `756.2436` 과 같은 부류이고, %만 내는 도구는 전부 이 자리를 지난다.**

### 18 LangAlpha — 시한을 내주는 유일한 편인데, 그 시한이 이미 지났다
기록: `conditional long NVDA · 진입 $221.56 초과 $222.94 이하 · 목표 $228.50–$230.40 · 무효화 below $221.56 ·
time exit 2026-09-22 16:00 ET`. 레벨은 손댈 것이 없다(둘 다 틱 0.01 배수). 목표 **범위**는 받는 칸이 없어 아래 끝 228.50 만 썼다.
그리고 그 `close_at` 을 기록 그대로 보내면 **422 `invalid_close_at`** 이다 — 계약이 "미래"를 요구한다.
날짜만 `2026-09-28` 로 굴려서 201. **기록된 절대 시각은 한 번 쓰면 끝나는 값**이고, 세션이 지난 기록에서는 언제나 필자가 갈아야 한다.
진입 조건의 하한($221.56 초과)은 담을 칸이 없다.

### 22 crypto-claude-desk — 여덟 편 중 유일한 무보강 PASS
기록: `BTC/USDT long 현물 · 손절 83,000 / 목표 88,000 / 시한 "48시간 안에"`.
`close_after_seconds=172800` · `max_duration_hours=48` · `take_profit=88000` · `stop_loss=83000` — **네 칸 전부 도구가 낸 값**이고
한 번에 201, 0.00008 BTC 체결, `armed`. 버린 것은 금액 $2,500·레버리지 1.0·확신도 0.65 세 개이고 전부 요청 필드가 없다.
"진입·손절·목표·손익비가 이미 숫자로 나온다"는 그 편의 평가가 이관에서 그대로 확인됐다.

### 23 crypto-trading-arena — 서버 수량은 0.00008 BTC 다
`23-A`(기록 그대로) 와 `23-B`(`max_duration_hours` 만) 가 같은 `exit_plan_deadline_required` 를 받는다. 시한은 필자 보강이다.
**서버가 정한 수량 = `0.00008` BTC** (`rule: smallest_lot_multiple_meeting_min_notional_within_price_headroom`,
lot 0.00001 · min_notional 5 · reference_px 85795.145 · headroom 20%) = 명목 **약 $6.86**.
저쪽 최소 티켓 0.1 BTC(자본 8.6% · 약 $8,580)의 **1/1250** 이다. 사이징 벽이 사라진다는 것은 사실이지만 **그 자리에 들어오는 값이 이것**이다.
청산까지 봤다 — 호가를 85850.00 으로 한 번 올리자 `exit_trigger: take_profit`, `exit_trigger_px 85850.00`(판정 호가),
체결 `85841.4099995`(목표 85,845 **아래**), `realized_pnl -0.010717571200` · `fees 0.01373161`.
글의 −$18.27(0.1 BTC)과 같은 왕복이 이 티켓에서는 −0.0107 USDT 다.

### 25 daily_stock_analysis — 가격 4칸 중 2칸만 자리가 있고, 방향은 도구가 안 낸다
판정이 **관망**이라 `side` 를 필자가 `long` 으로 썼다(보강). `stop_loss 218.90` · `take_profit 230.00` 은 그대로 들어간다.
`ideal_buy 219.01` · `secondary_buy 219.16` 은 **진입 조건 칸이 없어서 버렸다** — 이 도구의 간판인 "가격 네 칸"의 절반이다.
그리고 `25-D`: 2회차의 손절 `219.36`(매수가 219.01 **위**, 글이 "주문 세트로 성립하지 않는 숫자"라 적은 그 값)을 보냈더니 **201** 이다.
계약은 `take_profit > stop_loss` 만 본다 — 진입가와의 모순은 잡지 않는다.

### 26 ClawdTrader — 주장한 대로 422 다
도구가 내는 것은 `{action, symbol, quantity}` 뿐이다. `quantity` 를 `qty="30"` 으로 넣으면 **422 pydantic `extra_forbidden`**
(요청 모델이 `extra="forbid"`; 감사 원장에는 `invalid_request` 로 남는다). 레벨·시한이 없으니 나머지는 `exit_plan_deadline_required` 다
(`26-B` 빈 계획 · `26-C` `max_duration_hours=3` 만 — 둘 다 같은 코드).
`26-D` 의 201 은 **필자가 넣은 `close_after_seconds=10800` 하나로 성립**했고 수량은 서버가 정한 **1주**다.
즉 이 편에서 이관된 도구 산출물은 종목과 방향뿐이고, 숫자는 0개다.

### 27 AI Trading Analyst — 레벨은 통과, 지평은 못 통과
`235.75`/`211.00`/`222.60` 세 숫자 다 틱 0.01 에서 깨끗하다. 깨지는 것은 `TIMEFRAME: 3-6 months` 다 —
아래 끝 3개월을 `close_after_seconds=7776000` 으로 주면 **422 `invalid_close_after_seconds`**(상한 604,800),
`max_duration_hours=2160` 으로 주면 **422 `invalid_max_duration_hours`**(상한 168). 168h 로 잘라 201.
`27-D` 로 진입가를 `type=limit · limit_price=222.60` 으로도 넣어 201 을 받았다(현재가 227.38 아래 — "눌림을 기다리는 주문").
목표 2·3(280/315)과 33/33/34% 분할은 칸이 두 개뿐이라 버려진다.

### 28 trading-bot-agent — 숏은 usdm 에서 서는데, **어느 방향인지는 계약이 못 가린다**
`28-A` binance-spot 숏 = **422 `unsupported_direction`**(현물 `shortable=False`). venue 만 `binance-usdm` 으로 바꾼 `28-B` 는 201 이고,
최소 노셔널·로트가 맞는다 — **lot 0.001 · min_notional 50 · reference_px 85548.05 → 수량 0.001 BTC**(명목 약 $85.5 > 50),
`0.001 @ 85539.395195` 체결, `armed`, `effective_close_basis: close_after`.
그런데 같은 두 숫자를 **28편 본문의 읽기(롱 분기: 87,400 돌파 진입 · 81,270 무효)** 로 보낸 `28-C` 도 201 이다.
도구 내장 주문기의 완결 주문(`28-D` 매도 지정가 87,400 · 손절 92,000)과 9판 중 유일한 구조화 레벨 판
(`28-E` 진입 86,625 · 손절 87,383, Sell)도 둘 다 201 이다. **네 해석이 전부 접수된다** — 계약은 방향을 검증하지 않는다.

## 3. 종전 등급과 대조

어긋나는 것만 적는다.

| 편 | 종전 | 실측이 바꾸는 것 |
|---|---|---|
| **28** | **S** — "산문 한 줄이 숏 `exit_plan` 두 칸에 **정확히 앉는다**" | 호출은 실제로 선다(201·체결·armed). 그러나 **「정확히」가 실측으로 안 선다** — 28편 본문은 같은 산문을 **롱 분기**로 읽었고(87,400 = 진입 문턱, 81,270 = 손절), 그 롱 본문도 201 이다(`28-C`). 계약은 숏 해석과 롱 해석을 구분하지 않는다. S 의 근거였던 "정확히 앉는다"는 **계약의 무차별**이고 도구 출력의 명확성이 아니다. 리포트가 「현물이면 숏이 안 된다」를 미리 적어 둔 부분은 맞았다(`422 unsupported_direction` 실측) |
| **18** | **S** — "시한을 **필수 필드**로 만든다 … 유일한 완결 거래를 그대로 접수" | 시한 칸이 생긴다는 것은 맞다. 다만 **18편이 낸 시한은 절대 시각이라 기록이 늙으면 `422 invalid_close_at` 이다**(실측). 「그대로 접수」가 성립하는 것은 그 세션 안에서만이고, 보관된 기록에서는 언제나 날짜를 사람이 갈아야 한다. 그리고 목표가 **범위**($228.50–$230.40)는 칸이 없어 절반이 버려진다 |
| **23** | **S** — "0.1 BTC(자본 8.6%) 티켓이 서버 판정으로 **소멸한다**" | 소멸은 사실이다. 리포트가 안 적은 것은 **대체값의 크기**다 — 서버 수량 `0.00008` BTC ≒ $6.86, 저쪽 최소 티켓의 **1/1250**. 같은 왕복이 −$18.27 → −0.0107 USDT 가 된다. 비용 되먹임의 **비율**은 유지되지만 절대 금액으로 읽으면 안 된다 |
| **25** | **A** — 조합 표에 "`decision_type` + **가격 4칸**" | 가격 4칸 중 **2칸만 자리가 있다**(`stop_loss`·`take_profit`). `ideal_buy`·`secondary_buy` 는 진입 조건 칸이 없어 버려진다. 그리고 판정이 `관망`이라 **`side` 는 도구가 내지 않은 값**이다 — 실측 접수는 필자가 방향을 지어 넣은 결과다 |
| **17** | **A** | 판정은 유지되지만 리포트에 없는 벽이 하나 있다 — **%만 내는 도구는 환산 단계에서 반드시 틱 검사에 걸린다**(`422 invalid_take_profit`, 실측). 54편 `756.2436` 과 같은 부류이고 17편도 그 부류다 |
| **27** | **B** — 남은 벽에 "시간 지평이 안 맞는다" | 방향은 맞았고 **코드까지 실측**됐다: `invalid_close_after_seconds`(604,800 상한) · `invalid_max_duration_hours`(168 상한). 두 경로 다 막힌다 |
| **26** | **C** | 어긋나는 것 없음. "도구가 내는 유일한 칸(수량)을 시험주문이 받지 않는다"가 **422 `extra_forbidden`** 으로 그대로 확인됐다 |
| **22** | **A** | 어긋나는 것 없음. 여덟 편 중 **유일하게 한 자리도 보강 없이 201** 이 됐다 — 리포트가 22 에 A 를 준 것보다 이관 축에서는 더 강하다 |

### 계약 쪽에서 새로 확인된 것 셋

1. **`max_duration_hours` 는 시한이 아니다.** 17·23·25·26 네 편에서 `max_duration_hours` 만 주면 그대로
   `422 exit_plan_deadline_required` 다. `api-contract` §3.2 대로이지만, 편별 호출 예시를 읽고 "최대 기간을 줬으니 됐다"고
   믿을 여지가 있다.
2. **`cme` 는 `422 market_not_licensed`** 다(403 아님). `weltiq/api/server.py` 의 라이선스 게이트는 403 이고
   `/v1/trial-buys` 경로는 422 다 — `api-contract` §4.2 표와는 일치한다.
3. **계약은 앞뒤 안 맞는 레벨 세트를 잡지 않는다.** 25편 2회차의 손절 219.36(매수가 219.01 위)이 201 로 통과한다.
   검사는 `take_profit` 대 `stop_loss` 상하 관계 하나뿐이고, 진입가와의 관계는 보지 않는다.

## 4. 끝내기

자식 프로세스에 SIGINT → `pg_ctl -m fast stop`(`server stopped`) → 임시 디렉터리 삭제.
실행 후 확인: 포트 **8903·55543 미점유**, `/tmp/weltiq-xfer3-*` **없음**, `run_part3`·`weltiq_xfer3` 관련 프로세스 **없음**.

> 주의 — 이 디렉터리의 `initdb.txt`·`start.txt`·`stop.txt`·`schema.txt`·`db.txt`·`role.txt`·`migrate.txt` 는
> 같은 시간대에 돈 `run_part1`~`run_part4` 가 **같은 파일명을 공유**해서 마지막 실행이 덮어썼다. 이 편의 증거는
> `part3-evidence.json` 하나이며 위 정리 확인은 그 실행 직후 직접 관측한 것이다.
기존 로컬 엔진 **8722·8723·8724 는 그대로 LISTEN 중**이고 DB `weltiq_r1_claude` 에는 접속하지 않았다. 커밋하지 않았다.
