Guest User

Untitled

a guest
Jan 21st, 2026
776
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.16 KB | None | 0 0
  1. import time
  2. import sys
  3. import re
  4. from datetime import datetime, timedelta
  5. from selenium import webdriver
  6. from selenium.webdriver.common.by import By
  7. from selenium.webdriver.chrome.service import Service
  8. from selenium.webdriver.chrome.options import Options
  9. from webdriver_manager.chrome import ChromeDriverManager
  10. from selenium.webdriver.support.ui import WebDriverWait
  11. from selenium.webdriver.support import expected_conditions as EC
  12.  
  13.  
  14. # ---------- CONFIGURACIÓN ----------
  15. TRADE_WINDOW_MINUTES = 15 # Ventana de compra inicial en minutos
  16. BUY_THRESHOLD = 0.32
  17. TARGET_PROFIT = 0.20
  18. INITIAL_INVESTMENT = 10
  19. POLL_INTERVAL = 0.5
  20.  
  21. USE_REAL_FUNDS = True # ⬅️ CAMBIA A True cuando operes con dinero real
  22.  
  23. # Nuevos XPaths robustos
  24. XPATH_BUTTON_UP = "//button[.//span[contains(text(),'Up')]]"
  25. XPATH_BUTTON_DOWN = "//button[.//span[contains(text(),'Down')]]"
  26. XPATH_PRICE_INSIDE_BUTTON = ".//p[2]//span"
  27.  
  28. # ---------- XPATHS PARA TRADING REAL ----------
  29. XPATH_AMOUNT_INPUT = "//input[@id='market-order-amount-input']"
  30. XPATH_TRADE_BUTTON = "//button[contains(@class,'trading-button') and @data-color='blue']"
  31.  
  32. # ---------- XPATH POPUP SIN FONDOS ----------
  33. XPATH_POPUP_CLOSE = "//button[@data-testid='rk-close-button']"
  34.  
  35.  
  36.  
  37. def cerrar_popup_si_existe(driver, timeout=2):
  38. """
  39. Cierra el popup que aparece al operar sin fondos reales.
  40. No hace nada si no aparece.
  41. """
  42. if USE_REAL_FUNDS:
  43. return
  44.  
  45. try:
  46. btn = WebDriverWait(driver, timeout).until(
  47. EC.element_to_be_clickable((By.XPATH, XPATH_POPUP_CLOSE))
  48. )
  49. btn.click()
  50. print("ℹ️ Popup sin fondos cerrado")
  51. time.sleep(0.3)
  52. except:
  53. # No apareció popup → OK
  54. pass
  55.  
  56.  
  57. def ejecutar_compra_real(driver, side: str, amount: float):
  58. """
  59. Ejecuta una compra REAL en Polymarket.
  60. side: 'UP' o 'DOWN'
  61. amount: dólares a invertir
  62. Devuelve True si todo fue correcto, False si algo falló
  63. """
  64. try:
  65. # 1️⃣ Click en el lado correcto
  66. if side == "UP":
  67. driver.find_element(By.XPATH, XPATH_BUTTON_UP).click()
  68. elif side == "DOWN":
  69. driver.find_element(By.XPATH, XPATH_BUTTON_DOWN).click()
  70. else:
  71. print("❌ Lado inválido")
  72. return False
  73.  
  74. time.sleep(0.3)
  75.  
  76. # 2️⃣ Escribir el importe
  77. amount_input = driver.find_element(By.XPATH, XPATH_AMOUNT_INPUT)
  78. amount_input.click()
  79. amount_input.clear()
  80. amount_input.send_keys(str(round(amount, 2)))
  81.  
  82. time.sleep(0.3)
  83.  
  84. # 3️⃣ Click en Trade
  85. trade_btn = driver.find_element(By.XPATH, XPATH_TRADE_BUTTON)
  86. trade_btn.click()
  87.  
  88. time.sleep(0.5)
  89.  
  90. # 4️⃣ Cerrar popup si estamos sin fondos
  91. cerrar_popup_si_existe(driver)
  92.  
  93. print(f"✅ COMPRA REAL EJECUTADA → {side} ${amount}")
  94. return True
  95.  
  96. except Exception as e:
  97. print(f"❌ ERROR EJECUTANDO COMPRA REAL: {e}")
  98. return False
  99.  
  100.  
  101. # ---------- PARSE DE PRECIOS ----------
  102. def parse_price_text_to_float(s: str):
  103. if not s:
  104. return None
  105. s = s.strip()
  106. if "¢" in s:
  107. try:
  108. return float(s.replace("¢", "").strip()) / 100
  109. except:
  110. return None
  111. cleaned = s.replace("$", "").replace(",", ".")
  112. try:
  113. return float(cleaned)
  114. except:
  115. return None
  116.  
  117. def format_price_usd(value):
  118. if value is None:
  119. return "—"
  120. return f"{value:.2f}"
  121.  
  122. # ---------- LECTURA DE PRECIOS ----------
  123. def read_price(driver, xpath_button):
  124. try:
  125. btn = driver.find_element(By.XPATH, xpath_button)
  126. price_span = btn.find_element(By.XPATH, XPATH_PRICE_INSIDE_BUTTON)
  127. raw = price_span.text
  128. val = parse_price_text_to_float(raw)
  129. if val is not None:
  130. return val
  131. except:
  132. pass
  133. # Fallback JS
  134. try:
  135. script = """
  136. const btns = [...document.querySelectorAll("button")];
  137. for (const b of btns) {
  138. if (arguments[0] === "UP" && b.innerText.includes("Up")) {
  139. const m = b.innerText.match(/(\\d+¢|\\d+\\.\\d+)/);
  140. return m ? m[0] : null;
  141. }
  142. if (arguments[0] === "DOWN" && b.innerText.includes("Down")) {
  143. const m = b.innerText.match(/(\\d+¢|\\d+\\.\\d+)/);
  144. return m ? m[0] : null;
  145. }
  146. }
  147. return null;
  148. """
  149. raw = driver.execute_script(script, "UP" if "Up" in xpath_button else "DOWN")
  150. return parse_price_text_to_float(raw)
  151. except:
  152. return None
  153.  
  154. # ---------- INTERVALOS DE 15 MIN ----------
  155. def tiempo_restante_intervalo():
  156. now = datetime.now()
  157. next_minute = (now.minute // 15 + 1) * 15
  158. if next_minute == 60:
  159. next_time = now.replace(hour=now.hour + 1, minute=0, second=0, microsecond=0)
  160. else:
  161. next_time = now.replace(minute=next_minute, second=0, microsecond=0)
  162. delta = next_time - now
  163. mins, secs = divmod(delta.seconds, 60)
  164. return mins, secs
  165.  
  166. # ---------- CAMBIO DE URL ----------
  167. def siguiente_evento_url(url_actual: str):
  168. try:
  169. base = url_actual.split("?")[0]
  170. m = re.search(r"/event/(.+)-15m-(\d+)$", base)
  171. if not m:
  172. print("❌ No se pudo parsear la URL actual")
  173. return None
  174. slug = m.group(1)
  175. current_ts = int(m.group(2))
  176. next_ts = current_ts + 900
  177. nueva_url = f"https://polymarket.com/event/{slug}-15m-{next_ts}"
  178. return nueva_url
  179. except Exception as e:
  180. print(f"❌ Error generando siguiente URL: {e}")
  181. return None
  182.  
  183. # ---------- COBERTURA ----------
  184. def calcular_cobertura_estimada(entry_price, entry_amount, opposite_price, target_profit):
  185. if entry_price is None or opposite_price is None or entry_amount <= 0:
  186. return None, None, None
  187. try:
  188. p1 = float(entry_price)
  189. p2 = float(opposite_price)
  190. I1 = float(entry_amount)
  191. desired_profit = I1 * float(target_profit)
  192. shares_entry = I1 / p1
  193. upper_y = shares_entry - I1 - desired_profit
  194. if p2 <= 0 or p2 >= 1:
  195. return None, None, None
  196. denom = (1.0 / p2) - 1.0
  197. if denom <= 0:
  198. return None, None, None
  199. lower_y = (I1 + desired_profit) / denom
  200. if lower_y <= upper_y and upper_y > 0:
  201. y = round(lower_y, 2)
  202. return y, round(lower_y, 2), round(upper_y, 2)
  203. return None, round(lower_y, 2), round(upper_y, 2)
  204. except:
  205. return None, None, None
  206.  
  207. # ---------- LOG ----------
  208. def iniciar_logger():
  209. log_filename = f"polymarket_log_{datetime.now():%Y-%m-%d_%H-%M-%S}.txt"
  210. log_file = open(log_filename, "a", buffering=1, encoding="utf-8")
  211. class Tee:
  212. def __init__(self, *files):
  213. self.files = files
  214. def write(self, obj):
  215. for f in self.files:
  216. f.write(obj)
  217. f.flush()
  218. def flush(self):
  219. for f in self.files:
  220. f.flush()
  221. sys.stdout = Tee(sys.__stdout__, log_file)
  222. sys.stderr = Tee(sys.__stderr__, log_file)
  223. print(f"📜 Log activo: {log_filename}\n")
  224.  
  225. # ---------- MAIN ----------
  226. def main():
  227. iniciar_logger()
  228. results_file = open("results.txt", "a", encoding="utf-8")
  229.  
  230. options = Options()
  231. options.add_argument("--start-maximized")
  232. options.add_experimental_option("excludeSwitches", ["enable-automation"])
  233. options.add_experimental_option("useAutomationExtension", False)
  234. driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
  235. driver.get("https://polymarket.com")
  236.  
  237. print("Chrome abierto en polymarket.com")
  238. input("Navega al mercado 15m y pulsa ENTER...")
  239.  
  240. start_time = datetime.now()
  241. main_side = None
  242. main_price = None
  243. main_amount = 0
  244. main_time = None
  245. hedge_time = None
  246. hedge_side = None
  247. hedge_price = None
  248. hedge_amount = 0
  249. profit_secured = 0
  250. hedge_recorded = False
  251.  
  252. print("\nComenzando monitoreo...\n")
  253.  
  254. try:
  255. while True:
  256. mins, secs = tiempo_restante_intervalo()
  257. up_val = read_price(driver, XPATH_BUTTON_UP)
  258. down_val = read_price(driver, XPATH_BUTTON_DOWN)
  259. up_str = format_price_usd(up_val)
  260. down_str = format_price_usd(down_val)
  261. print(f"UP {up_str} | DOWN {down_str} | Time left: {mins}:{secs:02d}")
  262.  
  263. # ----- Cambio automático de sesión -----
  264. if mins == 0 and secs == 0:
  265. now = datetime.now()
  266. session_time = now.replace(minute=(now.minute // 15) * 15, second=0, microsecond=0)
  267. results_file.write(f"\n===== SESSION {session_time:%Y-%m-%d %H:%M} =====\n")
  268. if main_side:
  269. results_file.write(f"BUY | TIME: {main_time:%H:%M:%S} | SIDE: {main_side} | AMOUNT: {main_amount}$ | PRICE: {main_price:.2f}\n")
  270. if hedge_side:
  271. results_file.write(f"ARB | TIME: {hedge_time:%H:%M:%S} | SIDE: {hedge_side} | AMOUNT: {hedge_amount}$ | PRICE: {hedge_price:.2f} | PROFIT: {profit_secured:.2f}$\n")
  272. results_file.flush()
  273.  
  274. # Reset variables
  275. main_side = None
  276. main_price = None
  277. main_amount = 0
  278. hedge_side = None
  279. hedge_price = None
  280. hedge_amount = 0
  281. profit_secured = 0
  282. hedge_recorded = False
  283. main_time = None
  284. hedge_time = None
  285.  
  286. # Cambiar URL
  287. current_url = driver.current_url
  288. next_url = siguiente_evento_url(current_url)
  289. if next_url:
  290. print(f"⏭ Pasando al siguiente evento: {next_url}")
  291. driver.get(next_url)
  292. start_time = datetime.now()
  293. time.sleep(5)
  294. continue
  295.  
  296. # ----- Trading -----
  297. mins_passed = (datetime.now() - start_time).total_seconds() / 60
  298.  
  299. # Compra principal solo si estamos dentro de la ventana
  300. if main_side is None:
  301. if mins_passed <= TRADE_WINDOW_MINUTES:
  302. if up_val is not None and up_val <= BUY_THRESHOLD:
  303. if ejecutar_compra_real(driver, "UP", INITIAL_INVESTMENT):
  304. main_side = "UP"
  305. main_price = up_val
  306. main_amount = INITIAL_INVESTMENT
  307. main_time = datetime.now()
  308. print(f"🟢 Comprado UP a {main_price:.2f}")
  309. elif down_val is not None and down_val <= BUY_THRESHOLD:
  310. if ejecutar_compra_real(driver, "DOWN", INITIAL_INVESTMENT):
  311. main_side = "DOWN"
  312. main_price = down_val
  313. main_amount = INITIAL_INVESTMENT
  314. main_time = datetime.now()
  315. print(f"🔴 Comprado DOWN a {main_price:.2f}")
  316. else:
  317. print(f"⚠️ ERROR: Se pasó el tiempo de {TRADE_WINDOW_MINUTES} minutos para comprar. No se puede operar.")
  318.  
  319. # Cobertura / arbitraje
  320. if main_side == "UP" and down_val:
  321. y, low, high = calcular_cobertura_estimada(main_price, main_amount, down_val, TARGET_PROFIT)
  322. if y and not hedge_recorded:
  323. if ejecutar_compra_real(driver, "DOWN", y):
  324. hedge_side = "DOWN"
  325. hedge_time = datetime.now()
  326. hedge_price = down_val
  327. hedge_amount = y
  328. profit_secured = TARGET_PROFIT * main_amount
  329. hedge_recorded = True
  330. print(f"🔔 ARBITRAJE EJECUTADO: DOWN {hedge_amount} a {hedge_price:.2f}")
  331. else:
  332. print(f"🕓 Esperando cobertura: DOWN actualmente {down_val:.2f}, rango válido {low}-{high}")
  333.  
  334. elif main_side == "DOWN" and up_val:
  335. y, low, high = calcular_cobertura_estimada(main_price, main_amount, up_val, TARGET_PROFIT)
  336. if y and not hedge_recorded:
  337. if ejecutar_compra_real(driver, "UP", y):
  338. hedge_side = "UP"
  339. hedge_time = datetime.now()
  340. hedge_price = up_val
  341. hedge_amount = y
  342. profit_secured = TARGET_PROFIT * main_amount
  343. hedge_recorded = True
  344. print(f"🔔 ARBITRAJE EJECUTADO: UP {hedge_amount} a {hedge_price:.2f}")
  345. else:
  346. print(f"🕓 Esperando cobertura: UP actualmente {up_val:.2f}, rango válido {low}-{high}")
  347.  
  348. time.sleep(POLL_INTERVAL)
  349. print()
  350.  
  351. except KeyboardInterrupt:
  352. print("Saliendo...")
  353. results_file.close()
  354. driver.quit()
  355.  
  356. if __name__ == "__main__":
  357. main()
  358.  
Advertisement
Add Comment
Please, Sign In to add comment