polymarket-bot-testing / arbitrage_bot.py
frank0957's picture
Update arbitrage_bot.py
724132e verified
Raw
History Blame Contribute Delete
4.94 kB
# arbitrage_bot.py
# Scans all Polymarket binary markets for YES + NO < $1.00 opportunities.
# Dry‑run mode only logs and sends Telegram alerts.
# Also creates virtual positions for found opportunities (strategy = "arbitrage").
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()