본문 바로가기
기타

[시스템 설계] 증권 거래

by Nhahan 2025. 4. 6.

문제의 규모를 짐작해보자.

NYSE는 하루에 수십 억 건의 거래를, HKEX는 하루에 약 2천억 건의 주식 거래를 처리하고 있다.

 

1단계: 문제 이해 및 설계 범위 확정

  • 기능 요구사항
    • 주문 제출: 사용자는 특정 주식지정가 또는 시장가일정 수량 매수 및 매도 주문할 수 있다.
    • 주문 취소: 아직 체결되지 않은 주문은 사용자가 취소할 수 있다.
    • 주문 매칭: 매수 주문과 매도 주문의 가격과 시간을 고려해서 거래를 성사시킨다.
    • 시세 제공: 현재 주식 가격, 누가 얼마에 사고 팔려고 내놓았는지(호가창), 최근 거래 내역 등을 실시간으로 보여주어야한다.
    • 계정/포트폴리오 조회: 사용자가 자신의 현금 잔고나 보유 주식 현황을 볼 수 있어야한다.
  • 비기능 요구사항
    • 매우 낮은 지연 시간: 밀리초 단위의 응답 속도를 목표로 한다.
    • 매우 높은 처리량: 장 시작과 마감 시점이나 변동성이 클 때 초당 수천에서 수백만 건의 주문과 시세 업데이트를 처리할 수 있어야한다.
    • 고가용성: 시스템이 절대 멈추면 안된다. 아주 잠깐의 장애도 큰 금전적 손실로 이어진다.
    • 정확성 및 일관성: 돈과 주식이 오가는 시스템이므로 계산은 단 1원, 1주도 틀리면 안된다.
    • 보안성: 당연하다...
    • 공정성: 모든 사용자에게 공평한 거래 기회를 제공해야한다. (예, 주문 처리 순서)
  • 규모 추정

 

2단계: 개략적인 설계

  • 주요 컴포넌트
    • 클라이언트(Client)
    • 게이트웨이(Gateway): 수많은 클라이언트의 연결을 받아주는 첫 관문. 사용자 인증, 로드 밸런싱, 간단한 유효성 검사.
    • 주문 관리 시스템(Order Management System - OMS): 사용자의 주문을 접수하고, 유효한지 확인하고, 주문 상태를 관리하고, 주문 내역을 데이터베이스에 기록.
    • 매칭 엔진(Matching Engine, 중요): 특정 주식에 대한 모든 매수/매도 주문을 받아서, 정해진 규칙(가격 우선, 시간 우선)에 따라 실시간으로 매칭시켜 거래를 체결한다. 속도가 생명이므로 메모리 안에서 모든 작업을 처리한다.
    • 시세 정보 시스템(Market Data Sysem - MDS): 매칭 엔진에서 거래가 체결되거나 새로운 주문이 들어와 호가창이 바뀔 때마다 이 정보를 받아서 모든 클라이언트에게 실시간으로 뿌려주는(Boradcasting) 역할.
    • 계정/포트폴리오(Account/Portfolio): 사용자의 현금 잔고와 주식 보유 수량을 관리한다. 거래가 체결되면 여기서 잔고/수량을 업데이트.
    • 데이터베이스: 주문 기록, 체결 기록, 사용자 정보, 계좌 정보 등을 저장한다. 역할에 따라 여러 종류의 DB를 사용한다.
    • 메시지 큐/이벤트 스트림: 컴포넌트끼리 직접 통신하면 의존성이 높아지고 한쪽이 느려지면 전체가 느려질 수 있다. 메시지 큐를 중간에 둬서 OMS에서 매칭 엔진으로 주문을 전달하거나, 매칭 엔진에서 MDS나 계정 서비스로 체결 정보를 전달하는 등 비동기적으로 데이터를 안정적으로 전달한다.
  • flow 예시
    • 주문: 클라이언트 → 게이트웨이 → OMS → MQ → 매칭 엔진
    • 체결/취소: 클라이언트 → 게이트웨이 → 매칭엔진 → MQ → OMS/계정 서비스 → 게이트웨이 → 클라이언트
    • 시세: 클라이언트 → 게이트웨이 → 매칭엔진 → MQ → MDS → 스트리밍 → 클라이언트

 

3단계: 상세 설계

  • 매칭 엔진(중요)
    • 종목별 분산: 각 주식 종목 별로 별도의 매칭 엔진 인스턴스 또는 스레드를 할당하여 병렬 처리 및 장애 격리 효과
    • 주문장부 구현
      • 자료구조 선택
        • 옵션1: 가격 레벨은 정렬된 상태 유지가 중요하므로 `TreeMap`(자바 기준) 또는 이와 유사한 균형 이진 트리(Balanced Binary Tree)를 사용하여 가격 레벨을 키로 관리. 각 가격 레벨에는 해당 가격의 주문들을 시간 순서대로 담는 LinkedList 또는 Queue 사용. (가격 검색 O(log P), 시간 우선 순위O(1))
        • 옵션2: 매수 주문용 Max-Heap과 매도 주문용 Min-Heap(우선순위 큐)을 사용하여 최우선 매수/매도 호가(BBO, Best Bid/Offer)를 O(1)에 찾도록 구현. 주문 추가/삭제는 O(log N).
      • 주문 ID 조회: 주문 취소를 위해 주문 ID로 주문 정보를 빠르게 찾을 수 있는 HashMap도 함께 사용.
    • 매칭 로직
      • 지정가: 들어온 주문 가격이 상대편 최우선 호가보다 유리하거나 같으면 즉시 체결(Aggressing)하고 아니라면 주문 장부에 추가(Resting)
      • 시장가: 주문장부의 가장 유리한 가격부터 순차적으로 물량을 소진하며 체결("Walking the book").
        • 시장가 주문이 모든 물량을 소진하지 못할 경우 어떻게 처리할지 정책 ㅅ러정 필요
    • 동시성 제어: 하나의 종목에 대한 변경은 반드시 순서대로 처리되어야하므로, 보통 해당 종목의 주문장부는 단일 스레드로 처리한다. 락을 사용하면 성능 저하가 크기 때문에, 각 종목을 처리하는 스레드를 분리하고 스레드 간 통신은 큐를 사용.
    • 성능 최적화 팁
      • 메모리 관리: 매칭 중 동적 메모리 할당 최소화 (Object Pooling 등).
      • CPU 최적화: CPU 캐시 효율성을 높이는 데이터 구조 설계(Data Locality), CPU 코어에 스레드 고정(CPU Affinity).
      • 커널 바이패스(Kernel Bypass Networking): (HFT 환경) OS 네트워크 스택을 우회하여 지연 시간 최소화(DPDK, SolarFlare OpenOnLoad 등).
  • 시세 정보 시스템(MDS)
    • 프로토콜: 기관 투자자들은 주로 FIX(Financial Information eXchange) 프로토콜을 사용한다. 주문과 시세 정보 교환의 표준. 일반 사용자에게는 WebSocket이나 SSE가 더 적합하다.
    • 데이터 처리: 단순히 체결 정보만 보내는 것이 아니라, BBO, 호가창 깊이(Depth - 예: 상위 5개 호가), 요약 정보(개장가, 고가, 저가, 종가, 거래량) 등 다양한 형태로 가공하여 전송한다.
    • 최적화
      • 데이터 압축: 전송량 감소
      • 델타 업데이트: 전체 상태 대신 변경된 부분만 전송
      • 구독 필터링: 사용자가 원하는 종목이나 정보 유형만 구독하여 불필요한 트래픽 방지.
      • 팬아웃: 하나의 시세 정보를 수백만 사용자에게 효율적으로 전달하기 위해 메시지 큐의 파티셔닝 활용, 또는 계층적 배포 구조나 UDP 멀티캐스트(데이터센터 내) 사용.
    • 상태 복구: 클라이언트가 재접속했을 때 현재 시세를 받을 수 있도록 스냅샷 제공 또는 특정 시점 이후의 모든 업데이트를 재전송하는 매커니즘 필요.
  • 주문 관리 시스템(OMS)
    • 멱등성: 동일한 주문 요청이 여러 번 들어와도 실제로는 한번 만 처리되도록 보장(고유한 클라이언트 주문 ID 사용).
    • 감사 추적(Audit Trail): 모든 주문의 생성 변경, 체결, 취소 이력은 규제 준수 및 문제 해결을 위해 변경 불가능(Immutable)하게 기록되어야 함.
    • 사전 리스크 검증(Pre-Trade Risk Check, 중요):  주문이 매칭 엔진으로 가기 전에 OMS에서 사용자의 잔고, 증거금, 보유 수량, 거래 한도, 기타 규제 사항 등을 미리 검사해야한다. 여기서 걸러지지 않으면 매칭 엔진과 시장에 큰 혼란을 줄 수 있다.
  • 일관성(Consistency)
    • 딜레마: 매칭 엔진은 메모리에서 초고속으로 체결시키는데, 이 결과를 DB(OMS 원장, 계좌)에 반영하는 것은 상대적으로 느리다. 체결은 됐는데 DB 업데이트 전에 시스템에 문제가 생기면 돈과 주식이 사라지거나 잘못 계산 될 수 있다.
    • 2PC의 한계: 분산 트랜잭션을 위한 Two-Phase Commit은 모든 시스템이 응답할 때까지 기다려야 하므로 지연 시간이 길고, 코디네이터가 실패 지점이 될 수 있어 주식 거래소에는 부적합하다.
    • 현실적인 접근법 - (Event Driven + Durable Log):
      1. 매칭 엔진은 거래를 체결시킨 직후, 해당 체결 이벤트(Trade Event) 정보를 영속적인(Durable) 로그에 먼저 기록한다. (예: 로컬 디스크의 Write-Ahead Log 또는 고가용성 메시지 큐-Kafka-에 fsync 옵션으로 기록)
      2. OMS, 계정 서비스 등 다른 시스템들은 이 로그/이벤트를 비동기적으로 구독하여 각자의 DB 상태를 업데이트한다. (결과적 일관성 - Eventual Consistency)
    • 팁: 이 방식의 핵심은 가장 중요한 '체결' 행위 자체를 원자적으로 기록하고 영속화하는 것이다. 그 후 처리는 비동기적으로 진행하여 성능을 확보한다. 단, 비동기 처리 과정에서의 중복 처리 방지(멱등성, Idempotency) 및 최종 상태 확인/조정(Reconciliation) 매커니즘이 필요하다. 트레이드오프와 해결 방안 숙지

 

4단계: 마무리(운영 및 미래 고려)

  • 고가용성 및 복구(Availability & Recovery)
    • 매칭 엔진 복구: Active-Passive 구성 시, Standby 노드는 Active 노드의 이벤트 스트림을 실시간으로 복제(State Machine Replication)하여 상태를 동일하게 유지해야한다. 장애 발생 시 StandBy로 매우 빠르게 전환(FailOver)되어야 한다.
    • 데이터센터 이중화(Disaster Recovery): 지진, 화재 등 데이터센터 전체 장애에 대비하여 지리적으로 떨어진 여러 지역에 시스템을 구축하고 데이터를 실시간으로 복제해야한다.
  • 지연 시간 최적화 전략
    • Colocation: 거래 회사가 자신의 서버를 거래소 서버와 같은 데이터센터 건물 안에 설치하여 물리적 거리를 최소화
    • 네트워크: 전용선 사용, 네트워크 장비 최적화, 커널 바이패스
    • 하드웨어: 고성능 CPU, 충분한 RAM, 빠른 네트워크 카드(NIC), 경우에 따라 FPGA(프로그래머블 반도체)를 특정 연산(데이터 필터링 등) 가속에 사용
    • 소프트웨어: 저수준 언어 최적화, 알고리즘/자료구조 효율화, 비동기 처리, 불필요한 작업 제거.
  • 보안 강화: 시장 교란(Spoofing-허수 주문, Layering-단계적 허수 주문) 탐지 및 방지 매커니즘, DDoS 방어, 침입 탐지/방지 시스템(IDS/IPS)
  • 규제 준수: 서킷 브레이커(시장 과열 시 일시 거래 중단) 발동 및 해제 처리 등 규제 관련 기능 구현

댓글