import subprocess # 필요한 라이브러리 설치 및 업데이트 subprocess.run(["pip", "install", "--upgrade", "pip"]) subprocess.run(["pip", "install", "--upgrade", "openai", "yfinance", "gradio", "matplotlib", "Pillow"]) import os import matplotlib.font_manager as fm import matplotlib.pyplot as plt import yfinance as yf import numpy as np import re # 한글, 숫자, 기호를 남기기 위한 정규 표현식에 사용 import gradio as gr import io from PIL import Image from datetime import datetime, timedelta from openai import OpenAI # 1. 나눔고딕 폰트 설치 및 적용 def install_nanum_font(): try: subprocess.run(["apt-get", "update"], check=True) subprocess.run(["apt-get", "install", "-y", "fonts-nanum"], check=True) subprocess.run(["fc-cache", "-fv"], check=True) except Exception as e: print(f"폰트 설치 중 오류가 발생했습니다: {e}") install_nanum_font() # 나눔고딕 폰트 경로 설정 및 강제 적용 font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf' if os.path.exists(font_path): fm.fontManager.addfont(font_path) else: print("폰트를 찾을 수 없습니다.") # 나눔고딕 폰트 강제 적용 font_prop = fm.FontProperties(fname=font_path) plt.rcParams['font.family'] = font_prop.get_name() plt.rcParams['axes.unicode_minus'] = False # 마이너스 부호 깨짐 방지 # 2. yfinance로 주가 데이터 가져오기 (삼성전자 예시) ticker = '005930.KS' # 삼성전자 티커 stock = yf.Ticker(ticker) stock_data = stock.history(period="max") # 주가 데이터 출력 확인 print(stock_data.head()) # 주가 데이터 일부 확인 # 3. 주가 데이터 시각화 (pandas 데이터프레임을 numpy 배열로 변환하여 처리) plt.figure(figsize=(10, 6)) # 인덱스와 종가 데이터를 numpy 배열로 변환 dates = stock_data.index.to_numpy() closing_prices = stock_data['Close'].to_numpy() # 주가 데이터 시각화 plt.plot(dates, closing_prices, label='삼성전자 종가') # 그래프 제목, 축 라벨을 한글로 설정 plt.title('삼성전자 주가 추이', fontproperties=font_prop) plt.xlabel('날짜', fontproperties=font_prop) plt.ylabel('종가', fontproperties=font_prop) plt.legend(prop=font_prop) # B 코드 # Perplexity AI API 설정 API_KEY = "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" # 여기에 Perplexity AI API 키를 입력하세요. # 뉴스 요약을 가져오는 함수 def get_real_news_summary(company, date): # OpenAI 클라이언트 초기화 client = OpenAI(api_key=API_KEY, base_url="https://api.perplexity.ai") # 날짜 형식을 맞춰주기 위한 처리 target_date = datetime.strptime(date, '%Y-%m-%d') start_date = (target_date - timedelta(days=1)).strftime('%Y-%m-%d') end_date = (target_date + timedelta(days=1)).strftime('%Y-%m-%d') # API 요청을 위한 메시지 구성 - 한국어로만 응답을 받도록 지시 messages = [ {"role": "system", "content": "You are a helpful assistant that summarizes stock news strictly in Korean."}, {"role": "user", "content": f"Summarize the stock news for {company} between {start_date} and {end_date} in Korean. Only focus on news within this date range."} ] try: # API 요청 response = client.chat.completions.create( model="llama-3.1-sonar-large-128k-online", messages=messages ) # 응답에서 요약 추출 summary = response.choices[0].message.content # 한글, 숫자, 공백, 특수 기호만 남기는 정규 표현식 korean_only_summary = re.sub(r'[^\w\s#.,!%()\-\[\]]', '', summary) # ##로 시작하는 부분을 **으로 감싸서 Bold 처리 formatted_summary = re.sub(r'##\s*(.+)', r'**\1**', korean_only_summary) return formatted_summary except Exception as e: return f"뉴스 요약 중 에러가 발생했습니다: {str(e)}" # 뉴스 요약을 가져오는 함수 def handle_click(company_name, date_clicked): return get_real_news_summary(company_name, date_clicked) # Gradio에서 사용할 함수 (뉴스 요약 포함) def update_news(input_value, selected_date): if selected_date == "" or selected_date is None: return "날짜를 선택해주세요." else: # 종목명을 가져와서 Perplexity로 검색 ticker = name_to_ticker.get(input_value, input_value) company_name = input_value if ticker == input_value else list(name_to_ticker.keys())[list(name_to_ticker.values()).index(ticker)] return handle_click(company_name, selected_date) # 종목명과 티커를 매핑하는 딕셔너리 확장 및 조건별 종목 매핑 name_to_ticker = { "삼성전자": "005930.KS", "SK바이오팜": "326030.KS", "Apple": "AAPL", "Nvidia": "NVDA", "Vertex": "VRTX", "Eli Lilly": "LLY", # 추가된 항목 "현대차": "005380.KS", "카카오": "035720.KS", "LG화학": "051910.KS", "셀트리온": "068270.KS", "네이버": "035420.KS", "에코프로비엠": "247540.KS", "알테오젠": "196170.KQ", # 최신 시가총액 상위 종목을 반영하여 업데이트 "나스닥 시총 1위": "AAPL", # Apple "나스닥 바이오텍 시총 1위": "VRTX", # Vertex Pharmaceuticals "나스닥 헬스케어 시총 1위": "LLY", # Eli Lilly로 변경 "코스피 시총 1위": "005930.KS", # 삼성전자 "코스닥 시총 1위": "196170.KQ", # 알테오젠 } # 주가 데이터를 가져오고 조건에 맞는 날짜와 그래프를 반환하는 함수 def display_stock_with_highlight(input_value, change_type, percent_change): try: # 입력값을 티커로 변환 ticker = name_to_ticker.get(input_value, input_value) stock = yf.Ticker(ticker) stock_data = stock.history(period="5y") # 최근 5년 데이터로 제한 if stock_data.empty: return "주가 데이터를 찾을 수 없습니다.", [] stock_data['Change'] = stock_data['Close'].pct_change() * 100 percent_change = float(percent_change) if change_type == "상승": highlight_data = stock_data[stock_data['Change'] >= percent_change] color = "darkorange" elif change_type == "하락": highlight_data = stock_data[stock_data['Change'] <= -percent_change] color = "purple" else: return "Invalid change type", [] dates = stock_data.index.to_numpy() closing_prices = stock_data['Close'].to_numpy() plt.figure(figsize=(10, 6)) plt.plot(dates, closing_prices, color='gray', label=input_value) plt.scatter(highlight_data.index, highlight_data['Close'], color=color, label=f'{change_type} 포인트') for index, row in highlight_data.iterrows(): plt.text(index, row['Close'], index.strftime('%Y-%m-%d'), fontsize=10, fontweight='bold', color=color, ha='right') plt.axvline(x=index, color=color, linestyle='--', linewidth=1) # x축과의 연결선 점선으로 표시 # 종목명 + '주가 추이'로 제목 설정 (종목명 기반) company_name = list(name_to_ticker.keys())[list(name_to_ticker.values()).index(ticker)] plt.title(f'{company_name} 주가 추이', fontproperties=font_prop) plt.xlabel('날짜', fontproperties=font_prop) plt.ylabel('종가', fontproperties=font_prop) plt.legend() buf = io.BytesIO() plt.savefig(buf, format='png') plt.close() buf.seek(0) img = Image.open(buf) highlight_dates = highlight_data.index.strftime('%Y-%m-%d').tolist() return img, gr.update(choices=highlight_dates) except Exception as e: return f"Error processing data: {e}", gr.update(choices=[]) # Gradio 인터페이스 생성 (3열 레이아웃) with gr.Blocks() as demo: gr.Markdown("## 주가 그래프와 뉴스 요약") with gr.Row(): with gr.Column(): # 입력값을 담을 첫 번째 열 input_value = gr.Textbox(label="종목명 또는 티커 입력", placeholder="예: SK바이오팜, AAPL") change_type = gr.Dropdown(choices=["상승", "하락"], label="상승 또는 하락 선택", value="상승") percent_change = gr.Textbox(label="변동 퍼센트 (%)", placeholder="예: 10", value="10") submit_btn = gr.Button("Submit") # 예제 업데이트 examples = [["SK바이오팜"], ["나스닥 시총 1위"], ["나스닥 헬스케어 시총 1위"], # 수정된 항목 ["나스닥 바이오텍 시총 1위"], ["코스피 시총 1위"], ["코스닥 시총 1위"]] gr.Examples(examples=examples, inputs=[input_value]) with gr.Column(): # 그래프를 출력할 두 번째 열 plot = gr.Image(label="주가 그래프") date_dropdown = gr.Dropdown(label="조건에 해당하는 날짜 선택", choices=[]) with gr.Column(): # 뉴스 요약을 출력할 세 번째 열 # news_output을 gr.Markdown으로 변경 news_output = gr.Markdown(label="뉴스 요약", value="") # 빈 칸으로 기본 표시 # Submit 버튼 클릭 시 그래프 및 날짜 드롭다운 업데이트 submit_btn.click( fn=display_stock_with_highlight, inputs=[input_value, change_type, percent_change], outputs=[plot, date_dropdown] ) # 날짜 선택 시 뉴스 요약 업데이트 date_dropdown.change( fn=update_news, inputs=[input_value, date_dropdown], outputs=[news_output] ) # Gradio 실행 demo.launch()