주요 콘텐츠로 건너뛰기
AI 튜토리얼

jev-trader로 블록 단위 Monad 마켓 메이커 구축 및 실행

jev-trader를 설치하고 드라이 런 모드에서 안전하게 실행하는 방법, Jev 모델 연결, HTTP 및 SSE 피드 확인, 호가와 체결 해석, 그리고 약 300ms인 Monad의 블록 시간 안에 트레이딩 루프를 유지하는 지연 시간 최적화 방법을 알아보세요.

jev-trader로 블록 단위 Monad 마켓 메이커 구축 및 실행

jev-trader의 기능

jev-trader는 Monad의 Kuru MON-USDC 오더북을 대상으로 블록 단위로 거래하는 봇입니다. 새로운 블록이 생성될 때마다 실시간 오더북을 읽고, 모델에 매수 또는 매도 여부를 묻고, 선택한 방향에 포스트온리 지정가 주문 의도를 생성합니다.

주문 가격은 현재 최우선 호가 사이의 간격 안쪽으로 QUOTE_INSIDE_TICKS틱만큼 설정됩니다. 스프레드가 너무 좁으면 가격은 최우선 호가로 제한됩니다. 봇은 이전에 대기 중이던 주문을 취소하고 하나의 Kuru batchUpdate에서 대체 주문을 게시합니다.

포스트온리 주문이므로 즉시 체결되지 않고 오더북에 대기하도록 설계되었습니다. 다른 참여자가 해당 가격을 교차하는 테이커 주문을 제출하면 체결됩니다. 이를 통해 전략은 스프레드를 지불하는 대신 수익으로 얻을 수 있지만, 체결, 재고, 가스 및 시장 위험은 여전히 존재합니다.

주요 기능

  • Monad 블록당 한 번의 결정: 봇은 약 300ms마다 최신 블록을 처리합니다.
  • 실시간 Kuru 오더북 데이터: 사용자 지정 리더가 getL2Book을 디코딩하고 AMM 볼트를 오더북에 병합합니다.
  • 두 가지 모델 옵션: 기본 모의 모델은 모멘텀 휴리스틱을 사용하며, MODEL=jev를 설정하면 TypeSafe Jev 모델이 활성화됩니다.
  • 안전한 드라이런 동작: PRIVATE_KEY가 없으면 봇은 실제 오더북과 실제 시장 체결 내역을 사용하지만 자체 주문과 체결은 시뮬레이션합니다.
  • 포스트온리 주문 교체: 업데이트할 때마다 알려진 대기 주문을 취소하고 하나의 배치에서 새로운 포스트온리 주문을 제출합니다.
  • 포지션 및 손익 추적: 이벤트에 포지션, 실현 및 미실현 결과, 가스 비용, 체결 횟수 및 기타 합계가 보고됩니다.
  • HTTP 및 SSE 모니터링: 소형 Bun 서버가 최신 스냅샷, 최대 1,000개의 과거 블록 이벤트 및 실시간 이벤트 스트림을 제공합니다.
  • 비동기 확인: 실시간 트랜잭션은 주요 경로에서 제출하고, 영수증 및 관련 확인 작업은 나중에 처리합니다.

프로젝트 아키텍처

소스 트리는 블록 수집, 시장 접근, 모델 평가, 거래 로직 및 모니터링을 분리합니다.

  • src/config.ts는 환경 설정을 불러옵니다.
  • src/chain.ts는 WebSocket newHeads 업데이트를 수신하고, 폴링을 보조 수단으로 사용하며, 가장 최신 블록만 유지합니다.
  • src/book.ts는 한 번의 eth_call로 오더북을 읽고 디코딩합니다.
  • src/market.ts는 Kuru 오더북 조회, 마진 예치, 로컬 논스, 배치 업데이트 및 비동기 확인을 처리합니다.
  • src/model.ts는 모델 인터페이스를 정의하고 JevModel과 MockModel을 제공합니다.
  • src/trader.ts는 거래 루프를 실행하고 포지션과 손익을 추적합니다.
  • src/server.ts는 Bun.serve를 사용해 스냅샷, 기록 및 Server-Sent Events를 제공합니다.

설치 및 설정

1. 환경 파일 준비

저장소 디렉터리에서 예제 설정을 복사합니다.

cp .env.example .env

애플리케이션을 시작하기 전에 생성된 .env 파일을 검토하세요. 가장 중요한 동작은 PRIVATE_KEY의 설정 여부에 따라 결정됩니다.

첫 실행에서는 PRIVATE_KEY를 설정하지 마세요. 애플리케이션은 자체 주문을 브로드캐스트하지 않고 실제 시장 데이터를 대상으로 드라이런을 수행합니다.

2. 의존성 설치

이 프로젝트는 의존성 관리와 실행에 Bun을 사용합니다.

bun install

3. 트레이더 시작

bun run start

기본 설정에서는 모의 모멘텀 모델이 추론 대체 역할을 합니다. 그러면 서버를 통해 트레이더 상태와 이벤트 스트림에 접근할 수 있습니다.

결정 모델 선택

기본 모의 모델 사용

기본 mock 모델은 Jev를 호출하지 않고 전체 파이프라인을 테스트하는 데 적합합니다. 모멘텀 휴리스틱을 사용해 결정하며, 오더북 조회, 시뮬레이션 주문, 체결, 회계 처리 및 이벤트 스트리밍을 검증할 수 있습니다.

Jev 모델 활성화

Jev를 사용하려면 .env에 다음 값을 설정하세요.

MODEL=jev
TYPESAFE_AI_API_KEY=your_api_key

각 블록마다 선택한 모델에 HORIZON_BLOCKS 동안 예상되는 움직임을 묻습니다. 기본값은 100블록이며, 프로젝트 README에 따르면 약 30초입니다.

일반적인 결정은 buy 또는 sell 중 하나입니다. hold 결과는 세 번째 모델 전략을 의미하지 않습니다. 모델이 현재 블록의 마감 시간을 놓쳤을 때만 나타납니다. 이 경우 decision.late는 true가 되고 해당 블록에는 주문이 게시되지 않습니다.

드라이런 모드에서 안전하게 실행

PRIVATE_KEY가 설정되지 않으면 jev-trader는 실시간 트랜잭션을 제출하지 않습니다. 그래도 실제 Kuru 오더북을 읽고 실제 모델 결정을 생성합니다.

시뮬레이션된 호가는 status: "sim" 상태를 가집니다. 한 블록 동안 대기하며, 실제 시장 체결 내역이 가격을 교차할 때만 체결된 것으로 간주됩니다. 그 결과로 생성되는 체결에는 simulated: true가 설정됩니다.

이 모드는 다음을 확인하는 데 유용합니다.

  • 오더북 업데이트가 안정적으로 도착하는지 여부.
  • 모델 지연 시간이 블록 예산 내에 유지되는지 여부.
  • 호가가 체결될 예상 빈도.
  • 시뮬레이션 규칙에 따라 포지션과 손익이 어떻게 변하는지 여부.
  • 대시보드 또는 다른 SSE 클라이언트가 모든 이벤트 유형을 올바르게 처리하는지 여부.

HTTP 엔드포인트 확인

모의 모델을 사용하는 배포된 드라이런 인스턴스는 https://jev-trader-production.up.railway.app에서 이용할 수 있습니다.

최신 스냅샷 확인

루트 엔드포인트는 활성 모델, 지갑, 드라이런 상태 및 가장 최근 블록 이벤트를 보고합니다:

curl https://jev-trader-production.up.railway.app/

최근 기록 확인

기록 엔드포인트는 최근 1,000개의 블록 이벤트를 반환합니다:

curl https://jev-trader-production.up.railway.app/history

실시간 SSE 스트림 구독

curl -N을 사용하면 연결을 열린 상태로 유지하고 이벤트가 도착하는 즉시 표시할 수 있습니다:

curl -N https://jev-trader-production.up.railway.app/events

스트림은 클라이언트가 연결될 때 snapshot 이벤트를 보내고, 이후 처리된 각 블록마다 하나의 block 이벤트를 보냅니다. 실시간 트랜잭션 영수증은 quote 업데이트를 생성하며, 감지된 체결은 fill 이벤트를 생성합니다.

블록 이벤트 이해하기

블록 이벤트에는 시장 상태, 모델 결정, 새로운 호가 의도, 대기 중인 유동성, 포지션 정보 및 누적 합계가 포함됩니다. 축약된 예시는 다음과 같습니다:

{
  "block": 105488269,
  "mid": 0.022636,
  "bestBid": 0.022628,
  "bestAsk": 0.022644,
  "spreadBps": 7.07,
  "decision": {
    "action": "buy",
    "probabilities": { "buy": 0.77, "sell": 0.23, "hold": 0 },
    "upIn10": 0.77,
    "latencyMs": 81,
    "late": false
  },
  "quote": {
    "side": "buy",
    "price": 0.022629,
    "size": 200,
    "status": "sent",
    "capped": false
  }
}
  • mid, bestBid, bestAsk는 관측된 시장을 설명합니다.
  • spreadBps는 현재 스프레드를 베이시스 포인트로 나타냅니다.
  • decision.action은 모델이 선택한 방향입니다.
  • decision.probabilities는 모델의 매수 및 매도 확률을 유지합니다.
  • upIn10은 매수 확률과 같습니다.
  • decision.latencyMs는 모델 평가 지연 시간을 보고합니다.
  • quote는 해당 블록에서 시도한 주문을 설명합니다.
  • quote.cancel은 취소 대상으로 포함된 기존 대기 주문을 식별합니다.
  • resting은 업데이트 후 봇이 파악한 매수 및 매도 잔량을 기록합니다.
  • position은 방향, 수량, 진입 가격 및 미실현 결과를 보고합니다.
  • totals는 블록, 결정, 호가, 체결, 되돌림, 지연 블록, 모델 비용, 가스 및 손익을 누적합니다.

포지션 및 자금 한도

포지션 한도로 인해 요청한 방향이 제한되거나 실시간 증거금이 부족한 경우, 호가는 반대 방향으로 제출됩니다. 이 경우 quote.capped는 true입니다. 원래 모델 확률은 변경되지 않으므로, 소비자는 모델의 판단과 실제로 제출된 호가 방향을 구분할 수 있습니다.

실시간 호가 확인 이해하기

실시간 전송은 영수증을 기다리지 않도록 실행 후 결과를 기다리지 않고 처리됩니다. 따라서 초기 블록 이벤트에는 status: "sent"인 의도가 포함됩니다.

보고되는 gasMon은 가스 한도에 마지막으로 확인된 기본 수수료와 우선 수수료를 더한 값을 곱해 계산합니다. 프로젝트 문서에 따르면 Monad는 가스 한도를 청구하므로, 트랜잭션이 성공하거나 되돌려지더라도 이 값을 실제 비용으로 간주합니다.

일반적으로 영수증은 한두 블록 후 도착하며 별도의 호가 이벤트를 생성합니다:

event: quote
data: {
  "block": 105488269,
  "quote": {
    "status": "placed",
    "orderId": 100295812,
    "gasMon": 0.0357
  }
}

실시간 호가는 다음 상태 중 하나로 진행될 수 있습니다:

  • placed: 트랜잭션이 성공했으며 주문 ID가 포함됩니다.
  • reverted: 포함되기 전에 주문서가 제출된 가격을 지나 이동했거나, 요청된 취소가 이미 체결된 주문을 대상으로 했습니다.
  • lost: 10개 블록 후에도 영수증을 찾지 못했습니다.

체결 이벤트 이해하기

체결은 봇 자체의 주문 제출 트랜잭션에서 발생하지 않습니다. 다른 트레이더의 테이커 주문이 대기 주문을 체결할 때 발생합니다. 봇은 시장 정보를 모델에 제공하는 동일한 eth_getLogs 폴링 프로세스를 통해 해당 Trade 로그를 감지합니다.

event: fill
data: {
  "block": 105488271,
  "fill": {
    "side": "buy",
    "size": 200,
    "price": 0.022629,
    "txHash": "0x…",
    "orderId": 100295812,
    "simulated": false
  }
}

체결의 txHash는 테이커 트랜잭션에 속합니다. 체결이 처리되면 스트림에는 업데이트된 포지션 데이터, 실현 손익 및 누적 체결 합계도 반영됩니다.

실시간 거래로 전환하기

PRIVATE_KEY를 설정하면 애플리케이션이 드라이런 동작에서 실시간 트랜잭션 제출로 전환됩니다. 전환하기 전에 환경 구성, 증거금, 포지션 한도, 거래 규모, 호가 제출, 수수료 한도 및 RPC 안정성을 확인하세요.

실시간 모드는 실제 post-only Kuru 주문을 제출하며 실제 가스 비용이 발생합니다. 자금이 있는 키를 활성화하기 전에 전체 워크플로를 먼저 드라이런 모드에서 테스트하고 보수적인 한도를 사용하세요.

프로젝트에서 설명하는 주요 전략 관련 설정은 다음과 같습니다:

  • 주문 크기를 위한 TRADE_SIZE_MON.
  • 터치 가격 대비 제출 위치를 위한 QUOTE_INSIDE_TICKS.
  • 모델의 예측 지평을 위한 HORIZON_BLOCKS.
  • 타입 2 트랜잭션 수수료 한도를 위한 MAX_FEE_GWEI.
  • 주문서 조회를 위한 READ_RPC_URL.
  • 트랜잭션 제출을 위한 RPC_URL.

봇이 300 ms 예산을 충족하는 방식

핫 패스는 정확히 두 번의 RPC 왕복을 기준으로 설계되었습니다:

  1. 하나의 eth_call이 READ_RPC_URL을 통해 현재 주문서를 읽습니다.
  2. 하나의 eth_sendRawTransaction이 서명된 업데이트를 RPC_URL을 통해 제출하고, 수락되면 반환됩니다.

이 루프는 비용이 많이 들 수 있는 여러 작업을 의도적으로 피합니다.

  • 블록마다 eth_estimateGas를 호출하지 않습니다. 가스 한도는 하드코딩하거나 시작 시 한 번 계산합니다.
  • 트랜잭션이 제안될 때까지 기다리는 eth_sendRawTransactionSync를 사용하지 않습니다.
  • 핫 패스에서 가스 가격을 조회하지 않습니다. 트랜잭션은 우선 수수료 2 gwei와 MAX_FEE_GWEI 상한을 사용하는 정적 타입 2 수수료 설정을 사용합니다.
  • 의사 결정 루프에서 영수증을 기다리지 않습니다.
  • 주문을 지연시킬 수 있는 볼트 또는 수수료 확인을 인라인으로 수행하지 않습니다.

README에는 드라이런 목업 모델 벤치마크 결과가 북 조회 p50 약 18ms, 전체 루프 p50 100ms이며, 이 중 약 80ms가 목업 추론 대체 구현에 소요된다고 나와 있습니다.

검증 및 벤치마킹

커스텀 북 리더 벤치마크

포함된 벤치마크를 실행하여 정확성과 지연 시간 측면에서 커스텀 리더와 SDK를 비교합니다.

bun run scripts/bench-read.ts

트랜잭션 인코딩 오프라인 검증

드라이 인코딩 스크립트는 브로드캐스트하지 않고 매수 및 매도 트랜잭션에 서명한 다음, 생성된 calldata가 SDK와 일치하는지 확인합니다.

bun run scripts/dry-encode.ts

성능 중심의 마켓 모듈이 직접 인코딩한 batchUpdate calldata를 사용하므로, 라이브 모드를 활성화하기 전에 특히 유용합니다.

고급 운영 팁

  • 늦은 의사 결정 확인: decision.late와 totals.lateBlocks를 추적합니다. 모델 응답이 늦으면 해당 블록에 주문이 게시되지 않습니다.
  • 의도와 확인을 구분: status: "sent"를 주문이 체결되었다는 의미로 취급하지 마세요. 이후 placed, reverted 또는 lost가 포함된 quote 이벤트를 기다립니다.
  • 상한이 적용된 호가 추적: capped가 true이면 제출된 매수·매도 방향을 변경되지 않은 모델 확률과 비교하여 실행 결과가 신호와 달라진 이유를 파악합니다.
  • SSE 스냅샷 사용: 새로운 모니터링 클라이언트는 첫 번째 snapshot 이벤트로 초기화한 다음, 이어지는 블록, 호가 및 체결 업데이트를 적용해야 합니다.
  • 두 RPC 경로 모두 측정: 읽기가 느리면 의사 결정이 지연되고, 제출이 느리면 시장이 움직이기 전에 호가가 반영될 가능성이 낮아집니다.
  • 청구되는 가스 한도 고려: 되돌려진 트랜잭션도 상당한 비용이 발생할 수 있으므로, 체결만 평가하지 말고 gasMon, gasUsd 및 되돌림 총계를 모니터링합니다.
  • 드라이런 출력으로 검증: 시뮬레이션 체결은 실제 체결가가 주문 가격을 교차하는 상황에 따라 달라집니다. 따라서 드라이런 모드는 즉시 체결을 가정하는 것보다 현실적이지만, 실제 대기열 위치 및 실행 결과와는 여전히 다릅니다.

결론

jev-trader는 실시간 Kuru 북 리더, 목업 또는 Jev 의사 결정 모델, 최적화된 Monad 트랜잭션 제출, 포지션 회계 및 SSE 모니터링 서비스를 결합합니다. PRIVATE_KEY 없이 시작하고, 스냅샷과 이벤트 스트림을 확인하며, 핵심 경로를 벤치마크하고, 라이브 실행을 고려하기 전에 인코딩을 검증하세요. 프로덕션에서는 확인 상태, 늦은 블록, 상한 적용, 되돌림, 재고, 손익 및 가스 비용을 함께 모니터링해야 합니다.