Type/to search
2
Follow
490
Followers
Detonante: ¿Qué tipo de intento es dibujar las "noticias" en las velas?
Discussions
Created 2026-06-12 09:56:54  Updated 2026-06-16 14:26:06
 0
 159

1. Inicio: Trump dibuja velas a mano

La inmediatez de las noticias es indiscutible. Tras la noticia del ataque aéreo estadounidense contra Irán, el precio del crudo subió bruscamente en poco tiempo. Durante este proceso, factores como las declaraciones de Trump e Irán se entrelazaron y reforzaron mutuamente, empujando la cotización a nuevos rangos tras otro.

img

A menudo bromeamos diciendo que "Trump dibuja velas a mano": muchas veces, las fuertes fluctuaciones del precio no se deben a la evolución de los indicadores técnicos, sino que son directamente provocadas por un tuit, un discurso o una declaración política. El análisis técnico te dice "dónde estás ahora", pero las noticias suelen ser la clave para entender "por qué estás aquí y hacia dónde podrías ir a continuación".

img

La importancia de las noticias está fuera de duda, pero el problema es real: una persona no puede monitorear pantallas y noticias 24/7, y mucho menos captar al instante, en medio de un torrente de información, la noticia que realmente moverá el mercado. Así surge una idea sencilla: ¿podríamos "dibujar" las noticias directamente sobre el gráfico de velas, mostrando precio y noticias en la misma vista? Al menos, resolver primero el problema de "ver".

img

2. Elección de la fuente de noticias: acceso a Jin10 mediante MCP

Para integrar las noticias, el primer paso es encontrar una fuente que se actualice rápidamente y tenga una estructura relativamente estándar. En esta ocasión, hemos optado por Jin10 Data, accediendo a través del Protocolo de Contexto de Modelo (MCP, por sus siglas en inglés), invocando dos tipos de interfaces: list_flash (flash) y list_news (noticias).

No dedicaré mucho espacio a presentar Jin10 en sí; es solo una opción que utilizamos actualmente. La idea y la fuente de noticias específica están desacopladas: mientras una fuente pueda proporcionar títulos/cuerpos con marca de tiempo y se pueda invocar de manera estándar similar a MCP, se puede reemplazar. Lo importante es el diseño de esta capa de integración, no la vinculación a una herramienta concreta.

La conexión y gestión de sesiones de MCP son partes bastante "básicas" pero cruciales de este sistema:

python
def _mcp_post(payload, is_notification=False): global _mcp_session_id, _mcp_req_id if not is_notification: _mcp_req_id += 1 payload["id"] = _mcp_req_id body = json.dumps(payload, ensure_ascii=False).encode() req = urllib.request.Request( JIN10_MCP_URL, data=body, headers=_mcp_headers(), method="POST" ) try: with urllib.request.urlopen(req, timeout=30) as resp: sid = resp.headers.get("Mcp-Session-Id") if sid: _mcp_session_id = sid if resp.status == 202: return {} text = resp.read().decode("utf-8", errors="replace") except urllib.error.HTTPError as e: raise RuntimeError("HTTP %d: %s" % (e.code, e.read().decode()[:400])) except urllib.error.URLError as e: raise RuntimeError("Network: " + str(e)) return _mcp_parse(text) def mcp_init(): global _mcp_ready mcp_rpc("initialize", { "protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": {"name": "fuse-fmz", "version": "1.0"}, }) mcp_notify("notifications/initialized") _mcp_ready = True Log("MCP ready session_id=" + (_mcp_session_id or "(none)"))

Una vez establecida la sesión, obtener las noticias implica dos llamadas a herramientas, seguidas de una normalización y deduplicación uniforme del formato:

python
def refresh_news(): global _cached_news, _last_news_at, _mcp_ready if not JIN10_MCP_TOKEN: return now = int(time.time()) if now - _last_news_at < NEWS_REFRESH_SEC and _cached_news: return _last_news_at = now try: if not _mcp_ready: mcp_init() flash_raw = mcp_call_tool("list_flash") news_raw = mcp_call_tool("list_news") combined = ( _normalize(_extract_items(flash_raw), "flash") + _normalize(_extract_items(news_raw), "news") ) combined.sort(key=lambda x: x["ts"], reverse=True) _cached_news = combined[:80] Log("News updated: %d items" % len(_cached_news)) except Exception as e: Log("News refresh failed: " + str(e)) _mcp_ready = False

Las diferentes fuentes de noticias devuelven nombres de campo muy variados (title/content/introduction, time/ts/created_at...), por lo que se añade una capa intermedia _extract_items + _normalize para unificar todos los formatos en una estructura estándar {ts, time, title, source, full_text}, de modo que la lógica de gráficos y filtrado posterior no necesite saber de qué interfaz provienen los datos.

Nota: Se requiere solicitar la API de MCP para su uso.

img

3. Mecánica central: hacer que las noticias "crezcan" en las velas

Ahora viene la parte realmente interesante de esta herramienta: colocar las noticias y las velas en el mismo gráfico.

img

Hemos añadido una segunda serie en el gráfico, de tipo flags, vinculada a la serie de velas, que actúa como "capa de marcadores de noticias":

python
def init_chart(symbol): global _chart _chart = Chart({ "__isStock": True, "chart": {"style": {"fontFamily": "Microsoft YaHei, SimHei, Arial, sans-serif"}}, "title": {"text": "FUSE " + symbol}, "xAxis": {"type": "datetime"}, "series": [ { "id": "kline", "type": "candlestick", "name": symbol, "data": [], }, { "type": "flags", "name": "News", "onSeries": "kline", "shape": "circlepin", "color": "#F59E0B", "fillColor": "#F59E0B", "width": 16, "data": [], }, ], }) _chart.reset()

En cada actualización, primero se actualizan incrementalmente los datos de las velas K, luego se filtran las noticias "importantes" según palabras clave y se alinean temporalmente con la barra K correspondiente:

python
def draw_chart(records): global _last_bar_time, _last_news_hash, _flagged_news_ts if not _chart or not records: return # Detecta si hay actualizaciones en las noticias; si las hay, reinicia el gráfico y redibuja news_hash = hash(tuple(n.get("ts", 0) for n in _cached_news[:10])) news_changed = (news_hash != _last_news_hash) if news_changed: _chart.reset() _last_bar_time = 0 _last_news_hash = news_hash _flagged_news_ts = set() # serie 0: velas K, añadir incrementalmente for r in records: t = r['Time'] bar = [t, r['Open'], r['High'], r['Low'], r['Close']] if t > _last_bar_time: _chart.add(0, bar) _last_bar_time = t elif t == _last_bar_time: _chart.add(0, bar, -1) # serie 1: bandera de noticias por palabra clave, alineada con el tiempo de la barra K if not _cached_news: return kws = [k.strip() for k in NEWS_KEYWORD.split("|") if k.strip()] kw_news = [n for n in _cached_news if not kws or any(k in n.get("full_text", n["title"]) for k in kws)] if not kw_news: return p_ms = PERIOD_MS.get(KLINE_PERIOD, 60000) first = records[0]['Time'] last = records[-1]['Time'] by_bar = {} for n in kw_news: if not n.get("ts"): continue key = (n["ts"] // p_ms) * p_ms if key not in by_bar: by_bar[key] = n for ts, item in sorted(by_bar.items()): if not (first <= ts <= last): continue if ts in _flagged_news_ts: continue _chart.add(1, { "x": ts, "title": "📰", "text": item["title"][:100], }) _flagged_news_ts.add(ts)

El resultado es: cada vez que aparece un marcador 📰 en el gráfico, al pasar el ratón sobre él se puede ver el titular de la noticia correspondiente, y su ubicación coincide exactamente con la vela K del momento en que ocurrió la noticia. Los puntos de inflexión de los precios y los momentos de las noticias se presentan por primera vez de manera intuitiva en la misma imagen, eliminando la necesidad de cambiar entre dos ventanas para entender "qué causó este tramo".

NEWS_KEYWORD admite múltiples palabras clave separadas por | (por ejemplo, "Irán|subida de tipos|no agrícolas|aranceles"). El sistema priorizará marcar las noticias que contengan estas palabras clave en el gráfico, evitando que noticias irrelevantes saturen la pantalla.

IV. Panel de estado: cotización, posiciones, noticias en una sola pantalla

Además del gráfico, hemos creado un conjunto de tablas de estado que se muestran mediante LogStatus, que incluyen cotización en tiempo real, capital y ganancias/pérdidas de la cuenta, posiciones actuales, noticias que coinciden con palabras clave y los últimos titulares completos:

python
def make_status(symbol, ticker, positions, equity): # 4. Noticias por palabra clave (mostrar la palabra clave encontrada, no la fuente) kws = [k.strip() for k in NEWS_KEYWORD.split("|") if k.strip()] kw_rows = [] for item in _cached_news[:40]: t = item.get("time") or (_D(item["ts"]) if item.get("ts") else "-") title = item["title"][:90] text = item.get("full_text", item["title"]) hit_kws = [k for k in kws if k in text] if hit_kws: kw_rows.append([t, "/".join(hit_kws), title]) if not kw_rows: kw_rows = [["-", "-", "Sin noticias relacionadas con palabras clave"]]

Además, se ha añadido una interfaz de comandos manuales simple: abrir largo, abrir corto, cerrar largo, cerrar corto, cerrar todo con un clic y modificar la cantidad de la orden, todo mediante GetCommand():

python
def handle_command(symbol): global _cur_amount, _last_news_at cmd = GetCommand() if not cmd: return Log("CMD: " + cmd) parts = cmd.split(":") key = parts[0] val = parts[1] if len(parts) > 1 else "" if key == "openLong": market_order(symbol, "openLong", _cur_amount) elif key == "openShort": market_order(symbol, "openShort", _cur_amount) elif key == "closeLong": market_order(symbol, "closeLong", _cur_amount) elif key == "closeShort": market_order(symbol, "closeShort", _cur_amount) elif key == "closeAll": close_all(symbol) elif key == "amount": _cur_amount = float(val) Log("Amount updated: " + str(_cur_amount))

En conjunto, FUSE es esencialmente un panel de monitoreo que combina "información integrada + ejecución manual": reúne precios, noticias, posiciones y estado de la cuenta en la misma pantalla tanto como sea posible. La decisión sigue recayendo completamente en el humano — no juzga por ti, pero intenta que no te pierdas demasiadas cosas al momento de decidir.

V. Limitaciones: El ser humano sigue siendo la mayor variable

Las limitaciones de esta versión son bastante evidentes y no queremos evadirlas.

Primero, la correspondencia entre noticias y precios es de "granularidad gruesa" — simplemente cuelga las noticias en la vela del K-line correspondiente según su marca de tiempo, sin realizar una interpretación a nivel de contenido. Depende completamente del humano juzgar si una noticia es positiva o negativa, y si desencadenará movimientos en el mercado.

Segundo, el filtrado por palabras clave es en sí mismo un enfoque bastante básico. Que una noticia cumpla con las palabras clave no significa que sea realmente importante, y que no las cumpla tampoco significa que no lo sea. En este proceso, la percepción, experiencia e incluso el estado de ánimo del individuo ese día pueden pesar mucho — ante la misma noticia, diferentes personas mirando el mismo gráfico pueden llegar a conclusiones completamente distintas.

Tercero, todo el flujo sigue siendo "humano en el circuito", por lo que la velocidad de respuesta está limitada por la reacción humana, y en muchos casos la reacción del mercado a las noticias ocurre en minutos o incluso segundos.

Si les interesa esta dirección, en el futuro intentaremos crear una versión automatizada basada en un modelo grande, donde el modelo realice una interpretación preliminar de las noticias y evalúe su importancia, sirviendo como apoyo o incluso reemplazo de la decisión humana. Si están interesados, pueden seguirnos.

Código fuente de la estrategia: Sistema de detonador de noticias en tiempo real

Comment
All comments (0)
No data
No data
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)