Step-by-step tutorial to build a prediction market trading bot in Python. Screen markets, get AI analysis, generate signals with Kelly sizing, and track performance.
Create an authenticated client that you will reuse for every call. Set a generous timeout because analysis calls run a multi-stage research pipeline that takes 30-90 seconds.
import httpxAPI_KEY = "YOUR_API_KEY" # Replace with your key from https://rekko.ai/dashboardclient = httpx.Client( base_url="https://api.rekko.ai/v1", headers={"Authorization": f"Bearer {API_KEY}"}, timeout=120.0, # Analysis can take 30-90 seconds)# Verify the key worksme = client.get("/customers/me").json()print(f"Authenticated as {me['email']} ({me['plan']} plan)")
2
Screen markets for opportunities
The screening endpoint scores markets across a platform and returns the top candidates ranked by a composite of volume, price movement, and edge potential. Use it to find markets worth analyzing.
Quick recommendation: "ANALYZE", "WATCH", or "SKIP"
Filter by category to focus on a specific domain:
Python
# Screen only politics markets on Polymarketpolitics = client.post("/screen", json={ "platform": "polymarket", "category": "politics", "min_volume_24h": 5000, "min_score": 40, "limit": 10,}).json()
3
Get AI analysis on promising markets
Analysis runs a multi-stage research pipeline that takes 30-90 seconds. The pattern is: trigger, poll, retrieve.
import time# Pick the top-scored marketmarket = screen[0]platform = market["platform"]market_id = market["market_id"]# 1. Trigger analysisresp = client.post(f"/markets/{platform}/{market_id}/analyze")trigger = resp.json()analysis_id = trigger["analysis_id"]print(f"Analysis started: {analysis_id}")print(f"Estimated time: {trigger['estimated_seconds']}s")# 2. Poll until completewhile True: status = client.get( f"/markets/{platform}/{market_id}/analyze/{analysis_id}/status" ).json() if status["status"] == "complete": print("Analysis complete.") break if status["status"] == "error": raise Exception(status.get("error_message", "Analysis failed")) time.sleep(5)# 3. Retrieve the full analysisanalysis = client.get(f"/markets/{platform}/{market_id}/analysis").json()print(f"\n--- {analysis['title']} ---")print(f"Probability: {analysis['probability']:.0%}")print(f"Confidence: {analysis['confidence']:.0%}")print(f"Edge: {analysis['edge']:+.2f}")print(f"Risk: {analysis['risk_rating']}")print(f"Recommendation: {analysis['recommendation']}")print(f"Sources: {analysis['source_count']} analyzed")print(f"\nSummary: {analysis['summary']}")
Add ?expand=causal,scenarios to the retrieve call to get causal factor decomposition and bull/base/bear scenarios. See Expand Parameter.
4
Generate a trading signal
A signal wraps analysis into an actionable recommendation with Kelly criterion position sizing. Use ?wait=true for a synchronous response instead of the trigger/poll pattern.
signal = client.post("/signals", params={"wait": "true"}, json={ "market": f"{platform}/{market_id}", "risk_limit": "medium",}).json()print(f"Recommendation: {signal['recommendation']}")print(f"Target price: {signal['target_price']:.2f}")print(f"Edge: {signal['edge']:+.2f}")print(f"Confidence: {signal['confidence']:.0%}")print(f"Risk rating: {signal['risk_rating']}")print(f"Kelly fraction: {signal['size_pct']:.1%}")print(f"Time horizon: {signal['time_horizon']}")# Calculate dollar size on a $10,000 bankrollbankroll = 10_000position_size = signal["size_pct"] * bankrollprint(f"\nPosition size: ${position_size:.0f} (on ${bankroll:,} bankroll)")
Kelly-derived position size as fraction of bankroll
time_horizon
string
"hours", "days", or "weeks"
hedge
string
Suggested hedge position (if applicable)
expires_at
string
ISO 8601 timestamp — do not trade after this time
Always check expires_at before acting on a signal. Market conditions change and stale signals can lead to losses.
5
Execute the trade (paper trading)
Rekko provides intelligence — execution happens on the platform. Start with shadow trading (paper trades) to validate your strategy before risking real capital.
Shadow trades track the market price at entry, simulate fills, and resolve automatically when the market settles. They cost nothing and use the same tracking infrastructure as live trades.
Rekko does not execute live trades on your behalf. Use the execution guidance endpoint to get platform-specific order parameters, then place the order using your platform’s API:
Python
# Get execution guidance (includes platform-specific order params)guidance = client.post("/strategy/execution-guidance", json={ "market": f"{platform}/{market_id}", "side": side, "size_usd": size_usd,}).json()print(f"Limit price: {guidance['limit_price']}")print(f"Order type: {guidance['order_type']}")print(f"Platform URL: {guidance['platform_url']}")# Then place the order using the platform's own API# See: https://trading-api.readme.io (Kalshi)# See: https://docs.polymarket.com (Polymarket)
6
Track performance
Monitor your shadow portfolio to see how the strategy performs over time before committing real capital.
Here is the full bot as a single copy-paste-ready Python script. It screens markets, analyzes the top candidate, generates a signal, and places a shadow trade.
Python
#!/usr/bin/env python3"""Prediction market trading bot using the Rekko AI API.Screens markets for high-edge opportunities, runs AI analysis,generates Kelly-sized trading signals, and paper trades via shadow mode.Usage: pip install httpx export REKKO_API_KEY="your_key_here" python trading_bot.py"""import osimport sysimport timeimport httpxAPI_KEY = os.environ.get("REKKO_API_KEY")if not API_KEY: print("Set REKKO_API_KEY environment variable first.") print("Sign up free at https://rekko.ai/dashboard") sys.exit(1)BANKROLL = 10_000 # Starting bankroll in dollarsMIN_SCORE = 50 # Minimum screening score to analyzeMIN_EDGE = 0.05 # Minimum edge (5%) to tradePLATFORM = "kalshi" # "kalshi" or "polymarket"client = httpx.Client( base_url="https://api.rekko.ai/v1", headers={"Authorization": f"Bearer {API_KEY}"}, timeout=120.0,)def screen_markets() -> list[dict]: """Screen markets and return candidates sorted by score.""" results = client.post("/screen", json={ "platform": PLATFORM, "min_volume_24h": 10000, "min_score": MIN_SCORE, "limit": 10, }).json() print(f"Screened {len(results)} markets above score {MIN_SCORE}") return resultsdef analyze_market(platform: str, market_id: str) -> dict: """Trigger analysis, poll until complete, and return the result.""" resp = client.post(f"/markets/{platform}/{market_id}/analyze") analysis_id = resp.json()["analysis_id"] print(f" Analysis started: {analysis_id}") while True: status = client.get( f"/markets/{platform}/{market_id}/analyze/{analysis_id}/status" ).json() if status["status"] == "complete": break if status["status"] == "error": raise Exception(status.get("error_message", "Analysis failed")) time.sleep(5) return client.get(f"/markets/{platform}/{market_id}/analysis").json()def get_signal(platform: str, market_id: str) -> dict: """Generate a trading signal with Kelly position sizing.""" return client.post("/signals", params={"wait": "true"}, json={ "market": f"{platform}/{market_id}", "risk_limit": "medium", }).json()def place_shadow_trade(platform: str, market_id: str, side: str, size_usd: float) -> dict: """Place a paper trade via shadow trading.""" return client.post("/trades/shadow", json={ "ticker": market_id, "platform": platform, "side": side, "size_usd": round(size_usd, 2), }).json()def show_portfolio(): """Print current shadow portfolio summary.""" portfolio = client.get("/portfolio", params={"mode": "shadow"}).json() print(f"\n--- Portfolio ---") print(f"Open: {portfolio['open_count']} | Closed: {portfolio['closed_count']}") print(f"Win rate: {portfolio['win_rate']:.0%} | P&L: ${portfolio['total_pnl']:+.2f}")def main(): print(f"Screening {PLATFORM} markets...\n") candidates = screen_markets() if not candidates: print("No markets above threshold. Try lowering MIN_SCORE.") return for candidate in candidates[:3]: # Analyze top 3 market_id = candidate["market_id"] platform = candidate["platform"] print(f"\n{'='*60}") print(f"Analyzing: {candidate['title'][:55]}") print(f"Ticker: {market_id} | Score: {candidate['score']}") print(f"{'='*60}") analysis = analyze_market(platform, market_id) print(f" Probability: {analysis['probability']:.0%}") print(f" Edge: {analysis['edge']:+.2f}") print(f" Risk: {analysis['risk_rating']}") if abs(analysis["edge"]) < MIN_EDGE: print(f" Edge below {MIN_EDGE:.0%} threshold — skipping.") continue signal = get_signal(platform, market_id) print(f" Signal: {signal['recommendation']}") print(f" Kelly size: {signal['size_pct']:.1%} (${signal['size_pct'] * BANKROLL:.0f})") if signal["recommendation"] == "NO_TRADE": print(" No trade recommended.") continue side = "yes" if "YES" in signal["recommendation"] else "no" size_usd = signal["size_pct"] * BANKROLL trade = place_shadow_trade(platform, market_id, side, size_usd) print(f" Shadow trade placed: {trade['trade_id']}") print(f" {side.upper()} @ ${size_usd:.2f}") show_portfolio()if __name__ == "__main__": main()
When you have multiple open positions, standard signals do not account for correlation. The portfolio signal endpoint considers your existing holdings and adjusts sizing to avoid concentration:
Python
signal = client.post("/signals/portfolio", params={"wait": "true"}, json={ "market": f"{platform}/{market_id}", "risk_limit": "medium", "positions": portfolio["positions"], # Pass your current holdings "bankroll": BANKROLL,}).json()print(f"Adjusted size: {signal['size_pct']:.1%}") # May be smaller to reduce correlationif signal.get("concentration_warning"): print(f"Warning: {signal['concentration_warning']}")
A single screen-analyze-signal-trade cycle costs approximately 2.30∗∗ontheProplan(0.10 screen + 0.10trigger+0.10 retrieve + 2.00signal).Screeningandanalysisalone(withoutsignalgeneration)costs∗∗0.30.
The Free plan includes 10 INSIGHT calls per month. For automated bots that run frequently, upgrade to Pro for 500 INSIGHT calls and 50 STRATEGY calls.