From e786e885e6a5b48e9c4df686e1f938454454d412 Mon Sep 17 00:00:00 2001 From: xucheng Date: Mon, 12 Jan 2026 09:33:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=E5=A4=9A=E8=B4=A7?= =?UTF-8?q?=E5=B8=81=E7=9A=84=E8=8E=B7=E5=8F=96=E5=92=8C=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/data_routes.py | 9 +- backend/app/clients/bloomberg_client.py | 19 ++- backend/app/fetchers/bloomberg_fetcher.py | 20 +-- backend/app/schemas.py | 1 + backend/app/services/bloomberg_service.py | 12 +- backend/app/services/data_fetcher_service.py | 8 +- backend/server.log | 160 +++++++++++++++++++ frontend/src/app/page.tsx | 55 +++++-- frontend/src/components/bloomberg-view.tsx | 77 +++++++-- frontend/src/hooks/use-financial-data.ts | 5 +- frontend/src/lib/types.ts | 1 + 11 files changed, 308 insertions(+), 59 deletions(-) diff --git a/backend/app/api/data_routes.py b/backend/app/api/data_routes.py index a51ba40..7bacfeb 100644 --- a/backend/app/api/data_routes.py +++ b/backend/app/api/data_routes.py @@ -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同步连接 diff --git a/backend/app/clients/bloomberg_client.py b/backend/app/clients/bloomberg_client.py index cf57af5..ef067fe 100644 --- a/backend/app/clients/bloomberg_client.py +++ b/backend/app/clients/bloomberg_client.py @@ -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 - 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}") + 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 auto-detected currency: {currency}") # 2. Fetch Currency Data logger.info("Fetching Currency Data...") diff --git a/backend/app/fetchers/bloomberg_fetcher.py b/backend/app/fetchers/bloomberg_fetcher.py index a4c8476..a67232b 100644 --- a/backend/app/fetchers/bloomberg_fetcher.py +++ b/backend/app/fetchers/bloomberg_fetcher.py @@ -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: """兼容性空方法""" diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 4215531..bd2d5d7 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -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 diff --git a/backend/app/services/bloomberg_service.py b/backend/app/services/bloomberg_service.py index cf0642e..2b4b55d 100644 --- a/backend/app/services/bloomberg_service.py +++ b/backend/app/services/bloomberg_service.py @@ -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()) diff --git a/backend/app/services/data_fetcher_service.py b/backend/app/services/data_fetcher_service.py index 959814c..2e8c32d 100644 --- a/backend/app/services/data_fetcher_service.py +++ b/backend/app/services/data_fetcher_service.py @@ -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,7 +259,10 @@ def fetch_financial_data_sync( import inspect sig = inspect.signature(fetcher.sync_all_data) if 'progress_callback' in sig.parameters: - fetcher.sync_all_data(formatted_symbol, progress_callback=progress_callback) + 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) else: diff --git a/backend/server.log b/backend/server.log index 6861c19..ee210b9 100644 --- a/backend/server.log +++ b/backend/server.log @@ -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% diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 660d6df..2d56423 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -165,11 +165,47 @@ function CompanyAnalysisView({ const progress = updateStatus?.progress_percentage || 0 const progressStatus = updateStatus?.progress_message || "" + const [currency, setCurrency] = useState("Auto") + return (
- {/* 1. 状态显示 (进度或最后更新) */} + {/* 0. Operation Buttons (Update Data) */} + + + {/* 0.5 Currency Selector */} +
+ {["Auto", "CNY", "USD"].map((curr) => ( + + ))} +
+ + {/* 1. Status Display */}
{isUpdating ? (
@@ -192,21 +228,6 @@ function CompanyAnalysisView({ {company.market} {company.symbol}
- {/* 3. 操作按钮 (刷新) */} -
@@ -218,6 +239,8 @@ function CompanyAnalysisView({ dataSource === 'Bloomberg' ? ( ) : ( void + selectedCurrency?: string + userMarket?: string } -export function BloombergView({ companyId, onBack }: BloombergViewProps) { +export function BloombergView({ companyId, onBack, selectedCurrency = "Auto", userMarket }: BloombergViewProps) { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState("") @@ -79,12 +81,12 @@ export function BloombergView({ companyId, onBack }: BloombergViewProps) {
- +
) } -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 ( @@ -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() - 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 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()) diff --git a/frontend/src/hooks/use-financial-data.ts b/frontend/src/hooks/use-financial-data.ts index cca0c0a..1f772c0 100644 --- a/frontend/src/hooks/use-financial-data.ts +++ b/frontend/src/hooks/use-financial-data.ts @@ -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) { diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 963d783..c37311e 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -58,6 +58,7 @@ export interface FetchDataRequest { company_name: string data_source: string force_refresh?: boolean + currency?: string } export interface FetchDataResponse {