충격 뉴스 과소반응 편승 — 거래량 폭증을 동반하고 고점을 지키는 급변은 진짜 정보다. 대중이 덜 반영한 나머지 구간에 편승한다.
# 매수 토큰 기준 가격 p (YES 급등 → YES 매수, YES 급락 = NO 급등 → NO 매수)
base = min(p, 최근 6h 윈도우) # 윈도우 내 최저가
ENTER iff (p_now − base) ≥ +0.10 # 충격 점프 감지
AND 0.15 ≤ base ≤ 0.70 AND p_now ≤ 0.85 # 반등·favorite 배제 + 러닝룸
AND (최근 60분 고점 − p_now) ≤ 0.02 # 고점 유지 = 노이즈 스파이크 배제
AND vol24h_now ≥ 2.0 × avg(vol24h, 24h) # 거래량 폭증 = 진짜 정보의 서명대형 서프라이즈가 터지면 대중은 기존 가격에 앵커링된 채 "설마 그 정도일까"라며 새 정보를 일부만 가격에 반영한다. 보유자·관찰자 모두 "어제까지 30%였던 시장"이라는 기준점에 묶여 있어, 새 정보가 60%를 정당화해도 첫 반응은 45%에서 멈춘다. 주식시장의 PEAD(Post-Earnings Announcement Drift)와 동일한 구조로, 충격 직후의 첫 점프는 끝이 아니라 시작인 경우가 많다. 충격 뉴스는 확인-확산-수용의 단계를 거치며 대중 다수는 몇 시간 뒤에야 따라온다 — 봇은 3~5분 주기로 돌기 때문에 이 확산 구간의 앞쪽에 설 수 있다. 그리고 거래량은 정보의 서명이다: 노이즈 스파이크(소액 주문, 오호가)는 거래량이 미약하고 고점을 못 지키지만, 진짜 정보는 거래량 폭증과 고점 유지를 동반한다.
레포 리서치(golden-cherry/docs/polymarket-strategy-momentum.md 계열)는 "소폭(2-3%/일) 모멘텀은 +6-8% 지속, 10%+ 급변은 mean-revert"를 보고한다. lime은 이 명제를 정면으로 받아들이되, "10%+ 급변 전체가 회귀하는 것이 아니라, 거래량·고점 유지로 구분되는 하위 집단은 지속한다"는 가설을 검증한다. 즉 리서치의 평균적 결론에 조건부 예외가 존재하는지를 실측하는 실험이다.
elderberry와의 A/B 쌍 (핵심 설계) — golden-elderberry(Panic Fade)와 의도적으로 정반대 트리거의 A/B 쌍이다. 같은 이벤트 클래스("최근 몇 시간 내 ±0.10 이상 급변")에 대해:
| 급변의 성격 | 신호 | 담당 봇 | 액션 |
|---|---|---|---|
| 노이즈/공황 — 거래량 미약·불안정, 고점(저점) 반납 | 안정화 후 되돌림 | elderberry | 급락을 역매수 (mean-revert 베팅) |
| 진짜 정보 — 거래량 폭증(24h 평균의 2배+), 고점 유지 | 점프 후 유지 | lime | 급변 방향에 편승 (drift 베팅) |
두 봇의 트리거는 상호 배타적이다: elderberry는 "바닥 안정화 + 거래량 조건 없음 + 마감이 먼(≥48h) 급락"을, lime은 "거래량 2배 폭증 + 고점 유지(되돌림 ≤ 0.02)"를 요구한다. 4주 후 성적을 비교하면 "Polymarket의 10%+ 급변은 회귀인가 지속인가, 거래량이 그 구분자인가"가 시장 데이터로 검증된다. 한쪽만 수익이면 그쪽 가설이 맞는 것이고, 둘 다 수익이면 거래량 필터가 유효한 구분자라는 강한 증거다.
점프 감지는 "N시간 전 시점 가격 대비"가 아니라 최근 6h 윈도우 내 최저가(base) 대비 현재가 상승폭으로 판정한다. 시계열 정렬·보간·시점 매칭 없이 min() 하나로 끝나 단순하고, 스냅샷 간격이 불균일하거나(Jenkins 지연) 일부 결손이어도 판정이 흔들리지 않으며, 최저가 대비라 점프를 놓치는 방향의 오차가 없다. 대신 base 구간 제한([0.15, 0.70])이 "원래부터 바닥이던 시장의 자연 반등"을 걸러준다. 기존 봇의 교훈도 반영했다: banana의 개수 기반 윈도우 → timestamp 기반 윈도우 + 유효성 검증, 관대한 cold-start 폴백 → invalid 윈도우면 진입 금지 + prices-history 백필, cherry의 rapid_jump 영구 밴 → timestamp 기반 쿨다운(기본 24h), 영구 HOLDING 좀비 → EXPIRED 상태, TP 목표가 1.0 초과 → 0.99 캡.
모든 조건 AND. 점프 방향 토큰 기준 가격 p로 판정한다 (스냅샷은 항상 YES 가격으로 저장, NO면 1−p로 평가).
| # | 조건 | 값 (기본) | 이유 |
|---|---|---|---|
| 1 | 유동성 | liquidity ≥ $20,000 (min_liquidity) | 체결 가능성 확보 |
| 2 | 거래량 | volume24hr ≥ $10,000 (min_volume_24h) | 죽은 시장 배제 |
| 3 | 잔여시간 | 해결까지 ≥ 24h (entry_hours_min) | 마감 직전 정보성 급변(수렴)과 분리 |
| 4 | 윈도우 유효성 | 6h 윈도우에 ≥ 5 포인트 AND 커버리지 ≥ 50% (jump_window_hours 등) | 데이터 부족 시 진입 금지 (백필 시도 후에도 invalid면 skip) |
| 5 | 점프 | 6h 윈도우 최저가(base) 대비 현재가 상승 ≥ +0.10 (jump_min) | 충격 감지 |
| 6 | base 구간 | base ∈ [0.15, 0.70] (base_min, base_max) | 붕괴권 롱샷의 반등·이미 favorite인 시장 배제 |
| 7 | 현재가 상한 | 현재가 ≤ 0.85 (current_max) | 러닝룸 확보 (TP +12% 도달 가능) |
| 8 | 고점 유지 | 최근 60분 고점 대비 되돌림 ≤ 0.02 (hold_window_minutes, max_pullback) | 노이즈 스파이크 배제 (elderberry 영역) |
| 9 | 거래량 확인 | 현재 volume24hr ≥ 24h 윈도우 평균 × 2.0 (vol_mult_min) | 진짜 정보의 서명 |
| 10 | 재진입 쿨다운 | HOLDING 아님 + 마지막 매도/skip 24h 경과 (reentry_cooldown_hours) | 영구 one-shot 제거, 과잉 재진입 방지 |
방향: YES 급등 → YES(index 0) 매수, YES 급락(= NO 급등) → NO(index 1) 매수. 조건 5~8은 매수할 토큰 기준 가격으로 평가한다.
우선순위 순, 매 사이클 midpoint를 재조회해 검사한다.
| 순위 | 조건 | 파라미터 (기본값) | exit_reason |
|---|---|---|---|
| 1 | P&L ≤ -8% | stop_loss_percent = -0.08 | stop_loss |
| 2 | 현재가 ≥ min(매수가 × 1.12, 0.99) | take_profit_percent = 0.12 | take_profit |
| 3 | 현재가 < 최고가 × (1 − 0.06) | trailing_stop.percent = 0.06 | trailing_stop |
| 4 | 모멘텀 사망: 최근 3h 가격 변화 ≤ 0 | death_window_hours = 3 | momentum_death |
| 5 | 해결까지 < 12h | exit_hours = 12 | time_exit |
| - | midpoint 조회 불가 + endDate 24h 경과 | (고정) | resolved_unredeemed (EXPIRED, 수동 redeem 필요) |
모멘텀 사망은 "편승 근거(드리프트)가 소멸했다"는 신호다. 단, 매수 직후 데이터가 얕을 때(윈도우 포인트 < 2개 또는 커버리지 < 50%)는 판단을 보류해 즉시 청산을 방지한다.
우선순위: env > config.yaml > 코드 기본값.
공통 env (전 봇 동일 이름)
| env | 기본 | 의미 |
|---|---|---|
POLYMARKET_PRIVATE_KEY | (필수) | CLOB L1 인증 |
POLYMARKET_FUNDER_ADDRESS | (필수) | funder 지갑 주소 |
POLYBOT_BUY_AMOUNT | 5.0 | 1회 매수 USDC |
POLYBOT_MIN_LIQUIDITY | 20000 | 최소 유동성 $ |
POLYBOT_MIN_VOLUME_24H | 10000 | 최소 24h 거래량 $ |
POLYBOT_TAKE_PROFIT | 0.12 | 익절 % (목표가 0.99 캡) |
POLYBOT_STOP_LOSS | -0.08 | 손절 % |
POLYBOT_MAX_POSITIONS | -1 | 최대 동시 포지션 (-1 무제한) |
POLYBOT_REENTRY_COOLDOWN_HOURS | 24 | 재진입 쿨다운 |
POLYBOT_HISTORY_BACKFILL | true | prices-history 백필 |
POLYBOT_EXCLUDED_CATEGORIES | "" | 제외 카테고리 (comma 구분, 빈 값 = 비활성) |
LOG_LEVEL | INFO | 로그 레벨 (--verbose가 최우선) |
전략 전용 env
| env | 기본 | 의미 |
|---|---|---|
POLYBOT_JUMP_WINDOW_HOURS | 6 | 점프 감지 윈도우 |
POLYBOT_JUMP_MIN | 0.10 | 윈도우 내 최저가 대비 최소 상승폭 |
POLYBOT_BASE_MIN | 0.15 | 점프 시작 기준가 하한 |
POLYBOT_BASE_MAX | 0.70 | 점프 시작 기준가 상한 |
POLYBOT_CURRENT_MAX | 0.85 | 현재가 상한 (러닝룸) |
POLYBOT_HOLD_WINDOW_MINUTES | 60 | 고점 유지 확인 윈도우 |
POLYBOT_MAX_PULLBACK | 0.02 | 고점 대비 최대 되돌림 |
POLYBOT_VOL_MULT_MIN | 2.0 | 거래량 확인 배수 |
POLYBOT_DEATH_WINDOW_HOURS | 3 | 모멘텀 사망 판정 윈도우 |
POLYBOT_ENTRY_HOURS_MIN | 24 | 진입 최소 잔여 시간 |
POLYBOT_EXIT_HOURS | 12 | 시간 청산 기준 |
POLYBOT_TRAILING_STOP_ENABLED | true | 트레일링 on/off |
POLYBOT_TRAILING_STOP_PERCENT | 0.06 | 최고점 대비 하락률 |
momentum_death로 소손실 청산이 반복된다.volume24hr는 24시간 누적이라 "지금 이 순간의 폭증"을 정확히 못 잡는다. 점프 몇 시간 후에야 2배 조건이 충족되어 늦게 진입할 수 있다.uv run python main.py run --simulate --job sim-lime을 Jenkins 3~5분 주기로. data/sim-lime/trades_sim.db로 진입 빈도·가짜 신호 비율 확인.POLYBOT_BUY_AMOUNT=5로 실전 전환. elderberry와 같은 기간·같은 금액으로 병행 운영.momentum_death 청산 비율 > 50% → 편승분이 없다는 신호, jump_min 상향 또는 전략 폐기 검토entry_reason/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_JUMP_MIN=0.10 export POLYBOT_VOL_MULT_MIN=2.0 export POLYBOT_MAX_PULLBACK=0.02 cd ./golden-lime uv sync uv run python ./main.py run
3~5분 주기 cron 트리거를 권장한다 (스냅샷 해상도 = 실행 주기).