tbdavid2019 commited on
Commit
4593632
·
1 Parent(s): 0255349
Files changed (1) hide show
  1. app.py +35 -17
app.py CHANGED
@@ -13,6 +13,7 @@ from ta.momentum import RSIIndicator, StochasticOscillator
13
  from ta.trend import MACD
14
  from ta.volume import volume_weighted_average_price
15
 
 
16
  import dotenv
17
  dotenv.load_dotenv()
18
 
@@ -75,6 +76,7 @@ You have access to the following tools:
75
  - Output should be structured, easy to read, and in Traditional Chinese.
76
  """
77
 
 
78
  def period_to_start_end(period_str: str):
79
  """
80
  根據使用者選擇的時間區間,計算起始與結束日期。
@@ -91,15 +93,13 @@ def period_to_start_end(period_str: str):
91
  start = now - dt.timedelta(weeks=13)
92
  return start, now
93
 
 
94
  def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
95
  """Fetches historical stock price data and technical indicators for a given ticker."""
96
  try:
97
  start, end = period_to_start_end(period)
98
-
99
- # --- 除錯訊息開始 ---
100
  print(f"[get_stock_prices] Downloading data for ticker={ticker}, period={period}")
101
  print(f"[get_stock_prices] Start={start}, End={end}")
102
- # --- 除錯訊息結束 ---
103
 
104
  data = yf.download(
105
  ticker,
@@ -108,24 +108,27 @@ def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
108
  interval='1d'
109
  )
110
 
111
- # --- 除錯訊息:看資料長度與前幾筆 ---
112
  print(f"[get_stock_prices] Fetched data shape={data.shape}")
113
  if not data.empty:
114
  print(f"[get_stock_prices] Head of data:\n{data.head()}")
115
  else:
116
  print("[get_stock_prices] No data returned from yfinance.")
117
- # ---
118
 
119
  if data.empty:
120
  return {"error": f"No stock data found for {ticker}"}
121
 
122
- # 處理多層欄位
123
  if data.columns.nlevels > 1:
124
  data.columns = [col[0] for col in data.columns]
125
 
126
- data.reset_index(inplace=True)
127
- data["Date"] = data["Date"].astype(str)
128
 
 
 
 
 
 
129
  df = data.copy()
130
 
131
  # 指標只取最後 12 筆即可
@@ -136,24 +139,33 @@ def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
136
  # RSI
137
  rsi_series = RSIIndicator(df['Close'], window=14).rsi().iloc[-n:]
138
  indicators["RSI"] = {
139
- str(date.date()): float(value) for date, value in rsi_series.dropna().items()
 
140
  }
141
 
142
  # Stochastic
143
- sto_series = StochasticOscillator(df['High'], df['Low'], df['Close'], window=14).stoch().iloc[-n:]
 
 
 
 
 
144
  indicators["Stochastic_Oscillator"] = {
145
- str(date.date()): float(value) for date, value in sto_series.dropna().items()
 
146
  }
147
 
148
  # MACD
149
  macd = MACD(df['Close'])
150
  macd_series = macd.macd().iloc[-n:]
151
  indicators["MACD"] = {
152
- str(date.date()): float(value) for date, value in macd_series.items()
 
153
  }
154
  macd_signal_series = macd.macd_signal().iloc[-n:]
155
  indicators["MACD_Signal"] = {
156
- str(date.date()): float(value) for date, value in macd_signal_series.items()
 
157
  }
158
 
159
  # VWAP
@@ -164,17 +176,20 @@ def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
164
  volume=df['Volume']
165
  ).iloc[-n:]
166
  indicators["vwap"] = {
167
- str(date.date()): float(value) for date, value in vwap_series.items()
 
168
  }
169
 
170
  return {
171
- "stock_price": data.to_dict(orient="records"),
172
  "indicators": indicators
173
  }
 
174
  except Exception as e:
175
  print(f"[get_stock_prices] Exception: {str(e)}")
176
  return f"Error fetching price data: {str(e)}"
177
 
 
178
  def get_financial_news(ticker: str) -> Union[Dict, str]:
179
  """Fetches the latest financial news related to a given ticker."""
180
  try:
@@ -202,13 +217,15 @@ def get_financial_news(ticker: str) -> Union[Dict, str]:
202
  print(f"[get_financial_news] Exception: {str(e)}")
203
  return f"Error fetching news: {str(e)}"
204
 
 
205
  def get_financial_metrics(ticker: str) -> Union[Dict, str]:
206
  """Fetches key financial ratios for a given ticker."""
207
  try:
208
  print(f"[get_financial_metrics] Fetching financial metrics for ticker={ticker}")
209
  stock = yf.Ticker(ticker)
210
  info = stock.info
211
- # --- 除錯訊息:打印完整 info 來檢查看看 ---
 
212
  print(f"[get_financial_metrics] Info returned:\n{json.dumps(info, indent=2)}")
213
 
214
  return {
@@ -221,6 +238,7 @@ def get_financial_metrics(ticker: str) -> Union[Dict, str]:
221
  print(f"[get_financial_metrics] Exception: {str(e)}")
222
  return f"Error fetching ratios: {str(e)}"
223
 
 
224
  def analyze_stock(api_key: str, ticker: str, period: str) -> str:
225
  """
226
  根據輸入的 LLM API key、股票代號與時間區間,抓取各項資料後,
@@ -257,7 +275,6 @@ def analyze_stock(api_key: str, ticker: str, period: str) -> str:
257
  {prompt}
258
  """
259
 
260
- # 呼叫 LLM 生成最終分析報告
261
  print("[analyze_stock] Sending prompt to LLM...")
262
  response = llm.invoke(analysis_prompt)
263
 
@@ -267,6 +284,7 @@ def analyze_stock(api_key: str, ticker: str, period: str) -> str:
267
  print(f"[analyze_stock] Exception: {str(e)}")
268
  return f"分析過程中發生錯誤: {str(e)}\n{traceback.format_exc()}"
269
 
 
270
  # --- Gradio 介面 ---
271
  iface = gr.Interface(
272
  fn=analyze_stock,
 
13
  from ta.trend import MACD
14
  from ta.volume import volume_weighted_average_price
15
 
16
+ import pandas as pd
17
  import dotenv
18
  dotenv.load_dotenv()
19
 
 
76
  - Output should be structured, easy to read, and in Traditional Chinese.
77
  """
78
 
79
+
80
  def period_to_start_end(period_str: str):
81
  """
82
  根據使用者選擇的時間區間,計算起始與結束日期。
 
93
  start = now - dt.timedelta(weeks=13)
94
  return start, now
95
 
96
+
97
  def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
98
  """Fetches historical stock price data and technical indicators for a given ticker."""
99
  try:
100
  start, end = period_to_start_end(period)
 
 
101
  print(f"[get_stock_prices] Downloading data for ticker={ticker}, period={period}")
102
  print(f"[get_stock_prices] Start={start}, End={end}")
 
103
 
104
  data = yf.download(
105
  ticker,
 
108
  interval='1d'
109
  )
110
 
 
111
  print(f"[get_stock_prices] Fetched data shape={data.shape}")
112
  if not data.empty:
113
  print(f"[get_stock_prices] Head of data:\n{data.head()}")
114
  else:
115
  print("[get_stock_prices] No data returned from yfinance.")
 
116
 
117
  if data.empty:
118
  return {"error": f"No stock data found for {ticker}"}
119
 
120
+ # 如果是多層欄位,就平坦化
121
  if data.columns.nlevels > 1:
122
  data.columns = [col[0] for col in data.columns]
123
 
124
+ # 設定日期為索引 (DatetimeIndex)
125
+ data.index = pd.to_datetime(data.index)
126
 
127
+ # 保留一份副本來輸出給 LLM 用
128
+ df_for_output = data.copy().reset_index()
129
+ df_for_output["Date"] = df_for_output["Date"].astype(str)
130
+
131
+ # 另存一份給技術指標用 (確保是 DatetimeIndex)
132
  df = data.copy()
133
 
134
  # 指標只取最後 12 筆即可
 
139
  # RSI
140
  rsi_series = RSIIndicator(df['Close'], window=14).rsi().iloc[-n:]
141
  indicators["RSI"] = {
142
+ str(date.date()): float(value)
143
+ for date, value in rsi_series.dropna().items()
144
  }
145
 
146
  # Stochastic
147
+ sto_series = StochasticOscillator(
148
+ df['High'],
149
+ df['Low'],
150
+ df['Close'],
151
+ window=14
152
+ ).stoch().iloc[-n:]
153
  indicators["Stochastic_Oscillator"] = {
154
+ str(date.date()): float(value)
155
+ for date, value in sto_series.dropna().items()
156
  }
157
 
158
  # MACD
159
  macd = MACD(df['Close'])
160
  macd_series = macd.macd().iloc[-n:]
161
  indicators["MACD"] = {
162
+ str(date.date()): float(value)
163
+ for date, value in macd_series.dropna().items()
164
  }
165
  macd_signal_series = macd.macd_signal().iloc[-n:]
166
  indicators["MACD_Signal"] = {
167
+ str(date.date()): float(value)
168
+ for date, value in macd_signal_series.dropna().items()
169
  }
170
 
171
  # VWAP
 
176
  volume=df['Volume']
177
  ).iloc[-n:]
178
  indicators["vwap"] = {
179
+ str(date.date()): float(value)
180
+ for date, value in vwap_series.dropna().items()
181
  }
182
 
183
  return {
184
+ "stock_price": df_for_output.to_dict(orient="records"),
185
  "indicators": indicators
186
  }
187
+
188
  except Exception as e:
189
  print(f"[get_stock_prices] Exception: {str(e)}")
190
  return f"Error fetching price data: {str(e)}"
191
 
192
+
193
  def get_financial_news(ticker: str) -> Union[Dict, str]:
194
  """Fetches the latest financial news related to a given ticker."""
195
  try:
 
217
  print(f"[get_financial_news] Exception: {str(e)}")
218
  return f"Error fetching news: {str(e)}"
219
 
220
+
221
  def get_financial_metrics(ticker: str) -> Union[Dict, str]:
222
  """Fetches key financial ratios for a given ticker."""
223
  try:
224
  print(f"[get_financial_metrics] Fetching financial metrics for ticker={ticker}")
225
  stock = yf.Ticker(ticker)
226
  info = stock.info
227
+
228
+ # 顯示完整 info 以便除錯
229
  print(f"[get_financial_metrics] Info returned:\n{json.dumps(info, indent=2)}")
230
 
231
  return {
 
238
  print(f"[get_financial_metrics] Exception: {str(e)}")
239
  return f"Error fetching ratios: {str(e)}"
240
 
241
+
242
  def analyze_stock(api_key: str, ticker: str, period: str) -> str:
243
  """
244
  根據輸入的 LLM API key、股票代號與時間區間,抓取各項資料後,
 
275
  {prompt}
276
  """
277
 
 
278
  print("[analyze_stock] Sending prompt to LLM...")
279
  response = llm.invoke(analysis_prompt)
280
 
 
284
  print(f"[analyze_stock] Exception: {str(e)}")
285
  return f"分析過程中發生錯誤: {str(e)}\n{traceback.format_exc()}"
286
 
287
+
288
  # --- Gradio 介面 ---
289
  iface = gr.Interface(
290
  fn=analyze_stock,