NautilusTrader 설치부터 실전까지 — 백테스트와 라이브의 차이를 스스로 적어 둔 엔진

별 2만 9천 개짜리 Rust 트레이딩 엔진을 29초에 깔고, 나노초 호가틱 1만 3천 개를 48밀리초에 재생하고, 체결 가정 손잡이를 돌려 같은 전략의 결과가 얼마나 흔들리는지 재 봅니다. 그리고 이 도구의 문서를 보고 weltiq 가 무엇을 만들었는지 밝힙니다.

10편. NautilusTrader — 백테스트와 라이브가 어디서 갈라지는지 스스로 적어 둔 엔진

이 글의 완료 조건: 키 없이 나노초 호가틱으로 백테스트를 돌린 뒤, 전략 코드를 한 글자도 안 바꾼 채 체결 가정 손잡이만 돌려 결과가 얼마나 흔들리는지 재 보면 성공입니다.

하는 일 호가·체결·봉을 나노초 타임스탬프가 붙은 이벤트로 받아 전략에 넘기고, 시뮬레이션 거래소에서 체결시키고, 포지션을 관리합니다. 백테스트·샌드박스·라이브가 한 엔진에 있습니다
맞는 사람 틱 데이터가 있고 체결 가정이 결과를 얼마나 흔드는지 재 보고 싶은 사람. 백테스트가 재현되지 않아 디버깅을 포기해 본 적이 있는 사람
안 맞는 사람 봉만 갖고 빨리 결과를 보고 싶은 사람 · 대화로만 굴리고 싶은 사람 · 띄워 놓고 들여다볼 화면을 원하는 사람(대시보드가 없습니다)
이 글의 결과물 키 없이 받은 나노초 호가틱 1만 2천 개, 다섯 번 돌려 같은 해시가 나온 백테스트, 체결 가정 아홉 가지 표, 그리고 호가 잔량을 깎게 했더니 체결이 45건에서 19건으로 줄어든 기록
준비물·비용 코드 0원(LGPL-3.0) · 모델 없음(LLM 을 쓰지 않습니다) · 시세 0원, API 키 0개 · weltiq 5절을 따라 할 때만
시간·난도 설치 29초, 첫 백테스트까지 1분. 난도 상 — 봉 타임스탬프를 잘못 넣으면 오류 없이 틀린 결과가 나옵니다
weltiq 와의 관계 겹치는 자리이지 이어지는 자리가 아닙니다 — 이 엔진은 백테스트·페이퍼·실전을 자기 안에 다 갖고 있습니다. 붙는 자리는 봉 대조 하나이고, 진짜 접점은 두 「차이 문서」를 나란히 놓는 것입니다(5절)
검증 환경·날짜 macOS 26.6.2 · arm64 · CPython 3.11.16 · nautilus_trader 1.221.0 · 2026-09-22. 마지막 손잡이 한 칸만 3.14.7 · 1.231.0 이고 그 대목에 표시했습니다
제휴 고지 필자는 weltiq 로부터 이 연재의 원고료를 받습니다. 도구 평가는 필자가 직접 돌린 결과만 씁니다

거래소 계정도, API 키도 만들지 않았습니다. 거래소에는 공개 시세 조회만 했고 주문은 전부 로컬 백테스트 안에서 일어났습니다. 종목과 손익 숫자는 기능 확인용 예시일 뿐 어떤 매매 의견도 아닙니다.


1. 이게 뭔가요

nautechsystems/nautilus_trader 는 Rust 로 쓰인 이벤트 구동 트레이딩 엔진입니다. 전략은 파이썬으로 쓰고 엔진은 Rust 가 돕니다. 2018년에 시작했고 드물게 "기관에서 쓰라고 만들었다" 는 태도로 유지돼서, 볼 것도 "얼마나 쉬운가"가 아니라 "어디까지 정직한가" 쪽입니다.

항목 값 확인 방법
주소 github.com/nautechsystems/nautilus_trader
라이선스 LGPL-3.0 — 링크해 쓰면 전염되지 않지만 고쳐서 배포하면 수정분을 공개해야 합니다 GitHub API
별 수 ★29,234 · 포크 3,860 (관측일 2026-09-21) GitHub API
마지막 푸시 2026-09-21 GitHub API
개설 2018-06-25 · 열린 이슈 128 · 아카이브 아님 · 기본 브랜치 develop GitHub API
언어 비중 Rust 93.6% (70.8 MB) · Python 5.0% (3.8 MB) GitHub API
버전 PyPI nautilus_trader 1.221.0 · 저장소는 v2.0.0rc5 (2026-09-15) pip install · GitHub API

이 편의 핵심은 이 프로젝트의 문서 한 페이지입니다

개요 문서 첫 문단은 "같은 전략·실행 알고리즘 코드가 백테스트와 라이브를 그대로 지나갑니다"라고 적습니다. 그런 문장은 많은 프로젝트가 씁니다. 다른 점은 바로 다음 줄에서 "다만 여기가 다릅니다"라는 페이지를 가리킨다는 것입니다. docs/concepts/live.md 의 첫 절 제목이 "Backtest and live differences" 이고, 여섯 갈래를 나열합니다(원문 요약).

축 라이브에서 무엇이 새로 생기는가
Venue 거래소 규칙과 어댑터 능력이 어떤 주문 유형·지시가 애초에 가능한지를 정한다
Transport 네트워크가 끊기면 주문 명령의 결과를 모르는 상태가 남는다
Timing 독립된 입력들이 뒤섞여 들어오고, 러너는 하나의 전역 FIFO 순서를 보장하지 않는다
Persistence 캐시 백엔드의 쓰기가 거래소 전송과 따로 돈다
External activity 거래소 리포트에 이 노드 밖에서 생긴 주문이 섞여 들어온다
Reconciliation 기동할 때와 도는 동안, 내 상태를 거래소 리포트에 맞춰 고친다

Transport 가 가리키는 페이지는 증거를 셋으로 가릅니다 — 확정적으로 실패한 것 / 거래소가 확정해 준 것 / 결과를 모르는 것. 세 번째에 대해 "결과를 지어내지 말고 로그만 남긴다"고 적어 뒀습니다.

weltiq 는 이 문서를 보고 같은 것을 썼습니다. weltiq 도 "모드 분기는 어댑터 경계에서만"이라고 주장해 왔는데 주장만 있고 목록이 없었습니다. 그래서 나온 것이 MODE_DIFFERENCES.md 이고 5절에서 나란히 놓습니다. 나열하지 않으면 더 정직한 게 아니라 덜 검증 가능한 것입니다.


2. 설치

파이썬 하나면 됩니다. API 키도 거래소 계정도 안 만듭니다. 다만 전용 가상환경을 파십시오 — 컴파일된 확장 모듈 108개가 257 MB 깔립니다.

python3 -m venv ~/venvs/nautilus          # 3.12 이상
~/venvs/nautilus/bin/pip install nautilus_trader

29.3초, 15패키지. 이 중 20초가 93.2 MB 짜리 휠 다운로드입니다. 의존성이 열다섯 개뿐인 건 대부분이 Rust 로 이미 안에 들어와 있어서이고, 백테스트 엔진 하나가 63.8 MB 짜리 컴파일 바이너리(다음이 nautilus_pyo3 62.3 MB)입니다. 파이썬 루프가 아니라 Rust 이벤트 코어이고, 이게 뒤에 나올 속도와 결정론의 근거입니다.

파이썬 버전을 고르는 것이 기능을 고르는 것입니다

필자가 여기서 잘못 짚었고 그대로 적습니다. 맥의 기본 파이썬이 3.14 인 걸 보고 "새 버전이라 휠이 없겠지" 하고 확인도 안 하고 3.11 로 깔았습니다. 나중에 pip index versions 로 물어보니 3.11 에서는 1.221.0 까지만, 3.14 에서는 1.231.0 부터 보이고 두 목록이 한 칸도 안 겹쳤습니다. 1.222.0 에서 3.11 지원이 끊긴 것입니다. 오래된 파이썬을 고르면 오류도 경고도 없이 열 버전 뒤처진 물건을 받습니다.

3.11 → 1.221.0 3.14 → 1.231.0
FillModel(prob_fill_on_stop=…) 있습니다 없습니다 (1.222.0 에서 사라졌습니다)
add_venue(liquidity_consumption=…) 없습니다 있습니다 ← 4절의 핵심 손잡이
create_tearsheet() (3절) ImportError 있습니다
latency_model · bar_adaptive_high_low_ordering 있습니다 있습니다

파이썬 3.11 과 3.14 에서 각각 보이는 nautilus_trader 버전 목록

같은 명령을 두 가상환경에서 물어본 화면입니다. 가운데 두 블록의 첫 줄을 보십시오 — 3.11 은 nautilus_trader (1.221.0), 3.14 는 (1.231.0) 입니다. 그 아래 Available versions 두 줄에 겹치는 번호가 하나도 없습니다. 맨 위 Python 3.14.7 이 이 맥의 기본 파이썬이고, 제가 "휠이 없겠지" 하고 넘겨짚었던 그 버전입니다.

본문은 3.11 · 1.221.0 에서 돌렸고 4절 마지막 한 칸만 3.14 · 1.231.0 입니다. 숫자를 섞지 않았습니다.

막히는 자리

문서는 develop 브랜치(2.x rc) 기준인데 pip install 은 1.x 를 줍니다. 오늘 문서를 보고 오늘 설치하면 어긋나고, 어긋나는 정도가 파이썬 버전에 따라 다릅니다.

증상 원인 우회
문서에 있는 인자가 없음 파이썬이 낡아 옛 버전을 받았습니다 pip index versions nautilus_trader 로 확인
ImportError: … 'DefaultFillModel' 문서가 앞서 있습니다. 양쪽 버전 다 실패합니다 nautilus_trader.backtest.models.FillModel
체결 모델 11종(BestPriceFillModel 등)이 없음 문서에만 있습니다. 1.x 휠 어느 쪽에도 없습니다 2.0 정식판 대기 — 미확인
TypeError: … 'prob_fill_on_stop' 1.222.0 이후 없어진 인자입니다 빼고 부르십시오
invalid tick.bid_price.precision=4 … 가격 자릿수가 상품 정의와 다름 Price(float(x), inst.price_precision)
iterations 는 도는데 체결 0건 봉 타임스탬프가 시작 시각이거나 book_type 불일치 ts_init 을 마감 시각으로. L1 에 호가창 델타를 넣으면 무시됩니다
Cannot check MARKET order risk: no prices 봉만 넣고 호가를 안 넣어 위험 엔진이 사전 점검을 건너뜁니다 봉만 쓰면 그대로 둬도 돕니다. 무엇을 안 보고 있는지 엔진이 알려 준 것입니다

반대로 문서에 없는데 휠에 있는 것도 있습니다. latency_model=LatencyModel(base_latency_nanos=…) — 주문 제출·수정·취소에 나노초 단위 지연을 넣는 모델이고 양쪽 버전에 다 있습니다. 6절에서 다시 나옵니다.


3. 첫 결과

제일 작은 백테스트는 BTCUSDT 일봉 89개 · 거래소 하나 · 전략 하나입니다. 봉은 키 없는 공개 API(api.binance.com/api/v3/klines)로 90개를 받아 아직 안 끝난 마지막 하나를 버리고 89개로 돌립니다. 20일 고가 돌파 전략을 붙이면 이렇게 나옵니다.

BUY  MARKET 0.010000 @ 65043.98  2026-07-14 23:59:59.999999999+00:00
SELL MARKET 0.010000 @ 75644.48  2026-09-15 23:59:59.999999999+00:00
포지션 FLAT  65043.98 → 75644.48  realized_pnl 104.59811540 USDT  (0.16297)
iterations = 89

첫 백테스트 실행 결과 — 체결 2건과 포지션 1건

제일 작은 백테스트가 끝난 화면입니다. 맨 아래 iterations = 89 가 봉을 89개 지나갔다는 뜻이고, 그 위 체결표에 BUY MARKET·SELL MARKET 두 줄이 있으면 엔진이 제대로 돈 것입니다. 타임스탬프 끝자리 .999999999 가 나노초 해상도입니다. (로그 줄을 걷어내고 결과 부분만 잘랐습니다.)

이렇게 나오면 성공입니다. 0.53초 걸렸습니다. 이 수익률 숫자는 엔진이 돌았다는 증거일 뿐 어떤 전략도 추천하지 않습니다.

봉 타임스탬프에 함정이 있습니다. 체결 시뮬레이션을 쓰려면 ts_init 이 그 구간의 마감 시각이어야 합니다. 안 그러면 완성 전의 봉이 보여 미래 참조가 됩니다. Binance klines 는 [0] 이 시작, [6] 이 마감이라 [6] 을 써야 합니다. 전체 스크립트는 run01_first.py 에 있습니다.

대시보드는 없습니다 — 결과를 보는 두 길

상주 대시보드도 웹 UI 도 TUI 도 없습니다. 라이브러리이지 애플리케이션이 아닙니다. 찾느라 시간 쓰지 마십시오.

① 표로 찍기. eng.trader.generate_account_report(venue) · generate_order_fills_report() · generate_positions_report() 가 pandas 표를 돌려줍니다. 추가 설치가 없고 제일 자주 쓰게 되는 쪽입니다.

② 티어시트. create_tearsheet(engine=eng, output_path="tearsheet.html") 가 성과 요약을 자체 완결 HTML 로 뽑습니다. 단 1.231.0 이상에 pip install "nautilus_trader[visualization]" 이 필요합니다(3.98초). 1시간봉 2,159개로 뽑으니 4,868,898 바이트짜리 파일 한 개가 나옵니다 — Plotly 가 통째로 들어가서 그렇고, 그래서 서버도 인터넷도 로그인도 포트도 없이 open tearsheet.html 로 열리고 남에게 보내도 그대로 열립니다.

티어시트 첫 화면 — 실행 정보·성과 통계 표와 자산곡선·낙폭 그래프

티어시트를 브라우저로 연 첫 화면입니다. 위 두 표가 실행 정보(왼쪽)와 성과 통계(오른쪽)이고, 아래가 자산곡선과 낙폭입니다. 왼쪽 표의 Iterations 2_159 · Total orders 61 · Total positions 31 이 이 백테스트의 규모이고, Elapsed time 0 days 00:00:00.106934 가 엔진이 실제로 돈 시간(0.107초) 입니다.

티어시트 아래쪽 — 월별 수익률·수익률 분포·롤링 샤프·연간 수익률

같은 장표를 아래로 내린 화면입니다. 그래프 여섯 개(자산곡선·낙폭·월별 수익률·수익률 분포·롤링 샤프·연간 수익률)가 들어 있고 전부 Plotly 라 마우스를 올리면 값이 뜹니다. 왼쪽 아래 「Rolling Sharpe Ratio (60-day)」 가 비어 있는 것을 보십시오 — 구간이 89일뿐이라 60일 롤링이 거의 안 그려집니다. 빈 칸이 뜨는 게 정상입니다.

다만 그대로 믿기 전에 볼 것이 하나 있습니다. 같은 화면 안에서 PnL (total USDT) 는 −3,542.5035 인데 자산곡선은 1.0 에서 1.24 로 끝납니다. 방향이 반대입니다. 0.05 BTC 를 들고 끝났고 지표마다 그 미청산분을 다르게 세는 것으로 보이는데 끝까지 파지 않았습니다 — 미확인입니다. 인용하실 거면 어느 지표가 무엇을 세는지 먼저 확인하십시오.


4. 제일 잘 쓰는 법 — 전략은 그대로 두고 '체결 가정' 손잡이만 돌리기

먼저 봉 대신 틱을 넣으면 무엇이 달라지는지 봅니다. Binance 공개 WebSocket 에서 90초 동안 키 없이 호가틱 12,552개 · 체결틱 1,098개(초당 139개)를 받아 그대로 부었습니다.

틱 합계        : 13650  (호가 12552 · 체결 1098)
고유 나노초     : 13650
고유 밀리초     : 3788
밀리초로 뭉개면 : 9862개가 동률이 됩니다 (72.2%)
체결 ts_event=1790009103313999000 px=85857.19 qty=0.002000
     liquidity=<LiquiditySide.MAKER: 1> commission=0.17171438 USDT

이 두 줄이 나노초가 왜 필요한지를 말해 줍니다. 틱 13,650개는 나노초에서 전부 다른 시각인데 밀리초로 반올림하면 고유 시각이 3,788개로 줄고 9,862개(72.2%)가 동률이 됩니다. 동률이 되면 순서를 엔진이 임의로 정합니다 — 같은 밀리초 안에 "호가가 내 지정가에 닿았다"와 "체결이 났다"가 같이 들어오면 처리 순서로 내 주문이 채워졌는지가 갈립니다. 그게 70%의 틱에서 일어납니다.

정직하게 적습니다. 위 나노초 타임스탬프는 거래소 발행 시각이 아니라 수신 시각(time.time_ns())입니다. Binance bookTicker 페이로드에 이벤트 시각 필드가 없습니다. 그래서 "72.2%" 는 "이 스트림을 밀리초로 기록했다면 순서 정보를 이만큼 잃었을 것" 이라는 뜻이지 거래소에서 그만큼 동시에 일어났다는 뜻이 아닙니다.

best bid 아래 1틱(85,857.19)에 건 지정가는 2.298초 뒤 메이커로 채워졌습니다(slippage 0.0, 수수료 0.17171438 USDT). 그리고 빠릅니다 — eng.run() 이 0.0479초, 초당 285,114틱으로 90초치 시장을 48밀리초에, 실시간의 1,880배로 재생합니다(두 번 재서 1,880배·1,779배). 하루치 틱을 46초에 돌린다는 뜻입니다.

나노초 틱 백테스트와 처리 속도 측정

위쪽이 틱 백테스트, 아래쪽이 속도 측정입니다. 고유 나노초 : 13650 과 고유 밀리초 : 3788 두 줄을 나란히 보십시오 — 같은 틱 뭉치인데 밀리초로 내리면 9,862개가 동률이 됩니다. 「주문 일지」의 liquidity=<LiquiditySide.MAKER: 1> 는 지정가가 메이커로 채워졌다는 뜻입니다. (체결 리포트는 너무 넓어 잘랐습니다.)

같은 입력이면 같은 출력인지도 해시로 재 봤습니다. 체결·주문 리포트에서 실행 시각과 UUID 만 빼고 SHA-256 을 떠서 같은 프로세스에서 2번 + 별도 프로세스로 3번. 다섯 번 전부 ac16db29…f15256 이었고 주문 ID 까지 같았습니다 — 그 ID 가 벽시계가 아니라 데이터 시계에서 나오기 때문입니다. 결과가 재현되지 않으면 "왜 이렇게 됐나"를 물어볼 수 없습니다.

같은 백테스트를 다섯 번 돌려 나온 동일한 해시

위 두 줄이 같은 프로세스 안에서 두 번, 아래 세 줄이 별도 프로세스로 세 번 돌린 결과입니다. 다섯 줄의 hash 가 ac16db29…f15256 으로 전부 같습니다. 눈으로 앞 여덟 자와 뒤 여섯 자만 맞춰 보셔도 됩니다.

다만 "같은 데이터면 같은 결과"까지입니다. 난수를 쓰는 체결 모델을 켜면 시드를 고정해야 하고 라이브에서는 애초에 성립하지 않습니다. 이 프로젝트가 라이브 러너는 전역 FIFO 순서를 정의하지 않는다고 자기 문서에 적어 둔 것이 그 이야기입니다.

그리고 여기가 이 도구의 자리입니다

백테스트를 못 믿게 되는 이유는 대개 전략이 틀려서가 아니라 체결을 너무 후하게 봐줘서인데, "얼마나 후한가"를 물어볼 방법이 보통은 없습니다. NautilusTrader 는 그 가정들을 설정값으로 꺼내 놨습니다.

조건을 고정하고 전략 코드는 한 글자도 안 바꿨습니다. 봉은 BTCUSDT 1시간봉 2,159개(90일, 진행 중 봉 제외), 규칙은 20시간 고가 돌파 → 그 봉 종가에 대기 지정가 매수 → ±0.4% 브래킷, 자본 1,000,000 USDT. ±0.4% 로 좁게 잡은 이유는 한 봉 안에 손절선과 목표선이 둘 다 들어가야 "어느 쪽을 먼저 밟았다고 볼 것인가"가 결과를 가르기 때문입니다.

          label       지정가체결p 스톱체결p 경로adaptive  진입 제출 체결 청산 이긴 청산  최종 USDT   순손익
기준 — 전부 채워진다고 가정   1.00    1.00      False      97  194  97    52  999415.20 -584.80
  (지정가 0.75·0.5·0.25 · 스톱 0.75·0.5·0.25 여섯 줄이 전부 위와 같은 값)
       봉 내 경로 adaptive   1.00    1.00       True      97  194  97    50  999359.55 -640.45
         셋 다 보수적으로    0.50    0.50       True      97  194  97    50  999359.55 -640.45

체결 가정 아홉 가지를 돌린 결과표

전략은 고정하고 체결 가정만 아홉 가지로 바꾼 표입니다. 위 일곱 줄의 오른쪽 끝 숫자가 전부 52 999415.20 −584.80 으로 똑같습니다 — 확률을 1.00 에서 0.25 까지 내려도 아무것도 안 바뀌었습니다. 그런데 경로adaptive 가 True 인 아래 두 줄만 50 999359.55 −640.45 로 갈립니다. 거기가 유일하게 움직인 손잡이입니다. (아홉 줄 전문은 검증 기록에 있습니다.)

발견 1 — 확률 손잡이가 봉 위에서는 하나도 안 움직였습니다. 고장이 아닙니다. 이 확률은 시장이 내 가격에 "닿기만" 하고 관통하지 않을 때에만 적용되는데, 봉으로 돌리면 엔진이 봉 하나를 시가 → 고가 → 저가 → 종가 네 점의 합성 호가로 바꾸고 그 사이를 건너뜁니다. 정확히 "닿기만" 하는 일이 거의 안 일어납니다. 봉으로 백테스트하시는 분에게 이 손잡이는 없는 것이나 마찬가지입니다 — 도구의 한계가 아니라 봉이라는 데이터의 한계이고 문서도 그렇게 적습니다.

발견 2 — 봉 안의 경로는 움직였습니다. bar_adaptive_high_low_ordering 을 True 로 바꾸니 이긴 청산이 52 → 50 으로, 순손익이 −584.80 → −640.45 USDT 로 갈렸습니다. 기본값은 봉 안에서 언제나 시가 → 고가 → 저가 → 종가 순으로 밟고, True 는 시가에서 더 가까운 극값을 먼저 밟습니다. 손절선과 목표선이 한 봉 안에 둘 다 있으면 먼저 밟은 쪽이 먼저 체결되고, 그게 청산 두 건의 방향을 뒤집었습니다. 문서는 이 휴리스틱을 "결정론적 휴리스틱이지 실제 체결 순서의 복원이 아니다" 라고 스스로 적습니다. 정확도를 주장하지 않으면서 손잡이를 주는 쪽입니다.

그래서 같은 손잡이를 틱 위에서 다시 돌렸습니다. 호가틱 200개마다 best bid 에 지정가를 하나씩 — 정확히 "닿기만 하는" 자리입니다.

prob_fill_on_limit     제출     체결     체결률%
              1.00     62     68    109.7
              0.75     62     67    108.1
              0.50     62     67    108.1
              0.25     62     68    109.7
              0.00     62     49     79.0

틱 위에서는 듭니다. 다만 모양이 깔끔하지 않아 보이는 대로 적습니다. ① 체결률이 100% 를 넘습니다 — 주문 1건에 체결 이벤트가 여러 개 붙는 부분체결이고, 봉 백테스트에는 이게 아예 없었습니다. ② 0.0(닿아도 절대 안 채움)에서도 49건이 채워집니다 — 시장이 지정가를 관통한 경우라 이 확률의 대상이 아닙니다. 즉 68건 중 19건(28%)만 '닿기' 규칙에 걸려 있었습니다. ③ 1.0 → 0.25 구간은 단조롭지 않습니다(67·67·68). N = 62 는 작은 표본이니 방향을 읽지 마십시오.

제일 중요한 손잡이 — 내 체결이 호가창을 줄이는가

이 손잡이는 처음에 못 썼습니다. 1.221.0 에 없거든요. 2절의 버전 착오를 발견하고 파이썬 3.14 · 1.231.0 으로 다시 깔아 이 칸만 따로 돌렸습니다.

liquidity_consumption 은 내 체결이 그 호가 잔량을 깎는지를 켭니다. 끄면(기본) 같은 호가창이 연속 주문에 몇 번이든 다시 쓰이고, 켜면 레벨마다 소진량을 기록해 새 호가가 오기 전까지 남은 만큼만 채웁니다. 같은 호가틱 위에서 시장가 매수 0.5 BTC 를 연달아 다섯 건씩 던졌습니다.

python 3.14.7 · nautilus_trader 1.231.0

liquidity_consumption :          False   |             True
             체결 건수 :             45   |               19
         서로 다른 체결가 :              9   |               11
            최저 체결가 :       85842.14   |         85842.14
            최고 체결가 :       85883.78   |         85883.78
              가중평균 :     85865.0224   |       85872.9493

같은 주문 다발의 평균 체결가 차이: +7.9269 USDT (+0.92 bp)

호가 잔량 소진을 켰을 때와 껐을 때의 체결 비교

왼쪽이 기본(잔량을 안 깎음), 오른쪽이 liquidity_consumption=True 입니다. 체결 건수 : 45 | 19 줄을 보십시오 — 같은 주문 다발인데 26건이 아예 안 채워졌습니다. 맨 아랫줄의 +0.92 bp 가 채워진 것들의 평균 체결가 차이입니다. 맨 윗줄 python 3.14.7 · nautilus_trader 1.231.0 이 이 칸만 다른 버전에서 돌렸다는 표시입니다.

체결 건수가 45 → 19 로 줄었습니다. 26건이 호가가 동나서 아예 안 채워졌고, 채워진 것들도 평균 0.92 bp 비싸게 채워졌습니다. 이게 weltiq 이야기와 제일 세게 붙는 숫자입니다 — weltiq 의 MODE_DIFFERENCES.md 가 「안 흉내 내는 것」 1번으로 적어 둔 항목이 정확히 이것(호가창을 읽기만 하고 줄이지 않음)이고, 그 자리를 상수 β bps(바이낸스 현물 프로필에서 1 bp)로 뭉뚱그리고 있습니다. 0.92 bp 가 그 상수와 비슷한 크기이긴 하지만 한 조건에서 한 번 잰 것이지 분포가 아닙니다. 그리고 체결 건수가 절반 이하로 줄어든 쪽이 더 큰 이야기입니다 — weltiq 는 그 26건을 지금 전부 채워 주고 있습니다.

이 절에서 가져가실 것 하나. 백테스트 손익 숫자 옆에는 "어떤 체결 가정에서의 숫자인가" 가 같이 붙어야 합니다. 이 도구는 그 가정을 설정값으로 꺼내 놨기 때문에 숫자 하나가 아니라 폭으로 답할 수 있습니다.

이 편에서 안 한 것

한 엔진에 거래소 둘(BINANCE 현물 + BYBIT 무기한) · 상품 셋 · 전략 셋을 동시에 넣는 것은 돌아갔습니다 — iterations : 267(89×3), 계좌 종류가 다른 채로 한 엔진에서 돌고 설정만 늘 뿐 구조는 안 바뀝니다. 다만 거래소 간 자금 이동·차익거래 결제는 없습니다. 나머지는 조용히 빼지 않고 적습니다.


5. weltiq 로 옮기기 — 겹치는 자리이지 이어지는 자리가 아닙니다

NautilusTrader 는 백테스트·샌드박스·라이브를 자기 안에 다 갖고 있습니다. 네 단계(분석 → 시스템화 → 페이퍼 → 거래 분석) 중 뒤 셋을 한 엔진에 담고 그 뒤 실전까지 자기가 갑니다. weltiq 는 그 셋을 서비스로 제공하고 실전은 아직 열지 않았습니다. 그래서 억지로 늘리지 않고 둘만 적습니다.

5-1. 실제로 해 본 것 — 같은 봉인지 대조하기

1분봉  weltiq n=348 | binance n=400   라벨 그대로 → 일치 0 · 마감→시작 보정(-60s) → 348/348
1시간봉 weltiq n=711 | binance n=2160  라벨 그대로 → 일치 0 · 보정(-3600s) → 711/711
        (시가·종가·거래량 전부)

라벨을 한 칸 맞추면 1분봉 348개·1시간봉 711개가 시가·종가·거래량까지 전부 일치하고, 안 맞추면 하나도 안 맞습니다. Binance klines 는 봉에 시작 시각을, weltiq /v1/bars 는 마감 시각을 붙이기 때문입니다. 그리고 NautilusTrader 자신이 이 함정을 경고 박스로 박아 뒀습니다 — 체결 시뮬레이션에서 ts_init 은 반드시 구간의 마감이어야 한다고요. weltiq 의 규약이 이 엔진이 요구하는 쪽과 같아서 변환 없이 바로 쓸 수 있습니다.

이 조합이 해 주지 않는 것. weltiq 의 /v1/bars 는 최대 720시간(30일) 까지만 줍니다. 90일치를 받아다 백테스트하는 용도로는 안 맞고, 그건 거래소 공개 API 나 유료 데이터 공급자의 자리입니다.

대조하다 weltiq 쪽에서 하나 발견했습니다. 같은 시각에 시간틀별로 물으니 1분봉은 0.4분 전까지 채워져 있는데 1시간봉은 592.4분 전, 일봉은 1,012.4분 전이었고 5분봉은 n=0 이었습니다. 1분봉 수집은 실시간인데 상위 시간틀이 이 시점에 뒤처져 있었습니다. 이유를 확인하지 않았으므로 미확인으로 두고 그대로 적습니다. 위 1시간봉 711개 대조는 뒤처진 구간을 뺀 나머지에서 성립한 것입니다.

5-2. 이 편에만 있는 자리 — 두 「차이 문서」를 나란히

1절의 그 문서 이야기입니다. 두 문서가 같은 질문에 답하는데 답의 모양이 다릅니다.

NautilusTrader docs/concepts/live.md weltiq MODE_DIFFERENCES.md
무엇을 가른다 백테스트 ↔ 라이브 두 구간 백테스트 ↔ 페이퍼 ↔ 실전 세 구간
축의 수 6 9
검증 표기 없음 — 설계 서술 실측·설계·미확인 을 칸마다
안 흉내 내는 것 체결 모델 문서 곳곳에 분산 한 절에 15개를 편향 방향(낙관/보수)까지
실전 구간 돌고 있다 — 라이브 노드와 거래소 어댑터가 있습니다 미확인 — 실계좌 주문 경로가 닫혀 있습니다

저쪽은 "실전을 돌리기 전에 알아 두라"는 운영 안내이고, weltiq 쪽은 "검증하려는 사람에게 미리 깔아 두는 목록"입니다.

맞대 보니 빈칸이 하나 드러났습니다. 저쪽 여섯 축 중 Venue — "어떤 주문 유형·지시가 애초에 가능한지" — 가 weltiq 의 아홉 축 어디에도 독립된 자리가 없었습니다. weltiq 는 market·limit·stop_market 셋을 모든 거래소에 대해 똑같이 지원하는데 실제 거래소는 저마다 다릅니다. post-only·OCO·트레일링·아이스버그·유효기간이 다 다르고, 모의에서 되던 주문이 실전에서 "그 거래소엔 그런 주문이 없다" 로 막히는 자리가 생깁니다. weltiq 문서는 그걸 거래소 거부라는 실패 이벤트로는 적었지만 "애초에 무엇이 가능한가"라는 축으로는 세우지 않았습니다. 이 편을 쓰면서 드러난 빈칸이라 그대로 적어 둡니다. 반대로 weltiq 에 있고 저쪽에 없는 축은 「비용」 입니다 — 수수료·펀딩·일일정산이 세 구간을 같은 함수로 지나가고 프로필마다 편향 방향이 상수로 적혀 있습니다. 저쪽은 fee_model 자리만 열어 두고 내용은 사용자에게 맡깁니다. 우열이 아니라 설계 철학의 차이입니다.

그래서 이 도구는 이렇게 쓰는 게 제일 낫습니다. 틱이 있으시면 먼저 체결 가정의 폭을 재 보시고(4절) 그 폭을 알고 나서 어디서 돌릴지 정하십시오. 그건 weltiq 쪽만 써서는 지금 못 하는 일입니다 — weltiq 는 그 손잡이를 사용자에게 열어 두지 않았습니다.


6. 써 보니

좋았던 것

  1. 결정론이 말이 아니라 해시로 확인됩니다. 별도 프로세스 세 번을 포함해 다섯 번 돌려 같은 해시, 주문 ID 까지 같았습니다.
  2. 나노초가 진짜 나노초이고 빠릅니다. 틱 13,650개가 나노초에서는 전부 다른 시각이고(밀리초로 뭉개면 72.2%가 동률), 90초치 시장을 48밀리초에 재생합니다.
  3. 체결 가정을 설정값으로 꺼내 놨습니다. "내 백테스트가 얼마나 후한가"를 물어볼 수 있습니다. 이 하나만으로 쓸 이유가 되고, 그러면서도 정확도를 주장하지 않습니다.

아쉬웠던 것

  1. 문서와 패키지가 어긋납니다. 문서는 2.x 기준인데 pip install 은 1.x 를 줍니다. DefaultFillModel 임포트가 바로 깨지고 체결 모델 11종도 없습니다.
  2. 파이썬 버전이 기능을 정하는데 그게 안 보입니다. 3.11 과 3.14 에서 보이는 버전 목록이 하나도 안 겹칩니다. 필자는 그래서 4절의 제일 중요한 손잡이를 처음에 못 썼습니다.
  3. 무겁고, 대시보드가 없고, 데이터를 직접 구해 와야 합니다. 휠 93 MB · 설치본 257 MB · .so 108개. 간판인 L2·L3 호가창 백테스트는 유료 데이터가 있어야 열립니다.
  4. 오류 없이 틀린 결과가 나오는 자리가 여럿입니다. 봉 타임스탬프를 마감으로 안 맞추면 조용히 미래 참조가 되고, book_type 이 안 맞으면 체결이 하나도 안 나면서 오류도 안 납니다.

weltiq 쪽 이야기도 정직하게. 이 도구에 있고 weltiq 에 없는 것이 셋입니다. ① 호가 잔량 소진(4절) — 제일 큽니다. ② 체결 가정 손잡이 전반 — weltiq 는 체결 규칙을 프로필로 고정하고 열지 않았습니다. 편향 방향을 적어 두는 것과 재 보는 것은 다른 일입니다. ③ 제출 지연 모델 — 양쪽 휠에 있는 LatencyModel 이 weltiq 에는 없습니다. 선언만 있고 읽는 코드가 0건인 죽은 필드라 지웠는데, 없는 것을 선언만 해 두는 것보다는 낫지만 있는 것보다 낫지는 않습니다.

비용

항목 값
코드·시세 0원 (LGPL-3.0 · API 키 0개)
모델 없음 — LLM 을 쓰지 않습니다
시간·디스크 설치 29.3초 · 257 MB · 첫 백테스트 0.53초

7. 다음 편 예고

다음은 kyky2347/ALTA (★602) 이고 성격이 정반대입니다. NautilusTrader 는 백테스트도 라이브도 리컨실리에이션도 자기 것인 한 덩어리라 이번 5절이 "겹치는 자리"였습니다. 반대로 ALTA 는 안쪽에 Shadow(그림자 실행), 바깥쪽에 Broker API 라는 이분법이라 실행층이 처음부터 갈라져 있고 갈아 끼우게 되어 있습니다. 그래서 다음 편의 질문은 "안쪽 Shadow 를 바깥의 weltiq 로 빼내면 무엇이 달라지나" 입니다.

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

체결 가정의 폭을 이 엔진으로 재 보신 뒤, 그 전략을 24시간 무인으로 돌려 볼 자리입니다.


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