[코인 거래로 알아보는 매칭엔진] #3. 매칭 알고리즘
목차
같은 가격에 주문이 여러 개 줄 서 있을 때 누구의 주문부터 체결될까요? Coinbase·Binance 공식 문서로 Price-Time Priority를 살펴보고, CME Globex의 Pro-Rata와 조합형 알고리즘과 비교한 뒤 부분 체결과 잔량 처리, Self-Trade Prevention까지 정리합니다. 이 글은 코인 거래로 알아보는 매칭엔진 시리즈의 3번째 글입니다. 앞 편은 한 가지 질문으로 끝났습니다. Coinbase의 이 질문에 답하는 규칙이 Matching Algorithm(매칭 알고리즘)입니다. 이번 편에서는 Coinbase Exchange와 Binance의 공식 문서, CME Globex의 공식 위키, 그리고 Wikipedia와 Databento의 정리 글을 근거로 세 가지를 살펴봅니다. Coinbase Exchange의 매칭엔진 문서는 첫 문장에서 체결 규칙을 밝힙니다. Coinbase Exchange operates a continuous first-come, first-serve order book. Orders are executed in price-time priority as received by the matching engine. (Coinbase Exchange는 선착순으로 처리되는 연속 오더북을 운영합니다. 주문은 매칭엔진이 받은 순서를 기준으로 price-time priority에 따라 체결됩니다.) Price-Time Priority는 두 단계로 정렬하는 규칙입니다. 먼저 가격을 봅니다. 매수라면 더 비싸게 사겠다는 주문이, 매도라면 더 싸게 팔겠다는 주문이 앞섭니다. 가격이 같을 때만 시간을 봅니다. 매칭엔진에 먼저 도착한 주문이 앞섭니다. 같은 가격 안에서는 먼저 들어온 주문이 먼저 나가므로 FIFO(First-In-First-Out)라고도 부릅니다. 앞 편에서 본 가격 레벨을 떠올리면 이 규칙을 그림으로 그릴 수 있습니다. 가격 레벨마다 주문이 도착 순서대로 한 줄로 서 있고, 가격 레벨 자체는 유리한 가격부터 늘어서 있습니다. 매칭엔진은 가장 유리한 가격 레벨의 맨 앞 주문부터 체결하고, 그 레벨이 바닥나면 다음 레벨로 넘어갑니다. Binance 문서도 같은 규칙을 전제로 설명합니다. 뒤에서 살펴볼 Binance의 주문 정정 FAQ는 매수 주문 네 개가 쌓인 오더북을 예로 듭니다. 문서는 이 표를 두고 "Your order 15 is the second one in the queue based on price and time", 즉 주문 15가 가격과 시간 기준으로 대기열의 두 번째라고 설명합니다. 87,000에 있는 세 주문이 86,999의 주문보다 앞서고, 87,000 안에서는 먼저 들어온 User A가 앞섭니다. 우선순위만큼 중요한 것이 체결 가격입니다. 매수 주문과 매도 주문의 가격이 서로 다를 때 어느 쪽 가격으로 체결될까요? Coinbase Exchange 문서의 답은 오더북에 먼저 있던 주문의 가격입니다. Orders are matched against existing order book orders at the price of the order on the book, not at the price of the taker order. (주문은 오더북에 있던 주문과 체결되며, 체결 가격은 taker 주문의 가격이 아니라 오더북에 있던 주문의 가격입니다.) 문서가 드는 예시는 이렇습니다. User A가 1 BTC를 100 USD에 사겠다는 매수 주문을 먼저 넣고, 이어서 User B가 1 BTC를 80 USD에 팔겠다는 매도 주문을 넣습니다. 거래는 100 USD에 체결됩니다. 문서는 그 이유를 User A의 주문이 매칭엔진에 먼저 도착했고 가격 우선권을 가졌기 때문이라고 적습니다. User B는 80 USD에라도 팔 생각이었지만 100 USD를 받았습니다. 이렇게 Taker가 자기가 부른 가격보다 유리하게 체결되는 것을 Price Improvement(가격 개선)라고 부릅니다. 오더북에 먼저 올라가 가격을 제시한 Maker는 자기가 부른 가격을 그대로 받고, 나중에 들어와 그 가격을 받아들인 Taker는 자기 한도보다 나쁘지 않은 가격을 받습니다. 앞 편에서 시장가 주문이 여러 가격 레벨을 차례로 체결하며 레벨마다 다른 가격을 받던 것도 같은 규칙에서 나온 결과입니다. 시간 우선순위는 주문이 오더북에 올라간 순간 정해집니다. 그렇다면 이미 올라간 주문을 고치면 어떻게 될까요? 가장 단순한 방법은 기존 주문을 취소하고 새 주문을 넣는 cancel-replace입니다. Binance 문서는 이 방식의 결과를 분명히 적습니다. Time priority is lost. The new order executes after existing orders at the same price. (시간 우선순위를 잃습니다. 새 주문은 같은 가격의 기존 주문들 뒤에서 체결됩니다.) 앞의 표에서 주문 15의 수량을 5.50에서 5.00으로 줄이려고 cancel-replace를 쓰면, 주문 15는 취소되고 새 주문 22가 87,000 대기열의 맨 뒤, User B의 주문 20 뒤에 섭니다. 매칭엔진 입장에서는 새로 도착한 주문이기 때문입니다. Binance는 이 문제를 피하는 Order Amend Keep Priority 요청을 따로 제공합니다. 이 요청이 허용하는 정정은 한 가지, 수량을 줄이는 것뿐입니다. 같은 예시에서 이 요청으로 수량을 5.00으로 줄이면 주문 ID는 15 그대로이고 대기열의 두 번째 자리도 유지됩니다. 문서의 표현으로는 "Only the quantity of the order changes"입니다. 허용 범위가 수량 감소로 한정된 이유는 문서에 적혀 있지 않습니다. 다만 Price-Time Priority의 정의에서 추론해 볼 수는 있습니다. 가격을 바꾸면 주문이 서야 할 가격 레벨 자체가 바뀝니다. 수량을 늘리면 뒤에 선 주문들보다 더 많은 물량이 앞에서 먼저 체결되므로, 그 늘어난 물량만큼은 나중에 도착한 주문이 줄을 앞지르는 셈입니다. 수량을 줄이는 것만이 다른 참여자의 순서를 해치지 않는 정정인 셈입니다. 매칭 알고리즘은 체결 순서만 정하는 것이 아니라 참여자가 주문을 내는 방식에도 영향을 줍니다. Wikipedia의 Order matching system 문서는 Janecek과 Kabrhel의 2007년 논문 「Matching Algorithms of International Exchanges」를 인용해 Price/Time 알고리즘의 특징을 세 가지로 정리합니다. 세 가지 모두 한 가지 사실에서 나옵니다. Price-Time Priority에서는 같은 가격이라면 먼저 온 것이 가장 큰 무기입니다. 이 성질이 뒤에서 볼 Pro-Rata와 갈리는 지점입니다. 1편에서 파생상품 시장에서는 물량에 비례해 배분하는 방식도 쓰인다고 짧게 언급했습니다. 그 방식이 Pro-Rata입니다. Databento의 CME 매칭 알고리즘 정리 글은 이렇게 정의합니다. An incoming aggressor order quantity is matched based on each resting order's pro-rated percentage. Allocations are rounded down to the nearest integer, including 0. Excess lots are allocated FIFO. (들어온 aggressor 주문의 수량은 오더북에 있던 각 주문의 비례 지분에 따라 배분됩니다. 배분량은 가장 가까운 정수로 내림하며, 0이 될 수도 있습니다. 남는 수량은 FIFO로 배분합니다.) 여기서 aggressor 주문은 오더북에 있는 주문과 즉시 체결되려고 들어온 주문, 즉 Taker 주문을 가리킵니다. CME의 선물·옵션은 계약 단위(lot)로 거래되므로 배분량을 정수로 맞춰야 하고, 내림 처리로 생긴 나머지는 다시 시간 순서로 나눕니다. 두 알고리즘의 차이를 설명하기 위해 만든 가상의 예시로 비교해 보겠습니다. 한 가격 레벨에 매수 주문 세 개가 도착 순서대로 쌓여 있고, 여기에 45 lot의 매도 주문이 들어옵니다. FIFO는 앞에서부터 채웁니다. A와 B가 전량 체결되고, C는 남은 5 lot만 받습니다. Pro-Rata는 잔량 비율대로 나눕니다. 전체 잔량 100 lot 중 A는 10%, B는 30%, C는 60%이므로 45 lot을 이 비율로 나누면 4.5, 13.5, 27이 됩니다. 이를 내림하면 4, 13, 27이고 합계는 44 lot입니다. 남은 1 lot은 FIFO로 가장 먼저 도착한 A에게 돌아갑니다. 결과가 크게 다릅니다. FIFO에서는 가장 먼저 온 A가 전량 체결되고 가장 큰 C는 5 lot만 받았지만, Pro-Rata에서는 가장 큰 C가 가장 많이 받았습니다. Pro-Rata에서 많이 체결되고 싶다면 먼저 오는 것보다 크게 내는 것이 중요합니다. Wikipedia 문서가 Pro-Rata의 특징으로 대기열에 큰 지정가 주문으로 합류하려는 유인이 생기고, 그 결과 최우선 가격에 쌓인 물량이 비교적 커진다고 정리한 것도 이 때문입니다. 실제 거래소는 두 방식 중 하나만 고르지 않고 여러 규칙을 단계로 이어 붙이기도 합니다. CME Group의 전자 거래 플랫폼인 CME Globex가 대표적입니다. CME의 공식 위키는 매칭 과정을 이렇게 설명합니다. All CME Globex Matching Algorithms are composed of one or more steps. The steps are followed in a particular order. (모든 CME Globex 매칭 알고리즘은 하나 이상의 단계로 구성됩니다. 단계는 정해진 순서대로 진행됩니다.) 들어온 수량을 첫 번째 단계의 규칙대로 배분하고, 남은 수량이 있으면 다음 단계로 넘깁니다. 수량이 다 배분될 때까지 이를 반복합니다. 각 단계에 쓰이는 규칙은 Databento 글의 알고리즘 목록에서 확인할 수 있습니다. 이 규칙들을 조합하면 다양한 알고리즘이 나옵니다. 예를 들어 Databento 글이 설명하는 Allocation 알고리즘은 Top order 비율 배분, 최소 배분 기준이 2 lot인 Pro-Rata, FIFO 순서로 진행됩니다. Threshold pro rata with LMM은 Top order, LMM, 최소 배분이 있는 Pro-Rata, FIFO의 네 단계입니다. 두 조합 모두 마지막 단계는 FIFO입니다. CME 위키에는 알고리즘을 아예 건너뛰는 경우도 적혀 있습니다. 들어온 수량이 해당 가격 레벨의 잔량을 전부 채우고도 남는다면 어차피 모든 주문이 전량 체결되므로 배분 규칙이 필요 없습니다. 이때는 FIFO 순서로 전량을 체결하고, 남은 수량은 적절한 가격 레벨의 새 매수·매도 호가가 됩니다. 알고리즘은 한 가격 레벨을 다 채우지 못해 누구에게 얼마를 줄지 정해야 할 때만 의미가 있습니다. Databento 글은 CME의 알고리즘별로 적용된 대표 상품과 거래량 비중도 함께 적고 있습니다. 비중이 표시된 알고리즘만 옮기면 다음과 같습니다. 순수 FIFO가 70.3%로 가장 크고, 순수 Pro-Rata는 0.0001% 미만입니다. 대신 Pro-Rata 단계가 명시된 조합형 알고리즘인 Allocation과 Threshold pro rata 계열만 합쳐도 16.5%입니다. Pro-Rata는 단독보다 다른 규칙과 섞인 형태로 쓰이는 셈입니다. 다만 이 수치는 Databento 글에 실린 값이고, 측정 기간이나 기준 시점은 글에 적혀 있지 않습니다. 글의 게시일은 2025년 8월 1일입니다. 코인 거래소 쪽은 이번 편에서 확인한 문서 기준으로 Price-Time Priority입니다. Coinbase Exchange는 문서 첫 문장에 이를 명시하고, Binance는 주문 정정 FAQ에서 가격과 시간 기준의 대기열을 전제로 설명합니다. CME에서 거래량 비중이 가장 큰 FIFO와 같은 규칙이 코인 거래소에서도 쓰이는 모습입니다. 지금까지는 누구에게 얼마를 배분할지를 봤습니다. 이번에는 들어온 주문의 입장에서 보겠습니다. 들어온 주문의 수량이 한 번에 다 채워지지 않거나, 여러 주문과 나뉘어 체결되는 것을 Partial Fill(부분 체결)이라고 부릅니다. 설명을 위해 만든 가상의 오더북에 Price-Time Priority를 적용해 보겠습니다. 여기에 100,100,000원 지정가로 1.0 BTC를 사겠다는 매수 주문이 들어옵니다. 매칭엔진은 가장 싼 100,000,000원 레벨부터 봅니다. 그 레벨 안에서는 먼저 온 주문 1의 0.2 BTC, 이어서 주문 2의 0.4 BTC와 체결합니다. 이 레벨이 바닥나면 100,100,000원 레벨로 넘어가 주문 3의 0.3 BTC와 체결합니다. 매수 주문 하나가 세 번의 체결로 나뉘었고, 앞에서 본 규칙대로 체결 가격은 오더북에 있던 각 주문의 가격입니다. 1.0 BTC 중 0.9 BTC가 체결되었고 0.1 BTC가 남았습니다. 반대로 오더북에 있던 쪽이 부분 체결될 수도 있습니다. 매수 주문이 0.5 BTC였다면 주문 1이 전량 체결된 뒤 주문 2는 0.4 BTC 중 0.3 BTC만 체결되고, 남은 0.1 BTC는 100,000,000원 레벨의 맨 앞에서 다음 주문을 기다립니다. 앞선 주문이 모두 사라졌기 때문입니다. 앞 예시의 매수 주문에 남은 0.1 BTC는 어떻게 될까요? Coinbase Exchange 문서는 주문의 생명주기를 세 상태로 정리합니다. 즉시 체결되지 않은 부분은 open 상태로 오더북에 남고, 취소되거나 나중에 들어온 주문에 의해 다 채워질 때까지 머뭅니다. 앞 예시라면 남은 0.1 BTC는 100,100,000원 매수 호가로 오더북에 올라갑니다. 그 가격의 매수 주문은 이것뿐이므로 이 주문이 새 최우선 매수 호가가 됩니다. 들어온 시점에는 Taker였던 주문이 남은 수량으로는 Maker가 되는 것입니다. CME Globex도 같습니다. 위키는 들어온 수량이 한 가격 레벨의 잔량을 모두 채우고도 남으면 "Remaining aggressing unmatched quantity becomes a new bid/offer at the appropriate price level", 즉 남은 수량이 적절한 가격 레벨의 새 매수·매도 호가가 된다고 적습니다. 물론 남은 수량이 항상 오더북에 올라가는 것은 아닙니다. 앞 편에서 본 것처럼 IOC 주문은 남은 수량을 즉시 취소하고, FOK 주문은 전량을 채울 수 없으면 처음부터 전부 취소합니다. 시장가 주문은 어떤 부분도 오더북에 올라가지 않습니다. 부분 체결 뒤의 잔량 처리는 매칭 알고리즘이 아니라 주문에 붙은 조건이 정합니다. 잔량이 체결도 되지 않고 오더북에도 남지 않는 경우가 하나 더 있습니다. 내 주문끼리 만나는 경우입니다. 같은 사용자의 매수 주문과 매도 주문이 서로 체결되는 것을 Self-Trade(자기 거래)라고 부르고, Coinbase와 Binance는 모두 이를 막는 Self-Trade Prevention(STP)을 둡니다. Coinbase Exchange는 자기 거래를 허용하지 않습니다. 같은 사용자의 두 주문이 교차하면 서로 체결되지 않고, 대신 어떻게 처리할지를 주문의 STP 플래그로 정합니다. 이때 나중에 들어온 Taker 주문의 STP 지시가 오더북에 있던 오래된 주문의 지시보다 우선합니다. Binance도 비슷한 모드를 둡니다. STP를 적용하지 않는 어떤 모드가 적용될지는 원칙적으로 Taker 주문의 모드가 정합니다. 다만 Binance 문서에서 눈여겨볼 부분은 STP로 사라진 수량을 Prevented Quantity라는 별도 값으로 기록한다는 점입니다. 문서는 주문이 열려 있는 동안 다음 식이 성립한다고 적습니다. 원래 수량에서 체결된 수량과 STP로 막힌 수량을 빼면 앞으로 체결될 수 있는 수량이 남습니다. 부분 체결을 다룰 때 체결되지 않은 수량이 모두 대기 중인 것은 아니라는 뜻입니다. 문서의 매칭 알고리즘은 같은 가격에 주문이 여러 개 있을 때 누구에게 얼마를 체결시킬지 정하는 규칙입니다. 이번 편에서 본 규칙들은 모두 매칭엔진 하나가 주문의 도착 순서를 정한다는 전제에서 출발합니다. "먼저 온 주문"을 가리려면 누군가 순서를 정해야 하기 때문입니다. 다음 편에서는 이 전제를 CEX와 DEX 양쪽에서 비교합니다. 1편에서 잠깐 본 것처럼 dYdX는 오프체인 오더북에서 Price-Time Priority를 유지하고, Uniswap 같은 AMM은 매칭 규칙 자체를 없앴습니다. 각 구조에서 이번 편의 매칭 알고리즘이 어떻게 구현되거나 대체되는지, 그리고 체결 순서를 정하는 권한이 누구에게 있는지 살펴보겠습니다. 전체 목차:개요
num-orders가 2 이상인 가격 레벨, 즉 같은 가격에 주문이 여러 개 줄 서 있을 때 시장가 주문이 들어오면 누구의 주문부터 체결될까요?Price-Time Priority
가격이 먼저, 시간은 그다음
사용자 주문 ID 방향 가격 수량 User A 10 BUY 87,000 1.00 나 15 BUY 87,000 5.50 User B 20 BUY 87,000 4.00 User C 21 BUY 86,999 2.00 체결 가격은 먼저 온 주문의 가격
시간 우선순위를 잃는 순간
FIFO가 만드는 참여자의 행동
FIFO만 있는 것은 아니다 — Pro-Rata와 조합형 알고리즘
Pro-Rata — 물량에 비례해 나누기
주문 도착 순서 잔량 FIFO 배분 Pro-Rata 배분 A 1 10 lot 10 4 + 1 = 5 B 2 30 lot 30 13 C 3 60 lot 5 27 합계 100 lot 45 45 여러 단계를 잇는 CME Globex
단계 규칙 Databento 글의 설명 Top order 접수 시점에 시장 가격을 개선한 주문을 우선합니다. FIFO 같은 다른 알고리즘과 조합할 수 있습니다. LMM (Lead Market Maker) FIFO 매칭 전에 LMM의 주문이 aggressor 주문 수량의 설정된 비율을 먼저 배분받습니다. (FIFO with LMM 알고리즘의 설명) Minimum allocation 주문이 받아야 할 최소 수량입니다. 이 기준보다 작은 배분은 무시되거나 채워지지 않습니다. Leveling 내림으로 남은 수량을 참여 주문들에게 나누되, 한 주문이 1 lot보다 많이 받지 않게 합니다. 어떤 시장이 어떤 규칙을 쓰나
알고리즘 대표 상품 거래량 비중 FIFO ES, NQ, ZN, ZF, ZB, CL 단일 월물 70.3% Configurable ZT, ZQ, ZC, ZS, ZW 단일 월물 12.7% Allocation SR3 단일 월물 10.5% Threshold pro rata with LMM 10년 만기 T-note 옵션 4.6% Threshold pro rata 옥수수 옵션 1.4% FIFO with LMM SR3 averaged price bundles 0.5% Pro rata 6S, 6M 캘린더 스프레드 0.0001% 미만 부분 체결과 잔량 처리
한 주문이 여러 번에 나뉘어 체결되는 과정
구분 가격 도착 순서 잔량 매도 100,100,000원 3 0.3 BTC 매도 100,000,000원 1 0.2 BTC 매도 100,000,000원 2 0.4 BTC 체결 순서 상대 주문 체결 가격 체결 수량 1 주문 1 100,000,000원 0.2 BTC 2 주문 2 100,000,000원 0.4 BTC 3 주문 3 100,100,000원 0.3 BTC 합계 0.9 BTC 남은 수량은 어디로 가나
상태 문서의 설명 received "Valid orders that are sent to the matching engine and confirmed immediately." open "Any part of the order not filled immediately. Orders stay open until canceled or filled by new orders." done "An full order executed against another order immediately. A partial order filled or canceled (and no longer eligible for matching)" 체결되지 않고 사라지는 수량 — Self-Trade Prevention
STP 옵션 플래그 동작 Decrement & cancel (기본값) dc작은 주문을 취소하고, 큰 주문은 작은 주문의 수량만큼 줄입니다. 수량이 같으면 둘 다 취소합니다. Cancel oldest co오더북에 있던 오래된 주문을 전량 취소하고, 새 Taker 주문은 계속 체결을 진행합니다. Cancel newest cn새 Taker 주문을 전량 취소하고, 오래된 주문은 오더북에 남깁니다. Cancel both cb두 주문을 모두 즉시 취소합니다. NONE을 제외하면 다음 다섯 가지입니다.모드 동작 EXPIRE_TAKERTaker 주문의 남은 수량을 즉시 만료시킵니다. EXPIRE_MAKERMaker 주문의 남은 수량을 즉시 만료시킵니다. EXPIRE_BOTH두 주문의 남은 수량을 모두 즉시 만료시킵니다. DECREMENT막힌 수량만큼 두 주문의 Prevented Quantity를 늘리고, 작은 쪽을 만료시킵니다. 수량이 같으면 둘 다 만료시킵니다. TRANSFER같은 계정이면 DECREMENT와 같습니다. 같은 trade group에 속한 다른 계정이면 DECREMENT에 더해 막힌 수량과 그 금액을 두 계정 사이에 옮깁니다.TRANSFER는 Maker와 Taker 주문이 모두 TRANSFER일 때만 적용되고, 그렇지 않으면 DECREMENT로 처리됩니다.original order quantity - executed quantity - prevented quantity = quantity available for further executionDECREMENT 예시에서는 수량 6의 매수 주문이 오더북에 있을 때 같은 계정이 수량 2의 매도 주문을 넣습니다. 두 주문 모두 Prevented Quantity가 2가 되고, 전량이 막힌 매도 주문은 EXPIRED_IN_MATCH 상태로 만료됩니다. 매수 주문은 체결 수량 0인 채로 오더북에 남고, 앞의 식대로 앞으로 체결될 수 있는 수량은 6 − 0 − 2 = 4입니다.마무리
다음 편 예고
References