폴리마켓 봇 튜토리얼 · 32장 중 11장
폴리마켓에서 NegRisk 다중 결과 시장이 작동하는 방식: 합계 1 메커니즘, CLOB 주문의 negRisk 플래그, NegRisk 이벤트 전반에서 YES 레그가 서로 대체될 수 없는 이유, 그리고 실행 모범 사례입니다.
이 장에서 다루는 내용
NegRisk 시장은 폴리마켓에서 상호 배타적인 다중 결과 이벤트를 다루기 위한 메커니즘입니다. 예를 들어 2024년 선거 후보, 프리미어 리그 우승팀, 토너먼트 대진이 여기에 해당합니다. 대부분의 봇은 첫 시도에서 이를 제대로 처리하지 못하는데, 주문을 낼 때 필요한 플래그가 빠져 있으면 주문이 조용히 사라지기 때문입니다. 이 장에서는 그 작동 원리와 실전 코드 경로를 다룹니다.
- NegRisk의 의미 (상호 배타적인 YES 레그)
- YES 합계가 1 USD 근처를 유지하는 이유
- 주문에 쓰이는 neg_risk 파라미터
- NegRisk 레그 간 헤지
- NegRisk 차익거래가 되는 경우와 안 되는 경우
- 정산 관련 예외 상황
- 코드: NegRisk 주문 넣기
NegRisk의 의미 (상호 배타적인 YES 레그)
NegRisk("negative risk"의 줄임말)는 상호 배타적인 여러 결과를 가진 이벤트를 다루기 위한 폴리마켓의 메커니즘입니다. 즉, YES로 정산될 수 있는 결과는 하나뿐입니다. 2024년 미국 대선이 NegRisk 이벤트의 한 예였으며, Trump-YES 포지션과 Harris-YES 포지션이 동시에 지급될 수는 없습니다.
내부적으로 하나의 NegRisk 이벤트는 하나의 상위 question_id와 N개의 하위 시장을 가지며, 각 하위 시장은 자체 YES/NO를 갖습니다. 거래소는 정산 시 모든 YES 레그의 합이 1이 되도록 강제합니다. 정확히 하나는 1.0으로, 나머지는 0.0으로 정산됩니다.
봇의 관점에서 각 결과의 YES 레그는 자체 토큰처럼 거래되며, 고유한 호가창과 가격을 가집니다. 주문에 넣는 negRisk 플래그는 아래에서 설명하듯이 거래를 NegRisk 전용 거래소 컨트랙트로 라우팅합니다. 이 플래그가 없으면 주문이 표준 CTF 거래소로 전송되어, 정산 시 제대로 처리되지 않고 조용히 실패합니다.
YES 합계가 1 USD 근처를 유지하는 이유
차익거래자들은 모든 NegRisk 레그의 YES 가격 합계를 끊임없이 약 1.0으로 유지합니다. 예를 들어 Trump-YES가 0.55, Harris-YES가 0.40이고 다른 경쟁 후보가 없다면, 부족한 0.05는 대략 "그 밖의 어떤 결과"에 대한 내재 확률입니다. 이 빠진 부분이 내재된 꼬리 확률을 넘어서면 차익거래 기회가 생깁니다. 모든 YES 레그를 비례적으로 매수해 합계가 1.0 미만이 되도록 만들고 그 차이를 고정하는 것입니다.
실전에서는 이 차익거래가 매우 경쟁적입니다. 유동성이 있는 이벤트에서 보이는 할인폭은 보통 1~2센트 수준이며, 시장이 열린 뒤 몇 분 안에 사라집니다. 또한 차익거래는 유동성에 묶여 있어서, 1천 달러 규모의 할인은 잡을 수 있어도 2만 달러 규모는 불가능할 수 있습니다.
대부분의 봇은 NegRisk 차익거래를 노리는 것이 아니라 개별 레그를 거래하며, 정확한 실행을 위해 negRisk 플래그를 반드시 지켜야 합니다.
주문에 쓰이는 neg_risk 파라미터
CLOB v2 SDK에서 주문을 넣을 때는 불리언 negRisk 값을 담은 flags 객체를 전달합니다. 이 값은 시장 유형과 일치해야 합니다.
// Node (CLOB v2)
await client.createAndPostOrder(
{ tokenID, price: 0.45, size: 10, side: Side.BUY },
{ tickSize: '0.01', negRisk: true }, // <-- TRUE for NegRisk
OrderType.FOK
);
이 플래그는 대부분의 실전 봇이 사용하는 데몬 op 시그니처의 파라미터이기도 합니다.
{ op: 'buy', tokenID, price, size, neg_risk: true, order_type: 'FOK' }
이 값의 원천은 Gamma API의 market.negRisk입니다. 반드시 이 값을 읽어야 하며, 하드코딩해서는 안 됩니다. BTC 상승/하락 시장은 negRisk: false(이진 시장)이고, 토너먼트 우승팀 시장은 negRisk: true(다중 결과 시장)입니다. 이 둘을 혼동하면 주문이 잘못된 거래소로 전송되어 정산 시 이체 실패가 발생합니다.
NegRisk 레그 간 헤지
Trump-YES를 0.50에 보유하고 있고 Trump의 패배에 대비해 헤지하고 싶다면, NegRisk 안에서 두 가지 방법이 있습니다.
경쟁하는 NO 레그를 매수합니다. 예를 들어 Harris-NO를 0.45에 매수합니다. 이는 Harris가 지는 경우에 지급되며, 여기에는 Trump가 이기는 경우도 포함됩니다. 다만 비대칭적입니다. Trump가 이기면 수익이 나지만, Trump가 제3의 후보에게 지면 가치가 없습니다.
경쟁하는 모든 YES 레그를 비례적으로 매수합니다. 포트폴리오가 NegRisk 레그 전반에서 완전히 균형을 이루면, 정확히 하나만 지급되기 때문에 노출이 헤지됩니다. 이것이 합성 현금 포지션입니다.
기대와 다르게 작동하는 헤지 수단은 기존 시장의 NO 레그입니다. Trump-NO는 다른 YES 레그와 상관관계가 있지만 완벽하지는 않습니다. 정산 결과가 "Other"인 경우 Trump-YES와 Trump-NO가 모두 0이 됩니다. NegRisk 레그는 순수한 이진 상품이 아닙니다.
NegRisk 차익거래가 되는 경우와 안 되는 경우
"합계가 1.0보다 작다"는 NegRisk 차익거래는 다음 세 조건이 모두 충족될 때 성립합니다.
- 모든 레그에 유동성이 있을 것: 매수해야 하는 각 레그에 원하는 가격 기준으로 최소 1천 달러의 깊이가 있어야 합니다. 차익거래는 여러 호가창을 동시에 쳐야 하므로, 한 레그라도 유동성이 부족하면 거래 전체가 깨집니다.
- 스프레드가 충분히 좁을 것: 모든 레그에 걸친 누적 스프레드 비용이 할인폭보다 작아야 합니다. 레그당 0.5센트 스프레드인 레그가 다섯 개면 비용은 2.5센트입니다. 할인폭이 1.5센트라면 차익거래는 손실입니다.
- 정산까지 보유할 수 있을 것: NegRisk 차익거래는 정산 차익거래입니다. 상위 이벤트가 정산될 때 지급을 받으며, 그 시점이 6개월 뒤라면 그동안 자본이 묶입니다.
대부분의 봇, 즉 95%에게 NegRisk 차익거래는 전략이 아닙니다. 나머지 전략은 개별 레그를 거래하며, negRisk 플래그는 주문이 올바른 곳으로 라우팅되도록 보장하기 위해서만 존재합니다.
정산 관련 예외 상황
NegRisk 포지션을 정산까지 보유할 때 주의해야 할 예외 상황이 두 가지 있습니다.
"None of the above" 결과: 일부 NegRisk 이벤트에는 명시적인 "Other" 또는 "None of the above" 레그가 있습니다. 실제 답이 이름 붙은 어떤 레그와도 일치하지 않으면 이 레그가 이깁니다. 명시적인 Other 레그를 모델링하지 않는 봇은 이를 흔히 비정상 사례로 처리해 지급을 놓칩니다.
분쟁이 걸린 정산: NegRisk 정산은 다른 시장과 마찬가지로 UMA를 거칩니다. 분쟁이 제기되면 시장이 24~72시간 동안 정산되지 않은 상태로 남을 수 있습니다. 이 기간 동안 프런트엔드는 "정산됨"으로 표시할 수 있지만 온체인 지급은 아직 설정되지 않았을 수 있습니다. 이를 확인하는 안전한 방법은 CTF 컨트랙트의 payoutNumerators를 읽는 것입니다.
코드: NegRisk 주문 넣기
NegRisk YES 레그 매수 주문을 넣는 전체 Node 예제입니다.
import { ClobClient, Side, OrderType } from "@polymarket/clob-client-v2";
import { Wallet } from "ethers";
const c = new ClobClient({
host: "https://clob.polymarket.com", chain: 137,
signer: new Wallet(process.env.PRIVATE_KEY),
creds: { key: K, secret: S, passphrase: P },
signatureType: 2,
funderAddress: process.env.POLY_FUNDER,
});
// market.negRisk === true (verified via Gamma earlier)
const resp = await c.createAndPostOrder(
{ tokenID: "<YES_TOKEN_ID>", price: 0.42, size: 25, side: Side.BUY },
{ tickSize: "0.01", negRisk: true },
OrderType.FOK
);
console.log(resp.status, resp.orderID);
새로 봇을 만드는 사람이 가장 흔히 저지르는 버그 하나는 NegRisk 시장에서 negRisk: true를 빠뜨리는 것입니다. 주문은 CLOB에서 수락되지만 정산이 실패합니다. 항상 Gamma에서 market.negRisk를 읽어 전달해야 하며, 질문 제목만 보고 추측해서는 안 됩니다.



