Spaces:
Running
Running
Commit
·
ee48331
1
Parent(s):
2154d91
app.py
CHANGED
@@ -81,45 +81,45 @@ def period_to_start_end(period_str: str):
|
|
81 |
start = now - dt.timedelta(weeks=13)
|
82 |
return start, now
|
83 |
|
84 |
-
|
85 |
-
def get_stock_prices(ticker: str
|
|
|
86 |
try:
|
87 |
-
start_date, end_date = period_to_start_end(period)
|
88 |
data = yf.download(
|
89 |
ticker,
|
90 |
-
start=
|
91 |
-
end=
|
92 |
interval='1d'
|
93 |
)
|
94 |
|
95 |
if data.empty:
|
96 |
-
return f"
|
|
|
|
|
|
|
|
|
97 |
|
98 |
-
# reset_index() 之後,立刻把欄位名稱都轉成字串
|
99 |
data.reset_index(inplace=True)
|
100 |
-
data
|
101 |
|
102 |
-
# 如果你另外有 df = data.copy() 想做技術指標,也要確保欄位一致
|
103 |
df = data.copy()
|
104 |
|
105 |
-
# 計算技術指標
|
106 |
-
from ta.momentum import RSIIndicator, StochasticOscillator
|
107 |
-
from ta.trend import MACD
|
108 |
-
from ta.volume import volume_weighted_average_price
|
109 |
-
|
110 |
indicators = {}
|
111 |
-
|
112 |
n = min(12, len(df))
|
|
|
|
|
113 |
rsi_series = RSIIndicator(df['Close'], window=14).rsi().iloc[-n:]
|
114 |
indicators["RSI"] = {
|
115 |
str(date.date()): int(value) for date, value in rsi_series.dropna().items()
|
116 |
}
|
117 |
|
|
|
118 |
sto_series = StochasticOscillator(df['High'], df['Low'], df['Close'], window=14).stoch().iloc[-n:]
|
119 |
indicators["Stochastic_Oscillator"] = {
|
120 |
str(date.date()): int(value) for date, value in sto_series.dropna().items()
|
121 |
}
|
122 |
|
|
|
123 |
macd = MACD(df['Close'])
|
124 |
macd_series = macd.macd().iloc[-n:]
|
125 |
indicators["MACD"] = {
|
@@ -130,6 +130,7 @@ def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
|
|
130 |
str(date.date()): int(value) for date, value in macd_signal_series.items()
|
131 |
}
|
132 |
|
|
|
133 |
vwap_series = volume_weighted_average_price(
|
134 |
high=df['High'],
|
135 |
low=df['Low'],
|
@@ -141,34 +142,37 @@ def get_stock_prices(ticker: str, period: str = "3mo") -> Union[Dict, str]:
|
|
141 |
}
|
142 |
|
143 |
return {
|
144 |
-
|
145 |
-
|
146 |
}
|
147 |
except Exception as e:
|
148 |
return f"Error fetching price data: {str(e)}"
|
149 |
|
150 |
-
|
151 |
def get_financial_news(ticker: str) -> Union[Dict, str]:
|
|
|
152 |
try:
|
153 |
stock = yf.Ticker(ticker)
|
154 |
-
news = stock.news
|
155 |
if not news:
|
156 |
return {"news": "No recent news found."}
|
157 |
-
|
158 |
-
|
|
|
|
|
|
|
159 |
"title": item.get('title'),
|
160 |
"publisher": item.get('publisher'),
|
161 |
"link": item.get('link'),
|
162 |
"published_date": item.get('providerPublishTime')
|
163 |
-
}
|
164 |
-
for item in news[:5]
|
165 |
-
]
|
166 |
return {"news": latest_news}
|
167 |
except Exception as e:
|
168 |
return f"Error fetching news: {str(e)}"
|
169 |
|
170 |
-
|
171 |
def get_financial_metrics(ticker: str) -> Union[Dict, str]:
|
|
|
172 |
try:
|
173 |
stock = yf.Ticker(ticker)
|
174 |
info = stock.info
|
|
|
81 |
start = now - dt.timedelta(weeks=13)
|
82 |
return start, now
|
83 |
|
84 |
+
@tool
|
85 |
+
def get_stock_prices(ticker: str) -> Union[Dict, str]:
|
86 |
+
"""Fetches historical stock price data and technical indicator for a given ticker."""
|
87 |
try:
|
|
|
88 |
data = yf.download(
|
89 |
ticker,
|
90 |
+
start=dt.datetime.now() - dt.timedelta(weeks=13),
|
91 |
+
end=dt.datetime.now(),
|
92 |
interval='1d'
|
93 |
)
|
94 |
|
95 |
if data.empty:
|
96 |
+
return {"error": f"No stock data found for {ticker}"}
|
97 |
+
|
98 |
+
# 如果 columns 有多層,才做重命名
|
99 |
+
if data.columns.nlevels > 1:
|
100 |
+
data.columns = [col[0] for col in data.columns]
|
101 |
|
|
|
102 |
data.reset_index(inplace=True)
|
103 |
+
data["Date"] = data["Date"].astype(str)
|
104 |
|
|
|
105 |
df = data.copy()
|
106 |
|
|
|
|
|
|
|
|
|
|
|
107 |
indicators = {}
|
|
|
108 |
n = min(12, len(df))
|
109 |
+
|
110 |
+
# RSI
|
111 |
rsi_series = RSIIndicator(df['Close'], window=14).rsi().iloc[-n:]
|
112 |
indicators["RSI"] = {
|
113 |
str(date.date()): int(value) for date, value in rsi_series.dropna().items()
|
114 |
}
|
115 |
|
116 |
+
# Stochastic
|
117 |
sto_series = StochasticOscillator(df['High'], df['Low'], df['Close'], window=14).stoch().iloc[-n:]
|
118 |
indicators["Stochastic_Oscillator"] = {
|
119 |
str(date.date()): int(value) for date, value in sto_series.dropna().items()
|
120 |
}
|
121 |
|
122 |
+
# MACD
|
123 |
macd = MACD(df['Close'])
|
124 |
macd_series = macd.macd().iloc[-n:]
|
125 |
indicators["MACD"] = {
|
|
|
130 |
str(date.date()): int(value) for date, value in macd_signal_series.items()
|
131 |
}
|
132 |
|
133 |
+
# VWAP
|
134 |
vwap_series = volume_weighted_average_price(
|
135 |
high=df['High'],
|
136 |
low=df['Low'],
|
|
|
142 |
}
|
143 |
|
144 |
return {
|
145 |
+
"stock_price": data.to_dict(orient="records"),
|
146 |
+
"indicators": indicators
|
147 |
}
|
148 |
except Exception as e:
|
149 |
return f"Error fetching price data: {str(e)}"
|
150 |
|
151 |
+
@tool
|
152 |
def get_financial_news(ticker: str) -> Union[Dict, str]:
|
153 |
+
"""Fetches the latest financial news related to a given ticker."""
|
154 |
try:
|
155 |
stock = yf.Ticker(ticker)
|
156 |
+
news = stock.news # 從 Yahoo Finance 獲取新聞
|
157 |
if not news:
|
158 |
return {"news": "No recent news found."}
|
159 |
+
|
160 |
+
# 只取最新5則新聞
|
161 |
+
latest_news = []
|
162 |
+
for item in news[:5]:
|
163 |
+
latest_news.append({
|
164 |
"title": item.get('title'),
|
165 |
"publisher": item.get('publisher'),
|
166 |
"link": item.get('link'),
|
167 |
"published_date": item.get('providerPublishTime')
|
168 |
+
})
|
|
|
|
|
169 |
return {"news": latest_news}
|
170 |
except Exception as e:
|
171 |
return f"Error fetching news: {str(e)}"
|
172 |
|
173 |
+
@tool
|
174 |
def get_financial_metrics(ticker: str) -> Union[Dict, str]:
|
175 |
+
"""Fetches key financial ratios for a given ticker."""
|
176 |
try:
|
177 |
stock = yf.Ticker(ticker)
|
178 |
info = stock.info
|