Commit
7628d13
โ€ข
1 Parent(s): 56c0ae0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -9
app.py CHANGED
@@ -1,9 +1,4 @@
1
  import subprocess
2
-
3
- # ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ์—…๋ฐ์ดํŠธ
4
- subprocess.run(["pip", "install", "--upgrade", "pip"])
5
- subprocess.run(["pip", "install", "--upgrade", "openai", "yfinance", "gradio", "matplotlib", "Pillow"])
6
-
7
  import os
8
  import matplotlib.font_manager as fm
9
  import matplotlib.pyplot as plt
@@ -16,13 +11,171 @@ from PIL import Image
16
  from datetime import datetime, timedelta
17
  from openai import OpenAI
18
 
19
- # ... (์ค‘๊ฐ„ ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ (3์—ด ๋ ˆ์ด์•„์›ƒ)
22
  with gr.Blocks() as demo:
23
  gr.Markdown("## ์ฃผ๊ฐ€ ๊ทธ๋ž˜ํ”„์™€ ๋‰ด์Šค ์š”์•ฝ")
24
 
25
- # CSS๋กœ 3์—ด ๋‰ด์Šค ์š”์•ฝ ํƒ€์ดํ‹€์ด ๋‹ฌ๋ฆฐ ํšŒ์ƒ‰ ๋„ค๋ชจ ์นธ ์ƒ์„ฑ
26
  demo.css("#news_output_box { background-color: #f0f0f0; padding: 20px; }")
27
 
28
  with gr.Row():
@@ -33,7 +186,7 @@ with gr.Blocks() as demo:
33
 
34
  submit_btn = gr.Button("Submit")
35
 
36
- # ์˜ˆ์ œ (์ด์ „ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋ณต์›)
37
  examples = [["SK๋ฐ”์ด์˜คํŒœ"],
38
  ["๋‚˜์Šค๋‹ฅ ์‹œ์ด 1์œ„"],
39
  ["๋‚˜์Šค๋‹ฅ ํ—ฌ์Šค์ผ€์–ด ์‹œ์ด 1์œ„"],
@@ -47,7 +200,6 @@ with gr.Blocks() as demo:
47
  date_dropdown = gr.Dropdown(label="์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ๋‚ ์งœ ์„ ํƒ", choices=[])
48
 
49
  with gr.Column(): # ๋‰ด์Šค ์š”์•ฝ์„ ์ถœ๋ ฅํ•  ์„ธ ๋ฒˆ์งธ ์—ด
50
- # news_output์„ gr.Markdown์œผ๋กœ ๋ณ€๊ฒฝ, ๋นˆ ์นธ์„ ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ์œผ๋กœ ์„ค์ •
51
  news_output = gr.Markdown(label="๋‰ด์Šค ์š”์•ฝ", value="", elem_id="news_output_box")
52
 
53
  # Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๊ทธ๋ž˜ํ”„ ๋ฐ ๋‚ ์งœ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ
 
1
  import subprocess
 
 
 
 
 
2
  import os
3
  import matplotlib.font_manager as fm
4
  import matplotlib.pyplot as plt
 
11
  from datetime import datetime, timedelta
12
  from openai import OpenAI
13
 
14
+ # 1. ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ์—…๋ฐ์ดํŠธ
15
+ def install_libraries():
16
+ try:
17
+ subprocess.run(["pip", "install", "--upgrade", "pip"])
18
+ subprocess.run(["pip", "install", "--upgrade", "openai", "yfinance", "gradio", "matplotlib", "Pillow"])
19
+ except Exception as e:
20
+ print(f"๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}")
21
+
22
+ install_libraries()
23
+
24
+ # 2. ๋‚˜๋ˆ”๊ณ ๋”• ํฐํŠธ ์„ค์น˜ ๋ฐ ์ ์šฉ
25
+ def install_nanum_font():
26
+ try:
27
+ subprocess.run(["apt-get", "update"], check=True)
28
+ subprocess.run(["apt-get", "install", "-y", "fonts-nanum"], check=True)
29
+ subprocess.run(["fc-cache", "-fv"], check=True)
30
+ except Exception as e:
31
+ print(f"ํฐํŠธ ์„ค์น˜ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}")
32
+
33
+ install_nanum_font()
34
+
35
+ # ๋‚˜๋ˆ”๊ณ ๋”• ํฐํŠธ ๊ฒฝ๋กœ ์„ค์ • ๋ฐ ๊ฐ•์ œ ์ ์šฉ
36
+ font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
37
+ if os.path.exists(font_path):
38
+ fm.fontManager.addfont(font_path)
39
+ else:
40
+ print("ํฐํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
41
+ # ๋‚˜๋ˆ”๊ณ ๋”• ํฐํŠธ ๊ฐ•์ œ ์ ์šฉ
42
+ font_prop = fm.FontProperties(fname=font_path)
43
+ plt.rcParams['font.family'] = font_prop.get_name()
44
+ plt.rcParams['axes.unicode_minus'] = False # ๋งˆ์ด๋„ˆ์Šค ๋ถ€ํ˜ธ ๊นจ์ง ๋ฐฉ์ง€
45
+
46
+ # 3. Perplexity AI API ์„ค์ •
47
+ API_KEY = "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"
48
+
49
+ # ๋‰ด์Šค ์š”์•ฝ์„ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
50
+ def get_real_news_summary(company, date):
51
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
52
+ client = OpenAI(api_key=API_KEY, base_url="https://api.perplexity.ai")
53
+
54
+ # ๋‚ ์งœ ํ˜•์‹์„ ๋งž์ถฐ์ฃผ๊ธฐ ์œ„ํ•œ ์ฒ˜๋ฆฌ
55
+ target_date = datetime.strptime(date, '%Y-%m-%d')
56
+ start_date = (target_date - timedelta(days=1)).strftime('%Y-%m-%d')
57
+ end_date = (target_date + timedelta(days=1)).strftime('%Y-%m-%d')
58
+
59
+ # API ์š”์ฒญ์„ ์œ„ํ•œ ๋ฉ”์‹œ์ง€ ๊ตฌ์„ฑ
60
+ messages = [
61
+ {"role": "system", "content": "You are a helpful assistant that summarizes stock news strictly in Korean."},
62
+ {"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."}
63
+ ]
64
+
65
+ try:
66
+ # API ์š”์ฒญ
67
+ response = client.chat.completions.create(
68
+ model="llama-3.1-sonar-large-128k-online",
69
+ messages=messages
70
+ )
71
+
72
+ # ์‘๋‹ต์—์„œ ์š”์•ฝ ์ถ”์ถœ
73
+ summary = response.choices[0].message.content
74
+
75
+ # ํ•œ๊ธ€, ์ˆซ์ž, ๊ณต๋ฐฑ, ํŠน์ˆ˜ ๊ธฐํ˜ธ๋งŒ ๋‚จ๊ธฐ๋Š” ์ •๊ทœ ํ‘œํ˜„์‹
76
+ korean_only_summary = re.sub(r'[^\w\s#.,!%()\-\[\]]', '', summary)
77
+
78
+ # ##๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์„ **์œผ๋กœ ๊ฐ์‹ธ์„œ Bold ์ฒ˜๋ฆฌ
79
+ formatted_summary = re.sub(r'##\s*(.+)', r'**\1**', korean_only_summary)
80
+
81
+ return formatted_summary
82
+ except Exception as e:
83
+ return f"๋‰ด์Šค ์š”์•ฝ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
84
+
85
+ # ๋‰ด์Šค ์š”์•ฝ์„ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
86
+ def handle_click(company_name, date_clicked):
87
+ return get_real_news_summary(company_name, date_clicked)
88
+
89
+ # Gradio์—์„œ ์‚ฌ์šฉํ•  ํ•จ์ˆ˜ (๋‰ด์Šค ์š”์•ฝ ํฌํ•จ)
90
+ def update_news(input_value, selected_date):
91
+ if selected_date == "" or selected_date is None:
92
+ return "๋‚ ์งœ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”."
93
+ else:
94
+ # ์ข…๋ชฉ๋ช…์„ ๊ฐ€์ ธ์™€์„œ Perplexity๋กœ ๊ฒ€์ƒ‰
95
+ ticker = name_to_ticker.get(input_value, input_value)
96
+ company_name = input_value if ticker == input_value else list(name_to_ticker.keys())[list(name_to_ticker.values()).index(ticker)]
97
+ return handle_click(company_name, selected_date)
98
+
99
+ # ์ข…๋ชฉ๋ช…๊ณผ ํ‹ฐ์ปค๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๋”•์…”๋„ˆ๋ฆฌ ํ™•์žฅ ๋ฐ ์กฐ๊ฑด๋ณ„ ์ข…๋ชฉ ๋งคํ•‘
100
+ name_to_ticker = {
101
+ "์‚ผ์„ฑ์ „์ž": "005930.KS",
102
+ "SK๋ฐ”์ด์˜คํŒœ": "326030.KS",
103
+ "Apple": "AAPL",
104
+ "Nvidia": "NVDA",
105
+ "Vertex": "VRTX",
106
+ "ํ˜„๋Œ€์ฐจ": "005380.KS",
107
+ "์นด์นด์˜ค": "035720.KS",
108
+ "LGํ™”ํ•™": "051910.KS",
109
+ "์…€ํŠธ๋ฆฌ์˜จ": "068270.KS",
110
+ "๋„ค์ด๋ฒ„": "035420.KS",
111
+ "์—์ฝ”ํ”„๋กœ๋น„์— ": "247540.KS",
112
+ "์•Œํ…Œ์˜ค์  ": "196170.KQ",
113
+ # ์ตœ์‹  ์‹œ๊ฐ€์ด์•ก ์ƒ์œ„ ์ข…๋ชฉ์„ ๋ฐ˜์˜ํ•˜์—ฌ ์—…๋ฐ์ดํŠธ
114
+ "๋‚˜์Šค๋‹ฅ ์‹œ์ด 1์œ„": "AAPL", # Apple
115
+ "๋‚˜์Šค๋‹ฅ ๋ฐ”์ด์˜คํ… ์‹œ์ด 1์œ„": "VRTX", # Vertex Pharmaceuticals
116
+ "๋‚˜์Šค๋‹ฅ ํ—ฌ์Šค์ผ€์–ด ์‹œ์ด 1์œ„": "LLY", # Eli Lilly
117
+ "์ฝ”์Šคํ”ผ ์‹œ์ด 1์œ„": "005930.KS", # ์‚ผ์„ฑ์ „์ž
118
+ "์ฝ”์Šค๋‹ฅ ์‹œ์ด 1์œ„": "196170.KQ", # ์•Œํ…Œ์˜ค์  
119
+ }
120
+
121
+ # ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์กฐ๊ฑด์— ๋งž๋Š” ๋‚ ์งœ์™€ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
122
+ def display_stock_with_highlight(input_value, change_type, percent_change):
123
+ try:
124
+ # ์ž…๋ ฅ๊ฐ’์„ ํ‹ฐ์ปค๋กœ ๋ณ€ํ™˜
125
+ ticker = name_to_ticker.get(input_value, input_value)
126
+ stock = yf.Ticker(ticker)
127
+ stock_data = stock.history(period="5y") # ์ตœ๊ทผ 5๋…„ ๋ฐ์ดํ„ฐ๋กœ ์ œํ•œ
128
+
129
+ if stock_data.empty:
130
+ return "์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", []
131
+
132
+ stock_data['Change'] = stock_data['Close'].pct_change() * 100
133
+
134
+ percent_change = float(percent_change)
135
+
136
+ if change_type == "์ƒ์Šน":
137
+ highlight_data = stock_data[stock_data['Change'] >= percent_change]
138
+ color = "darkorange"
139
+ elif change_type == "ํ•˜๋ฝ":
140
+ highlight_data = stock_data[stock_data['Change'] <= -percent_change]
141
+ color = "purple"
142
+ else:
143
+ return "Invalid change type", []
144
+
145
+ dates = stock_data.index.to_numpy()
146
+ closing_prices = stock_data['Close'].to_numpy()
147
+
148
+ plt.figure(figsize=(10, 6))
149
+ plt.plot(dates, closing_prices, color='gray', label=input_value)
150
+ plt.scatter(highlight_data.index, highlight_data['Close'], color=color, label=f'{change_type} ํฌ์ธํŠธ')
151
+
152
+ for index, row in highlight_data.iterrows():
153
+ plt.text(index, row['Close'], index.strftime('%Y-%m-%d'), fontsize=10, fontweight='bold', color=color, ha='right')
154
+ plt.axvline(x=index, color=color, linestyle='--', linewidth=1) # x์ถ•๊ณผ์˜ ์—ฐ๊ฒฐ์„  ์ ์„ ์œผ๋กœ ํ‘œ์‹œ
155
+
156
+ # ์ข…๋ชฉ๋ช… + '์ฃผ๊ฐ€ ์ถ”์ด'๋กœ ์ œ๋ชฉ ์„ค์ • (์ข…๋ชฉ๋ช… ๊ธฐ๋ฐ˜)
157
+ company_name = list(name_to_ticker.keys())[list(name_to_ticker.values()).index(ticker)]
158
+ plt.title(f'{company_name} ์ฃผ๊ฐ€ ์ถ”์ด', fontproperties=font_prop)
159
+ plt.xlabel('๋‚ ์งœ', fontproperties=font_prop)
160
+ plt.ylabel('์ข…๊ฐ€', fontproperties=font_prop)
161
+ plt.legend()
162
+
163
+ buf = io.BytesIO()
164
+ plt.savefig(buf, format='png')
165
+ plt.close()
166
+ buf.seek(0)
167
+ img = Image.open(buf)
168
+
169
+ highlight_dates = highlight_data.index.strftime('%Y-%m-%d').tolist()
170
+
171
+ return img, gr.update(choices=highlight_dates)
172
+ except Exception as e:
173
+ return f"Error processing data: {e}", gr.update(choices=[])
174
 
175
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ (3์—ด ๋ ˆ์ด์•„์›ƒ)
176
  with gr.Blocks() as demo:
177
  gr.Markdown("## ์ฃผ๊ฐ€ ๊ทธ๋ž˜ํ”„์™€ ๋‰ด์Šค ์š”์•ฝ")
178
 
 
179
  demo.css("#news_output_box { background-color: #f0f0f0; padding: 20px; }")
180
 
181
  with gr.Row():
 
186
 
187
  submit_btn = gr.Button("Submit")
188
 
189
+ # ์˜ˆ์ œ
190
  examples = [["SK๋ฐ”์ด์˜คํŒœ"],
191
  ["๋‚˜์Šค๋‹ฅ ์‹œ์ด 1์œ„"],
192
  ["๋‚˜์Šค๋‹ฅ ํ—ฌ์Šค์ผ€์–ด ์‹œ์ด 1์œ„"],
 
200
  date_dropdown = gr.Dropdown(label="์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ๋‚ ์งœ ์„ ํƒ", choices=[])
201
 
202
  with gr.Column(): # ๋‰ด์Šค ์š”์•ฝ์„ ์ถœ๋ ฅํ•  ์„ธ ๋ฒˆ์งธ ์—ด
 
203
  news_output = gr.Markdown(label="๋‰ด์Šค ์š”์•ฝ", value="", elem_id="news_output_box")
204
 
205
  # Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๊ทธ๋ž˜ํ”„ ๋ฐ ๋‚ ์งœ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ