Vibe-Trading 설치부터 실전까지 — 별 3만 3천 개짜리 정면 경쟁자와 나란히 재 봤습니다
같은 전략 하나를 Vibe-Trading 과 weltiq 페이퍼에서 각각 돌리고 같은 항목을 쟀습니다. 외부 커넥터가 왜 읽기 전용인지, 그래서 무엇이 되고 무엇이 안 되는지까지 실측으로.
17편. Vibe-Trading — 정면 경쟁자를 나란히 놓고 같은 일을 시켜 봤습니다이 글의 완료 조건: 독자는 같은 규칙 하나를
HKUDS/Vibe-Trading과 weltiq 양쪽에서 돌려 봉 수·체결 수·왕복 수·실현손익·총수수료를 나란히 놓으면 성공입니다.
| 하는 일 | 자연어로 시키면 전략 코드를 직접 짜서 자기 엔진으로 백테스트를 돌리고, 티어시트와 차트가 붙은 대시보드를 내놓습니다. 브로커 커넥터 18개·스킬 90개·메신저 16종 연동이 붙어 있습니다 |
| 맞는 사람 | 아이디어가 아직 문장 상태고 규칙으로 옮기는 것부터 도움이 필요한 사람 · 여러 시장 자료를 키 없이 한자리에서 받고 싶은 사람 · 이미 브로커 계정이 있는 사람 |
| 안 맞는 사람 | 브로커 계정을 만들기 전에 주문을 내 보고 싶은 사람(주문 가능한 페이퍼 프로필 13개가 전부 계정을 요구합니다) · LLM 요금이 부담인 사람(백테스트 한 판이 호출 수십 회) |
| 이 글의 결과물 | 같은 규칙을 양쪽에서 돌린 실측 비교표, 양쪽 백테스트의 재현성 검사, 그리고 weltiq 장부를 저쪽 포트폴리오에 읽기 전용으로 올린 화면 |
| 준비물·비용 | 코드 0원(MIT) · 모델 필요합니다 — 이 글의 비교 한 판이 호출 27회 · 151만 토큰 · $1 미만 · 브로커 계정 없음(그래서 저쪽 주문은 못 냅니다) |
| 시간·난도 | 클론 5.2초 + 설치 49.8초. 비교 한 판에 17분 49초. 난도 중 |
| weltiq 와의 관계 | 정면으로 같은 일을 합니다. 그래서 5절은 "연결하는 법"이 아니라 왜 붙여서 같이 쓰는 실습이 정직하게 안 나오는가 이고, 6절은 용도별 선택표입니다 |
| 검증 환경·날짜 | macOS 26.6.2 · arm64 · CPython 3.14.7 · PyPI vibe-trading-ai 0.1.15 · 2026-09-22 01:45~03:25 KST |
| 제휴 고지 | 필자는 weltiq 로부터 이 연재의 원고료를 받습니다. 도구 평가는 필자가 직접 돌린 결과만 씁니다 |
사람 쪽 문장은 실제로 친 명령을 대화로 옮긴 것이고, 출력은 실측이며 숫자는 손대지 않았습니다. 브로커 계정도 거래소 계정도 만들지 않았습니다 —
HKUDS/Vibe-Trading쪽 주문 실행은 전부 브로커 계정이 필요해서 못 했고, 못 했다고 그대로 적었습니다. 주문이 실제로 나간 것은 weltiq 페이퍼뿐입니다. 이 글은 어떤 전략도 추천하지 않고 종합적으로 어느 도구가 더 낫다고도 말하지 않습니다 — 축별로 어디가 낫고 어디가 못한지만 적습니다. 원문 로그는 검증 기록에 있습니다.
1. 이게 뭔가요
HKUDS/Vibe-Trading 은 홍콩대 데이터과학 연구실(HKUDS)에서 나온 자연어 금융 리서치·백테스트 에이전트입니다. "이 규칙으로 백테스트 돌려 줘"라고 쓰면 전략 코드를 직접 짜서 자기 엔진으로 돌리고, 티어시트와 차트가 붙은 대시보드를 내놓습니다.
| 항목 | 값 | 확인 방법 |
|---|---|---|
| 주소 | github.com/HKUDS/Vibe-Trading |
|
| 라이선스 | MIT | GitHub API |
| 별 수 | ★33,780 · 포크 5,511 (관측일 2026-09-22) | GitHub API |
| 마지막 푸시 | 2026-09-21T12:54:29Z (어제) | GitHub API |
| 개설 | 2026-04-01 · 열린 이슈 28 · 아카이브 아님 | GitHub API |
| 버전 | PyPI vibe-trading-ai 0.1.15 |
vibe-trading --version |
| 파이썬 | 선언은 3.11+, 분류자는 3.13 까지. 3.14.7 에서 설치·구동 확인 | 이 글의 환경 |
| 스킬 · 커넥터 | 스킬 90개 · 브로커 커넥터 18개 · 프로필 55개 | --skills · BUILTIN_PROFILES 실측 |
README 파일 하나가 317,515 바이트이고 한국어판을 포함해 7개 언어로 번역돼 있습니다.
겹치는 문장은 이겁니다 — "페이퍼와 실계좌를 설정값이 아니라 구조로 가른다." weltiq 도 같은 말을 하고 저쪽도 같은 말을 합니다. 그래서 정면 비교가 정직합니다. 커넥터 프로필 55개를 뜯어 세어 봤습니다.
built-in profiles: 55 connectors: 18
alpaca, binance, dhan, etoro, futu, ibkr, kis, longbridge, mt5, okx,
robinhood, scalable, shoonya, tiger, toss, trading212, upbit, zerodha
order-capable profiles: 21 paper: 28 live: 27 paper AND order-capable: 13
주문까지 낼 수 있는 페이퍼 프로필이 13개입니다 — Alpaca Paper, Binance Paper, OKX Paper, KIS Paper, Upbit Paper, Tiger Paper. 한국 브로커(KIS·Toss)와 업비트가 이미 목록에 있습니다. 다만 13개 전부 그 브로커의 계정과 API 키가 있어야 합니다.
$ vibe-trading connector use alpaca-paper-trade && vibe-trading connector check
Connector alpaca · Environment paper · Transport broker_sdk · alpaca-py missing
Alpaca connector not configured: missing api_key, secret_key.
이 글에서 계정을 만들지 않기로 했으므로 저쪽의 주문 경로는 한 건도 못 돌렸습니다. 이게 이 비교의 가장 큰 구멍이고, 6절에 어디가 빈칸인지 그대로 남겼습니다.
2. 설치
경고 — 이 도구는 돈이 듭니다. 백테스트 한 번이 LLM 호출 수십 회입니다. 이 글의 비교 실행 한 판이
deepseek/deepseek-v4-pro로 호출 27회 · 입력 1,464,686 토큰(그중 캐시 히트 1,307,013) · 출력 45,717 토큰 · 합계 1,510,403 토큰을 썼습니다. 도구가 스스로llm_usage.json에 적어 둔 값입니다. 요금으로는 $1 이 안 되는 선이었고, 정확한 달러 값을 한 자리까지 못 박지 않는 이유는 7절 끝에 적었습니다. Ollama 로 로컬 모델을 쓰면 0원이지만 README 스스로 작은 모델은 도구 호출이 불안정하다고 적어 두었습니다.
PyPI 한 줄(pip install vibe-trading-ai)로도 되지만, 코드를 읽어야 해서 저장소를 받았습니다.
git clone --depth 1 https://github.com/HKUDS/Vibe-Trading.git && cd Vibe-Trading
python3 -m venv .venv314 && ./.venv314/bin/pip install -e .
클론이 5.2초(118MB), 설치가 49.8초입니다(pip 캐시가 더워진 상태). 패키지 178개를 끌고 옵니다 — langchain·langgraph·ccxt·akshare·tushare·yfinance·scikit-learn·weasyprint·matplotlib 가 다 들어 있습니다.
설치 완료 화면
클론부터 버전 출력까지. 볼 곳은 두 줄입니다 — vibe-trading 0.1.15 가 뜨면 설치가 끝난 것이고, Python 3.14.7 은 이 프로젝트가 공식 분류자에 적어 두지 않은 버전인데도 그대로 깔렸다는 뜻입니다.
LLM 키는 agent/.env 에 넣습니다 — LANGCHAIN_PROVIDER=openrouter, OPENROUTER_API_KEY=sk-or-..., LANGCHAIN_MODEL_NAME=deepseek/deepseek-v4-pro.
막히는 자리
| 증상 | 원인 | 해결 |
|---|---|---|
pip install -e . 가 3.14 에서 깨질까 걱정 |
분류자에 3.14 가 없음 | 이 글에선 그냥 됐습니다. 178개 전부 휠이 있었습니다 |
| 실행 파일이 어디 있는지 모르겠다 | 명령이 셋입니다 | vibe-trading(TUI) · vibe-trading serve(웹) · vibe-trading-mcp(MCP) |
| 설정 파일이 어디 쌓이는지 모르겠다 | 기본이 ~/.vibe-trading |
VIBE_TRADING_HOME 으로 옮길 수 있습니다 |
| 백테스트가 "run_dir 이 허용 경로 밖"이라고 거부한다 | 실행 디렉터리를 화이트리스트로 잠가 둠 | VIBE_TRADING_ALLOWED_RUN_ROOTS 에 추가. 안전장치지 버그가 아닙니다 |
3. 첫 결과 — 그리고 대시보드 띄우는 법
CLI 한 줄이 제일 작은 예제입니다.
./.venv314/bin/vibe-trading run -p "Backtest a BTC-USDT 20/50 moving-average strategy and summarize return and drawdown"
이렇게 나오면 성공입니다. 프리플라이트 표가 먼저 뜨고(LLM 연결·OKX·yfinance·akshare·ccxt 가 각각 OK/N/A, 6/7 services ready 같은 줄), 그다음 load_skill(...) → write_file code/signal_engine.py → backtest ... 순서로 도구 호출이 한 줄씩 흐릅니다.
대표 화면은 터미널이 아니라 대시보드입니다. 저장소에서 받은 경우 프런트를 먼저 빌드해야 합니다(install 2.5초 + build 5.7초).
cd frontend && npm install && npm run build && cd .. # Node 22 이상
./.venv314/bin/vibe-trading serve --port 9101 # 이게 대시보드 띄우는 한 줄
들어가는 주소는 http://localhost:9101, 기본 포트는 8899 입니다. 같은 컴퓨터에서 열면 로그인도 키도 없습니다. 다른 기기에서 보려면 agent/.env 에 API_AUTH_KEY 를 넣고 Settings 에서 같은 키를 입력해야 하고, 안 하면 민감한 엔드포인트가 403 을 냅니다. 화면 자체는 전부 보이지만 에이전트를 돌리는 순간부터 LLM 요금입니다.
처음 연 화면
vibe-trading serve --port 9101 로 띄운 직후. 볼 곳은 왼쪽 사이드바입니다 — Agent·Runtime·Scheduled·Reports·Portfolio·Alpha Zoo·Options Lab·Correlation Matrix. 처음 열면 Agent 뿐이고, 쓸 만한 화면은 한 번 돌린 뒤에 생깁니다. 그래서 가장 먼저 눌러야 할 곳은 가운데 입력창입니다.
쓸 만한 화면까지 가는 길은 둘입니다. Agent 에 프롬프트를 하나 넣으면(돈이 듭니다) 그 실행에 대시보드·차트·티어시트·Trades·Run Card 탭이 통째로 생기고, Alpha Zoo 를 열면 LLM 을 한 번도 안 부르고 미리 만들어 둔 알파 462개가 목록으로 들어 있습니다.
Alpha Zoo
LLM 호출 없이 열리는 화면. 볼 곳은 목록의 규모입니다 — 이미 등록된 알파를 vibe-trading alpha bench --zoo ... --universe btc-usdt 로 CLI 에서도 벤치할 수 있고, 이 경로는 에이전트를 거치지 않아 요금이 안 붙습니다.
4. 제일 잘 쓰는 법 — 같은 일을 양쪽에서
조건을 먼저 못 박습니다. 나중에 바꾸면 그 순간 이 글은 광고가 됩니다.
| 고정한 것 | 값 |
|---|---|
| 전략 | 단순이동평균 20/60 골든크로스 진입 → 손절 3% · 익절 6% |
| 종목·시간틀 | BTC/USDT 현물 · 1시간 봉 |
| 기간 | 2026-06-24 02:00 ~ 2026-09-21 16:00 (KST) |
| 수량·수수료 | 0.01 BTC 고정, 한 번에 한 포지션 · 0.1% taker 양방향 |
| 잰 항목 | 봉 수 · 체결 수 · 왕복 수 · 실현손익 · 총수수료 · 체결 원문이 나오나 · 두 번 돌리면 같나 |
이 전략을 고른 이유는 양쪽에 똑같이 있어서입니다. weltiq 의 기본 템플릿 TPL-005(골든크로스 추세)가 정확히 이 규칙이고 무료 등급입니다. 저쪽에는 같은 규칙을 자연어 한 문단으로 줬습니다(원문은 검증 기록에).
저쪽 결과 — 17분 49초 뒤에 돌아왔습니다.
Status: SUCCESS Elapsed: 17m 49s Run ID: 20260922_015121_68_3A0E89
Reason: final answer released with unverified figures redacted after 3 rejected drafts
Metrics: return=0.0010, sharpe=2.8231, max_dd=-0.0007, trades=4.0000
자연어 한 문단에서 전략 코드(signal_engine.py)를 직접 써서 자기 엔진으로 돌렸고 왕복 4번을 찾아냈습니다. 도구 호출 수십 회·17분 49초·$0.987102 가 들었고, 중간에 설정을 고쳐 가며 백테스트를 세 번 다시 돌렸습니다. 그런데 다섯 숫자를 달라고 했는데 표의 칸이 대부분 (omitted) 로 비어서 돌아왔습니다 — 왜 그런지는 아래에 있습니다.
Vibe-Trading 리서치 대시보드
같은 일을 시킨 저쪽 화면. 볼 곳은 상단 지표 띠입니다 — TRADES 4, WIN RATE +75.00%, 그리고 오른쪽 끝의 AVG HOLD DAYS 300.5. 3개월짜리 백테스트에서 평균 보유가 300일일 수는 없습니다. 아래 카드에는 데이터 출처가 okx, 엔진이 DAILY engine 이라고 적혀 있습니다 — 1시간 봉을 달라고 했는데 엔진 라벨은 daily 입니다.
체결 목록
같은 실행의 Trades 탭. 볼 곳은 세 칸입니다 — QTY 가 0.009995~0.010001(요청한 0.01 이 맞게 들어갔습니다), 맨 오른쪽 REASON 이 signal ×7 · end_of_backtest ×1(마지막은 손절·익절이 아니라 기간이 끝나서 강제로 닫은 것), 그리고 상단의 8 trades Buy 4 · Sell 4 Total P&L +121.29. weltiq 화면에는 이 REASON 칸에 해당하는 것이 없습니다.**
진입·청산이 찍힌 캔들 차트
Chart 탭. 볼 곳은 캔들 위의 초록 B(매수)와 빨강 S(매도) 핀 8개입니다. 어느 봉에서 들어가고 나왔는지가 한눈에 들어옵니다. weltiq 에는 이 화면이 없습니다 — 같은 정보를 보려면 체결 표를 읽고 봉을 따로 띄워야 합니다. 상단 지표 띠는 앞 캡처와 같은 실행이라 같은 숫자입니다.
산출물은 디스크에 그대로 떨어집니다 — trades.csv·fills.jsonl·equity.csv·positions.csv·metrics.csv·risk_xray.json·ohlcv_BTC-USDT.csv·code/signal_engine.py·config.json·run_card.md. 그리고 run_card.md 에 config_hash·strategy_hash 와 산출물 12개의 sha256 이 전부 박혀 있습니다. 이건 저쪽이 아주 잘하는 대목입니다.
weltiq 결과 — 같은 규칙으로 binance-spot BTCUSDT 1시간 봉 2160시간을 돌렸습니다.
{ "run_id": "bt_31eb97f8e955c4a7", "n_bars": 2151, "n_fills": 9,
"pnl": "114.13547620000", "fees": "6.16770728", "n_trades": 4,
"execution_assumptions": ["bar_close_modeled_spread"], "data_mode": "idealized" }
봉 2151개, 체결 9건, 왕복 4번, 실현손익 114.13547620 USDT, 수수료 6.16770728 USDT. 두 번 돌린 결과가 숫자까지 동일했고 다른 것은 run_id 뿐입니다. 체결 9건의 시각·가격·수수료·유동성 구분이 전부 원장에 남고, 9번째는 아직 안 닫힌 매수라 왕복은 4번입니다.
나란히 놓으면
| 잰 것 | Vibe-Trading | weltiq |
|---|---|---|
| 봉 출처 | OKX (자동 선택) | Binance (요청한 venue) |
| 봉 수 | 산출물에 있으나 답변에서는 가려짐 | 2151 |
| 왕복 수 | 4 | 4 |
| 체결 수 | 8 (마지막을 강제 청산) | 9 (마지막 매수 미청산) |
| 수량 (요청 0.01) | 0.009993 ~ 0.010001 (자본금 값을 3회 조정한 끝에) | 0.01 (선언 그대로) |
| 청산 사유 기록 | signal ×3, end_of_backtest ×1 |
없습니다 |
| 기간 끝 처리 | 강제 청산 | 포지션을 그대로 둠 |
| 실현손익 | +121.2863 USDT | +114.1355 USDT |
| 총수수료 | 2.0298 USDT (실효 진입 0.05% · 청산 0.02%) | 6.16770728 USDT (실효 0.1000% 전건) |
| 수수료가 요청대로 붙었나 | 아니요 (요청 0.1% 양방향) | 예 |
| 두 번 돌리면 | 엔진은 동일 · 에이전트는 보장 없음 | 동일 |
| 산출물 | CSV·JSONL 12종 + sha256 | REST 응답 + 원장 SQL |
| 걸린 시간 · 비용 | 17분 49초 · LLM 호출 27회 · 151만 토큰 | curl 한 번 · LLM 호출 0회 |
왕복 수가 4로 같습니다. 서로 다른 거래소 봉을 쓰고 서로 다른 엔진으로 돌렸는데 같은 규칙이 같은 횟수를 잡아냈다는 건, 양쪽 신호 계산이 적어도 이 구간에서는 같은 것을 보고 있다는 뜻입니다.
손익 숫자(121.29 vs 114.14)를 우열로 읽지 마십시오. 같은 기준이 아닙니다 — 봉 출처가 다르고(OKX vs Binance), 저쪽은 기간 끝에 포지션을 강제로 닫아 마지막 구간의 +42.36 을 실현시킨 반면 weltiq 는 열어 둔 채 끝냈고, 수수료가 저쪽은 2.03 이쪽은 6.17 입니다. 수수료 차이만 4.14 니 손익 차이 7.15 의 절반이 넘습니다. 이건 성과 비교가 아니라 계측기 비교입니다.
그리고 표를 만들다가 weltiq 쪽 구멍을 하나 찾았습니다. 매수 주문에는 "signal": "golden_cross" 가 붙는데 매도 주문에는 사유가 안 붙습니다. 그래서 06-30 의 매도가 손절이었는지 익절이었는지 기록만 보고는 알 수 없고 가격을 직접 나눠 봐야 합니다(60594 → 58406 이니 −3.6%, 손절로 보입니다 — 하지만 이건 추론이지 기록이 아닙니다). 저쪽은 trades.csv 의 reason 칸과 by_exit_reason 집계로 이걸 그냥 줍니다. 이 축은 저쪽이 앞섭니다.
두 번 돌리면 같나 — 무승부입니다
저쪽 엔진에 같은 config.json 과 같은 signal_engine.py 를 넣고 두 번 돌렸더니 trades.csv·equity.csv·metrics.csv·fills.jsonl 의 sha256 이 바이트까지 같았습니다. 저쪽 백테스트 엔진은 결정적입니다.
다만 에이전트 루프는 다릅니다. 같은 프롬프트라도 LLM 이 매번 같은 signal_engine.py 를 쓴다는 보장이 없고, 실제로 이 한 번의 실행 안에서도 설정을 바꿔 가며 세 번 다시 돌렸습니다. "엔진이 결정적"과 "그 도구에게 시키면 같은 답이 나온다"는 다른 이야기입니다. weltiq 는 전략 문서가 먼저 고정되고 엔진이 그걸 읽는 구조라 실행 경로에 이 층이 없습니다 — 자연어를 문서로 수식화하는 자리는 있지만 거기서 나오는 것은 임의의 파이썬 코드가 아니라 선언적 문서입니다. 저쪽은 코드를 생성하므로 표현할 수 있는 규칙의 폭이 훨씬 넓고, 대신 그 코드가 맞는지를 매 실행마다 사람이 확인해야 합니다.
여기서 갈렸습니다 — 넷
하나. "0.01 BTC 고정"을 맞추려고 백테스트를 세 번 돌렸습니다. 저쪽 엔진은 수량이 아니라 비중(weight) 으로 포지션을 잡아서 "0.01 BTC 고정"을 그대로 넣을 칸이 없고, LLM 이 weight = POSITION_BTC * close.iloc[i] / INITIAL_CASH 로 번역했습니다. 이 코드가 의도대로 돌려면 config.json 의 initial_cash 가 코드 안의 INITIAL_CASH 와 같아야 하고, 에이전트는 그 값을 세 번 바꿔 가며 찾았습니다 — 1,000 → 10,000,000(수량이 약 1.0 BTC, 목표의 100배) → 100,000(수량 0.009993~0.010001). 2회차에서 100배가 나왔고 에이전트가 스스로 잡아서 3회차에 고쳤습니다.
정정. 이 글을 쓰면서 필자는 2회차의 중간 산출물을 최종본으로 잘못 읽고 "수량이 100배로 나갔고 아무도 못 잡았다"고 적을 뻔했습니다. 캡처를 다시 찍다가 화면의 수량이 0.01 인 것을 보고 발견했습니다. 틀린 쪽은 저쪽이 아니라 필자였습니다. 이 문단을 지우지 않고 남겨 둡니다.
진짜로 읽을 것은 여기입니다 — 비중 기반 엔진에 "수량 고정"을 넣으려면 자본금 값으로 우회해야 하고, 그 값을 맞추는 데 백테스트 3회와 LLM 호출 27회, 17분 49초가 들었습니다. weltiq 는 "sizing": {"qty": "0.01"} 한 줄이고 한 번에 끝납니다. 대신 weltiq 는 비중 기반 사이징을 아예 못 씁니다. 어느 쪽이 나은 게 아니라 축이 다른 물건입니다.
둘. 지정한 수수료가 그대로 붙지 않았습니다. 양쪽에 0.1% taker 양방향을 못 박았는데, 실제로 물린 수수료를 체결 금액으로 나눠 보니 갈렸습니다.
Vibe-Trading (artifacts/fills.jsonl · 8건 전부)
open notional=644.7138 fee=0.3224 -> 0.0500%
close notional=624.6759 fee=0.1249 -> 0.0200%
... (8건 모두 동일) 합계 2.0298 USDT
weltiq (원장 9건 전부)
notional=605.9442 fee=0.60594419 -> 0.1000%
notional=816.4807 fee=0.81648074 -> 0.1000% 합계 6.16770728 USDT
진입에 0.05%, 청산에 0.02% 가 붙었습니다. 8건 전부 이 두 값이고 예외가 없으니 실수가 아니라 규칙인데, 그 규칙이 config.json 에 넣은 commission: 0.001 과 다릅니다. 왜 그렇게 되는지는 이 글에서 확인하지 못했습니다 — 크립토 기본 요율이 덮어썼을 수도, 커미션을 양쪽에 나눠 붙이는 규칙일 수도 있습니다. 읽을 점은 이겁니다. 수수료를 지정했는데 다르게 붙으면, 그 백테스트의 손익은 내가 물어본 질문의 답이 아닙니다.
weltiq 체결 내역 화면
weltiq 쪽 체결 내역 화면. 볼 곳은 Fee 열입니다 — 0.01 @ 85,795.15 에 0.858, 0.001 @ 85,860.28 에 0.086. 전부 정확히 0.1% 입니다. 그리고 같은 화면에서 weltiq 버그도 하나 보입니다 — 상단 요약의 Turnover 가 비어 있고 Effective rate 가 NaN % 입니다. 이 계정에 USD·USDT 체결이 섞여 있어 합산이 안 되는 것인데, 에러 대신 NaN 을 그대로 화면에 내보내고 있습니다. 가리지 않고 그대로 싣습니다.
셋. 근거 가드가 답을 지웠습니다 — 그리고 이건 저쪽이 앞선 축입니다. 부탁한 다섯 숫자(봉 수·체결 수·왕복 수·실현손익·총수수료)가 전부 (omitted) 로 돌아왔고, 맨 아래에 이렇게 적혀 있었습니다.
54 figure(s) that could not be matched to this session's tool data were omitted. Observed OHLC range: BTC-USDT: 80857.6–86351.4 USDT (source: okx; currency conversion: none).
이 도구는 자기가 이번 세션의 도구 출력으로 대조할 수 없는 숫자를 답변에서 지웁니다. 초안을 세 번 스스로 물리고, 그래도 못 맞춘 숫자 54개를 가린 뒤에야 답을 내놨습니다. LLM 이 그럴듯한 숫자를 지어내는 문제에 대해 weltiq 에는 이런 층이 아예 없습니다. 동시에 이건 오탐도 냅니다 — 봉 수도 체결 수도 artifacts/ 안에 멀쩡히 있는데 가려졌고, 숫자는 검사하지만 문장은 검사하지 않는다는 것도 위의 "~0.01 BTC" 사례가 보여 줍니다. 정확한 평가는 좋은 층이고 아직 촘촘하지는 않습니다.
넷. avg_holding_days: 300.5. 3개월 백테스트인데 평균 보유가 300일로 찍혔습니다. trades.csv 의 holding_bars 는 151·407·635·9 이고 평균이 정확히 300.5 입니다 — 봉 수를 평균 내서 '일'이라고 라벨을 붙였습니다. 1시간 봉을 넣었는데 엔진 라벨이 daily 인 것과 같은 뿌리로 보입니다. holding_days 칸 자체는 6·16·26 으로 맞게 들어 있으니 요약 지표를 만드는 자리에서만 어긋났습니다.
weltiq 쪽만 돌린 칸 — 주문·무인 실행·복구
저쪽은 이 셋을 못 돌렸습니다. 주문이 나가는 프로필 13개가 전부 브로커 계정을 요구하고 이 글은 계정을 만들지 않았습니다. 빈칸이 아니라 "계정이 있으면 되는데 안 만들었다" 입니다.
weltiq 쪽은 한도 승인부터 막힙니다 — {"code":"envelope_required","detail":"이 심볼을 허용하는 주문 한도 승인이 없습니다"}. 그리고 그 한도는 API 키로는 못 만들고 브라우저 세션(사람)만 만들 수 있습니다. 한도를 만든 뒤 낸 왕복은 매수 53ms·매도 54ms 에 filled 이었습니다. 자율 에이전트가 한도를 스스로 넓힐 수 없다 — 이게 "테두리는 사람이 친다"가 코드로 구현된 자리입니다.
고리 한 바퀴는 절반만 했습니다. 분석 → 주문 → 체결 확인까지는 아래 그대로지만, 기록을 보고 규칙을 고쳐 다시 주문하는 단계는 안 했습니다. 이 편의 주제가 비교라 컨테이너를 하나만 쓰기로 했고, 이 글을 쓸 때는 규칙을 바꾸려면 컨테이너를 새로 띄워야 했습니다.
그 뒤 (2026-09-23): 정비 기능이 생겼습니다. 정지한 컨테이너의 전략만 갈아 끼우면 됩니다 — 컨테이너도, 모의 자본도, 그때까지의 손익도 그대로 이어집니다(정지 → 교체 → 재시작). 지금 이 실험을 다시 한다면 컨테이너 하나로 고리를 끝까지 돌 수 있습니다.
무인 컨테이너를 하나 띄웠습니다. 진입 신호가 나기 쉽게 이평을 2/3 으로 줄이고, 중지 조건은 일손익 −0.5 USDT · 실행 기한 1시간으로 걸었습니다. 중지 조건은 선택이 아니라 필수 필드라 빼면 아예 안 뜹니다. 그리고 무엇을 하고 있는지가 상태 기계로 남습니다.
02:12:13 started → 02:12:19 warmup → 02:13:09 entry_blocked (reason=short_not_supported)
→ 02:14:10 no_signal → 02:20:00 order_pending → 02:21:00 holding
02:20:00 에 golden_cross 신호로 0.01 BTC 를 85795.14865650 에 체결했고 수수료 0.85795149 USDT 가 원장에서 차감됐습니다. 같은 시각에 손절 지정가 매도가 stop_market 으로 같이 걸립니다 — 사람이 안 보고 있어도 테두리가 먼저 서 있습니다.
weltiq 컨테이너 상세
weltiq 쪽 같은 순간의 화면. 볼 곳은 세 군데입니다 — 상태 줄의 Holding — watching exit conditions, ORDERS 의 sell (close) / stop_market / acked(진입과 동시에 걸린 손절), 그리고 오른쪽 끝 Auto-stop −0.5 · 9/22, 03:12 AM. 에이전트가 꺼져 있어도 이 세 개는 돌아갑니다.
건 조건 두 개 중 실행 기한이 먼저 도달해 03:12:41 stopping → 03:13:27 stopped 로 넘어갔습니다. autostop {"reason":"expired","daily_pnl":"1.7125384300","position_kept":false} — 멈추면서 포지션도 닫았고 체결이 왕복으로 채워졌습니다(02:20:00 buy 0.01 @ 85795.14865650 fee 0.85795149 / 03:13:00 sell 0.01 @ 85966.40249950 fee 0.85966402).
여기서 weltiq 쪽 문제를 하나 발견했습니다. 로그의 daily_pnl 1.7125384300 은 (85966.40249950 − 85795.14865650) × 0.01 로 수수료를 빼기 전 값입니다. 실제로 원장에서 빠진 수수료는 두 건 합쳐 1.71761551 이니 수수료까지 넣으면 이 왕복은 −0.00507708 로 아주 살짝 마이너스입니다. 코드가 pnl = sign * (px - pos.entry_px) * qty * mult 로 수수료를 안 넣습니다. 그래서 stop_max_loss 는 수수료를 뺀 손실이 아니라 가격 차이만 보고 판정합니다.
다만 이번 실행이 손실 조건에 안 걸린 이유는 이 결함이 아닙니다. 수수료까지 넣은 순손익 −0.00507708 은 건 기준 −0.5 에 한참 못 미칩니다. 수수료를 제대로 뺐더라도 이 왕복은 애초에 발동할 자리가 아니었습니다. 결함이 실제로 드러나는 것은 수수료로 난 손실이 기준선에 닿는 경우이고, 잦게 거래하는 전략일수록 그 자리에 빨리 닿습니다. 시간 조건의 발동은 실제로 봤고, 손실 조건의 발동은 미확인입니다.
끊고 다시 붙이기는 일부만 했습니다. 엔진 프로세스 재시작은 이 환경에서 금지돼 있어 못 했고, 대신 멈춘 컨테이너를 다시 켰습니다. 그때 무엇이 넘어오는지를 API 가 명시적으로 돌려줍니다 — previous_stop_reasons: ["expired"], previous_cumulative_pnl: "1.7125384300", cumulative_pnl: "reset_to_zero", daily_pnl: "kept_if_same_trading_day", positions_carried: []. 무엇이 유지되고 무엇이 초기화되는지를 재개 응답에 문자열로 적어서 줍니다 — 추측할 필요가 없습니다. 엔진 프로세스 재시작 복구는 이 글에서 미확인입니다. 확인이 끝난 뒤 이 컨테이너는 정지·삭제했습니다.
5. 읽기 전용 커넥터 — 왜 "붙여서 같이"가 안 되나
이 절이 독자에게 제일 실질적인 정보라고 생각합니다.
HKUDS/Vibe-Trading 은 사용자가 자기 커넥터를 만들어 꽂을 수 있게 해 뒀습니다(connector.json + adapter.py 한 쌍). 그래서 weltiq 커넥터를 만들어 capabilities 에 orders.write 를 한 줄 넣고 검사했습니다.
쓰기 매니페스트 거부
볼 곳은 빨간 줄 둘입니다. 쓰기 권한을 선언하면 read capabilities only 로 거부되고, readonly 플래그만 false 로 내려도 must be read-only 로 거부됩니다. 읽기 전용으로 되돌려야만 통과합니다.
코드에도 그대로 있습니다 — agent/src/trading/local_plugins.py 가 _REQUIRED_CAPABILITIES = {"account.read", "positions.read"} 를 요구하고, _ALLOWED_CAPABILITIES 밖의 것이 하나라도 있으면 local connector plugins may declare read capabilities only 로 거부합니다. README 의 한 줄이 그대로 지켜집니다 — "A manifest declaring any write capability is rejected."
이건 나쁜 결정이 아닙니다. 남이 만든 코드에 주문 권한을 주지 않겠다는 건 방어적으로 옳은 선택입니다. 다만 그 결과로 weltiq 가 저쪽의 실행층으로 들어갈 문은 잠겨 있습니다.
MCP 로는 되나 — 됩니다, 그런데 게이트를 지납니다. 외부 MCP 서버의 툴을 자기 레지스트리에 끌어올 수 있는데 브로커성 서버는 다르게 다룹니다. 분류가 3단 사다리이고 — ① readOnlyHint ② 유지보수자가 관리하는 도구 이름 맵(맵이 이기고, 거짓 readOnlyHint 로 WRITE 를 못 낮춥니다) ③ 둘 다 아니면 UNKNOWN 이고 WRITE 와 똑같이 취급합니다(fail-closed) — WRITE/UNKNOWN 툴은 주문 게이트로 감싸집니다(위임장 → 만료 → 킬스위치 → 계좌 바인딩 → 주문 의도 파싱 → 포지션·잔고 조회 → 한도 검사).
요약하면: weltiq MCP 서버를 꽂는 것 자체는 막혀 있지 않습니다. 다만 주문 툴은 저쪽 게이트를 통과해야 하고, 그러면 테두리를 weltiq 가 치는 게 아니라 저쪽이 치게 됩니다. 중지 조건·한도·킬스위치가 두 군데에 생깁니다. 이건 통합이 아니라 이중화고, 그래서 이 편은 "붙여서 같이 쓰는 법"을 안내하지 않습니다. (이 대목은 코드를 읽은 것까지가 확인 범위입니다 — 실제로 꽂아 주문 툴을 호출해 보지는 않았습니다.)
대신 읽기는 실제로 이어 붙였습니다. weltiq 의 /v1/account/summary 를 읽는 어댑터를 70줄쯤 써서 설치했습니다.
$ vibe-trading connector validate ./plug/weltiq-paper → Valid read-only connector
$ vibe-trading connector account currency USDT · balance 548038.3639757 · equity 669503.4621897
$ vibe-trading connector positions BTCUSDT 1.4132 USDT
$ vibe-trading connector orders
profile 'weltiq-paper-live-readonly' does not support get_open_orders through the generic trading tool yet
읽기는 되고, 주문 경로는 애초에 등록조차 되지 않습니다. 포트폴리오 화면에 올리려니 한 번 더 막혔습니다 — "error": "portfolio currency is not a valid ISO-4217 code: USDT". 저쪽 포트폴리오 평가 코어는 ISO-4217 통화만 받고(USD·HKD·CNY) USDT 는 토큰 티커라 거부됩니다. 그리고 실패한 소스를 합계에 몰래 넣지 않고 제외한 뒤 스냅샷을 INCOMPLETE 로 표시합니다 — 옳은 동작이면서 크립토 페이퍼 장부를 그대로 올릴 수 없다는 실질적 한계입니다. 어댑터가 USD 로 보고하도록 고치자 바로 붙었습니다.
weltiq 페이퍼 장부가 Vibe-Trading 포트폴리오에 올라온 화면
볼 곳은 가운데 도넛 아래 범례입니다 — BTCUSDT·AAPL·NVDA 가 weltiq 페이퍼 계정 하나에서 올라왔습니다. 왼쪽 위 배지가 Local read-only · Multi-account 입니다. 읽기는 이렇게 이어지고, 이 화면에서 주문을 낼 방법은 없습니다. 오른쪽 Valuation coverage 15.5% 는 필자 어댑터가 현금과 포지션을 다 채우지 못해서 나온 값이지 저쪽 문제가 아닙니다.
Type 칸에 BTCUSDT 가 stock 으로 찍히는 것도 그대로 두었습니다. 직접 만든 커넥터는 기본이 Experimental 등급이고 저쪽이 화면에 "브로커별 검증이 필요합니다"라고 경고를 띄웁니다 — 숨기지 않는다는 뜻입니다.
6. 써 보니 — 축별로 어디가 낫고 어디가 못한가
weltiq 가 지는 축
| 축 | 근거 |
|---|---|
| 유통·설치 | pip install vibe-trading-ai 한 줄. ★33,780 · 포크 5,511. weltiq 는 공개 배포가 없습니다 |
| 표현할 수 있는 규칙의 폭 | 한 문단에서 signal_engine.py 를 직접 써서 돌립니다. weltiq 는 선언적 문서라 블록에 없는 규칙은 못 씁니다 |
| 화면 | 대시보드·차트·티어시트·귀속분석·상관행렬·알파 462개. 진입·청산이 캔들에 핀으로 찍히는 화면이 weltiq 에 없습니다 |
| 자료 폭 | OKX·yfinance·akshare·mootdx·CCXT 100+ 를 키 없이. weltiq 는 자기가 모은 봉만 씁니다 |
| 숫자 근거 가드 | 도구 출력으로 대조 안 되는 숫자를 답변에서 지웁니다. weltiq 에는 이 층이 아예 없습니다 |
| 재현 매니페스트 | config_hash·strategy_hash + 산출물 12개 sha256. weltiq 는 산출물 해시가 없습니다 |
| 청산 사유 기록 | reason 칸과 by_exit_reason 집계. weltiq 매도 주문에는 사유 칸이 아예 없습니다 |
| 한국 브로커 프로필 | KIS·Toss·Upbit 프로필이 이미 목록에 있습니다 |
| weltiq 결함 둘 | daily_pnl 이 수수료를 안 뺍니다 · 체결 요약이 Effective rate: NaN % 를 화면에 그대로 내보냅니다 · /v1/fees/compare 가 혼합 통화에서 안 돕니다 |
weltiq 가 앞서는 축
| 축 | 근거 |
|---|---|
| 계정 없이 주문 | 저쪽 페이퍼 주문 프로필 13개가 전부 브로커 계정 필요. weltiq 는 키 하나로 53ms 에 체결 |
| 중지 조건이 필수 | 없이는 컨테이너가 안 뜹니다. 기한 도달 시 포지션까지 닫는 것을 실제로 확인 |
| 무인 상주의 기록 | warmup → entry_blocked → no_signal → order_pending → holding 이 초 단위로 남습니다 |
| 한도는 사람만 | API 키로는 주문 한도를 못 만듭니다. 브라우저 세션만 가능 |
| 지정한 수수료가 그대로 | 0.1% 를 넣으면 9건 전부 0.1000%. 저쪽은 같은 설정에서 진입 0.05%·청산 0.02% |
| 수량 선언의 직접성 | "qty": "0.01" 한 줄. 저쪽은 비중 기반이라 자본금 값으로 우회해야 하고 3회 조정이 들었습니다 |
| 수수료 = 원장 | fills_fee 와 ledger_fee 가 matched: true |
무승부 — 엔진 재현성(양쪽 다 같은 입력 → 같은 출력, 저쪽은 산출물 sha256 까지 동일) · 같은 규칙에서 나온 왕복 수(둘 다 4) · 외부 확장에 주문을 안 주는 것(저쪽은 매니페스트를 거부, weltiq 는 세션 없이는 한도를 안 넓힙니다 — 방식이 다를 뿐 방향이 같습니다).
정직하게 못 잰 축
- 저쪽의 주문·중지·복구: 브로커 계정을 안 만들어서 한 건도 못 쟀습니다. 이 비교의 가장 큰 구멍입니다
- 저쪽 백테스트의 슬리피지·체결 모델: 산출물에
fee는 있지만 체결가가 어떻게 정해지는지는 미확인 - 저쪽 수수료가 왜 0.05%/0.02% 로 붙었는지: 8건 전부 일관되니 규칙인 것은 분명한데 확인하지 못했습니다. 필자 설정 실수일 가능성도 배제하지 않습니다
- 비중 기반 사이징 자체: weltiq 에 없는 기능이라 나란히 잴 수가 없었습니다. "비중 고정을 weltiq 가 아예 못 한다" 는 반대 방향은 재지 않았습니다
- N=1: 한 판입니다. 어느 쪽이 낫다는 결론을 낼 수 없고, 내지 않았습니다
7. 그래서 어느 쪽인가 — 당신 상황 기준으로
우열을 매기지 않겠습니다. 대신 무엇을 하려는지로 갈라 드립니다.
| 이런 상황이면 | 이쪽 |
|---|---|
| 아이디어가 아직 문장 상태고 규칙으로 옮기는 것부터 도움이 필요하다 | HKUDS/Vibe-Trading |
| 여러 시장(A주·홍콩·미국·크립토) 자료를 키 없이 한자리에서 받고 싶다 | HKUDS/Vibe-Trading |
| 차트·티어시트·귀속분석이 붙은 보기 좋은 리서치 산출물이 필요하다 | HKUDS/Vibe-Trading |
| 이미 브로커 계정과 API 키가 있고 그 계좌의 페이퍼 환경에서 돌리려 한다 | HKUDS/Vibe-Trading |
| 브로커 계정을 만들기 전에 주문을 실제로 내 보고 싶다 | weltiq |
| 노트북을 닫아도 포지션과 중지 조건이 계속 돌아야 한다 | weltiq |
| 에이전트에게 맡기되 에이전트가 못 넘는 한도를 사람이 치고 싶다 | weltiq |
| 체결 하나하나의 수수료가 원장과 맞는지 대조해야 한다 | weltiq |
둘 다 쓰는 법은 한 가지뿐입니다. 저쪽에서 규칙을 만들고, 그 규칙을 사람이 읽어서 weltiq 전략 문서로 옮기고, weltiq 컨테이너에서 돌린 뒤, weltiq 페이퍼 장부를 읽기 전용 커넥터로 저쪽 포트폴리오에 되먹이는 것. 5절에서 실제로 해 봤고 됩니다. 다만 자동으로 이어지지는 않습니다 — 가운데 사람이 한 번 들어갑니다.
정정 예고 — 요금을 한 자리까지 못 박지 못했습니다. 2절에서 달러 값을 안 적은 이유입니다. 쓴 OpenRouter 키가 다른 실험들과 함께 쓰는 키라, 계정 전체 사용량 증가분(이 작업 구간에서 $1.584998)에는 이 실행이 아닌 것도 섞여 있습니다. 그래서 토큰 수만 적었습니다 — 그건 도구가 자기
llm_usage.json에 남긴 이 실행만의 값이라 확실합니다. 실행 직후 측정한 계정 증가분은 $0.987102 였고 이게 이 한 판의 상한입니다.
8. 다음 편 예고
18편은 LangAlpha 입니다.
이번 편에서는 도구가 낸 판단을 규칙으로 사람이 옮기는 자리를 봤습니다. 18편은 그 자리를 도구 쪽이 미리 채워 두는 쪽입니다 — LangAlpha 는 종목 하나를 놓고 진입 조건·목표·무효 가격·시간 청산·크기를 한 장에 같이 적습니다. "왜 사는가" 옆에 "어디서 틀렸다고 인정하는가" 가 함께 오는 것이죠. 그 무효 가격이 주문 조건으로 그대로 옮겨지는지, 그리고 옮긴 뒤에 무엇이 남는지를 봅니다.
weltiq 준비 — 3줄 1. 가입: {{CTA_URL}} 2. 마이 → 연결 관리에서 API 키 발급 3.
claude mcp add weltiq --header "Authorization: Bearer <키>" https://weltiq.ai/mcp브로커 계정을 만들기 전에 같은 규칙을 실제 주문으로 내 보고 싶으시면 그때 보실 자리입니다.
이 글의 모든 명령·출력·숫자는 2026-09-22 에 실제로 돌려서 얻었습니다. 브로커 계정을 하나도 만들지 않았고 주문이 나간 곳은 weltiq 페이퍼뿐입니다. 어떤 매매 의견도 아니며, 종합적으로 어느 도구가 낫다고도 말하지 않습니다. 여기 적지 않은 시행착오·원문 로그·돌리지 않은 항목은 검증 기록에 있습니다.