Refactor and translate comments and docstrings in utility modules to English for better clarity and maintainability. Update Gunicorn and application startup messages for consistency in language. Enhance documentation with English translations for better accessibility.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
美股数据源
|
||||
使用 yfinance 和 finnhub 获取数据
|
||||
US stock data source
|
||||
Get data using yfinance and finnhub
|
||||
"""
|
||||
from typing import Dict, List, Any, Optional
|
||||
from datetime import datetime, timedelta
|
||||
@@ -15,11 +15,11 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class USStockDataSource(BaseDataSource):
|
||||
"""美股数据源"""
|
||||
"""US stock data source"""
|
||||
|
||||
name = "USStock/yfinance"
|
||||
|
||||
# yfinance 时间周期映射
|
||||
# yfinance time period mapping
|
||||
INTERVAL_MAP = {
|
||||
'1m': '1m',
|
||||
'5m': '5m',
|
||||
@@ -31,7 +31,7 @@ class USStockDataSource(BaseDataSource):
|
||||
'1W': '1wk'
|
||||
}
|
||||
|
||||
# 不同周期获取数据的天数范围
|
||||
# The range of days to obtain data in different periods
|
||||
DAYS_MAP = {
|
||||
'1m': lambda limit: min(7, max(1, (limit // 390) + 2)),
|
||||
'5m': lambda limit: min(60, max(1, (limit // 78) + 2)),
|
||||
@@ -44,7 +44,7 @@ class USStockDataSource(BaseDataSource):
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
# 初始化 finnhub 作为备选
|
||||
# Initialize finnhub as an alternative
|
||||
self.finnhub_client = None
|
||||
try:
|
||||
import finnhub
|
||||
@@ -56,45 +56,45 @@ class USStockDataSource(BaseDataSource):
|
||||
|
||||
def get_ticker(self, symbol: str) -> Dict[str, Any]:
|
||||
"""
|
||||
获取美股实时报价
|
||||
Get realtime quotes for U.S. stocks
|
||||
|
||||
优先使用 Finnhub(更实时),降级使用 yfinance fast_info
|
||||
Use Finnhub first (more real-time), downgrade to yfinance fast_info
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
'last': 当前价格,
|
||||
'change': 涨跌额,
|
||||
'changePercent': 涨跌幅,
|
||||
'high': 最高价,
|
||||
'low': 最低价,
|
||||
'open': 开盘价,
|
||||
'previousClose': 昨收价
|
||||
'last': current price,
|
||||
'change': change amount,
|
||||
'changePercent': increase or decrease,
|
||||
'high': highest price,
|
||||
'low': lowest price,
|
||||
'open': opening price,
|
||||
'previousClose': yesterday's closing price
|
||||
}
|
||||
"""
|
||||
symbol = (symbol or '').strip().upper()
|
||||
|
||||
# 优先使用 Finnhub(实时数据)
|
||||
# Prefer using Finnhub (live data)
|
||||
if self.finnhub_client:
|
||||
try:
|
||||
quote = self.finnhub_client.quote(symbol)
|
||||
if quote and quote.get('c'):
|
||||
return {
|
||||
'last': quote.get('c', 0), # 当前价格
|
||||
'change': quote.get('d', 0), # 涨跌额
|
||||
'changePercent': quote.get('dp', 0), # 涨跌幅
|
||||
'high': quote.get('h', 0), # 日内最高
|
||||
'low': quote.get('l', 0), # 日内最低
|
||||
'open': quote.get('o', 0), # 开盘价
|
||||
'previousClose': quote.get('pc', 0) # 昨收价
|
||||
'last': quote.get('c', 0), # current price
|
||||
'change': quote.get('d', 0), # Changes
|
||||
'changePercent': quote.get('dp', 0), # Increase or decrease
|
||||
'high': quote.get('h', 0), # Best in Japan
|
||||
'low': quote.get('l', 0), # Lowest within the day
|
||||
'open': quote.get('o', 0), # opening price
|
||||
'previousClose': quote.get('pc', 0) # Yesterday's closing price
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"Finnhub quote failed for {symbol}: {e}")
|
||||
|
||||
# 降级使用 yfinance
|
||||
# Downgrade to use yfinance
|
||||
try:
|
||||
ticker = yf.Ticker(symbol)
|
||||
|
||||
# 尝试 fast_info(更快)
|
||||
# Try fast_info (faster)
|
||||
try:
|
||||
fast_info = ticker.fast_info
|
||||
last_price = fast_info.get('lastPrice') or fast_info.get('last_price')
|
||||
@@ -115,7 +115,7 @@ class USStockDataSource(BaseDataSource):
|
||||
except Exception as e:
|
||||
logger.debug(f"yfinance fast_info failed for {symbol}: {e}")
|
||||
|
||||
# 降级使用 info(较慢但数据更全)
|
||||
# Downgrade to use info (slower but more complete data)
|
||||
try:
|
||||
info = ticker.info
|
||||
last_price = info.get('regularMarketPrice') or info.get('currentPrice')
|
||||
@@ -136,7 +136,7 @@ class USStockDataSource(BaseDataSource):
|
||||
except Exception as e:
|
||||
logger.debug(f"yfinance info failed for {symbol}: {e}")
|
||||
|
||||
# 最后降级:使用最近的 1 分钟 K 线
|
||||
# Last downgrade: use the most recent 1-minute K-line
|
||||
try:
|
||||
hist = ticker.history(period='1d', interval='1m')
|
||||
if hist is not None and not hist.empty:
|
||||
@@ -152,7 +152,7 @@ class USStockDataSource(BaseDataSource):
|
||||
'high': float(hist['High'].max()),
|
||||
'low': float(hist['Low'].min()),
|
||||
'open': open_price,
|
||||
'previousClose': open_price # 近似
|
||||
'previousClose': open_price # approximate
|
||||
}
|
||||
except Exception as e:
|
||||
logger.debug(f"yfinance history fallback failed for {symbol}: {e}")
|
||||
@@ -169,7 +169,7 @@ class USStockDataSource(BaseDataSource):
|
||||
limit: int,
|
||||
before_time: Optional[int] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""获取美股K线数据"""
|
||||
"""Get U.S. stock K-line data"""
|
||||
klines = []
|
||||
|
||||
try:
|
||||
@@ -177,7 +177,7 @@ class USStockDataSource(BaseDataSource):
|
||||
days_func = self.DAYS_MAP.get(timeframe, lambda x: x + 1)
|
||||
days = days_func(limit)
|
||||
|
||||
# 计算日期范围
|
||||
# Calculate date range
|
||||
if before_time:
|
||||
end_date = datetime.fromtimestamp(before_time)
|
||||
start_date = end_date - timedelta(days=days)
|
||||
@@ -185,13 +185,13 @@ class USStockDataSource(BaseDataSource):
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(days=days)
|
||||
|
||||
# logger.info(f"使用 yfinance 获取 {symbol}, 周期: {interval}, 日期: {start_date.date()} ~ {end_date.date()}")
|
||||
# logger.info(f"Use yfinance to get {symbol}, period: {interval}, date: {start_date.date()} ~ {end_date.date()}")
|
||||
|
||||
# 尝试 yfinance
|
||||
# Try yfinance
|
||||
df = self._fetch_yfinance(symbol, interval, start_date, end_date)
|
||||
|
||||
if df is None or df.empty:
|
||||
# 尝试 finnhub
|
||||
# try finnhub
|
||||
if self.finnhub_client and timeframe == '1D':
|
||||
klines = self._fetch_finnhub(symbol, start_date, end_date, limit)
|
||||
if klines:
|
||||
@@ -199,10 +199,10 @@ class USStockDataSource(BaseDataSource):
|
||||
else:
|
||||
klines = self._convert_dataframe(df, limit)
|
||||
|
||||
# 过滤和限制
|
||||
# Filter and restrict
|
||||
klines = self.filter_and_limit(klines, limit, before_time)
|
||||
|
||||
# 记录结果
|
||||
# Record results
|
||||
self.log_result(symbol, klines, timeframe)
|
||||
|
||||
except Exception as e:
|
||||
@@ -213,12 +213,12 @@ class USStockDataSource(BaseDataSource):
|
||||
return klines
|
||||
|
||||
def _fetch_yfinance(self, symbol: str, interval: str, start_date: datetime, end_date: datetime):
|
||||
"""使用 yfinance 获取数据"""
|
||||
"""Use yfinance to get data"""
|
||||
try:
|
||||
ticker = yf.Ticker(symbol)
|
||||
|
||||
# yfinance 的 end 参数是不包含的(exclusive),所以需要加一天才能包含 end_date 当天的数据
|
||||
# 例如:end="2026-01-12" 实际只返回到 2026-01-11 的数据
|
||||
# The end parameter of yfinance is not included (exclusive), so you need to add one day to include the end_date data of the current day.
|
||||
# For example: end="2026-01-12" actually only returns the data of 2026-01-11
|
||||
end_date_inclusive = end_date + timedelta(days=1)
|
||||
|
||||
df = ticker.history(
|
||||
@@ -226,7 +226,7 @@ class USStockDataSource(BaseDataSource):
|
||||
end=end_date_inclusive.strftime('%Y-%m-%d'),
|
||||
interval=interval
|
||||
)
|
||||
# logger.info(f"yfinance 返回 {len(df) if df is not None and not df.empty else 0} 条数据")
|
||||
# logger.info(f"yfinance returns {len(df) if df is not None and not df.empty else 0} pieces of data")
|
||||
return df
|
||||
except Exception as e:
|
||||
logger.warning(f"yfinance fetch failed: {e}")
|
||||
@@ -239,13 +239,13 @@ class USStockDataSource(BaseDataSource):
|
||||
end_date: datetime,
|
||||
limit: int
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""使用 finnhub 获取日线数据"""
|
||||
"""Use finnhub to get daily data"""
|
||||
klines = []
|
||||
try:
|
||||
start_ts = int(start_date.timestamp())
|
||||
end_ts = int(end_date.timestamp())
|
||||
|
||||
# logger.info(f"使用 Finnhub 获取 {symbol} 日线数据")
|
||||
# logger.info(f"Use Finnhub to obtain {symbol} daily data")
|
||||
candles = self.finnhub_client.stock_candles(symbol, 'D', start_ts, end_ts)
|
||||
|
||||
if candles and candles.get('s') == 'ok':
|
||||
@@ -258,18 +258,18 @@ class USStockDataSource(BaseDataSource):
|
||||
close=candles['c'][i],
|
||||
volume=candles['v'][i]
|
||||
))
|
||||
# logger.info(f"Finnhub 返回 {len(klines)} 条数据")
|
||||
# logger.info(f"Finnhub returns {len(klines)} pieces of data")
|
||||
except Exception as e:
|
||||
logger.error(f"Finnhub fetch failed: {e}")
|
||||
|
||||
return klines
|
||||
|
||||
def _convert_dataframe(self, df, limit: int) -> List[Dict[str, Any]]:
|
||||
"""转换 DataFrame 为K线列表"""
|
||||
"""Convert DataFrame to K-line list"""
|
||||
klines = []
|
||||
df = df.tail(limit).reset_index()
|
||||
|
||||
# 确定时间列名(日线是 Date,分钟级是 Datetime)
|
||||
# Determine the time column name (the daily line is Date, the minute level is Datetime)
|
||||
time_col = None
|
||||
if 'Datetime' in df.columns:
|
||||
time_col = 'Datetime'
|
||||
@@ -284,7 +284,7 @@ class USStockDataSource(BaseDataSource):
|
||||
|
||||
for _, row in df.iterrows():
|
||||
try:
|
||||
# 处理时间戳
|
||||
# Processing timestamps
|
||||
time_value = row[time_col]
|
||||
if hasattr(time_value, 'timestamp'):
|
||||
ts = int(time_value.timestamp())
|
||||
|
||||
Reference in New Issue
Block a user