实现了多货币的获取和显示

This commit is contained in:
xucheng 2026-01-12 09:33:52 +08:00
parent 11fa7093ba
commit e786e885e6
11 changed files with 308 additions and 59 deletions

View File

@ -80,7 +80,8 @@ async def fetch_data(
market=request.market,
symbol=request.symbol,
data_source=request.data_source,
update_id=data_update.id
update_id=data_update.id,
currency=request.currency
)
return FetchDataResponse(
@ -96,7 +97,8 @@ def fetch_data_background(
market: str,
symbol: str,
data_source: str,
update_id: int
update_id: int,
currency: str = None
):
"""后台数据获取任务 - 完全同步执行,避免event loop冲突"""
import sys
@ -117,7 +119,8 @@ def fetch_data_background(
market=market,
symbol=symbol,
data_source=data_source,
update_id=update_id
update_id=update_id,
currency=currency
)
# 更新数据更新记录 - 使用psycopg2同步连接

View File

@ -399,7 +399,7 @@ class BloombergClient:
# --- Core Fetching Logic ---
def fetch_company(self, market, symbol, progress_callback=None):
def fetch_company(self, market, symbol, progress_callback=None, force_currency=None):
"""
Main entry point to fetch data for a single company.
"""
@ -445,13 +445,16 @@ if 'bquery' not in globals():
self.save_data(basic_data)
# Determine currency
if force_currency and force_currency != "Auto":
currency = force_currency
logger.info(f"Using forced currency: {currency}")
else:
currency = "USD"
if "JP" in market.upper(): currency = "JPY"
elif "VN" in market.upper(): currency = "VND"
elif "CN" in market.upper(): currency = "CNY"
elif "HK" in market.upper(): currency = "HKD"
logger.info(f"Using currency: {currency}")
logger.info(f"Using auto-detected currency: {currency}")
# 2. Fetch Currency Data
logger.info("Fetching Currency Data...")

View File

@ -52,7 +52,7 @@ class BloombergFetcher(DataFetcher):
# placeholders for 'IN' clause
placeholders = ','.join(['%s'] * len(indicators))
query = f"""
SELECT indicator, value, value_date
SELECT indicator, value, value_date, currency
FROM stockcard
WHERE Company_code = %s AND indicator IN ({placeholders})
ORDER BY value_date DESC
@ -65,15 +65,17 @@ class BloombergFetcher(DataFetcher):
if not rows:
return pd.DataFrame()
df = pd.DataFrame(rows, columns=['indicator', 'value', 'value_date'])
df = pd.DataFrame(rows, columns=['indicator', 'value', 'value_date', 'currency'])
# Pivot
# Index: value_date
# Index: [value_date, currency] -> ensures we keep both JPY and USD rows for the same date
# Columns: indicator
# Values: value
df_pivot = df.pivot(index='value_date', columns='indicator', values='value').reset_index()
df_pivot = df.pivot(index=['value_date', 'currency'], columns='indicator', values='value').reset_index()
df_pivot.rename(columns={'value_date': 'end_date'}, inplace=True)
# No need to manual merge currency back, it's already in the index
# Clean columns? No, they are standard from config.
return df_pivot
@ -103,14 +105,12 @@ class BloombergFetcher(DataFetcher):
except Exception as e:
print(f"Bloomberg fetch failed (ignoring, checking DB): {e}")
def sync_all_data(self, symbol: str, progress_callback=None) -> None:
def sync_all_data(self, symbol: str, progress_callback=None, force_currency=None):
"""
全量同步 Bloomberg 数据
触发一次全量抓取即可不需要分别抓取各个报表
数据会自动存储到数据库
Sync all data for the company.
Delegates to the universal client.
"""
self._ensure_data_fetched(symbol, progress_callback=progress_callback)
self.client.fetch_company(self.market, symbol, progress_callback=progress_callback, force_currency=force_currency)
def get_income_statement(self, symbol: str) -> pd.DataFrame:
"""兼容性空方法"""

View File

@ -47,6 +47,7 @@ class FetchDataRequest(BaseModel):
company_name: str
data_source: str
force_refresh: bool = False
currency: Optional[str] = None
class FetchDataResponse(BaseModel):
update_id: int

View File

@ -79,14 +79,14 @@ async def get_bloomberg_data(
date_str = v_date.isoformat() if hasattr(v_date, 'isoformat') else str(v_date)
if date_str not in data_by_date:
data_by_date[date_str] = {
# Key by (date, currency) to support multiple currencies for the same date
unique_key = f"{date_str}_{curr}"
if unique_key not in data_by_date:
data_by_date[unique_key] = {
"end_date": date_str,
"currency": curr
}
elif data_by_date[date_str].get("currency") is None and curr:
# If existing entry has no currency, but we have one now, update it
data_by_date[date_str]["currency"] = curr
# Normalize key to match frontend expectation (mostly lowercase)
# e.g. "ROE" -> "roe", "PE" -> "pe"
@ -95,7 +95,7 @@ async def get_bloomberg_data(
# Special case mapping if needed, but lowercase covers most
# frontend uses: net_income, revenue, etc.
data_by_date[date_str][norm_indicator] = val
data_by_date[unique_key][norm_indicator] = val
# 4. 转换为列表并排序
full_list = list(data_by_date.values())

View File

@ -209,7 +209,8 @@ def fetch_financial_data_sync(
market: str,
symbol: str,
data_source: str,
update_id: int
update_id: int,
currency: Optional[str] = None
):
"""
同步方式获取财务数据在后台任务中调用
@ -258,6 +259,9 @@ def fetch_financial_data_sync(
import inspect
sig = inspect.signature(fetcher.sync_all_data)
if 'progress_callback' in sig.parameters:
if 'force_currency' in sig.parameters:
fetcher.sync_all_data(formatted_symbol, progress_callback=progress_callback, force_currency=currency)
else:
fetcher.sync_all_data(formatted_symbol, progress_callback=progress_callback)
else:
fetcher.sync_all_data(formatted_symbol)

View File

@ -1396,3 +1396,163 @@ sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.Program
2026-01-11 21:30:59,004 - app.clients.bloomberg_client - INFO - ✅ Completed processing for 2503 JP Equity
2026-01-11 21:30:59,004 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=49, Msg=Bloomberg data sync complete, Progress=90%
2026-01-11 21:30:59,770 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=49, Msg=Bloomberg 数据同步完成, Progress=100%
2026-01-12 08:38:48,085 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=正在初始化数据获取..., Progress=0%
2026-01-12 08:38:48,574 - app.clients.bloomberg_client - INFO - Connecting to Jupyter at http://192.168.3.161:8888...
2026-01-12 08:38:48,811 - app.clients.bloomberg_client - INFO - ✅ Authentication successful.
2026-01-12 08:38:48,841 - app.clients.bloomberg_client - INFO - ✅ Found existing kernel: bc27f3b1-b028-434a-99fa-c1cad4495a87 (remote_env)
2026-01-12 08:38:48,892 - app.clients.bloomberg_client - INFO - ✅ WebSocket connected.
2026-01-12 08:38:48,892 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=数据源连接成功, Progress=10%
2026-01-12 08:38:49,179 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=正在连接 Bloomberg 终端..., Progress=20%
2026-01-12 08:38:49,424 - app.clients.bloomberg_client - INFO - 🚀 Starting fetch for: 2503 JP Equity
2026-01-12 08:38:49,424 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=Starting Bloomberg session..., Progress=20%
2026-01-12 08:38:49,723 - app.clients.bloomberg_client - INFO - Fetching Basic Data...
2026-01-12 08:38:49,723 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=Fetching Company Basic Info..., Progress=27%
2026-01-12 08:38:51,005 - app.clients.bloomberg_client - INFO - ✅ Parsed 2 items from remote.
2026-01-12 08:38:51,593 - app.clients.bloomberg_client - INFO - ✅ Saved 2 records to database.
2026-01-12 08:38:51,594 - app.clients.bloomberg_client - INFO - Using auto-detected currency: JPY
2026-01-12 08:38:51,594 - app.clients.bloomberg_client - INFO - Fetching Currency Data...
2026-01-12 08:38:51,594 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=正在获取货币指标 (JPY)..., Progress=41%
2026-01-12 08:38:52,587 - app.clients.bloomberg_client - INFO - ✅ Parsed 331 items from remote.
2026-01-12 08:39:02,351 - app.clients.bloomberg_client - INFO - ✅ Saved 331 records to database.
2026-01-12 08:39:02,354 - app.clients.bloomberg_client - INFO - Fetching Non-Currency Data...
2026-01-12 08:39:02,354 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=正在获取非货币指标..., Progress=55%
2026-01-12 08:39:03,417 - app.clients.bloomberg_client - INFO - ✅ Parsed 382 items from remote.
2026-01-12 08:39:14,223 - app.clients.bloomberg_client - INFO - ✅ Saved 382 records to database.
2026-01-12 08:39:14,224 - app.clients.bloomberg_client - INFO - Fetching Price Data (Aligned)...
2026-01-12 08:39:14,225 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=正在获取价格指标..., Progress=69%
2026-01-12 08:39:14,707 - app.clients.bloomberg_client - INFO - Found 14 revenue reporting dates. Fetching aligned price data...
2026-01-12 08:39:20,704 - app.clients.bloomberg_client - INFO - ✅ Parsed 42 items from remote.
2026-01-12 08:39:22,410 - app.clients.bloomberg_client - INFO - ✅ Saved 42 records to database.
2026-01-12 08:39:22,411 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=Finalizing data..., Progress=82%
2026-01-12 08:39:22,999 - app.clients.bloomberg_client - INFO - ✅ Cleanup and View Refresh completed.
2026-01-12 08:39:23,000 - app.clients.bloomberg_client - INFO - ✅ Completed processing for 2503 JP Equity
2026-01-12 08:39:23,000 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=Bloomberg data sync complete, Progress=90%
2026-01-12 08:39:23,269 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=50, Msg=Bloomberg 数据同步完成, Progress=100%
2026-01-12 08:39:31,217 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=正在初始化数据获取..., Progress=0%
2026-01-12 08:39:31,485 - app.clients.bloomberg_client - INFO - Connecting to Jupyter at http://192.168.3.161:8888...
2026-01-12 08:39:31,706 - app.clients.bloomberg_client - INFO - ✅ Authentication successful.
2026-01-12 08:39:31,733 - app.clients.bloomberg_client - INFO - ✅ Found existing kernel: bc27f3b1-b028-434a-99fa-c1cad4495a87 (remote_env)
2026-01-12 08:39:31,785 - app.clients.bloomberg_client - INFO - ✅ WebSocket connected.
2026-01-12 08:39:31,786 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=数据源连接成功, Progress=10%
2026-01-12 08:39:32,035 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=正在连接 Bloomberg 终端..., Progress=20%
2026-01-12 08:39:32,286 - app.clients.bloomberg_client - INFO - 🚀 Starting fetch for: 2503 JP Equity
2026-01-12 08:39:32,286 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=Starting Bloomberg session..., Progress=20%
2026-01-12 08:39:32,593 - app.clients.bloomberg_client - INFO - Fetching Basic Data...
2026-01-12 08:39:32,593 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=Fetching Company Basic Info..., Progress=27%
2026-01-12 08:39:33,771 - app.clients.bloomberg_client - INFO - ✅ Parsed 2 items from remote.
2026-01-12 08:39:34,217 - app.clients.bloomberg_client - INFO - ✅ Saved 2 records to database.
2026-01-12 08:39:34,218 - app.clients.bloomberg_client - INFO - Using auto-detected currency: JPY
2026-01-12 08:39:34,218 - app.clients.bloomberg_client - INFO - Fetching Currency Data...
2026-01-12 08:39:34,218 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=正在获取货币指标 (JPY)..., Progress=41%
2026-01-12 08:39:35,121 - app.clients.bloomberg_client - INFO - ✅ Parsed 331 items from remote.
2026-01-12 08:39:44,689 - app.clients.bloomberg_client - INFO - ✅ Saved 331 records to database.
2026-01-12 08:39:44,690 - app.clients.bloomberg_client - INFO - Fetching Non-Currency Data...
2026-01-12 08:39:44,691 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=正在获取非货币指标..., Progress=55%
2026-01-12 08:39:45,574 - app.clients.bloomberg_client - INFO - ✅ Parsed 382 items from remote.
2026-01-12 08:39:55,843 - app.clients.bloomberg_client - INFO - ✅ Saved 382 records to database.
2026-01-12 08:39:55,845 - app.clients.bloomberg_client - INFO - Fetching Price Data (Aligned)...
2026-01-12 08:39:55,845 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=正在获取价格指标..., Progress=69%
2026-01-12 08:39:56,105 - app.clients.bloomberg_client - INFO - Found 14 revenue reporting dates. Fetching aligned price data...
2026-01-12 08:40:02,124 - app.clients.bloomberg_client - INFO - ✅ Parsed 42 items from remote.
2026-01-12 08:40:03,440 - app.clients.bloomberg_client - INFO - ✅ Saved 42 records to database.
2026-01-12 08:40:03,441 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=Finalizing data..., Progress=82%
2026-01-12 08:40:04,197 - app.clients.bloomberg_client - INFO - ✅ Cleanup and View Refresh completed.
2026-01-12 08:40:04,197 - app.clients.bloomberg_client - INFO - ✅ Completed processing for 2503 JP Equity
2026-01-12 08:40:04,197 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=Bloomberg data sync complete, Progress=90%
2026-01-12 08:40:04,447 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=51, Msg=Bloomberg 数据同步完成, Progress=100%
2026-01-12 08:40:18,684 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=正在初始化数据获取..., Progress=0%
2026-01-12 08:40:18,940 - app.clients.bloomberg_client - INFO - Connecting to Jupyter at http://192.168.3.161:8888...
2026-01-12 08:40:19,174 - app.clients.bloomberg_client - INFO - ✅ Authentication successful.
2026-01-12 08:40:19,206 - app.clients.bloomberg_client - INFO - ✅ Found existing kernel: bc27f3b1-b028-434a-99fa-c1cad4495a87 (remote_env)
2026-01-12 08:40:19,405 - app.clients.bloomberg_client - INFO - ✅ WebSocket connected.
2026-01-12 08:40:19,406 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=数据源连接成功, Progress=10%
2026-01-12 08:40:19,653 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=正在连接 Bloomberg 终端..., Progress=20%
2026-01-12 08:40:19,900 - app.clients.bloomberg_client - INFO - 🚀 Starting fetch for: 2503 JP Equity
2026-01-12 08:40:19,901 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=Starting Bloomberg session..., Progress=20%
2026-01-12 08:40:20,212 - app.clients.bloomberg_client - INFO - Fetching Basic Data...
2026-01-12 08:40:20,212 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=Fetching Company Basic Info..., Progress=27%
2026-01-12 08:40:21,392 - app.clients.bloomberg_client - INFO - ✅ Parsed 2 items from remote.
2026-01-12 08:40:21,956 - app.clients.bloomberg_client - INFO - ✅ Saved 2 records to database.
2026-01-12 08:40:21,956 - app.clients.bloomberg_client - INFO - Using auto-detected currency: JPY
2026-01-12 08:40:21,956 - app.clients.bloomberg_client - INFO - Fetching Currency Data...
2026-01-12 08:40:21,957 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=正在获取货币指标 (JPY)..., Progress=41%
2026-01-12 08:40:22,751 - app.clients.bloomberg_client - INFO - ✅ Parsed 331 items from remote.
2026-01-12 08:40:33,054 - app.clients.bloomberg_client - INFO - ✅ Saved 331 records to database.
2026-01-12 08:40:33,056 - app.clients.bloomberg_client - INFO - Fetching Non-Currency Data...
2026-01-12 08:40:33,057 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=正在获取非货币指标..., Progress=55%
2026-01-12 08:40:34,013 - app.clients.bloomberg_client - INFO - ✅ Parsed 382 items from remote.
2026-01-12 08:40:45,644 - app.clients.bloomberg_client - INFO - ✅ Saved 382 records to database.
2026-01-12 08:40:45,647 - app.clients.bloomberg_client - INFO - Fetching Price Data (Aligned)...
2026-01-12 08:40:45,647 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=正在获取价格指标..., Progress=69%
2026-01-12 08:40:45,903 - app.clients.bloomberg_client - INFO - Found 14 revenue reporting dates. Fetching aligned price data...
2026-01-12 08:40:51,580 - app.clients.bloomberg_client - INFO - ✅ Parsed 42 items from remote.
2026-01-12 08:40:53,077 - app.clients.bloomberg_client - INFO - ✅ Saved 42 records to database.
2026-01-12 08:40:53,092 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=Finalizing data..., Progress=82%
2026-01-12 08:40:53,882 - app.clients.bloomberg_client - INFO - ✅ Cleanup and View Refresh completed.
2026-01-12 08:40:53,883 - app.clients.bloomberg_client - INFO - ✅ Completed processing for 2503 JP Equity
2026-01-12 08:40:53,883 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=Bloomberg data sync complete, Progress=90%
2026-01-12 08:40:54,126 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=52, Msg=Bloomberg 数据同步完成, Progress=100%
2026-01-12 08:43:24,711 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=正在初始化数据获取..., Progress=0%
2026-01-12 08:43:25,139 - app.clients.bloomberg_client - INFO - Connecting to Jupyter at http://192.168.3.161:8888...
2026-01-12 08:43:25,365 - app.clients.bloomberg_client - INFO - ✅ Authentication successful.
2026-01-12 08:43:25,393 - app.clients.bloomberg_client - INFO - ✅ Found existing kernel: bc27f3b1-b028-434a-99fa-c1cad4495a87 (remote_env)
2026-01-12 08:43:25,442 - app.clients.bloomberg_client - INFO - ✅ WebSocket connected.
2026-01-12 08:43:25,442 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=数据源连接成功, Progress=10%
2026-01-12 08:43:25,702 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=正在连接 Bloomberg 终端..., Progress=20%
2026-01-12 08:43:25,971 - app.clients.bloomberg_client - INFO - 🚀 Starting fetch for: 2503 JP Equity
2026-01-12 08:43:25,971 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=Starting Bloomberg session..., Progress=20%
2026-01-12 08:43:26,469 - app.clients.bloomberg_client - INFO - Fetching Basic Data...
2026-01-12 08:43:26,469 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=Fetching Company Basic Info..., Progress=27%
2026-01-12 08:43:27,652 - app.clients.bloomberg_client - INFO - ✅ Parsed 2 items from remote.
2026-01-12 08:43:28,341 - app.clients.bloomberg_client - INFO - ✅ Saved 2 records to database.
2026-01-12 08:43:28,342 - app.clients.bloomberg_client - INFO - Using forced currency: USD
2026-01-12 08:43:28,342 - app.clients.bloomberg_client - INFO - Fetching Currency Data...
2026-01-12 08:43:28,342 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=正在获取货币指标 (USD)..., Progress=41%
2026-01-12 08:43:29,720 - app.clients.bloomberg_client - INFO - ✅ Parsed 331 items from remote.
2026-01-12 08:43:39,230 - app.clients.bloomberg_client - INFO - ✅ Saved 331 records to database.
2026-01-12 08:43:39,232 - app.clients.bloomberg_client - INFO - Fetching Non-Currency Data...
2026-01-12 08:43:39,232 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=正在获取非货币指标..., Progress=55%
2026-01-12 08:43:40,229 - app.clients.bloomberg_client - INFO - ✅ Parsed 382 items from remote.
2026-01-12 08:43:50,591 - app.clients.bloomberg_client - INFO - ✅ Saved 382 records to database.
2026-01-12 08:43:50,593 - app.clients.bloomberg_client - INFO - Fetching Price Data (Aligned)...
2026-01-12 08:43:50,593 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=正在获取价格指标..., Progress=69%
2026-01-12 08:43:50,851 - app.clients.bloomberg_client - INFO - Found 14 revenue reporting dates. Fetching aligned price data...
2026-01-12 08:43:56,659 - app.clients.bloomberg_client - INFO - ✅ Parsed 42 items from remote.
2026-01-12 08:43:58,108 - app.clients.bloomberg_client - INFO - ✅ Saved 42 records to database.
2026-01-12 08:43:58,109 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=Finalizing data..., Progress=82%
2026-01-12 08:43:58,702 - app.clients.bloomberg_client - INFO - ✅ Cleanup and View Refresh completed.
2026-01-12 08:43:58,703 - app.clients.bloomberg_client - INFO - ✅ Completed processing for 2503 JP Equity
2026-01-12 08:43:58,703 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=Bloomberg data sync complete, Progress=90%
2026-01-12 08:43:58,998 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=53, Msg=Bloomberg 数据同步完成, Progress=100%
2026-01-12 09:30:47,629 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=正在初始化数据获取..., Progress=0%
2026-01-12 09:30:48,117 - app.clients.bloomberg_client - INFO - Connecting to Jupyter at http://192.168.3.161:8888...
2026-01-12 09:30:48,341 - app.clients.bloomberg_client - INFO - ✅ Authentication successful.
2026-01-12 09:30:48,532 - app.clients.bloomberg_client - INFO - ✅ Found existing kernel: bc27f3b1-b028-434a-99fa-c1cad4495a87 (remote_env)
2026-01-12 09:30:48,587 - app.clients.bloomberg_client - INFO - ✅ WebSocket connected.
2026-01-12 09:30:48,588 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=数据源连接成功, Progress=10%
2026-01-12 09:30:48,836 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=正在连接 Bloomberg 终端..., Progress=20%
2026-01-12 09:30:49,112 - app.clients.bloomberg_client - INFO - 🚀 Starting fetch for: 2503 JP Equity
2026-01-12 09:30:49,112 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=Starting Bloomberg session..., Progress=20%
2026-01-12 09:30:49,445 - app.clients.bloomberg_client - INFO - Fetching Basic Data...
2026-01-12 09:30:49,445 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=Fetching Company Basic Info..., Progress=27%
2026-01-12 09:30:50,731 - app.clients.bloomberg_client - INFO - ✅ Parsed 2 items from remote.
2026-01-12 09:30:51,108 - app.clients.bloomberg_client - INFO - ✅ Saved 2 records to database.
2026-01-12 09:30:51,109 - app.clients.bloomberg_client - INFO - Using forced currency: CNY
2026-01-12 09:30:51,109 - app.clients.bloomberg_client - INFO - Fetching Currency Data...
2026-01-12 09:30:51,109 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=正在获取货币指标 (CNY)..., Progress=41%
2026-01-12 09:30:52,131 - app.clients.bloomberg_client - INFO - ✅ Parsed 331 items from remote.
2026-01-12 09:31:03,562 - app.clients.bloomberg_client - INFO - ✅ Saved 331 records to database.
2026-01-12 09:31:03,563 - app.clients.bloomberg_client - INFO - Fetching Non-Currency Data...
2026-01-12 09:31:03,564 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=正在获取非货币指标..., Progress=55%
2026-01-12 09:31:04,637 - app.clients.bloomberg_client - INFO - ✅ Parsed 382 items from remote.
2026-01-12 09:31:16,853 - app.clients.bloomberg_client - INFO - ✅ Saved 382 records to database.
2026-01-12 09:31:16,854 - app.clients.bloomberg_client - INFO - Fetching Price Data (Aligned)...
2026-01-12 09:31:16,855 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=正在获取价格指标..., Progress=69%
2026-01-12 09:31:17,130 - app.clients.bloomberg_client - INFO - Found 14 revenue reporting dates. Fetching aligned price data...
2026-01-12 09:31:23,659 - app.clients.bloomberg_client - INFO - ✅ Parsed 42 items from remote.
2026-01-12 09:31:25,896 - app.clients.bloomberg_client - INFO - ✅ Saved 42 records to database.
2026-01-12 09:31:25,896 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=Finalizing data..., Progress=82%
2026-01-12 09:31:26,487 - app.clients.bloomberg_client - INFO - ✅ Cleanup and View Refresh completed.
2026-01-12 09:31:26,489 - app.clients.bloomberg_client - INFO - ✅ Completed processing for 2503 JP Equity
2026-01-12 09:31:26,490 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=Bloomberg data sync complete, Progress=90%
2026-01-12 09:31:26,791 - app.services.data_fetcher_service - INFO - 🔄 [进度更新] ID=54, Msg=Bloomberg 数据同步完成, Progress=100%

View File

@ -165,11 +165,47 @@ function CompanyAnalysisView({
const progress = updateStatus?.progress_percentage || 0
const progressStatus = updateStatus?.progress_message || ""
const [currency, setCurrency] = useState("Auto")
return (
<div className="space-y-6">
<HeaderPortal>
<div className="flex items-center justify-end w-full gap-4 text-sm h-full">
{/* 1. 状态显示 (进度或最后更新) */}
{/* 0. Operation Buttons (Update Data) */}
<Button
onClick={() => fetchData(true, currency)}
disabled={fetching}
variant={!status?.has_data ? "default" : "outline"}
size="sm"
className="h-8"
>
{fetching ? (
<Loader2 className="h-3.5 w-3.5 animate-spin mr-2" />
) : (
<RefreshCw className="h-3.5 w-3.5 mr-2" />
)}
{status?.has_data ? "更新数据" : "获取数据"}
</Button>
{/* 0.5 Currency Selector */}
<div className="bg-muted/30 p-1 rounded-lg border flex items-center gap-1 h-8">
{["Auto", "CNY", "USD"].map((curr) => (
<button
key={curr}
onClick={() => setCurrency(curr)}
className={`
px-2 py-0.5 text-xs font-medium rounded-md transition-all
${currency === curr
? "bg-background text-primary shadow-sm border"
: "text-muted-foreground hover:bg-muted/50"}
`}
>
{curr}
</button>
))}
</div>
{/* 1. Status Display */}
<div className="flex items-center text-muted-foreground mr-auto">
{isUpdating ? (
<div className="flex items-center gap-2 text-xs font-mono bg-muted/50 rounded-full px-3 py-1 animate-pulse">
@ -192,21 +228,6 @@ function CompanyAnalysisView({
<Badge variant="secondary" className="font-mono h-6">{company.market} {company.symbol}</Badge>
</div>
{/* 3. 操作按钮 (刷新) */}
<Button
onClick={() => fetchData(true)}
disabled={fetching}
variant={!status?.has_data ? "default" : "outline"}
size="sm"
className="h-8"
>
{fetching ? (
<Loader2 className="h-3.5 w-3.5 animate-spin mr-2" />
) : (
<RefreshCw className="h-3.5 w-3.5 mr-2" />
)}
{status?.has_data ? "更新数据" : "获取数据"}
</Button>
</div>
</HeaderPortal>
@ -218,6 +239,8 @@ function CompanyAnalysisView({
dataSource === 'Bloomberg' ? (
<BloombergView
companyId={companyId}
selectedCurrency={currency}
userMarket={company.market}
/>
) : (
<FinancialTables

View File

@ -12,9 +12,11 @@ import type { FinancialDataResponse } from "@/lib/types"
interface BloombergViewProps {
companyId: number
onBack?: () => void
selectedCurrency?: string
userMarket?: string
}
export function BloombergView({ companyId, onBack }: BloombergViewProps) {
export function BloombergView({ companyId, onBack, selectedCurrency = "Auto", userMarket }: BloombergViewProps) {
const [data, setData] = useState<FinancialDataResponse | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState("")
@ -79,12 +81,12 @@ export function BloombergView({ companyId, onBack }: BloombergViewProps) {
</Button>
</div>
<RawDataTable title="财务数据总表" data={mergedData} />
<RawDataTable title="财务数据总表" data={mergedData} selectedCurrency={selectedCurrency} userMarket={userMarket} />
</div>
)
}
function RawDataTable({ data, title }: { data: any[], title: string }) {
function RawDataTable({ data, title, selectedCurrency = "Auto", userMarket }: { data: any[], title: string, selectedCurrency?: string, userMarket?: string }) {
if (!data || data.length === 0) {
return (
<Card>
@ -98,12 +100,53 @@ function RawDataTable({ data, title }: { data: any[], title: string }) {
)
}
// ---------------------------------------------------------------------------
// 1. Filter Data Rows (Handle Multi-Currency) - DO THIS FIRST!
// ---------------------------------------------------------------------------
// Determine Target Currency Strategy
let targetCurrency = selectedCurrency
if (targetCurrency === "Auto" && userMarket) {
const market = userMarket.toUpperCase()
if (market.includes("JP")) targetCurrency = "JPY"
else if (market.includes("VN")) targetCurrency = "VND"
else if (market.includes("CN")) targetCurrency = "CNY"
else if (market.includes("HK")) targetCurrency = "HKD"
else targetCurrency = "USD"
}
const filteredRows = data.filter(row => {
// Basic Validity Check
if (!row || row.revenue === null || row.revenue === undefined) return false
// Currency Filtering
// Case 1: Specific Currency Selected (or resolved from Auto)
if (targetCurrency !== "Auto") {
if (row.currency && row.currency !== targetCurrency) {
return false
}
}
// Case 2: Pure Auto (Fallback if no userMarket)
else if (selectedCurrency === "Auto") {
if (data.length > 0 && data[0].currency && row.currency && row.currency !== data[0].currency) {
return false
}
}
return true
})
// ---------------------------------------------------------------------------
// 2. Extract Indicators (From Filtered Data ONLY)
// ---------------------------------------------------------------------------
// 提取所有指标键值 (排除元数据)
const excludeKeys = ['id', 'company_code', 'code', 'symbol', 'market', 'update_date', 'create_time', 'end_date', 'ts_code']
// 从合并后的数据中收集所有可能的指标
const allIndicators = new Set<string>()
data.forEach(row => {
filteredRows.forEach(row => {
Object.keys(row).forEach(k => {
if (!excludeKeys.includes(k)) {
allIndicators.add(k)
@ -166,19 +209,29 @@ function RawDataTable({ data, title }: { data: any[], title: string }) {
return a.localeCompare(b)
})
// ---------------------------------------------------------------------------
// 1. Filter Data Rows (Handle Multi-Currency)
// ---------------------------------------------------------------------------
// We must filter BEFORE creating the Map, because multiple rows might exist
// for the same date (e.g. 2023 JPY and 2023 USD).
// The Map key is 'end_date', so it can only hold one currency's data per date.
// ---------------------------------------------------------------------------
// 2. Build Data Structures from Filtered Rows
// ---------------------------------------------------------------------------
// 构建查找表: Map<DateString, RowData>
const dataMap = new Map()
data.forEach(row => {
filteredRows.forEach(row => {
dataMap.set(row.end_date, row)
})
// 过滤日期: 只保留有营业收入 (revenue) 的列
const dates = Array.from(new Set(data.map(row => row.end_date)))
.filter(date => {
const row = dataMap.get(date)
// Check if revenue exists and is not null/undefined
return row && row.revenue !== null && row.revenue !== undefined
})
// 提取日期列表
const dates = Array.from(new Set(filteredRows.map(row => row.end_date)))
.sort((a, b) => new Date(b).getTime() - new Date(a).getTime())

View File

@ -58,7 +58,7 @@ export function useFinancialData(company: SearchResult | null, dataSource: strin
}, [updateId, fetching, checkStatus])
// Trigger data fetch
const fetchData = async (forceRefresh = false) => {
const fetchData = async (forceRefresh = false, currency?: string) => {
if (!company) return
setFetching(true)
setError("")
@ -68,7 +68,8 @@ export function useFinancialData(company: SearchResult | null, dataSource: strin
symbol: company.symbol,
company_name: company.company_name,
data_source: dataSource,
force_refresh: forceRefresh
force_refresh: forceRefresh,
currency: currency
})
setUpdateId(response.update_id)
} catch (err: any) {

View File

@ -58,6 +58,7 @@ export interface FetchDataRequest {
company_name: string
data_source: string
force_refresh?: boolean
currency?: string
}
export interface FetchDataResponse {