StockPulse / app.py
llllllllllllllllllllllllllleeeeeeeeeeeeee's picture
Update app.py
05c7630 verified
raw
history blame
6.85 kB
import subprocess
import sys
# νŒ¨ν‚€μ§€ μ„€μΉ˜ (ν•„μš”ν•œ 라이브러리 μ„€μΉ˜ 및 μ—…λ°μ΄νŠΈ)
def install_packages():
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "pip"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "openai", "yfinance", "gradio", "matplotlib", "Pillow"])
subprocess.check_call(["apt-get", "install", "-y", "fonts-nanum"])
subprocess.check_call(["fc-cache", "-fv"])
# νŒ¨ν‚€μ§€ μ„€μΉ˜
install_packages()
# μ„€μΉ˜ ν›„ ν•„μš”ν•œ νŒ¨ν‚€μ§€ μž„ν¬νŠΈ
import os
import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import io
from PIL import Image
import yfinance as yf
import gradio as gr
from datetime import datetime
from openai import OpenAI
# λ‚˜λˆ”κ³ λ”• 폰트 경둜 μ„€μ • 및 κ°•μ œ 적용
font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
if os.path.exists(font_path):
fm.fontManager.addfont(font_path)
font_prop = fm.FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()
plt.rcParams['axes.unicode_minus'] = False # λ§ˆμ΄λ„ˆμŠ€ λΆ€ν˜Έ 깨짐 λ°©μ§€
else:
print("폰트λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
# 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")
# API μš”μ²­μ„ μœ„ν•œ λ©”μ‹œμ§€ ꡬ성
messages = [
{"role": "system", "content": "You are a helpful assistant that summarizes stock news in Korean."},
{"role": "user", "content": f"Summarize the latest stock news for {company} on {date} in Korean. If there's no specific news for that date, provide the most recent relevant information."}
]
try:
# API μš”μ²­
response = client.chat.completions.create(
model="llama-3.1-sonar-large-128k-online",
messages=messages
)
# μ‘λ‹΅μ—μ„œ μš”μ•½ μΆ”μΆœ
summary = response.choices[0].message.content
return summary
except Exception as e:
return f"λ‰΄μŠ€ μš”μ•½ 쀑 μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}"
# λ‰΄μŠ€ μš”μ•½μ„ κ°€μ Έμ˜€λŠ” ν•¨μˆ˜
def handle_click(input_value, date_clicked):
return get_real_news_summary(input_value, date_clicked)
# Gradioμ—μ„œ μ‚¬μš©ν•  ν•¨μˆ˜ (λ‰΄μŠ€ μš”μ•½ 포함)
def update_news(input_value, selected_date):
if selected_date == "" or selected_date is None:
return "λ‚ μ§œλ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”."
else:
return handle_click(input_value, selected_date)
# μ’…λͺ©λͺ…κ³Ό 티컀λ₯Ό λ§€ν•‘ν•˜λŠ” λ”•μ…”λ„ˆλ¦¬
name_to_ticker = {
"μ‚Όμ„±μ „μž": "005930.KS",
"SKλ°”μ΄μ˜€νŒœ": "326030.KS",
"Apple": "AAPL",
# ν•„μš”ν•œ μ’…λͺ©λ“€μ„ μΆ”κ°€ν•˜μ„Έμš”
}
# μ£Όκ°€ 데이터λ₯Ό κ°€μ Έμ˜€κ³  쑰건에 λ§žλŠ” λ‚ μ§œμ™€ κ·Έλž˜ν”„λ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜
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μΆ•κ³Όμ˜ μ—°κ²°μ„  μ μ„ μœΌλ‘œ ν‘œμ‹œ
plt.title(f'{input_value} μ£Όκ°€ 좔이 ({change_type} ν‘œμ‹œ)')
plt.xlabel('λ‚ μ§œ')
plt.ylabel('μ’…κ°€')
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.Textbox(label="λ‰΄μŠ€ μš”μ•½")
# 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()