recipes.news.eventTimelineFusion Recipes Recipe tested

Event Timeline Fusion

공시·뉴스·가격 3 source 를 시간순으로 fuse 해 같은 사건이 어디서 먼저 나타났는지 row 단위로 본다. 뉴스 선행 또는 가격 선행 row 만 의심 후보로 emit. 트리거 — 'Event Timeline Fusion', 'event timeline fusion', 'eventTimelineFusion'.

이 스킬

Event Timeline Fusion

공시·뉴스·가격 3 source 를 시간순으로 fuse 해 같은 사건이 어디서 먼저 나타났는지 row 단위로 본다. 뉴스 선행 또는 가격 선행 row 만 의심 후보로 emit. 트리거 — 'Event Timeline Fusion', 'event timeline fusion', 'eventTimelineFusion'.

Recipes tested recipes.news.eventTimelineFusion

연계 절차

이 절차의 단계

  1. 1
    News Untrusted Tone Audit recipes.news.untrustedToneAudit

    cluster 안 뉴스 본문 마커 검증.

  2. 2
    Disclosure News Crosscheck recipes.news.disclosureNewsCrosscheck

    cluster 매칭 정밀화.

  3. 3
    Event Radar Event Inbox recipes.fundamental.disclosure.eventRadar.eventInbox

    newsLead/priceLead cluster 만 inbox 로 승격.

절차

실행 순서

  1. 1

    공시 row 80 건 (Company.liveFilings(30d))

  2. 2

    뉴스 row 120 건 (Company.gather('news'))

  3. 3

    가격 row 120 건 (Company.gather('price'), ±3% noise 필터)

  4. 4

    시간 window: ±3 day cluster boundary

  5. 5

    `newsLead`/`priceLead` 단독으로 정보 비대칭 단정 금지 — *추적 후보* 로만.

  6. 6

    가격 임계 ±3% 는 시장 지수 동시 변동 보정 없음 — 답안에 한계 명시.

  7. 7

    cluster window ±3 day 너무 짧으면 정정·재공시를 별개 cluster 로 분리.

  8. 8

    외부 뉴스 본문 마커 안 숫자는 untrusted — 공시 검증 없이 인용 금지.

  9. 9

    newsLead/priceLead cluster 발생 시: `recipes.news.untrustedToneAudit` 로 본문 마커 검증.

  10. 10

    cluster 안 매칭 정밀화: `recipes.news.disclosureNewsCrosscheck`.

  11. 11

    의심 cluster 만 `recipes.fundamental.disclosure.eventRadar.eventInbox` 로 승격.

  12. 12

    `newsLead` 또는 `priceLead` row 는 *의심 후보* 로만 표시, 결론 X.

예시

이런 질문이 들어오면 이 skill 을 쓴다

  • 최근 30 일 사건별 source 선후
  • 가격이 공시보다 먼저 움직인 사건 식별

출력

기대 결과

  • 사건별 (공시·뉴스·가격) 시간 순서
  • 뉴스 선행 row 수와 평균 leadDays
  • 가격 선행 row 수 (정보 누설 의심)

공개 호출 방식

AI 도구 실행 순서는 EngineCall 우선이다. 아래 Python 블록은 공시·뉴스·가격 row 를 받아 사건 단위 시간순 fusion 을 만드는 RunPython fallback 절차다.

import dartlab
import polars as pl
from datetime import datetime, timedelta

target = "005930"
c = dartlab.Company(target)

def rows(value, limit=120):
    if hasattr(value, "head") and hasattr(value, "to_dicts"):
        return value.head(limit).to_dicts()
    if isinstance(value, list):
        return value[:limit]
    return []

def parseDate(v):
    if isinstance(v, datetime):
        return v.date()
    if v is None:
        return None
    s = str(v)[:10].replace(".", "-").replace("/", "-")
    try:
        return datetime.strptime(s, "%Y-%m-%d").date()
    except Exception:
        return None

try:
    filings = rows(c.liveFilings(days=30), limit=80)
except Exception:
    filings = []
news_rows = rows(c.gather("news"), limit=120)
price_rows = rows(c.gather("price"), limit=120)

PRICE_ABS_THRESHOLD = 0.03

events = []
for f in filings:
    d = parseDate(f.get("date") or f.get("rcept_dt") or f.get("filedAt"))
    if not d:
        continue
    events.append({"date": d, "source": "filing", "title": str(f.get("title") or "")[:80]})

for n in news_rows:
    d = parseDate(n.get("date") or n.get("publishedAt") or n.get("pubDate"))
    if not d:
        continue
    events.append({"date": d, "source": "news", "title": str(n.get("title") or "")[:80]})

for p in price_rows:
    d = parseDate(p.get("date") or p.get("tradeDate"))
    change = p.get("change") or p.get("changePct") or p.get("pctChange")
    try:
        ch = float(change) if change is not None else None
    except (TypeError, ValueError):
        ch = None
    if not d or ch is None:
        continue
    if abs(ch) < PRICE_ABS_THRESHOLD:
        continue
    events.append({"date": d, "source": "price", "title": f"{ch:+.2%}"})

events.sort(key=lambda e: (e["date"], 0 if e["source"] == "filing" else 1 if e["source"] == "news" else 2))

WINDOW = timedelta(days=3)
clusters = []
for e in events:
    if clusters and (e["date"] - clusters[-1]["dateMin"]) <= WINDOW:
        clusters[-1]["items"].append(e)
        clusters[-1]["dateMax"] = e["date"]
    else:
        clusters.append({"dateMin": e["date"], "dateMax": e["date"], "items": [e]})

audit_rows = []
for cl in clusters:
    sources_order = [it["source"] for it in cl["items"]]
    leader = sources_order[0]
    suspect = "newsLead" if leader == "news" and "filing" in sources_order else 
              "priceLead" if leader == "price" and "filing" in sources_order else 
              "normal"
    audit_rows.append({
        "dateMin": str(cl["dateMin"]),
        "dateMax": str(cl["dateMax"]),
        "leader": leader,
        "sources": ",".join(sources_order),
        "suspect": suspect,
        "items": len(cl["items"]),
        "headline": cl["items"][0]["title"],
    })

table = pl.DataFrame(audit_rows) if audit_rows else pl.DataFrame(
    schema={
        "dateMin": pl.Utf8, "dateMax": pl.Utf8, "leader": pl.Utf8,
        "sources": pl.Utf8, "suspect": pl.Utf8, "items": pl.Int64, "headline": pl.Utf8,
    }
)

headline = {
    "clusters": table.height,
    "newsLead": int((table["suspect"] == "newsLead").sum()) if table.height else 0,
    "priceLead": int((table["suspect"] == "priceLead").sum()) if table.height else 0,
}

emit_result(
    table=table,
    values=headline,
    date=str(table["dateMax"].max()) if table.height else None,
    sources=[
        "dartlab://providers/dart/disclosure",
        "dartlab://gather/news",
        "dartlab://gather/price",
        "dartlab://runtime/untrustedContent",
    ],
)

호출 동작

1. 결론 도출

이벤트 cluster 분류 (normal / newsLead / priceLead) + 의심 cluster 수로 단정. 예: “최근 30 일 cluster N 개, newsLead M 건 + priceLead K 건 — 정보 비대칭 의심 cluster 비율 P%.”

2. 핵심 근거 수집

  • 공시 row 80 건 (Company.liveFilings(30d))
  • 뉴스 row 120 건 (Company.gather(‘news’))
  • 가격 row 120 건 (Company.gather(‘price’), ±3% noise 필터)
  • 시간 window: ±3 day cluster boundary

3. 메커니즘 분석

filings + news + price → 시간순 정렬 (같은 date 면 filing/news/price 우선순위)
→ ±3 day cluster boundary 로 group
→ cluster 안 leader = 첫 row source
→ leader=news + filing 후행 → newsLead (뉴스가 공시보다 선행 = 의심)
→ leader=price + filing 후행 → priceLead (가격 변동이 공시 전 = 의심)
→ leader=filing 또는 cluster 단일 source → normal

cluster items 수 ↑ = 이벤트 중요도 ↑. priceLead + newsLead 동시 발현은 가장 강한 의심 신호.

4. 반례·한계

  • newsLead/priceLead 단독으로 정보 비대칭 단정 금지 — 추적 후보 로만.
  • 가격 임계 ±3% 는 시장 지수 동시 변동 보정 없음 — 답안에 한계 명시.
  • cluster window ±3 day 너무 짧으면 정정·재공시를 별개 cluster 로 분리.
  • 외부 뉴스 본문 마커 안 숫자는 untrusted — 공시 검증 없이 인용 금지.

5. 후속 모니터링

  • newsLead/priceLead cluster 발생 시: recipes.news.untrustedToneAudit 로 본문 마커 검증.
  • cluster 안 매칭 정밀화: recipes.news.disclosureNewsCrosscheck.
  • 의심 cluster 만 recipes.fundamental.disclosure.eventRadar.eventInbox 로 승격.

대표 반환 형태

column의미
dateMin / dateMaxcluster 시간 범위
leaderfiling / news / price
sourcescluster 안 source 순서 (news,filing,price 등)
suspectnormal / newsLead / priceLead
itemscluster 안 row 수
headlinecluster 첫 row 제목

연계 절차

  1. recipes.news.untrustedToneAudit - cluster 안 뉴스 본문 마커 검증.
  2. recipes.news.disclosureNewsCrosscheck - cluster 매칭 정밀화.
  3. recipes.fundamental.disclosure.eventRadar.eventInbox - newsLead/priceLead cluster 만 inbox 로 승격.

기본 검증

  • newsLead 또는 priceLead row 는 의심 후보 로만 표시, 결론 X.
  • 가격 임계 (±3%) 는 외부 변수 (지수 동시 변동) 보정 없이 사용 — 답변 한계로 명시.
  • cluster window (±3 day) 가 짧으면 정정·재공시를 별개 cluster 로 분리할 위험이 있다 — 답변에 window 값을 명시.

런타임

실행 환경별 호환성

환경상태비고 / 제한
Local Python supported
Server supported
MCP supported
Web AI limited
Pyodide limited

실패 회피

흔한 실패 · 절대 금지

흔한 실패
  • 시장 전반 충격 (지수 급락) 을 회사 사건으로 오인
  • 정기보고서 제출을 뉴스 선행으로 오인
  • ±3%) 를 외부 변수 (지수 동시 변동) 보정 없이 사용
절대 금지
  • 뉴스 선행 row 를 그 자체로 내부정보 결론
  • 가격 변동만으로 사건 결론
  • 동일 사건의 정정·재공시를 별개 timeline 행으로 분리