|
| 1 | +""" |
| 2 | +Paper trading v3 — demonstrates the full pipeline end-to-end. |
| 3 | +Relaxes the restricted filter for demo purposes since Polymarket |
| 4 | +geofences most events. |
| 5 | +""" |
| 6 | + |
| 7 | +import logging |
| 8 | +import sys |
| 9 | +import os |
| 10 | + |
| 11 | +logging.basicConfig( |
| 12 | + level=logging.INFO, |
| 13 | + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", |
| 14 | + handlers=[logging.StreamHandler(sys.stdout)], |
| 15 | +) |
| 16 | +logger = logging.getLogger("paper-trading") |
| 17 | + |
| 18 | +sys.path.insert(0, os.path.dirname(__file__)) |
| 19 | + |
| 20 | +from agents.polymarket.gamma import GammaMarketClient |
| 21 | +from agents.polymarket.polymarket import Polymarket |
| 22 | +from agents.utils.objects import SimpleMarket, SimpleEvent |
| 23 | + |
| 24 | +STEP_SEP = "=" * 70 |
| 25 | + |
| 26 | + |
| 27 | +def step(n, title): |
| 28 | + logger.info("\n%s\nSTEP %d: %s\n%s", STEP_SEP, n, title, STEP_SEP) |
| 29 | + |
| 30 | + |
| 31 | +def main(): |
| 32 | + logger.info("Starting paper trading run v3...\n") |
| 33 | + |
| 34 | + gamma = GammaMarketClient() |
| 35 | + poly = Polymarket() |
| 36 | + |
| 37 | + # ── Step 1: Fetch and filter events (relaxed for paper trading) ──── |
| 38 | + step(1, "Fetching active events from Gamma API") |
| 39 | + |
| 40 | + raw_events = gamma.get_current_events(limit=100) |
| 41 | + logger.info("✅ Fetched %d active events from Gamma API", len(raw_events)) |
| 42 | + |
| 43 | + # Parse through Polymarket model |
| 44 | + all_events = [] |
| 45 | + for e in raw_events: |
| 46 | + try: |
| 47 | + ed = poly.map_api_to_event(e) |
| 48 | + all_events.append(SimpleEvent(**ed)) |
| 49 | + except Exception as ex: |
| 50 | + logger.debug("Skipping event: %s", ex) |
| 51 | + |
| 52 | + # Relaxed filter: active + not closed + not archived (skip restricted for demo) |
| 53 | + paper_tradeable = [ |
| 54 | + e for e in all_events |
| 55 | + if e.active and not e.closed and not e.archived |
| 56 | + ] |
| 57 | + logger.info("Events passing paper-trade filter: %d", len(paper_tradeable)) |
| 58 | + |
| 59 | + if paper_tradeable: |
| 60 | + logger.info("\nTop tradeable events:") |
| 61 | + for e in paper_tradeable[:10]: |
| 62 | + n_markets = len(e.markets.split(",")) if e.markets else 0 |
| 63 | + logger.info( |
| 64 | + " - [%d] %s (%d markets, volume=%.0f)", |
| 65 | + e.id, |
| 66 | + e.title[:65], |
| 67 | + n_markets, |
| 68 | + 0, # SimpleEvent doesn't have volume |
| 69 | + ) |
| 70 | + |
| 71 | + # ── Step 2: Fetch market details for top events ──────────────────── |
| 72 | + step(2, "Fetching market details for top events") |
| 73 | + |
| 74 | + markets_data = [] |
| 75 | + for event in paper_tradeable[:5]: |
| 76 | + if not event.markets: |
| 77 | + continue |
| 78 | + market_ids = event.markets.split(",")[:2] # Max 2 per event |
| 79 | + for mid in market_ids: |
| 80 | + mid = mid.strip() |
| 81 | + if not mid: |
| 82 | + continue |
| 83 | + try: |
| 84 | + raw = gamma.get_market(mid) |
| 85 | + m = poly.map_api_to_market(raw) |
| 86 | + markets_data.append(m) |
| 87 | + except Exception as ex: |
| 88 | + logger.debug("Skipping market %s: %s", mid, ex) |
| 89 | + |
| 90 | + logger.info("✅ Fetched %d market details", len(markets_data)) |
| 91 | + |
| 92 | + # ── Step 3: Display market analysis ──────────────────────────────── |
| 93 | + step(3, "Market analysis (what the agent evaluates)") |
| 94 | + |
| 95 | + for i, m in enumerate(markets_data[:10]): |
| 96 | + question = m.get("question", "N/A") |
| 97 | + outcomes = m.get("outcomes", "N/A") |
| 98 | + prices = m.get("outcome_prices", "N/A") |
| 99 | + active = m.get("active", False) |
| 100 | + funded = m.get("funded", False) |
| 101 | + spread = m.get("spread", "N/A") |
| 102 | + |
| 103 | + # Parse outcome prices for analysis |
| 104 | + try: |
| 105 | + price_list = eval(prices) if isinstance(prices, str) else prices |
| 106 | + yes_price = float(price_list[0]) if price_list else 0 |
| 107 | + no_price = float(price_list[1]) if len(price_list) > 1 else 0 |
| 108 | + except: |
| 109 | + yes_price = 0 |
| 110 | + no_price = 0 |
| 111 | + |
| 112 | + # Simple edge detection |
| 113 | + logger.info( |
| 114 | + "\n 📊 Market %d: %s\n" |
| 115 | + " Outcomes: %s\n" |
| 116 | + " Current prices: Yes=%.3f (%.1f%%) | No=%.3f (%.1f%%)\n" |
| 117 | + " Active: %s | Funded: %s | Spread: %s\n" |
| 118 | + " 💡 Agent would run Superforecaster prompt to assess if " |
| 119 | + "the market is mispriced vs its base rate", |
| 120 | + i + 1, |
| 121 | + question[:80], |
| 122 | + outcomes, |
| 123 | + yes_price, yes_price * 100, |
| 124 | + no_price, no_price * 100, |
| 125 | + active, funded, spread, |
| 126 | + ) |
| 127 | + |
| 128 | + # ── Step 4: What a paper trade would look like ───────────────────── |
| 129 | + step(4, "Paper trade example (simulated)") |
| 130 | + |
| 131 | + if markets_data: |
| 132 | + m = markets_data[0] |
| 133 | + question = m.get("question", "N/A") |
| 134 | + prices = m.get("outcome_prices", "['0.5','0.5']") |
| 135 | + |
| 136 | + try: |
| 137 | + price_list = eval(prices) if isinstance(prices, str) else prices |
| 138 | + yes_price = float(price_list[0]) |
| 139 | + except: |
| 140 | + yes_price = 0.5 |
| 141 | + |
| 142 | + # Simulate what the agent would decide |
| 143 | + simulated_prediction = 0.6 # Agent thinks 60% chance of Yes |
| 144 | + edge = simulated_prediction - yes_price |
| 145 | + |
| 146 | + if edge > 0.05: |
| 147 | + action = "BUY" |
| 148 | + size = min(0.1, edge) # Scale size by edge |
| 149 | + reasoning = f"Market underprices YES at {yes_price:.1%}, agent predicts {simulated_prediction:.0%}" |
| 150 | + elif edge < -0.05: |
| 151 | + action = "SELL" |
| 152 | + size = min(0.1, abs(edge)) |
| 153 | + reasoning = f"Market overprices YES at {yes_price:.1%}, agent predicts {simulated_prediction:.0%}" |
| 154 | + else: |
| 155 | + action = "HOLD" |
| 156 | + size = 0 |
| 157 | + reasoning = f"Market fairly priced at {yes_price:.1%}, agent predicts {simulated_prediction:.0%}" |
| 158 | + |
| 159 | + logger.info( |
| 160 | + "\n 🎯 Simulated paper trade:\n" |
| 161 | + " Market: %s\n" |
| 162 | + " Current Yes price: %.3f (%.1f%%)\n" |
| 163 | + " Agent prediction: %.0f%%\n" |
| 164 | + " Edge: %+.1f%%\n" |
| 165 | + " Action: %s\n" |
| 166 | + " Size: %.1f%% of portfolio\n" |
| 167 | + " Reasoning: %s\n" |
| 168 | + "\n 🔒 This is a SIMULATION — no real trade executed.", |
| 169 | + question[:80], |
| 170 | + yes_price, yes_price * 100, |
| 171 | + simulated_prediction * 100, |
| 172 | + edge * 100, |
| 173 | + action, |
| 174 | + size * 100, |
| 175 | + reasoning, |
| 176 | + ) |
| 177 | + |
| 178 | + # ── Step 5: Pipeline status ──────────────────────────────────────── |
| 179 | + step(5, "Pipeline readiness") |
| 180 | + |
| 181 | + openai_key = os.getenv("OPENAI_API_KEY") |
| 182 | + poly_key = os.getenv("POLYGON_WALLET_PRIVATE_KEY") |
| 183 | + |
| 184 | + logger.info( |
| 185 | + "\n Environment:\n" |
| 186 | + " OPENAI_API_KEY: %s\n" |
| 187 | + " POLYGON_WALLET_PRIVATE_KEY: %s\n" |
| 188 | + "\n Pipeline stages completed:\n" |
| 189 | + " ✅ Step 1: Fetch events from Gamma API\n" |
| 190 | + " ✅ Step 2: Fetch market details\n" |
| 191 | + " ✅ Step 3: Display market analysis\n" |
| 192 | + " ✅ Step 4: Simulate paper trade\n" |
| 193 | + "\n Pipeline stages requiring OPENAI_API_KEY:\n" |
| 194 | + " %s RAG filter events (ChromaDB + embeddings)\n" |
| 195 | + " %s RAG filter markets\n" |
| 196 | + " %s Superforecaster LLM prediction\n" |
| 197 | + " %s Trade decision generation\n" |
| 198 | + "\n Pipeline stages requiring POLYGON_WALLET_PRIVATE_KEY:\n" |
| 199 | + " %s USDC balance check\n" |
| 200 | + " %s Order signing and execution\n" |
| 201 | + "\n Trade execution: 🔒 DISABLED (paper trading mode)", |
| 202 | + "✅ Set" if openai_key else "❌ Not set", |
| 203 | + "✅ Set" if poly_key else "❌ Not set", |
| 204 | + "✅" if openai_key else "⏭️", |
| 205 | + "✅" if openai_key else "⏭️", |
| 206 | + "✅" if openai_key else "⏭️", |
| 207 | + "✅" if openai_key else "⏭️", |
| 208 | + "✅" if poly_key else "⏭️", |
| 209 | + "✅" if poly_key else "⏭️", |
| 210 | + ) |
| 211 | + |
| 212 | + logger.info("\n%s\nPaper trading run complete. 0 real trades. 0 USDC risked.\n%s\n", STEP_SEP, STEP_SEP) |
| 213 | + |
| 214 | + |
| 215 | +if __name__ == "__main__": |
| 216 | + main() |
0 commit comments