File size: 9,177 Bytes
0dbb99d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import os
import datetime as dt
import yfinance as yf
import traceback
import json
from typing import Union, Dict

import gradio as gr
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

from ta.momentum import RSIIndicator, StochasticOscillator
from ta.trend import MACD
from ta.volume import volume_weighted_average_price

import dotenv
dotenv.load_dotenv()

FUNDAMENTAL_ANALYST_PROMPT = """
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.

You have access to the following tools:
1. **get_stock_prices**: Retrieves stock price data and technical indicators.
2. **get_financial_metrics**: Retrieves key financial metrics and financial ratios.
3. **get_financial_news**: Retrieves the latest financial news related to the stock.
4. **get_industry_data** *(if available)*: Retrieves industry trends and competitive positioning information.

---

### Your Task:
1. Use the provided stock symbol to query the tools.
2. Analyze the following areas in sequence:
   - **Stock price movements and technical indicators**: Examine recent price trends, volatility, and signals from RSI, MACD, VWAP, and other indicators.
   - **Financial health and key financial ratios**: Assess profitability, liquidity, solvency, and operational efficiency using metrics such as:
     - Profitability Ratios: Gross Profit Margin, Net Profit Margin, Operating Profit Margin
     - Liquidity Ratios: Current Ratio, Quick Ratio
     - Solvency Ratios: Debt-to-Equity Ratio, Interest Coverage Ratio
     - Efficiency Ratios: Inventory Turnover, Accounts Receivable Turnover
     - Market Ratios: Price-to-Earnings Ratio (P/E), Price-to-Book Ratio (P/B)
   - **Recent news and market sentiment**: Identify significant events or trends impacting the company's market perception.
   - **Industry analysis**: Evaluate the industry’s growth trends, technological advancements, and regulatory environment. Identify how the industry is evolving and how it affects the target company.
   - **Competitor analysis**: Compare the target company with key competitors in terms of market share, financial health, and growth potential.

3. Provide a concise and structured summary covering all sections, ensuring each area has actionable insights.

---

### Output Format : 以下請用繁體中文輸出
{
  "stock": "",
  "price_analysis": "<股票價格趨勢與技術指標分析>",
  "technical_analysis": "<技術指標分析與見解>",
  "financial_analysis": {
      "profitability_ratios": "<獲利能力比率分析>",
      "liquidity_ratios": "<流動性比率分析>",
      "solvency_ratios": "<償債能力比率分析>",
      "efficiency_ratios": "<營運效率比率分析>",
      "market_ratios": "<市場表現比率分析>",
      "summary": "<財務整體健康狀況與分析結論>"
  },
  "news_analysis": "<近期新聞摘要與其對股價的潛在影響>",
  "industry_analysis": "<產業趨勢、成長動力與潛在風險>",
  "competitor_analysis": "<主要競爭對手比較與市場地位分析>",
  "final_summary": "<整體綜合結論與投資建議>",
  "Asked Question Answer": "<根據上述分析的具體回答>"
}
"""

def period_to_start_end(period_str: str):
    """
    根據使用者選擇的時間區間,計算起始與結束日期。
    """
    now = dt.datetime.now()
    if period_str == "3mo":
        start = now - dt.timedelta(weeks=13)
    elif period_str == "6mo":
        start = now - dt.timedelta(weeks=26)
    elif period_str == "1yr":
        start = now - dt.timedelta(weeks=52)
    else:
        start = now - dt.timedelta(weeks=13)
    return start, now

# --- 取得股票價格與技術指標 ---
def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
    """
    使用 start 與 end 取得歷史股價資料(避免連線到 fc.yahoo.com),
    並計算 RSI、Stochastic、MACD 與 VWAP 指標。
    """
    try:
        start_date, end_date = period_to_start_end(period)
        data = yf.download(
            ticker,
            start=start_date,
            end=end_date,
            interval='1d'
        )
        df = data.copy()
        # 若有 multi-index,調整欄位名稱
        if df.columns.nlevels > 1:
            df.columns = [col[0] for col in df.columns]
        data.reset_index(inplace=True)
        data['Date'] = data['Date'].astype(str)

        indicators = {}

        # RSI
        rsi_series = RSIIndicator(df['Close'], window=14).rsi().iloc[-12:]
        indicators["RSI"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in rsi_series.dropna().to_dict().items()}

        # Stochastic Oscillator
        sto_series = StochasticOscillator(df['High'], df['Low'], df['Close'], window=14).stoch().iloc[-12:]
        indicators["Stochastic_Oscillator"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in sto_series.dropna().to_dict().items()}

        # MACD 與訊號線
        macd = MACD(df['Close'])
        macd_series = macd.macd().iloc[-12:]
        indicators["MACD"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in macd_series.to_dict().items()}
        macd_signal_series = macd.macd_signal().iloc[-12:]
        indicators["MACD_Signal"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in macd_signal_series.to_dict().items()}

        # VWAP
        vwap_series = volume_weighted_average_price(
            high=df['High'],
            low=df['Low'],
            close=df['Close'],
            volume=df['Volume']
        ).iloc[-12:]
        indicators["vwap"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in vwap_series.to_dict().items()}

        return {'stock_price': data.to_dict(orient='records'), 'indicators': indicators}
    except Exception as e:
        return f"Error fetching price data: {str(e)}"

# --- 取得財務新聞 ---
def get_financial_news(ticker: str) -> Union[Dict, str]:
    try:
        stock = yf.Ticker(ticker)
        news = stock.news
        if not news:
            return {"news": "No recent news found."}
        latest_news = [
            {
                "title": item.get('title'),
                "publisher": item.get('publisher'),
                "link": item.get('link'),
                "published_date": item.get('providerPublishTime')
            }
            for item in news[:5]
        ]
        return {"news": latest_news}
    except Exception as e:
        return f"Error fetching news: {str(e)}"

# --- 取得財務指標 ---
def get_financial_metrics(ticker: str) -> Union[Dict, str]:
    try:
        stock = yf.Ticker(ticker)
        info = stock.info
        return {
            'pe_ratio': info.get('forwardPE'),
            'price_to_book': info.get('priceToBook'),
            'debt_to_equity': info.get('debtToEquity'),
            'profit_margins': info.get('profitMargins')
        }
    except Exception as e:
        return f"Error fetching ratios: {str(e)}"

# --- 綜合基本面分析 ---
def analyze_stock(api_key: str, ticker: str, period: str) -> str:
    """
    根據輸入的 LLM API key、股票代號與時間區間,抓取各項資料後,
    組合成分析 Prompt 呼叫 LLM,最後回傳基本面分析結果。
    """
    try:
        # 建立 LLM 實例
        llm = ChatOpenAI(
            model='gpt-4o',
            openai_api_key=api_key,
            temperature=0
        )
        # 取得資料
        price_data = get_stock_prices(ticker, period)
        metrics = get_financial_metrics(ticker)
        news = get_financial_news(ticker)

        # 準備 prompt
        prompt = FUNDAMENTAL_ANALYST_PROMPT.replace("{company}", ticker)
        user_question = "Should I buy this stock?"
        analysis_prompt = f"""
根據以下 {ticker} 的資料,進行全面的基本面分析並回答使用者問題:"{user_question}"

股價與技術指標資料:
{json.dumps(price_data, ensure_ascii=False, indent=2)}

財務指標:
{json.dumps(metrics, ensure_ascii=False, indent=2)}

相關新聞:
{json.dumps(news, ensure_ascii=False, indent=2)}

{prompt}
"""
        # 呼叫 LLM 生成最終分析報告
        response = llm.invoke(analysis_prompt)
        return response.content
    except Exception as e:
        return f"分析過程中發生錯誤: {str(e)}\n{traceback.format_exc()}"

# --- Gradio 介面 ---
iface = gr.Interface(
    fn=analyze_stock,
    inputs=[
        gr.Textbox(label="LLM API Key", type="password", placeholder="請輸入 OpenAI API Key"),
        gr.Textbox(label="股票代號", placeholder="例如:TSLA 或 2330.TW"),
        gr.Dropdown(choices=["3mo", "6mo", "1yr"], label="時間區間", value="3mo")
    ],
    outputs=gr.Textbox(label="基本面分析結果"),
    title="股票基本面分析 App",
    description="輸入您的 LLM API key、股票代號與時間區間,取得該股票的基本面分析報告。"
)

if __name__ == "__main__":
    iface.launch()