tbdavid2019 commited on
Commit
fb2652f
·
verified ·
1 Parent(s): 2970268
Files changed (1) hide show
  1. app.py +46 -45
app.py CHANGED
@@ -17,7 +17,7 @@ import pandas as pd
17
  import dotenv
18
  dotenv.load_dotenv()
19
 
20
- # --- 新版 Prompt ---
21
  FUNDAMENTAL_ANALYST_PROMPT = """
22
  You are a fundamental analyst specializing in evaluating company (whose symbol is {company}) performance based on stock prices, technical indicators, financial metrics, recent news, industry trends, competitor positioning, and financial ratios. Your task is to provide a comprehensive summary.
23
 
@@ -47,35 +47,43 @@ You have access to the following tools:
47
 
48
  ---
49
 
50
- ### Output Format : 以下請用繁體中文輸出
51
- {
52
- "stock": "<Stock Symbol>",
53
- "price_analysis": "<股票價格趨勢與技術指標分析>",
54
- "technical_analysis": "<技術指標分析與見解>",
55
- "financial_analysis": {
56
- "profitability_ratios": "<獲利能力比率分析>",
57
- "liquidity_ratios": "<流動性比率分析>",
58
- "solvency_ratios": "<償債能力比率分析>",
59
- "efficiency_ratios": "<營運效率比率分析>",
60
- "market_ratios": "<市場表現比率分析>",
61
- "summary": "<財務整體健康狀況與分析結論>"
62
- },
63
- "news_analysis": "<近期新聞摘要與其對股價的潛在影響>",
64
- "industry_analysis": "<產業趨勢、成長動力與潛在風險>",
65
- "competitor_analysis": "<主要競爭對手比較與市場地位分析>",
66
- "final_summary": "<整體綜合結論與投資建議>",
67
- "Asked Question Answer": "<根據上述分析的具體回答>"
68
- }
69
 
70
- ---
 
71
 
72
- ### Guidelines:
73
- - Use the provided tools for data. If any data is unavailable, clearly state so in the respective section.
74
- - Ensure the analysis is objective, data-driven, and free of speculative language.
75
- - Keep responses concise but informative. Highlight actionable insights and risks.
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
  """
@@ -93,7 +101,6 @@ def period_to_start_end(period_str: str):
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:
@@ -184,12 +191,10 @@ def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
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:
@@ -201,23 +206,23 @@ def get_financial_news(ticker: str) -> Union[Dict, str]:
201
  print("[get_financial_news] No news data returned from yfinance.")
202
  return {"news": "No recent news found."}
203
 
204
- # 只取最新5則新聞
205
  latest_news = []
206
- for item in news[:5]:
 
207
  latest_news.append({
208
- "title": item.get('title'),
209
- "publisher": item.get('publisher'),
210
- "link": item.get('link'),
211
- "published_date": item.get('providerPublishTime')
212
  })
213
 
214
- print(f"[get_financial_news] Found {len(latest_news)} news items.")
215
  return {"news": latest_news}
216
  except Exception as e:
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:
@@ -225,7 +230,6 @@ def get_financial_metrics(ticker: str) -> Union[Dict, str]:
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,11 +242,10 @@ def get_financial_metrics(ticker: str) -> Union[Dict, str]:
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、股票代號與時間區間,抓取各項資料後,
245
- 組合成分析 Prompt 呼叫 LLM,最後回傳基本面分析結果。
246
  """
247
  try:
248
  # 建立 LLM 實例
@@ -278,13 +281,11 @@ def analyze_stock(api_key: str, ticker: str, period: str) -> str:
278
  print("[analyze_stock] Sending prompt to LLM...")
279
  response = llm.invoke(analysis_prompt)
280
 
281
- return response.content
282
-
283
  except Exception as e:
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,
@@ -295,7 +296,7 @@ iface = gr.Interface(
295
  ],
296
  outputs=gr.Textbox(label="基本面分析結果"),
297
  title="股票基本面分析 App",
298
- description="輸入您的 LLM API key、股票代號與時間區間,取得該股票的基本面分析報告。"
299
  )
300
 
301
  if __name__ == "__main__":
 
17
  import dotenv
18
  dotenv.load_dotenv()
19
 
20
+ # --- 新版 Prompt(改為人類可讀的非 JSON 輸出) ---
21
  FUNDAMENTAL_ANALYST_PROMPT = """
22
  You are a fundamental analyst specializing in evaluating company (whose symbol is {company}) performance based on stock prices, technical indicators, financial metrics, recent news, industry trends, competitor positioning, and financial ratios. Your task is to provide a comprehensive summary.
23
 
 
47
 
48
  ---
49
 
50
+ ### Output Format: 以下請用繁體中文輸出,且以「易讀文字段落/清單」方式呈現 (非 JSON):
51
+ 請根據以下結構,撰寫一份詳細的分析報告:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ 1. 【股票代碼】
54
+ - 說明:顯示本次分析所針對的股票代號。
55
 
56
+ 2. 【股票價格趨勢與技術指標分析】
57
+ - 說明:闡述近期股價變化趨勢、波動情況,並搭配 RSI、MACD、VWAP 等技術指標,提供觀察與解讀。
58
+
59
+ 3. 【技術指標分析與見解】
60
+ - 說明:進一步解釋 RSI、MACD 等所呈現的訊號,與可能的後續走勢。
61
+
62
+ 4. 【財務分析】
63
+ - 4.1 獲利能力比率分析
64
+ - 4.2 流動性比率分析
65
+ - 4.3 償債能力比率分析
66
+ - 4.4 營運效率比率分析
67
+ - 4.5 市場表現比率分析
68
+ - 4.6 總結財務健康狀況
69
+
70
+ 5. 【新聞分析】
71
+ - 說明:列出近期相關新聞及其對股價、企業形象可能產生的影響。
72
 
73
+ 6. 【產業分析】
74
+ - 說明:討論產業趨勢、發展動能與可能風險。
75
+
76
+ 7. 【競爭對手分析】
77
+ - 說明:比較主要競爭對手之優勢與劣勢,以及對目標公司的影響。
78
+
79
+ 8. 【最終結論與投資建議】
80
+ - 說明:歸納整體分析結果,給出投資人應採取之行動或建議。
81
+
82
+ 9. 【回答使用者問題】
83
+ - 針對「Should I buy this stock?」之回應,提出依據上述分析的結論性建議。
84
+
85
+ ※ 請確保敘述完整、清晰、條理分明、方便一般讀者閱讀。同時避免使用 JSON 格式,改以段落或清單式敘述。
86
+ """
87
 
88
  def period_to_start_end(period_str: str):
89
  """
 
101
  start = now - dt.timedelta(weeks=13)
102
  return start, now
103
 
 
104
  def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
105
  """Fetches historical stock price data and technical indicators for a given ticker."""
106
  try:
 
191
  "stock_price": df_for_output.to_dict(orient="records"),
192
  "indicators": indicators
193
  }
 
194
  except Exception as e:
195
  print(f"[get_stock_prices] Exception: {str(e)}")
196
  return f"Error fetching price data: {str(e)}"
197
 
 
198
  def get_financial_news(ticker: str) -> Union[Dict, str]:
199
  """Fetches the latest financial news related to a given ticker."""
200
  try:
 
206
  print("[get_financial_news] No news data returned from yfinance.")
207
  return {"news": "No recent news found."}
208
 
209
+ # 詳細列印前 5 則新聞資訊
210
  latest_news = []
211
+ for i, item in enumerate(news[:5], start=1):
212
+ print(f"[get_financial_news] News item {i} RAW: {json.dumps(item, indent=2)}")
213
  latest_news.append({
214
+ "title": item.get("title"),
215
+ "publisher": item.get("publisher"),
216
+ "link": item.get("link"),
217
+ "published_date": item.get("providerPublishTime")
218
  })
219
 
220
+ print(f"[get_financial_news] Found {len(latest_news)} news items (detailed above).")
221
  return {"news": latest_news}
222
  except Exception as e:
223
  print(f"[get_financial_news] Exception: {str(e)}")
224
  return f"Error fetching news: {str(e)}"
225
 
 
226
  def get_financial_metrics(ticker: str) -> Union[Dict, str]:
227
  """Fetches key financial ratios for a given ticker."""
228
  try:
 
230
  stock = yf.Ticker(ticker)
231
  info = stock.info
232
 
 
233
  print(f"[get_financial_metrics] Info returned:\n{json.dumps(info, indent=2)}")
234
 
235
  return {
 
242
  print(f"[get_financial_metrics] Exception: {str(e)}")
243
  return f"Error fetching ratios: {str(e)}"
244
 
 
245
  def analyze_stock(api_key: str, ticker: str, period: str) -> str:
246
  """
247
  根據輸入的 LLM API key、股票代號與時間區間,抓取各項資料後,
248
+ 組合成分析 Prompt 呼叫 LLM,最後回傳『易讀文字段落』的基本面分析結果。
249
  """
250
  try:
251
  # 建立 LLM 實例
 
281
  print("[analyze_stock] Sending prompt to LLM...")
282
  response = llm.invoke(analysis_prompt)
283
 
284
+ return response.content # 回傳「非 JSON」的易讀文字段落
 
285
  except Exception as e:
286
  print(f"[analyze_stock] Exception: {str(e)}")
287
  return f"分析過程中發生錯誤: {str(e)}\n{traceback.format_exc()}"
288
 
 
289
  # --- Gradio 介面 ---
290
  iface = gr.Interface(
291
  fn=analyze_stock,
 
296
  ],
297
  outputs=gr.Textbox(label="基本面分析結果"),
298
  title="股票基本面分析 App",
299
+ description="輸入您的 LLM API key、股票代號與時間區間,取得該股票的分析報告(非 JSON、易讀文字段落)。"
300
  )
301
 
302
  if __name__ == "__main__":