Una serie de errores documentables que aparecieron generando código nuevo, en lugar de reutilizar funciones probadas en el mismo proyecto.
Artículo 2 de Ventaja Mecánica II. En el artículo anterior conté qué pasó cuando intentamos ejecutar la mecánica que cerraba la S1. Este es el catálogo técnico de bugs que la IA introdujo en el proceso. Si llegaste sin contexto: este es un bot de trading en Polymarket construido con Claude Code, capital pasó de $80 a $9, y los siguientes errores son una parte importante del por qué.
Este artículo es para programadores que están considerando «vibe coding» con IA para construir sistemas que tocan dinero real. Voy a contarte ocho bugs concretos, con su log de error correspondiente, que aparecieron en un proyecto donde tenía YA escrito y funcionando el código correcto, pero la IA generó versiones nuevas con bugs nuevos.
Todos son del mismo proyecto: un bot de trading en Polymarket. Algunos los detecté en dry-run, otros me costaron dinero real.
Bug 1: round(shares, 2) redondea HACIA ARRIBA
# Lo que escribí
shares = round(shares, 2)
Compras 1.0870 shares. round(1.0870, 2) = 1.09. Al intentar venderlas:
{"error": "not enough balance / allowance: the balance is not enough -> balance: 1086950, order amount: 1090000"}
Pides vender 3.050 unidades raw más de las que tienes. La orden FOK rechaza el bloque completo. Tu posición se queda abierta. Si BTC se da la vuelta en los 15 segundos siguientes, palmas el dólar entero.
Solución correcta:
shares = math.floor(shares * 100) / 100
Donde estaba la versión correcta: en copybot_positions.py línea 522, en el mismo proyecto, lleva semanas funcionando. La IA no la consultó.
Bug 2: FOK en mercados con liquidez fina
return client.create_and_post_order(args, options=opts, order_type=OrderType.FOK)
FOK (Fill-Or-Kill) exige que la cantidad COMPLETA se llene al precio. En un mercado que se está hundiendo (stop_loss típico), no hay 1,25 shares al bid de 0,55. La orden muere. Posición intacta. Si era perdedora, palmas el dólar.
{"error": "order couldn't be fully filled. FOK orders are fully filled or killed."}
Solución correcta: usar FAK (Fill-And-Kill) con precio límite tick (= 0,01):
return client.create_and_post_order(args, options=opts, order_type=OrderType.FAK)
FAK permite llenados parciales. Un precio límite de 0,01 dice «acepto cualquier bid». El CLOB siempre da el mejor disponible.
Donde estaba la versión correcta: en realidad, ninguna. Este bug es mío al construir el bot nuevo. Pero el código del repo de referencia ya advertía contra FOK en mercados de cierre.
Bug 3: MarketOrderArgs no importado
from py_clob_client_v2 import (
ClobClient, SignatureTypeV2, ApiCreds,
OrderArgs, OrderType, PartialCreateOrderOptions,
BalanceAllowanceParams, AssetType,
)
Note la ausencia de MarketOrderArgs. Pero más abajo en el código:
args = MarketOrderArgs(token_id=token, amount=amount, side="BUY", price=max_price)
Error al comprar: name 'MarketOrderArgs' is not defined
Una orden perdida en plena ventana de entrada. Esos 5 minutos no vuelven.
Donde estaba la versión correcta: en copybot_positions.py línea 30, donde MarketOrderArgs SÍ está en el import.
Bug 4: takingAmount como raw vs float
shares = float(resp.get("takingAmount") or 0)
Polymarket a veces devuelve takingAmount: "1.05894" (float-like) y otras veces takingAmount: 1058940 (raw, multiplicado por 1e6). El código asume float. Cuando viene en raw, calculas que tienes 1.058.940 shares. Mandas vender un número astronómico. Rechazo.
Solución correcta:
taking = float(resp.get("takingAmount") or 0)
if taking > 1000: # raw units
shares = round(taking / 1e6, 4)
else:
shares = round(taking, 4)
Donde estaba la versión correcta: en copybot_positions.py líneas 720-729.
Bug 5: min notional = $1 en BUY
size = round(stake_usd / ask, 4) # stake=1.00, ask=0.95 → size=1.0526
args = OrderArgs(token_id=token, price=0.95, size=1.0526, side="BUY")
Notional = 1.0526 × 0.95 = $0.9997. Polymarket requiere notional ≥ $1.
{"error":"invalid amount for a marketable BUY order ($0.9975), min size: 1"}
Solución correcta: usar MarketOrderArgs que toma directamente el amount USDC en 2 decimales, no OrderArgs con price y size que se multiplican.
Donde estaba la versión correcta: misma función buy_market del copybot que ya manejaba esto.
Bug 6: neg_risk hardcodeado
opts = PartialCreateOrderOptions(neg_risk=False)
Polymarket tiene dos tipos de mercados: estándar y neg_risk (multi-outcome mutuamente excluyentes). Si pones False cuando es True o viceversa, la orden falla.
Solución correcta:
opts = PartialCreateOrderOptions(
tick_size=tick_str,
neg_risk=client.get_neg_risk(token)
)
Donde estaba la versión correcta: función is_neg_risk en copybot_positions.py línea 467.
Bug 7: Shadow comparando contra resolución no confirmada
# Esperamos 10s tras el cierre del bucket, luego consultamos Gamma
time.sleep(end_ts + 10 - time.time())
r = http_get(f"{gamma}/events", {"slug": slug})
prc = json.loads(r.json()[0]["markets"][0]["outcomePrices"])
winner = "UP" if float(prc[0]) >= 0.99 else "DOWN"
Sutil: UMA (el oráculo de Polymarket) tarda más de 10 segundos a veces en marcar la resolución final. Antes de eso, outcomePrices muestra los últimos precios live (algo como [0.987, 0.013]). Mi check >= 0.99 falla. El código asume «DOWN ganó» cuando realmente UP estaba a 0.987 (claramente ganador, solo aún no UMA-confirmed).
Resultado: tres días de análisis de «shadow HODL vs SL» contaminados porque el ganador estaba mal identificado en un porcentaje significativo de los trades.
Bot dice: winner=DOWN
Realidad: UP ganó (resolvió a $1)
Solución correcta: poll hasta que UMA confirme O usa comparación entre lados (si up_p > dn_p + 0.5, UP es ganador).
Donde estaba la versión correcta: en ningún lado del proyecto. Este lo cazó el usuario notando que el shadow mentía.
Bug 8: wallet_stop_loss = budget
{
"max_budget_usdc": 20.0,
"wallet_stop_loss_usdc": 20.0
}
«Si la cartera baja de $20, para el bot». La cartera EMPIEZA en $20. El bot se pausa en el primer céntimo perdido.
Discutimos durante días por qué solo se abrían pocas posiciones. Yo daba explicaciones sobre filtros, slippage, el ritmo del objetivo. La realidad: el bot estaba pausado por el stop diario desde el minuto uno.
Solución correcta:
{ "wallet_stop_loss_usdc": 0 }
O quitar el check entero. La experiencia de usuario debería ser obvia: si quieres usar TODO tu capital, el stop tiene que estar por debajo del balance inicial.
El patrón común
Cada uno de estos bugs habría sido imposible si la IA hubiera leído primero el código que ya tenía funcionando en el mismo proyecto. Cada función buy_market, sell_limit, get_tick_str ya estaba escrita, probada y operativa en copybot_positions.py. Pero al pedirle un nuevo bot, generó versiones desde cero — con los mismos bugs que el código existente ya había resuelto meses atrás.
Cuando se lo señalé, su respuesta fue: «tienes razón, debí haber copiado el código que ya funcionaba en vez de reescribir desde cero». Lo añadió a su memoria. Y a los dos prompts, volvió a hacerlo.
Coste real
- Tiempo: probablemente 30-40 horas de debugging que no habrían existido.
- Dinero: difícil de cuantificar exactamente. Estimación conservadora: $5-10 perdidos por trades donde la orden FOK falló y la posición se cerró a 0 cuando habría redimido a $1.
- Tokens: miles. La conversación entera podría haberse evitado con un «primero, copia las funciones de orders del archivo X».
- Confianza: ese es el coste más alto. Cuando llevas tres días viendo análisis con tres decimales sobre datos que la propia IA contaminó con bugs que ella misma generó, dejas de poder fiarte.
Para el lector que va a empezar un proyecto con IA
Tres reglas que pagaría por haber sabido antes:
- Si tienes código probado en el repo, OBLIGA a la IA a copiar y adaptar, no a regenerar. Pasa el archivo entero en el contexto, di explícitamente: «extrae estas funciones y úsalas, no escribas versiones nuevas».
- Cualquier sistema que toca dinero merece tests automáticos antes del primer trade en vivo. La IA es rápida generando código pero no genera tests con la misma honestidad — tiende a escribir tests que validan que el código pasa, no que el código es correcto.
- Cualquier «PnL» calculado por código generado por IA tiene que cruzarse con la fuente de verdad (en este caso, la interfaz de Polymarket o el ledger on-chain). Nunca te fíes del número que dice el bot.
El siguiente artículo de la serie analiza cuándo la IA SÍ es útil para programar y cuándo definitivamente no.