정보 폭포 편승 — "소폭이지만 일관된 24h 드리프트 + 거래량 가속"이 확인된 시장에 편승해, 정보가 대중 전체에 확산되기 전 구간의 추가 이동을 수확한다.
# 매수 토큰 기준 가격 p (YES 상승 드리프트 → YES 매수, YES 하락 → NO 매수)
drift_24h = p_now − p_24h_ago
ENTER iff 0.40 ≤ p_now ≤ 0.80
AND +0.04 ≤ drift_24h ≤ +0.10 # 소폭·일관 드리프트만 (10%+는 mean-revert)
AND nonneg_buckets(4h 버킷 × 6) ≥ 70% # 버킷 일관성 게이트
AND vol24h_now ≥ 1.2 × avg(vol24h, 24h) # 거래량 가속 게이트예측시장 참가자는 동시에 정보를 얻지 않는다. 정보에 가까운 소수가 먼저 베팅하면 가격이 소폭 움직이고, 그 가격 변화 자체가 신호가 되어 나머지 대중이 뒤따라 베팅한다(정보 폭포, information cascade). 이 확산이 완료되기 전까지 가격은 새 정보를 부분적으로만 반영한 상태이므로, 초기 드리프트에 편승하면 잔여 이동을 수확할 수 있다. 반대로 급격한 대형 움직임은 이미 확산이 끝났거나 과잉반응이므로 편승 가치가 없다.
레포 공유 리서치(docs/polymarket-strategy-momentum.md, banana/cherry 레포)는 "2-3% 일일 변화는 같은 방향으로 추가 이동할 확률이 6-8% 상승(가장 강한 모멘텀)", "10%+ 일일 변화는 모멘텀 약화, 평균회귀 가능성 높음"을 보고한다. grape의 드리프트 밴드 [+0.04, +0.10]은 이 발견을 그대로 구현한 것이다 — 하한 0.04는 노이즈(±1~2%p 일상 변동)를 걸러내고, 상한 0.10은 mean-revert 영역을 배제한다.
또 하나의 축은 banana 골든크로스 전략 실패의 교정이다. banana STRATEGY_ANALYSIS.md에서 확인된 실패 원인 5가지를 grape는 다음과 같이 고쳤다.
| # | banana의 실패 원인 | grape의 교정 |
|---|---|---|
| 1 | 도달 불가 threshold: golden cross 임계값 0.02가 "스냅샷당 기울기" 단위여서 15분에 약 6%p 급등해야 발화 — 실로그 diff는 ±0.007 이하로 사실상 무전략으로 퇴화 | 임계값을 윈도우 전체 변화량(%p)으로 재정의. 24h에 +4~10%p는 리서치가 실측한, 실제로 도달 가능한 밴드 |
| 2 | count 기반 윈도우: "최신 N개 스냅샷"이라 Jenkins 중단 시 "15분 윈도우"가 몇 시간을 커버해도 그대로 계산 | timestamp 기반 윈도우(ts ≥ now − 24h) + 커버리지 검증(포인트 ≥ 5개 AND 커버 시간 ≥ lookback의 50%) |
| 3 | 관대한 cold-start 폴백: 스냅샷 6개 미만이면 "단기 momentum > 0"만으로 진입 — 실운영 매수의 전부가 이 우회 경로 | 폴백 제거. invalid → /prices-history 백필 시도 → 그래도 invalid면 진입하지 않는다 |
| 4 | 양 끝점 2개 값만 사용: 끝점 노이즈에 민감, 중간 경로 무시 | 버킷 일관성 게이트: 24h를 4h 버킷 6개로 나눠 70% 이상이 비음(≥ 0) 변화여야 진입 |
| 5 | 확인 신호 부재: 가격 기울기 하나로만 판단 | 거래량 가속 게이트: volume24hr가 24h 평균의 1.2배 이상 — "가격이 오르는데 거래량도 붙는다"는 이중 확인 |
기존 봇 공통 버그도 함께 수정했다: 영구 one-shot 거래 제한 → 24h 쿨다운 재진입, rapid_jump 영구 skip → timestamp 기반 skip, 해결된 시장의 HOLDING 영구 잔류 → EXPIRED 처리, take_profit 도달 불가 → 목표가 0.99 캡, LOG_LEVEL env 지원.
모든 조건 AND. 드리프트 방향 토큰 기준 가격 p로 판정한다.
| # | 조건 | 값 (기본) | 이유 |
|---|---|---|---|
| 1 | 유동성 / 거래량 / 잔여시간 | liquidity ≥ $20,000 · volume24hr ≥ $10,000 · 해결까지 ≥ 48h | 신호 신뢰성 + 러닝룸 확보 |
| 2 | 윈도우 유효성 | 24h 윈도우 포인트 ≥ 5개, 커버리지 ≥ 12h. invalid면 /prices-history 백필 시도, 그래도 invalid면 진입 금지 | banana 교정 #2, #3 — 데이터 없이는 베팅하지 않는다 |
| 3 | 방향 결정 | YES 상승 드리프트 → YES 매수, YES 하락(= NO 상승) → NO 매수. 이하 조건은 매수 토큰 기준 | Polymarket에 숏 없음 |
| 4 | 가격 밴드 | p ∈ [0.40, 0.80] | 러닝룸 있는 중간 구간 |
| 5 | 24h 드리프트 | +0.04 ≤ (현재가 − 24h 전 가격) ≤ +0.10 | 리서치: 소폭 모멘텀 지속, 10%+ 회귀 |
| 6 | 일관성 | 4h 버킷 6개 중 ≥ 70%가 비음(≥ 0) 변화. 판정 가능 버킷이 절반 미만이면 데이터 부족으로 진입 금지 | banana 교정 #4 — 끝점 노이즈 배제 |
| 7 | 거래량 가속 | 현재 volume24hr ≥ 24h 윈도우 평균의 1.2배 | banana 교정 #5 — 정보 확산의 이중 확인 |
| 8 | 재진입 | HOLDING 없음 + 마지막 청산/skip 후 24h 경과 | 영구 one-shot 제거 |
매수 직전 CLOB midpoint로 재검증한다: 밴드 상한(0.80) 초과 급등이면 skip(rapid_jump, 쿨다운 후 재평가), 하한(0.40) 미만 하락이면 이번 사이클만 skip.
우선순위 순으로 매 사이클 검사한다.
| 순위 | 조건 | 값 | exit_reason |
|---|---|---|---|
| 1 | 손절 | P&L ≤ -8% | stop_loss |
| 2 | 익절 | 현재가 ≥ min(진입가 × 1.15, 0.99) | take_profit |
| 3 | 드리프트 소멸 | 최근 6h 매수 토큰 가격 변화 ≤ 0 (포인트 2개 미만이면 판단 보류) | drift_death |
| 4 | 트레일링 스탑 | 최고가 대비 -6% | trailing_stop |
| 5 | 시간 청산 | 해결까지 < 24h | time_exit |
| - | 해결 시장 | midpoint 조회 실패 + end_date 24h 경과 → EXPIRED 마감 (수동 redeem 필요) | resolved_unredeemed |
우선순위: env > config.yaml > 코드 기본값.
전략 파라미터
| env var | 기본값 | 의미 |
|---|---|---|
POLYBOT_PROB_MIN | 0.40 | 매수 토큰 가격 하한 |
POLYBOT_PROB_MAX | 0.80 | 매수 토큰 가격 상한 |
POLYBOT_DRIFT_LOOKBACK_HOURS | 24 | 드리프트 판정 윈도우 (h) |
POLYBOT_DRIFT_MIN | 0.04 | 드리프트 하한 (+4%p) |
POLYBOT_DRIFT_MAX | 0.10 | 드리프트 상한 (mean-revert 배제) |
POLYBOT_BUCKET_HOURS | 4 | 일관성 버킷 크기 (h) |
POLYBOT_CONSISTENCY_MIN | 0.70 | 비음 버킷 비율 하한 |
POLYBOT_VOL_ACCEL_MIN | 1.2 | 거래량 가속 배수 하한 |
POLYBOT_DEATH_WINDOW_HOURS | 6 | 드리프트 소멸 판정 윈도우 (h) |
POLYBOT_ENTRY_HOURS_MIN | 48 | 해결까지 최소 잔여시간 (h) |
POLYBOT_EXIT_HOURS | 24 | 시간 청산 기준 (h) |
POLYBOT_TRAILING_STOP_ENABLED | true | 트레일링 스탑 on/off |
POLYBOT_TRAILING_STOP_PERCENT | 0.06 | 트레일링 스탑 % |
공통 파라미터 (전 봇 동일 이름)
| env var | 기본값 | 의미 |
|---|---|---|
POLYBOT_BUY_AMOUNT | 5.0 | 1회 매수 USDC |
POLYBOT_MIN_LIQUIDITY | 20000 | 최소 유동성 $ |
POLYBOT_MIN_VOLUME_24H | 10000 | 최소 24h 거래량 $ |
POLYBOT_TAKE_PROFIT | 0.15 | 익절 % (목표가 0.99 캡) |
POLYBOT_STOP_LOSS | -0.08 | 손절 % |
POLYBOT_MAX_POSITIONS | -1 | 최대 동시 포지션 (-1 무제한) |
POLYBOT_REENTRY_COOLDOWN_HOURS | 24 | 재진입 쿨다운 (h) |
POLYBOT_HISTORY_BACKFILL | true | prices-history 백필 |
POLYBOT_EXCLUDED_CATEGORIES | "" | 제외 카테고리 (comma 구분, 기본 비활성) |
LOG_LEVEL | INFO | 로그 레벨 |
volume24hr는 시장 전체 거래량이지 방향별 거래량이 아니다. 반대 방향 매물 폭증도 "가속"으로 잡힌다.uv run python main.py run --simulate --job sim-grape를 Jenkins 3~5분 주기로 실행. data/sim-grape/trades_sim.db로 신호 빈도와 가상 손익 확인. 신호가 주 3건 미만이면 POLYBOT_CONSISTENCY_MIN/POLYBOT_VOL_ACCEL_MIN 완화를 검토.POLYBOT_BUY_AMOUNT=5로 실전 전환. 대시보드(GOLDEN-GRAPE)와 daily-report로 잔고 추적.drift_death/trailing_stop 비중이 70%를 넘으면 진입 신호가 늦다는 뜻 → 드리프트 하한 완화 검토. 승률 < 45% → 전략 기각 또는 파라미터 재설계.-- 검증 쿼리 (data/{job}/trades.db) — status는 enum 이름으로 저장됨 (COMPLETED/EXPIRED)
SELECT exit_reason, COUNT(*) cnt, ROUND(AVG(realized_pnl), 4) avg_pnl,
SUM(CASE WHEN realized_pnl > 0 THEN 1 ELSE 0 END) * 1.0 / COUNT(*) win_rate
FROM trades WHERE status = 'COMPLETED' GROUP BY exit_reason;
#!/bin/bash export POLYBOT_BUY_AMOUNT=20 export POLYMARKET_PRIVATE_KEY=<Jenkins credential> export POLYMARKET_FUNDER_ADDRESS=<Jenkins credential> export LOG_LEVEL=INFO # 전략 파라미터 예시 (선택) export POLYBOT_DRIFT_MIN=0.04 export POLYBOT_CONSISTENCY_MIN=0.70 export POLYBOT_VOL_ACCEL_MIN=1.2 cd ./golden-grape uv sync uv run python ./main.py run
3~5분 주기 cron 트리거를 권장한다 (스냅샷 축적 주기 = 드리프트 판정 해상도).