aishare-txt 2025.11.6.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,593 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 股票数据获取模块
5
+ 负责从各种数据源获取股票相关数据
6
+ """
7
+
8
+ import akshare as ak
9
+ import pandas as pd
10
+ import numpy as np
11
+ import pandas_market_calendars as mcal
12
+ from typing import Optional, Union, cast
13
+ from datetime import datetime, timedelta, time
14
+ from .config import IndicatorConfig as Config
15
+ from ..utils.utils import LoggerManager
16
+ import warnings
17
+ warnings.filterwarnings('ignore')
18
+
19
+
20
+ class StockDataFetcher:
21
+ """股票数据获取器"""
22
+
23
+ def __init__(self):
24
+ self.config = Config()
25
+ self.logger = LoggerManager.get_logger('data_fetcher')
26
+ # 获取上交所(SSE)日历
27
+ self.sse_calendar = mcal.get_calendar('SSE')
28
+
29
+ def _is_trading_day_and_not_closed(self) -> bool:
30
+ """
31
+ 判断今天是否是交易日且未收盘
32
+
33
+ Returns:
34
+ bool: True表示今天是交易日且未收盘,False表示非交易日或已收盘
35
+ """
36
+ try:
37
+ now = datetime.now()
38
+ today = now.date()
39
+ current_time = now.time()
40
+
41
+ # 首先使用 pandas_market_calendars 判断是否为交易日
42
+ is_trading_day = self._is_trading_day(today)
43
+
44
+ if not is_trading_day:
45
+ self.logger.debug(f"今天 {today} 不是交易日")
46
+ return False
47
+
48
+ # 收盘时间:15:00
49
+ market_close = time(15, 0)
50
+
51
+ # 判断今天是否已收盘
52
+ is_market_closed = current_time >= market_close
53
+
54
+ self.logger.debug(f"当前时间: {now}")
55
+ self.logger.debug(f"是否为交易日: {is_trading_day}")
56
+ self.logger.debug(f"市场是否已收盘: {is_market_closed}")
57
+
58
+ # 如果是交易日且未收盘,返回True
59
+ return not is_market_closed
60
+
61
+ except Exception as e:
62
+ self.logger.warning(f"判断交易时间时出错:{str(e)}")
63
+ # 如果无法判断,返回False(保守处理)
64
+ return False
65
+
66
+ def _is_trading_day(self, date_to_check) -> bool:
67
+ """
68
+ 判断指定日期是否为交易日
69
+
70
+ Args:
71
+ date_to_check: 要检查的日期
72
+
73
+ Returns:
74
+ bool: True表示是交易日,False表示非交易日
75
+ """
76
+ try:
77
+ # 获取日期前后的交易日历(扩大范围以确保能获取到数据)
78
+ start_date = date_to_check - timedelta(days=7) # 往前7天
79
+ end_date = date_to_check + timedelta(days=7) # 往后7天
80
+
81
+ schedule = self.sse_calendar.schedule(start_date=start_date, end_date=end_date)
82
+
83
+ if schedule.empty:
84
+ self.logger.debug(f"无法获取 {start_date} 到 {end_date} 的交易日历")
85
+ return self._fallback_trading_day_check(date_to_check)
86
+
87
+ # 检查指定日期是否在交易日历中
88
+ trading_days = schedule.index.date
89
+ is_trading = date_to_check in trading_days
90
+
91
+ self.logger.debug(f"使用日历检查 {date_to_check}: {'是交易日' if is_trading else '非交易日'}")
92
+ return is_trading
93
+
94
+ except Exception as e:
95
+ self.logger.warning(f"使用pandas_market_calendars判断交易日失败:{str(e)}")
96
+ # 如果 pandas_market_calendars 失败,回退到简单的周一到周五判断
97
+ return self._fallback_trading_day_check(date_to_check)
98
+
99
+ def _fallback_trading_day_check(self, date_to_check) -> bool:
100
+ """
101
+ 备用的交易日判断方法(简单的周一到周五判断)
102
+
103
+ Args:
104
+ date_to_check: 要检查的日期
105
+
106
+ Returns:
107
+ bool: True表示是交易日,False表示非交易日
108
+ """
109
+ weekday = date_to_check.weekday()
110
+ is_trading = weekday < 5 # 0-4 是周一到周五
111
+ self.logger.debug(f"使用备用方法检查 {date_to_check}: {'是交易日' if is_trading else '非交易日'}")
112
+ return is_trading
113
+
114
+ def _remove_incomplete_trading_data(self, data: pd.DataFrame) -> pd.DataFrame:
115
+ """
116
+ 如果今天是交易日且未收盘,移除最后一个不完整的交易日数据
117
+
118
+ Args:
119
+ data: 股票数据DataFrame
120
+
121
+ Returns:
122
+ 处理后的DataFrame
123
+ """
124
+ if self._is_trading_day_and_not_closed():
125
+ self.logger.info("检测到当前为交易日且未收盘,移除最新不完整数据")
126
+ # 移除最后一行数据
127
+ if len(data) > 0:
128
+ data = data.iloc[:-1].copy()
129
+ self.logger.info(f"已移除最新数据,剩余 {len(data)} 条记录")
130
+ else:
131
+ self.logger.debug("当前为非交易日或已收盘,保留所有数据")
132
+
133
+ return data
134
+
135
+ def _add_stock_prefix(self, stock_code: str) -> str:
136
+ """
137
+ 为股票代码添加市场前缀
138
+
139
+ Args:
140
+ stock_code (str): 6位股票代码,如 '000001'
141
+
142
+ Returns:
143
+ str: 带前缀的股票代码,如 'sz000001' 或 'sh600000'
144
+ """
145
+ if stock_code.startswith('6'):
146
+ return f'sh{stock_code}'
147
+ else:
148
+ return f'sz{stock_code}'
149
+
150
+ def _process_stock_data(self, data: pd.DataFrame, stock_code: str) -> Optional[pd.DataFrame]:
151
+ """
152
+ 处理和清洗股票数据
153
+
154
+ Args:
155
+ data: 原始股票数据
156
+ stock_code: 股票代码
157
+
158
+ Returns:
159
+ 处理后的数据或None
160
+ """
161
+ try:
162
+ if data is None or len(data) == 0:
163
+ self.logger.error(f"{self.config.ERROR_MESSAGES['no_data']}: {stock_code}")
164
+ return None
165
+
166
+ self.logger.debug(f"获取到数据,形状: {data.shape}")
167
+
168
+ # 标准化列名
169
+ data = data.rename(columns=self.config.COLUMN_MAPPING)
170
+
171
+ # 检查必要的列是否存在
172
+ required_columns = self.config.DATA_CONFIG['required_columns']
173
+ missing_columns = [col for col in required_columns if col not in data.columns]
174
+
175
+ if missing_columns:
176
+ self.logger.error(f"数据缺少必要列 {missing_columns}")
177
+ return None
178
+
179
+ # 确保数据类型正确
180
+ for col in ['open', 'close', 'high', 'low', 'volume']:
181
+ data[col] = pd.to_numeric(data[col], errors='coerce')
182
+
183
+ # 删除包含NaN的行
184
+ data = data.dropna(subset=['open', 'close', 'high', 'low', 'volume'])
185
+
186
+ # 数据清洗:处理异常价格数据
187
+ original_len = len(data)
188
+
189
+ # 删除价格为负数的行
190
+ price_cols = ['open', 'close', 'high', 'low']
191
+ for col in price_cols:
192
+ mask = data[col] >= 0
193
+ data = cast(pd.DataFrame, data[mask].copy())
194
+
195
+ # 删除成交量为负数的行
196
+ volume_mask = data['volume'] >= 0
197
+ data = cast(pd.DataFrame, data[volume_mask].copy())
198
+
199
+ # 删除价格逻辑错误的行(最高价小于最低价等)
200
+ high_low_mask = data['high'] >= data['low']
201
+ data = cast(pd.DataFrame, data[high_low_mask].copy())
202
+
203
+ close_range_mask = (data['close'] >= data['low']) & (data['close'] <= data['high'])
204
+ data = cast(pd.DataFrame, data[close_range_mask].copy())
205
+
206
+ open_range_mask = (data['open'] >= data['low']) & (data['open'] <= data['high'])
207
+ data = cast(pd.DataFrame, data[open_range_mask].copy())
208
+
209
+ cleaned_count = original_len - len(data)
210
+ if cleaned_count > 0:
211
+ self.logger.info(f"数据清洗:移除了 {cleaned_count} 条异常数据")
212
+
213
+ if len(data) == 0:
214
+ self.logger.error("数据清洗后为空")
215
+ return None
216
+
217
+ # 按日期排序
218
+ if not isinstance(data, pd.DataFrame):
219
+ self.logger.error("数据类型错误:期望DataFrame类型")
220
+ return None
221
+ data = data.sort_values('date').reset_index(drop=True)
222
+
223
+ # 检查并处理未收盘的不完整数据
224
+ original_length = len(data)
225
+ data = self._remove_incomplete_trading_data(data)
226
+
227
+ if len(data) == 0:
228
+ self.logger.error("处理后数据为空")
229
+ return None
230
+
231
+ self.logger.info(f"✓ 成功处理股票 {stock_code} 的数据,共 {len(data)} 条记录")
232
+ if original_length != len(data):
233
+ self.logger.info(f"已移除 {original_length - len(data)} 条不完整交易数据")
234
+
235
+ self.logger.debug(f"数据日期范围: {data['date'].iloc[0]} 到 {data['date'].iloc[-1]}")
236
+ self.logger.info(f"最新收盘价: {data['close'].iloc[-1]:.{self.config.DISPLAY_PRECISION['price']}f}")
237
+
238
+ return data
239
+
240
+ except Exception as e:
241
+ self.logger.error(f"处理股票数据时出错:{str(e)}")
242
+ return None
243
+
244
+ def _fetch_from_default_api(self, stock_code: str, period: str, start_date: str, adjust: str) -> Optional[pd.DataFrame]:
245
+ """
246
+ 从默认API获取数据(东方财富)
247
+
248
+ Args:
249
+ stock_code: 股票代码
250
+ period: 周期
251
+ start_date: 开始日期
252
+ adjust: 复权类型
253
+
254
+ Returns:
255
+ 原始数据或None
256
+ """
257
+ try:
258
+ self.logger.info(f"[方法1] 尝试从东方财富获取股票 {stock_code} 的数据(从 {start_date} 开始)...")
259
+ raw_data = ak.stock_zh_a_hist(
260
+ symbol=stock_code,
261
+ period=cast(str, period),
262
+ start_date=start_date,
263
+ adjust=cast(str, adjust)
264
+ )
265
+ return cast(pd.DataFrame, raw_data)
266
+ except Exception as e:
267
+ self.logger.warning(f"[方法1] 东方财富API获取失败:{str(e)}")
268
+ return None
269
+
270
+ def _fetch_from_sina_api(self, stock_code: str, start_date: str, adjust: str) -> Optional[pd.DataFrame]:
271
+ """
272
+ 从新浪API获取数据(备用方案1)
273
+
274
+ Args:
275
+ stock_code: 股票代码
276
+ start_date: 开始日期
277
+ adjust: 复权类型
278
+
279
+ Returns:
280
+ 原始数据或None
281
+ """
282
+ try:
283
+ stock_code_with_prefix = self._add_stock_prefix(stock_code)
284
+ end_date = datetime.now().strftime('%Y%m%d')
285
+
286
+ self.logger.info(f"[方法2] 尝试从新浪获取股票 {stock_code_with_prefix} 的数据...")
287
+ raw_data = ak.stock_zh_a_daily(
288
+ symbol=stock_code_with_prefix,
289
+ start_date=start_date,
290
+ end_date=end_date,
291
+ adjust=adjust
292
+ )
293
+ return cast(pd.DataFrame, raw_data)
294
+ except Exception as e:
295
+ self.logger.warning(f"[方法2] 新浪API获取失败:{str(e)}")
296
+ return None
297
+
298
+ def _fetch_from_tencent_api(self, stock_code: str, start_date: str, adjust: str) -> Optional[pd.DataFrame]:
299
+ """
300
+ 从腾讯API获取数据(备用方案2)
301
+
302
+ Args:
303
+ stock_code: 股票代码
304
+ start_date: 开始日期
305
+ adjust: 复权类型
306
+
307
+ Returns:
308
+ 原始数据或None
309
+ """
310
+ try:
311
+ stock_code_with_prefix = self._add_stock_prefix(stock_code)
312
+ end_date = datetime.now().strftime('%Y%m%d')
313
+
314
+ self.logger.info(f"[方法3] 尝试从腾讯获取股票 {stock_code_with_prefix} 的数据...")
315
+ raw_data = ak.stock_zh_a_hist_tx(
316
+ symbol=stock_code_with_prefix,
317
+ start_date=start_date,
318
+ end_date=end_date,
319
+ adjust=adjust
320
+ )
321
+ return cast(pd.DataFrame, raw_data)
322
+ except Exception as e:
323
+ self.logger.warning(f"[方法3] 腾讯API获取失败:{str(e)}")
324
+ return None
325
+
326
+ def fetch_stock_data(self, stock_code: str, period: Optional[str] = None, adjust: Optional[str] = None, start_date: Optional[str] = None) -> Optional[pd.DataFrame]:
327
+ """
328
+ 获取股票历史价格数据(支持多数据源备用方案)
329
+
330
+ Args:
331
+ stock_code (str): 股票代码,如 '000001'
332
+ period (str): 周期,默认为 'daily'
333
+ adjust (str): 复权类型,默认为 'qfq'
334
+ start_date (str): 开始日期,如 '20230101',默认为当前时间往前推4个月
335
+
336
+ Returns:
337
+ pd.DataFrame or None: 股票数据DataFrame,失败返回None
338
+ """
339
+ if period is None:
340
+ period = self.config.DATA_CONFIG['default_period']
341
+ if adjust is None:
342
+ adjust = self.config.DATA_CONFIG['default_adjust']
343
+
344
+ # 如果没有指定开始日期,默认为当前时间往前推配置的月数
345
+ if start_date is None:
346
+ months_back = self.config.DATA_CONFIG.get('default_months_back', 4)
347
+ default_start = datetime.now() - timedelta(days=months_back * 30) # 每月按30天计算
348
+ start_date = default_start.strftime('%Y%m%d')
349
+
350
+ # 尝试多个数据源
351
+ raw_data = None
352
+
353
+ # 方法1:东方财富(默认)
354
+ raw_data = self._fetch_from_default_api(stock_code, cast(str, period), start_date, cast(str, adjust))
355
+
356
+ # 方法2:新浪(备用1)
357
+ if raw_data is None or len(raw_data) == 0:
358
+ self.logger.info("尝试使用备用方案1:新浪数据源")
359
+ raw_data = self._fetch_from_sina_api(stock_code, start_date, cast(str, adjust))
360
+
361
+ # 方法3:腾讯(备用2)
362
+ if raw_data is None or len(raw_data) == 0:
363
+ self.logger.info("尝试使用备用方案2:腾讯数据源")
364
+ raw_data = self._fetch_from_tencent_api(stock_code, start_date, cast(str, adjust))
365
+
366
+ # 如果所有方法都失败
367
+ if raw_data is None or len(raw_data) == 0:
368
+ self.logger.error(f"所有数据源均获取失败:{stock_code}")
369
+ self.logger.info("可能的原因:")
370
+ self.logger.info("1. 股票代码格式错误(请确保是6位数字,如 000001)")
371
+ self.logger.info("2. 网络连接问题")
372
+ self.logger.info("3. 所有数据源API暂时不可用")
373
+ self.logger.info("4. 股票代码不存在或已退市")
374
+ return None
375
+
376
+
377
+
378
+ # 处理数据
379
+ return self._process_stock_data(raw_data, stock_code)
380
+
381
+ def get_fund_flow_data(self, stock_code):
382
+ """
383
+ 获取主力资金流数据
384
+
385
+ Args:
386
+ stock_code (str): 股票代码
387
+
388
+ Returns:
389
+ dict: 资金流数据字典
390
+
391
+ Note:
392
+ akshare 的 stock_individual_fund_flow API 已经处理了交易日不完整数据的问题,
393
+ 不需要额外移除当日数据
394
+ """
395
+ try:
396
+ self.logger.info("正在获取主力资金流数据...")
397
+
398
+ fund_flow_data = {}
399
+
400
+ # 获取个股资金流数据
401
+ try:
402
+ # 根据股票代码判断市场
403
+ market = self._determine_market(stock_code)
404
+ fund_df = ak.stock_individual_fund_flow(stock=stock_code, market=market)
405
+
406
+ if fund_df is not None and len(fund_df) > 0:
407
+ # 获取最新一天的数据
408
+ latest_row = fund_df.iloc[-1]
409
+
410
+ # 解析资金流数据
411
+ fund_flow_data = self._parse_fund_flow_data(latest_row)
412
+
413
+ # 计算5日累计数据
414
+ if len(fund_df) >= 5:
415
+ fund_flow_data.update(self._calculate_5day_fund_flow(fund_df))
416
+
417
+ self.logger.info("✓ 主力资金流数据获取成功")
418
+ self.logger.debug(f"资金流数据范围: {fund_df.iloc[0].get('日期', 'N/A')} 到 {fund_df.iloc[-1].get('日期', 'N/A')}")
419
+
420
+ except Exception as e:
421
+ self.logger.warning(f"获取个股资金流数据失败:{str(e)}")
422
+
423
+ # 数据清洗和格式化
424
+ fund_flow_data = self._clean_fund_flow_data(fund_flow_data)
425
+
426
+ return fund_flow_data
427
+
428
+ except Exception as e:
429
+ self.logger.error(f"获取主力资金流数据失败:{str(e)}")
430
+ return {}
431
+
432
+ def get_stock_basic_info(self, stock_code):
433
+ """
434
+ 获取股票基本信息
435
+
436
+ Args:
437
+ stock_code (str): 股票代码
438
+
439
+ Returns:
440
+ dict: 股票基本信息字典
441
+ """
442
+ try:
443
+ self.logger.info("正在获取股票基本信息...")
444
+
445
+ # 获取个股信息
446
+ df = ak.stock_individual_info_em(symbol=stock_code)
447
+
448
+ # 创建字段映射
449
+ info_dict = {}
450
+ for i, row in df.iterrows():
451
+ key = str(row.iloc[0]).strip()
452
+ value = str(row.iloc[1]).strip()
453
+ info_dict[key] = value
454
+
455
+ # 解析基本信息
456
+ stock_info = self._parse_basic_info(info_dict, stock_code)
457
+
458
+ # 格式化数值
459
+ stock_info = self._format_market_values(stock_info)
460
+
461
+ self.logger.info("✓ 股票基本信息获取成功")
462
+ return stock_info
463
+
464
+ except Exception as e:
465
+ self.logger.warning(f"获取股票基本信息失败:{str(e)}")
466
+ # 返回基础信息
467
+ return self._get_default_basic_info(stock_code)
468
+
469
+ def _determine_market(self, stock_code):
470
+ """根据股票代码判断市场"""
471
+ first_digit = stock_code[0]
472
+ return self.config.MARKET_MAPPING.get(first_digit, 'sz')
473
+
474
+ def _parse_fund_flow_data(self, latest_row):
475
+ """解析资金流数据"""
476
+ # 处理日期字段,确保为字符串格式
477
+ date_value = latest_row.get('日期', '')
478
+ if hasattr(date_value, 'strftime'):
479
+ date_str = date_value.strftime('%Y-%m-%d')
480
+ else:
481
+ date_str = str(date_value)
482
+
483
+ return {
484
+ '日期': date_str,
485
+ '主力净流入额': latest_row.get('主力净流入-净额', 0),
486
+ '主力净流入占比': latest_row.get('主力净流入-净占比', 0),
487
+ '超大单净流入额': latest_row.get('超大单净流入-净额', 0),
488
+ '超大单净流入占比': latest_row.get('超大单净流入-净占比', 0),
489
+ '大单净流入额': latest_row.get('大单净流入-净额', 0),
490
+ '大单净流入占比': latest_row.get('大单净流入-净占比', 0),
491
+ '中单净流入额': latest_row.get('中单净流入-净额', 0),
492
+ '中单净流入占比': latest_row.get('中单净流入-净占比', 0),
493
+ '小单净流入额': latest_row.get('小单净流入-净额', 0),
494
+ '小单净流入占比': latest_row.get('小单净流入-净占比', 0),
495
+ '收盘价': latest_row.get('收盘价', 0),
496
+ '涨跌幅': latest_row.get('涨跌幅', 0)
497
+ }
498
+
499
+ def _calculate_5day_fund_flow(self, fund_df):
500
+ """计算5日累计资金流"""
501
+ recent_5_days = fund_df.tail(5)
502
+
503
+ result = {
504
+ '5日主力净流入额': recent_5_days['主力净流入-净额'].sum() if '主力净流入-净额' in recent_5_days.columns else 0,
505
+ '5日超大单净流入额': recent_5_days['超大单净流入-净额'].sum() if '超大单净流入-净额' in recent_5_days.columns else 0,
506
+ '5日大单净流入额': recent_5_days['大单净流入-净额'].sum() if '大单净流入-净额' in recent_5_days.columns else 0,
507
+ '5日中单净流入额': recent_5_days['中单净流入-净额'].sum() if '中单净流入-净额' in recent_5_days.columns else 0,
508
+ '5日小单净流入额': recent_5_days['小单净流入-净额'].sum() if '小单净流入-净额' in recent_5_days.columns else 0,
509
+ }
510
+
511
+ # 计算5日平均占比
512
+ for col_name, avg_key in [
513
+ ('主力净流入-净占比', '5日主力净流入占比'),
514
+ ('超大单净流入-净占比', '5日超大单净流入占比'),
515
+ ('大单净流入-净占比', '5日大单净流入占比'),
516
+ ('中单净流入-净占比', '5日中单净流入占比'),
517
+ ('小单净流入-净占比', '5日小单净流入占比')
518
+ ]:
519
+ if col_name in recent_5_days.columns:
520
+ result[avg_key] = recent_5_days[col_name].mean()
521
+
522
+ return result
523
+
524
+ def _clean_fund_flow_data(self, fund_flow_data):
525
+ """清洗和格式化资金流数据"""
526
+ for key, value in fund_flow_data.items():
527
+ if key == '日期': # 日期字段保持字符串
528
+ continue
529
+ try:
530
+ if isinstance(value, str):
531
+ # 去除百分号并转换为数值
532
+ if '%' in str(value):
533
+ fund_flow_data[key] = float(str(value).replace('%', ''))
534
+ else:
535
+ fund_flow_data[key] = float(value) if value != '-' else 0.0
536
+ else:
537
+ fund_flow_data[key] = float(value) if value is not None else 0.0
538
+ except (ValueError, TypeError):
539
+ fund_flow_data[key] = 0.0
540
+
541
+ return fund_flow_data
542
+
543
+ def _parse_basic_info(self, info_dict, stock_code):
544
+ """解析基本信息"""
545
+ return {
546
+ '股票代码': info_dict.get('代码', info_dict.get('股票代码', stock_code)),
547
+ '股票简称': info_dict.get('简称', info_dict.get('股票简称', '未知')),
548
+ '行业': info_dict.get('行业', info_dict.get('所属行业', '未知')),
549
+ '流通股本': self._safe_float_conversion(info_dict.get('流通股本', 0)),
550
+ '流通市值': self._safe_float_conversion(info_dict.get('流通市值', 0)),
551
+ '总股本': self._safe_float_conversion(info_dict.get('总股本', 0)),
552
+ '总市值': self._safe_float_conversion(info_dict.get('总市值', 0)),
553
+ '市盈率': info_dict.get('市盈率-动态', info_dict.get('市盈率', '未知')),
554
+ '市净率': info_dict.get('市净率', '未知')
555
+ }
556
+
557
+ def _safe_float_conversion(self, value):
558
+ """安全的浮点数转换"""
559
+ try:
560
+ return float(value)
561
+ except (ValueError, TypeError):
562
+ return 0.0
563
+
564
+ def _format_market_values(self, stock_info):
565
+ """格式化市值数据(转换为亿为单位)"""
566
+ # 格式化数值(亿为单位)
567
+ for field, unit_field in [
568
+ ('流通市值', '流通市值_亿'),
569
+ ('总市值', '总市值_亿'),
570
+ ('流通股本', '流通股本_亿股'),
571
+ ('总股本', '总股本_亿股')
572
+ ]:
573
+ value = stock_info.get(field, 0)
574
+ if value > 0:
575
+ stock_info[unit_field] = value / 100000000
576
+ else:
577
+ stock_info[unit_field] = 0
578
+
579
+ return stock_info
580
+
581
+ def _get_default_basic_info(self, stock_code):
582
+ """获取默认基本信息"""
583
+ return {
584
+ '股票代码': stock_code,
585
+ '股票简称': "未知",
586
+ '行业': "未知",
587
+ '流通市值_亿': 0,
588
+ '总市值_亿': 0,
589
+ '流通股本_亿股': 0,
590
+ '总股本_亿股': 0,
591
+ '市盈率': "未知",
592
+ '市净率': "未知"
593
+ }