전체 목차
이 파이프라인으로 무엇을 할 수 있는가
주식 블로그를 운영하면서 가장 힘든 게 뭔지 아시나요? 매일 장 마감 후 직접 시황을 확인하고, 글을 쓰고, 발행하는 반복 노동입니다. 이 파이프라인은 그 작업 전체를 자동화합니다.
🎯 매일 자동으로 생성되는 것들
💎 장기 데이터 축적의 진짜 가치
처음 1주일은 그냥 “오늘 코스피 시황” 블로그입니다. 하지만 3~6개월이 지나면 완전히 다른 이야기가 됩니다.
- 심리점수 70 이상인 날 다음날 코스피 평균 수익률은? (백테스팅)
- 반도체 키워드가 등장한 날의 평균 등락률 분석
- 외국인 순매수 상위 10일 — 어떤 뉴스가 공통적으로 있었나?
- 월요일 vs 금요일 코스피 평균 성과 비교 (6개월치)
- 나만의 KOSPI 심리 지수 차트 (경쟁 블로그 전혀 없음)
Ollama를 홈랩 서버에서 실행하기 때문에 ChatGPT API나 Claude API 비용이 전혀 없습니다. qwen2.5:14b 모델은 RTX 3060 12GB에서 충분히 실행 가능합니다. 추가 비용은 서버 전기세뿐입니다.
전체 시스템 아키텍처
5개 서비스가 순서대로 연결되어 매일 자동 실행됩니다. 각 단계는 독립적으로 재실행할 수 있어 중간에 실패해도 해당 단계부터 다시 시작할 수 있습니다.
🗺️ 전체 데이터 흐름
FinanceDataReader로 당일 시가·고가·저가·종가·거래량을 가져옵니다. 실패 시 Yahoo Finance로 자동 전환됩니다.🔧 서비스별 역할 분담
| 서비스 | 역할 | 위치 | 대체 수단 |
|---|---|---|---|
| n8n | 전체 오케스트레이션 · 스케줄링 · 알림 | 192.168.1.253:5678 | cron + systemd |
| Python | 데이터 수집 · DB 저장 · HTML 생성 | 홈랩 서버 | – |
| FinanceDataReader | KRX KOSPI 지수 데이터 | pip 라이브러리 | Yahoo Finance (yfinance) |
| BeautifulSoup | 네이버 금융 뉴스 파싱 | pip 라이브러리 | RSS 피드 파싱 |
| Ollama + qwen2.5:14b | AI 시황 분석 · 블로그 제목 생성 | 192.168.1.253:11434 | llama3.1:8b (빠름) |
| SQLite | 모든 데이터 영속 저장 | kospi.db 파일 | PostgreSQL |
| WordPress REST API | 블로그 초안 자동 등록 | agibop.com | 수동 복붙 |
DB 스키마 설계 — 데이터 창고 만들기
이 파이프라인의 핵심은 데이터를 구조화해서 쌓는 것입니다. 잘 설계된 스키마가 나중에 백테스팅과 통계 분석을 가능하게 합니다.
🗄️ 5개 테이블 구조
| 테이블 | 저장 내용 | 핵심 컬럼 |
|---|---|---|
market_daily | 일별 KOSPI 지수 OHLCV | trade_date, close_price, change_rate, foreign_net |
news_raw | 당일 뉴스 헤드라인 원문 | trade_date, title, url, source |
ai_analysis | Ollama AI 분석 결과 | sentiment_score, summary_3line, keywords, market_context |
blog_posts | 생성된 블로그 초안 | title, content_html, wp_post_id, wp_status |
pipeline_log | 파이프라인 실행 기록 | step, status, message, duration_sec |
-- 일별 지수 데이터 (파이프라인의 핵심 원본) CREATE TABLE IF NOT EXISTS market_daily ( trade_date DATE NOT NULL UNIQUE, -- 거래일 (중복 방지) open_price REAL, -- 시가 high_price REAL, -- 고가 low_price REAL, -- 저가 close_price REAL NOT NULL, -- 종가 (필수) change_rate REAL, -- 등락률 % (+상승/-하락) foreign_net BIGINT DEFAULT 0, -- 외국인 순매수 (억원) created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- AI 분석 결과 (심리 점수가 핵심) CREATE TABLE IF NOT EXISTS ai_analysis ( trade_date DATE NOT NULL UNIQUE, summary_3line TEXT, -- 핵심 3줄 요약 sentiment_score INTEGER -- 투자 심리 1~100 CHECK(sentiment_score BETWEEN 1 AND 100), keywords TEXT, -- JSON 배열 market_context TEXT, -- 시황 인과관계 해석 tomorrow_watch TEXT -- 내일 관전 포인트 ); -- 30일 통합 뷰 (분석용 — 조인 없이 한 번에) CREATE VIEW IF NOT EXISTS v_daily_summary AS SELECT m.trade_date, m.close_price, m.change_rate, a.sentiment_score, a.keywords, b.wp_status FROM market_daily m LEFT JOIN ai_analysis a ON m.trade_date = a.trade_date LEFT JOIN blog_posts b ON m.trade_date = b.trade_date ORDER BY m.trade_date DESC LIMIT 30;
# SQLite DB 생성 + 스키마 적용 sqlite3 kospi.db < schema.sql # 제대로 만들어졌는지 확인 sqlite3 kospi.db ".tables" # 출력: ai_analysis blog_posts market_daily news_raw pipeline_log
Python 파이프라인 완전 해설
kospi_pipeline.py는 856줄짜리 단일 파일로, 수집부터 블로그 초안 생성까지 모든 로직이 담겨 있습니다. 각 단계별로 어떻게 동작하는지 핵심만 짚겠습니다.
📥 1단계 — KOSPI 지수 수집
import FinanceDataReader as fdr # 1순위: FinanceDataReader (KRX 공식 데이터) df = fdr.DataReader("KOSPI", trade_date, trade_date) # 자동 fallback: FDR 실패 시 Yahoo Finance로 전환 # ^KS11 = KOSPI 티커 (Yahoo Finance 코드) import yfinance as yf ticker = yf.Ticker("^KS11") df = ticker.history(start=trade_date, end=trade_date)
📰 2단계 — 뉴스 헤드라인 수집
from bs4 import BeautifulSoup url = "https://finance.naver.com/news/news_list.naver?mode=HEAD" resp = requests.get(url, headers=headers, timeout=10) soup = BeautifulSoup(resp.text, "html.parser") # 헤드라인 최대 15개 수집 items = soup.select(".realtimeNewsList .articleSubject a")[:15] for item in items: title = item.get_text(strip=True) db.execute("INSERT OR IGNORE INTO news_raw ...") # OR IGNORE: 같은 날 중복 제목 자동 방지
🤖 3단계 — Ollama AI 분석 (가장 중요한 단계)
수집된 지수 데이터와 뉴스를 하나의 프롬프트로 조합해 Ollama에 보냅니다. 핵심은 출력 형식을 JSON으로 강제해서 파싱을 쉽게 만드는 것입니다.
prompt = f"""당신은 한국 주식 시장 전문 애널리스트입니다.
{trade_date} 코스피 데이터를 분석하고 JSON으로만 답변하세요.
## 코스피 데이터
- 종가: {close_price:,.2f}p ({change_rate:+.2f}%)
## 오늘 주요 뉴스
{news_text}
## 요청 형식 (JSON만, 다른 텍스트 금지)
{{
"summary_3line": "요약1\\n요약2\\n요약3",
"sentiment_score": 1~100 정수,
"keywords": ["키워드1", ..., "키워드5"],
"market_context": "인과관계 3~4문장",
"tomorrow_watch": "내일 관전 포인트",
"blog_title_candidates": ["제목1", "제목2", "제목3"]
}}"""
payload = {
"model": "qwen2.5:14b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.3} # 낮을수록 일관된 분석
}
resp = requests.post(f"{OLLAMA_URL}/api/generate",
json=payload, timeout=180)
# 마크다운 코드블록 제거 후 JSON 파싱
clean = re.sub(r"```(?:json)?|```", "", result_text).strip()
analysis = json.loads(clean)AI 분석에서 창의성보다 일관성이 중요합니다. temperature가 낮을수록 매일 비슷한 형식으로 분석이 나와 DB 파싱이 안정적으로 됩니다. 블로그 제목 후보 생성만 살짝 높은 온도(0.5)를 써도 좋습니다.
📝 4단계 — HTML 블로그 초안 자동 생성
수집된 모든 데이터를 조합해 바로 WordPress에 붙여넣을 수 있는 HTML을 생성합니다. 투자 심리 점수에 따라 색상도 자동으로 바뀝니다.
| 심리 점수 | 레이블 | 색상 |
|---|---|---|
| 1 ~ 30 | 😨 비관적 | ■ 빨강 |
| 31 ~ 45 | 🟡 다소 약세 | ■ 주황 |
| 46 ~ 55 | ⚖️ 중립 | ■ 회색 |
| 56 ~ 70 | 🟢 다소 강세 | ■ 초록 |
| 71 ~ 100 | 🚀 낙관적 | ■ 진초록 |
⚡ CLI 단계별 실행 옵션
# 전체 실행 (기본) python kospi_pipeline.py # 특정 날짜 지정 python kospi_pipeline.py --date 2026-05-29 # 수집만 (뉴스·지수) python kospi_pipeline.py --step collect # AI 분석만 (수집은 이미 됐을 때) python kospi_pipeline.py --step analyze # 블로그 초안만 재생성 python kospi_pipeline.py --step post
n8n 워크플로우 구성 — 오케스트레이터
Python 스크립트가 일을 하는 일꾼이라면, n8n은 매니저입니다. 언제 실행할지, 성공했는지, 실패하면 누구에게 알릴지를 n8n이 담당합니다.
🔗 워크플로우 노드 구성 (9개 노드)
| 순서 | 노드 | 유형 | 역할 |
|---|---|---|---|
| 1 | ⏰ 평일 15:40 실행 | Schedule Trigger | Cron: 40 15 * * 1-5 |
| 2 | 📊 1단계: 수집 | Execute Command | Python 수집 스크립트 실행 |
| 3 | ✅ 수집 성공 확인 | IF | exit code 0 여부 분기 |
| 4 | 🤖 2단계: AI 분석 | Execute Command | Python 분석 스크립트 실행 |
| 5 | 📝 3단계: 초안 생성 | Execute Command | Python 포스팅 생성 스크립트 |
| 6 | 🗄️ DB 데이터 읽기 | SQLite | blog_posts 테이블에서 초안 조회 |
| 7 | 🌐 WordPress 발행 | HTTP Request | REST API로 draft 저장 |
| 8 | 💾 Post ID 저장 | SQLite | WordPress 반환 Post ID를 DB에 기록 |
| 9 | 📱 Telegram 알림 | Telegram | 완료/오류 알림 전송 |
🔑 크리덴셜 설정 (3개 필요)
DB 파일 경로:
/opt/kospi-pipeline/kospi.dbn8n: HTTP Basic Auth → 아이디 + Application Password 입력
/newbot → 토큰 발급본인 Chat ID: @userinfobot에 아무 메시지 → ID 확인
n8n: Telegram API → 토큰 입력
📤 n8n 워크플로우 임포트 방법
1. http://192.168.1.253:5678 접속 2. 왼쪽 메뉴 → Workflows → New Workflow 3. 우측 상단 ⋮ 메뉴 → Import from File 4. n8n_kospi_workflow.json 선택 5. 각 노드 클릭 → 크리덴셜 선택 (SQLite / WordPress / Telegram) 6. 우측 상단 토글 → Active ON 7. 내일 15:40까지 기다리거나 → Test workflow로 즉시 테스트
- n8n 서버가 UTC로 실행 중이라면 Cron을
30 06 * * 1-5로 설정하세요 (UTC+9 보정) - 서버 시간대 확인:
docker exec n8n date - KST로 맞추려면: Docker Compose에
TZ=Asia/Seoul환경변수 추가
실제 결과물 확인 & 발행 프로세스
📋 자동 생성되는 블로그 포스팅 구성
| 섹션 | 내용 | 데이터 소스 |
|---|---|---|
| 헤더 배지 | 날짜 · 코스피 시황 · AI 자동 분석 레이블 | trade_date |
| 핵심 지표 카드 | 종가 · 등락률 · 투자 심리 점수 · 거래량 | market_daily + ai_analysis |
| 키워드 배지 | 당일 AI 추출 핵심 키워드 5개 | ai_analysis.keywords |
| 핵심 요약 | AI 생성 3줄 요약 | ai_analysis.summary_3line |
| 시황 해석 | 뉴스 근거 기반 인과관계 분석 | ai_analysis.market_context |
| OHLCV 테이블 | 시가·고가·저가·종가 정리표 | market_daily |
| 내일 관전 포인트 | AI 생성 2~3가지 주목 사항 | ai_analysis.tomorrow_watch |
| 면책 조항 | 투자 면책 문구 (자동 삽입) | 하드코딩 |
| 이미지 프롬프트 | ComfyUI용 대표 이미지 생성 프롬프트 | ai_analysis.keywords 기반 |
📱 Telegram 완료 알림 예시
✅ 코스피 시황 파이프라인 완료
📅 날짜: 2026년 05월 29일
📊 종가: 2,687.25p (▲1.24%)
🧠 심리점수: 72/100
📝 초안: agibop.com/?p=1234
검토 후 발행 예정
✅ 발행 프로세스 (하루 5분이면 완료)
지금 바로 시작하기 — 설치 & 실행
📦 패키지 설치
# 필수 패키지 pip install FinanceDataReader requests beautifulsoup4 python-dotenv # fallback용 (FinanceDataReader 실패 시) pip install yfinance # 설치 확인 python -c "import FinanceDataReader; print('FDR OK')"
⚙️ 환경변수 설정
cp .env.example .env nano .env # 반드시 수정할 항목: DB_PATH=./kospi.db OLLAMA_URL=http://192.168.1.253:11434 # 본인 Ollama 주소 OLLAMA_MODEL=qwen2.5:14b # WordPress (선택, 설정하면 자동 발행) WP_URL=https://agibop.com WP_USER=admin WP_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
🚀 첫 실행 & 결과 확인
# 1. DB 초기화 sqlite3 kospi.db < schema.sql # 2. 최근 거래일로 테스트 실행 python kospi_pipeline.py --date 2026-05-29 # 정상 출력 예시: # [15:41:02] [INFO] [collect_market] ✓ 수집 완료 | 종가: 2,687.25 | 등락: +1.24% # [15:41:05] [INFO] [collect_news] ✓ 뉴스 12건 수집 완료 # [15:42:18] [INFO] [ollama_analyze] ✓ 분석 완료 | 심리점수: 72 | 소요: 73.2s # [15:42:19] [INFO] [generate_post] ✓ 블로그 초안 생성 완료 # [15:42:19] [INFO] [main] ✅ 전체 파이프라인 완료 | 총 소요: 77.4초 # 3. DB 결과 확인 sqlite3 kospi.db "SELECT trade_date, close_price, change_rate, sentiment_score FROM v_daily_summary LIMIT 5;" # 4. 생성된 블로그 HTML 확인 sqlite3 kospi.db "SELECT title, substr(content_html, 1, 200) FROM blog_posts ORDER BY created_at DESC LIMIT 1;"
- ✓Python 3.10+ 필요 (3.12 권장)
- ✓Ollama 실행 중 확인:
curl http://192.168.1.253:11434/api/tags - ✓qwen2.5:14b 모델 다운로드:
ollama pull qwen2.5:14b - !주말·공휴일에 실행하면 데이터 없음 경고가 뜨지만 오류는 아닙니다
- iOllama 분석은 RTX 3060 기준 약 60~90초 소요됩니다
데이터 축적 후 가능한 심화 활용
처음 한 달은 그냥 “일일 시황 블로그”입니다. 하지만 데이터가 쌓일수록 다른 블로그가 절대 못 따라하는 콘텐츠가 생깁니다.
📊 3개월 후 가능한 분석 쿼리 예시
-- 1. 심리점수 70 이상인 날의 다음날 평균 수익률 (백테스팅) SELECT AVG(m2.change_rate) AS avg_next_day_return, COUNT(*) AS sample_count FROM ai_analysis a1 JOIN market_daily m1 ON a1.trade_date = m1.trade_date JOIN market_daily m2 ON m2.trade_date = ( SELECT trade_date FROM market_daily WHERE trade_date > a1.trade_date ORDER BY trade_date LIMIT 1 ) WHERE a1.sentiment_score >= 70; -- 2. 반도체 키워드 등장일 평균 등락률 SELECT AVG(m.change_rate) AS avg_return, COUNT(*) AS days_count FROM market_daily m JOIN ai_analysis a ON m.trade_date = a.trade_date WHERE a.keywords LIKE '%반도체%'; -- 3. 요일별 평균 코스피 등락률 SELECT CASE strftime('%w', trade_date) WHEN '1' THEN '월요일' WHEN '2' THEN '화요일' WHEN '3' THEN '수요일' WHEN '4' THEN '목요일' WHEN '5' THEN '금요일' END AS weekday, ROUND(AVG(change_rate), 3) AS avg_return, COUNT(*) AS count FROM market_daily GROUP BY strftime('%w', trade_date) ORDER BY strftime('%w', trade_date); -- 4. 월별 심리점수 평균 추이 SELECT strftime('%Y-%m', trade_date) AS month, ROUND(AVG(sentiment_score), 1) AS avg_sentiment, ROUND(AVG(close_price), 2) AS avg_close FROM v_daily_summary GROUP BY strftime('%Y-%m', trade_date) ORDER BY month;
🚀 6개월 후 가능한 독점 콘텐츠 아이디어
- 1개월차: 일일 시황 자동화 완성. 발행 시간을 15분 → 5분으로 단축
- 3개월차: 심리점수 시계열 차트 포스팅 가능. 요일 효과 검증 포스팅
- 6개월차: 나만의 백테스팅 포스팅. 월간 자동 리포트. 키워드 트렌드 분석
- 1년차: KOSPI 심리 지수를 실제 독자들이 참고하는 지표로 브랜딩
💡 시작이 곧 데이터입니다. 오늘 설치하고 내일부터 데이터가 쌓이기 시작합니다. 6개월 뒤에 이 데이터로 무엇을 할 수 있는지는 그 때 가서 알게 됩니다. 파이프라인 구성 중 막히는 부분은 댓글로 남겨 주세요!
