| |
| |
| |
| |
|
|
| import os, time, json, requests |
| from polymarket_client import get_client |
| from datetime import datetime, timezone |
|
|
| DRY_RUN = True |
| MIN_PROFIT_USD = float(os.getenv("MIN_PROFIT_USD", "0.10")) |
| SCAN_INTERVAL = 30 |
|
|
| TELEGRAM_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") |
| TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID") |
|
|
| def send_alert(msg: str): |
| if not TELEGRAM_TOKEN or not TELEGRAM_CHAT_ID: |
| return |
| try: |
| url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage" |
| requests.post(url, json={"chat_id": TELEGRAM_CHAT_ID, "text": msg}, timeout=10) |
| except Exception as e: |
| print(f"[Arbitrage] Telegram alert failed: {e}", flush=True) |
|
|
| def get_all_binary_markets(client): |
| try: |
| result = client.get_markets() |
| if isinstance(result, dict): |
| markets = result.get("data", []) |
| else: |
| markets = result |
| return [m for m in markets if m.get("outcomes") == ["Yes", "No"]] |
| except Exception as e: |
| print(f"[Arbitrage] Failed to fetch markets: {e}", flush=True) |
| return [] |
|
|
| def check_market_arbitrage(client, market, max_bet): |
| try: |
| tokens = market.get("tokens", []) |
| if len(tokens) != 2: |
| return None |
| yes_token = next((t for t in tokens if t.get("outcome") == "Yes"), None) |
| no_token = next((t for t in tokens if t.get("outcome") == "No"), None) |
| if not yes_token or not no_token: |
| return None |
| yes_book = client.get_order_book(yes_token["token_id"]) |
| no_book = client.get_order_book(no_token["token_id"]) |
| yes_asks = yes_book.get("asks", []) if isinstance(yes_book, dict) else [] |
| no_asks = no_book.get("asks", []) if isinstance(no_book, dict) else [] |
| if not yes_asks or not no_asks: |
| return None |
| yes_ask = float(yes_asks[0]["price"]) |
| no_ask = float(no_asks[0]["price"]) |
| total = yes_ask + no_ask |
| if total < 1.0: |
| profit_per_unit = round(1.0 - total, 4) |
| profit_on_max = round(profit_per_unit * max_bet, 4) |
| return { |
| "market_slug": market.get("slug", market.get("id")), |
| "yes_ask": yes_ask, |
| "no_ask": no_ask, |
| "total_cost": round(total, 4), |
| "profit_per_unit": profit_per_unit, |
| "profit_on_max_bet": profit_on_max, |
| "token_id_yes": yes_token["token_id"], |
| "token_id_no": no_token["token_id"], |
| "max_bet_used": max_bet |
| } |
| except: |
| pass |
| return None |
|
|
| def create_virtual_arbitrage_position(opp): |
| try: |
| from dry_run_simulator import create_position, is_simulate_active |
| if not is_simulate_active(): |
| return |
| create_position( |
| market=opp["market_slug"], |
| action="arbitrage", |
| confidence=1.0, |
| entry_price=opp["total_cost"], |
| signal_summary=f"Arbitrage: YES+NO={opp['total_cost']}", |
| strategy="arbitrage" |
| ) |
| print(f"[Arbitrage] Virtual arbitrage position created for {opp['market_slug']}", flush=True) |
| except Exception as e: |
| print(f"[Arbitrage] Failed to create virtual position: {e}", flush=True) |
|
|
| def run_arbitrage_scan(): |
| client = get_client() |
| print(f"[Arbitrage] Scanner started (DRY_RUN = {DRY_RUN})", flush=True) |
| while True: |
| try: |
| markets = get_all_binary_markets(client) |
| if not markets: |
| time.sleep(SCAN_INTERVAL) |
| continue |
| for market in markets: |
| opp = check_market_arbitrage(client, market, 1.0) |
| if opp is None or opp["profit_on_max_bet"] < MIN_PROFIT_USD: |
| continue |
| print(f"[Arbitrage] OPPORTUNITY: {opp['market_slug']} | " |
| f"YES ask ${opp['yes_ask']:.2f}, NO ask ${opp['no_ask']:.2f} | " |
| f"Profit ${opp['profit_per_unit']:.2f} per $1 | " |
| f"Max bet profit ${opp['profit_on_max_bet']:.2f}", |
| flush=True) |
| send_alert( |
| f"🔍 Arbitrage found: {opp['market_slug']}\n" |
| f"YES ask: ${opp['yes_ask']:.2f}, NO ask: ${opp['no_ask']:.2f}\n" |
| f"Cost: ${opp['total_cost']:.2f} → Profit: ${opp['profit_per_unit']:.2f}/$1\n" |
| f"Max trade profit: ${opp['profit_on_max_bet']:.2f}" |
| ) |
| create_virtual_arbitrage_position(opp) |
| except Exception as e: |
| print(f"[Arbitrage] Scan error: {e}", flush=True) |
| time.sleep(SCAN_INTERVAL) |
|
|
| if __name__ == "__main__": |
| run_arbitrage_scan() |