TradingAgents: 116.096 estrellas, un Sharpe de 8,21 y nueve correcciones de look-ahead que el paper daba por resueltas

TradingAgents publica un 26,62% de rentabilidad acumulada y un Sharpe de 8,21 en un backtest de 2024, y afirma haber eliminado el look-ahead. Instalé el framework, ejecuté sus 1.362 tests, instrumenté una decisión completa y consulté las fuentes de datos en una fecha dentro de la ventana del paper: cuatro de las seis entradas del equipo de analistas devuelven avisos de 63 caracteres, no datos. La fórmula de rentabilidad anualizada del propio paper no reproduce su propia tabla.

116.096 estrellas. 866 watchers.

Ese cociente —0,75%— es lo primero que conviene mirar en TradingAgents, el framework multiagente de Tauric Research que reparte el análisis de una acción entre analistas fundamental, de sentimiento, de noticias y técnico, hace debatir a un toro y un oso, y entrega el resultado a un trader, un comité de riesgos de tres voces y un gestor de cartera. Lo segundo es que el paper que lo respalda, arXiv:2412.20138v7, publica un 26,62% de rentabilidad acumulada y un ratio de Sharpe de 8,21 en Apple durante 88 días, y afirma en su apartado experimental:

Agents make decisions based solely on data available up to each trading day, ensuring no future data is used (eliminating look-ahead bias).

El CHANGELOG del propio repositorio, mientras tanto, registra nueve correcciones de look-ahead o de integridad point-in-time repartidas en seis versiones de 2026. La última, once días antes de este artículo, en la v0.7.0.

Ahí está el artículo. No en «las estrellas están infladas» (pueden ser perfectamente reales) ni en «el trading con LLM es un fraude» (el repositorio es, por partes, inusualmente cuidadoso). La pregunta interesante es más estrecha y sí tiene respuesta: si instalas esto hoy y lo ejecutas sobre la ventana que el paper puntuó, ¿qué obtienes exactamente?

Instalé el framework, ejecuté su suite de tests, instrumenté una decisión completa y consulté cada ruta de datos de los analistas en una fecha dentro del backtest del paper. Todo lo que sigue se reproduce en una tarde.

La hoja de afirmaciones

Antes de cualquier crítica, esto es lo que se afirma y dónde.

AfirmaciónFuente¿Verificable por terceros?
26,62% / 24,36% / 23,21% de rentabilidad acumulada en AAPL / GOOGL / AMZN, 1 ene – 29 mar 2024Tabla 1 del paperSolo reejecutando con noticias y datos sociales archivados que el repo no incluye
Sharpe de 8,21 / 6,39 / 5,60Tabla 1 del paperIgual
«no future data is used (eliminating look-ahead bias)»Paper §5.1Sí: leyendo el código y sondeando los proveedores
11 llamadas al LLM y más de 20 de herramientas por predicciónNota al pie † del paperSí: instrumentando una ejecución
«Backtest results are not guaranteed to match any published figure»README, sección ReproducibilitySí, está en el README
«not intended as financial, investment, or trading advice»README + tauric.ai/disclaimerSí

Fíjate en la forma de la tabla. Las dos filas que más interesan —la rentabilidad y el Sharpe— son justo las dos que no se pueden comprobar, y el repositorio lo dice en el README casi con esas palabras:

Backtest results are not guaranteed to match any published figure. Returns depend on the model, the temperature, the date range, data quality, and the sampling above. Treat the framework as a research scaffold for studying multi-agent analysis, not as a strategy with a fixed, replicable return.

Es una frase honesta y es también un descargo que resuelve la tensión renunciando a la afirmación. Lo que sigue es lo que pasa cuando tomas el framework al pie de la letra e intentas reproducirlo.

Primero: la aritmética del paper no cierra

El apéndice S1.2 define la rentabilidad anualizada así:

AR = ((V_end / V_start) ^ (1/N) - 1) x 100%     (S2)

donde N es el número de años de la simulación. La simulación declarada va del 1 de enero al 29 de marzo de 2024: 88 días naturales, es decir N = 0,2411.

La tabla 1 publica esto:

AcciónCR%AR% impresaAR% según S2 en 88 díasN implícito
AAPL26,6230,50166,20,887 años = 324 días
GOOGL24,3627,58147,00,895 años = 327 días
AMZN23,2124,90137,70,939 años = 343 días

Componer un 26,62% en un trimestre da una cifra anualizada cercana al 166%, no al 30,5%. Para obtener el 30,50% impreso hace falta una ventana de unos 324 días, y las otras dos filas exigen 327 y 343: no es una constante equivocada aplicada por igual. El paper no explica qué convención produjo esa columna.

El patrón se repite en toda la tabla, y esto es lo que no esperaba. Dividiendo la AR impresa entre la CR impresa:

FilasMultiplicador AR/CRRango
Buy & Hold, MACD, KDJ&RSI, ZMR, SMA (14 celdas con datos)0,93 – 1,05ARR ≈ CR
TradingAgents (3 celdas)1,073 / 1,132 / 1,146ARR > CR

Todas las filas de referencia están «anualizadas» por un factor cercano a 1,0 —o sea, sin anualizar—, mientras que las tres filas del sistema propuesto se multiplican por 1,07 a 1,15. Sea lo que sea esa columna, ninguna fila de la tabla 1 satisface la fórmula S2 del propio paper para una ventana de 88 días, y las únicas filas que recibieron un empujón son las de los autores. Dos celdas de referencia están además visiblemente rotas: ZMR en GOOGL imprime CR −0,58 con AR +0,58 (cambio de signo) y MACD en AMZN son cuatro guiones sin explicación.

Dos incoherencias más dentro de la misma sección:

  • Cinco acciones anunciadas, tres publicadas. El §5.1 dice que la simulación cubre «major technology stocks including Apple, Nvidia, Microsoft, Meta, and Google». El §6.1.1 habla de «the three sampled stocks». La tabla 1 tiene columnas para AAPL, GOOGL y AMZN: Amazon no aparece nunca en el planteamiento, y Nvidia, Microsoft y Meta no figuran en ningún resultado.
  • Drawdown «not exceeding 2». §6.1.3: «ensured that the maximum drawdown remained within a manageable limit, not exceeding 2». La tabla 1 publica MDD de AMZN = 2,11.

Nada de esto demuestra fraude y no lo estoy insinuando. Demuestra algo más útil: los números del paper no se pueden recalcular con los números del paper. Quien quiera saber si el framework hace algo tiene que ejecutarlo, y ese es el experimento del resto del artículo.

La nota al pie que merece más atención que la tabla

Bajo la sección del Sharpe hay una nota que, en mi opinión, es el párrafo más honesto del paper:

We benchmarked TradingAgents over 3 months due to intensive LLM and tool use (11 LLM calls & 20+ tool calls/prediction). The highest Sharpe Ratio exceeds our expected empirical range (SR above 2 — very good, above 3 — excellent). We exported TradingAgents’s decision sequences and examined them to ensure calculation correctness. We believe the exceptionally high SR resulted from the phenomenon that there were few pullbacks in TradingAgents during that period. We report results as they are in our experiments faithfully.

Lee la segunda frase dos veces. Los autores te están diciendo que un número que publicaron queda fuera del rango que ellos mismos consideran creíble, y que decidieron no ajustarlo. La explicación que ofrecen —«hubo pocas correcciones en ese periodo»— no es un mecanismo: es repetir el resultado. Sesenta y un sesiones de mercado bastan para que el error estándar de un Sharpe anualizado ronde 2 (con la aproximación habitual SE ≈ √((1 + SR²/2)/T) × √252, T = 61), así que «8,21» es una estimación puntual cuya distribución muestral abarca algo así como de 4 a 12. Un trimestre sin drawdown produce un ratio alto por construcción. No produce evidencia de habilidad.

La primera frase de la nota es además el único sitio donde el paper declara su presupuesto de inferencia: 11 llamadas al LLM y más de 20 de herramientas por predicción. Guarda ese número; lo voy a medir.

Segundo: el código ya no sostiene la afirmación sobre el look-ahead

Esto es lo que registra el CHANGELOG. Cada fila son palabras del proyecto, con su número de incidencia.

VersiónFechaDefecto admitido
0.2.32026-03-29«Backtesting fetchers no longer leak look-ahead data when curr_date is in the middle of a fetched window. (#475)» — reportado por un colaborador externo
0.3.12026-07-05«The fundamentals payload is a JSON string, so the dict-only guard skipped filtering and future-dated reports leaked into historical runs. (#1115)»
0.4.02026-08-31«Historical macro requests were served from today’s data vintage, leaking later revisions into a backtest. (#1275)»
0.4.02026-08-31«StockTwits and Reddit were fetched with no date, so a historical run showed today’s chatter as if it were from the as-of date. (#1220)»
0.4.02026-08-31«get_past_context returned every resolved lesson regardless of the run date»: el resultado futuro se filtraba en la memoria de decisiones pasadas (#1251)
0.4.02026-08-31«A decision was settled on a partial return if a rerun happened before its holding window fully traded. (#1169)»
0.4.02026-08-31«The newest bar with a NaN close was silently dropped before the date cutoff, making the previous trading day look like the latest. (#1201)»
0.5.02026-09-18Las herramientas con fecha toman la fecha del estado del grafo, «so an omitted or later date cannot reach a vendor» (#1331, #1319, #1118); los estados de EDGAR se sirven tal como se presentaron, no reexpresados
0.5.22026-09-29«A past-dated run gets no Yahoo or Alpha Vantage statements or insider trades, which carry no filing date»
0.7.02026-10-16«A decision is no longer scored against news it could not have traded on: a run dated d reads news published after the close on d, so the memory log and backtests enter it at the first close after d, not the close of d. (#1518)»

Nueve correcciones, seis versiones. El paper (v7) es de junio de 2025 y afirma que el look-ahead estaba eliminado antes de que existiera ninguna de ellas. Varias cambian decisiones y por tanto rentabilidades: el feed social sirviendo conversación de 2026 a una ejecución de 2024, la vendimia macro, la fuga de memoria, el filtro que se saltaba el JSON de fundamentales. Otras cambian la medición de la rentabilidad; la #1518 en particular: hasta hace once días, una decisión tomada con información posterior al cierre del día d se puntuaba desde el cierre de d, es decir con una sesión de ventaja metida dentro del propio sistema de puntuación.

Conviene ser preciso con lo que esto implica y con lo que no. No implica que los números del paper se calcularan con los errores posteriores (el paper es anterior a casi todo ese código). Implica algo más estrecho y más dañino para quien quiere usar esto: el artefacto que puedes descargar hoy no es el artefacto que el paper midió, y la dirección del viaje ha sido siempre la misma: «encontramos otra fuga». Nueve veces.

Hay un problema de segundo orden que ningún changelog puede arreglar, y el README lo declara sin rodeos:

Live data moves. News, StockTwits, and Reddit return different content as time passes, so a run today sees different inputs than a run last week even for the same historical trade date. Pin the analysis date to hold the price and indicator window fixed, but the social and news sources still reflect “now”.

Esa frase hoy es falsa, y lo es en la dirección tranquilizadora. El código lo demuestra.

Tercero: qué devuelve realmente el stack por defecto en una fecha del paper

Cloné el repositorio en la v0.7.0, lo instalé en un entorno virtual de Python 3.13 (pip install -e ".[dev]") y llamé a las mismas funciones de datos que usan los agentes, con una ventana de 2024-02-25 a 2024-03-01, dentro de la simulación del paper. Configuración de proveedores por defecto, red real.

Entrada del analistaQué devolvióCaracteres
Precios (get_stock_data AAPL, 2024-02-01..2024-03-01)21 filas de OHLCV reales1.298
Noticias de la empresa (get_news)<Yahoo Finance news unavailable for 2024-02-25..2024-03-01: Yahoo search returns only a sample of recent articles about AAPL, so this is not an absence of news for AAPL>169
Noticias macro (get_global_news)<... unavailable ... it only serves recent items, so this is not an absence of market news>137
StockTwits (fetch_stocktwits_messages)<StockTwits unavailable ... it only serves recent items ...>131
Reddit (fetch_reddit_posts)<Reddit search unavailable ... it only serves recent items ...>132
Fundamentales (get_fundamentals)«Profile fundamentals are withheld for this date … Statements filed by 2024-03-01 are served as filed where the filing date is known (SEC EDGAR)»581

Las mismas llamadas con una ventana reciente devuelven contenido de verdad: 4.166 caracteres de artículos para 2026-10-20..2026-10-26, 4.596 caracteres de mensajes de StockTwits y 907 caracteres de posts de Reddit.

La lectura honesta es esta: cuatro de las seis entradas de las que depende el equipo de analistas no están disponibles justo en las fechas en las que el paper publicó rentabilidades del 23% al 27%. El analista de noticias, la parte macro de su trabajo y las dos mitades del analista de sentimiento reciben un párrafo que explica por qué no tienen datos. Las dos entradas que sí funcionan son los precios y los informes de la SEC tal como se presentaron (y esto último está genuinamente bien hecho: el ejemplo del propio README es que los activos totales de Apple de 2008 se presentaron como 39.600 millones y se reexpresaron a 36.200 millones en 2010, así que una ejecución fechada entre ambos lee 39.600 millones).

Aquí hay que reconocer el trabajo técnico, porque la decisión de diseño es la correcta. Los feeds devuelven «unavailable … this is not an absence of X» en lugar de una cadena vacía, que es la diferencia entre un analista que sabe que está ciego y uno que alucina a partir del silencio. La entrada del CHANGELOG que lo introdujo (v0.5.0) lo dice tal cual: «A feed that never observed a window reports it as unavailable rather than as an absence, across news, Reddit and StockTwits».

Pero la consecuencia es inevitable: el experimento del paper no se puede reproducir en el stack publicado. La sección de datos del paper cita Bloomberg, Yahoo, EODHD, FinnHub y Reddit como fuentes de noticias. De todas ellas, solo Yahoo está en el registro de proveedores, y Yahoo no puede servir el pasado. Un backtest que ejecutes hoy es un sistema de precios más informes de la SEC vestido con el nombre del paper.

La sección Reproducibility del README es el único punto donde la documentación va por detrás del código: avisa de que las fuentes sociales y de noticias «still reflect ’now’», lo que era cierto antes de la v0.4.0 y hoy es lo contrario de lo que ocurre. Si solo lees el README, esperarás entradas contaminadas; lo que vas a encontrar son entradas ausentes. Ninguna de las dos cosas es el conjunto de datos de 2024 que usó el paper.

Cuarto: qué mide el backtest, en palabras del propio código

El módulo de puntuación abre con un docstring que debería estar en la portada del README:

Scope: this evaluates decision quality. It is not a portfolio simulator, and must not grow one. Turning a rating into a filled order needs a quantity, a fill price and a cash ledger, none of which the system has; inventing them here would put an execution model behind an evaluation tool. Cells are therefore independent, and a portfolio, when given, is the same standing book for every cell rather than a position carried forward.

Es una postura inusualmente disciplinada, y también significa que la salida no es la rentabilidad de una estrategia. En concreto:

PropiedadQué hace el código
Unidad puntuadaUna celda: un par (ticker, fecha), ejecutado una vez
EntradaEl primer cierre posterior a la fecha de análisis, no el cierre de esa fecha
SalidaEl cierre holding_period_days sesiones después (por defecto 5)
MétricaAlfa contra el índice regional del instrumento (SPY en EE. UU., ^N225 en .T, ^TWII en .TW, etc.)
EscalaBuy / Overweight / Hold / Underweight / Sell; Hold no afirma dirección y nunca cuenta como acierto
CostesNinguno. Sin comisión, sin horquilla, sin deslizamiento, sin impacto de mercado
Tamaño de posiciónNinguno. Sin caja, sin capitalización compuesta, sin correlación entre celdas
MuestrasUna sola muestra del modelo por celda
Reproducibilidad de los feeds de texto«text feeds are not archived, so these figures are indicative rather than repeatable», impreso por la propia herramienta

Esa última línea sale del pie de salida de summarize(), no la escribo yo. El framework, cuando publica un número, le añade una frase diciendo que el número no es repetible.

Dos consecuencias inmediatas.

El horizonte es una semana. Un pipeline que gasta 30 llamadas al modelo y 48 a herramientas para decidir se evalúa después sobre cinco sesiones de movimiento, solapadas entre celdas consecutivas. Con rejilla diaria, la celda n y la n+1 comparten cuatro de sus cinco sesiones de resultado: el agregado tiene mucha menos información independiente de la que sugiere el recuento de celdas.

Un alfa a cinco días con coste cero es el test más indulgente posible. Ningún modelo necesita habilidad cuando el tamaño de posición, la financiación, la horquilla y el impacto son gratis.

Quinto: la rejilla duplica la muestra sin avisar

iter_grid() avanza en días naturales. La llamé sobre la ventana del paper:

>>> iter_grid("2024-01-01", "2024-03-29", every_n_days=1)
89 celdas          # 24 caen en sábado o domingo
>>> len(get_closes("AAPL", "2024-01-01", "2024-03-29"))
61                # sesiones reales en esa ventana

89 celdas frente a 61 sesiones. Después comprobé a qué se resuelven las celdas de fin de semana, usando la propia función de liquidación del framework:

celda 2024-03-01 (vie): primer cierre posterior -> 2024-03-04, salida -> 2024-03-11
celda 2024-03-02 (sáb): primer cierre posterior -> 2024-03-04, salida -> 2024-03-11
celda 2024-03-03 (dom): primer cierre posterior -> 2024-03-04, salida -> 2024-03-11

Viernes, sábado y domingo entran al cierre del lunes y salen en el mismo cierre posterior. Si las tres celdas llevan la misma calificación —lo habitual, porque el modelo ve entradas casi idénticas— son tres copias de un mismo resultado, contadas por separado en el alfa medio y en n. Una rejilla diaria sobre el primer trimestre de 2024 informa de n = 89 para lo que son como mucho 61 observaciones: un 46% de inflación antes de corregir el solapamiento.

No es un bug de look-ahead y no favorece ninguna dirección; simplemente hace que la confianza que depositas en un n= impreso sea menor de lo que parece. every_n_days=7 (13 celdas) lo evita por accidente.

Sexto: cuánto cuesta una decisión, medido

La nota del paper dice 11 llamadas al LLM por predicción. Instrumenté el test end-to-end que el propio repositorio trae para ejecutarse sin red —modelo de chat guionizado, los cuatro analistas, configuración por defecto (max_debate_rounds=1, max_risk_discuss_rounds=1, max_tool_rounds=20)— y conté cada invocación al modelo y cada llamada a herramienta.

=== ONE FULL RUN (default config, 4 analysts) ===
model_calls: 30
tool_calls: 48
max_tools_in_one_turn: 5

Treinta, no once. Y esto es un suelo: el modelo guionizado responde tras llamar una vez a cada herramienta enlazada, mientras un analista real puede iterar hasta 20 rondas. Multiplica por una rejilla:

RejillaCeldasLlamadas al modelo (suelo)Llamadas a herramientas (suelo)
Ventana del paper, 3 tickers, --every 12678.01012.816
Ventana del paper, 3 tickers, solo días hábiles1835.4908.784
Ventana del paper, 5 tickers, --every 144513.35021.360

Cada una de esas celdas además liquida las celdas anteriores de su ticker, lo que suma llamadas de reflexión por encima. La nota del propio paper explica que la presión de presupuesto es la razón de que el backtest dure tres meses: «Future work will optimize LLM reasoning & tool use to enable longer backtesting under limited budgets». Tres meses fueron una restricción de presupuesto, y la versión actual cuesta unas 2,7 veces más por decisión que la que generó la tabla publicada.

Ponle tú el precio con las tarifas de tu proveedor. Deliberadamente no invento una cifra en dólares porque los dos modelos por defecto (gpt-6-sol y gpt-6-luna) cobran lo que cobre tu endpoint, y el catálogo del framework lista 19 entradas de proveedor (openai, anthropic, google, xai, deepseek, qwen, qwen-cn, glm, glm-cn, kimi, minimax, minimax-cn, ollama, openai_compatible, mistral, groq, nvidia, bedrock, chatgpt).

Séptimo: la salud del repositorio, en números

Las estrellas son una señal de popularidad. Estas son las señales de si alguien mantiene esto.

MétricaValorLectura
Estrellas / watchers116.096 / 8660,75%: la mayoría de las estrellas son marcadores, no notificaciones
Forks / estrellas22.275 / 116.096 = 19,2%Alto para un framework que se ejecuta más que se bifurca; compatible con «ya lo probaré»
Commits493 en total, 430 de un solo autor (87,2%)Factor autobús de uno, con 24 colaboradores en los márgenes
Versiones16 desde 2025-06-05; 3 en las últimas cuatro semanasPublicación activa, muy rápida últimamente
PR abiertos / cerrados109 / 910Hay revisión real, y hay cola real
Tests1.360 pasan, 1 falla, 1 omitido, 100 subtests, 17,58 sSuite seria: 89 archivos, incluidos test_news_lookahead, test_social_lookahead, test_fundamentals_lookahead, test_memory_pointintime, test_statement_knowledge_time
Lintruff check . → All checks passedLimpio
Fijación de dependenciasSin lockfile; solo cotas inferiores (langgraph>=1.2.12, pandas>=3.0.6, yfinance>=1.7.0)Una versión de hace cinco semanas con pandas>=3.0.6 instalará un stack distinto del que se probó

La suite es la señal más fuerte de toda la auditoría. Tests dedicados al look-ahead —código cuyo único propósito es afirmar que una ejecución histórica no puede ver el futuro— no es lo que escribe un proyecto indiferente al problema. Alguien se preocupó lo suficiente como para escribirlos nueve veces.

El test que falla, y por qué es un bug de verdad

El fallo es tests/test_api_key_env.py::test_ensure_api_key_prompts_and_writes_to_env. Comprueba que, tras pedir una clave de API que falta, existe un .env en el directorio temporal del test. No existe. La causa está en cli/prompts.py:

env_path = find_dotenv(usecwd=True) or str(Path.cwd() / ".env")

find_dotenv(usecwd=True) recorre el árbol de directorios hacia arriba hasta encontrar un .env existente. En un checkout anidado —un monorepo, un contenedor, un workspace de CI bajo un home que ya tiene .env— encuentra el archivo del ancestro y escribe la clave ahí. Durante mi ejecución escribió DEEPSEEK_API_KEY en un .env tres directorios por encima del temporal del test. En tu máquina ese archivo puede ser el de secretos reales de un proyecto.

Borré la línea después, y lo cuento en lugar de parchearlo en silencio porque es un ejemplo limpio del argumento general: la suite se defiende del look-ahead y todavía tiene un bug de escritura de rutas que solo aparece cuando el entorno no es el del autor. El propio conftest se toma molestias para que los tests no lean el .env del desarrollador («The test suite is independent of the developer’s .env, time zone and network», v0.5.2), y esta ruta se escapa igual.

Lo que aquí está genuinamente bien

Una auditoría que solo enumera defectos engaña tanto como un README. Según la evidencia:

  • Fundamentales de la SEC tal como se presentaron, sin clave de API, con un ejemplo trabajado en el README del mismo dato presentado como 39.600 millones y reexpresado después a 36.200. Eso es point-in-time bien hecho, y es raro.
  • Proveedores que fallan en voz alta. VendorUnavailableError en lugar de un resultado vacío; una cadena que lo dice cuando todos los proveedores están caídos; coverage_gap() produciendo «this is not an absence of X» en lugar de silencio.
  • Un centinela REVIEW en vez de un Hold silencioso. Una calificación ilegible del gestor de cartera se convertía en un Hold negociable; ahora sale a revisión, porque «a decision nobody can read is not a Hold» (#1170).
  • La honestidad de ejecución en backtest.py: negarse a convertirse en un simulador de cartera e imprimir «these figures are indicative rather than repeatable» bajo su propia salida.
  • Índices de referencia por sufijo de mercado, para que .TW se compare con el TAIEX y no con el SPY: un detalle que casi todos los backtests minoristas hacen mal.
  • 1.360 tests en 17,6 segundos y ruff limpio.

Si buscas un andamio de investigación para estudiar la argumentación multiagente sobre una acción, este es uno de los mejor construidos. Si buscas una estrategia, nada en el repositorio sostiene ese uso, y el repositorio lo dice.

Antes de usarlo para algo que importe

Seis comprobaciones que puedes hacer tú. Todas tardan minutos y ninguna necesita clave de API salvo la última.

  1. Sondea una fecha histórica. Llama a get_news("AAPL", "2024-02-25", "2024-03-01") y lee lo que vuelve. Si es un placeholder y no artículos, no estás probando el sistema que crees.
  2. Compara celdas con sesiones. len(iter_grid(inicio, fin, 1)) frente al número de filas que devuelve get_closes() para la misma ventana. Si el primero es mayor, tu n está inflado: usa every_n_days=7 o filtra los fines de semana.
  3. Lee una entrada del log de memoria. ~/.tradingagents/memory/trading_memory.md. Es la tabla de resultados real: calificación, rentabilidad bruta, alfa, índice de referencia, ventana y reflexión. Si los alfas se agrupan dentro de ±1% tras cinco días, el sistema no se distingue del ruido.
  4. Ejecuta la suite antes de fiarte de la build. Si pip install -e ".[dev]" && pytest -q no te da 1.360 aprobados en tu máquina, estás sobre un stack que nadie probó: no hay lockfile que te salve.
  5. Mira el n por calificación, no el total. Una línea Sell: n=3, called the direction 67% no es un hallazgo. summarize() la imprimirá sin quejarse.
  6. Y solo entonces, presupuesta una decisión en vivo. Con un suelo medido de 30 llamadas al modelo por decisión, un barrido diario de diez tickers son cinco cifras de llamadas por trimestre.

Preguntas frecuentes

¿TradingAgents gana dinero? Nada publicado permite responderlo. La rentabilidad del paper no se puede recalcular con su propia tabla (véase la columna AR), su afirmación sobre el look-ahead ha sido contradicha nueve veces por el changelog del proyecto, y la capa de datos publicada no puede servir la ventana del paper. La frase del README —«not as a strategy with a fixed, replicable return»— es la respuesta correcta.

¿El Sharpe de 8,21 es falso? Es aritmética sobre una muestra corta. En 61 sesiones el error estándar de un Sharpe anualizado ronda 2, así que 8,21 es una estimación de distribución ancha; los propios autores escriben que «exceeds our expected empirical range». Un trimestre sin drawdown, más costes de transacción nulos, más una convención de entrada favorable, lo producen. Yo no lo llamaría falso; lo llamaría poco informativo.

¿El paper usó look-ahead? El paper afirma que no, y yo no puedo testear un pipeline de 2024 que no puedo ejecutar. Lo que sí puedo testear es el código que existe hoy: nueve correcciones de look-ahead o point-in-time publicadas entre marzo y octubre de 2026, incluida una de puntuación el 2026-10-16. Si los números de 2024 se beneficiaron del comportamiento del código anterior es una pregunta sin respuesta desde fuera, y el paper no ofrece rastro de auditoría: ni semillas, ni logs de decisiones, ni entradas archivadas.

¿Por qué get_news devuelve un placeholder en vez de noticias de 2024? Porque el feed de Yahoo solo lleva artículos recientes, y el framework ahora se niega a hacer pasar por resultado vacío una ventana que nunca observó. Ese comportamiento es el correcto y es justo lo que hace irreproducible el experimento del paper: su sección de datos cita Bloomberg, EODHD y FinnHub, ninguno de los cuales está en el registro de proveedores.

¿Puedo añadir un proveedor histórico y obtener resultados reales? En principio sí: data_vendors es una cadena, y el endpoint de noticias de Alpha Vantage acepta ventanas históricas si tienes clave. Pero entonces estás evaluando tu propio conjunto de datos, no el del paper, y conviene decirlo en tus notas. La fontanería point-in-time del framework no te va a contaminar; tu proveedor de datos sí puede.

¿Son sospechosas las 116.096 estrellas? Por sí solas, no. El patrón de commits (430 de 493 de un autor, 24 colaboradores) es normal en un proyecto de investigación con un líder claro, y 910 pull requests cerrados es mantenimiento real. El número que parece raro no son las estrellas: son 866 watchers, que es la gente que pidió que le avisaran cuando algo cambie.

La versión corta

TradingAgents no es un fraude ni una estrategia. Es un andamio de investigación bien testeado cuyos números publicados no se pueden reproducir, cuyo propio changelog documenta nueve correcciones de look-ahead después de que el paper declarara el problema resuelto, y cuya pila de datos por defecto se niega a servir la ventana histórica de la que salieron esos números.

Esa última parte es, paradójicamente, el comportamiento más fiable del repositorio: cuando el código actual no puede ver el pasado, lo dice, con una frase diseñada para que no confundas el silencio con datos. Las 116.096 estrellas las ganó el diagrama de arquitectura. Los 866 watchers son a quienes deberías preguntar antes de apuntar esto a una cuenta de bróker.