Inspiration

Cada analista con el que hablamos describió la misma mañana: más de 200 titulares, 40 minutos, y ninguna forma de saber cuáles tres mueven algo de verdad. Las herramientas que prometen resolver esto caen en dos campos — dashboards que solo te muestran más noticias, o bots caja negra que te dicen que compres algo y nunca explican por qué. No queríamos construir ninguno de los dos. La idea que originó CHROMA MARKET fue pequeña y terca:

What it does

CHROMA MARKET ingiere noticias y datos de mercado y emite señales estructuradas para acciones, crédito, cripto y otros instrumentos. Cada señal trae: Impacto — positivo / negativo / neutral / incierto Confianza — un score $c \in [0, 100]$, explícitamente no calibrado como probabilidad Evidencia — citas y fuentes verificables, referenciadas inline como [cN] Acción de investigación sugerida — nunca una instrucción de compra/venta Las señales sensibles caen en una cola de revisión humana antes de poder convertirse en decisión. El recorrido del usuario es un "camino feliz": Radar → Noticias → Revisar → Briefing, donde el Briefing es un ledger de decisiones que un humano realmente tomó — aprobar, escalar, descartar — cada una con su justificación escrita.

How we built it

El grafo de agentes Dos agentes, una arista condicional: Next.js + Clerk → NestJS API → LangGraph: loadMarket → Analista → gate → Asesor ├─ MongoDB: noticias, señales y revisión humana ├─ Currents / NewsAPI: noticias └─ Finnhub / CoinGecko / Alpha Vantage: mercado El Analista siempre se ejecuta. Toma una noticia más el contexto de mercado, identifica instrumentos afectados, clasifica impacto, calcula confianza, extrae evidencia y propone una acción de investigación. El Asesor se ejecuta condicionalmente. Nunca ve noticias crudas — solo la señal estructurada del Analista. Produce tesis, tres escenarios, riesgos y pasos de validación. El gate La pieza interesante es la arista entre ambos. Correr un segundo LLM en cada titular es caro y casi siempre desperdicio — la mayoría de las noticias son ruido. Por eso el Asesor está gateado:

needsAdvice

f ∨ ( i ∈ { pos , neg } ∧ c ≥ τ ) needsAdvice=f∨(i∈{pos,neg}∧c≥τ) donde $f$ es el override explícito forceAdvice, $i$ es el impacto clasificado y $\tau$ es ADVICE_CONFIDENCE_THRESHOLD (por defecto $\tau = 70$). Los impactos neutral e incierto se detienen en el Analista. Quien quiera la lectura profunda de todos modos siempre puede forzarla.

Ese único booleano es lo que mantiene el pipeline costeable a escala — el agente caro solo dispara en la cola de la distribución que importa.

Stack Capa Tecnología Frontend Next.js 15 · React 19 · TypeScript 5 · Tailwind CSS 4 Backend NestJS 11 · TypeScript Orquestación LangGraph 1 + LangChain LLM OpenAI gpt-4o-mini Base de datos MongoDB Atlas Auth Clerk Noticias Currents API · NewsAPI Mercado Finnhub · CoinGecko · Alpha Vantage Despliegue Vercel (frontend) · Render (API) Guardrails antialucinación Schemas Zod validan toda salida de los agentes. Una señal malformada falla ruidosamente en vez de filtrarse al feed. Matemática determinista. Los cálculos de charts y forecast son código plano, no salida de LLM. Los modelos clasifican y explican; no calculan. Disciplina de citas. El agente de chat está anclado a señales guardadas, noticias ingeridas y herramientas internas de mercado. Toda afirmación material lleva una cita [cN]. Ignorancia declarada. Sin evidencia, el sistema lo dice y propone un siguiente paso. No rellena el vacío con conocimiento preentrenado. Humano en el circuito Las señales nacen en estado pendiente. Un usuario autorizado las marca revisada, escalada o descartada con justificación y etiquetas. Ese feedback fluye al contexto posterior del Asesor — así, un sesgo que un humano ya rechazó no se vuelve a servir mañana.

Challenges we ran into

Confianza no es probabilidad. Nuestra primera versión mostraba la confianza como porcentaje junto a la etiqueta de impacto, y cada tester lo leyó como "72% de probabilidad de que suba". No lo es — es la fuerza autorreportada del vínculo con la evidencia. Terminamos etiquetándolo explícitamente como no calibrado, y emparejándolo con la evidencia en vez de con el precio. Si un número puede malinterpretarse como promesa, se malinterpretará.

El umbral del gate. Muy bajo y pagábamos el Asesor en cada titular trivial; muy alto y señales genuinamente interesantes nunca recibían la lectura profunda. $\tau = 70$ salió de calibrar contra noticias reales ya ingeridas, y es una variable de entorno precisamente porque el valor correcto depende del volumen de noticias del que estés bebiendo — es una perilla, no una constante.

Separar revisión del ledger. Durante mucho tiempo, Revisar y Briefing fueron una sola pantalla. Estaba sutilmente mal: "señales que aún debo juzgar" y "decisiones que ya tomé" son modos mentales distintos, y mezclarlos hacía que los usuarios relitigaran casos cerrados y saltaran los abiertos. Separarlos en /revisar (una cola con avance automático al siguiente pendiente) y /briefing (un ledger que se siente inmutable, agrupado por watchlist) arregló el flujo más de lo que cualquier cantidad de pulido visual habría logrado.

Los agentes que aconsejan quieren dar órdenes. Los LLM se deslizan hacia "deberías comprar" con una persistencia notable. Mantener al Asesor en priorización de investigación costó trabajo de prompt, restricciones de schema y pruebas que afirman el límite — no solo un system prompt pidiéndolo amablemente.

Loop de re-render en el chat. Un hook useI18n desestabilizado provocaba que el chat se re-renderizara sin fin. Clásico, humillante, y un commit real en nuestro historial.

Accomplishments that we're proud of

11 suites Jest / 79 pruebas en backend, más smoke tests y casos manuales documentados. TypeScript strict mode con next build como chequeo de tipos end-to-end. Un sistema de diseño con columna vertebral: señal antes que espectáculo, un color semántico saturado por tarjeta (impacto), acento lima reservado solo para interacción — foco, activo, CTA. Nunca decorativo. Accesibilidad como requisito, no como aprobado: meta WCAG AA, prefers-reduced-motion obligatorio, touch targets ≥44px en móvil. Documentación viva — el flujo de agentes, los guardrails y la matriz de pruebas en docs/ describen el sistema que realmente se desplegó.

What we learned

Las restricciones generan producto. "Nunca ejecutar una operación" suena a limitación. Es lo que hizo necesarias la cola de revisión, el campo de justificación y el panel de evidencia — y esas son las features a las que la gente realmente reaccionó.

Determinismo donde importa. La decisión arquitectónica más fuerte que tomamos fue trazar una línea: los modelos clasifican y explican, el código calcula. Cada número sobre el que un usuario podría actuar viene de rutas de código deterministas.

Un ruteo barato le gana a un modelo barato. Obtuvimos más margen de costo de un solo gate booleano que el que habríamos conseguido bajando de modelo — y sin sacrificar calidad de salida en las señales que importan.

La evidencia es la interfaz. La confianza en este producto no se construye con un número de confianza. Se construye dejando la fuente siempre a un gesto de distancia.

What's next for CHROMA-MARKET

Calibrar la confianza contra resultados realizados para que el score pueda eventualmente ganarse una lectura probabilística. Ingesta guiada por watchlist — traer noticias de lo que el usuario sigue, no de toda la manguera. Hacer medible el loop de feedback humano: ¿el sesgo rechazado por humanos realmente deja de repetirse? Backtesting del gate: ¿qué dejó pasar $\tau$ que importaba, y qué descartó?

Built With

Share this project:

Updates

Submission history