트렌딩 딥다이브 · 2026-07-23 · TrendShift Daily #7 · VNPY

vnpy/vnpy (VeighNa) 딥다이브
— 브로커 접속과 전략을 이벤트 버스 하나로 묶는 퀀트 트레이딩 OS

VeighNa(옛 이름 vn.py)는 파이썬 기반 오픈소스 퀀트 트레이딩 프레임워크다. 한 줄 요약: 증권·선물 브로커 접속(게이트웨이) 20여 종과 CTA·차익·옵션·포트폴리오 같은 전략 앱들을, 단 하나의 이벤트 엔진(event-driven engine) 위에 꽂아 쓰는 "트레이딩 운영체제"다. 2015년에 시작해 중국 사모펀드·증권사·선물사가 실전에 쓰는 사실상의 표준으로 자랐다.

기존 백테스트 중심 도구(backtrader·zipline 등)와 결정적으로 다른 점 — 그들은 과거 데이터 시뮬레이션이 주인공이고 실거래 연결은 곁다리다. VeighNa는 반대로 실전 라이브 트레이딩과 실제 브로커 연결이 1급 시민(first-class citizen)이다. 틱 하나가 브로커에서 들어와 전략을 거쳐 주문으로 나가는 전체 왕복 경로가 프레임워크의 뼈대이고, 백테스트는 그 위에 얹힌 앱일 뿐이다.

(저장소 vnpy/vnpy · VeighNa 4.4.0 · Python 3.10+ (권장 3.13, 64bit) · GUI PySide6(Qt6) · 핵심 EventEngine · MainEngine · BaseGateway · BaseApp · OmsEngine · 4.0의 AI 모듈 vnpy.alpha · 라이선스 MIT · ⭐ 약 43.7k · 저자 Xiaoyou Chen(陈晓优) · shallow clone 소스 직접 분석)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 주목받는가 — 라이브 트레이딩이 1급 시민인 프레임워크
  3. 기술 스택 전체 지도 — 이벤트 엔진 · Qt6 · DB/데이터피드 · ML
  4. 아키텍처 심화 — 이벤트 버스와 게이트웨이 추상화
  5. 디렉토리 구조 해부
  6. 학습 포인트 — 여기서 훔쳐올 설계
  7. 시스템 / 실행 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

"파이썬으로 실전 자동매매 시스템을 만들려면 뭘 써야 하나" — 그 질문의 중국발 정답
한 문장으로

VeighNa = 서로 다른 브로커(게이트웨이)와 전략(앱)을 같은 콘센트 규격에 꽂게 해 주는 멀티탭 + 배전반, 그 배전반의 전선이 곧 이벤트 버스

자동매매를 직접 짜려면 골치 아픈 게 한둘이 아니다. 브로커마다 API가 다르고(누구는 C++, 누구는 REST), 시세·주문·체결·잔고가 실시간으로 쏟아지며, 전략은 이걸 놓치지 않고 받아 판단해 다시 주문을 내야 한다. 각 조각을 직접 연결하면 거미줄 같은 강결합이 된다.

VeighNa는 그 사이에 이벤트 엔진(EventEngine)이라는 중앙 배전반을 놓는다. 게이트웨이는 데이터를 이 버스에 "던지기만" 하고, 전략·주문관리(OMS)·화면은 필요한 이벤트를 "구독만" 한다. 서로를 직접 호출하지 않으니, 브로커를 바꾸든 전략을 갈아끼우든 나머지는 그대로다.

이 프로젝트는 두 개의 얼굴을 가진다. 하나는 얇은 코어 프레임워크(vnpy/ 패키지) — 이벤트 엔진, 메인 엔진, 게이트웨이·앱 추상 클래스, 주문관리 엔진(OMS)이 전부다. 다른 하나는 수십 개의 vnpy_* 확장 패키지 생태계 — 실제 브로커 접속(vnpy_ctp·vnpy_ib…), 전략 앱(vnpy_ctastrategy…), DB·데이터피드가 전부 별도 pip 패키지로 분리돼 있다. 코어는 "규격"만 정의하고, 실물은 플러그인으로 갈아 끼운다.

# 권장 설치: VeighNa Studio 올인원 인스톨러(윈도우) 또는
pip install vnpy                 # 코어 프레임워크
pip install vnpy_ctp             # CTP 선물 게이트웨이(플러그인)
pip install vnpy_ctastrategy     # CTA 전략 엔진(앱 플러그인)

코드로 시스템을 띄우는 최소 골격은 이렇게 생겼다. 이벤트 엔진 → 메인 엔진 → 게이트웨이/앱 등록 → GUI의 4단 조립이 항상 반복된다:

from vnpy.event import EventEngine
from vnpy.trader.engine import MainEngine
from vnpy.trader.ui import MainWindow, create_qapp
from vnpy_ctp import CtpGateway
from vnpy_ctastrategy import CtaStrategyApp

qapp = create_qapp()
event_engine = EventEngine()               # ① 배전반(이벤트 버스)
main_engine = MainEngine(event_engine)     # ② 오케스트레이터
main_engine.add_gateway(CtpGateway)        # ③ 브로커 접속 플러그인
main_engine.add_app(CtaStrategyApp)        # ③ 전략 앱 플러그인
window = MainWindow(main_engine, event_engine)  # ④ Qt GUI
window.showMaximized()
qapp.exec()
용어
게이트웨이 · 앱 · 이벤트 드리븐 · OMS
게이트웨이(Gateway)는 특정 브로커/거래소 접속을 표준 규격으로 감싼 어댑터다("이 콘센트에 꽂으면 CTP 선물이 나온다"). 앱(App)은 전략·백테스트·리스크 같은 기능 모듈로, 각자 자기 엔진을 갖고 붙는다. 이벤트 드리븐(event-driven)은 "시세가 왔다·주문이 체결됐다" 같은 사건을 큐에 넣고, 관심 있는 쪽이 골라 받는 방식이다. OMS(Order Management System)는 지금 살아있는 주문·포지션·잔고·최신 시세를 메모리에 들고 있는 "상황판"이다.

정리하면 VeighNa의 정체는 "백테스트 라이브러리"가 아니라 "라이브 트레이딩 플랫폼"이다. ① 이질적인 브로커들을 게이트웨이 추상화로 통일하고 ② 그 사이를 이벤트 버스로 느슨하게 연결하며 ③ 전략·백테스트·리스크·데이터관리를 플러그인 앱으로 갈아 끼운다. 값(시세·주문 처리)은 브로커의 C++ API가 내지만, 이 레포의 진짜 가치는 그 모든 이질성을 하나의 규격으로 묶은 아키텍처 그 자체에 있다.

2왜 주목받는가

10년 된 프로젝트가 다시 트렌딩에 오른 이유 — "AI-Powered"로의 세대교체

VeighNa가 다시 트렌딩에 오른 건 신생 레포라서가 아니다. 정반대로 2015년부터 10년을 버틴 프로젝트(⭐ 약 43.7k, 포크 12.2k)가 4.0에서 "By Traders, For Traders, AI-Powered"를 내걸고 세대교체했기 때문이다. 그 무게를 떠받치는 사실들은 이렇다.

핵심 주장 ①

라이브 트레이딩과 "실제 브로커 연결"이 설계의 중심이다

파이썬 퀀트 도구 대부분(backtrader·zipline·backtesting.py)은 백테스트가 주인공이고 실거래는 어댑터로 억지로 붙인다. VeighNa는 뼈대 자체가 틱 → 이벤트 엔진 → 전략 → 주문 → 체결의 실시간 왕복이다. 그래서 20종이 넘는 중국 국내 선물/주식/옵션 게이트웨이(CTP·XTP·오방차이푸…)와 해외 IB(Interactive Brokers)가 모두 동일한 추상 인터페이스로 붙는다.

바꿔 말하면, 백테스트에서 검증한 CtaTemplate 전략 코드를 한 줄도 고치지 않고 실전 계좌에 올릴 수 있다. "연구 코드 → 실전 코드" 사이의 재작성이 없다는 게 실무자에게 결정적이다.

둘째, 트렌딩을 만든 진짜 방아쇠는 4.0에서 추가된 AI/ML 모듈 vnpy.alpha다. 10주년을 기념해 넣은 이 모듈은 멀티팩터 머신러닝 전략을 개발·연구·실전까지 한 곳에서 돌리게 한다. 설계는 마이크로소프트의 퀀트 프레임워크 Qlib에서 영감을 받았고, Qlib의 유명한 Alpha 158 팩터 세트까지 이식했다.

vnpy.alpha 하위 모듈역할
dataset팩터 피처 엔지니어링. alpha_158 세트, 기술적/시계열/횡단면/수학 함수와 전처리기(processor).
modelML 모델 템플릿 — Lasso · LightGBM · MLP(다층 퍼셉트론).
strategy횡단면(cross-sectional) + 시계열(time-series) ML 전략 템플릿 + 백테스팅.
lab.py리서치 워크플로 매니저. examples/alpha_research에 주피터 노트북 예제 동봉.

셋째, 여전히 살아 움직이는 유지보수다. 최신 4.4.0은 numpy 2.x·pandas 2.2·Python 3.13까지 따라가고, QuestDB 데이터베이스 어댑터(vnpy_questdb)와 WeChat(위챗) 알림 엔진을 새로 넣었으며, macOS 지원까지 확장했다. 열린 이슈가 21개뿐일 만큼 촘촘하게 관리된다. 10년 된 코드베이스가 최신 런타임을 이 정도로 바짝 따라가는 건 드문 일이다.

비유로 이해하기

backtrader/zipline이 "비행 시뮬레이터"라면(과거 항로를 다시 날아보는 게 핵심), VeighNa는 "관제탑 + 실제 활주로"다. 시뮬레이터도 물론 있지만(백테스트 앱), 진짜 목적은 지금 이 순간 뜨고 내리는 실제 항공기(주문)를 관제하는 것이다. 그리고 vnpy.alpha는 그 관제탑에 얹은 AI 부조종사 — 수백 개 팩터를 학습해 "무엇을 살까"를 예측한다.

주의 — 만능이 아니다
중국 시장 중심 + 실전 연결의 진입 장벽

게이트웨이 생태계가 중국 국내 브로커에 크게 편중돼 있다. 해외 개인은 사실상 vnpy_ib(Interactive Brokers) 정도가 현실적 선택지다. 또 CTP류 게이트웨이는 브로커의 C++ API를 감싼 컴파일 확장이라, 버전이 안 맞으면 접속이 깨진다(그래서 PySide6 버전이 정확히 6.8.2.1로 못박혀 있다). 문서·커뮤니티도 중국어가 1차 언어라, 한국 사용자는 영어 README(README_ENG.md)와 자동번역에 의존하게 된다.

3기술 스택 전체 지도

코어는 놀랄 만큼 얇다 — 표준 라이브러리 스레딩/큐로 이벤트 엔진을 굴린다

VeighNa의 스택은 "얇은 코어 + 두꺼운 생태계"다. 코어 이벤트 엔진은 외부 의존성 없이 파이썬 표준 threading·queue만으로 돌아간다. 무거운 것들(GUI·차트·DB·ML)은 전부 선택적 계층으로 얹힌다. 계층별로 훑어보자.

백엔드 · 코어 (이벤트 엔진 = 심장)

계층기술왜 이걸 썼나
언어Python 3.10+ (권장 3.13)64bit 전용. mypy strict + ruff로 타입/린트 강제. 빌드 백엔드는 hatchling(setup.py 없음), 버전은 vnpy/__init__.py 한 곳에서 관리.
이벤트 엔진표준 threading + queue.Queue코어 중의 코어. 스레드 2개(이벤트 소비 + 타이머)로 큐를 드레인하고 타입별 핸들러에 분배. 외부 의존성 0.
수치/데이터numpy ≥ 2.2.3 · pandas ≥ 2.2.3바(bar) 배열 관리, 지표 계산, 백테스트 데이터 처리. numpy 2.x 대응 완료.
기술적 지표ta-lib ≥ 0.6.4ArrayManager가 감싸 MA·RSI·MACD 등을 제공. 네이티브 TA-Lib C 라이브러리 필요.
파라미터 최적화deap ≥ 1.4.2전략 파라미터를 격자(grid) + 유전 알고리즘(genetic)으로 탐색.
분산 통신pyzmq ≥ 26.3.0 (ZeroMQ)vnpy/rpc가 REQ-REP(함수 호출) + PUB-SUB(데이터/하트비트)로 프로세스 간 통신.
로깅/알림loguru · qrcode · requests로그, 위챗 바인딩 QR, HTTP. 이메일/위챗 알림 엔진이 코어에 내장(4.4.0).
용어
EventEngine · 핸들러 · vt_symbol
EventEngine은 사건(Event)을 담는 큐 하나와, 그 큐를 계속 비우며 등록된 핸들러(handler) 함수들에게 사건을 나눠 주는 스레드로 구성된다. 핸들러는 register(타입, 함수)로 특정 사건 타입을 구독한다. vt_symbol"IF2312.CFFEX"처럼 {종목}.{거래소} 형식의 전역 유일 키로, 모든 데이터 객체가 __post_init__에서 자동 생성한다. 이 키가 딕셔너리 키이자 이벤트 이름의 접미사로 쓰여 라우팅의 뼈대가 된다.

프론트엔드 · GUI (Qt6 데스크톱)

VeighNa는 웹앱이 아니라 데스크톱 GUI 애플리케이션이다. 이 계층은 세 조각으로 이뤄진다. ① PySide6 == 6.8.2.1 — Qt6 바인딩. 버전이 정확히 고정된 이유는, C++로 컴파일된 게이트웨이 확장과 ABI(바이너리 호환성)를 맞춰야 하기 때문이다. ② pyqtgraph ≥ 0.13.7vnpy/chart가 이걸로 대용량 K라인(캔들) 차트를 실시간 갱신까지 되게 그린다(CandleItem·VolumeItem). ③ qdarkstyle — 트레이더가 밤새 보는 다크 테마. 참고로 vnpy_webtrader 앱을 붙이면 REST + WebSocket 서버가 떠서 웹 UI로도 쓸 수 있지만, 그건 별도 플러그인이다.

데이터 · 인프라 (전부 vnpy_* 플러그인)

구분플러그인(일부)비고
데이터베이스vnpy_sqlite(기본) · vnpy_mysql · vnpy_postgresql · vnpy_questdb · vnpy_dolphindb · vnpy_taos(TDengine) · vnpy_mongodb코어의 BaseDatabase 추상을 구현. SQL부터 시계열 DB까지.
데이터피드vnpy_rqdata · vnpy_xt(迅投研) · vnpy_tushare · vnpy_wind · vnpy_tqsdk · vnpy_polygon …과거/실시간 시세 공급. BaseDatafeed 구현.
ML 스택(alpha extra)torch ≥ 2.6 · lightgbm ≥ 4.6 · scikit-learn · polars · alphalens-reloaded · pyarrow · scipyvnpy.alpha용. pip install vnpy[alpha]로 선택 설치.
시각화plotly ≥ 6.0백테스트 결과 차트(자산 곡선·낙폭 등).
비유로 이해하기

VeighNa의 스택은 모듈형 오디오 시스템과 같다. 코어(EventEngine + MainEngine)는 앰프다 — 자체론 소리를 안 내지만 모든 신호가 여길 통과한다. 게이트웨이는 입력 소스(턴테이블·CD·스트리밍), 앱은 이펙터(이퀄라이저·리버브), DB/데이터피드는 레코드 보관함이다. 앰프 규격(추상 클래스)만 맞으면 어떤 소스·이펙터든 꽂힌다. 그래서 코어 코드는 얇고, 생태계는 두껍다.

4아키텍처 심화 분석

모든 것이 이벤트 버스를 통한다 — 게이트웨이는 전략을 "직접 부르지 않는다"

전체 구조의 한 문장 요약: MainEngine이 오케스트레이터, EventEngine이 신경망, 나머지(게이트웨이·엔진·앱)는 그 위에 등록되는 참여자다. 핵심은 참여자들이 서로를 직접 호출하지 않는다는 것 — 오직 이벤트 큐를 통해서만 대화한다.

┌──────────────────────────────────────────────────────────────┐ │ MainEngine (오케스트레이터) │ │ gateways{} · engines{} · apps{} · exchanges[] │ │ connect / subscribe / send_order / cancel_order → 게이트웨이 위임│ └───────┬───────────────────┬───────────────────┬────────────────┘ │ add_gateway │ add_engine/app │ (OMS 조회 메서드 ▼ ▼ ▼ 를 자기에 바인딩) ┌─────────┐ ┌──────────┐ ┌──────────┐ │BaseGate │ │ OmsEngine│ │ BaseApp │ │ way │ │ LogEngine│ │(vnpy_cta │ │(vnpy_ctp│ │EmailEng. │ │ strategy)│ │ vnpy_ib)│ │WechatEng.│ └────┬─────┘ └────┬────┘ └────┬─────┘ │ │ on_tick/on_order │ register(...) │ register(...) │ on_trade ... │ │ ▼ ▼ ▼ ╔══════════════════════════════════════════════════════════╗ ║ EventEngine (이벤트 버스 = 심장) ║ ║ _queue(Queue) ── _run 스레드 ──► _process(event) ║ ║ 타입별 핸들러 dict + 범용 핸들러 list + 1초 타이머 ║ ╚══════════════════════════════════════════════════════════╝ ▲ │ 분배 │ put(Event) ▼ [브로커 서버] ──C++/REST API──► 시세·체결 [구독자들이 콜백 수신]

핵심 설계 패턴 ① — 이벤트 드리븐 디커플링

이벤트 엔진의 심장은 30줄 남짓이다. 큐에서 사건을 꺼내(_run), 타입별 핸들러와 범용 핸들러에게 뿌린다(_process). 게이트웨이가 시세를 받으면 전략을 직접 부르는 게 아니라, 그냥 큐에 put할 뿐이다. 누가 그 시세를 받을지는 register한 쪽이 결정한다 — 발신자와 수신자가 서로를 모른다.

# vnpy/event/engine.py — 이벤트 루프의 핵심
def _run(self):
    while self._active:
        try:
            event = self._queue.get(block=True, timeout=1)
            self._process(event)
        except Empty:
            pass

def _process(self, event):
    if event.type in self._handlers:
        [handler(event) for handler in self._handlers[event.type]]
    if self._general_handlers:
        [handler(event) for handler in self._general_handlers]

핵심 설계 패턴 ② — 게이트웨이 추상화 + 이중 이벤트 발행

BaseGateway는 모든 브로커가 지켜야 할 계약이다. connect·subscribe·send_order·cancel_order·query_account·query_position은 반드시 구현하고, 데이터가 들어오면 on_tick·on_order·on_trade 같은 콜백으로 이벤트 엔진에 "밀어 넣는다". 영리한 부분은 같은 데이터를 두 번 발행한다는 것: 범용 이벤트(EVENT_TICK)와, vt_symbol이 붙은 종목별 이벤트(EVENT_TICK + "IF2312.CFFEX")를 함께 던진다.

# vnpy/trader/gateway.py — 왜 두 번 발행하나?
def on_tick(self, tick):
    self.on_event(EVENT_TICK, tick)                    # ① 모두에게(OMS·모니터)
    self.on_event(EVENT_TICK + tick.vt_symbol, tick)   # ② 이 종목 구독자에게만

def on_event(self, type, data=None):
    event = Event(type, data)
    self.event_engine.put(event)                       # 큐에 던지고 끝

이 이중 발행 덕분에, 특정 종목만 매매하는 전략은 EVENT_TICK + vt_symbol만 구독해 자기 종목 틱만 받는다. 수천 종목의 시세가 흘러도 매번 필터링(O(n))할 필요 없이, 이벤트 이름으로 O(1) 라우팅이 된다.

핵심 설계 패턴 ③ — OMS는 "모든 데이터 이벤트를 구독하는 상황판"

OmsEngine은 초기화 때 모든 데이터 이벤트의 핸들러로 자기를 등록한다. 그래서 시스템에 흐르는 틱·주문·체결·포지션·잔고·계약정보가 전부 OMS의 메모리 딕셔너리에 최신 상태로 쌓인다. 그리고 MainEngine은 OMS의 조회 메서드(get_tick·get_contract·get_all_active_orders…)를 자기 몸에 바인딩해, 외부에선 main_engine.get_tick(...)처럼 투명하게 쓰게 한다.

# vnpy/trader/engine.py — OMS가 모든 데이터 이벤트를 구독
def register_event(self):
    self.event_engine.register(EVENT_TICK, self.process_tick_event)
    self.event_engine.register(EVENT_ORDER, self.process_order_event)
    self.event_engine.register(EVENT_TRADE, self.process_trade_event)
    self.event_engine.register(EVENT_POSITION, self.process_position_event)
    self.event_engine.register(EVENT_ACCOUNT, self.process_account_event)
    self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)

def process_tick_event(self, event):
    tick = event.data
    self.ticks[tick.vt_symbol] = tick   # 최신 틱을 상황판에 갱신
눈여겨볼 것
중국 선물의 "평금/평작"을 코드로 — OffsetConverter

중국 선물은 포지션을 닫을 때 오늘 연 것(평금, 平今)어제 것(평작, 平昨)의 수수료가 다르다. 그래서 OMS는 주문 이벤트를 처리하며 OffsetConverterOffset.OPEN/CLOSE/CLOSETODAY/CLOSEYESTERDAY를 자동 변환한다. 해외 도구엔 없는, "실전 중국 시장을 안 겪으면 모르는" 디테일이 코어에 박혀 있다는 게 이 프레임워크의 실전 내공을 보여준다.

데이터 흐름 — 틱 하나의 왕복

[브로커] ──► CtpGateway 가 TickData 생성 → self.on_tick(tick) │ on_tick → EVENT_TICK + (EVENT_TICK+vt_symbol) 두 번 put() ▼ EventEngine._queue ──(_run 스레드)──► _process(event) ├─► OmsEngine.process_tick_event (ticks[vt_symbol] 갱신) ├─► CtaEngine → strategy.on_tick(tick) (이 종목 구독자) └─► UI 모니터 위젯 갱신 ▼ 전략이 매수 결정 → OrderRequest 생성 cta_engine.send_order → MainEngine.send_order(req, gateway_name) ▼ gateway.send_order(req) → 브로커로 전송, on_order(order) 콜백 ▼ EVENT_ORDER 발행 → OmsEngine(active_orders 갱신 + OffsetConverter) └─► strategy.on_order / UI 갱신 ... 체결되면 on_trade → EVENT_TRADE → strategy.on_trade

핵심은 모든 화살표가 이벤트 큐를 통과한다는 것이다. 게이트웨이는 전략의 존재를 모르고, 전략은 게이트웨이의 종류를 모른다. 이 완전한 디커플링이 "백테스트 코드 = 실전 코드"를 가능하게 하는 근본 이유다 — 백테스트 엔진도 그저 또 하나의 이벤트 소스일 뿐이다.

5디렉토리 구조 해부

vnpy/ 아래 6개 하위 패키지가 전부 — 각자 뚜렷한 한 가지 책임
vnpy/ ├── __init__.py ← __version__ = "4.4.0" (버전의 단일 진실원) ├── event/ ★ 이벤트 엔진 (코어 중의 코어) │ └── engine.py Event, EventEngine (스레드 2개 + 큐 분배) ├── trader/ ★ 트레이딩 플랫폼 코어 │ ├── engine.py MainEngine · BaseEngine · OmsEngine · Log/Email/WechatEngine │ ├── gateway.py BaseGateway (브로커 접속 추상 계약) │ ├── app.py BaseApp (앱 플러그인 추상 descriptor) │ ├── object.py dataclass: TickData·BarData·OrderData·TradeData·*Request │ ├── constant.py Enum: Direction·Offset·Status·OrderType·Exchange·Interval │ ├── event.py 이벤트 타입 문자열 상수 (EVENT_TICK="eTick." 등) │ ├── converter.py OffsetConverter (평금/평작 오프셋 변환) │ ├── database.py BaseDatabase 추상 + BarOverview/TickOverview │ ├── datafeed.py BaseDatafeed 추상 + get_datafeed() 동적 로더 │ ├── optimize.py 파라미터 최적화 (격자 + 유전 알고리즘) │ ├── utility.py BarGenerator·ArrayManager(지표) 등 유틸(40KB) │ ├── locale/ i18n (중국어/영어 gettext) │ └── ui/ Qt GUI: mainwindow.py·widget.py·qt.py ├── alpha/ ★ 4.0 AI/ML 퀀트 모듈 (Qlib 계보) │ ├── dataset/ 팩터 피처 엔지니어링 (alpha_158, ta/ts/cs/math) │ ├── model/ ML 모델 (Lasso·LightGBM·MLP) │ ├── strategy/ ML 전략 템플릿 + 백테스트 │ └── lab.py 리서치 워크플로 매니저 ├── rpc/ ★ 분산 통신 (ZeroMQ) │ ├── server.py RpcServer (REQ-REP + PUB-SUB + 하트비트) │ ├── client.py RpcClient │ └── common.py HEARTBEAT_TOPIC, HEARTBEAT_INTERVAL └── chart/ ★ 고성능 캔들 차트 (pyqtgraph) ├── widget.py ChartWidget └── item.py CandleItem, VolumeItem

구조가 말해 주는 것: 책임이 칼같이 나뉘어 있다. event/는 순수 메시징(트레이딩을 전혀 모름), trader/는 트레이딩 도메인(주문·포지션·거래소), alpha/는 ML, rpc/는 분산, chart/는 시각화. 이렇게 한 패키지 = 한 관심사로 쪼갠 덕에, 이벤트 엔진만 떼어다 다른 프로젝트에 써도 될 만큼 독립적이다. 그리고 앞서 강조했듯 게이트웨이·앱·DB·데이터피드는 이 트리에 없다 — 전부 별도 vnpy_* 패키지로, datafeed.py의 동적 로더가 런타임에 import_module("vnpy_" + 이름)으로 불러온다(없으면 pip install vnpy_xxx 안내를 띄우고 우아하게 실패).

6학습 포인트

퀀트에 관심 없어도 훔쳐올 수 있는, 범용 소프트웨어 설계 교본
배울 것 ①

이벤트 드리븐 아키텍처를 표준 라이브러리만으로 구현하는 법

Kafka·RabbitMQ 같은 무거운 미들웨어 없이, threading + queue.Queue 30줄로 발신자-수신자 완전 분리를 달성한다. register/put/_process 3개 메서드만 이해하면 "옵저버 패턴 + 메시지 큐"의 본질이 손에 잡힌다. 실시간 데이터를 다루는 어떤 앱(채팅·모니터링·IoT)에도 그대로 이식 가능한 패턴.

배울 것 ②

추상 기반 클래스(ABC)로 이질적 외부 시스템을 통일하기

BaseGateway는 "브로커가 다 달라도 인터페이스는 하나"를 보여주는 교과서다. 20종이 넘는 게이트웨이가 같은 7개 추상 메서드를 구현할 뿐이다. 결제 게이트웨이·클라우드 provider·메신저 어댑터 등 "공급자는 여럿, 규격은 하나" 상황의 정석 설계.

배울 것 ③

플러그인 시스템 — 동적 임포트 + 우아한 폴백

코어는 vnpy_*컴파일 타임에 모른다. import_module(f"vnpy_{name}")로 런타임에 찾고, 없으면 예외로 죽는 대신 추상 기본형으로 폴백하며 설치 안내를 출력한다. "코어를 건드리지 않고 기능을 확장한다"는 개방-폐쇄 원칙(OCP)의 실전 구현.

배울 것 ④

dataclass + __post_init__로 도메인 모델에 불변식 심기

TickData가 생성되는 순간 vt_symbol = f"{symbol}.{exchange.value}"가 자동 계산된다. "이 객체는 항상 이 파생 키를 가진다"는 규칙을 생성자에 못박아 어디서도 깨지지 않게 한다. LLM 파이프라인이든 ETL이든, 값 객체(value object)를 설계하는 사람이 배울 패턴.

배울 것 ⑤

파사드(Facade)로 복잡한 하위 시스템 감추기

MainEngine은 OMS의 조회 메서드를 자기 몸에 바인딩(self.get_tick = oms.get_tick)해, 외부엔 "메인 엔진 하나만 알면 된다"는 단순한 표면을 준다. 내부는 여러 엔진으로 쪼개져 있어도 사용자는 몰라도 된다 — 대규모 시스템의 API 표면을 얇게 유지하는 법.

실습 아이디어 (짧게)

vnpy/event/engine.py를 처음부터 끝까지 읽어라(200줄 미만). 그 다음 trader/gateway.pyon_tick/on_order 이중 발행과, trader/engine.pyOmsEngine.register_event가 어떻게 짝을 이루는지 따라가 보라. 이 둘만 연결해서 봐도 "이벤트를 던지는 쪽 ↔ 구독하는 쪽"의 전체 그림이 완성된다.

7시스템 / 실행 요구사항

순수 파이썬 코어는 가볍지만, 실전 게이트웨이는 네이티브 빌드가 필요하다
항목요구사항 / 수치
운영체제Windows 11+ / Windows Server 2022+ / Ubuntu 22.04 LTS+ (일부 macOS 지원, 4.4.0에서 vnpy_tts macOS 추가). 64bit 전용
Python3.10 이상 (64bit), 권장 3.13. 3.10/3.11/3.12/3.13 지원
권장 설치VeighNa Studio 4.4.0 올인원 인스톨러 — 프레임워크 + VeighNa Station 관리도구 번들. 스크립트: install.bat(Win) / install.sh(Ubuntu) / install_osx.sh(macOS)
네이티브 빌드ta-lib는 시스템에 TA-Lib C 라이브러리 설치 필요. CTP류 게이트웨이는 C++ API 래퍼라 소스 빌드 시 C++ 툴체인(Win=MSVC, Linux=gcc) 필요
버전 고정PySide6 == 6.8.2.1 정확히 고정 — 컴파일된 게이트웨이 확장과의 ABI 호환 보장용. 함부로 올리면 접속이 깨질 수 있음
인증/네트워크실거래는 브로커 계좌 + 접속 정보(default_setting) 필요. 시세·주문은 브로커 서버와 상시 연결
ML 확장(선택)vnpy.alpha는 torch ≥ 2.6 · lightgbm ≥ 4.6 등 추가. GPU는 필수 아님(팩터 모델은 CPU로도 학습 가능하나 대규모는 GPU 권장)
주의 — 처음 겪는 함정
"pip install vnpy"만으론 아무 브로커도 안 붙는다

코어만 깔면 게이트웨이가 하나도 없어 실거래·시세가 안 된다. 게이트웨이·앱·DB·데이터피드를 각각 pip install vnpy_xxx로 따로 깔아야 비로소 돌아간다. 입문자는 이 "조립식" 구조에서 자주 막힌다 — 그래서 필요한 걸 미리 번들한 VeighNa Studio가 공식 권장 경로다. 또 CTP 실계좌 없이 연습하려면 시뮬레이션 게이트웨이 vnpy_tts나 로컬 페이퍼 계좌 vnpy_paperaccount를 쓴다.

8직접 해볼 수 있는 실습 과제

난이도별로 — 이벤트 흐름 관찰부터 ML 팩터 전략까지

과제 1. 시뮬레이션으로 이벤트 흐름 눈으로 보기 난이도 ★☆☆

VeighNa Studio를 설치하고 시뮬레이션 게이트웨이(vnpy_tts)나 페이퍼 계좌(vnpy_paperaccount)로 접속하라. GUI의 로그·시세·주문 모니터를 띄워 놓고 수동 주문을 한 번 내 본다. 틱 → 주문 → 체결이 각 모니터에 순서대로 뜨는 걸 보며, 4장의 데이터 흐름 다이어그램과 대조해 보라.

과제 2. 더블 MA CTA 전략 작성 + 백테스트 난이도 ★★☆

vnpy_ctastrategyCtaTemplate을 상속해 "단기 이평이 장기 이평을 상향 돌파하면 매수" 전략을 짜라. on_bar에서 ArrayManager로 이평을 계산하고, vnpy_ctabacktester GUI로 과거 데이터 백테스트를 돌려 자산 곡선·낙폭을 확인한다. 핵심 체험: 같은 전략 코드가 백테스트와 실전에서 동일하다는 것.

과제 3. 나만의 게이트웨이 스켈레톤 만들기 난이도 ★★☆

BaseGateway를 상속해 가짜 게이트웨이를 만들어라. connect에서 타이머로 랜덤 TickData를 생성해 on_tick으로 발행하고, send_order는 즉시 체결된 척 on_order+on_trade를 쏜다. 실제 브로커 없이도 추상 계약만 지키면 전체 시스템이 도는 걸 확인하는 게 목표.

과제 4. RPC로 전략 서버·클라이언트 분리 난이도 ★★★

vnpy/rpcRpcServer/RpcClient(또는 vnpy_rpcservice 앱)로, 게이트웨이·전략은 서버 프로세스에서 돌리고 GUI는 다른 프로세스(또는 다른 PC)에서 붙여라. ZeroMQ의 REQ-REP(함수 호출) + PUB-SUB(데이터 푸시)가 어떻게 결합되는지, 하트비트로 연결 상태를 어떻게 감시하는지 server.py를 읽으며 확인한다.

과제 5. vnpy.alpha 멀티팩터 ML 전략 난이도 ★★★

examples/alpha_research의 주피터 노트북을 따라, alpha_158 팩터 세트로 피처를 만들고 LightGBM 모델을 학습해 종목별 수익률을 예측하라. alphalens로 팩터의 IC(정보계수)를 평가한 뒤, 횡단면 전략 템플릿으로 백테스트한다. Qlib 계보의 "팩터 → 모델 → 전략" 파이프라인을 한 바퀴 도는 게 목표.

9관련 기술 심화 학습 로드맵

이 레포를 온전히 이해하려면 밟을 5주 코스
주차주제무엇을 · 왜
1주차동시성 + 이벤트 드리븐파이썬 threading·queue.Queue, 옵저버 패턴, 메시지 큐 기초. vnpy/event/engine.py를 한 줄씩 재현해 나만의 미니 이벤트 엔진을 만든다.
2주차트레이딩 도메인 모델틱·바·주문·체결·포지션의 개념, 거래소·주문유형·오프셋(평금/평작) 이해. trader/object.pyconstant.py를 읽고 vt_symbol 규칙을 체득.
3주차게이트웨이 + OMS추상 기반 클래스로 외부 시스템 통합, 이중 이벤트 발행, 상태 저장 엔진. gateway.py+engine.py의 OMS를 짝지어 분석하고 과제 3(가짜 게이트웨이)을 수행.
4주차CTA 전략 + 백테스트CtaTemplate 생명주기(on_tick/on_bar/on_order/on_trade), ArrayManager 지표, 파라미터 최적화(격자·유전). 과제 2로 전략을 실제로 돌려 본다.
5주차ML 팩터 투자 + 분산vnpy.alpha의 팩터 엔지니어링·LightGBM·횡단면 전략(Qlib 계보), 그리고 rpc(ZeroMQ)로 분산 배포. 연구 파이프라인과 운영 아키텍처를 함께 잡는다.

10핵심 키워드 사전

이 문서에 나온 개념들을 한 줄로
EventEngine (이벤트 엔진)
사건을 큐에 담고 스레드로 꺼내 등록된 핸들러에게 분배하는 코어. VeighNa의 심장이자 모든 통신의 통로.
MainEngine (메인 엔진)
게이트웨이·엔진·앱을 등록·관리하는 오케스트레이터. 트레이딩 명령을 게이트웨이로 위임하고 OMS 조회를 자기 표면에 노출.
BaseGateway (게이트웨이)
특정 브로커/거래소 접속을 표준 규격으로 감싼 추상 어댑터. connect·subscribe·send_order 등을 구현하고 on_tick 등으로 이벤트 발행.
BaseApp (앱)
전략·백테스트·리스크 같은 기능 모듈의 추상 descriptor. 각자 자기 engine_class를 갖고 별도 vnpy_* 패키지로 배포.
OmsEngine (OMS)
모든 데이터 이벤트를 구독해 주문·포지션·잔고·최신 시세를 메모리에 들고 있는 상황판. 시스템의 실시간 상태 단일 출처.
vt_symbol · vt_orderid
{종목}.{거래소}·{게이트웨이}.{주문ID} 형식의 전역 유일 키. 딕셔너리 키이자 이벤트 이름 접미사로 O(1) 라우팅을 가능케 함.
이벤트 드리븐 디커플링
발신자와 수신자가 서로를 모른 채 이벤트 큐로만 대화하는 구조. 게이트웨이·전략을 자유롭게 교체·재사용하게 함.
OffsetConverter (오프셋 변환)
중국 선물의 평금(平今, 오늘 것 청산)/평작(平昨, 어제 것 청산) 구분을 자동 처리하는 변환기. 실전 시장 디테일의 대표 사례.
CTA 전략 · CtaTemplate
추세추종형 단일 종목 전략(Commodity Trading Advisor). on_bar/on_tick 콜백으로 시세를 받아 매매하는 전략 앱의 기본 템플릿.
vnpy.alpha (팩터 ML)
4.0에서 추가된 멀티팩터 머신러닝 모듈. Qlib 계보로 alpha_158 팩터, Lasso/LightGBM/MLP 모델, 횡단면·시계열 전략 제공.
ZeroMQ RPC
vnpy/rpc가 REQ-REP(함수 호출)+PUB-SUB(데이터 푸시)+하트비트로 프로세스·머신 간 분산 배포를 지원하는 통신 계층.
게이트웨이 추상화
브로커가 20종이어도 인터페이스는 하나라는 설계. "공급자는 여럿, 규격은 하나" 문제의 정석 해법.

11참고 링크

원문으로 더 파고들기