Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

트레이드 마크아웃 개념과 체결 품질 분석 방법

트레이드 마크아웃은 체결 이후 중간 가격의 변화를 측정하여 거래의 역선택 여부를 판단하는 지표입니다. 1초부터 5분까지의 기간별 마크아웃 곡선 분석을 통해 체결 품질을 평가하는 방법과 부호 규약의 원리를 상세히 설명합니다.

트레이드 마크아웃(trade markout)은 체결(fill) 이후 고정된 기간 동안 중간 가격(mid price)이 얼마나 변했는지를 체결 시점부터 측정하는 지표입니다. 수치가 양수라면 거래 이후 시장이 유리한 방향으로 움직였음을 의미합니다. 음수라면 시장이 불리하게 움직였음을 뜻하며, 트레이딩 데스크(trading desk)에서는 이를 역선택(adverse selection)이라고 부릅니다. 1초나 5분 등 어떤 기간(horizon)을 설정하느냐에 따라 이 지표가 답하는 질문의 성격이 달라집니다.

트레이드 마크아웃이란 무엇인가?

중간 가격, 즉 최우선 매수 호가(best bid)와 최우선 매도 호가(best offer)의 중간 지점인 NBBO(National Best Bid and Offer, 전국 최우선 호가)의 중간값에서 시작합니다. 마크아웃은 체결 후 선택한 기간의 중간 가격에서 기준 가격을 뺀 뒤, 거래 방향(side)에 따라 부호를 결정합니다.

**기간 h에서의 마크아웃 = 방향 * (t+h 시점의 중간 가격 - 기준 가격)**, 여기서 방향은 매수 시 +1, 매도 시 -1입니다.

부호 규약에서 혼동이 자주 발생합니다. 매수 후 중간 가격이 하락하면 마크아웃은 음수가 됩니다. 즉, 매수했는데 가격이 내려갔으므로 상대방(매도자)이 더 유리한 거래를 한 셈입니다. 이것이 바로 숫자로 표현된 역선택입니다. 반대로 매도 후 중간 가격이 하락하면 부호가 반전되어 마크아웃은 양수가 됩니다. 모든 체결에는 양면이 존재하며, 한쪽의 마크아웃은 다른 쪽의 마크아웃에 부호를 반대로 한 것과 같습니다.

세 가지 기준 가격

어떤 기준 가격을 빼느냐에 따라 마크아웃이 측정하는 대상이 달라집니다.

  • 체결 가격(fill price): 마크아웃은 금액이 됩니다. 즉, 지불한 가격 대비 t+h 시점 포지션의 가치를 나타냅니다. 여기에는 지불하거나 수취한 스프레드(spread)가 포함됩니다.
  • 체결 시점의 중간 가격: 스프레드를 제거하고 체결 이후 시장이 어떻게 움직였는지만을 분리해냅니다. 역선택을 가장 명확하게 파악할 수 있는 방법입니다.
  • 거래한 방향의 호가: 체결된 호가(touch)를 기준으로 측정하며, 실제로 이용 가능한 가격 대비 체결이 어떠했는지를 보여줍니다.

실행 품질(execution quality) 분석에서는 역선택을 파악하기 위해 두 번째 방식을, 손익(P&L)을 파악하기 위해 첫 번째 방식을 주로 사용합니다. 아래 첫 번째 패널에는 일련의 체결 건에 대해 두 방식이 모두 나타나 있습니다.

마크아웃 곡선과 그 형태가 의미하는 것

여러 기간에 걸쳐 동일한 통계를 산출하면 곡선이 그려집니다. 이 곡선의 형태는 단일 숫자보다 더 많은 정보를 담고 있습니다.

  • 첫 1초 이내에 급격히 하락한 뒤 낮은 수준을 유지하는 곡선은 유동성 공급자가 가격을 선점당하는(getting picked off) 흐름을 나타냅니다. 체결 직후 가격이 불리하게 움직인 것입니다.
  • 수 분 동안 계속 하락하는 곡선은 가격 방향에 대한 실질적인 정보를 담고 있는 흐름을 나타냅니다.
  • 0 근처에서 평탄한 곡선은 무해한 흐름을 의미합니다. 5분이 지나도 중간 가격이 체결 당시와 비슷하기 때문입니다.
  • 음수로 시작했다가 0으로 다시 올라오는 곡선은 리베이트(rebate)를 수취하는 수동적 시장 조성자(passive maker)가 선호하는 형태입니다. 초기 하락은 스프레드와 즉각적인 시장 충격이며, 회복은 시장이 제자리를 찾아가는 과정입니다.

기간 선택 자체가 무엇을 측정할지에 대한 결정입니다. 1초 미만은 주로 지연 시간(latency)과 큐 포지션(queue position)을 읽어냅니다. 1분으로 늘리면 흐름 속에 담긴 단기 정보를 읽어냅니다. 5분이 넘어가면 일반적인 시장 변동(drift)이 체결 자체의 효과를 압도하기 시작합니다.

실제 마크아웃 곡선, 체결별 분석

아래 패널은 2026년 6월 10일 정규장 중 INTC의 모든 체결 건을 대상으로 합니다. 각 체결을 체결 직전 1초의 마지막 호가 대비 매수 주도 또는 매도 주도로 분류하고, 5 기간의 부호화된 마크아웃을 평균했습니다. 동일한 체결 건에 대해 체결 시점 중간 가격 기준과 체결 가격 기준, 두 가지 시리즈를 산출했습니다.

조회INTC 마크아웃 곡선(markout curve), 2026년 6월 10일, 두 가지 기준점 대비 측정
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
    mid_by_second AS
    (
        SELECT
            dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
            argMax((toFloat64(bid_price) + toFloat64(ask_price)) / 2, sip_timestamp)    AS mid
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'INTC'
          AND sip_timestamp >= toDateTime('2026-06-10 13:30:00', 'UTC')
          AND sip_timestamp <  toDateTime('2026-06-10 20:00:00', 'UTC')
          AND bid_price > 0
          AND ask_price > bid_price
        GROUP BY sec
    ),
    signed_fills AS
    (
        SELECT
            t.sec        AS sec,
            t.fill_price AS fill_price,
            q.mid        AS ref_mid,
            if(t.fill_price > q.mid, 1, -1) AS side
        FROM
        (
            SELECT
                dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
                sec - 1          AS ref_sec,
                toFloat64(price) AS fill_price
            FROM global_markets.stocks_trades
            WHERE ticker = 'INTC'
              AND sip_timestamp >= toDateTime('2026-06-10 13:30:01', 'UTC')
              AND sip_timestamp <  toDateTime('2026-06-10 19:55:00', 'UTC')
              AND price > 0
              AND size > 0
        ) AS t
        INNER JOIN mid_by_second AS q ON q.sec = t.ref_sec
        WHERE t.fill_price != q.mid
    )
SELECT
    multiIf(f.horizon_s < 60,
            concat(toString(f.horizon_s), ' sec'),
            concat(toString(intDiv(f.horizon_s, 60)), ' min'))              AS horizon,
    round(avg(f.side * (fut.mid - f.ref_mid) / f.ref_mid) * 10000, 3)       AS mid_basis_bps,
    round(avg(f.side * (fut.mid - f.fill_price) / f.fill_price) * 10000, 3) AS fill_basis_bps,
    count()                                                                 AS fill_count
FROM
(
    SELECT
        sec,
        fill_price,
        ref_mid,
        side,
        horizon_s,
        sec + horizon_s AS future_sec
    FROM signed_fills
    ARRAY JOIN [1, 5, 15, 60, 300] AS horizon_s
) AS f
INNER JOIN mid_by_second AS fut ON fut.sec = f.future_sec
GROUP BY f.horizon_s
ORDER BY f.horizon_s
Run this yourself

900593개의 분류된 체결 건에 걸쳐, 중간 가격 기준 시리즈는 1 sec 시점에 3.068 베이시스 포인트(bp), 5 min 시점에 2.977 베이시스 포인트를 기록합니다. 베이시스 포인트는 가격의 1만 분의 1을 의미하므로, 한 번의 체결은 반올림 오차 수준의 움직임을 보이지만 백만 주 단위에서는 실질적인 금액 차이를 만듭니다.

체결 가격 기준 시리즈는 -0.488 베이시스 포인트에서 시작합니다. 가장 짧은 기간에서 두 시리즈 사이의 거리는 유효 반 스프레드(effective half spread)입니다. 즉, 체결이 중간 가격에서 얼마나 떨어져 있었는지를 나타냅니다. 유동성 공급자(taker)는 진입 시 이를 지불하고, 시장 조성자(maker)는 이를 수취합니다. 첫 번째 기간 이후의 모든 움직임은 시장의 변동입니다.

체결 규모와 곡선의 형태

역선택은 모든 체결 건에 균등하게 발생하지 않습니다. 같은 세션을 체결 규모별로 나누어 소규모 체결과 1,000주 이상의 블록(block) 거래를 동일한 축에 놓아 비교합니다.

조회동일 곡선의 체결 규모별 분류: 소규모 체결 대 대량 주문(block)
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
    mid_by_second AS
    (
        SELECT
            dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
            argMax((toFloat64(bid_price) + toFloat64(ask_price)) / 2, sip_timestamp)    AS mid
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'INTC'
          AND sip_timestamp >= toDateTime('2026-06-10 13:30:00', 'UTC')
          AND sip_timestamp <  toDateTime('2026-06-10 20:00:00', 'UTC')
          AND bid_price > 0
          AND ask_price > bid_price
        GROUP BY sec
    ),
    signed_fills AS
    (
        SELECT
            t.sec        AS sec,
            t.fill_size  AS fill_size,
            q.mid        AS ref_mid,
            if(t.fill_price > q.mid, 1, -1) AS side
        FROM
        (
            SELECT
                dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
                sec - 1          AS ref_sec,
                toFloat64(price) AS fill_price,
                size             AS fill_size
            FROM global_markets.stocks_trades
            WHERE ticker = 'INTC'
              AND sip_timestamp >= toDateTime('2026-06-10 13:30:01', 'UTC')
              AND sip_timestamp <  toDateTime('2026-06-10 19:55:00', 'UTC')
              AND price > 0
              AND size > 0
        ) AS t
        INNER JOIN mid_by_second AS q ON q.sec = t.ref_sec
        WHERE t.fill_price != q.mid
    )
SELECT
    multiIf(f.horizon_s < 60,
            concat(toString(f.horizon_s), ' sec'),
            concat(toString(intDiv(f.horizon_s, 60)), ' min'))                                   AS horizon,
    round(avgIf(f.side * (fut.mid - f.ref_mid) / f.ref_mid, f.fill_size < 1000) * 10000, 3)       AS small_fill_bps,
    round(avgIf(f.side * (fut.mid - f.ref_mid) / f.ref_mid, f.fill_size >= 1000) * 10000, 3)      AS block_fill_bps,
    countIf(f.fill_size >= 1000)                                                                 AS block_fill_count
FROM
(
    SELECT
        sec,
        fill_size,
        ref_mid,
        side,
        horizon_s,
        sec + horizon_s AS future_sec
    FROM signed_fills
    ARRAY JOIN [1, 5, 15, 60, 300] AS horizon_s
) AS f
INNER JOIN mid_by_second AS fut ON fut.sec = f.future_sec
GROUP BY f.horizon_s
HAVING countIf(f.fill_size < 1000) > 0
   AND countIf(f.fill_size >= 1000) > 0
ORDER BY f.horizon_s
Run this yourself

5 min 기간에서 1,000주 미만의 체결은 평균 2.983 베이시스 포인트인 반면, 1,000주 이상의 11588 체결은 평균 2.495 베이시스 포인트를 기록합니다. 동일한 총 거래량을 이 두 그룹에 대해 체결하더라도 하루를 마감하는 결과는 같지 않습니다.

마크아웃이 실행 보고서의 스프레드 수치가 되는 과정

마크아웃은 표준 스프레드 분해(spread decomposition)에서 빠진 항목입니다. 유효 스프레드(effective spread)는 유동성 공급자가 실제로 지불한 비용으로, 중간 가격에서 체결 가격까지의 부호화된 거리를 두 배 한 값입니다. 이를 둘로 나눕니다. 하나는 실현 스프레드(realized spread)로, 시장이 움직인 후 시장 조성자가 보유하는 수익입니다. 다른 하나는 역선택으로, 중간 가격 기준 마크아웃에 2를 곱한 값입니다. 이 항등식은 정확합니다: 유효 스프레드 = 실현 스프레드 + 역선택.

조회실효 스프레드(effective spread)의 실현 스프레드 및 역선택(adverse selection) 분해, 30분 단위
모든 수치 뒤에 숨겨진 정확한 SQL
WITH
    mid_by_second AS
    (
        SELECT
            dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
            argMax((toFloat64(bid_price) + toFloat64(ask_price)) / 2, sip_timestamp)    AS mid
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'INTC'
          AND sip_timestamp >= toDateTime('2026-06-10 13:30:00', 'UTC')
          AND sip_timestamp <  toDateTime('2026-06-10 20:00:00', 'UTC')
          AND bid_price > 0
          AND ask_price > bid_price
        GROUP BY sec
    ),
    signed_fills AS
    (
        SELECT
            t.sec + 60   AS future_sec,
            t.et_time    AS et_time,
            t.fill_price AS fill_price,
            q.mid        AS ref_mid,
            if(t.fill_price > q.mid, 1, -1) AS side
        FROM
        (
            SELECT
                dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
                sec - 1          AS ref_sec,
                toFloat64(price) AS fill_price,
                formatDateTime(toStartOfInterval(toTimeZone(sip_timestamp, 'America/New_York'), toIntervalMinute(30)), '%H:%i') AS et_time
            FROM global_markets.stocks_trades
            WHERE ticker = 'INTC'
              AND sip_timestamp >= toDateTime('2026-06-10 13:30:01', 'UTC')
              AND sip_timestamp <  toDateTime('2026-06-10 19:55:00', 'UTC')
              AND price > 0
              AND size > 0
        ) AS t
        INNER JOIN mid_by_second AS q ON q.sec = t.ref_sec
        WHERE t.fill_price != q.mid
    )
SELECT
    f.et_time                                                                  AS et_time,
    round(avg(2 * f.side * (f.fill_price - f.ref_mid) / f.ref_mid) * 10000, 3) AS effective_spread_bps,
    round(avg(2 * f.side * (f.fill_price - fut.mid) / f.ref_mid) * 10000, 3)   AS realized_spread_bps,
    round(avg(2 * f.side * (fut.mid - f.ref_mid) / f.ref_mid) * 10000, 3)      AS adverse_selection_bps,
    count()                                                                    AS fill_count
FROM signed_fills AS f
INNER JOIN mid_by_second AS fut ON fut.sec = f.future_sec
GROUP BY f.et_time
ORDER BY f.et_time
Run this yourself

동부 표준시 09:30에 시작하는 30분 동안 유효 스프레드는 평균 10.651 베이시스 포인트이며, 이 중 8.313은 60초 기간의 역선택 항목이고 2.338는 시장 조성자가 보유하는 수익입니다. 이 패널은 13개의 30분 단위 구간을 다룹니다.

이러한 구성 요소들은 Rule 605 및 606 실행 보고서에서 거래소별로 요약되는 수치이며, 서브 페니 룰(sub-penny rule) 및 가격 개선(price improvement)의 근거가 되는 산술적 지표입니다. 리베이트는 주당 고정된 크레딧입니다. 역선택은 호가가 실제로 체결되는 건에 대해 발생하는 주당 변동 비용입니다. 헤드라인 요율표가 아닌 측정된 곡선을 통해 두 항목을 비교하는 것이 메이커-테이커 수수료 및 리베이트를 검증하는 방법이며, 이는 시장 조성자가 손실을 보는 이유의 메커니즘과 함께 고려됩니다.

수치 산출 방식

모든 패널은 2026년 6월 10일 정규장(09:30~16:00 동부 표준시)의 INTC 호가 및 체결 데이터를 사용합니다. 호가는 초당 하나의 중간 가격으로 압축되며, 해당 초 내에 마지막으로 기록된 값을 사용합니다. 체결은 이전 초의 중간 가격보다 높으면 매수 주도, 낮으면 매도 주도로 분류하는 표준 호가 규칙을 따릅니다. 중간 가격에서 정확히 체결된 건은 제외하므로, 중간 가격 체결은 표본에서 빠집니다. 이전 초의 중간 가격을 기준으로 삼아 미래 정보를 참조하지 않도록 했습니다. 기간은 체결이 발생한 초부터 계산된 전체 초 단위이며, 대상 초에 호가가 존재할 때만 체결 건이 기간에 포함됩니다.

FAQ

트레이딩에서 마크아웃이란 무엇인가?

마크아웃은 체결 이후 고정된 기간 동안 중간 가격이 변화한 정도를 부호화한 값입니다. 양수는 체결 이후 시장이 거래 방향으로 움직였음을 의미합니다. 음수는 반대 방향으로 움직였음을 의미하며, 이것이 바로 역선택의 정의입니다.

마크아웃이 음수이면 항상 나쁜 체결인가?

아닙니다. 시장가 주문(marketable order)에서 1초 마크아웃이 음수인 것은 대부분 주문자가 지불한 스프레드이며, 이는 즉시 체결을 얻기 위한 비용입니다. 더 중요한 것은 곡선이 더 긴 기간에도 계속 하락하는지, 아니면 평탄해지는지 여부입니다.

마크아웃과 슬리피지(slippage)의 차이는 무엇인가?

슬리피지는 체결 가격을 결정 가격이나 도착 가격과 비교하므로 주문 전체의 경로를 평가합니다. 마크아웃은 체결 가격을 체결 이후 특정 기간의 중간 가격과 비교하므로 체결 이후에 일어난 일을 평가합니다. 슬리피지는 부모 주문(parent order)을 점수화하고, 마크아웃은 개별 체결을 점수화합니다.

마크아웃은 어떤 기간을 사용해야 하는가?

체결의 목적에 따라 다릅니다. 1초 미만의 기간은 지연 시간에 민감한 실행을 평가하며, 1분은 유동성이 높은 종목의 역선택을 평가하는 일반적인 기본값입니다. 수치 옆에 기간을 명시해야만 서로 다른 두 마크아웃을 비교할 수 있습니다.

마크아웃은 유효 스프레드와 어떤 관계인가?

유효 스프레드는 실현 스프레드와 역선택 항목으로 나뉘며, 역선택 항목은 중간 가격 기준 마크아웃에 2를 곱한 값입니다. 기간을 변경하면 두 항목 간의 분할 비율도 함께 변하므로, 기간이 명시되지 않은 스프레드 통계는 불완전합니다.


여기에 포함된 모든 패널은 이를 생성한 SQL과 함께 제공됩니다. Strasmore 터미널에서 패널을 열고 티커나 날짜를 변경하면 동일한 마크아웃 곡선이 처음부터 다시 생성됩니다.