Commit
91f296a
โ€ข
1 Parent(s): b9cf696

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -44
app.py CHANGED
@@ -1,72 +1,159 @@
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)
71
 
72
  if change_type == "์ƒ์Šน":
@@ -76,23 +163,24 @@ def display_stock_with_highlight(input_value, change_type, 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,36 +191,41 @@ def display_stock_with_highlight(input_value, change_type, percent_change):
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์œ„"],
125
  ["๋‚˜์Šค๋‹ฅ ํ—ฌ์Šค์ผ€์–ด ์‹œ์ด 1์œ„"],
 
126
  ["์ฝ”์Šคํ”ผ ์‹œ์ด 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 ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๊ทธ๋ž˜ํ”„ ๋ฐ ๋‚ ์งœ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ
138
  submit_btn.click(
@@ -148,4 +241,7 @@ with gr.Blocks() as demo:
148
  outputs=[news_output]
149
  )
150
 
 
151
  demo.launch()
 
 
 
 
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
10
+ import yfinance as yf
11
  import numpy as np
12
+ import re # ํ•œ๊ธ€, ์ˆซ์ž, ๊ธฐํ˜ธ๋ฅผ ๋‚จ๊ธฐ๊ธฐ ์œ„ํ•œ ์ •๊ทœ ํ‘œํ˜„์‹์— ์‚ฌ์šฉ
13
+ import gradio as gr
14
  import io
15
  from PIL import Image
16
  from datetime import datetime, timedelta
17
+ from openai import OpenAI
18
 
19
+ # 1. ๋‚˜๋ˆ”๊ณ ๋”• ํฐํŠธ ์„ค์น˜ ๋ฐ ์ ์šฉ
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
+ # 2. yfinance๋กœ ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (์‚ผ์„ฑ์ „์ž ์˜ˆ์‹œ)
44
+ ticker = '005930.KS' # ์‚ผ์„ฑ์ „์ž ํ‹ฐ์ปค
45
+ stock = yf.Ticker(ticker)
46
+ stock_data = stock.history(period="max")
47
+
48
+ # ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ ํ™•์ธ
49
+ print(stock_data.head()) # ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์ผ๋ถ€ ํ™•์ธ
50
+
51
+ # 3. ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” (pandas ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ numpy ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ฒ˜๋ฆฌ)
52
+ plt.figure(figsize=(10, 6))
53
+
54
+ # ์ธ๋ฑ์Šค์™€ ์ข…๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ numpy ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜
55
+ dates = stock_data.index.to_numpy()
56
+ closing_prices = stock_data['Close'].to_numpy()
57
+
58
+ # ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”
59
+ plt.plot(dates, closing_prices, label='์‚ผ์„ฑ์ „์ž ์ข…๊ฐ€')
60
+
61
+ # ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ, ์ถ• ๋ผ๋ฒจ์„ ํ•œ๊ธ€๋กœ ์„ค์ •
62
+ plt.title('์‚ผ์„ฑ์ „์ž ์ฃผ๊ฐ€ ์ถ”์ด', fontproperties=font_prop)
63
+ plt.xlabel('๋‚ ์งœ', fontproperties=font_prop)
64
+ plt.ylabel('์ข…๊ฐ€', fontproperties=font_prop)
65
+ plt.legend(prop=font_prop)
66
+
67
+ # B ์ฝ”๋“œ
68
+
69
+ # Perplexity AI API ์„ค์ •
70
+ API_KEY = "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" # ์—ฌ๊ธฐ์— Perplexity AI API ํ‚ค๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.
71
+
72
+ # ๋‰ด์Šค ์š”์•ฝ์„ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
73
  def get_real_news_summary(company, date):
74
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
75
+ client = OpenAI(api_key=API_KEY, base_url="https://api.perplexity.ai")
76
+
77
+ # ๋‚ ์งœ ํ˜•์‹์„ ๋งž์ถฐ์ฃผ๊ธฐ ์œ„ํ•œ ์ฒ˜๋ฆฌ
78
+ target_date = datetime.strptime(date, '%Y-%m-%d')
79
+ start_date = (target_date - timedelta(days=1)).strftime('%Y-%m-%d')
80
+ end_date = (target_date + timedelta(days=1)).strftime('%Y-%m-%d')
81
+
82
+ # API ์š”์ฒญ์„ ์œ„ํ•œ ๋ฉ”์‹œ์ง€ ๊ตฌ์„ฑ - ํ•œ๊ตญ์–ด๋กœ๋งŒ ์‘๋‹ต์„ ๋ฐ›๋„๋ก ์ง€์‹œ
83
+ messages = [
84
+ {"role": "system", "content": "You are a helpful assistant that summarizes stock news strictly in Korean."},
85
+ {"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."}
86
+ ]
87
+
88
+ try:
89
+ # API ์š”์ฒญ
90
+ response = client.chat.completions.create(
91
+ model="llama-3.1-sonar-large-128k-online",
92
+ messages=messages
93
+ )
94
+
95
+ # ์‘๋‹ต์—์„œ ์š”์•ฝ ์ถ”์ถœ
96
+ summary = response.choices[0].message.content
97
+
98
+ # ํ•œ๊ธ€, ์ˆซ์ž, ๊ณต๋ฐฑ, ํŠน์ˆ˜ ๊ธฐํ˜ธ๋งŒ ๋‚จ๊ธฐ๋Š” ์ •๊ทœ ํ‘œํ˜„์‹
99
+ korean_only_summary = re.sub(r'[^\w\s#.,!%()\-\[\]]', '', summary)
100
+
101
+ # ##๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์„ **์œผ๋กœ ๊ฐ์‹ธ์„œ Bold ์ฒ˜๋ฆฌ
102
+ formatted_summary = re.sub(r'##\s*(.+)', r'**\1**', korean_only_summary)
103
+
104
+ return formatted_summary
105
+ except Exception as e:
106
+ return f"๋‰ด์Šค ์š”์•ฝ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
107
 
108
+ # ๋‰ด์Šค ์š”์•ฝ์„ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
109
  def handle_click(company_name, date_clicked):
110
  return get_real_news_summary(company_name, date_clicked)
111
 
112
+ # Gradio์—์„œ ์‚ฌ์šฉํ•  ํ•จ์ˆ˜ (๋‰ด์Šค ์š”์•ฝ ํฌํ•จ)
113
  def update_news(input_value, selected_date):
114
+ if selected_date == "" or selected_date is None:
115
  return "๋‚ ์งœ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”."
116
  else:
117
+ # ์ข…๋ชฉ๋ช…์„ ๊ฐ€์ ธ์™€์„œ Perplexity๋กœ ๊ฒ€์ƒ‰
118
  ticker = name_to_ticker.get(input_value, input_value)
119
+ company_name = input_value if ticker == input_value else list(name_to_ticker.keys())[list(name_to_ticker.values()).index(ticker)]
120
  return handle_click(company_name, selected_date)
121
 
122
+ # ์ข…๋ชฉ๋ช…๊ณผ ํ‹ฐ์ปค๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๋”•์…”๋„ˆ๋ฆฌ ํ™•์žฅ ๋ฐ ์กฐ๊ฑด๋ณ„ ์ข…๋ชฉ ๋งคํ•‘
123
  name_to_ticker = {
124
+ "์‚ผ์„ฑ์ „์ž": "005930.KS",
125
  "SK๋ฐ”์ด์˜คํŒœ": "326030.KS",
126
+ "Apple": "AAPL",
127
+ "Nvidia": "NVDA",
128
+ "Vertex": "VRTX",
129
+ "ํ˜„๋Œ€์ฐจ": "005380.KS",
130
+ "์นด์นด์˜ค": "035720.KS",
131
+ "LGํ™”ํ•™": "051910.KS",
132
+ "์…€ํŠธ๋ฆฌ์˜จ": "068270.KS",
133
+ "๋„ค์ด๋ฒ„": "035420.KS",
134
+ "์—์ฝ”ํ”„๋กœ๋น„์— ": "247540.KS",
135
+ "์•Œํ…Œ์˜ค์  ": "196170.KQ",
136
+ # ์ตœ์‹  ์‹œ๊ฐ€์ด์•ก ์ƒ์œ„ ์ข…๋ชฉ์„ ๋ฐ˜์˜ํ•˜์—ฌ ์—…๋ฐ์ดํŠธ
137
+ "๋‚˜์Šค๋‹ฅ ์‹œ์ด 1์œ„": "AAPL", # Apple
138
+ "๋‚˜์Šค๋‹ฅ ๋ฐ”์ด์˜คํ… ์‹œ์ด 1์œ„": "VRTX", # Vertex Pharmaceuticals
139
+ "๋‚˜์Šค๋‹ฅ ํ—ฌ์Šค์ผ€์–ด ์‹œ์ด 1์œ„": "LLY", # Eli Lilly
140
+ "์ฝ”์Šคํ”ผ ์‹œ์ด 1์œ„": "005930.KS", # ์‚ผ์„ฑ์ „์ž
141
+ "์ฝ”์Šค๋‹ฅ ์‹œ์ด 1์œ„": "196170.KQ", # ์•Œํ…Œ์˜ค์  
142
  }
143
 
144
+ # ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์กฐ๊ฑด์— ๋งž๋Š” ๋‚ ์งœ์™€ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
 
 
145
  def display_stock_with_highlight(input_value, change_type, percent_change):
146
  try:
147
+ # ์ž…๋ ฅ๊ฐ’์„ ํ‹ฐ์ปค๋กœ ๋ณ€ํ™˜
148
  ticker = name_to_ticker.get(input_value, input_value)
149
  stock = yf.Ticker(ticker)
150
+ stock_data = stock.history(period="5y") # ์ตœ๊ทผ 5๋…„ ๋ฐ์ดํ„ฐ๋กœ ์ œํ•œ
151
 
152
  if stock_data.empty:
153
+ return "์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", []
154
 
155
  stock_data['Change'] = stock_data['Close'].pct_change() * 100
156
+
157
  percent_change = float(percent_change)
158
 
159
  if change_type == "์ƒ์Šน":
 
163
  highlight_data = stock_data[stock_data['Change'] <= -percent_change]
164
  color = "purple"
165
  else:
166
+ return "Invalid change type", []
167
 
168
+ dates = stock_data.index.to_numpy()
169
+ closing_prices = stock_data['Close'].to_numpy()
170
 
171
  plt.figure(figsize=(10, 6))
172
  plt.plot(dates, closing_prices, color='gray', label=input_value)
173
  plt.scatter(highlight_data.index, highlight_data['Close'], color=color, label=f'{change_type} ํฌ์ธํŠธ')
174
 
175
  for index, row in highlight_data.iterrows():
176
+ plt.text(index, row['Close'], index.strftime('%Y-%m-%d'), fontsize=10, fontweight='bold', color=color, ha='right')
177
+ plt.axvline(x=index, color=color, linestyle='--', linewidth=1) # x์ถ•๊ณผ์˜ ์—ฐ๊ฒฐ์„  ์ ์„ ์œผ๋กœ ํ‘œ์‹œ
178
+
179
+ # ์ข…๋ชฉ๋ช… + '์ฃผ๊ฐ€ ์ถ”์ด'๋กœ ์ œ๋ชฉ ์„ค์ • (์ข…๋ชฉ๋ช… ๊ธฐ๋ฐ˜)
180
+ company_name = list(name_to_ticker.keys())[list(name_to_ticker.values()).index(ticker)]
181
+ plt.title(f'{company_name} ์ฃผ๊ฐ€ ์ถ”์ด', fontproperties=font_prop)
182
+ plt.xlabel('๋‚ ์งœ', fontproperties=font_prop)
183
+ plt.ylabel('์ข…๊ฐ€', fontproperties=font_prop)
184
  plt.legend()
185
 
186
  buf = io.BytesIO()
 
191
 
192
  highlight_dates = highlight_data.index.strftime('%Y-%m-%d').tolist()
193
 
194
+ return img, gr.update(choices=highlight_dates)
195
  except Exception as e:
196
+ return f"Error processing data: {e}", gr.update(choices=[])
 
197
 
198
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ (3์—ด ๋ ˆ์ด์•„์›ƒ)
199
  with gr.Blocks() as demo:
200
  gr.Markdown("## ์ฃผ๊ฐ€ ๊ทธ๋ž˜ํ”„์™€ ๋‰ด์Šค ์š”์•ฝ")
201
 
202
+ # CSS๋กœ 3์—ด ๋‰ด์Šค ์š”์•ฝ ํƒ€์ดํ‹€์ด ๋‹ฌ๋ฆฐ ํšŒ์ƒ‰ ๋„ค๋ชจ ์นธ ์ƒ์„ฑ
203
+ demo.queue().css("#news_output_box { background-color: #f0f0f0; padding: 20px; }")
204
+
205
  with gr.Row():
206
+ with gr.Column(): # ์ž…๋ ฅ๊ฐ’์„ ๋‹ด์„ ์ฒซ ๋ฒˆ์งธ ์—ด
207
  input_value = gr.Textbox(label="์ข…๋ชฉ๋ช… ๋˜๋Š” ํ‹ฐ์ปค ์ž…๋ ฅ", placeholder="์˜ˆ: SK๋ฐ”์ด์˜คํŒœ, AAPL")
208
  change_type = gr.Dropdown(choices=["์ƒ์Šน", "ํ•˜๋ฝ"], label="์ƒ์Šน ๋˜๋Š” ํ•˜๋ฝ ์„ ํƒ", value="์ƒ์Šน")
209
  percent_change = gr.Textbox(label="๋ณ€๋™ ํผ์„ผํŠธ (%)", placeholder="์˜ˆ: 10", value="10")
210
+
211
  submit_btn = gr.Button("Submit")
212
 
213
+ # ์˜ˆ์ œ (์ด์ „ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋ณต์›)
214
  examples = [["SK๋ฐ”์ด์˜คํŒœ"],
215
  ["๋‚˜์Šค๋‹ฅ ์‹œ์ด 1์œ„"],
 
216
  ["๋‚˜์Šค๋‹ฅ ํ—ฌ์Šค์ผ€์–ด ์‹œ์ด 1์œ„"],
217
+ ["๋‚˜์Šค๋‹ฅ ๋ฐ”์ด์˜คํ… ์‹œ์ด 1์œ„"],
218
  ["์ฝ”์Šคํ”ผ ์‹œ์ด 1์œ„"],
219
  ["์ฝ”์Šค๋‹ฅ ์‹œ์ด 1์œ„"]]
220
  gr.Examples(examples=examples, inputs=[input_value])
221
 
222
+ with gr.Column(): # ๊ทธ๋ž˜ํ”„๋ฅผ ์ถœ๋ ฅํ•  ๋‘ ๋ฒˆ์งธ ์—ด
223
  plot = gr.Image(label="์ฃผ๊ฐ€ ๊ทธ๋ž˜ํ”„")
224
  date_dropdown = gr.Dropdown(label="์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ๋‚ ์งœ ์„ ํƒ", choices=[])
225
 
226
+ with gr.Column(): # ๋‰ด์Šค ์š”์•ฝ์„ ์ถœ๋ ฅํ•  ์„ธ ๋ฒˆ์งธ ์—ด
227
+ # news_output์„ gr.Markdown์œผ๋กœ ๋ณ€๊ฒฝ, ๋นˆ ์นธ์„ ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ์œผ๋กœ ์„ค์ •
228
+ news_output = gr.Markdown(label="๋‰ด์Šค ์š”์•ฝ", value="", elem_id="news_output_box")
229
 
230
  # Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๊ทธ๋ž˜ํ”„ ๋ฐ ๋‚ ์งœ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ
231
  submit_btn.click(
 
241
  outputs=[news_output]
242
  )
243
 
244
+ # Gradio ์‹คํ–‰
245
  demo.launch()
246
+
247
+