Skip to content

grbuguj/poly-sniper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⚡ poly-sniper

BTC 5분봉 가격 예측시장 자동 배팅 엔진 — Spring Boot 기반 실시간 퀀트 트레이딩 봇

Java Spring Boot Web3j Gradle H2 License

Chainlink 가격 피드와 Polymarket CLOB을 연결한 실시간 latency-arbitrage 시스템


📌 개요

poly-sniperPolymarket의 BTC 5분봉 Up/Down 예측시장 자동 배팅 봇입니다.

Polymarket의 btc-updown-5m-* 마켓은 5분마다 "BTC 가격이 오를까, 내릴까"를 예측하는 이진 예측시장입니다. 실시간 가격은 Chainlink에서 약 1초 내외로 갱신되지만 시장의 오즈는 거래자들의 주문에 의해 수 초~수십 초 뒤에 반영됩니다. 이 비효율 구간을 100ms 주기로 탐지해 +EV 구간에만 선택적으로 진입합니다.

주요 기능

  • 100ms 주기 실시간 스캔 루프
  • Polymarket RTDS WebSocket으로 Chainlink BTC/USD 가격 수신 (좀비 연결 자동 재연결)
  • EIP-712 주문 서명 Web3j 직접 구현 (SDK 미사용)
  • OkHttp HTTP/2 멀티플렉싱 + 커넥션 프리워밍
  • Thymeleaf 실시간 대시보드 (500ms 폴링)
  • DRY-RUN / LIVE 이중 모드

적용한 퀀트 기법

기법 출처 용도
CUSUM 필터 Lopez de Prado, Advances in Financial ML (2018) 횡보장 방향성 필터
ATR(14) 동적 임계값 Wilder (1978) 변동성 적응형 진입 기준
Kelly Criterion Kelly (1956), Thorp (1969) 엣지 비례 배팅 사이징
변동성 레짐 스위칭 Hamilton (1989) 기반 단순화 4단계 레짐별 파라미터 자동 조정
서킷브레이커 일반 리스크 관리 3연패 시 5분 강제 중단

🏗️ 시스템 아키텍처

┌─────────────────────────────────────────────────────────────────────┐
│                       poly-sniper (Spring Boot)                     │
│                                                                     │
│  ┌───────────────────────┐         ┌──────────────────────────┐     │
│  │ ChainlinkPriceService │         │     OddsService          │     │
│  │ ─────────────────────│         │ ────────────────────────│     │
│  │ • RTDS WebSocket     │         │ • Gamma API (이벤트)    │     │
│  │ • BTC/USD 실시간     │         │ • CLOB API (오즈)       │     │
│  │ • ATR(14) 계산       │         │ • 100ms 프리페치        │     │
│  │ • 변동성 레짐 분류   │         │ • slug 변경 감지        │     │
│  └──────────┬───────────┘         └────────────┬─────────────┘     │
│             │                                   │                   │
│             ▼                                   ▼                   │
│       ┌──────────────────────────────────────────────────┐          │
│       │          SniperScanner (100ms scan loop)         │          │
│       │ ─────────────────────────────────────────────── │          │
│       │  ① 연결 확인  → ② 캔들 포지션  → ③ 변동폭 필터  │          │
│       │  ④ 횡보 감지  → ⑤ CUSUM 필터   → ⑥ 스프레드 체크 │          │
│       │  ⑦ 모멘텀 일관성 → ⑧ 오즈 상한 → ⑨ 티어드 조기진입│          │
│       │  ⑩ EvCalculator → (EV > 임계값) → executeTrade  │          │
│       └────────────┬─────────────────────────────────────┘          │
│                    │                                                │
│          ┌─────────┼──────────┐                                     │
│          ▼         ▼          ▼                                     │
│  ┌───────────┐ ┌─────────┐ ┌────────────────┐                       │
│  │EvCalculator│ │BalanceSvc│ │ OrderService   │                     │
│  │Kelly+EV   │ │USDC 동기화│ │ EIP-712 서명  │                     │
│  └───────────┘ └─────────┘ │ FOK 주문      │                       │
│                            │ HMAC-SHA256   │                       │
│                            └───────┬────────┘                       │
│                                    ▼                                │
│                         ┌──────────────────────┐                    │
│                         │   Polymarket CLOB    │                    │
│                         └──────────┬───────────┘                    │
│                                    │                                │
│        ┌───────────────────────────┴────────────────────┐           │
│        ▼                                                ▼           │
│  ┌──────────────┐                              ┌──────────────────┐ │
│  │ResultChecker │ (체결/결과 확인, PENDING→WIN/LOSE)│ RedeemService ││
│  │              │                              │ (Python 스크립트) │ │
│  └──────────────┘                              └──────────────────┘ │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │       DashboardController (Thymeleaf + REST API)            │    │
│  │  GET /api/scan (500ms) | /api/stats | POST /api/toggle      │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘

📁 패키지 구조

com.sniper.btc
├── BtcSniperApplication.java       # @SpringBootApplication + @EnableScheduling
│
├── controller/
│   └── DashboardController.java    # 실시간 대시보드 + REST API (282 LoC)
│
├── entity/
│   └── Trade.java                  # JPA 엔티티 — 거래 이력
│
├── repository/
│   └── TradeRepository.java        # Spring Data JPA
│
└── service/
    ├── ChainlinkPriceService.java  # WebSocket 가격 수신 + ATR (374 LoC)
    ├── OddsService.java            # Polymarket 오즈 캐시 + 프리페치 (278 LoC)
    ├── EvCalculator.java           # 확률 추정 + Kelly 배팅 사이징 (275 LoC)
    ├── SniperScanner.java          # ★ 메인 스캔 엔진 (1,043 LoC)
    ├── OrderService.java           # EIP-712 서명 + FOK 주문 (441 LoC)
    ├── BalanceService.java         # USDC 잔액 동기화 (299 LoC)
    ├── ResultChecker.java          # 체결 확인 + 결과 판정 (498 LoC)
    └── RedeemService.java          # CTF 토큰 상환 (252 LoC)

총 3,800+ LoC (Java) + Python 스크립트 (CTF redeem)

🧠 핵심 알고리즘

1️⃣ 10단계 필터 파이프라인

스캐너는 매 스캔마다 10개의 직렬 필터를 통과시켜 거짓 신호를 걸러냅니다. 한 필터라도 실패하면 즉시 리턴(early-exit)하여 평균 스캔 시간을 최소화합니다.

[0] 마스터 스위치 OFF          → 대시보드 토글
[1] Chainlink 연결 / 워밍업    → 첫 5M 경계 전 배팅 금지
[2] 캔들 쿨다운                → 같은 5분봉에서 1건만
[3] 서킷브레이커 (3연패 → 5분) → 손실 연쇄 차단
[4] ATR 기반 동적 최소 변동폭  → 노이즈 필터
[5] 횡보 감지 (시초가 5회+ 교차) → 방향성 없는 캔들 제외
[6] CUSUM 필터                → 누적 이탈 임계값 미만이면 스킵
[7] 캔들 포지션 (0~4초/285초+) → 시초가 동기화 대기 + 마감 차단
[8] 스프레드 검증 (upOdds+downOdds ≤ 1.05)
[9] 모멘텀 일관성 (10틱, 레짐별 동적 임계값)
    + 모멘텀 방향 ≠ 가격 방향이면 스킵 (mean-reversion 함정 방지)
[10] 오즈 상한 (> 60¢이면 손익비 불리 → 스킵)

2️⃣ CUSUM 필터 (Lopez de Prado)

횡보장에서 가짜 방향성에 속지 않기 위해 누적합(CUSUM) 필터를 구현했습니다.

// SniperScanner.java — trackCusum()
double ret = ((currentPrice - lastCusumPrice) / lastCusumPrice) * 100;

cusumPos = Math.max(0, cusumPos + (ret - CUSUM_EXPECTED_RETURN));  // 상승 누적
cusumNeg = Math.min(0, cusumNeg + (ret + CUSUM_EXPECTED_RETURN));  // 하락 누적

double h = chainlink.getATRPct() * params[3]; // 임계값 = ATR × 레짐별 배율

if (cusumPos > h || Math.abs(cusumNeg) > h) {
    cusumTriggered = true;  // 유의미한 방향성 감지
}

왜 이게 중요한가? — 단순 변동폭 필터는 "오르락내리락 ±0.1%" 같은 횡보장에서도 트리거됩니다. CUSUM은 한 방향으로 누적 이탈해야만 발동하므로, 진짜 추세가 생길 때까지 진입을 보류합니다. 각 캔들 시작 시 cusumPos, cusumNeg를 0으로 리셋하여 캔들별로 독립 판정합니다.

3️⃣ 변동성 레짐 자동 스위칭

ATR(14) 퍼센트 값에 따라 시장을 4단계 레짐으로 분류하고, 각 레짐마다 5개 파라미터를 동적으로 조정합니다.

레짐 ATR% 범위 entryMult rangeMult momentumMin cusumMult gapAdj
LOW 🟢 < 0.04% 0.40 0.25 0.35 0.35 −0.01
NORMAL 🔵 0.04–0.10% 0.50 0.30 0.40 0.40 0.00
HIGH 🟡 0.10–0.18% 0.60 0.35 0.50 0.50 +0.01
EXTREME 🔴 ≥ 0.18% 0.70 0.40 0.60 0.60 +0.02

조용한 시장에선 공격적으로, 격동기엔 방어적으로 — Sharpe Ratio 최적화를 염두에 둔 파라미터 적응입니다.

4️⃣ Kelly Criterion 기반 배팅 사이징

엣지가 클 때만 크게 배팅하는 Kelly 공식을 오즈 스케일링으로 보정했습니다.

// EvCalculator.java — calcBetSize()
double kellyFraction = ev / (payout - 1.0);           // 이론 Kelly
double kellyMultiplier = ev >= 1.0 ? 0.35 :
                        ev >= 0.5 ? 0.30 :
                        ev >= 0.3 ? 0.25 : 0.20;      // Fractional Kelly
double safeFraction = kellyFraction * kellyMultiplier;

// 잔액/시드 비율에 따른 티어 상한
double ratio = balance / initialBalance;
double maxFraction = ratio < 1.0 ? 0.03 :             // 시드 이하: 안정
                     ratio < 2.0 ? 0.04 :             // ~2배: 성장
                     ratio < 5.0 ? 0.05 : 0.06;       // 5배+: 풀 공격

// 오즈 스케일링 — 싼 오즈(30¢)일수록 엣지가 크므로 더 과감하게
double oddsScale = clamp(0.50 / marketOdds, 0.80, 2.0);
maxFraction = Math.min(maxFraction * oddsScale, 0.12); // 최대 12% 하드캡

5️⃣ 티어드 조기 진입 (Latency Arbitrage)

캔들 시작 후 15~40초 구간은 특별한 구간입니다. 가격은 이미 움직였지만 오즈는 아직 반영되지 않은 비효율의 창입니다.

// 15초~: Tier 1 — 큰 움직임(≥0.10%) + 매우 싼 오즈(≤45¢) → 즉시 진입
// 30초~: Tier 2 — 중간 움직임(≥0.08%) + 적당 오즈(≤50¢) → 진입
// 40초~: 기본 진입 허용
// 285초~: 마감 15초 차단 (마켓 정산/교체 구간)

6️⃣ FOK 주문 + 즉시 재시도 루프

Polymarket CLOB은 FOK(Fill-Or-Kill) 주문을 지원합니다. 체결 실패 시 scan() 재진입 없이 executeTrade() 내부에서 직접 재시도하여 오즈 상한 필터의 재평가를 회피합니다.

1차 주문 실패 → +2틱 올려서 재시도 (50ms 대기)
2차 주문 실패 → +2틱 추가 (누적 +4틱)
3차 주문 실패 → 이 캔들 포기
60¢ 초과 시   → 손익비 불리, 즉시 포기

🔐 EIP-712 주문 서명 (Web3j 직접 구현)

Polymarket CLOB은 EIP-712 구조화 서명을 요구합니다. SDK 없이 Web3j만으로 처음부터 구현했습니다.

// OrderService.java — 핵심 발췌
private static final String ORDER_TYPE_STRING =
    "Order(uint256 salt,address maker,address signer,address taker," +
    "uint256 tokenId,uint256 makerAmount,uint256 takerAmount," +
    "uint256 expiration,uint256 nonce,uint256 feeRateBps," +
    "uint8 side,uint8 signatureType)";

private static final byte[] ORDER_TYPE_HASH_BYTES =
    Hash.sha3(ORDER_TYPE_STRING.getBytes(StandardCharsets.UTF_8));

⚡ 주문 지연 최소화 최적화

최적화 효과
Credentials 사전 캐싱 서명마다 Credentials.create() 호출 생략
DomainSeparator 1회 계산 EIP-712 도메인 해시 프리빌드
HMAC 키 SecretKeySpec 캐시 인증 서명마다 키 초기화 생략
padded address 바이트 프리빌드 maker/signer/taker 32바이트 패딩 재사용
tokenId BigInteger 프리파싱 오즈 변경 시에만 재계산
HTTP/2 멀티플렉싱 + ConnectionPool TCP/TLS 핸드셰이크 제거
커넥션 프리워밍 애플리케이션 시작 시 워밍업 요청

결과: 스캔 → 서명 → 주문 전송까지 평균 수 ms 이내.


🛠️ 기술 스택

계층 기술
런타임 Java 17 (toolchain)
프레임워크 Spring Boot 3.4.2, Spring Web, Spring Data JPA, Thymeleaf
블록체인 Web3j 4.10.3 (EIP-712 서명)
HTTP OkHttp 4.12.0 (HTTP/2, 커넥션 풀)
직렬화 Jackson Databind
DB H2 (파일 기반, AUTO_SERVER 모드)
빌드 Gradle 8.x (Spring Dependency Management)
유틸 Lombok (불변 객체, 로깅)
정산 Python 3 (py_clob_client, Builder SDK)

🚀 Getting Started

요구 사항

  • JDK 17+
  • Python 3.9+ (CTF redeem 스크립트용)
  • Polymarket 계정 + CLOB API 크레덴셜
  • (선택) 프록시 지갑 funder 주소

환경변수 설정

export POLY_PRIVATE_KEY=0x...        # EOA 프라이빗 키
export POLY_API_KEY=...              # CLOB API 키
export POLY_API_SECRET=...           # CLOB 시크릿
export POLY_PASSPHRASE=...           # CLOB 패스프레이즈
export BUILDER_API_KEY=...           # Builder API (선택)
export BUILDER_SECRET=...
export BUILDER_PASSPHRASE=...

빌드 & 실행

# 1. 저장소 클론
git clone https://github.com/grbuguj/poly-sniper.git
cd poly-sniper

# 2. DRY-RUN 모드 확인 (application.yml)
# sniper.dry-run: true  ← 실거래 전 반드시 확인

# 3. 빌드 + 실행
./gradlew bootRun

# 4. 대시보드 접속
open http://localhost:8090
# H2 콘솔: http://localhost:8090/h2

주요 설정 (application.yml)

sniper:
  dry-run: true                # DRY-RUN / LIVE 스위칭
  initial-balance: 80.0        # 초기 시드 ($)
  scan-interval-ms: 100        # 스캔 주기
  min-bet: 1.0                 # 최소 배팅
  max-bet: 50.0                # 최대 배팅 (유동성 한계)
  odds-cache-ttl-ms: 500       # 오즈 캐시 TTL
  http-timeout-ms: 2000

📊 대시보드 기능

실시간 Thymeleaf 대시보드는 2가지 폴링 주기로 서버 부하를 분산합니다.

  • GET /api/scan (500ms 폴링, DB 조회 없음)
    • 스캔 속도 (scans/sec), 마지막 필터 히트, ATR%, CUSUM S±, 변동성 레짐
  • GET /api/stats (5s 폴링, DB 집계)
    • 뱅크롤, ROI, 승률, 전체 거래 이력, 이퀄리티 커브

REST API

Method Path 설명
GET / Thymeleaf 대시보드
GET /api/scan 경량 스캔 메트릭 (500ms 폴링용)
GET /api/stats 전체 통계 + 거래 이력
POST /api/toggle 스나이퍼 ON/OFF
POST /api/reset 통계 초기화
GET /api/test/balance Polymarket 실잔액 조회
POST /api/test/order 수동 주문 (디버깅용)
GET /api/test/redeem 수동 CTF 상환

🧩 설계 메모

왜 Spring Boot? — 단일 인스턴스 + 단순 스케줄링이면 충분한 규모. JPA, Thymeleaf, Actuator 같은 배터리 포함 생태계가 빠른 개발에 유리. 스캔 루프는 Spring 관리 밖에서 ScheduledExecutorService로 돌려 오버헤드 제거.

왜 H2 파일 DB? — 거래 이력 몇천 건 수준이라 외부 DB는 오버엔지니어링. 재시작 시 이력 보존 필요해서 인메모리 불가. AUTO_SERVER=TRUE로 외부 H2 Console 접근 가능해서 디버깅 편함.

왜 Web3j로 EIP-712 직접? — Polymarket 공식 SDK가 Python/TypeScript만 제공. Java에서 바이트 단위로 직접 짜서 서명 경로 전체를 통제하고 지연을 최소화.

왜 FOK? — 5분봉 마켓은 오즈가 급변해서 미체결 대기가 손해. FOK는 슬리피지 노출 제거 + 부분 체결 복잡도 제거. 실패 시 즉시 다음 기회로.


⚠️ 주의

  • 예측시장/파생상품 거래는 원금 손실 가능성이 있습니다
  • 일부 국가(미국 등)에서는 Polymarket 접근이 제한됩니다
  • 프라이빗 키 관리에 주의하세요 (.env를 커밋하지 마세요)
  • 이 소프트웨어의 사용으로 인한 금전적 손실에 대해 저자는 책임지지 않습니다

📚 참고 문헌

  • Marcos López de Prado. Advances in Financial Machine Learning (Wiley, 2018) — CUSUM filter
  • J. L. Kelly Jr. A New Interpretation of Information Rate (Bell System Technical Journal, 1956)
  • Edward O. Thorp. Optimal Gambling Systems for Favorable Games (1969) — Fractional Kelly
  • J. Welles Wilder. New Concepts in Technical Trading Systems (1978) — ATR
  • EIP-712: Typed structured data hashing and signing
  • Polymarket CLOB Documentation

🤖 Built with AI

Claude와 페어 프로그래밍으로 만들었습니다. 전략 설계(CUSUM 필터 도입, 변동성 레짐 스위칭, Kelly 기반 사이징 등)와 파라미터 튜닝은 실거래 관찰하면서 직접 결정했고, 구현은 AI와 주고받으며 진행했습니다.


☕ Java 17 · 🍃 Spring Boot 3.4.2 · 🤖 Vibe-coded with Claude

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors