ALTA 설치부터 실전까지 — LLM 에이전트 네 명이 리서치하고, 코드가 실행을 막는 연구용 트레이딩 플랫폼

도커 없이 ALTA 를 올린 실제 경로, 막힌 네 지점과 우회, 자율 사이클 아홉 번의 결과, 그리고 ALTA 의 내부 Shadow 체결을 weltiq 페이퍼로 빼내 본 기록입니다.

11편. ALTA — 판단은 에이전트가, 실행 경계는 코드가

이 글의 완료 조건: 도커 없이 ALTA 백엔드를 올려 자율 사이클을 한 번 완주시키고, 그 결과가 "아무것도 사지 않음"일 때 왜 안 샀는지를 에이전트 문장으로 읽어 내면 성공입니다.

하는 일 정찰 에이전트 넷이 각자 다른 질문으로 자료를 찾고, 서로 모르는 심사역 둘이 반박하고, 조정자가 불일치를 드러내고, 코드가 순위를 매기고, 또 다른 에이전트가 표현 수단을 고르고 감사합니다. 전부 노트북 안에서
맞는 사람 에이전트의 판단 과정을 감사하고 싶은 사람 — 누가 무엇을 보고 무슨 근거로 말했고 누가 반박했는지가 레코드로 남기를 원하는 사람. 그리고 유료 자료원 비용을 낼 의향이 있는 사람
안 맞는 사람 지금 당장 주문을 내 보고 싶은 사람 · 도커를 안 쓰는 사람 · 한국 주식이 필요한 사람 · 가격 손절이 규칙으로 돌아야 하는 사람(그 칸이 여기 없습니다)
이 글의 결과물 도커 없이 선 ALTA 백엔드, 자율 사이클 아홉 번의 기록, Shadow 체결 게이트 네 개를 직접 발동시킨 출력, 그리고 ALTA 의 실행 경계를 옮긴 규칙이 weltiq 에서 체결되고 스스로 청산·정지한 기록
준비물·비용 코드 0원(Apache-2.0) · 모델 이 글 실측 $0.7362(사이클당 $0.05~0.12) · 시세 유료 자료원은 선택이지만 없으면 품질이 크게 떨어집니다 · weltiq 5절을 따라 할 때만
시간·난도 설치 자체는 12초, 막힌 넷을 푸는 데 약 20분. 난도 상 — PostgreSQL 을 손으로 세울 수 있어야 도커 없이 갑니다
weltiq 와의 관계 붙습니다. 다만 빈칸을 메우는 게 아니라 이미 있는 칸을 밖으로 빼는 일입니다 — ALTA 는 실행층(Shadow)을 이미 갖고 있습니다
검증 환경·날짜 macOS 26.6.2 · arm64 · Node 26.8.1 · Python 3.12.13 · PostgreSQL 16 + pgvector 0.8.0 · Redis · 2026-09-21
제휴 고지 필자는 weltiq 로부터 이 연재의 원고료를 받습니다. 도구 평가는 필자가 직접 돌린 결과만 씁니다

주문은 전부 모의입니다. ALTA 쪽은 내부 Shadow 만 썼고 브로커 커넥터는 한 번도 켜지 않았습니다. weltiq 쪽은 실계좌 주문 경로 자체가 닫혀 있습니다(live_orders: false). 종목은 기능 확인용 예시일 뿐 어떤 매매 의견도 아닙니다.


1. 이게 뭔가요

kyky2347/ALTA — Autonomous LLM Trading Asterism. 저장소가 스스로를 "연구 전용 다중 에이전트 트레이딩 플랫폼" 이라 부릅니다. 트레이딩 봇이 아니라 가상의 리서치 회사를 흉내 내는 쪽 — 하나의 논지가 반박과 비용과 시간을 견디고 나서야 자본을 받을 자격이 생긴다는 구조를 코드로 박아 둔 물건입니다. React 콘솔 · Node 게이트웨이(실행 제어·자격증명·도구 경계) · Python 리서치 서비스(스케줄러·수명주기·PostgreSQL) 세 덩어리에, OpenAI Codex 를 고정 버전으로 따다 고친 에이전트 하니스가 붙습니다.

항목 값 확인 방법
주소 · 라이선스 github.com/kyky2347/ALTA · Apache-2.0 GitHub API
별 수 ★621 · 포크 77 · 열린 이슈 4 (관측일 2026-09-21) GitHub API
개설 / 마지막 푸시 2026-08-27 / 2026-09-13 GitHub API
규모 파이썬 135파일 38,001줄(테스트 제외) · Node 46파일 12,715줄 · 마이그레이션 20개 실측
클론 4.02초 · 92MB 실측
필요한 것 Node 22+ · Python 3.12 · uv · PostgreSQL(pgvector) · Redis · (선택) Rust README

"두 가지 모드뿐" 이 이 도구를 이해하는 열쇠입니다. Shadow 는 ALTA 가 자기 안에서 체결·비용·포지션·청산을 관리하고 브로커 주문이 없는 모드, Broker API 는 감사를 통과한 계획만 명시적으로 고른 계좌로 나가는 모드입니다. Paper 냐 Live 냐는 그 계좌의 속성이지 제3의 모드가 아닙니다.

ALTA 콘솔의 Broker connections — 커넥터 여섯이 전부 Not configured

왼쪽 목록이 커넥터 여섯입니다. 이 글에서는 여섯 다 Not configured 인 채로 끝냈습니다. 오른쪽 Alpaca 폼을 보시면 Paper account 와 Exact account ID 가 따로 있고, 그 아래 문장이 이 제품의 태도를 요약합니다 — "저장한다고 거래가 승인되지 않습니다. 검증은 선택한 계좌를 읽기만 합니다."**

커넥터는 여섯(Tiger·Alpaca·IBKR·Futu·Longport·Schwab)인데 저장소가 먼저 못을 박습니다 — "여섯 개의 구현이 여섯 개의 실계좌 승인을 뜻하지는 않는다." Longbridge 와 Schwab 은 아예 승인 차단이고 나머지도 조건부 경로입니다. 이 글에서는 그 경계를 한 번도 건드리지 않았습니다. 자리로 보면 ALTA 는 판단하는 쪽인데 특이하게 자기 실행층(Shadow)을 이미 갖고 있어서, weltiq 가 들어갈 자리는 "비어 있는 칸"이 아니라 "이미 있는 칸을 밖으로 빼는 일" 입니다(5절).


2. 설치 — 도커 없이 끝까지 간 경로

⚠ 이 편에서 제일 중요한 줄

이 기계에는 도커가 없습니다. 그리고 도커를 깔지 않고 끝까지 갔습니다. 아래는 실제로 통과한 경로 그대로이고, 막힌 지점 넷을 하나도 숨기지 않았습니다.

시키는 대로 하면 여기까지는 갑니다.

git clone --depth 1 https://github.com/kyky2347/ALTA.git && cd ALTA
./alta env setup --dev
Installed Python 3.12.13 in 2.11s · Prepared 30 packages in 6.03s · ALTA Python runtime ready
ALTA v3.5: Docker is missing; install and start OrbStack (recommended) or Docker Desktop
./alta env setup --dev  11.677 total

설치 첫 시도 — 파이썬 런타임까지는 11.7초에 끝나고 마지막 한 줄에서 막힙니다

파이썬 런타임 설치는 11.677초에 끝났습니다. 마지막 줄을 보세요 — Docker is missing. 여기가 첫 번째 벽입니다.

막힌 것 ① — 도커 없이 PostgreSQL·Redis 를 올린다

./alta env up 은 docker compose 한 갈래뿐입니다. 그런데 compose.yaml 이 원하는 것은 딱 둘 — pgvector 가 들어간 PostgreSQL 16 과 비밀번호가 걸린 Redis. 접속 주소는 alta-src/environment-config.mjs 가 .alta/services.env 의 포트·계정과 .alta/secrets/ 의 비밀번호를 합쳐 자식 프로세스에 넣습니다. 즉 그 주소에 맞는 서버가 이미 떠 있으면 도커는 필요 없습니다.

# 포트를 9062/9063 으로 바꾼다 (기본 55432/56379)
sed -i '' 's/"55432"/"9062"/; s/"56379"/"9063"/' .alta/services.env

export LC_ALL=C          # 이거 없으면 macOS 에서 postmaster 가 죽습니다 (아래)
initdb -D services/pgdata -U alta --auth=scram-sha-256 \
       --pwfile=.alta/secrets/postgres_password -E UTF8 --locale=C
pg_ctl -D services/pgdata -o "-p 9062 -c listen_addresses=127.0.0.1 -c timezone=UTC" start
psql -h 127.0.0.1 -p 9062 -U alta -d postgres -c "CREATE DATABASE alta OWNER alta;"
psql -h 127.0.0.1 -p 9062 -U alta -d alta   -c "CREATE EXTENSION IF NOT EXISTS vector;"

redis-server services/redis.conf          # requirepass 만 맞추면 됩니다 (포트 9063)

CREATE DATABASE·CREATE EXTENSION·vector 0.8.0·PONG 이 나오면 선 것이고, ./alta env python -m alta_asterism migrate upgrade 가 0.70초에 {"revision":"b20_0018"} 를 냅니다.

도커 대신 로컬 PostgreSQL 16 · Redis 를 9062·9063 에 올리고 마이그레이션까지 통과한 화면

위에서부터 순서대로 보세요 — PostgreSQL 기동 → vector 0.8.0 확장 확인 → Redis PONG → 마이그레이션 b20_0018 → replay 해시 → soak PASS. 이 다섯 줄이 나오면 도커 없이도 ALTA 백엔드가 산 것입니다.

LC_ALL=C 를 빼먹으면 FATAL: postmaster became multithreaded during startup 으로 죽습니다. 실제로 처음에 이렇게 죽었습니다. ALTA 문제가 아니라 macOS + Homebrew PostgreSQL 의 알려진 함정입니다.

잃은 것: compose 가 걸어 두는 컨테이너 자원 한도(mem_limit 1g·cpus 2·pids_limit·no-new-privileges)가 안 걸립니다. 혼자 쓰는 개발 기계면 상관없지만 상주로 돌리실 거라면 의미가 있습니다.

막힌 것 ② — 에이전트 하니스를 빌드할 수 없다

ALTA 는 vendor/openai-codex/ 의 고정 Codex 하니스를 씁니다. 빌드 명령은 V8_FROM_SOURCE=1 ./alta setup 인데 rust-build.mjs 가 V8 아티팩트를 절대 내려받지 않습니다 — "Verified local V8 artifacts are required… The build never downloads them." 보안 설계로는 옳지만 직접 구해다 놓지 않으면 빌드가 시작조차 안 됩니다.

우회. ALTA 자신이 파이썬 의존성으로 이미 받아 둔 것을 씁니다.

ls .alta/python/venv/lib/python3.12/site-packages/codex_cli_bin/bin
# codex (260MB)  codex-code-mode-host (46MB)
cp .alta/python/venv/.../codex_cli_bin/bin/codex .alta/bin/codex
cp .alta/python/venv/.../codex_cli_bin/bin/codex-code-mode-host .alta/bin/

openai-codex-cli-bin 0.144.4 이고 ./alta doctor 가 바로 알아봅니다.

잃은 것 — 이게 제일 큽니다. 이건 ALTA 가 고쳐 둔 하니스가 아닙니다. CHANGES.md 가 자기가 고친 파일 13개를 나열해 두었고 거기엔 app-server/src/lib.rs 와 core/src/tools/handlers/multi_agents_v2.rs 가 있습니다. 같은 문서가 "인증·샌드박스 네트워크 플래그·승인 의미·와이어 프로토콜은 바꾸지 않았다"고 적어 프로토콜 수준에서는 호환이지만 프로바이더 간 협업 별칭과 ALTA 신원 표시는 빠집니다. 이 글의 실행은 그 차이 위에 서 있습니다.

막힌 것 ③ — 그 바이너리가 ALTA 의 설정을 거부한다

$ ./alta kimi app-server
Error: error loading default config after config error:
model_providers.deepseek: provider name must not be empty

메시지가 한 다리 건너 있습니다. 진짜 원인은 그 위의 config.toml:8:1: invalid type: integer 4, expected struct AgentRoleToml 입니다. [agents] max_concurrent_threads_per_session = 4 한 줄을 0.144.4 는 다른 뜻으로 읽고, 설정이 통째로 무효가 되어 기본값으로 떨어지니 -c model_providers.<x>.base_url=… 로 덮어쓴 값만 남아 name 이 비어 버립니다. 같은 이유로 model_catalog_json 도 거부됐습니다.

우회. 생성 쪽에서 두 줄을 뺐습니다(증적 19-alta-patches.diff).

-    "[agents]",
-    `max_concurrent_threads_per_session = ${agentThreads}`,
+    `# [agents] max_concurrent_threads_per_session = ${agentThreads}`
-    "-c",
-    `model_catalog_json=${toml(state.catalogs.combinedFile)}`,
+    // codex-cli 0.144.4 는 model_catalog_json 을 거부한다

잃은 것: 세션당 동시 스레드 한도(4)와 모델 카탈로그(문맥창·툴 모드 같은 모델별 메타). 카탈로그가 없으니 하니스가 모델 성질을 기본값으로 가정합니다.

막힌 것 ④ — DeepSeek·Grok·Kimi 키가 없다

ALTA 의 기본 배역은 이종 모델입니다 — 정찰 DeepSeek V4 Flash, 심사 DeepSeek Pro 대 Grok 4.6, 조정 Kimi K3, 감사 Grok 4.6, 반대 역할에는 반드시 다른 모델. 세 키가 다 없었는데 alta-src/providers.mjs 에 문이 하나 있습니다.

const BASE_OVERRIDES = { deepseek: "ALTA_DEEPSEEK_BASE_URL",
                         xai: "ALTA_XAI_BASE_URL", kimi: "ALTA_KIMI_BASE_URL" };

Kimi 계열은 프로토콜이 OpenAI 호환 chat/completions 라 OpenRouter 로 그대로 돌릴 수 있습니다. 다만 ALTA 는 모델 이름 접두사를 검사하는데(kimi- 로 시작해야 kimi 프로바이더) OpenRouter 의 이름은 moonshotai/kimi-k2.5 라 슬래시가 걸립니다. 그래서 127.0.0.1:9064 에 얇은 셰임 한 장(95줄, 증적 22-openrouter_shim.py)을 놓았습니다 — /models 에는 moonshotai/ 를 뗀 이름을 돌려주고 /chat/completions 에서 다시 붙여 넘깁니다. 셰임은 Authorization 헤더를 그대로 흘려보내므로 키를 저장하지 않습니다.

export ALTA_KIMI_BASE_URL=http://127.0.0.1:9064
export ALTA_ALLOW_INSECURE_LOOPBACK=1     # 루프백 HTTP 허용 (ALTA 가 제공하는 스위치)
export MOONSHOT_API_KEY=<OpenRouter 키>

한 군데 더 손봐야 했습니다. ALTA 는 에이전트 자식에게 허용 목록에 있는 환경변수만 넘기는데(agent_launch.py) ALTA_KIMI_BASE_URL 이 그 목록에 없어서 두 줄을 추가했습니다. 그러자 ./alta models --refresh 에 kimi-k2.5·kimi-k2-thinking·kimi-k2.6(전부 context=262144)이 붙었습니다.

잃은 것: 이종 모델 배역이 사라졌습니다. 반대 역할은 여전히 서로 다르지만 전부 Kimi 계열이라 ALTA 가 말한 다양성이 제 실행에서는 더 얕습니다. 이 글의 판단 품질을 ALTA 기본 구성의 품질로 읽으시면 안 됩니다.

여기까지의 실측

명령 자체는 빠릅니다 — 클론 4.02초(92MB) · env setup 11.68초 · initdb 0.37초 · migrate 0.70초 · pnpm install 3.84초(484패키지). 막힌 것 넷을 푸는 데 든 시간이 약 20분이었습니다(env setup 실패 16:46 → 첫 사이클 완주 17:06). 디스크는 .alta/ 1.5GB · node_modules 238MB · vendor 69MB, 저장소 전체 1.9GB. 메모리는 사이클 한 번에 파이썬 77MB + Node 4×83MB + codex 넷이 붙고 정찰 넷이 동시에 돌 때가 제일 무겁습니다.


3. 첫 결과 — 키 없이 도는 길부터

ALTA 는 키 없이 도는 길을 따로 만들어 뒀습니다. 모델도 외부 자료도 부르지 않고 픽스처로 수명주기 전체를 통과시키는 길이고, 처음 깔았으면 여기부터가 맞습니다.

$ demo   {"status":"MVP_RUNNING","replayed":false, 정찰 넷 전부 "succeeded",
          "replay_hash":"a60c6c36dd53dff2ba41b6950f4f05e79eac76d5a08e00a42aca1d067ad2527d"}  0.662s
$ replay {"replayed":true, "replay_hash":"a60c6c36dd…7ad2527d"}                               0.42s
$ soak   {"status":"PASS","cycles":14,"failures":[],"manual_database_repairs":0,
          "session":"2026-08-24T13:30:00Z ~ 20:00:00Z"}                                       4.123s

이렇게 나오면 성공입니다. 볼 곳은 둘 — demo 와 replay 의 replay_hash 가 한 글자도 다르지 않고(같은 입력이면 같은 수명주기, 이게 ALTA 가 파는 "재현 가능성"의 실물), soak 는 미국장 한 세션을 30분 간격 14사이클로 통과하면서 수동 DB 보정이 0 입니다. DB 에는 evidence 15 · run 10 · assessment 6 · opportunity 3 · expression 1 · shadow_position 1 · ops.event 36 이 쌓입니다. 모델도 인터넷도 한 번 안 썼는데 "증거 → 정찰 → 논지 → 심사 → 순위 → 표현 → Shadow 진입 → 모니터 → 청산"이 한 바퀴 돕니다.

계약 검사도 돌렸습니다 — ./alta test 306 pass / 0 fail(3.2초), 대시보드 44 pass(0.12초), broker-python 84 pass(12.0초), capital-python 38 pass(7.4초). 저장소가 기록해 둔 916개 중 472개이고, 나머지(리서치 통합 묶음)는 돌고 있는 사이클과 같은 DB 를 써서 안 돌렸습니다 — 미확인입니다.

콘솔을 띄우는 법

ALTA 의 얼굴은 터미널이 아니라 웹 콘솔입니다. ① 띄우는 명령은 ./alta dashboard --host 127.0.0.1 --port 9070 --no-open(= npm run dashboard, 기본 포트 8877). ② 기동 로그가 endpoint: 와 open manually: …/open/<일회용 토큰> 을 알려 주는데 /open/<토큰> 쪽으로 들어가셔야 합니다 — 맨 주소를 열면 잠깁니다. --host 를 바꿔도 루프백 밖으로는 안 열립니다. ③ 계정 가입도 로그인도 없고 일회용 부트스트랩 토큰이 곧 인증이라 링크는 한 번 쓰면 끝입니다.

두 번째 접속 — 콘솔이 잠깁니다

"This console needs its secure launch URL." 부트스트랩 토큰은 한 번만 씁니다. 다시 들어가려면 ./alta dashboard 를 다시 띄워 새 링크를 받으세요. 로컬 전용 콘솔인데도 이렇게 잠가 두는 건 꼼꼼한 쪽입니다.

콘솔·리서치 서비스·Shadow 는 전부 무료이고 돈이 드는 것은 모델 호출뿐입니다. 유료 자료원(Massive·Finlight·Finnhub)은 선택인데 없으면 리서치 품질이 크게 떨어집니다(4절).

ALTA 오퍼레이터 콘솔 첫 화면 — 왼쪽 메뉴가 이 제품의 목차입니다

처음 열면 Opportunity workspace 가 비어 있고 가운데에 Start research runtime 버튼 하나가 있습니다. 왼쪽 아래를 보세요 — Research only / Shadow environment / Capital disabled 가 항상 붙어 있습니다. 그리고 위쪽 RUNTIME STOPPED: 콘솔은 조종석일 뿐 스케줄러가 아닙니다. 백엔드를 켜야 값이 채워집니다.

누르는 순서는 API connections(키 입력) → Agents(역할별 모델, 반대 역할엔 다른 모델) → Start research runtime → Opportunities 상세 → Stop 입니다. Stop 은 리서치를 비우고 DB·캐시를 내리지만 브로커 보유분은 청산하지 않습니다. 그리고 세 번째에서 막혔습니다 — Start research runtime 도 docker compose 를 부릅니다.

Agents 화면이 런타임 준비를 기다리며 잠겨 있습니다

Specialized minds 화면에 들어가도 내용이 안 나옵니다 — "The research runtime is recovering." 위쪽 상태가 RUNTIME RECOVERING 이고 버튼은 Restoring readiness… 로 잠겨 있습니다. 콘솔이 지켜보는 것은 launchd 로 등록되는 관리 서비스이고, 그 서비스도 결국 docker compose 를 지나갑니다. 그래서 저는 백엔드를 2절처럼 손으로 올려 터미널과 DB 로 나머지를 확인했습니다. 이 편의 콘솔 화면이 비어 있는 이유입니다 — 숨기지 않고 적어 둡니다.

백엔드는 따로 켤 수 있습니다 — ALTA_SERVICE_PORT=9071 ./alta env python -m alta_asterism opportunityd --host 127.0.0.1. 읽기 전용이고 주문 엔드포인트가 아예 없습니다(/health/ready → {"ready":true}, /api/v1/system/summary → 영속 객체 개수, /api/v1/stream → SSE). 잘 떴지만 콘솔은 이걸 자기 백엔드로 인정하지 않습니다 — 콘솔이 확인하는 것은 launchd 에 등록된 관리 서비스의 pid 와 감독자 상태여서 손으로 띄운 프로세스는 그 조건을 못 채웁니다. ./alta service install 은 하지 않았습니다 — 그 경로도 결국 도커를 지나갑니다.

남은 막히는 자리

증상 원인 우회
콘솔이 "needs its secure launch URL" 부트스트랩 토큰이 이미 쓰였습니다 ./alta dashboard 를 다시 띄워 새 링크를 받습니다
./alta doctor --live kimi 가 FAILED 프로브가 함수 호출을 기대하는데 모델이 추론 토큰만 쓰고 끝냈습니다 프로브 실패와 실제 동작은 별개입니다. 4절에서 실제로는 돌았습니다
어느 포트가 무엇인지 헷갈린다 이 글은 기본값을 다 바꿨습니다 9062 PG(기본 55432) · 9063 Redis(56379) · 9070 콘솔(8877) · 9071 리서치 API(8765) · 9064 셰임. 앞 둘은 .alta/services.env, 콘솔은 --port, 서비스는 ALTA_SERVICE_PORT

4. 제일 잘 쓰는 법 — "Wait 도 정답이다"를 진짜로 하는지 본다

ALTA 의 README 는 "건강한 서비스가 다음 사이클을 기다리는 중일 수 있고, Wait 는 올바른 결과일 수 있다" 고 적습니다. 트레이딩 도구가 "안 하는 것도 답"이라고 적어 두는 건 흔치 않아서, 사실인지 재 봤습니다. 미국 대형주 열여섯 개를 유니버스로 놓고 자율 사이클을 Shadow 모드로 돌렸습니다.

wake.created     universe=[SPY QQQ IWM DIA AAPL MSFT NVDA AMZN GOOGL META TSLA AVGO JPM XOM LLY UNH]
source.posture   massive_transport disabled · finlight_transport disabled
                 durable_database  degraded (no_durable_evidence_scout_search_allowed)
mvp.stage.completed  scouts / foundry
mvp.stage.skipped    private_assessment_debate · ranking · expression · shadow_open
                     (전부 no_opportunity_survived)
mvp.pipeline.completed  status=MVP_IDLE  expression=null

자율 사이클 한 번의 이벤트 흐름 — 정찰과 파운드리는 완료, 그 뒤 다섯 단계는 건너뜀

위 세 줄(source.posture)이 이 사이클의 전제입니다 — 유료 자료원 둘은 disabled, DB 근거는 degraded. 그 아래를 보세요: scouts 와 foundry 는 completed, 그다음 다섯 단계는 전부 skipped · no_opportunity_survived. 마지막 줄 expression=null 이 결론입니다.

사이클은 끝까지 갔고 아무것도 사지 않았습니다. 그 이유가 에이전트 말로 남습니다 — 성공한 정찰의 출력 원문입니다.

"TSLA 는 신호 구간(2026-09-17~09-21)에 SEC 공시가 0건입니다. MSFT 는 정기 Form 4 내부자 거래뿐이고 중대한 비정기 사건·실적 변경·영업 상태 변화의 징후가 없습니다." — change_event_scout

"검색 호출이 무관한 결과만 돌려줬습니다(대만 관광, 마이크로소프트 고객지원). TSLA·NVDA·QQQ 에 대해 감사 가능한 기대 격차를 찾지 못했습니다." — expectation_gap_scout

"받아 온 시장 자료에는 유의미한 괴리가 없습니다. MSFT 는 $487~$505 의 좁은 범위에서 정상 거래량으로 움직였고, TSLA 는 $356 에서 $373 까지 거래량 증가와 함께 꾸준히 올랐는데 모멘텀과 일관됩니다." — market_dislocation_scout

정찰 셋의 판단이 DB 에 남은 모습 — 전부 no_op 이고 이유와 도구 호출 수가 같이 남습니다

kind: no_op 아래 reason 이 에이전트가 쓴 문장이고, 맨 아래 tools 가 실제로 부른 도구 수입니다(2·3·4회). ALTA 는 이 숫자가 0 이면 그 리서치를 거부합니다. Finnhub unavailable·Japanese dictionary entries 같은 문장이 유료 자료원 없이 돌릴 때 실제로 겪는 것입니다.

이게 이 도구의 자리입니다. 자료가 부실하면 부실하다고 적고 멈춥니다. 명령은 한 줄(autonomous --once)이고 한 사이클에 2~5분, 정찰 넷이 동시에 돕니다. 사이클을 아홉 번 돌렸고 끝까지 간 세 번 모두 매수 0건이며, 왜 안 샀는지가 구조화돼 남습니다. 다만 유료 자료 없이는 근거가 안 모입니다 — 그 결과가 위 인용문입니다.

ALTA 콘솔의 API connections — 모델·시장자료·뉴스 자격증명 현황

1 CONFIGURED / 3 READY / 0 ATTENTION. 왼쪽 목록에서 Kimi / Moonshot 만 API verified 에 ENV 배지가 붙어 있고 DeepSeek·xAI·Massive·Finnhub·Finlight 는 전부 Credential required 입니다. 이 화면이 4절 결과의 원인입니다. 오른쪽 입력칸 설명도 보세요 — "쓰기 전용: 비밀은 브라우저로 다시 돌려주지 않습니다."

모델을 바꾸니 결과가 갈렸습니다

첫 사이클은 아예 실패했습니다. 정찰 넷이 도구를 한 번도 부르지 않고 답부터 내놨거든요(failed active_research_required, 4/4). ALTA 는 이걸 거부합니다 — 검색을 한 번도 안 하고 쓴 리서치는 리서치가 아니라는 겁니다. 게다가 거부로 끝내지 않고 무엇이 틀렸는지 구조화해 돌려주며 최대 3회까지 다시 시킵니다(scout_feedback.py). 세 번 다 도구를 안 불러 실패했습니다. 모델을 kimi-k2.5 → kimi-k2-thinking 으로 바꾸자 셰임 로그에 finish_reason 이 tool_calls 로 찍히기 시작했고 그다음부터 정찰이 통과했습니다.

사이클 정찰 모델 결과 정찰 성공
…-165229 · …-165327 kimi-k2.5 실패 app_server_error 0/4 · 0/4
…-165646 〃 실패 active_research_required 0/4
…-170650 kimi-k2-thinking 완주 · no_opportunity_survived 2/4
…-170937 〃 완주 · no_opportunity_survived 1/4
…-171153 〃 + 유니버스 16 · effort high 실패 0/4
…-171452 〃 완주 · no_opportunity_survived 2/4
…-171603 · …-172814 〃 실패 (뒤쪽은 중단 사이클 복구 뒤 재시도) 0/4 · 0/4

앞의 둘(app_server_error)은 2절 ④ 에서 고친 환경변수 문제였습니다 — 허용 목록 밖 변수를 안 넘겨 셰임 주소가 전달되지 않았고, 그 두 번이 agent_launch.py 에 두 줄을 더한 이유입니다. 나머지 일곱 중 셋이 끝까지 갔고 셋 다 아무것도 사지 않았고 넷은 정찰에서 실패했습니다. 같은 프롬프트·같은 도구인데 정찰 성공률이 0/4 에서 2/4 사이를 오갑니다. ALTA 의 결함이라기보다 기본 배역을 못 쓰고 대체 모델을 넣은 대가인데, 독자가 알아 두실 값은 이겁니다 — 이 파이프라인은 모델이 도구를 성실히 부르는지에 통째로 걸려 있습니다.

ALTA 콘솔의 Agent models — 역할별 모델 배정 화면

역할마다 모델을 따로 고릅니다. Thesis assessment 와 Independent challenge 가 서로 달라야 저장이 됩니다(여기서는 kimi-k2-thinking 대 kimi-k2.6). 위쪽 안내가 중요합니다 — "모델을 바꾸기 전에 리서치 런타임을 멈추세요. 이미 돈 실행은 자기 모델 기록을 그대로 갖습니다." 나중에 모델을 바꿔도 과거 기록의 출처가 흐려지지 않습니다.**

중지 조건과 재연결도 실제로 발동시켰습니다

① ALTA 의 Shadow 체결 게이트. 체결 함수 evaluate_shadow_fill 을 직접 불러 같은 AAPL 호가를 시각만 바꿔 넣었습니다.

정상 (호가가 결정보다 뒤에 있고 신선)   → filled   conservative_quote_fill  @339.159495
얼린 지연 0.5초 안에 온 호가          → no_fill  quote_not_after_frozen_latency
결정 시각보다 앞선 호가               → no_fill  quote_not_forward_of_intent
89초 묵은 호가 (한도 60초)           → no_fill  price_stale
지정가가 호가 아래 (시장성 없음)        → no_fill  guarded_limit_not_market

넷 다 발동했습니다. 특히 첫 두 개가 핵심입니다 — ALTA 는 주문 결정 시각(committed_at)을 얼려 두고 그 뒤 0.5초가 지나 도착한 호가로만 체결합니다. 결정보다 앞선 호가로 체결하는 미래참조를 구조로 막는 겁니다. 이런 걸 코드 계약으로 두는 백테스터는 드뭅니다.

② 그런데 가격 손절이 없습니다. Shadow 청산 판정(monitor_position)의 사유는 다섯뿐입니다 — 논지 무효(falsifier_triggered) · 시간 청산 · 더 나은 기회 · 호가 미도착 · 시세 노후(60초). "−2% 면 잘라라"에 해당하는 칸이 없습니다. 논지 무효는 에이전트가 켜 주는 불리언이고 자연어 무효 조건은 문장이라 누군가 깨어 있어야 다시 읽습니다. 브로커 경로 문서도 자기 입으로 적어 둡니다 — "소프트웨어가 관리하는 청산이지 브로커의 보호 주문이 아니다. 다운타임은 연속 보호가 아니다."

③ weltiq 쪽 시간 한도. 5절에서 올린 전략 컨테이너의 종료 시각을 90초 뒤로 당겨 봤습니다.

limits_changed  until 18:20:58Z → 17:34:16Z  direction=tighten
autostop        reason=expired  position_kept=false  cumulative_pnl=-0.5327405

기한이 되자 스스로 포지션을 청산하고 멈췄습니다. 매수 339.043881 → 매도 338.5111405, 왕복 −0.5327405 USD. 사람도 ALTA 도 아무것도 안 눌렀습니다.

④ 끊고 다시 붙이기. ALTA 의 읽기 전용 서비스를 SIGKILL 로 죽이고 다시 띄워 같은 조회를 받으니 run 186 · evidence 57 · candidate 45 · opportunity 45 · assessment 90 · expression 15 · shadow_position 15 · event 649 · run_artifact 161 · job 24 — 18개 카운터가 한 칸도 안 바뀌었습니다. ALTA 는 상태를 자기 프로세스가 아니라 PostgreSQL 에 들고 있어서 복구랄 것이 없습니다. 1편의 알파카와 같은 성질인데 이유가 다릅니다 — 알파카는 상태가 브로커 서버에 있어서였고 ALTA 는 내 기계의 DB에 있어서입니다. 다만 여기가 weltiq 얘기가 시작되는 지점입니다. 죽어 있는 동안 포지션을 지켜 주는 주체가 없습니다 — 청산이 전부 소프트웨어 판정이라 프로세스가 죽으면 판정도 멈춥니다. 그 시간 동안 weltiq 쪽은 보호 손절 주문을 이미 등록해 둔 채로 돌고 있었습니다. 살아 있는 건 기록이지 보호가 아닙니다.

안 돌린 것

왜
글로벌 종목 하나 보기 시세 조회기가 아닙니다. 정찰이 자료를 받긴 하지만 봉을 내주는 인터페이스가 없습니다 — 해당 없음
한국 종목 이 도구로는 안 됩니다. 유니버스도 커넥터도 미국(+홍콩 Futu) 쪽입니다
여러 종목 훑어 좁히기 4절이 곧 이것입니다 — 16종목을 훑어 0개로 좁혔습니다
수수료 바꿔 다시 재기 체결이 1건뿐이라 요율 비교의 의미가 없습니다. 안 돌렸습니다
브로커 커넥터 6종 한 번도 안 켰습니다. 계좌 검증·승인 경로에 들어가지 않았습니다
유료 자료원 (Massive·Finlight·Finnhub) 키가 없습니다. 미확인 — 이 편 결과의 대부분이 이 결핍 위에 있습니다
리서치 파이썬 통합 테스트 묶음 자율 사이클과 같은 DB 를 씁니다. 안 돌렸습니다

5. weltiq 로 옮기기 — 이미 있는 칸을 밖으로 빼기

ALTA 는 실행층이 비어 있지 않습니다 — Shadow 가 이미 그 자리에 있어서, 질문이 "빈칸을 어떻게 메우나"가 아니라 "이미 있는 칸을 왜, 어디까지 밖으로 빼나" 입니다.

5-1. 옮기는 순서와 그 출처

ALTA 의 결론은 Expression 이라는 레코드이지 주문이 아닙니다. 옮기는 일은 그 레코드를 규칙 문서로 다시 쓰는 것이고 순서는 넷 — ① POST /v1/strategy-rules 규칙 문서 ② POST /v1/ai/thesis 근거 ③ POST /v1/containers 중지 조건을 건 전략 컨테이너 ④ 무인 실행. ②와 ③은 건너뛸 수 없고, 빼고 던지면 이렇게 막힙니다.

중지 조건을 빼면: {"detail":[{"type":"missing","loc":["body","stop_max_loss"],"msg":"Field required"},
                            {"type":"missing","loc":["body","stop_after_hours"],"msg":"Field required"}]}
근거를 안 쓰면:  {"detail":{"code":"thesis_required","questions":[
   "이 전략이 수익을 내는 이유를 한 문장으로 쓰세요 (본인의 말로).",
   "어떤 시장 조건에서 이 논리가 깨집니까?",
   "무효 조건(가격 레벨 또는 시점)을 숫자로 지정하세요."]}}

이 두 게이트가 자율의 테두리입니다. ALTA 가 몇 번을 스스로 판단하든 상주 실행으로 가는 문에는 사람이 적은 한도와 근거가 필요합니다. 어디까지가 ALTA 의 것이고 어디부터 필자의 것인지 칸마다 밝힙니다.

weltiq 쪽 칸 값 출처
방향 long ALTA ExpressionProposal — side 는 "long" 만 허용됩니다
정수 주 · 정규장 게이트 · 진입 1회 1주 단위 · clock('regular') · execution('daily_entries') < 1 ALTA 실행 경계 문서 "whole shares" · "regular-hours boundary" · "One active plan per account"
시간 청산 stop_after_hours ALTA PositionThesis.time_exit_at
수량 1 (당시) ALTA 가 실제로 낸 Expression 레코드는 10 이었습니다 → 5-3. 2026-09-23 수정 뒤로는 10 이 그대로 들어갑니다
가격 손절 평단 −1.5% ALTA 에 없는 칸입니다. weltiq 쪽 필수 게이트라 여기서 생깁니다
종목 AAPL ALTA 가 고르지 못했습니다. 끝까지 간 세 사이클이 전부 Wait 였으므로 필자가 예시로 넣었습니다

마지막 두 줄이 이 편의 정직함입니다. 매수 종목은 ALTA 의 선택이 아닙니다.

5-2. 제일 좋은 조합 — 판단은 ALTA, 체결과 보호는 weltiq

같은 AAPL 을 같은 시각 근처에 양쪽 체결 모델에 넣었습니다. ALTA 쪽은 evaluate_shadow_fill 을 직접 임포트해 돌렸고(옮겨 적은 게 아닙니다), weltiq 쪽은 실제로 낸 체결입니다.

ALTA Shadow   호가 bid 338.94 / ask 338.99
              체결 339.159495 = ask × (1 + 5bp)  슬리피지 정확히 5.0000bp
              수수료 0.033916 = 체결금액 × 1bp (최소 $0.01)
weltiq        기대가 338.91 → 체결 339.043881   슬리피지 3.95bp (실측)
              수수료 0E-8 USD (us-equity 페이퍼는 0)
              보호 손절 333.7918750 이 같은 순간 자동 등록 (stop_market, reduce_only)

두 숫자가 비슷해 보이지만 만들어진 방식이 다릅니다. ALTA 의 5.00bp 는 정책 상수이고 weltiq 의 3.95bp 는 호가창을 걸어 내려가며 나온 결과입니다. 차이가 드러나는 자리는 수량입니다.

qty 1      → filled 339.159495  수수료 0.033916   슬리피지 5.0000bp
qty 10000  → filled 339.159495  수수료 339.16     슬리피지 5.0000bp
(그때 실제 매도호가 잔량: 80주)

호가에 80주가 걸려 있는데 10,000주를 같은 가격에 채워 줍니다 — 125배입니다. ALTA 의 Shadow 는 호가 잔량을 보지 않습니다. 비난이 아니라 눈금의 범위입니다 — 목적이 "이 논지가 비용 차감 뒤에도 살아남는가"이지 유동성이 아니니까요. 다만 얇은 종목·큰 수량을 이 계측기로 검증하시면 성과가 구조적으로 좋게 나옵니다.

ALTA Shadow weltiq
체결 기준 호가 × 고정 5bp 호가창 20레벨 소진
수량 민감도 없음 (1주 = 10,000주) 있음
수수료 1bp · 최소 $0.01 (상수) 실제 수수료표 (us-equity 페이퍼는 0)
미래참조 차단 있음 — 결정 +0.5초 뒤 호가만 확정 봉 기준
가격 손절 없음 필수 — 없으면 전략 컨테이너가 안 뜹니다
논지 무효 판정 에이전트가 켜 주는 불리언 — (문장을 못 읽습니다)
꺼져 있는 동안 판정이 멈춥니다 등록된 보호 주문이 남습니다

이 조합이 해 주지 않는 것. ALTA 의 자연어 무효 조건은 weltiq 가 못 읽습니다. "규제 발표로 논지가 깨지면 나와라"는 문장이고 규칙 문법에 그 칸이 없어서, ALTA 가 깨어나 판단해야 합니다. 역할이 갈리는 조합이지 하나가 다른 하나를 대체하는 조합이 아닙니다.

5-3. 고리 한 바퀴 — 첫 칸은 채우지 못했습니다

먼저 정직하게 적습니다. 이 고리의 첫 칸을 ALTA 의 라이브 판단으로 채우지 못했습니다. 아홉 사이클 중 끝까지 간 셋은 전부 no_opportunity_survived 였으니 "ALTA 가 사라고 한 것을 weltiq 가 체결했다"는 문장은 이 글에 없습니다 — 미확인입니다. 대신 배관이 끝까지 통하는지는 통과시켰습니다.

① 테두리를 칩니다 — 누적 손실 60 USD 에서 중지, 1시간 뒤 자동 종료, 보호 손절 1.5%. ② 규칙 문서와 근거를 내고 컨테이너를 만듭니다 — 규칙 문서 지문 c659c651b5d7…, 근거 {"ok":true}, 컨테이너 status=running. ③ 그런데 진입이 막혔습니다.

{"light":"yellow","code":"entry_blocked","reason":"DENY",
 "text":{"ko":"진입이 한도·수량 규칙으로 막힘"}}

원인은 weltiq 쪽입니다. API 키로 올린 규칙 문서에는 정책 한도를 실을 수 없고(_q 키를 직접 넣으면 422 forged_provenance — 이건 지금도 그렇습니다), 그래서 당시에는 주문 수량 상한이 기본값 1 로 섰습니다. ALTA 의 Expression 레코드가 들고 있던 수량은 10 이었으니 첫 시도에서 바로 걸린 겁니다.

그 뒤 (2026-09-23): 고쳤습니다. _q 없이도 규칙 문서가 선언한 sizing.qty 를 그대로 주문 상한으로 읽습니다 — 지금 같은 문서를 올리면 10 이 그대로 들어갑니다. 아래의 「10 → 1 로 내려서 다시 냈다」는 그날의 실측이라 그대로 둡니다.

④ 그걸 근거로 고칩니다 — 수량을 10 → 1 로 내려 문서를 다시 만들었고(지문 f182c5703f8d…) ⑤ 다시 냈더니 체결됐습니다.

주문  buy  market  1  signal=pine_entry:A                  → filled
주문  sell stop_market 1 stop=333.7918750 reduce_only=true  → acked
체결  AAPL 1 @ 339.043881  fee 0E-8 USD  quote_source=alpaca-iex  liquidity=taker
      기대가 338.91 → 슬리피지 3.95bp

weltiq 전략 컨테이너 상세 — ALTA 의 실행 경계를 옮긴 규칙이 실제로 체결된 화면

제목 아래 한 줄이 옮겨진 규칙입니다 — (정규장 AND (position.size = 0)) AND (execution(daily_entries) < 1) → 진입 long A. ORDERS 를 보세요: 매수 체결과 같은 시각에 보호 손절(stop_market)이 acked 로 걸려 있습니다. ALTA 가 꺼져 있어도 이 주문은 남습니다. 오른쪽 Auto-stop −60 · 03:20 AM 이 ①에서 건 테두리입니다.

⑥ 테두리가 작동하는 것까지 봤습니다 — 4절에서 기한을 당겼더니 스스로 청산하고 멈췄습니다(왕복 −0.5327405 USD). 고리는 닫혔습니다. 다만 한 칸이 대체돼 있습니다.

고리의 칸 채운 주체
분석 ALTA — 아홉 사이클, 끝까지 간 셋의 결론은 전부 Wait
무엇을 살까 ALTA 아님 — 글쓴이가 AAPL 을 예시로 넣었습니다
어떻게 실행할까 ALTA — 정규장·정수 주·계좌당 1계획·시간 청산
주문·체결 weltiq — 1주 @ 339.043881, 보호 손절 동시 등록
결과를 읽고 고침 weltiq 기록 — 수량 10 거부 → 1 로 재제출
무인 구간 weltiq — ALTA 를 죽여도 손절 주문은 남습니다

같은 문서가 백테스트와 페이퍼를 같은 몸으로 지나간다는 것은 이 편에서도 그대로였습니다. 다만 실전 구간은 아직 열려 있지 않습니다(live_orders: false).


6. 써 보니

좋았던 것

  1. "안 하는 것"이 1급 결과입니다. 아홉 사이클 중 끝까지 간 셋이 전부 Wait 였고 그때마다 왜 안 했는지가 에이전트 문장으로 남았습니다. 트레이딩 도구가 빈손을 성공으로 기록하는 건 흔치 않습니다.
  2. 리서치 안 한 리서치를 거부합니다. 도구 호출이 0건이면 거부하고 무엇이 틀렸는지 구조화해 돌려주며 최대 3회까지 다시 시킵니다.
  3. 미래참조가 계약으로, 재현이 해시로. 주문 결정 시각을 얼리고 그 뒤 0.5초가 지난 호가로만 체결하며, demo 와 replay 의 해시가 동일했고 장중 소크 14사이클이 수동 DB 보정 0으로 통과했습니다.
  4. 경계를 자기가 먼저 적어 둡니다. "커넥터 6개가 실계좌 승인 6건이 아니다", "소프트웨어 청산이지 브로커 보호 주문이 아니다", "다운타임은 연속 보호가 아니다" — 전부 자기 문서의 문장입니다. 자격증명 격리도 진짜여서, 셰임 주소를 넘기려다 막힌 것이 그 증거입니다(2절 ④).

아쉬웠던 것

  1. 설치가 무겁고 도커가 사실상 필수입니다. env up 도 Start research runtime 도 docker compose 한 갈래뿐이라, 손으로 우회했어도 콘솔은 끝까지 RUNTIME STOPPED 에서 벗어나지 못했습니다. 하니스를 빌드하려면 V8 아티팩트도 직접 구해 와야 합니다.
  2. 유료 자료원이 사실상 전제입니다. 비면 정찰이 무료 공개 검색으로 떨어지고 실제로 "대만 관광"과 "일본어 사전" 결과를 받습니다. README 는 선택 사항처럼 적지만 결과의 품질은 여기서 갈립니다.
  3. 가격 손절이 없습니다. 청산 사유는 논지 무효·시간·더 나은 기회·시세 노후뿐이고, 논지 무효는 에이전트가 깨어 있어야 판정합니다.
  4. Shadow 체결이 호가 잔량을 안 봅니다. 매도호가 80주에 10,000주를 같은 가격으로 채웁니다.
  5. 정찰 성공률이 모델에 크게 좌우됩니다. 대체 모델에서는 0/4~2/4 를 오갔습니다. 기본 배역으로는 어떤지 미확인입니다. 그리고 별 621개 규모치고 저장소가 큽니다(1.9GB).

비용

저장소·콘솔·Shadow 0원
모델 호출 (이 글의 138건, OpenRouter 경유 Moonshot Kimi) $0.7362 · 사이클당 대략 $0.05~0.12
유료 자료원 안 썼습니다. 썼다면 여기가 제일 큰 비용이 됩니다

같은 키의 총 사용량은 같은 구간에 $1.5707 늘었지만 그 키로 다른 작업도 함께 돌고 있어서, 이 글에 귀속되는 금액은 셰임이 건별로 기록한 $0.7362 입니다. 둘을 다 적어 둡니다.


7. 다음 편 예고

12편은 TauricResearch/TradingAgents 입니다. ALTA 가 에이전트 하나의 판단을 감사 가능하게 만드는 쪽이었다면, 이쪽은 여러 에이전트가 서로 반박하게 만듭니다 — 애널리스트들이 자료를 나눠 읽고, 리스크 팀이 그 결론을 공격하고, 트레이더가 최종 제안을 냅니다. 특히 궁금한 것이 하나 있습니다. 이 도구는 제가 돌려 본 것 중 "아무것도 하지 마라"를 답으로 낸 적이 있는 유일한 물건입니다. 그게 성격인지 그날의 우연이었는지 — 여러 판 돌려서 세어 보겠습니다. 한 번의 신중함으로 신중한 도구라고 부르면 안 된다는 것은 9편에서 배웠습니다.

weltiq 준비 — 3줄 1. 가입: {{CTA_URL}} 2. 마이 → 연결 관리에서 API 키 발급 3. claude mcp add weltiq --header "Authorization: Bearer <키>" https://weltiq.ai/mcp

ALTA 가 꺼져 있는 동안에도 손절이 남아 있어야 한다면 그때 보실 자리입니다.


이 글의 모든 명령·출력·숫자는 2026-09-21 에 실제로 돌려서 얻었습니다. 실제 돈이 오간 주문은 없고 전부 모의입니다. 어떤 매매 의견도 아닙니다. 여기 적지 않은 시행착오·원문 로그·결함 목록은 검증 기록에 있습니다.