QuantDinger 설치부터 실전까지 — PostgreSQL·Redis까지 직접 세우는 자체호스팅 퀀트 플랫폼

도커 없이 QuantDinger 5.4.1 을 소스에서 4분 53초 만에 띄우고, 대시보드·Agent Gateway·MCP 를 전부 돌려 본 기록입니다. 지표 함수의 실제 버그 하나와, weltiq 체결 기록으로 파라미터가 바뀌는 고리 한 바퀴까지.

14편. QuantDinger — 전략 런타임을 통째로 자기 서버에 세우는 물건

이 글의 완료 조건: 독자는 도커 없이 백엔드·PostgreSQL·Redis 둘·대시보드를 세우고, 키 없이 시세를 받고, 백테스트 격자를 돌려 파라미터 하나를 고르면 성공입니다.

하는 일 AI 시장 리서치, 파이썬 지표·전략 편집기, 서버측 백테스트, 페이퍼·실전 체결, 그리고 웹·모바일·API·Agent Gateway·MCP 라는 다섯 개의 입구가 한 저장소 안에 들어 있습니다
맞는 사람 파이썬으로 전략을 직접 쓰고 차트 위에서 눈으로 확인하면서 고치고 싶은 사람. 시장 데이터·전략 코드·브로커 자격증명을 자기 통제 아래 두고 싶은 사람
안 맞는 사람 "설치 없이 바로"를 원하는 사람(PostgreSQL 과 Redis 둘을 세워야 시작됩니다) · 24시간 상주만 필요하고 전략은 이미 정해진 사람(가진 것의 대부분을 안 쓰면서 운영 부담만 집니다)
이 글의 결과물 도커 없이 로컬에 선 전체 스택, 키 0개로 받은 시세, 백테스트 격자 넷의 결과, 그리고 지표 함수에서 찾은 실제 버그 하나
준비물·비용 코드 0원(Apache-2.0, 계정 가입 없음) · 모델 AI 기능을 쓸 때만(OpenRouter·OpenAI·DeepSeek 중 하나) · 시세 0원, 거래소 API 키 0개 · 디스크 1.5 GB
시간·난도 받아서 healthy 까지 4분 53초(순수 설치는 15초 남짓). 난도 상 — 이 연재에서 제일 무겁습니다
weltiq 와의 관계 많이 겹칩니다. 그래서 5절은 "끼워 넣는 법"이 아니라 어느 쪽을 어디에 두는 게 나은가 입니다. 실제로 돌린 조합은 판단은 QuantDinger, 체결은 weltiq, 그 기록을 다시 QuantDinger 로
검증 환경·날짜 macOS 26.6.2 · arm64 · Python 3.12 · PostgreSQL 16.13 · Redis 8.6.2 · Node 26.8.1 · 소스 5.4.1(커밋 96cec2dc) · 2026-09-22 01:45~02:40 KST
제휴 고지 필자는 weltiq 로부터 이 연재의 원고료를 받습니다. 도구 평가는 필자가 직접 돌린 결과만 씁니다

사람 쪽 문장은 실제로 친 명령을 대화로 옮긴 것이고, 출력은 실측이며 숫자는 손대지 않았습니다. 주문은 전부 weltiq 페이퍼(모의)에서 냈고 실계좌 연결은 한 건도 없습니다. QuantDinger 쪽에는 거래소 API 키를 한 개도 넣지 않았습니다 — 시세 조회와 백테스트는 키 없이 됩니다. 어떤 매매 의견도 아닙니다. 원문 로그는 검증 기록에 있습니다.


1. 이게 뭔가요

OpenByteInc/QuantDinger 는 스스로를 "오픈소스 AI 트레이딩 OS" 라고 부릅니다. 과장된 이름 같지만 열어 보면 납득이 갑니다. 중요한 건 자체호스팅이라는 점입니다 — README 첫 문단이 "시장 데이터, 전략 코드, 브로커 자격증명, 배포를 운영자 통제 아래 둔다"고 적고 있고, 실제로 클라우드 계정을 만들 필요가 없습니다. 이 글도 계정 가입 없이, 인터넷에서 받은 건 소스 코드와 파이썬 패키지뿐입니다.

자리로 보면 판단도 하고 실행도 합니다. AI 가 트레이딩 아이디어를 파이썬 전략으로 옮기고, 그 전략을 거래소 커넥터로 실제 주문까지 보냅니다. 실행층이 app/services/live_trading/ 에 격리돼 있고 README 가 "어댑터 확장(adapter extensions)"이라고 명시해 둬서 비워 둔 자리가 있는 쪽으로 봤는데, 그 판단이 맞았는지는 5절에서 코드를 열어 확인합니다.

항목 값 확인 방법
주소 github.com/OpenByteInc/QuantDinger
라이선스 Apache-2.0 GitHub API
별 수 ★11,912 · 포크 2,450 (관측일 2026-09-22) GitHub API
마지막 푸시 2026-09-21 08:01 UTC GitHub API
개설 2025-12-28 · 열린 이슈 41 · 워처 78 · 아카이브 아님 GitHub API
소스 버전 VERSION 파일 5.4.1 (받은 커밋 96cec2dc) 저장소
프런트엔드 별도 저장소 OpenByteInc/QuantDinger-Vue README
MCP 패키지 PyPI quantdinger-mcp 0.6.2, 툴 58개 3절에서 직접 셉니다
Agent Gateway 경로 51개 docs/agent/agent-openapi.json
백엔드 크기 파이썬 파일 444개 · 컴포즈 서비스 15개 find app -name "*.py" \| wc -l

사소한 혼란이 하나 있습니다. VERSION 파일은 5.4.1 인데 서버가 기동하며 찍는 배너는 QuantDinger Python API v2.2.2 입니다. 둘은 다른 번호 체계를 씁니다 — 이슈를 올리실 때는 둘 다 적으시는 편이 안전합니다.


2. 설치

공식 두 경로는 둘 다 도커를 요구합니다.

무엇 전제
Option A curl -fsSL .../install.sh \| bash — GHCR 이미지를 받아 컴포즈로 기동 Docker + Compose v2
Option B 소스 체크아웃 후 docker compose up -d --build Docker + Compose v2

이 글을 쓴 맥에는 도커가 없었습니다. 그래서 세 번째 길로 갔습니다 — 컴포즈 파일이 무엇을 띄우는지 읽고 그걸 그대로 로컬 프로세스로 세우는 겁니다. 결과부터 말씀드리면 됩니다. 그리고 도커보다 빨랐습니다.

준비물은 파이썬 3.12, PostgreSQL 16.13(배지는 18 이라고 적혀 있지만 16 에서 그대로 돌았습니다), Redis 8.6.2 두 개(캐시용·작업큐용 — 축출 정책이 달라 분리해 둔 QuantDinger 의 설계입니다), 웹 대시보드를 쓸 때만 Node.js 입니다.

# ① 받기 — 2.581초, 37 MB
git clone --depth 1 https://github.com/OpenByteInc/QuantDinger.git

# ② 파이썬 의존성 — 130개. 캐시 비우고 6.27초 / 캐시 있으면 3.75초. 가상환경 475 MB
uv venv -p 3.12 venv
uv pip install --python venv/bin/python -r QuantDinger/backend_api_python/requirements.txt

# ③ DB 와 큐 — 기본 포트(5432/6379)를 쓰지 마세요. 전용 클러스터를 9080번대에
export LC_ALL=C LANG=C
initdb -D pgdata -U tut14 --auth=trust -E UTF8 --locale=C          # 0.37초
pg_ctl -D pgdata -o "-p 9080 -c unix_socket_directories= -c listen_addresses=127.0.0.1" -l pg.log start
psql -h 127.0.0.1 -p 9080 -U tut14 -d postgres -c "CREATE DATABASE tut14_quantdinger;"
redis-server --port 9081 --dir redisdata --dbfilename cache.rdb --daemonize yes --save ""
redis-server --port 9082 --dir redisdata --dbfilename jobs.rdb --daemonize yes --appendonly no

무거운 건 pandas 3.0.6, ccxt 4.5.82, TA-Lib 0.7.1, litellm, alpaca-py, ib_insync 입니다. TA-Lib 은 보통 C 라이브러리를 따로 깔아야 해서 악명이 높은데 0.7.x 휠에 들어 있어 그냥 깔렸습니다.

설정은 env.example 을 .env 로 복사하는 것인데, 724줄이지만 실제로 손대야 하는 건 여섯 칸입니다 — SECRET_KEY·CREDENTIAL_ENCRYPTION_KEY(각각 secrets.token_hex(32)), ADMIN_USER·ADMIN_PASSWORD(123456 은 원클릭 설치기도 거부합니다), DATABASE_URL, 그리고 REDIS_*·CELERY_REDIS_*·PYTHON_API_* 의 호스트·포트.

cd backend_api_python && LC_ALL=C python run.py

여기까지 오면 제대로 된 겁니다. 스키마가 알아서 올라갑니다.

DB_POOL_MAX=auto selected max=50
  (postgres max_connections=100, reserved=3, reserve_for_other_clients=20, gunicorn_workers=1)
[migration] schema-init: applying 136452 bytes
[migration] market-symbols-master: applying 1845804 bytes
[migration] strategy-v2-templates: applying 37490 bytes
[migration] all components complete
DB permission probe OK (10 critical tables readable)   Created user: tut14admin (id=1)
Agent Gateway v1 mounted at /api/agent/v1
QuantDinger Python API v2.2.2   Service starting at: http://127.0.0.1:9083

SKIP_AUTO_MIGRATE=false 가 기본이라 부팅할 때마다 스키마를 맞춥니다(전부 CREATE TABLE IF NOT EXISTS 라 무해합니다). 끝나면 테이블 90개, 데이터베이스 62 MB 이고 그중 1.8 MB 짜리 market-symbols-master 가 전 세계 종목 마스터 14,171행을 심습니다. 풀 크기 자동 계산이 인상적이었습니다 — max_connections 를 실제로 읽고 다른 클라이언트 몫 20개를 떼어 둔 뒤 자기 몫 50을 잡습니다.

git clone 01:45:05 → /api/health 가 healthy 01:49:58. 다 합쳐 4분 53초입니다. 디스크는 파이썬 가상환경 475 MB + 프런트 node_modules 433 MB + PG 데이터 155 MB + 저장소 64 MB = 1.5 GB. 메모리는 백엔드 파이썬 464 MB, PostgreSQL 23 MB, Redis 둘 합쳐 8.4 MB 이고 vite 개발 서버를 같이 띄우면 578 MB 가 더 붙습니다. "무거운 도구"라는 예상은 절반만 맞았습니다 — 부품 수는 많은데(프로세스 5개, 포트 5개) 5분 중 4분은 필자가 막힌 데서 헤맨 시간이고 순수 설치는 15초 남짓입니다.

설치 직후 첫 화면 — QuantDinger 웹 대시보드의 로그인 창

대시보드를 127.0.0.1:9084 로 띄우고 처음 만나는 화면입니다. 왼쪽 제품 소개는 번들에 들어 있는 것이고, 오른쪽 Welcome to QuantDinger 가 .env 의 ADMIN_USER/ADMIN_PASSWORD 로 들어가는 자리입니다. 회원 가입도 외부 계정 연동도 없습니다 — 여기 들어가면 이미 관리자입니다.

막히는 자리 — 일곱 개, 숨기지 않겠습니다

이 편의 값어치는 여기 있습니다. 실제로 걸린 곳 전부입니다.

# 어디서 무슨 메시지 어떻게 넘어갔나
1 설치 시작 docker not found 공식 두 경로가 다 막힙니다. 컴포즈 파일을 읽고 소스로 직접 세웠습니다
2 initdb invalid locale settings; check LANG and LC_* environment variables export LC_ALL=C LANG=C 후 --locale=C
3 pg_ctl start Unix-domain socket path is too long (maximum 103 bytes) 데이터 경로가 길어서입니다. -c unix_socket_directories= 로 소켓을 끄고 TCP 만 씁니다
4 pg_ctl start (2차) postmaster became multithreaded during startup 2번의 LC_ALL=C 를 pg_ctl 을 부르는 셸에도 걸어야 합니다
5 Agent 토큰 발급 에러 없이 HTTP 200. 그런데 GET /markets 가 빈 배열 {"markets":["Crypto"]} 처럼 JSON 배열을 주면 ["['Crypto']"] 로 저장돼 어느 시장도 허용되지 않습니다. "markets":"Crypto" 처럼 쉼표로 구분한 문자열을 줘야 합니다(app/utils/agent_auth.py:231 의 parse_csv_list(str)). Agent OpenAPI 는 이 본문을 그냥 object 로만 적어 둬서 모양을 알 길이 없습니다
6 AI 전략 생성 {"error":"strategy.invalidAssetType"} assetType 의 허용값은 script 와 portfolio_strategy 둘뿐입니다. 자산군이 아니라 전략 종류를 넣는 칸입니다
7 AI 전략 생성 {"error":"strategyV2.aiBehaviorOpenLegMissing:long"} 모델을 둘 바꿔 세 번 시도했는데 전부 같은 곳에서 거절. 4절이 통째로 이 이야기입니다

5번이 제일 고약했습니다. 실패인데 200 이 돌아오기 때문입니다. 토큰은 발급되고 발급 응답에도 "markets": ["['Crypto']"] 라고 찍히는데, 따옴표가 하나 더 있는 걸 눈치채지 못하면 그다음 모든 요청이 조용히 빈 결과를 돌려줍니다.


3. 첫 결과

키를 하나도 안 넣은 상태에서 뭐가 되는지부터 확인합니다.

이렇게 말합니다

방금 띄운 QuantDinger 에 로그인해서 비트코인 현재가랑 최근 일봉 다섯 개만 받아 줘.

그러면 이런 게 돌아옵니다

POST /api/auth/login                                   HTTP 200
  role=admin  permissions=[dashboard, view, indicator, backtest,
                           strategy, portfolio, settings, user_manage, credentials]
GET /api/agent/v1/price?market=Crypto&symbol=BTC/USDT  HTTP 200
  price 85939.4   high 86351.4   low 80588.0   open 81288.6   +5.72%
  source "ticker"  quote_type "latest_available"
GET /api/agent/v1/klines?...&frequency=1d&limit=5      HTTP 200
  2026-09-19  O 80903.0  H 81953.1  L 80847.5  C 81265.8  V 4232.76
  2026-09-20  O 81265.8  H 81500.0  L 80133.4  C 81179.6  V 3666.85
  2026-09-21  O 81180.1  H 86351.4  L 80857.6  C 85939.4  V 10228.51

이렇게 나오면 성공입니다. 거래소 키를 하나도 안 넣었는데 실시간 시세가 옵니다 — 시세는 ccxt 의 공개 엔드포인트를 씁니다. 키가 필요한 건 주문뿐입니다.

대시보드 — 프런트엔드는 별도 저장소입니다

여기서 한 번 놀랐습니다. 백엔드 저장소에 프런트엔드 소스가 없습니다. 컴포즈 파일은 ghcr.io/openbyteinc/quantdinger-frontend 이미지를 받아 쓰고, 도커가 없으면 그 길이 막힙니다. README 673번째 줄에 답이 있습니다 — 소스는 별도 저장소 OpenByteInc/QuantDinger-Vue 입니다.

git clone --depth 1 https://github.com/OpenByteInc/QuantDinger-Vue.git && cd QuantDinger-Vue
npx --yes pnpm@10.33.4 install --frozen-lockfile          # 6.47초
VITE_DEV_PROXY_TARGET=http://127.0.0.1:9083 \
  npx --yes pnpm@10.33.4 exec vite --port 9084 --host 127.0.0.1   # 기동 871밀리초

의존성 설치 6.47초, 기동 871밀리초 — 도커 이미지를 받는 것보다 빠릅니다. 프런트엔드가 백엔드를 찾는 길은 VITE_DEV_PROXY_TARGET 하나이고, 안 주면 기본값 http://127.0.0.1:5000 으로 찾아가서 아무것도 안 뜹니다. 부품이 많으니 어느 포트가 무엇인지 표로 둡니다.

포트 무엇 기본값 바꾸는 곳
9084 웹 대시보드 — 여기로 들어갑니다 8888 vite --port / FRONTEND_PORT
9083 백엔드 API (/api/...) 5000 .env 의 PYTHON_API_PORT
9080 PostgreSQL 5432 DATABASE_URL · pg_ctl -o "-p"
9081 · 9082 Redis (캐시 · 작업큐) 6379 REDIS_PORT · CELERY_REDIS_PORT

로그인은 .env 에 적은 계정뿐이고 가입 절차도 이메일 인증도 없습니다. 거래소 키는 시세·차트·백테스트·전략 편집에 전부 불필요하고 주문에만 필요합니다. LLM 키는 AI 기능에만 필요합니다. 과금은 BILLING_ENABLED=false 가 기본이라 자체호스팅에서는 제한이 없습니다.

로그인 직후 — AI Research 작업공간

처음 열리는 화면입니다. 오른쪽 Watchlist 의 MSFT 496.19, TSLA 373.38, SOL/USDT 117.74 같은 값은 기본 관심종목 7개가 실시간으로 채워진 것입니다 — 로그인만 했는데 시세가 붙어 있다는 게 여기서 확인됩니다. 가운데 What are you researching? 는 LLM 키가 있어야 답합니다.

처음 오면 뭘 눌러야 할지 감이 안 옵니다. 필자 경험으로는 위 메뉴에서 Indicator 를 먼저 누르시는 게 좋습니다 — 키 없이 바로 쓸 수 있으면서 이 도구가 뭘 하는 물건인지 한 화면에 다 보여 주거든요.

Indicator 화면 — 왼쪽 파이썬 편집기, 오른쪽 차트와 주문 패널

이 한 장이 QuantDinger 의 대표 그림입니다. 왼쪽은 번들 샘플 지표(SuperTrend)의 파이썬 소스 — 주석에 "최종 밴드는 경로 의존적이라 벡터화 대신 바 단위 루프를 쓴다", "직전 봉의 최종 밴드하고만 비교해서 look-ahead 가 없다"까지 적혀 있습니다. 가운데 위는 그 지표가 얹힌 BTC/USDT 일봉 차트. 아래는 Quick Trade 주문 패널이고, 오른쪽 끝 AI Decision Filter 토글이 진입 전에 LLM 에게 한 번 물어보는 장치입니다. 편집기·차트·주문이 한 화면에 있는 게 이 도구의 성격을 그대로 말해 줍니다.

대화로 모는 분을 위해 MCP 도 확인했습니다. uv pip install "quantdinger-mcp==0.6.2" 가 1.39초이고, stdio 로 붙어 tools/list 를 세어 보니 58개였습니다 — whoami·get_klines 같은 읽기부터 submit_backtest, save_strategy_source, place_quick_order, emergency_stop_trading 까지 있습니다. 설계 둘이 눈에 띕니다: 모든 쓰기 툴이 idempotency_key 를 요구하고(재시도해도 두 번 나가지 않습니다), 주문·정지·알림 발송은 confirm_*=true 를 따로 요구합니다(LLM 이 실수로 부르는 걸 막는 장치입니다).


4. 제일 잘 쓰는 법 — 말 한마디가 돌아가는 파이썬 전략이 되는가

QuantDinger 의 첫 줄이 "트레이딩 아이디어를 파이썬 전략으로 바꾼다" 이니 여기부터 재야 합니다.

이렇게 말합니다

BTC/USDT 1시간봉에서, 빠른 EMA가 느린 EMA를 위로 지날 때 롱으로 들어가고 아래로 지날 때 전량 청산하는 전략을 만들어 줘. ATR 기준 손절을 넣고, 파라미터는 밖에서 바꿀 수 있게 해 줘.

그러면 이런 게 돌아옵니다

POST /api/strategies/generate          HTTP 400   (36.3초)
{"msg":"strategyV2.generationInvalid",
 "data":{"error":"strategyV2.aiBehaviorOpenLegMissing:long"}}
서버 로그:
  repairing invalid generated strategy attempt=1/2: strategyV2.aiBehaviorOpenLegMissing:long
  repairing invalid generated strategy attempt=2/2: strategyV2.aiBehaviorOpenLegMissing:long
  strategy generation failed: strategyV2.aiBehaviorOpenLegMissing:long

프롬프트를 더 단순하게 바꿔 다시(46.0초) — 같은 곳. 모델을 openai/gpt-5.6-luna 에서 anthropic/claude-sonnet-5 로 바꿔 다시(177.4초) — 또 같은 곳. 세 번 다 실패했고, 실패하면 코드를 한 줄도 돌려주지 않습니다.

그 게이트가 뭔가요 — 이게 이 도구의 제일 좋은 부분입니다

app/services/strategy_ai_behavior.py 를 열어 봤습니다. QuantDinger 는 AI 가 낸 전략을 컴파일만 하고 끝내지 않습니다. 결정론적으로 만든 합성 봉 위에서 실제로 돌려 보고, 요청한 방향으로 포지션을 한 번이라도 열었는지 확인합니다.

# _scenario_frame() — 난수가 없습니다. 사인파 둘에 국면 전환을 얹은 결정론적 프레임
primary   = 320.0 * math.sin(offset / 18.0)
secondary =  95.0 * math.sin(offset / 5.5)
regime    = 90.0 if (offset // 72) % 2 == 0 else -90.0
closes.append(max(100.0, 2500.0 + primary + secondary + regime))

# 롱 전용이라고 선언했으면 롱을 실제로 열어야 통과
if _required_open_sides(manifest) - opened_sides:
    raise StrategyV2ContractError(f"strategyV2.aiBehaviorOpenLegMissing:{detail}")

"컴파일되는 코드"와 "실제로 거래하는 코드"를 구분하는 게이트입니다. 이 연재에서 본 AI 전략 생성 중 가장 엄격합니다 — 문법만 맞고 아무것도 안 하는 전략을 돌려주는 도구가 훨씬 많거든요. 게이트 자체는 멀쩡합니다. 번들 예제를 같은 게이트에 직접 넣어 봤더니 통과합니다.

번들 예제 strategy_v2_dual_ema_long.py → PASS
  {'executed': True, 'sample_count': 500, 'total_executions': 182, 'opened_sides': ['long']}

그런데 왜 AI 코드는 매번 떨어질까 — 지표 함수에 버그가 있습니다

생성된 코드를 라우트가 안 돌려주니, 같은 프롬프트·같은 모델로 파이프라인을 그대로 재현해 코드를 꺼냈습니다. 나온 99줄은 읽어 보면 맞는 코드였습니다 — 골든크로스에 진입, 데드크로스에 청산, ATR 손절. 그런데 한 번도 진입하지 않습니다. 로그를 끼워 넣어 추적했더니 원인이 나왔습니다.

bar=50   fp=2856.67  fc=2850.89  sp=2856.67  sc=2850.89  pos=0.000000
bar=300  fp=2471.50  fc=2457.93  sp=2471.50  sc=2457.93  pos=0.000000

ema(5) last=2457.9258 · ema(200) last=2457.9258 · identical=True     ← period 가 무시된다
timeperiod= : ema(5)=2312.3110  ema(200)=2528.1882  identical=False  ← 이 이름은 먹는다
sma period= : sma(5)=2311.5846  sma(200)=2526.9734  identical=False  ← SMA 만 먹는다
pandas ewm(5)=2312.3110                                              ← timeperiod=5 와 정확히 일치

빠른 EMA 와 느린 EMA 가 소수점까지 똑같습니다. 교차가 일어날 수 없으니 영원히 진입하지 않습니다. 원인은 app/services/factors/talib_adapter.py:92 세 줄입니다.

if library_id == "SMA" and "period" in normalized:
    period = normalized.pop("period")
    normalized.setdefault("timeperiod", period)

period → timeperiod 이름 바꿔치기가 SMA 일 때만 걸려 있습니다. EMA·RSI·ATR 등은 period=5 를 TA-Lib 에 그대로 넘기는데, TA-Lib 의 abstract 인터페이스는 모르는 인자를 조용히 버리고 기본값 timeperiod=30 을 씁니다. 그래서 ema(period=5) 도 ema(period=200) 도 전부 EMA(30) 입니다.

그리고 AI 가 period= 를 쓰는 건 자연스럽습니다 — QuantDinger 자신의 작성 규약(/strategy-sources/authoring-contract)이 예시로 indicator("supertrend", symbol, period=period, ...) 라고 적어 두고 있거든요. 그 예시의 supertrend 는 TA-Lib 이 아니라 내장 지표라 제대로 동작하고, TA-Lib 쪽으로 가는 순간 무시됩니다. 이건 AI 생성만의 문제가 아닙니다 — 같은 indicator() 가 모든 백테스트에서 쓰입니다. 사람이 손으로 indicator("rsi", sym, period=14) 라고 써도 RSI(30) 이 나옵니다. 조용히, 경고 없이.

회피법: TA-Lib 지표에는 period= 말고 TA-Lib 자신의 인자 이름을 쓰세요 — timeperiod=, fastperiod=, slowperiod=. 실제로 docs/trading/STRATEGY_DEV_GUIDE.md 485줄의 MACD 예시는 fastperiod=12, slowperiod=26 이라고 올바르게 적혀 있습니다. 규약 문서와 개발 가이드가 서로 다른 이름을 쓰고 있는 셈입니다.

대신 이건 아주 잘합니다 — 대화로 고치기

같은 AI 인데 대화 모드는 완전히 다릅니다. 전략 IDE 에서 같은 요구를 대화로 던졌더니 이렇게 답했습니다.

전략 IDE — AI Collaborate 가 요청과 현재 소스를 표로 대조

오른쪽 AI Collaborate 패널을 보세요. 요청(1시간봉·EMA·ATR 손절)과 현재 소스(1분봉·SMA·고정 5% 손절)를 항목별 표로 대조하고, 아래 「주요 제한」에 "빠른/느린 기간은 각각 EMA 파라미터처럼 이름 붙였지만 실제로는 SMA 기간입니다" 라고 스스로 지적합니다. 왼쪽 편집기는 필자 소스 그대로 — "Apply 를 누르기 전에는 편집기를 바꾸지 않는다" 가 패널 안내문에 적혀 있습니다.

한 방에 코드를 뽑는 건 못 하는데, 옆에 앉아 같이 읽는 건 잘합니다. 6절의 "이런 분에게 맞다"가 여기서 나옵니다.


5. weltiq 로 옮기기

먼저 솔직하게 적습니다. 이 도구는 weltiq 와 많이 겹칩니다. 앞선 편들의 도구 대부분은 "판단은 하는데 주문 낼 자리가 비어 있다"였는데 QuantDinger 는 아닙니다 — 전략 편집기도, 체결 가정을 명시하는 백테스트 엔진도, 원장 대조도, 거래소 여덟 곳 커넥터도, 실전 주문도 있습니다. 그래서 이 절은 "끼워 넣는 법"이 아니라 어느 쪽을 어디에 두는 게 나은가 입니다.

사람이 옮기는 순서

QuantDinger 에서 파라미터를 고르고(POST /api/agent/v1/backtest/run) → weltiq 규칙 문서로 다시 쓰고(POST /v1/strategy-rules) → 근거를 본인의 말로 적고(POST /v1/ai/thesis, 안 적으면 422 thesis_required 와 세 질문) → 한도와 함께 컨테이너를 띄웁니다(POST /v1/containers, 중지 조건이 없으면 422 Field required). 옮기다 한 번 막혔습니다 — 청산을 kind: "exit" 로 적었더니 invalid_order_rule 이 떴습니다. weltiq 문법에서 exit 은 가격 레벨(stop/limit)을 요구하는 칸이고, 조건으로 전량 청산하는 건 close_all 입니다.

제일 나은 조합 — 비용 되먹임

QuantDinger 의 백테스트는 비용을 인자로 받습니다. POST /backtest/run 이 commission 과 slippage 를 받고 결과에 executionAssumptions 로 자기가 쓴 값을 전부 돌려줍니다. 그러면 그 인자에 실측값을 넣는 자리가 생깁니다.

① QuantDinger 가 판단합니다. 번들 예제(이동평균 두 개의 대소로 목표 비중을 정하는 전략)를 BTC/USDT 1분봉으로 옮기고 기간 조합 넷을 같은 창(2026-09-14~09-20, 10,080봉)에서 돌렸습니다. 한 판에 4.5초입니다.

fast / slow 수익률 거래 수수료 PF 최대낙폭
10 / 30 −32.82% 332 1,697.18 0.171 −32.90%
20 / 50 −19.00% 187 980.75 0.265 −19.11%
30 / 80 −16.00% 149 725.35 0.269 −16.22%
60 / 180 −5.34% 68 315.90 0.512 −8.62%

자본 10,000 에 수수료가 1,697 입니다. 이 주 비트코인 1분봉 이동평균 교차는 넷 다 손실이고, 느릴수록 덜 잃었습니다. 수수료 때문입니다.

백테스트 센터 — API 로 낸 여섯 판이 UI 에 그대로

오른쪽 Recent backtest runs 를 보세요. Agent Gateway 로 던진 백테스트 여섯 판이 웹 UI 에 그대로 쌓여 있습니다 — −5.34%, −16.00%, −19.00%, −32.82% 가 위 표의 값과 같습니다. API 로 한 일과 화면에서 한 일이 같은 기록에 남는다는 뜻입니다. 왼쪽 1m backtests can cover up to 29 days per run 은 분봉 백테스트의 상한 안내입니다.

백테스트 결과 — 체결 표시가 찍힌 차트와 체결 가정

맨 위 초록 띠를 보세요. "Fills, fees, positions, cash, and final equity passed independent ledger reconciliation" — 체결·수수료·포지션·현금·최종 자산이 독립 원장 대조를 통과했다고 적고 Ledger reconciled 배지를 답니다. 그 아래 Fill rule: Close signal → next bar open; scheduled order → current bar open, Commission 0.05%, Slippage 0.05%, Funding: Not modeled 까지 체결 가정을 전부 명시합니다. 차트의 초록 L/빨강 Exit 이 68번의 왕복입니다.

② weltiq 가 체결합니다. 고른 fast=60, slow=180 을 규칙 문서로 옮기고(sma(close, 60) 과 sma(close, 180) 의 대소로 1:1 변환, 손절 5% 는 예제 값 그대로) 같은 창을 리플레이한 뒤 컨테이너를 띄웠습니다.

POST /v1/backtest  → n_bars 10068 · n_fills 68 · n_trades 34
                     pnl 37.85877647000 · fees 53.41523101 · intent_outcomes {accepted: 68}  (0.6초)
POST /v1/containers → status=running · stop {max_loss 150, until …T19:13:31Z}
20초 뒤 첫 체결      buy  0.01 @ 85979.70711050  fee 0.85979707  taker  OPEN
정지하며 반대편       sell 0.01 @ 85861.83295750  fee 0.85861833         CLOSE

숫자를 나란히 놓기 전에 다른 점부터 밝힙니다. 이 둘은 그대로 비교하면 안 됩니다.

QuantDinger weltiq
창 2026-09-15 ~ 09-21 (9,673봉) 최근 168시간 (10,068봉)
수량 기준 자산의 95% (≈0.11 BTC) 고정 0.01 BTC
보호 손절 5% (가정 안에 포함) 5% (별도 stop 주문)
수수료 가정 0.05% 실측 요율
체결 63왕복 / 116체결 34왕복 / 68체결
수수료 합 303.20 53.42

수량 기준이 11배 다르므로 수익률·손익 숫자는 비교 대상이 아닙니다. 비교되는 건 체결 횟수의 자릿수와 수수료가 손익에서 차지하는 비중이고, 둘 다 같은 이야기를 합니다 — 이 규칙은 이 주에 너무 자주 들락거렸습니다.

weltiq 전략 컨테이너 상세 — QuantDinger 가 고른 규칙이 상주 중

고리가 이어진 증거입니다. 제목 아래 규칙 한 줄이 QuantDinger 가 고른 60/180 을 weltiq 문법으로 옮긴 것이고, SYMBOLS 행의 buy 0.01 @ 85979.70711050, ORDERS 의 두 줄(체결된 시장가 매수 + acked 상태의 보호 손절), FILLS 의 수수료 0.85979707 USDT 가 위 출력과 같은 기록입니다. 오른쪽 끝 Auto-stop −150 · 9/22, 04:13 AM 이 ①에서 친 테두리입니다.

③ 그 기록을 QuantDinger 에 되먹입니다. 체결 하나에서 실제 비용을 뽑았더니 수수료는 가정의 두 배(0.0010 vs 0.0005), 슬리피지는 5분의 1(0.0001 vs 0.0005) 이었습니다. 이 두 값을 그대로 넣어 같은 격자를 다시 돌립니다. 사람은 옮기기만 합니다 — 해석하거나 힌트를 주지 않습니다.

fast / slow 1차 (가정 비용) 2차 (실측 비용) 수수료 변화
10 / 30 −32.82% −35.55% 1,697 → 3,331
20 / 50 −19.00% −20.76% 981 → 1,941
30 / 80 −16.00% −17.33% 725 → 1,440
60 / 180 −5.34% −5.96% 316 → 630

순위는 안 바뀌었습니다. 60/180 이 여전히 1위이고 되먹임이 결론을 뒤집지 못했습니다 — 그것도 결과이고 그대로 싣습니다. 그런데 느릴수록 덜 잃는 기울기가 더 가팔라졌습니다. 비용이 오르면 최적점이 더 느린 쪽으로 밀립니다. 그래서 격자를 두 칸 넓혔더니 90/270 이 −3.30%(54거래·수수료 446·PF 0.586), 120/360 이 −1.99%(33거래·수수료 308·PF 0.679) 로 답이 바뀌었습니다. weltiq 체결 기록 한 건이 QuantDinger 의 답을 바꾼 겁니다.

④ 그런데 weltiq 가 그 답을 못 받았습니다. 그대로 옮기려다 422 {"code":"invalid_rules","detail":"period_limit"} 로 거절당했습니다 — weltiq 규칙 문법의 기간 상한이 200 이라 360 을 못 받습니다. 근사하지 않고 이 사실을 그대로 남깁니다. 다만 벽시계로 같은 길이는 표현할 수 있어서 1분봉 120/360 을 5분봉 24/72 로 옮겨 다시 제출했고, 새 컨테이너는 2분 40초 뒤 체결되며 왕복을 마쳤습니다(buy 0.01 @ 85795.14865650 / sell 0.01 @ 85788.41030050, 실현 −0.0673835600, 수수료 1.71583559). 이건 필자가 한 번역 선택이지 QuantDinger 의 답이 아닙니다.

고리가 한 바퀴 돌았습니다. QuantDinger 10회 호출 + weltiq 14회 호출이고, 사람이 한 일은 값을 옮긴 것과 기간 상한 때문에 시간틀을 바꾼 것 둘뿐입니다.

판단하는 쪽을 죽여 봤습니다

이 편에서 제일 마음에 든 칸입니다.

02:13:41  QuantDinger 프로세스 종료   → /api/health HTTP 000 (죽음)
02:14:00  buy 0.01 BTCUSDT @ 85979.70711050  fee 0.85979707  OPEN   ← 19초 뒤, 꺼진 채로 주문이 나감
02:15:12  QuantDinger 재기동          → {"status":"healthy","role":"legacy"}

판단하는 쪽이 꺼져 있는 91초 동안 포지션이 열리고 유지됐습니다. 그리고 QuantDinger 는 올라오자마자 정상입니다 — 상태가 전부 PostgreSQL 에 있고 프로세스는 상태를 들고 있지 않습니다. (QuantDinger 자기 상주 전략의 복구는 띄우지 않아 확인 못 했습니다 — 미확인입니다.)

중지 조건은 발동시키지 못했습니다 — 미확인

좁은 한도를 걸고 정말 멈추는지 두 번 시도했고 둘 다 실패했습니다. 1차(1분봉 60/180, 한도 1 USDT)는 4분 동안 체결이 한 건도 없어(no_signal · reason: pine_hold) 한도를 시험할 기회가 없었습니다. 2차(5분봉 24/72, 한도 0.5 USDT)는 첫 체결 수수료만 0.86 USDT 라 산술적으로는 넘기는데 18분을 기다려도 안 멈췄습니다.

이유가 기록에 있습니다 — performance.basis 가 avg_cost_realized_pnl_minus_fees_sampled_at_fills 입니다. weltiq 의 중지 조건은 실현 손익 기준이고 포지션이 열려 있는 동안 실현 손익은 0 이라, 평가손익이 아무리 흔들려도 한도에 닿지 않습니다. 18분 뒤 직접 정지 명령을 넣었고 다음 5분봉에서 청산되며 그제서야 realized_pnl −0.5711299200 이 나왔습니다. 숫자만 보면 한도 −0.5 를 넘지만 그 값이 생긴 건 정지 뒤입니다(fired_on_its_own: false, waited_seconds: 1080).

이 회차에서 중지 조건은 "발동하는 것까지" 확인하지 못했습니다. 확인한 것은 한도가 걸려 기록에 남는다는 것과 그 기준이 실현 손익이라는 것까지이고, 실제 발동은 미확인입니다. 다음에는 실현 손실을 바로 만드는 규칙으로 다시 하겠습니다.

이 조합이 해 주지 않는 것

비용을 정확히 넣어도 순위가 바뀐다는 보장은 없습니다. 1차 격자 넷은 순서가 그대로였고 격자를 넓히고 나서야 답이 움직였습니다. 비용 되먹임은 정밀도를 올리는 장치이지 다른 결론을 만드는 장치가 아닙니다.

그리고 요율을 바꿔 다시 재는 API 가 이번에 안 됐습니다 — GET /v1/fees/compare 가 통화가 섞였다며 거부했고(mixed_currency_not_supported, ["USD","USDT"]) 크립토 체결만 골라낼 인자가 없었습니다. weltiq 쪽 과제로 적어 둡니다. 직접 곱하면 이 편 체결 4건(명목 3,434.25 USDT) 기준으로 실제(테이커 0.10%) 3.43425099 · 전부 메이커였다면(0.02%) 0.68685020 · 가정대로였다면(0.05%) 1.71712550 입니다. 전부 메이커로 채울 수 있었다면 비용이 5분의 1입니다.

그리고 어디는 QuantDinger 가 낫나

단계 QuantDinger weltiq
① 설계 (전략 만들기) 강함 — 파이썬 편집기, AI 대화, 지표 IDE, 차트 위 검증 규칙 문법·템플릿·Pine 가져오기
② 검증 (백테스트) 강함 — 체결 가정 명시, 원장 대조, 데이터 스냅샷 해시 같은 문서로 리플레이
③ 상주 (24시간 실행) 자기 서버를 켜 둬야 합니다 여기가 weltiq 자리
④ 승격 (실전으로) 실전까지 갑니다 live_orders: false — 아직 안 갑니다

①과 ②는 겹치고, 솔직히 QuantDinger 쪽이 더 두껍습니다. 차트 위에서 결정 지점을 눈으로 보고, 지표를 즉석에서 고쳐 다시 그리고, AI 와 소스를 놓고 대화하는 것 — weltiq 에 없습니다. ③이 갈리는 칸입니다. QuantDinger 로 24시간 돌리려면 그 스택을 켜 둬야 하고, 잰 것만 해도 프로세스 5개에 1.05 GB 입니다. ④는 정반대로 갈립니다 — QuantDinger 는 실전 주문을 내고 weltiq 는 안 냅니다. 다만 그 말은 실계좌 키가 그 스택 안에 들어간다는 뜻이고, 그때부터 ③의 운영 부담이 "귀찮음"이 아니라 "위험"이 됩니다.

어댑터 자리는 있는데 등록 창구가 없습니다. app/services/live_trading/contracts.py 에 ExchangeOrderAdapter 프로토콜이 타입까지 정의돼 있고, FillSnapshot 에는 통화별 수수료 분해(fees_by_ccy)와 수수료 확정 여부(fee_status: "actual" | "pending")까지 들어 있습니다 — 이 칸이 있다는 것 자체가 설계자가 무엇을 아는 사람인지 말해 줍니다. 그런데 실제로 클라이언트를 고르는 factory.py:218 의 create_client() 는 if exchange_id == "binance" / "okx" / … / "alpaca" 로 이어지는 하드코딩 사슬이고 플러그인 등록도 entry_points 도 없습니다. README 605번째 줄이 오히려 정직합니다 — 포크하거나 PR 을 넣으라는 뜻입니다. (import ccxt 는 전부 시세용이고 주문 경로에는 안 씁니다.) 그래서 이 편에서 실제로 쓴 길은 저장소를 한 줄도 안 고치고 판단은 QuantDinger 에, 주문은 weltiq API 에 보내는 것이었습니다.

공정하게 말하면 같은 해시로 구간을 꿰는 장치도 양쪽에 있습니다 — QuantDinger 는 컴파일 매니페스트의 codeHash 와 백테스트 결과의 dataProvenance(쓴 봉 데이터의 내용 해시)를 갖고 있습니다. 차이는 그 실이 어디까지 이어지느냐입니다. QuantDinger 는 자기 실행까지 가고, weltiq 는 백테스트와 페이퍼까지 가며 실전 구간은 아직 열려 있지 않습니다(live_orders: false). 능력이 아니라 관문이라는 것을 정확히 적어 둡니다.


6. 써 보니

좋았던 것

  1. 체결 가정을 전부 말합니다. 백테스트 결과에 fillRule, barClosePolicy: closed_bars_only, intrabarMode: conservative, fundingMode: not_modeled, protectionRule: gap_open_then_intrabar_trigger 가 스무 칸 가까이 들어 있습니다. "펀딩은 모형화 안 했다"고 스스로 적는 백테스트 엔진은 흔하지 않고, 거기에 Ledger reconciled 배지까지 답니다
  2. AI 가 낸 전략을 실제로 돌려 보고 막습니다. 4절에서 세 번 막힌 그 게이트입니다. 합성 봉에 난수가 없고, 요청한 방향으로 포지션을 실제로 열었는지 확인합니다. 작업을 막았지만 이건 칭찬입니다
  3. 남의 자원을 존중합니다. DB 풀을 잡을 때 max_connections 를 읽고 다른 클라이언트 몫 20을 떼어 둡니다. 샌드박스가 type()·getattr() 같은 내성 함수를 막고, 쓰기 API 가 전부 멱등키를, 주문·정지가 별도 확인 플래그를 요구합니다
  4. 도커 없이도 설 수 있습니다. 공식 문서는 도커만 안내하지만 컴포즈 파일이 읽을 만하게 쓰여 있어 그대로 로컬 프로세스로 옮길 수 있었습니다 — 4분 53초입니다

아쉬웠던 것

  1. indicator(period=) 가 TA-Lib 지표에서 조용히 무시됩니다. 이 편에서 찾은 가장 큰 문제입니다. ema(period=5) 와 ema(period=200) 이 같은 값을 돌려주고 경고도 없습니다. AI 생성 실패의 실제 원인이었고, 사람이 손으로 써도 똑같이 당합니다. 회피법은 timeperiod= 인데 정작 이 도구의 작성 규약 예시가 period= 를 쓰고 있습니다
  2. 실패할 때 아무것도 안 돌려줍니다. AI 전략 생성이 세 번 실패하는 동안 받은 건 에러 문자열 하나였습니다. 생성된 코드도, 어느 조건에서 안 열렸는지도 없습니다. 게이트가 좋을수록 왜 걸렸는지를 돌려줘야 고칠 수 있습니다
  3. 조용한 성공이 있습니다. Agent 토큰의 markets 에 JSON 배열을 주면 HTTP 200 이 오고 토큰이 발급되는데, 그 토큰은 아무 시장도 못 씁니다. 검증에서 떨어뜨렸어야 할 입력입니다
  4. 상주가 운영 작업입니다. 프로세스 5개, 1.05 GB. 백테스트하고 끄는 용도라면 아무 문제 없는데 24시간 돌릴 생각이면 그때부터는 서버를 운영하는 일이 됩니다

비용

항목 값
코드·시세 0원 (Apache-2.0 · 거래소 API 키 0개 · 계정 가입 없음)
모델 AI 기능을 쓸 때만. 안 쓰면 0원
시간·자원 설치 4분 53초 · 디스크 1.5 GB · 메모리 1.05 GB(대시보드 포함)

이런 분에게 맞습니다 — 파이썬으로 전략을 직접 쓰고 차트 위에서 눈으로 확인하면서 고치고 싶은 분. 편집기·차트·백테스트·주문 패널이 한 화면에 있는 구성은 이 연재에서 본 것 중 가장 완성도가 높습니다. 안 맞는 분은 "설치 없이 바로"를 원하는 분, 그리고 24시간 상주만 필요하고 전략은 이미 정해진 분입니다.


7. 다음 편 예고

15편은 moss-site/moss-trade-bot-skills(★385)입니다.

QuantDinger 가 PostgreSQL 과 Redis 까지 세우는 플랫폼이었다면, Moss 는 SKILL.md 한 장이 본체인 스킬 묶음입니다. 무게가 정반대입니다. 그리고 판단하는 자리에 있는 것이 저장소의 코드가 아니라 그 지시문을 읽은 여러분의 LLM 이라, "미션 한 문장 → 36칸 파라미터"가 한 번의 대화로 끝납니다. 이번 편에서 세 번 막힌 AI 전략 생성이 거기서는 어떻게 되는지 보겠습니다.

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

QuantDinger 스택을 24시간 켜 두는 대신 상주 칸 하나만 맡기고 싶으시면 그때 보실 자리입니다.


이 글의 모든 명령·출력·숫자는 2026-09-22 에 실제로 돌려서 얻었습니다. 거래소 키는 0개, 실계좌 연결은 없고 주문은 전부 모의입니다. 어떤 매매 의견도 아닙니다. 이 편에서 만든 weltiq 컨테이너 4개는 전부 정지·삭제했습니다. 여기 적지 않은 시행착오·원문 로그·LLM 실지출은 검증 기록에 있습니다.