Últimamente la IA está causando furor.
En los últimos dos años, el tema principal más fuerte en el mercado, sin importar cómo se mire, básicamente gira en torno a la IA. NVIDIA, AMD, Broadcom, TSMC, Micron, Microsoft, Google, Meta, CoreWeave, Supermicro... Cada cierto tiempo, siempre hay una empresa que se dispara de repente debido a informes financieros, pedidos, gastos de capital, HBM, GPU, computación en la nube, centros de datos y otras palabras clave.
Lo más interesante es que a menudo no suben de forma aislada.
A veces NVIDIA se mueve primero, y luego el mercado comienza a investigar ASIC, módulos ópticos, servidores, refrigeración líquida, energía; otras veces, el informe financiero de Micron supera las expectativas, y luego el capital se fija en HBM, almacenamiento, equipos semiconductores, empaquetado avanzado; y otras veces, los proveedores de la nube aumentan el gasto de capital, y toda la cadena de infraestructura de IA se revaloriza.
Esto se parece más a una rotación de activos dentro del propio tema de la IA. El mercado no está simplemente comprando una acción, sino que bajo una gran narrativa, busca constantemente nuevas ramas, nuevas explicaciones y nuevos objetivos de recuperación tardía.
El problema surge: si estas relaciones se organizan manualmente, es demasiado agotador.
Una persona puede conocer NVIDIA, AMD, Micron, Microsoft, Google, pero es difícil seguir continuamente los cambios de negocio, noticias, clasificaciones sectoriales y vinculaciones de precios de decenas o cientos de acciones TradFi. Lo más problemático es que muchas empresas no tienen una única atribución. Google es al mismo tiempo nube de IA, Gemini y plataforma publicitaria; Microsoft es a la vez nube, Copilot y software empresarial; Broadcom tiene ASIC, chips de red y también está relacionado con centros de datos de IA.
Así que empecé a intentar otra cosa: ¿podemos dejar que el modelo grande nos ayude a construir un mapa de acciones de IA?
No es que el modelo grande nos diga directamente qué comprar, sino que haga lo que mejor sabe hacer: entender los negocios de las empresas, clasificar los subsectores de IA, organizar noticias, explicar eventos y juzgar qué acciones del mismo tema podrían verse afectadas por el movimiento de una empresa. La verdadera decisión de trading se deja al precio, la estadística y la gestión de riesgos.
Primero, colocar las acciones en la cesta correcta
El primer paso de la estrategia no es trading, sino construir el mapa.
El exchange actualmente tiene varios contratos de acciones TradFi, con los que se puede obtener directamente un conjunto de contratos similares a NVDA, AMD, MU, MSFT, GOOGL, SMCI. El sistema primero filtra los contratos de tipo acción, conservando solo los verdaderos activos EQUITY.
Aquí hay un detalle muy importante: no se deben mezclar ETF, índices o ETF apalancados. Por ejemplo, cosas como QQQ, SOXL, EWT, aunque también puedan aparecer en la lista de contratos, no son acciones de empresas. Si se incluyen en la cesta de acciones de IA, las relaciones estadísticas posteriores se contaminarán.
La lógica central de filtrado es aproximadamente así:
python
def refresh_equity_universe(store, force_reanalyze):
ms = exchange.GetMarkets()
symbols = []
for key, market in ms.items():
info = market.get("Info", {}) or {}
sub_type = info.get("underlyingSubType", []) or []
underlying_type = info.get("underlyingType", "")
if ".swap" not in key:
continue
if "TradFi" not in sub_type:
continue
if underlying_type != "EQUITY":
continue
symbol = normalize_symbol(key)
store["markets"][symbol] = {
"symbol": symbol,
"contract": key,
"underlyingType": underlying_type,
"amountPrecision": market.get("AmountPrecision", 0),
"pricePrecision": market.get("PricePrecision", 2),
"ctVal": market.get("CtVal", 1) or 1,
"minQty": market.get("MinQty", 0) or 0,
}
symbols.append(symbol)
Una vez obtenido el conjunto de acciones, el sistema permite que el modelo grande haga un perfil de cada acción.
El perfil no es simplemente escribir "esto es una acción tecnológica", sino determinar a qué subsectores de IA pertenece. Por ejemplo, una acción puede pertenecer a múltiples cestas al mismo tiempo. Google puede pertenecer a nube de IA, modelos de aplicación de IA y publicidad en plataforma de IA; Microsoft puede pertenecer a nube de IA y aplicaciones empresariales de IA; NVIDIA puede pertenecer a potencia computacional de IA y servidores de IA; Micron puede pertenecer a almacenamiento de IA.
El punto clave aquí es: las acciones en la era de la IA no tienen una sola etiqueta, sino múltiples etiquetas.
Cada acción tiene varios atributos: a qué cestas pertenece, qué tan expuesta está, si dentro de esa cesta es más líder o seguidora, qué empresas pueden afectarla y a qué otras empresas puede afectar.
Impuse una fuerte restricción en la estructura de salida del modelo grande:
python
schema = {
"symbol": symbol,
"asset_type": "stock|adr|etf|leveraged_etf|index|fund|other",
"company_name": "",
"sector": "",
"business_summary": "",
"tradable": False,
"model_confidence": 0,
"baskets": [
{
"id": "ai_cloud",
"exposure": 0.0,
"role": "leader|follower|both|observer",
"sub_theme": "",
"reason": ""
}
],
"search_names": [],
"event_keywords": [],
"negative_keywords": [],
"possible_leaders": [],
"possible_followers": [],
"brave_queries": [],
"reason": ""
}
Lo más importante aquí es asset_type. Solo stock y adr pueden entrar en la cesta; ETF, ETF apalancados, índices, fondos, materias primas o ETF regionales se filtran directamente.
Además, reason debe estar en chino, los nombres de campo deben ser consistentes y la confianza debe normalizarse entre 0 y 100. De lo contrario, la página de trading real se vería desordenada.
La información sectorial no se puede basar solo en suposiciones
No basta con tener la clasificación de acciones.
El sector de la IA cambia demasiado rápido. Hoy el mercado puede estar operando GPU, mañana HBM, pasado mañana módulos ópticos, y unos días después, refrigeración líquida, energía, o gastos de capital de proveedores de la nube.
Si solo se confía en el conocimiento propio del modelo grande, es fácil que quede desactualizado. Por eso el sistema necesita fuentes de información en tiempo real.
Aquí conecté BraveSearch. El sistema busca periódicamente información sectorial de cada cesta de IA, y también busca noticias sobre acciones clave dentro de la cesta.
Por ejemplo, para la cesta de almacenamiento de IA, busca palabras clave como Micron, Western Digital, SanDisk, HBM, DRAM, NAND; para la cesta de comunicaciones ópticas de IA, busca información sobre Ciena, Credo, Lumentum, AAOI, módulos ópticos de 800G, 1.6T.
El flujo principal de inteligencia sectorial es:
python
def refresh_basket_intelligence(store):
if not BRAVE_KEY:
return
if not LLM_API_KEY:
return
intel = {}
for bid, basket in store["baskets"].items():
members = sorted(
basket["members"],
key=lambda x: x.get("exposure", 0),
reverse=True
)
top_symbols = [m["symbol"] for m in members[:8]]
basket_news = fetch_basket_news(basket, top_symbols)
stock_news = {}
for symbol in top_symbols[:6]:
profile = store["profiles"].get(symbol, {})
stock_news[symbol] = fetch_stock_news(profile, bid)
Sleep(800)
analysis = analyze_basket_intel_with_llm(
store, basket, top_symbols, basket_news, stock_news
)
if analysis:
analysis["basket"] = bid
analysis["basket_name"] = basket["name"]
analysis["symbols"] = top_symbols
analysis["updatedAt"] = int(time.time())
intel[bid] = analysis
store["basket_intel"] = intel
El objetivo de este paso es crear un contexto dinámico para cada sub-sector de AI.
El modelo grande, basándose en las noticias obtenidas por BraveSearch, generará la tendencia del sector, los catalizadores clave, los riesgos clave, los líderes candidatos, los seguidores candidatos y las relaciones que necesitan ser verificadas posteriormente.
Esto es diferente a preguntarle directamente al modelo grande "¿qué opinas de AI Storage?". Primero le alimentamos las noticias en tiempo real y luego le pedimos que resuma.
No es trading impulsado por noticias, sino que el precio se anormaliza primero
Al principio también pensé en un enfoque impulsado por noticias.
Por ejemplo, al ver que los resultados de una empresa superan las expectativas, dejar que el modelo grande juzgue qué acciones se benefician y luego genere señales directamente. Pero en la práctica, este camino tiene mucho ruido. Hay demasiadas noticias; muchas parecen importantes, pero el mercado no las toma en cuenta.
Por eso invertí la lógica.
Primero se mira el precio, luego las noticias.
Si todos los activos en un basket se mantienen relativamente estables, el sistema solo observa. Solo cuando un activo se desvía claramente del rendimiento promedio del sector, convirtiéndose en el líder actual, el sistema comienza a preguntar: ¿por qué se movió?
Este paso utiliza el ZScore dentro del basket.
En términos simples, se trata de ver si un activo es anormalmente fuerte o débil en comparación con otros del mismo sector. Si un activo se destaca de repente mientras los demás del mismo basket aún no han reaccionado de forma sincronizada, puede existir una oportunidad de propagación.
La lógica central para calcular el ZScore del basket es la siguiente:
python
def calc_basket_zscores(store, symbols):
returns = {}
min_len = 999999
for symbol in symbols:
bars = get_records(
store["markets"][symbol]["contract"],
KLINE_PERIOD,
SYNC_WINDOW + 10
)
if len(bars) < SYNC_WINDOW + 2:
return None
r = calc_returns(bars)[-SYNC_WINDOW:]
returns[symbol] = r
min_len = min(min_len, len(r))
latest = {s: returns[s][-1] for s in symbols}
avg_latest = mean(list(latest.values()))
dev_series = {s: [] for s in symbols}
for i in range(min_len):
row = [returns[s][-min_len + i] for s in symbols]
row_avg = mean(row)
for s in symbols:
dev_series[s].append(returns[s][-min_len + i] - row_avg)
zscores = {}
max_abs = 0
for s in symbols:
sigma = std(dev_series[s])
z = (latest[s] - avg_latest) / sigma if sigma > 0 else 0
zscores[s] = round(z, 3)
max_abs = max(max_abs, abs(z))
return {"zscores": zscores, "maxAbs": max_abs}
Si la desviación máxima de un basket supera el umbral de ruptura, se pasa al estado BREAKOUT_DETECTED.
En ese momento, el sistema no opera de inmediato, sino que comienza a buscar la causa.
Las noticias solo sirven para explicar, las relaciones estadísticas para verificar
Cuando el líder aparece con una ruptura de precio, el sistema captura sus noticias más recientes y luego le pide al modelo grande que determine si esa noticia puede explicar la anomalía del precio.
Pero aquí no se trata simplemente de preguntar "¿esta noticia es alcista?".
El sistema entrega al modelo grande el basket actual, la inteligencia del sector, el estado del precio en tiempo real, el ZScore del líder, los seguidores candidatos, las relaciones estadísticas ya verificadas y las noticias obtenidas por BraveSearch.
El modelo grande debe responder:
(1) Si esta noticia puede explicar la anomalía de precio del líder.
(2) Si la dirección de la noticia coincide con la dirección del precio.
(3) Si se permite operar.
(4) A qué seguidores afecta.
(5) Cuál es el impacto en cada seguidor.
(6) Por qué esos seguidores podrían verse afectados.
La lógica central de búsqueda de causas es la siguiente:
python
def explain_breakout_with_news(store, bid, leader, direction, leader_z, followers, edges):
if not BRAVE_KEY:
return None
if not LLM_API_KEY:
return None
profile = store["profiles"].get(leader, {})
news = fetch_stock_news(profile, bid)
if not news:
return None
event = analyze_breakout_news_with_llm(
store, bid, leader, direction, leader_z,
followers, edges, news
)
if not event:
return None
if not event.get("explains_price_move"):
return None
expected_direction = "bullish" if direction == "long" else "bearish"
if event.get("direction") != expected_direction:
return None
event["confidence"] = normalize_confidence(event.get("confidence", 0))
if event["confidence"] < MIN_NEWS_CONFIDENCE:
return None
if not event.get("trade_allowed"):
return None
return event
Pero incluso si el modelo de lenguaje grande dice que cierto follower podría verse afectado, no se puede operar de inmediato.
También hay que observar la relación estadística de los gráficos de velas.
El sistema calculará la correlación histórica entre las acciones dentro de cada basket, por ejemplo, si después de que el source rompe un nivel, el target suele seguirlo, cuál es la correlación, cuál es el período de desfase óptimo y cuál es el rendimiento medio de seguimiento.
Solo cuando la relación estadística es válida, el par source -> target se convierte en un edge activo.
python
def rebuild_basket_stats(store):
edges = []
for bid, basket in store["baskets"].items():
symbols = [
m["symbol"] for m in basket["members"]
if m["symbol"] in store["markets"]
]
for source in symbols:
for target in symbols:
if source == target:
continue
metric = validate_pair(store, source, target)
if not metric:
continue
source_exp = basket_member_info(basket, source).get("exposure", 0)
target_exp = basket_member_info(basket, target).get("exposure", 0)
score = clamp(
metric["corr"] * 0.3 +
metric["follow"] * 0.4 +
min(source_exp, target_exp) * 0.3,
0, 1
)
edge = {
"basket": bid,
"source": source,
"target": target,
"corr": round(metric["corr"], 3),
"follow": round(metric["follow"], 3),
"lag": metric["lag"],
"avgReturn": round(metric["avgReturn"], 6),
"sample": metric["sample"],
"score": round(score, 3),
"active": (
score >= MIN_EDGE_SCORE and
metric["corr"] >= MIN_CORR and
metric["follow"] >= MIN_FOLLOW_SUCCESS
),
"updatedAt": int(time.time()),
}
edges.append(edge)
store["edges"] = edges
Este paso es importante.
Los modelos de lenguaje grande son buenos entendiendo la lógica, pero el mercado no siempre sigue la lógica. La relación estadística sirve para verificar si esa lógica ha sido transada por el mercado en el pasado.
Se transa la difusión, no la persecución de máximos
Esta estrategia no persigue al leader.
El leader ya ha roto el nivel, y entrar directamente detrás de él fácilmente se convierte en comprar en máximos. Lo que realmente busca la estrategia son las oportunidades de difusión dentro del mismo basket de IA.
Es decir: el leader ya se movió, la noticia tiene sentido, pero algunos followers aún no han reaccionado por completo. Si estos followers y el leader tienen una relación de correlación histórica, entonces podría existir una oportunidad de seguimiento a corto plazo.
En el escaneo de roturas, primero se buscan edges activos, y luego followers rezagados:
python
def scan_price_breakouts(store):
for bid, rt in store["realtime"].items():
if rt["state"] != "BREAKOUT_DETECTED":
continue
leader = rt["leader"]
z = 0
for m in rt["members"]:
if m["symbol"] == leader:
z = m["z"]
direction = "long" if z > 0 else "short"
edges = [
e for e in store["edges"]
if e["active"] and e["basket"] == bid and e["source"] == leader
]
followers = []
for e in edges:
target_rt = find_realtime_member(rt, e["target"])
if target_rt and abs(target_rt["z"]) < BREAKOUT_THRESHOLD * 0.75:
followers.append(e["target"])
if not followers:
continue
Solo cuando la atribución causal de la noticia también es válida, se genera una señal confirmada.
Los objetos de transacción son principalmente los followers.
Si actualmente no hay posición, se abre según la dirección de la señal. Si ya existe una posición en la misma dirección en el mismo contrato, se acumula. Si ya existe una posición en dirección opuesta, primero se cierra y luego se abre en la nueva dirección.
python
def execute_symbol_trade(store, signal, symbol, detail):
direction = signal["direction"]
if TRADE_MODE != "trade":
return open_or_add_position(store, signal, symbol, detail, add_mode=False)
pos = get_position_by_symbol(symbol)
if pos:
pos_dir = position_direction(pos)
if pos_dir == direction:
return open_or_add_position(store, signal, symbol, detail, add_mode=True)
close_existing_position(store, symbol, pos, "reverse_signal")
Sleep(1000)
return open_or_add_position(store, signal, symbol, detail, add_mode=False)
Esto también se debe a que muchas acciones aparecen simultáneamente en múltiples baskets. Una acción puede ser follower en el basket de IA en la nube, y en el basket de aplicaciones de IA ser miembro con otra lógica. Señales de diferentes baskets pueden afectarla al mismo tiempo, por lo que es necesario unificar el tratamiento de la dirección del mismo contrato.
Control de riesgos: primero simple, pero obligatorio
En cuanto al control de riesgos, actualmente se utiliza un stop loss fijo y un trailing stop.
Cuando la pérdida alcanza el umbral, se sale; cuando la ganancia alcanza un cierto porcentaje, se activa el trailing stop, y si la ganancia flotante máxima se reduce demasiado, se bloquean las ganancias y se sale.
python
def monitor_positions(store):
for pos in exchange.GetPosition() or []:
symbol = position_symbol(pos)
if symbol not in store["markets"]:
continue
state = _G(position_state_key(symbol)) or {}
market = store["markets"][symbol]
ticker = exchange.GetTicker(market["contract"])
direction = position_direction(pos)
pnl_pct = (
(ticker["Last"] - pos["Price"]) /
pos["Price"] * 100 *
(1 if direction == "long" else -1)
)
if pnl_pct > state.get("peakPnlPct", 0):
state["peakPnlPct"] = pnl_pct
if not state.get("trailActive") and state.get("peakPnlPct", 0) >= TRAIL_ACTIVATE_PCT:
state["trailActive"] = True
hard_stop = False
if state.get("stop"):
hard_stop = (
ticker["Last"] <= state["stop"]
if direction == "long"
else ticker["Last"] >= state["stop"]
)
trail_hit = False
if state.get("trailActive"):
giveback = state.get("peakPnlPct", 0) - pnl_pct
allowed = max(
1.5,
state.get("peakPnlPct", 0) * TRAIL_GIVEBACK_PCT / 100.0
)
trail_hit = giveback >= allowed
if hard_stop or trail_hit:
close_existing_position(
store, symbol, pos,
"hard_stop" if hard_stop else "trailing_stop"
)
Esta no es la versión final del control de riesgos, pero como primera versión para observación en vivo es suficiente.
Más adelante habrá que añadir límites de posición total a nivel de cuenta, exposición por sector, límite de pérdida diaria, confirmación de ejecución y límite de número de adiciones.
¿Qué nivel de desarrollo tiene el sistema actual?
El sistema actual ya es capaz de completar un ciclo de investigación bastante completo.
Puede escanear automáticamente contratos TradFi de acciones, hacer que el modelo grande genere automáticamente un perfil de cesta larga de acciones, construir automáticamente sub-sectores de IA, obtener automáticamente información del sector y de las acciones mediante BraveSearch, resumir automáticamente la inteligencia del sector, detectar automáticamente la fortaleza en tiempo real de cada cesta, calcular automáticamente las relaciones de seguimiento de las acciones dentro de la cesta, y cuando el líder rompe el precio, realizar una búsqueda causal en las noticias.
Si la explicación de la noticia se sostiene, la dirección es coherente y las relaciones estadísticas también lo respaldan, el sistema genera una señal confirmada. En modo predeterminado solo avisa, no opera en vivo; solo se pasa a modo trade para operar.
La interfaz en vivo ya no solo muestra logs, sino que presenta en formato de tabla el resumen del sistema, el estado en tiempo real de la cesta, la inteligencia del sector, las últimas señales, las últimas operaciones y los últimos pasos. De este modo se puede ver claramente en qué paso se atasca el sistema: si solo hay una anomalía de precio, si las noticias no tienen explicación, si los seguidores no cumplen las condiciones, o si el control de riesgos lo ha bloqueado.
Lo que aún no está maduro
Sinceramente, este sistema todavía no puede considerarse una estrategia de trading automática madura.
Es más un marco de investigación de rotación temática con IA, que ya es capaz de encadenar "perfilado de acciones, inteligencia sectorial, anomalías de precio, búsqueda causal en noticias, validación estadística y señales de trading", pero aún necesita más validación.
Lo que más necesita mejorarse son varios aspectos.
(1) Calidad del perfilado de acciones. El modelo grande a veces incluye acciones marginales en la cesta, por lo que es obligatorio forzar el asset_type para evitar que se mezclen ETFs, índices o fondos regionales.
(2) Calidad de las noticias. Las noticias obtenidas por BraveSearch no siempre son las noticias clave; el modelo necesita juzgar cuáles explican realmente el precio.
(3) Relaciones estadísticas. Que una relación de seguimiento histórica haya existido no garantiza que sea válida en el futuro; es necesario hacer revisiones continuas.
(4) Control de riesgos de trading. Antes de operar automáticamente, hay que añadir límites de posición a nivel de cuenta, exposición por sector, pérdida diaria y confirmación de ejecución.
Por eso ahora es más recomendable ejecutar primero el modo notify, para observar la calidad de las señales confirmadas.
Si después de un tiempo se descubre que las señales realmente explican los movimientos del mercado y que los seguidores suelen reaccionar después, entonces se podría considerar operar con poco capital en vivo.
Para concluir
Esta estrategia no consiste en que la IA ordene directamente la compra.
Siempre he pensado que la posición más adecuada de los grandes modelos en un sistema de trading no es "decirme qué comprar", sino ayudarnos a procesar estructuras de información que son difíciles de mantener manualmente de manera continua: el negocio de las empresas, la pertenencia a sectores, las noticias en tiempo real, la explicación de eventos, la ruta de rotación de capital, los potenciales afectados.
El verdadero trading debe dejarse al precio, a la estadística y al control de riesgos.
Una estrategia cuantitativa en la era de la IA no debería limitarse a conectar una interfaz de modelo grande, sino que debe integrar datos en tiempo real, grafos temáticos, eventos noticiosos, estructuras de precios, ejecución de operaciones y explicación de resultados.
Esta estrategia de grafo de acciones con IA es un intento en esa dirección.
Código fuente de la estrategia: Sistema de trading de contratos TradFi de cadena industrial con IA
- 1





