Iran / war / oil prediction agent + Polymarket auto-trading orchestration
Taco is a pipeline that collects and analyzes geopolitical and market signals to produce positions (BUY_YES / BUY_NO / HOLD) on Polymarket prediction markets and executes orders via a NestJS trading server.
| Challenge | Taco approach |
|---|---|
| Manually combining news, satellite, oil, etc. | LangGraph agent: collect → aggregate → LLM analyze → position calculation |
| Manual Polymarket orders | 12 traders (trader01–trader12) mapped to markets; batch send via execute-order |
| Slow reaction to market moves | Trigger on reference values (brent, polymarket prob); re-run agent and refresh orders |
| Stop-loss / take-profit | net_pct-based stop/take lines; auto-send opposite position (close) when hit |
┌─────────────────────────────────────────────────────────────────────────────┐
│ predict-win-rate (Python) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ News/Sat/ │ │ Aggregate │ │ LLM │ │ notify │ │
│ │ Oil/Geo │ → │ WarRiskScore│ → │ (GPT-4o) │ → │ market_predict │ │
│ │ collect │ │ │ │ JSON pos’n │ │ + send_orders │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └────────┬────────┘ │
│ ↑ ↑ ↑ │ │
│ RSS, Sentinel, aggregator.py analyze_node webhook.py │
│ oil_signals, country_strike (Claude-style TRADER_MARKET│
│ market_signals oil_predictor prompt) _MAP │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ POST /polymarket/execute-order
│ { traderId, recommendation }
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ polymarket-trader-main (NestJS) │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ PolymarketService.executeOrder(traderId, recommendation) │ │
│ │ • CONSTANTS.POLYMARKET_MARKET[traderId] → slug, tokenIdYes/No │ │
│ │ • BUY_YES: close NO, then buy YES at market (FAK) │ │
│ │ • BUY_NO: close YES, then buy NO at market (FAK) │ │
│ │ • HOLD: close all positions │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ CLOB Client (Polygon), MongoDB (trade history), Swagger /docs │
└─────────────────────────────────────────────────────────────────────────────┘
- Workspace: All signal collection, analysis, and position calculation runs here.
- Agent cycle: Each run collects news (RSS), satellite (Sentinel), oil (WTI/COT/EIA), geo/military (ADS-B, vessels, quakes), Polymarket prices in parallel, then aggregate → LLM → per-question positions → trading server and dashboard.
- Config: Single “Iran / war / oil” setup:
config/settings,TRADER_MARKET_MAP. - Persistence: Order state lives in polymarket-trader MongoDB and
trade-pnlAPI; predict-win-rate talks viaorder_url(execute-order) and trade-pnl (net_pct for stop/take). - Convoy: One cycle’s 12 market recommendations =
market_predictions; each trader gets one recommendation per cycle.
- Role: APIs for market data, balance, order execution, trade history, PnL on Polymarket CLOB.
- Traders (trader01–trader12): Each has its own wallet (EOA or Safe) and one Polymarket market (
POLYMARKET_MARKETinconstants.ts). - execute-order:
POST /polymarket/execute-orderwith{ traderId, recommendation }runs BUY_YES/BUY_NO/HOLD logic (FAK market orders) for that trader’s market. - trade-pnl:
GET /polymarket/trade-pnl/:traderIdreturns PnL time series; predict-win-rate uses it for net_pct and sends opposite position on stop/take.
| predict-win-rate | polymarket-trader-main |
|---|---|
order_url → POST .../execute-order |
executeOrder(dto) → CLOB order execution |
send_orders(market_predictions) |
Receives per-trader recommendation |
TRADER_MARKET_MAP (trader → market_predictions path) |
POLYMARKET_MARKET (trader → slug, tokenIdYes/No) |
| Fetches net_pct from trade-pnl URL → sends opposite position on stop/take | Serves trade-pnl API |
The agent is a LangGraph state graph; one cycle runs in this order.
- News: RSS multi-source → topic and severity labels.
- Satellite: Sentinel Hub → Iran nuclear/military site change scores.
- Oil: WTI, COT, EIA, OPEC schedule, etc.
- Geo/military: ADS-B/OpenSky military flights, Hormuz vessels, quakes, GDELT.
- Market: Yahoo Finance (brent, gold, VIX), Polymarket Iran war probability (polymarket_prob).
- WarEscalationScore = 0.35×military + 0.25×naval + 0.20×satellite + 0.20×news, with momentum adjustment.
- Country strike probabilities (country_strike): news and geo bonuses.
- Diplomatic score: used for ceasefire/negotiation-related positions.
- LLM (GPT-4o) gets aggregated summary, headlines, geo, oil; returns fixed JSON:
war_probability,trend,hotspots,recommendation(BUY_YES/BUY_NO/HOLD)- Per-question
market_predictions(us_enter_iran_march14, us_enter_iran_march31, …).
- build_all_market_predictions: Combines LLM output, country_strike, oil targets (WTI $100/$110/$120/$150, $80 LOW), ceasefire question → position / probability / confidence / reasoning for every Polymarket question.
- EV filter (weatherbot-style): When
polymarket_prob(market YES price) is present, applies min edge and entry thresholds; otherwise flips to HOLD.
- send_orders(market_predictions): For trader01–trader12, reads the corresponding
market_predictionsentry fromTRADER_MARKET_MAPand POSTs toorder_url(execute-order) only when the position changed. - Stop/take: Fetches each trader’s net_pct from trade-pnl; when past the configured lines, sends opposite position once (close) and applies cooldown.
- Dashboard: If
dashboard_urlis set, POSTs the same payload as signal. - Trading server: If
trading_server_urlis set, POSTs payload only when overall recommendation or probability meaningfully changed (avoids HOLD spam).
- Scheduled run: Every
SCHEDULER_INTERVAL_MINUTES(default 1; recommend 15 for production), one agent cycle → 12 recommendations → execute-order. - Reference trigger: Every
RECOMMENDATION_TRIGGER_INTERVAL_MINUTES(default 1), only brent and polymarket_prob are fetched. If change vs previous exceeds RECOMMENDATION_TRIGGER_CHANGE_PCT (default 2%), run the agent again and send new recommendations to execute-order. Cooldown after trigger prevents repeated runs.
See predict-win-rate/docs/agent-schedule-and-trigger.md for details.
| traderId | predict-win-rate (TRADER_MARKET_MAP) | polymarket-trader (POLYMARKET_MARKET slug) |
|---|---|---|
| trader01 | us_forces_enter_iran_march31 | us-forces-enter-iran-by-march-31 |
| trader02 | us_forces_enter_iran_dec31 | us-forces-enter-iran-by-december-31-... |
| trader03 | us_iran_ceasefire_march31 | us-x-iran-ceasefire-by-march-31 |
| trader04 | country_strike_by_march31 → uae | will-uae-strike-iran-by-march-31 |
| trader05 | country_strike_by_march31 → saudi_arabia | will-saudi-arabia-strike-iran-by-march-31 |
| trader06 | another_country_strike_iran_march31 | will-another-country-strike-iran-by-march-31-833 |
| trader07–10 | wti_reach_100/110/120/150_march31 | Will Crude Oil (CL) hit (HIGH) $100/$110/$120/$150 by end of March |
| trader11 | wti_low_80_march31 | Will Crude Oil (CL) hit (LOW) $80 by end of March |
| trader12 | us_invade_iran_march31 | will-the-us-invade-iran-by-march-31 |
When changing mapping, update both predict-win-rate signals/market_predictions.py (TRADER_MARKET_MAP) and polymarket-trader-main src/common/config/constants.ts (POLYMARKET_MARKET).
- predict-win-rate: Python 3.10+, OpenAI API key, Sentinel Hub credentials; optional: RapidAPI, OpenSky, etc.
- polymarket-trader-main: Node 18+, MongoDB, Polymarket Builder API and wallet keys, Polygon RPC.
cd predict-win-rate
pip install -r requirements.txt # or install from pyproject.toml
cp .env.example .env # set OpenAI, Sentinel, ORDER_URL, etc.
python main.py # start scheduler and triggersORDER_URL: Full execute-order URL of polymarket-trader (e.g.http://localhost:8080/polymarket/execute-order).TRADING_SERVER_URL: Optional server for aggregate signals.DASHBOARD_URL: Optional dashboard signal URL.
cd polymarket-trader-main
npm install
cp .env.example .env # MONGODB_*, POLYMARKET_*, POLYGON_RPC, TRADER01_KEY, etc.
npm run start:dev # default port 8080- API docs: After starting, open
http://localhost:8080/docs(Swagger). - execute-order:
POST /polymarket/execute-orderwith body{ "traderId": "trader01", "recommendation": "BUY_YES" }.
- Start polymarket-trader-main so execute-order and trade-pnl are available.
- Set
ORDER_URLin predict-win-rate.env, then runpython main.py.
| Setting | Default | Description |
|---|---|---|
SCHEDULER_INTERVAL_MINUTES |
1 | Agent run interval (minutes). Recommend 15 for production. |
RECOMMENDATION_TRIGGER_INTERVAL_MINUTES |
1 | Reference (brent, polymarket) check interval (minutes). 0 = disabled. |
RECOMMENDATION_TRIGGER_CHANGE_PCT |
2.0 | Trigger when brent (%) or polymarket (%p) change exceeds this. |
ORDER_URL |
(none) | Full execute-order URL. |
ORDER_ENABLED |
true | Set false to disable order sending. |
siege_stop_loss_pct / siege_take_profit_pct |
10 | Per-trader net_pct stop/take (%); then send opposite position. |
confidence_threshold |
0.40 | BUY_YES/BUY_NO only when confidence ≥ this; else HOLD. |
polymarket_min_edge, polymarket_entry_max_yes, etc. |
(see settings) | EV filter and entry thresholds. |
- Orders not sent: Check
ORDER_URL, that polymarket-trader is running, andORDER_ENABLED=true. Look for “주문 전송” / “주문 전송 실패” in logs. - Trader–market mismatch: Ensure
TRADER_MARKET_MAPandPOLYMARKET_MARKETslugs/questions refer to the same markets. - Stop/take not firing: Verify trade-pnl URL is derived from
order_url(.../execute-order→.../trade-pnl) and that the endpoint returns 200 with PnL data. - Agent runs but orders skipped:
send_ordersonly sends when the recommendation changed for that trader. Repeated HOLD is skipped; log shows “주문 스킵 (변화 없음)”.
See each subproject (polymarket-trader-main, predict-win-rate) for its license.