Update app.py
Browse files
app.py
CHANGED
@@ -1,151 +1,70 @@
|
|
|
|
1 |
import subprocess
|
2 |
|
3 |
-
# ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น ๋ฐ ์
๋ฐ์ดํธ
|
4 |
subprocess.run(["pip", "install", "--upgrade", "pip"])
|
5 |
-
subprocess.run(["pip", "install", "--upgrade", "
|
6 |
|
|
|
7 |
import yfinance as yf
|
8 |
-
import
|
9 |
-
import matplotlib.font_manager as fm
|
10 |
import matplotlib.pyplot as plt
|
|
|
11 |
import numpy as np
|
12 |
-
import
|
13 |
-
import
|
14 |
import io
|
15 |
from PIL import Image
|
16 |
from datetime import datetime, timedelta
|
17 |
-
from openai import OpenAI
|
18 |
|
19 |
-
#
|
20 |
def install_nanum_font():
|
21 |
try:
|
22 |
subprocess.run(["apt-get", "update"], check=True)
|
23 |
subprocess.run(["apt-get", "install", "-y", "fonts-nanum"], check=True)
|
24 |
subprocess.run(["fc-cache", "-fv"], check=True)
|
|
|
25 |
except Exception as e:
|
26 |
print(f"ํฐํธ ์ค์น ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {e}")
|
27 |
|
28 |
install_nanum_font()
|
29 |
|
30 |
-
#
|
31 |
-
font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
|
32 |
-
|
33 |
-
if os.path.exists(font_path):
|
34 |
-
fm.fontManager.addfont(font_path)
|
35 |
-
else:
|
36 |
-
print("ํฐํธ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.")
|
37 |
-
|
38 |
-
# ๋๋๊ณ ๋ ํฐํธ ๊ฐ์ ์ ์ฉ
|
39 |
-
font_prop = fm.FontProperties(fname=font_path)
|
40 |
-
plt.rcParams['font.family'] = font_prop.get_name()
|
41 |
-
plt.rcParams['axes.unicode_minus'] = False # ๋ง์ด๋์ค ๋ถํธ ๊นจ์ง ๋ฐฉ์ง
|
42 |
-
|
43 |
-
# Perplexity AI API ์ค์
|
44 |
-
API_KEY = "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" # ์ฌ๊ธฐ์ Perplexity AI API ํค๋ฅผ ์
๋ ฅํ์ธ์.
|
45 |
-
|
46 |
-
# ๋ด์ค ์์ฝ์ ๊ฐ์ ธ์ค๋ ํจ์ (๊ธฐ์กด ์ฝ๋ ์ ์ง)
|
47 |
def get_real_news_summary(company, date):
|
48 |
-
|
49 |
-
|
50 |
-
# ๋ ์ง ํ์ ์กฐ์
|
51 |
-
target_date = datetime.strptime(date, '%Y-%m-%d')
|
52 |
-
start_date = (target_date - timedelta(days=1)).strftime('%Y-%m-%d')
|
53 |
-
end_date = (target_date + timedelta(days=1)).strftime('%Y-%m-%d')
|
54 |
-
|
55 |
-
messages = [
|
56 |
-
{"role": "system", "content": "You are a helpful assistant that summarizes stock news strictly in Korean."},
|
57 |
-
{"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."}
|
58 |
-
]
|
59 |
-
|
60 |
-
try:
|
61 |
-
response = client.chat.completions.create(
|
62 |
-
model="llama-3.1-sonar-large-128k-online",
|
63 |
-
messages=messages
|
64 |
-
)
|
65 |
-
summary = response.choices[0].message.content
|
66 |
-
|
67 |
-
# ํ๊ธ, ์ซ์, ๊ณต๋ฐฑ, ํน์ ๊ธฐํธ๋ง ๋จ๊ธฐ๋ ์ ๊ท ํํ์
|
68 |
-
korean_only_summary = re.sub(r'[^\w\s#.,!%()\-\[\]]', '', summary)
|
69 |
-
formatted_summary = re.sub(r'##\s*(.+)', r'**\1**', korean_only_summary)
|
70 |
-
|
71 |
-
return formatted_summary
|
72 |
-
except Exception as e:
|
73 |
-
return f"๋ด์ค ์์ฝ ์ค ์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}"
|
74 |
|
75 |
def handle_click(company_name, date_clicked):
|
76 |
return get_real_news_summary(company_name, date_clicked)
|
77 |
|
78 |
-
# Gradio์์ ์ฌ์ฉํ ํจ์ (๋ด์ค ์์ฝ ํฌํจ)
|
79 |
def update_news(input_value, selected_date):
|
80 |
-
if
|
81 |
return "๋ ์ง๋ฅผ ์ ํํด์ฃผ์ธ์."
|
82 |
else:
|
83 |
-
ticker =
|
84 |
company_name = ticker_to_name.get(ticker, input_value)
|
85 |
return handle_click(company_name, selected_date)
|
86 |
|
87 |
-
#
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
return get_top_market_cap_stock("์ฝ์คํผ")
|
97 |
-
elif input_value == "์ฝ์ค๋ฅ ์์ด 1์":
|
98 |
-
return get_top_market_cap_stock("์ฝ์ค๋ฅ")
|
99 |
-
else:
|
100 |
-
return name_to_ticker.get(input_value, input_value)
|
101 |
-
|
102 |
-
# ์๊ฐ์ด์ก ์์ ์ข
๋ชฉ ๊ฐ์ ธ์ค๊ธฐ
|
103 |
-
def get_top_market_cap_stock(market, sector=None, industry=None):
|
104 |
-
if market == "๋์ค๋ฅ":
|
105 |
-
etf_ticker = "QQQ"
|
106 |
-
elif market == "์ฝ์คํผ":
|
107 |
-
etf_ticker = "EWY"
|
108 |
-
elif market == "์ฝ์ค๋ฅ":
|
109 |
-
tickers = ["035420.KQ", "068270.KQ", "035720.KQ"]
|
110 |
-
else:
|
111 |
-
return None
|
112 |
-
|
113 |
-
if market in ["๋์ค๋ฅ", "์ฝ์คํผ"]:
|
114 |
-
etf = yf.Ticker(etf_ticker)
|
115 |
-
holdings = etf.info.get('holdings', [])
|
116 |
-
tickers = [holding['symbol'] for holding in holdings]
|
117 |
|
118 |
-
|
119 |
-
top_ticker = None
|
120 |
|
121 |
-
|
122 |
-
stock = yf.Ticker(ticker)
|
123 |
-
stock_info = stock.info
|
124 |
-
market_cap = stock_info.get('marketCap', 0)
|
125 |
-
stock_sector = stock_info.get('sector', None)
|
126 |
-
stock_industry = stock_info.get('industry', None)
|
127 |
-
|
128 |
-
if sector and stock_sector != sector:
|
129 |
-
continue
|
130 |
-
if industry and stock_industry != industry:
|
131 |
-
continue
|
132 |
-
|
133 |
-
if market_cap > largest_market_cap:
|
134 |
-
largest_market_cap = market_cap
|
135 |
-
top_ticker = ticker
|
136 |
-
ticker_to_name[ticker] = stock_info.get('shortName', ticker)
|
137 |
-
|
138 |
-
return top_ticker
|
139 |
-
|
140 |
-
# ์ฃผ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์กฐ๊ฑด์ ๋ง๋ ๋ ์ง์ ๊ทธ๋ํ๋ฅผ ๋ฐํํ๋ ํจ์
|
141 |
def display_stock_with_highlight(input_value, change_type, percent_change):
|
142 |
try:
|
143 |
-
ticker =
|
144 |
stock = yf.Ticker(ticker)
|
145 |
stock_data = stock.history(period="5y")
|
146 |
|
147 |
if stock_data.empty:
|
148 |
-
return "์ฃผ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.", []
|
149 |
|
150 |
stock_data['Change'] = stock_data['Close'].pct_change() * 100
|
151 |
percent_change = float(percent_change)
|
@@ -157,23 +76,23 @@ def display_stock_with_highlight(input_value, change_type, percent_change):
|
|
157 |
highlight_data = stock_data[stock_data['Change'] <= -percent_change]
|
158 |
color = "purple"
|
159 |
else:
|
160 |
-
return "Invalid change type", []
|
161 |
|
162 |
-
dates = stock_data.index
|
163 |
-
closing_prices = stock_data['Close']
|
164 |
|
165 |
plt.figure(figsize=(10, 6))
|
166 |
plt.plot(dates, closing_prices, color='gray', label=input_value)
|
167 |
plt.scatter(highlight_data.index, highlight_data['Close'], color=color, label=f'{change_type} ํฌ์ธํธ')
|
168 |
|
169 |
for index, row in highlight_data.iterrows():
|
170 |
-
plt.text(index, row['Close'], index.strftime('%Y-%m-%d'), fontsize=
|
171 |
plt.axvline(x=index, color=color, linestyle='--', linewidth=1)
|
172 |
|
173 |
company_name = ticker_to_name.get(ticker, input_value)
|
174 |
-
plt.title(f'{company_name} ์ฃผ๊ฐ ์ถ์ด'
|
175 |
-
plt.xlabel('๋ ์ง'
|
176 |
-
plt.ylabel('์ข
๊ฐ'
|
177 |
plt.legend()
|
178 |
|
179 |
buf = io.BytesIO()
|
@@ -184,23 +103,22 @@ def display_stock_with_highlight(input_value, change_type, percent_change):
|
|
184 |
|
185 |
highlight_dates = highlight_data.index.strftime('%Y-%m-%d').tolist()
|
186 |
|
187 |
-
return img, gr.update(choices=highlight_dates)
|
188 |
except Exception as e:
|
189 |
-
|
|
|
190 |
|
191 |
# Gradio ์ธํฐํ์ด์ค ์์ฑ (3์ด ๋ ์ด์์)
|
192 |
with gr.Blocks() as demo:
|
193 |
gr.Markdown("## ์ฃผ๊ฐ ๊ทธ๋ํ์ ๋ด์ค ์์ฝ")
|
194 |
|
195 |
with gr.Row():
|
196 |
-
with gr.Column():
|
197 |
input_value = gr.Textbox(label="์ข
๋ชฉ๋ช
๋๋ ํฐ์ปค ์
๋ ฅ", placeholder="์: SK๋ฐ์ด์คํ, AAPL")
|
198 |
change_type = gr.Dropdown(choices=["์์น", "ํ๋ฝ"], label="์์น ๋๋ ํ๋ฝ ์ ํ", value="์์น")
|
199 |
percent_change = gr.Textbox(label="๋ณ๋ ํผ์ผํธ (%)", placeholder="์: 10", value="10")
|
200 |
-
|
201 |
submit_btn = gr.Button("Submit")
|
202 |
|
203 |
-
# ์์ (๋์ ์ผ๋ก ๋งค์นญ)
|
204 |
examples = [["SK๋ฐ์ด์คํ"],
|
205 |
["๋์ค๋ฅ ์์ด 1์"],
|
206 |
["๋์ค๋ฅ ๋ฐ์ด์คํ
์์ด 1์"],
|
@@ -209,11 +127,11 @@ with gr.Blocks() as demo:
|
|
209 |
["์ฝ์ค๋ฅ ์์ด 1์"]]
|
210 |
gr.Examples(examples=examples, inputs=[input_value])
|
211 |
|
212 |
-
with gr.Column():
|
213 |
plot = gr.Image(label="์ฃผ๊ฐ ๊ทธ๋ํ")
|
214 |
date_dropdown = gr.Dropdown(label="์กฐ๊ฑด์ ํด๋นํ๋ ๋ ์ง ์ ํ", choices=[])
|
215 |
|
216 |
-
with gr.Column():
|
217 |
news_output = gr.Markdown(label="๋ด์ค ์์ฝ", value="") # ๋น ์นธ์ผ๋ก ๊ธฐ๋ณธ ํ์
|
218 |
|
219 |
# Submit ๋ฒํผ ํด๋ฆญ ์ ๊ทธ๋ํ ๋ฐ ๋ ์ง ๋๋กญ๋ค์ด ์
๋ฐ์ดํธ
|
@@ -230,5 +148,4 @@ with gr.Blocks() as demo:
|
|
230 |
outputs=[news_output]
|
231 |
)
|
232 |
|
233 |
-
# Gradio ์คํ
|
234 |
demo.launch()
|
|
|
1 |
+
# ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น ๋ฐ ์
๋ฐ์ดํธ
|
2 |
import subprocess
|
3 |
|
|
|
4 |
subprocess.run(["pip", "install", "--upgrade", "pip"])
|
5 |
+
subprocess.run(["pip", "install", "--upgrade", "yfinance", "gradio", "matplotlib", "Pillow"])
|
6 |
|
7 |
+
# ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ํฌํธ
|
8 |
import yfinance as yf
|
9 |
+
import gradio as gr
|
|
|
10 |
import matplotlib.pyplot as plt
|
11 |
+
import matplotlib.font_manager as fm
|
12 |
import numpy as np
|
13 |
+
import pandas as pd
|
14 |
+
import re
|
15 |
import io
|
16 |
from PIL import Image
|
17 |
from datetime import datetime, timedelta
|
|
|
18 |
|
19 |
+
# ๋๋๊ณ ๋ ํฐํธ ์ค์น ๋ฐ ์ ์ฉ
|
20 |
def install_nanum_font():
|
21 |
try:
|
22 |
subprocess.run(["apt-get", "update"], check=True)
|
23 |
subprocess.run(["apt-get", "install", "-y", "fonts-nanum"], check=True)
|
24 |
subprocess.run(["fc-cache", "-fv"], check=True)
|
25 |
+
plt.rcParams['font.family'] = 'NanumGothic'
|
26 |
except Exception as e:
|
27 |
print(f"ํฐํธ ์ค์น ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {e}")
|
28 |
|
29 |
install_nanum_font()
|
30 |
|
31 |
+
# ๋ด์ค ์์ฝ ํจ์ (์ฌ๊ธฐ์๋ ๊ฐ๋จํ ์์๋ก ์ฒ๋ฆฌ)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
def get_real_news_summary(company, date):
|
33 |
+
# ์ค์ API๋ฅผ ์ฌ์ฉํ ์ ์๋ ํ๊ฒฝ์ด๋ฏ๋ก, ๊ฐ๋จํ ๋ฌธ์์ด๋ก ๋์ฒดํฉ๋๋ค.
|
34 |
+
return f"{company}์ {date} ์ฃผ๋ณ ๋ด์ค ์์ฝ์
๋๋ค."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
def handle_click(company_name, date_clicked):
|
37 |
return get_real_news_summary(company_name, date_clicked)
|
38 |
|
|
|
39 |
def update_news(input_value, selected_date):
|
40 |
+
if not selected_date:
|
41 |
return "๋ ์ง๋ฅผ ์ ํํด์ฃผ์ธ์."
|
42 |
else:
|
43 |
+
ticker = name_to_ticker.get(input_value, input_value)
|
44 |
company_name = ticker_to_name.get(ticker, input_value)
|
45 |
return handle_click(company_name, selected_date)
|
46 |
|
47 |
+
# ์ข
๋ชฉ๋ช
๊ณผ ํฐ์ปค ๋งคํ ๋์
๋๋ฆฌ
|
48 |
+
name_to_ticker = {
|
49 |
+
"SK๋ฐ์ด์คํ": "326030.KS",
|
50 |
+
"๋์ค๋ฅ ์์ด 1์": "AAPL",
|
51 |
+
"๋์ค๋ฅ ๋ฐ์ด์คํ
์์ด 1์": "AMGN",
|
52 |
+
"๋์ค๋ฅ ํฌ์ค์ผ์ด ์์ด 1์": "JNJ",
|
53 |
+
"์ฝ์คํผ ์์ด 1์": "005930.KS",
|
54 |
+
"์ฝ์ค๋ฅ ์์ด 1์": "247540.KQ", # ์์ฝํ๋ก๋น์
|
55 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
+
ticker_to_name = {v: k for k, v in name_to_ticker.items()}
|
|
|
58 |
|
59 |
+
# ์ฃผ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๊ทธ๋ํ๋ฅผ ๊ทธ๋ฆฌ๋ ํจ์
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
def display_stock_with_highlight(input_value, change_type, percent_change):
|
61 |
try:
|
62 |
+
ticker = name_to_ticker.get(input_value, input_value)
|
63 |
stock = yf.Ticker(ticker)
|
64 |
stock_data = stock.history(period="5y")
|
65 |
|
66 |
if stock_data.empty:
|
67 |
+
return "์ฃผ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.", gr.Dropdown.update(choices=[])
|
68 |
|
69 |
stock_data['Change'] = stock_data['Close'].pct_change() * 100
|
70 |
percent_change = float(percent_change)
|
|
|
76 |
highlight_data = stock_data[stock_data['Change'] <= -percent_change]
|
77 |
color = "purple"
|
78 |
else:
|
79 |
+
return "Invalid change type", gr.Dropdown.update(choices=[])
|
80 |
|
81 |
+
dates = stock_data.index
|
82 |
+
closing_prices = stock_data['Close']
|
83 |
|
84 |
plt.figure(figsize=(10, 6))
|
85 |
plt.plot(dates, closing_prices, color='gray', label=input_value)
|
86 |
plt.scatter(highlight_data.index, highlight_data['Close'], color=color, label=f'{change_type} ํฌ์ธํธ')
|
87 |
|
88 |
for index, row in highlight_data.iterrows():
|
89 |
+
plt.text(index, row['Close'], index.strftime('%Y-%m-%d'), fontsize=8, color=color, ha='right')
|
90 |
plt.axvline(x=index, color=color, linestyle='--', linewidth=1)
|
91 |
|
92 |
company_name = ticker_to_name.get(ticker, input_value)
|
93 |
+
plt.title(f'{company_name} ์ฃผ๊ฐ ์ถ์ด')
|
94 |
+
plt.xlabel('๋ ์ง')
|
95 |
+
plt.ylabel('์ข
๊ฐ')
|
96 |
plt.legend()
|
97 |
|
98 |
buf = io.BytesIO()
|
|
|
103 |
|
104 |
highlight_dates = highlight_data.index.strftime('%Y-%m-%d').tolist()
|
105 |
|
106 |
+
return img, gr.Dropdown.update(choices=highlight_dates)
|
107 |
except Exception as e:
|
108 |
+
print(f"Error: {e}")
|
109 |
+
return f"๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {e}", gr.Dropdown.update(choices=[])
|
110 |
|
111 |
# Gradio ์ธํฐํ์ด์ค ์์ฑ (3์ด ๋ ์ด์์)
|
112 |
with gr.Blocks() as demo:
|
113 |
gr.Markdown("## ์ฃผ๊ฐ ๊ทธ๋ํ์ ๋ด์ค ์์ฝ")
|
114 |
|
115 |
with gr.Row():
|
116 |
+
with gr.Column():
|
117 |
input_value = gr.Textbox(label="์ข
๋ชฉ๋ช
๋๋ ํฐ์ปค ์
๋ ฅ", placeholder="์: SK๋ฐ์ด์คํ, AAPL")
|
118 |
change_type = gr.Dropdown(choices=["์์น", "ํ๋ฝ"], label="์์น ๋๋ ํ๋ฝ ์ ํ", value="์์น")
|
119 |
percent_change = gr.Textbox(label="๋ณ๋ ํผ์ผํธ (%)", placeholder="์: 10", value="10")
|
|
|
120 |
submit_btn = gr.Button("Submit")
|
121 |
|
|
|
122 |
examples = [["SK๋ฐ์ด์คํ"],
|
123 |
["๋์ค๋ฅ ์์ด 1์"],
|
124 |
["๋์ค๋ฅ ๋ฐ์ด์คํ
์์ด 1์"],
|
|
|
127 |
["์ฝ์ค๋ฅ ์์ด 1์"]]
|
128 |
gr.Examples(examples=examples, inputs=[input_value])
|
129 |
|
130 |
+
with gr.Column():
|
131 |
plot = gr.Image(label="์ฃผ๊ฐ ๊ทธ๋ํ")
|
132 |
date_dropdown = gr.Dropdown(label="์กฐ๊ฑด์ ํด๋นํ๋ ๋ ์ง ์ ํ", choices=[])
|
133 |
|
134 |
+
with gr.Column():
|
135 |
news_output = gr.Markdown(label="๋ด์ค ์์ฝ", value="") # ๋น ์นธ์ผ๋ก ๊ธฐ๋ณธ ํ์
|
136 |
|
137 |
# Submit ๋ฒํผ ํด๋ฆญ ์ ๊ทธ๋ํ ๋ฐ ๋ ์ง ๋๋กญ๋ค์ด ์
๋ฐ์ดํธ
|
|
|
148 |
outputs=[news_output]
|
149 |
)
|
150 |
|
|
|
151 |
demo.launch()
|