Polymarket Bot Tutorial · 32개 중 17장

Polymarket order book imbalance를 단기 가격 신호로 활용하는 방법: bid-ask volume ratio, microprice 계산, signal half-life, 그리고 imbalance bots가 random execution보다 유리한 경우.

이 장에서 다루는 내용

Order-book imbalance는 limit order book에서 매수 측 depth와 매도 측 depth의 비율입니다. Polymarket에서는 실제로 유효하지만 수명이 짧은 predictive edge가 있으며, 보통 mid가 움직이기 5~30초 전에 나타납니다. 이 장에서는 이 신호를 계산하는 방식과, 어떤 조건에서 신호가 틀리는지 다룹니다.

  • order book imbalance란 무엇인가
  • microprice 계산
  • 방향성 신호로서의 imbalance
  • Polymarket에서의 signal half-life
  • imbalance 신호가 틀리는 경우
  • Code: 모든 WS tick마다 imbalance 계산

order book imbalance란 무엇인가

Order book imbalance는 limit order book에서 총 매수 측 depth와 총 매도 측 depth의 비율입니다. top-N level에서 계산하며(일반적으로 N=5), 아직 mid-price에 반영되지 않은 집단적인 trader pressure를 포착합니다.

Formula: imbalance = (Σ bid[i].price × bid[i].size for i in [0..N]) − (Σ ask[i].price × ask[i].size) / (Σ both). 범위는 -1에서 +1까지이며, 양수는 매수 압력이 더 크다는 뜻이고 음수는 매도 압력이 더 크다는 뜻입니다.

이 신호는 Polymarket에서 실증적으로 실제로 존재하지만 noisy합니다. 단 하나의 whale이 30~60초 동안 imbalance를 fake-print한 뒤 arb로 사라질 수 있습니다. 단독 트리거로 쓰기에는 위험하고, 여러 feature 중 하나로는 유용합니다.

microprice 계산

Microprice는 단순한 mid를 개선한 값으로, best bid와 best ask를 각각의 size로 가중한 weighted average입니다.

microprice = (best_bid × ask_size + best_ask × bid_size) / (bid_size + ask_size)

bid-side queue가 ask side보다 훨씬 크면 microprice는 ask에 더 가까워집니다. 직관은 이렇습니다. 기다리는 buyers가 더 많다는 것은 다음 trade가 ask를 끌어올릴 가능성이 더 높다는 뜻이므로, fair value는 ask 쪽에 더 가깝습니다.

Microprice는 실제 mid가 움직이기 5~30초 전의 leading indicator입니다. production bots는 naive mid 대신 take-profit decision의 reference price로 이것을 사용합니다.

방향성 신호로서의 imbalance

production observation에 따르면, news event가 동반되지 않은 상태에서 10초 안에 imbalance가 -0.3에서 +0.5로 바뀌면, 다음 30~60초 내에 mid가 1~2센트 올라갈 확률이 약 65%입니다.

실제로 edge는 있지만, fee를 제하면 작은 position size에서는 금방 사라집니다. 수익화하려면 bot이 움직임에서 fee를 뺀 만큼은 잡을 수 있을 정도로 충분히 size를 가져야 하지만, 동시에 book 자체를 움직일 정도로 크면 안 됩니다. Polymarket의 book은 대체로 얇아서 50 shares를 넘으면 시장에 영향을 주는 경우가 많습니다.

imbalance는 다른 feature와 함께 결합해야 합니다: trade velocity(거래가 많을수록 실제 신호), best-bid가 실제로 올라가는지 여부(단순히 depth만 바뀌는 것이 아님), market이 news-driven mode가 아닌지 여부입니다.

Polymarket에서의 signal half-life

imbalance 신호는 시간이 지나면 decay합니다. 우리 trader의 production data에 따르면, imbalance > 0.6이면 60초 내에 expected mid move는 1.2c이며, half-life는 약 30초입니다. 90초가 지나면 predictive value는 0이 됩니다.

bot 설계에 대한 시사점은 명확합니다. 빠르게 반응하거나, 아니면 건너뛰어야 합니다. 판단에 15초가 걸리는 bot은 order를 내기 전에 edge의 절반을 소모하는 셈입니다. imbalance strategy의 latency budget는 signal에서 FOK fired까지 5초 미만이어야 합니다.

half-life(1~2분)보다 오래 position을 보유하는 전략은 현재 신호가 아니라 다음 신호에 베팅하는 것입니다. 이 점을 명확히 해야 합니다. imbalance-driven position을 실수로 resolution까지 보유하지 않도록 하세요.

imbalance 신호가 틀리는 경우

다음 세 가지 조건 중 하나가 성립하면 신호가 오도됩니다.

  • News-driven move: imbalance가 아직 보지 못한 news의 결과일 때입니다. 이를 거슬러 거래하면 손해를 봅니다. 반대로 따라가는 것은 news arbitrage이며, 다른 전략입니다.
  • Whale spoofing: 큰 order를 넣었다가 빠르게 취소해 지속 시간 동안 fake imbalance를 만드는 경우입니다. 트리거를 걸기 전에 imbalance가 10초 이상 지속되는지 확인해 필터링하세요.
  • End-of-period rebalancing: 정보 때문이 아니라 inventory 이유로 market makers가 quotes를 철수하는 경우입니다. 이때 imbalance는 몇 분 후 MM이 다시 quote를 내면서 반전됩니다.

결합 필터는 다음과 같습니다: imbalance > threshold AND trade velocity > baseline AND 지난 5분 내 news event 없음. 각 필터를 단독으로 쓰면 false positive가 너무 많습니다.

Code: 모든 WS tick마다 imbalance 계산

Reference: WebSocket book updates를 구독하고, 모든 tick마다 imbalance를 다시 계산합니다.

def on_book_message(msg):
    bids = msg.get("bids", [])[:5]
    asks = msg.get("asks", [])[:5]
    bid_usd = sum(float(b["price"]) * float(b["size"]) for b in bids)
    ask_usd = sum(float(a["price"]) * float(a["size"]) for a in asks)
    total = bid_usd + ask_usd
    if total < 100: return  # illiquid
    imb = (bid_usd - ask_usd) / total
    state[msg["asset_id"]] = {
        "imb": imb,
        "best_bid": float(bids[0]["price"]) if bids else 0,
        "best_ask": float(asks[0]["price"]) if asks else 1,
        "ts": time.time()
    }
    # decision logic with cooldown + filters
    if imb > 0.6 and time.time() - last_fired.get(msg["asset_id"], 0) > 60:
        check_filters_and_maybe_fire(msg["asset_id"])

State는 토큰별로 관리합니다. Cooldown은 같은 신호에 대해 과도하게 발화되는 것을 막습니다. Filters(news check, trade velocity)는 실제 trade를 실행할지 여부를 결정합니다.

자주 묻는 질문

order book imbalance란 무엇인가요?
book 상단에서 bid volume과 ask volume의 비율입니다. bid 쏠림이 큰 book(터치 부근에서 seller보다 buyer가 더 많음)은 매우 단기적으로 상승 압력을 시사합니다. 이 신호는 crypto와 equities에서 잘 알려져 있으며, Polymarket에서는 유동성이 풍부한 market에서는 작동하지만 얇은 market에서는 사라집니다.
microprice는 어떻게 계산하나요?
microprice = (best_ask * bid_size + best_bid * ask_size) / (bid_size + ask_size)입니다. mid-price의 volume-weighted 버전으로, volume이 적은 쪽 - 즉 가장 먼저 "소진되는" 쪽 - 에 더 가깝게 기울어집니다. bot은 이를 imbalance를 반영한 fair-value 추정치로 사용합니다.
Polymarket에서 imbalance 신호의 half-life는 얼마인가요?
활발한 market에서는 5~30초입니다. 얇은 market에서는 더 길 수 있는데, 새 order가 imbalance를 압도하는 데 더 오래 걸리기 때문입니다. bot이 1초 이내에 반응하면 일부를 잡을 수 있습니다. 5초 이상 걸리면 보통 너무 늦습니다.
imbalance는 언제 틀리나요?
한쪽에 큰 단일 order가 놓여 있고 실제로 체결되기를 기다리는 경우입니다(큰 resting bid 하나, 다른 activity 없음). imbalance 자체는 실제지만 가격을 예측하지는 않습니다. 그저 의지가 있는 한 명의 buyer를 보여줄 뿐입니다. volume만 보지 말고 order 수를 세어 필터링하세요: N개의 order에 대한 imbalance가 1개의 order에 집중된 5배 volume보다 더 정보성이 높습니다.
order book imbalance만으로 단독 거래가 가능한가요?
대개 아닙니다. 단독 신호로는 약하고 arb 당합니다. 더 지속적인 edge를 위해 다른 신호(예: macroprice mean reversion, news flag, sports state)와 결합하세요. imbalance만으로는 fee 이후 random execution보다 성과가 떨어지는 경향이 있습니다.
Polymarket WS에서 imbalance를 계산할 수 있는 Python library가 있나요?
우리가 아는 한 바로 쓸 수 있는 off-the-shelf library는 없습니다. 몇십 줄의 코드면 충분합니다. py-clob-client를 통해 market book에 subscribe하고, 각 price_change event마다 top-of-book bid/ask size를 다시 계산한 뒤 imbalance metric을 내보내면 됩니다. 마지막 값을 cache하고 의미 있는 변화가 있을 때만 다시 trigger하세요.