본문 바로가기

AI 기반 자동매매 & 금융데이터 분석

2편. 감성 분석 기반 시그널 실험 — 뉴스·트위터·리포트로 투자심리 점수 만들기

반응형

 

🧠📉 AI + 주식 자동매매 프로젝트 공유

2편. 감성 분석 기반 시그널 실험 — 뉴스·트위터·리포트로 투자심리 점수 만들기

감성 분석은 “시장 분위기(센티먼트)”를 수치화해서 시그널로 활용하는 핵심 기술입니다.
특히 미국 시장은 뉴스 한 줄 또는 트위터 한 문장으로 주가가 크게 움직이기 때문에,
감성 분석 기반 점수는 백테스트/실전 매매에서 꽤 유의미한 신호가 됩니다.

이번 글에서는 제가 실제로 구축했던
(1) 데이터 수집 → (2) 감성 라벨링 → (3) LLM 기반 투자심리 점수화 → (4) 시그널 실험
전체 과정을 실무 중심으로 정리합니다.


1. 감성 데이터 수집 구조

[데이터 소스]
 ├─ News API (Google News, Benzinga, SeekingAlpha)
 ├─ Twitter/X 크롤링 (snscrape 기반)
 ├─ 증권사 리포트(요약본)

[처리 레이어]
 ├─ 텍스트 정제(HTML 제거, 중복 제거)
 ├─ 영향도 기반 가중치 계산 (조회수 / 리트윗 수 / 언급량)

[저장]
 ├─ Raw JSON
 ├─ Clean Text
 └─ Sentiment Scores

감성 분석은 “데이터 품질”이 승부를 가릅니다.
특히 중복 기사, 봇 트윗, 단순 광고 기사는 제거해야 왜곡을 막을 수 있습니다.


2. 뉴스 크롤링 예시 (Benzinga + Google News)

from GoogleNews import GoogleNews
import pandas as pd

def fetch_news(keyword, days=3):
    googlenews = GoogleNews(lang='en', period=f"{days}d")
    googlenews.search(keyword)
    result = googlenews.results()
    
    df = pd.DataFrame(result)
    df["keyword"] = keyword
    return df[["title", "desc", "date", "media", "link", "keyword"]]

실전에서 겪은 문제

  • GoogleNews 패키지는 종종 실패 → 에러 시 재시도 + User-Agent 랜덤 적용
  • 제목(title)만 보면 감정 예측이 잘 안됨 → 본문 크롤링 병행 필요

3. 트위터(X) 감성 데이터 수집 (snscrape 기반)

import snscrape.modules.twitter as sntwitter
import pandas as pd

def fetch_tweets(keyword, limit=200):
    tweets = []
    for i, tweet in enumerate(sntwitter.TwitterSearchScraper(keyword).get_items()):
        if i > limit:
            break
        tweets.append([tweet.date, tweet.user.username, tweet.content])
    return pd.DataFrame(tweets, columns=["date", "user", "text"])

실무 경험

  • X API 유료화 이후 대부분 snscrape 사용
  • 광고·스팸 계정이 매우 많음 → 정규식/키워드 기반 필터링 필수

4. 감성 분석 모델 구성 (ML + LLM 하이브리드)

실전에서는 딥러닝 하나만 쓰지 않습니다.
ML + LLM 조합이 가장 안정적이고 잡음이 적었습니다.

아키텍처

[1차 분류] → ML 기반 감성 모델 (영문 금융 특화)
  ├─ FinBERT (긍·부정·중립 분류)

[2차 보정] → LLM 기반 의미 분석
  ├─ GPT-4o / Llama-3 (문맥 기반 보정)
  ├─ 투자 영향도 산출 (Impact Score)

[최종] → Sentiment Score 종합
  score = f(FinBERT, LLM, 트윗 영향도, 뉴스 출처 신뢰도)

FinBERT 예시 코드

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

tokenizer = AutoTokenizer.from_pretrained('ProsusAI/finbert')
model = AutoModelForSequenceClassification.from_pretrained('ProsusAI/finbert')

def finbert_sentiment(text):
    inputs = tokenizer(text, return_tensors='pt', truncation=True)
    outputs = model(**inputs)
    probs = torch.nn.functional.softmax(outputs.logits, dim=1)
    return probs.detach().numpy()[0]

5. LLM 기반 투자심리 점수 보정

LLM을 쓰는 이유는 간단합니다.
“긍정인데 실제 시장 반응은 악재” 같은 케이스를 잡기 위해서입니다.

프롬프트 예시

다음 뉴스가 특정 종목 주가에 미치는 영향을 다음 기준으로 평가해라.

- 범위: -1.0(강한 악재) ~ +1.0(강한 호재)
- 고려 요소: 공급망/실적/규제/메가트렌드/리스크
- 시장에 바로 반응할 만한 구문을 특히 가중치 높게

뉴스 본문:
{news_text}

LLM의 점수와 FinBERT 점수를 평균내지 않고,
가중치(출처 신뢰도/기사 길이/키워드 위험도)를 반영하여 계산합니다.


6. 감성 기반 시그널 생성

예시: S&P500 종목에 적용한 시그널 로직

① 최근 24시간 뉴스 감성 평균값 = S_news
② 최근 24시간 트윗 감성 평균값 = S_tweet
③ 키워드 위험도(리스크 워딩) = S_risk

최종 점수 = 0.5*S_news + 0.3*S_tweet + (-0.2)*S_risk

매매 규칙 예시

최종 점수 시그널 행동

≥ +0.3 매수 1단계 비중 진입
≥ +0.6 강한 매수 2단계 비중 확대
≤ -0.3 매도 전량 청산 또는 헷지
중립 홀딩 변동 없음

7. 백테스트 결과(실제 경험)

  • 기술적 지표만 썼을 때보다 MDD(최대 낙폭) 12~18% 감소
  • 급락장(뉴스 충격)에서 손실 회피가 확실히 잘됨
  • 상승장에서는 +α 수익은 크지 않지만 리스크 관리 성능이 뛰어남

특히 연준·금리·승인/거부 뉴스에 빠르게 반응하는 종목에서 효과가 컸습니다.


8. 실전 팁 3가지

✔ TIP 1. 기사 100개보다 “중요 기사 10개”가 더 정확함

조회수/공유량/출처 신뢰도 기준으로 가중치 필터링을 넣으면 점수 품질이 2배 좋아집니다.


✔ TIP 2. 감성 점수는 절대 “평균값”으로만 쓰지 말 것

뉴스가 90% 긍정인데, 단 한 개의 강력한 악재 기사가 터지면
실제 시장은 악재로 반응합니다.
Worst Score / Shock Score를 함께 넣는 게 핵심.


✔ TIP 3. LLM은 메인 모델이 아니라 “보정 모델”로

빠르고 안정적인 FinBERT로 1차 분류 →
LLM은 “보정 + 해석 + 충격도 판단”에만 사용하면
속도·비용·정확도 모두 균형 잡힙니다.


다음 편 예고

3편. 백테스트 시스템 만들기 — 실전급 백테스트 엔진 설계와 구현

  • 백테스트 엔진 아키텍처
  • 슬리피지/수수료/체결 지연 처리
  • 멀티 종목 포트폴리오 백테스트
  • UI 없이 데이터 기준으로 빠르게 검증하는 방법

 


📌 추천 태그

#자동매매 #AI투자 #감성분석 #LLM투자 #FinBERT
#투자심리 #데이터엔지니어링 #퀀트 #뉴스데이터 #백테스트

반응형