ํด๊ฒฐ๊น์ง 24~720h ๋จ์ 75~92% favorite์ ๋งค์ํด ํด๊ฒฐ ์ง์ ์๋ ด(0.95+)์ ์บก์ฒ โ ์์ ยท์ต์ ยทํธ๋ ์ผ๋งยท์๊ฐ ์ฒญ์ฐ์ 4์ค ๋ฆฌ์คํฌ ๊ด๋ฆฌ.
BUY : 0.75 โค p โค 0.92 (์๋ ํฌํจ) AND 24h โค (endDate - now) โค 720h
AND liquidity โฅ 10,000 AND condition_id๋น ํ์ 1ํ
SELL (์ฐ์ ์์ ์, ๋งค ์ฌ์ดํด midpoint ๊ฒ์ฌ):
โ P&L โค -8% (stop_loss) โก P&L โฅ +10% (take_profit)
โข ํ์ฌ๊ฐ < ์ต๊ณ ๊ฐ ร 0.95 (trailing_stop) โฃ ํด๊ฒฐ 12h ์ (time_exit)Golden Cherry๋ "ํด๊ฒฐ(resolution)์ด ๊ฐ๊น์์ง ๊ณ ํ๋ฅ favorite์ 1.0์ผ๋ก ์๋ ดํ๋ค"๋ ๊ฐ์ ์ ๋ฒ ํ ํ๋ ๋ด์ด๋ค. Gamma API๋ก ์ ๋์ฑ ์๋ ํ์ฑ ์์ฅ์ ์ ์ ์ค์บํด์, ํ๋ฅ ์ด 75~92% ๊ตฌ๊ฐ(config ๊ธฐ์ค)์ด๊ณ ํด๊ฒฐ๊น์ง 24h~720h(1~30์ผ) ๋จ์ ์์ฅ์ favorite ํ ํฐ์ ์์ฅ๋น 1ํ, ๊ณ ์ ๊ธ์ก(USDC)์ผ๋ก ๋งค์ํ๋ค. ์ดํ Jenkins๊ฐ 3~5๋ถ๋ง๋ค ๋ด์ ์ฌ์คํํ ๋๋ง๋ค ๋ณด์ ํฌ์ง์ ์ midpoint๋ฅผ ์กฐํํด ์์ (-8%) โ ์ต์ (+10%) โ ํธ๋ ์ผ๋ง ์คํ(์ต๊ณ ์ ๋๋น -5%) โ ์๊ฐ ์ฒญ์ฐ(ํด๊ฒฐ 12h ์ ) ์์ผ๋ก ์ฒญ์ฐ ์กฐ๊ฑด์ ๊ฒ์ฌํ๋ค. ํต์ฌ ์์ต ์์ฒ์ "๋งค์๊ฐ 0.75~0.92 โ ํด๊ฒฐ ์ง์ 0.95~0.99 ๋งค๋"์ ์๋ ด ๊ตฌ๊ฐ ์บก์ฒ์ด๋ฉฐ, ํ ๋ฒ ๊ฑฐ๋(๋๋ skip)ํ ์์ฅ์ ์๊ตฌํ ์ฌ์ง์ ํ์ง ์๋๋ค.
์๋ PRD๋ "80% ๋งค์ โ 90% ๋งค๋" ๋จ์ ์ ๋ต์ด์๊ณ , ์ดํ ๋ชจ๋ฉํ (๊ณจ๋ ํฌ๋ก์ค) ์ ๋ต์ ๊ฑฐ์ณ ํ์ฌ์ Resolution Momentum(์๊ฐ ๊ธฐ๋ฐ ์ง์ + 4์ค ์ฒญ์ฐ)์ผ๋ก ์ ์ฐฉํ๋ค.
๋งค์ โ ์ค์บ ์กฐ๊ฑด (์๋๋ฅผ ๋ชจ๋ ํต๊ณผํด์ผ ํ๋ณด):
| # | ์กฐ๊ฑด | ๊ตฌํ | ๋น๊ณ |
|---|---|---|---|
| 1 | ํ์ฑ ์์ฅ | Gamma GET /markets?active=true&closed=false ํ์ด์ง๋ค์ด์
(100๊ฐ์ฉ, offsetโฅ5000 ์์ ์ค๋จ) | |
| 2 | ์นดํ ๊ณ ๋ฆฌ ํํฐ | is_sports_market() โ excluded_categories๊ฐ ๋น ๋ฐฐ์ด์ด๋ฉด ํํฐ ์์ ๋นํ์ฑํ | ํ์ฌ config๋ [] โ ์คํฌ์ธ ํฌํจ ์ ์ฒด ์ค์บ |
| 3 | ์ ๋์ฑ | liquidity โฅ min_liquidity (ํด๋ผ์ด์ธํธ ์ธก) | config 10,000 / ์ฝ๋ ๊ธฐ๋ณธ 50,000 |
| 4 | side ์ ํ | ๊ธฐ๋ณธ: Yes/No ์ค ํ๋ฅ ๋์ ์ชฝ. --yes-only: ๋ฌด์กฐ๊ฑด index 0(Yes) ํ ํฐ๋ง ํ๊ฐ | ์๋ yes-only ์ค๋ช ์ฐธ๊ณ |
| 5 | ํ๋ฅ ๊ตฌ๊ฐ | buy_threshold โค p โค sell_threshold (์๋ ํฌํจ) | 0.75 โค p โค 0.92 |
| 6 | ์๊ฐ ๊ตฌ๊ฐ | entry_hours_min โค (endDate - now) โค entry_hours_max (time_based ํ์ฑ ์). endDate ์์/๊ณผ๊ฑฐ๋ฉด ์ ์ธ | 24h ~ 720h |
๋งค์ โ ์คํ ์ฌ๊ฒ์ฆ (ํ๋ณด๋ณ):
| # | ์กฐ๊ฑด | ๋์ |
|---|---|---|
| 1 | is_already_traded(condition_id) | trades ๋๋ skipped_markets์ ์์ผ๋ฉด skip (์๊ตฌ) |
| 2 | max_positions (>0์ผ ๋๋ง) | HOLDING ์ ๋๋ฌ ์ skip. ๊ธฐ๋ณธ -1(๋ฌด์ ํ) |
| 3 | CLOB get_midpoint๋ก ํ์ฌ๊ฐ ์ฌ์กฐํ | ์กฐํ ์คํจ ์ skip |
| 4 | ํ์ฌ๊ฐ > sell_threshold | rapid_jump๋ก skipped_markets์ ์๊ตฌ ๋ฑ๋ก ํ skip |
| 5 | ํ์ฌ๊ฐ < buy_threshold | skip (์๊ตฌ ์๋, ๋ค์ ์ฌ์ดํด ์ฌ๋์ ๊ฐ๋ฅ) |
| 6 | buy_shares = buy_amount_usdc รท ํ์ฌ๊ฐ < 5.0 | skip โ Polymarket ์ต์ ์ฃผ๋ฌธ 5์ฃผ |
| 7 | limit order (midpoint 0.01 tick ๋ฐ์ฌ๋ฆผ, [0.01, 0.99] clamp, BUY, GTC) | ์ฑ๊ณต ์ ์ฆ์ HOLDING ๊ธฐ๋ก, max_price = buy_price๋ก ์ด๊ธฐํ (์ฒด๊ฒฐ ํ์ธ ์์) |
์ฌ์ด์ง: buy_amount_usdc๋ USDC ๋ฌ๋ฌ ๋จ์. ์ฃผ์ = ๊ธ์ก รท ๊ฐ๊ฒฉ โ ์: $5 @ 0.80 โ 6.25์ฃผ. ๊ฐ๊ฒฉ 0.92์์๋ 5์ฃผ ์ต์์๊ฑด ๋๋ฌธ์ ์ต์ $4.6 ํ์.
๋งค๋ (๋งค ์ฌ์ดํด ๋ณด์ ํฌ์ง์
์ ์ ๊ฒ์ฌ. midpoint ์กฐํ โ max_price ๊ฐฑ์ โ P&L = (ํ์ฌ๊ฐ-๋งค์๊ฐ)/๋งค์๊ฐ):
| ์์ | ์กฐ๊ฑด | ํ๋ผ๋ฏธํฐ | config ๊ฐ | exit_reason |
|---|---|---|---|---|
| 1 | P&L โค ์์ ์ | stop_loss_percent | -0.08 | stop_loss |
| 2 | P&L โฅ ์ต์ ์ | take_profit_percent | +0.10 (์ฝ๋ ๊ธฐ๋ณธ 0.15) | take_profit |
| 3 | ํ์ฌ๊ฐ < ์ต๊ณ ๊ฐร(1-ํธ๋ ์ผ๋ง) | trailing_stop.percent | 0.05 | trailing_stop |
| 4 | ํด๊ฒฐ๊น์ง < ์ฒญ์ฐ์๊ฐ | time_based.exit_hours | 12h | time_exit |
๋งค๋ = ํ์ฌ midpoint์ ์ง์ ๊ฐ GTC SELL(๋ณด์ ์ฃผ์ ์ ๋). ์ฑ๊ณต ํ์ ์ฆ์ COMPLETED + realized_pnl = (๋งค๋๊ฐ-๋งค์๊ฐ)ร์ฃผ์ ๊ธฐ๋ก, ์๋ณ CSV(data/{job}/trades_YYYY-MM.csv) append.
์ค์ํ ์ํธ์์ฉ: max_price๊ฐ ๋งค์๊ฐ๋ก ์ด๊ธฐํ๋๋ฏ๋ก, ๊ฐ๊ฒฉ์ด ์ค๋ฅด์ง ๋ชปํ๋ฉด ํธ๋ ์ผ๋ง -5%๊ฐ ์์ -8%๋ณด๋ค ๋จผ์ ๋ฐ๋ํ๋ค. ์ฆ ์คํจ ์์ ์ ์ -5%์ด๊ณ , -8% ์์ ์ ํ ์ฌ์ดํด(3~5๋ถ) ์ฌ์ด 3%p ์ด์ ๊ฐญ ํ๋ฝํ ๋๋ง ์๋ํ๋ ๋ฐฑ์
์ด๋ค. ๋ TP +10%๋ ๋งค์๊ฐ โค 0.909์ผ ๋๋ง ๋๋ฌ ๊ฐ๋ฅ(๊ฐ๊ฒฉ ์ํ 1.0) โ 0.91~0.92 ์ง์
๊ฑด์ ์ต์ ๋ถ๊ฐ, ํธ๋ ์ผ๋ง/์๊ฐ ์ฒญ์ฐ์ผ๋ก๋ง ๋๊ฐ๋ค. ์ค์ ์์ต์ ๋ณธ๋ฅ๋ "์๋ ด ํ ํด๊ฒฐ 12h ์ time_exit์ผ๋ก 0.95+ ๋งค๋"๋ค.
--yes-only์ side ์ ํ: ๊ธฐ๋ณธ ๋ชจ๋๋ Yes/No ์ค ๋์ ์ชฝ์ favorite์ผ๋ก ์ ํํด NO ํ ํฐ๋ ๋งค์ํ๋ค(longshot ์์ฅ์ No @ 0.9 ๋ฑ, ๋ก๊ทธ์์ ๋ค์ ํ์ธ). --yes-only(Jenkins ์ด์ ๋ชจ๋)๋ ํญ์ index 0 ํ ํฐ๋ง ๋ฐํ โ NO ํ ํฐ์ ์ ๋ ๋งค์ ๋ถ๊ฐ, Yes ํ๋ฅ 75% ๋ฏธ๋ง ์์ฅ(= No๊ฐ favorite)์ ํ๋ฅ ํํฐ์์ ํ๋ฝ. negRisk ๋คํ๋ณด ์ด๋ฒคํธ์์๋ "1์ ํ๋ณด๋ง ์ฐ๋ค"๋ ์๋ฏธ๊ฐ ๋๋ค. ์ฃผ์: CLI --yes-only๋ ์ผ๊ธฐ๋ง ๊ฐ๋ฅ(๋๋ ค๋ฉด ํ๋๊ทธ ์๋ต + env/yaml false), --simulate๋ ๋์ผ ๊ตฌ์กฐ.
ํ๋ผ๋ฏธํฐ ์ดํ (์ฐ์ ์์: env > config.yaml > ์ฝ๋ ๊ธฐ๋ณธ๊ฐ)
| ํ๋ผ๋ฏธํฐ | env | yaml ํค | ์ฝ๋ ๊ธฐ๋ณธ | ํ์ฌ yaml | ์๋ฏธ |
|---|---|---|---|---|---|
| ๋งค์ ํํ | POLYBOT_BUY_THRESHOLD | trading.buy_threshold | 0.75 | 0.75 | ์ด ํ๋ฅ ์ด์๋ง ๋งค์ |
| ๋งค์ ์ํ | POLYBOT_SELL_THRESHOLD | trading.sell_threshold | 0.92 | 0.92 | ์ด ํ๋ฅ ์ด๊ณผ ์ rapid_jump ์๊ตฌ skip |
| ๋งค์ ๊ธ์ก | POLYBOT_BUY_AMOUNT | trading.buy_amount_usdc | 5.0 | 5.0 | ๊ฑด๋น USDC (๋ฌ๋ฌ ๋จ์) |
| ์ต์ ์ ๋์ฑ | POLYBOT_MIN_LIQUIDITY | trading.min_liquidity | 50000 | 10000 | $ ๊ธฐ์ค |
| ์ต๋ ํฌ์ง์ | POLYBOT_MAX_POSITIONS | trading.max_positions | -1 | -1 | -1=๋ฌด์ ํ |
| ์ต์ | POLYBOT_TAKE_PROFIT | trading.take_profit_percent | 0.15 | 0.10 | ์ง์ ๊ฐ ๋๋น |
| ์์ | POLYBOT_STOP_LOSS | trading.stop_loss_percent | -0.08 | -0.08 | ์ง์ ๊ฐ ๋๋น |
| ํธ๋ ์ผ๋ง on/off | POLYBOT_TRAILING_STOP_ENABLED | trading.trailing_stop.enabled | true | true | |
| ํธ๋ ์ผ๋ง % | POLYBOT_TRAILING_STOP_PERCENT | trading.trailing_stop.percent | 0.05 | 0.05 | ์ต๊ณ ์ ๋๋น |
| ์๊ฐํํฐ on/off | POLYBOT_TIME_BASED_ENABLED | trading.time_based.enabled | true | true | false ์ ๋งค์ ๋ก๊ทธ f-string crash ๋ฒ๊ทธ ์์ (์ฝ์ 9) |
| ์ง์ ์ต๋ ์์ฌ | POLYBOT_ENTRY_HOURS_MAX | trading.time_based.entry_hours_max | 24 | 720 | ์๊ฐ |
| ์ง์ ์ต์ ์์ฌ | POLYBOT_ENTRY_HOURS_MIN | trading.time_based.entry_hours_min | 4 | 24 | ์๊ฐ |
| ์ฒญ์ฐ ์์ฌ | POLYBOT_EXIT_HOURS | trading.time_based.exit_hours | 4 | 12 | ์๊ฐ |
| YES-Only | POLYBOT_YES_ONLY (CLI --yes-only๊ฐ ์ต์ฐ์ ) | trading.yes_only_mode | false | (์์) | |
| ์ ์ธ ์นดํ ๊ณ ๋ฆฌ | (env ์์) | trading.excluded_categories | ์คํฌ์ธ 10์ข | [] (๋นํ์ฑ) | |
| ์๋ฎฌ๋ ์ด์ | (CLI --simulate) | simulation_mode | false | false |
๋ก๊ทธ(2026-05-20) ๊ธฐ์ค ์ค์ ์ด์ ์ธ์คํด์ค๋ env override๋ก 85%~95% / ์ ๋์ฑ $20k / buy $2,000 ์กฐํฉ๋ ์ฌ์ฉ๋ ์ ์ด ์๋ค. yaml์ baseline์ผ ๋ฟ, Jenkins env๊ฐ ์ค์ ์ด์๊ฐ์ด๋ค. ํ์ credential์ POLYMARKET_PRIVATE_KEYยทPOLYMARKET_FUNDER_ADDRESS(์์ผ๋ฉด ์ฆ์ ์ข
๋ฃ, private key์ hex prefix๋ ์๋ ์ ๊ฑฐ).
time_exit์ผ๋ก ๊ทธ ๋ฆฌ์คํฌ๋ฅผ ๋ค์ ์ฌ๋์๊ฒ ๋๊ธฐ๋ฉด์ ์๋ ด๋ถ๋ง ์ทจํ๋ค.time_exit์ด ํด๊ฒฐ 12h ์ 0.95+์์ ๊ฐ์ ๋ก ์ด์ต์ ์คํ์์ผ, favorite ์น๋ฆฌ ์ 8~20% ๊ตฌ๊ฐ ์์ต์ ์ฒด๊ณ์ ์ผ๋ก ํ์ํ๋ค.orderID๋ง ๋ฐ์ผ๋ฉด ์ฆ์ HOLDING/COMPLETED๋ก ๊ธฐ๋ก. ๋ฏธ์ฒด๊ฒฐยท๋ถ๋ถ์ฒด๊ฒฐ์ด๋ฉด DB์ ์ง๊ฐ์ด ์๊ตฌํ ์ด๊ธ๋๋ค. ๋ฏธ์ฒด๊ฒฐ ์ฃผ๋ฌธ ์ทจ์/์ฌ์๋ ๋ก์ง๋ ์์.sell_threshold๋ฅผ ๋์ผ๋ฉด ์๊ตฌ skip โ ์ดํ ๊ฐ๊ฒฉ์ด ๊ตฌ๊ฐ์ผ๋ก ๋๋์์๋ ๊ธฐํ๋ฅผ ์์ ์๋๋ค (30์ผ ์ง์
์ฐฝ์์๋ ๊ณผ๋ํ๊ฒ ๋ณด์์ ).endDate๋ ์ค์ ํด๊ฒฐ ์๊ฐ์ด ์๋๋ผ ๋ง๊ฐ ์์ ์ผ์ธ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์กฐ๊ธฐ ํด๊ฒฐ์ด๋ฉด time_exit ๋ฐ๋ ๊ธฐํ ์์ฒด๊ฐ ์๊ณ , ๋ฆ์ ํด๊ฒฐ์ด๋ฉด 12h ์ ์ฒญ์ฐ์ด ๋ฌด์๋ฏธํ๊ฒ ์ผ์ฐ ๋๊ฐ๋ค. no_end_date ์์ฅ์ ์ ๋ถ ๋ฐฐ์ .max_positions=-1 + ๊ณ ์ ๊ธ์ก โ ํ๋ณด 100๊ฐ๋ฉด 100๊ฑด ๋งค์. ์ด๋ฒคํธ ์๊ด๊ด๊ณ ์ ์ด๊ฐ ์์ด ๊ฐ์ ์ ๊ฑฐ์ "A ๋น์ Yes"์ "B ๋น์ No"๋ฅผ ๋์ ๋งค์(2026-05-20 ๋ก๊ทธ์ Texas primary ์ฌ๋ก)ํด ๋์ผ ๋ฆฌ์คํฌ๋ฅผ 2๋ฐฐ๋ก ์ง๋ค.time_based.enabled=false๋ฉด ๋งค์ crash: ๋งค์ ๋ก๊ทธ f-string์ด None์ .1f ํฌ๋งท์ ์ ์ฉํด TypeError โ ์ฌ์ดํด ์ ์ฒด ์คํจ. ํ๋ฅ -only ๋ชจ๋๋ ํ์ฌ ์ฝ๋๋ก ๋์ ๋ถ๊ฐ.no such column: trades.max_price ์ฌ์ดํด ์คํจ ๊ธฐ๋ก).excluded_categories: []๋ก ์์ ๊บผ์ ธ ์๊ณ , ์ค์ NHL/์๋์ปต/ํ
๋์ค ์์ฅ์ ์ค์บยท๋งค์ ์ค (2026-05-20 ๋ก๊ทธ: French Open No ๋งค์).โ ์๋ฎฌ๋ ์ด์
: --simulate / yaml simulation_mode / scripts/simulate.py 3๊ฒฝ๋ก. DB๊ฐ trades_sim.db๋ก ๋ถ๋ฆฌ๋๊ณ ์ฃผ๋ฌธ๋ง ๊ฐ์ง ์ฑ๊ณต(SIM_BUY_xxxx)์ผ๋ก ๋ฆฌํดํ๋, ๊ฐ๊ฒฉ ์กฐํ(midpoint)๋ ์ค์ API๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์๋ฎฌ๋ ์ด์
์๋ ์คํค(.env)๊ฐ ํ์ํ๋ค. โก ์์ก ์ค๊ฑฐ๋: yaml buy_amount_usdc: 5.0. โข ํ๋ผ๋ฏธํฐ ๋ณํ ํ ์ฆ์ก: Jenkins env override๋ก ๋ณํ ์ด์(์ค๋ก๊ทธ ๊ธฐ์ค 85~95% / ์ ๋์ฑ $20k / ๊ฑด๋น $2,000 ์กฐํฉ ์ด๋ ฅ). ํ๋จ ๊ธฐ์ค: ์๋ณ CSV์ ์์ด๋ hours_until_resolution_at_buyยทentry_reason์ผ๋ก ์์ฌ์๊ฐ ๋ฒํท๋ณ(24-72h / 72-240h / 240-720h) ์น๋ฅ ยท์์ต์ ์ง๊ณํด ์ง์
์ฐฝ์ ์ฐ์ด์ ๋ฐ์ดํฐ๋ก ๊ฒ์ฆํ๋ค.
# Jenkins cron (3~5๋ถ ์ฃผ๊ธฐ, one-shot โ ๋ด ๋ด๋ถ ๋ฃจํ ์์)
cd ./golden-cherry
export POLYMARKET_PRIVATE_KEY=<Jenkins credential>
export POLYMARKET_FUNDER_ADDRESS=<Jenkins credential>
uv sync --frozen
uv run polybot run --yes-only # ์ด์ ๋ชจ๋: Yes(index 0) ํ ํฐ๋ง ๋งค์
# ์ํ๋ data/{job}/trades.db, ์๋ณ CSV๋ data/{job}/trades_YYYY-MM.csv