alpha-dc-common 0.1.23__tar.gz

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.
Files changed (39) hide show
  1. alpha_dc_common-0.1.23/.gitignore +90 -0
  2. alpha_dc_common-0.1.23/PKG-INFO +6 -0
  3. alpha_dc_common-0.1.23/pyproject.toml +20 -0
  4. alpha_dc_common-0.1.23/pyproject.toml.backup +20 -0
  5. alpha_dc_common-0.1.23/src/dc_common/__init__.py +15 -0
  6. alpha_dc_common-0.1.23/src/dc_common/core/logger.py +48 -0
  7. alpha_dc_common-0.1.23/src/dc_common/data_sources/__init__.py +16 -0
  8. alpha_dc_common-0.1.23/src/dc_common/data_sources/base_source.py +176 -0
  9. alpha_dc_common-0.1.23/src/dc_common/data_sources/daily_quote_source.py +195 -0
  10. alpha_dc_common-0.1.23/src/dc_common/schemas/__init__.py +46 -0
  11. alpha_dc_common-0.1.23/src/dc_common/schemas/a_stock.py +24 -0
  12. alpha_dc_common-0.1.23/src/dc_common/schemas/base.py +45 -0
  13. alpha_dc_common-0.1.23/src/dc_common/schemas/hk_announcement.py +31 -0
  14. alpha_dc_common-0.1.23/src/dc_common/schemas/hk_company_profile.py +51 -0
  15. alpha_dc_common-0.1.23/src/dc_common/schemas/hk_stock.py +25 -0
  16. alpha_dc_common-0.1.23/src/dc_common/schemas/hs_industry.py +26 -0
  17. alpha_dc_common-0.1.23/src/dc_common/schemas/hs_industry_company.py +27 -0
  18. alpha_dc_common-0.1.23/src/dc_common/schemas/index_basic.py +26 -0
  19. alpha_dc_common-0.1.23/src/dc_common/schemas/index_company.py +19 -0
  20. alpha_dc_common-0.1.23/src/dc_common/schemas/index_daily.py +30 -0
  21. alpha_dc_common-0.1.23/src/dc_common/schemas/margin_account.py +29 -0
  22. alpha_dc_common-0.1.23/src/dc_common/schemas/margin_analysis.py +40 -0
  23. alpha_dc_common-0.1.23/src/dc_common/schemas/margin_detail.py +29 -0
  24. alpha_dc_common-0.1.23/src/dc_common/schemas/results.py +50 -0
  25. alpha_dc_common-0.1.23/src/dc_common/schemas/sw_index_daily.py +34 -0
  26. alpha_dc_common-0.1.23/src/dc_common/schemas/sw_industry.py +54 -0
  27. alpha_dc_common-0.1.23/src/dc_common/schemas/sw_industry_company.py +28 -0
  28. alpha_dc_common-0.1.23/src/dc_common/utils/auth_token.py +54 -0
  29. alpha_dc_common-0.1.23/src/dc_common/utils/baidu_utils.py +122 -0
  30. alpha_dc_common-0.1.23/src/dc_common/utils/cache_utils.py +141 -0
  31. alpha_dc_common-0.1.23/src/dc_common/utils/config_reader.py +79 -0
  32. alpha_dc_common-0.1.23/src/dc_common/utils/eastmoney_utils.py +118 -0
  33. alpha_dc_common-0.1.23/src/dc_common/utils/jin10_utils.py +153 -0
  34. alpha_dc_common-0.1.23/src/dc_common/utils/logging_config.py +104 -0
  35. alpha_dc_common-0.1.23/src/dc_common/utils/markdown_convert.py +121 -0
  36. alpha_dc_common-0.1.23/src/dc_common/utils/money_format.py +21 -0
  37. alpha_dc_common-0.1.23/src/dc_common/utils/proxy_utils.py +253 -0
  38. alpha_dc_common-0.1.23/src/dc_common/utils/schema_extract.py +179 -0
  39. alpha_dc_common-0.1.23/src/dc_common/utils/xueqiu_utils.py +253 -0
@@ -0,0 +1,90 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *.cover
48
+ *.py,cover
49
+ .hypothesis/
50
+ .pytest_cache/
51
+
52
+ # Jupyter Notebook
53
+ .ipynb_checkpoints
54
+
55
+ # IPython
56
+ profile_default/
57
+ ipython_config.py
58
+
59
+ # pyenv
60
+ .python-version
61
+
62
+ # PEP 582; __pypackages__
63
+ __pypackages__/
64
+
65
+ # Environments
66
+ .env
67
+ .venv
68
+ env/
69
+ venv/
70
+ ENV/
71
+ env.bak/
72
+ venv.bak/
73
+
74
+ # mypy
75
+ .mypy_cache/
76
+ .dmypy.json
77
+ dmypy.json
78
+
79
+ # uv lock file
80
+ uv.lock
81
+
82
+ # Log files
83
+ logs/
84
+ *.log
85
+
86
+ # Data directories (runtime generated data)
87
+ data/
88
+
89
+ # Project specific files
90
+ services/worker_service/data_fetchers/StockClassifyUse_stock.xls
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: alpha-dc-common
3
+ Version: 0.1.23
4
+ Summary: Shared schemas and utils for the datacenter project.
5
+ Author-email: Hang GuangLiang <hanguangliang@alphaaidig.com>
6
+ Requires-Dist: pydantic>=2.0
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "alpha-dc-common"
7
+ version = "0.1.23"
8
+ description = "Shared schemas and utils for the datacenter project."
9
+ authors = [
10
+ {name = "Hang GuangLiang", email = "hanguangliang@alphaaidig.com"},
11
+ ]
12
+ dependencies = [
13
+ "pydantic>=2.0",
14
+ ]
15
+
16
+ [tool.hatch.build.targets.wheel]
17
+ packages = ["src/dc_common"]
18
+
19
+ [tool.uv]
20
+ index-url = "https://pypi.tuna.tsinghua.edu.cn/simple"
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "dc_common"
7
+ version = "0.1.23"
8
+ description = "Shared schemas and utils for the datacenter project."
9
+ authors = [
10
+ {name = "Hang GuangLiang", email = "hanguangliang@alphaaidig.com"},
11
+ ]
12
+ dependencies = [
13
+ "pydantic>=2.0",
14
+ ]
15
+
16
+ [tool.hatch.build.targets.wheel]
17
+ packages = ["src/dc_common"]
18
+
19
+ [tool.uv]
20
+ index-url = "https://pypi.tuna.tsinghua.edu.cn/simple"
@@ -0,0 +1,15 @@
1
+ """
2
+ DataCenter Common - 公共库
3
+
4
+ 提供:
5
+ - 数据源(Tushare、本地缓存等)
6
+ - 工具函数
7
+ - 核心组件
8
+ """
9
+ from .data_sources import BaseDataSource, DataSourceStatus, DailyQuoteSource
10
+
11
+ __all__ = [
12
+ 'BaseDataSource',
13
+ 'DataSourceStatus',
14
+ 'DailyQuoteSource',
15
+ ]
@@ -0,0 +1,48 @@
1
+ import logging
2
+ import sys
3
+ import os
4
+ from pathlib import Path
5
+
6
+ def setup_logger(name: str = "datacenter") -> logging.Logger:
7
+ """设置并返回配置好的logger实例"""
8
+
9
+ # 区分开发和生产环境
10
+ if os.environ.get('ENVIRONMENT') == 'production':
11
+ # 生产环境使用标准日志目录
12
+ log_dir = Path("/var/log/datacenter")
13
+ log_dir.mkdir(exist_ok=True, mode=0o755)
14
+ else:
15
+ # 开发环境使用相对路径
16
+ log_dir = Path("logs")
17
+ log_dir.mkdir(exist_ok=True)
18
+
19
+ # 创建logger
20
+ logger = logging.getLogger(name)
21
+ logger.setLevel(logging.INFO)
22
+
23
+ # 清除现有的handlers
24
+ logger.handlers.clear()
25
+
26
+ # 创建formatter
27
+ formatter = logging.Formatter(
28
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
29
+ )
30
+
31
+ # 文件handler
32
+ file_handler = logging.FileHandler(log_dir / "app.log", encoding="utf-8")
33
+ file_handler.setLevel(logging.INFO)
34
+ file_handler.setFormatter(formatter)
35
+
36
+ # 控制台handler
37
+ console_handler = logging.StreamHandler(sys.stdout)
38
+ console_handler.setLevel(logging.INFO)
39
+ console_handler.setFormatter(formatter)
40
+
41
+ # 添加handlers
42
+ logger.addHandler(file_handler)
43
+ logger.addHandler(console_handler)
44
+
45
+ return logger
46
+
47
+ # 创建默认logger实例
48
+ logger = setup_logger()
@@ -0,0 +1,16 @@
1
+ """
2
+ 数据源模块 - 提供各类数据源的实现
3
+
4
+ 本模块提供统一的数据源接口,支持:
5
+ - Tushare 数据源
6
+ - 本地缓存管理
7
+ - 数据质量验证
8
+ """
9
+ from .base_source import BaseDataSource, DataSourceStatus
10
+ from .daily_quote_source import DailyQuoteSource
11
+
12
+ __all__ = [
13
+ 'BaseDataSource',
14
+ 'DataSourceStatus',
15
+ 'DailyQuoteSource'
16
+ ]
@@ -0,0 +1,176 @@
1
+ """
2
+ 数据源基类定义
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from datetime import datetime, time
6
+ from typing import Dict, Any, Optional
7
+ import pandas as pd
8
+ import logging
9
+ from pathlib import Path
10
+ from enum import Enum
11
+
12
+
13
+ class DataSourceStatus(Enum):
14
+ """数据源状态"""
15
+ PENDING = "pending" # 等待中
16
+ READY = "ready" # 数据就绪
17
+ UPDATING = "updating" # 更新中
18
+ FAILED = "failed" # 更新失败
19
+ NOT_NEEDED = "not_needed" # 该日不需要更新
20
+
21
+
22
+ class BaseDataSource(ABC):
23
+ """数据源基类
24
+
25
+ 每个数据源有独立的调度策略和更新时间
26
+ """
27
+
28
+ def __init__(self, data_dir: str, logger: Optional[logging.Logger] = None):
29
+ self.data_dir = Path(data_dir)
30
+ self.data_dir.mkdir(parents=True, exist_ok=True)
31
+ self.logger = logger or logging.getLogger(__name__)
32
+
33
+ @property
34
+ @abstractmethod
35
+ def source_name(self) -> str:
36
+ """数据源唯一标识"""
37
+ pass
38
+
39
+ @property
40
+ @abstractmethod
41
+ def display_name(self) -> str:
42
+ """显示名称"""
43
+ pass
44
+
45
+ @property
46
+ @abstractmethod
47
+ def update_time(self) -> str:
48
+ """
49
+ 期望更新时间
50
+ 例如: "17:00", "09:30"
51
+ """
52
+ pass
53
+
54
+ @property
55
+ def update_delay_days(self) -> int:
56
+ """
57
+ 更新延迟天数
58
+ 0 = 当天更新
59
+ 1 = 次日更新
60
+ """
61
+ return 0
62
+
63
+ @property
64
+ def priority(self) -> int:
65
+ """
66
+ 优先级(数字越小越优先)
67
+ 基础数据优先级高,衍生数据优先级低
68
+ """
69
+ return 100
70
+
71
+ @abstractmethod
72
+ def fetch_data(self, trade_date: str) -> pd.DataFrame:
73
+ """
74
+ 获取数据
75
+
76
+ Args:
77
+ trade_date: 交易日期 (YYYYMMDD)
78
+
79
+ Returns:
80
+ 数据 DataFrame
81
+ """
82
+ pass
83
+
84
+ @abstractmethod
85
+ def validate_data(self, df: pd.DataFrame) -> bool:
86
+ """
87
+ 验证数据质量
88
+
89
+ Returns:
90
+ True=数据有效, False=数据无效
91
+ """
92
+ pass
93
+
94
+ def is_ready(self, trade_date: str) -> DataSourceStatus:
95
+ """
96
+ 检查数据是否就绪
97
+
98
+ Args:
99
+ trade_date: 交易日期
100
+
101
+ Returns:
102
+ 数据源状态
103
+ """
104
+ # 1. 检查本地是否已有数据
105
+ if self._has_local_data(trade_date):
106
+ return DataSourceStatus.READY
107
+
108
+ # 2. 检查是否到了更新时间
109
+ if not self._is_update_time():
110
+ return DataSourceStatus.PENDING
111
+
112
+ return DataSourceStatus.READY
113
+
114
+ def update(self, trade_date: str) -> Dict[str, Any]:
115
+ """
116
+ 更新数据
117
+
118
+ Args:
119
+ trade_date: 交易日期
120
+
121
+ Returns:
122
+ 更新结果字典
123
+ """
124
+ result = {
125
+ 'source': self.source_name,
126
+ 'trade_date': trade_date,
127
+ 'status': 'unknown',
128
+ 'rows': 0,
129
+ 'message': ''
130
+ }
131
+
132
+ try:
133
+ self.logger.info(f"🔄 开始更新 {self.display_name}: {trade_date}")
134
+
135
+ # 1. 获取数据
136
+ df = self.fetch_data(trade_date)
137
+
138
+ # 2. 验证数据
139
+ if not self.validate_data(df):
140
+ result['status'] = 'failed'
141
+ result['message'] = '数据验证失败'
142
+ return result
143
+
144
+ # 3. 保存数据
145
+ self._save_data(df, trade_date)
146
+
147
+ result['status'] = 'success'
148
+ result['rows'] = len(df)
149
+ result['message'] = f'成功更新 {len(df)} 条数据'
150
+
151
+ self.logger.info(f"✅ {self.display_name} 更新完成: {len(df)} 条")
152
+
153
+ except Exception as e:
154
+ result['status'] = 'failed'
155
+ result['message'] = str(e)
156
+ self.logger.error(f"❌ {self.display_name} 更新失败: {e}")
157
+
158
+ return result
159
+
160
+ def _is_update_time(self) -> bool:
161
+ """检查是否到了更新时间"""
162
+ try:
163
+ hour, minute = map(int, self.update_time.split(':'))
164
+ target_time = time(hour, minute)
165
+ now = datetime.now().time()
166
+ return now >= target_time
167
+ except:
168
+ return True
169
+
170
+ def _has_local_data(self, trade_date: str) -> bool:
171
+ """检查本地是否已有数据(子类覆盖)"""
172
+ return False
173
+
174
+ def _save_data(self, df: pd.DataFrame, trade_date: str):
175
+ """保存数据(子类实现)"""
176
+ pass
@@ -0,0 +1,195 @@
1
+ """
2
+ 日线行情数据源
3
+ """
4
+ import os
5
+ import pandas as pd
6
+ from pathlib import Path
7
+ from typing import Optional
8
+ import logging
9
+
10
+ from .base_source import BaseDataSource, DataSourceStatus
11
+
12
+
13
+ class DailyQuoteSource(BaseDataSource):
14
+ """日线行情数据源
15
+
16
+ 从 Tushare 获取日线行情数据
17
+ 更新时间:每天 17:00
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ data_dir: str,
23
+ tushare_token: Optional[str] = None,
24
+ logger: Optional[logging.Logger] = None
25
+ ):
26
+ super().__init__(data_dir, logger)
27
+
28
+ self.token = tushare_token or os.getenv('TUSHARE_TOKEN')
29
+ if not self.token:
30
+ raise ValueError("Tushare token 未配置,请设置 TUSHARE_TOKEN 环境变量")
31
+
32
+ # 缓存文件直接存储在 data_dir 下(不再添加 raw 子目录)
33
+ self.cache_file = self.data_dir / 'daily_quotes.parquet'
34
+ self.raw_cache_file = self.data_dir / 'daily_quotes_raw.parquet'
35
+
36
+ # 延迟初始化 Tushare(避免导入时立即连接)
37
+ self._pro = None
38
+
39
+ @property
40
+ def pro(self):
41
+ """延迟初始化 Tushare Pro API"""
42
+ if self._pro is None:
43
+ import tushare as ts
44
+ ts.set_token(self.token)
45
+ self._pro = ts.pro_api()
46
+ return self._pro
47
+
48
+ @property
49
+ def source_name(self) -> str:
50
+ return "daily_quotes"
51
+
52
+ @property
53
+ def display_name(self) -> str:
54
+ return "日线行情"
55
+
56
+ @property
57
+ def update_time(self) -> str:
58
+ return "17:00"
59
+
60
+ @property
61
+ def update_delay_days(self) -> int:
62
+ return 0
63
+
64
+ @property
65
+ def priority(self) -> int:
66
+ return 10
67
+
68
+ def fetch_data(self, trade_date: str) -> pd.DataFrame:
69
+ """从 Tushare 获取日线数据"""
70
+ self.logger.info(f"从 Tushare 获取 {trade_date} 的日线数据...")
71
+
72
+ df = self.pro.daily(
73
+ trade_date=trade_date
74
+ )
75
+
76
+ if df.empty:
77
+ self.logger.warning(f"{trade_date} 无数据(可能是非交易日)")
78
+ else:
79
+ self.logger.info(f"获取到 {len(df)} 条数据")
80
+
81
+ return df
82
+
83
+ def validate_data(self, df: pd.DataFrame) -> bool:
84
+ """验证数据质量"""
85
+ if df.empty:
86
+ return True # 空数据也是有效的(可能是非交易日)
87
+
88
+ required_fields = ['ts_code', 'trade_date', 'close']
89
+ return all(field in df.columns for field in required_fields)
90
+
91
+ def _has_local_data(self, trade_date: str) -> bool:
92
+ """检查本地是否已有数据"""
93
+ if not self.raw_cache_file.exists():
94
+ return False
95
+
96
+ try:
97
+ df = pd.read_parquet(self.raw_cache_file)
98
+ return trade_date in df['trade_date'].values
99
+ except Exception as e:
100
+ self.logger.warning(f"检查本地数据失败: {e}")
101
+ return False
102
+
103
+ def _save_data(self, df: pd.DataFrame, trade_date: str):
104
+ """保存数据(追加模式)"""
105
+ # 确保目录存在
106
+ self.raw_cache_file.parent.mkdir(parents=True, exist_ok=True)
107
+ self.cache_file.parent.mkdir(parents=True, exist_ok=True)
108
+
109
+ # 保存原始长格式数据
110
+ if self.raw_cache_file.exists():
111
+ existing_df = pd.read_parquet(self.raw_cache_file)
112
+
113
+ # 移除旧的同日期数据(如果有)
114
+ existing_df = existing_df[existing_df['trade_date'] != trade_date]
115
+
116
+ # 合并数据
117
+ combined_df = pd.concat([existing_df, df], ignore_index=True)
118
+
119
+ # 去重(基于 trade_date 和 ts_code,保留后出现的)
120
+ combined_df = combined_df.drop_duplicates(
121
+ subset=['trade_date', 'ts_code'],
122
+ keep='last'
123
+ )
124
+ else:
125
+ combined_df = df
126
+
127
+ combined_df.to_parquet(self.raw_cache_file, compression='snappy')
128
+ self.logger.info(f"原始数据已保存: {self.raw_cache_file}")
129
+
130
+ # 转换并保存宽表格式(用于因子计算)
131
+ self._save_wide_format(combined_df)
132
+
133
+ def _save_wide_format(self, df: pd.DataFrame):
134
+ """转换为宽表格式并保存(增量追加)"""
135
+ if df.empty:
136
+ return
137
+
138
+ # 排序
139
+ df = df.sort_values(['trade_date', 'ts_code'])
140
+
141
+ # 如果文件已存在,进行增量追加
142
+ if self.cache_file.exists():
143
+ try:
144
+ # 读取已有数据
145
+ existing_df = pd.read_parquet(self.cache_file)
146
+
147
+ # 移除与新数据重复的日期
148
+ existing_dates = existing_df['trade_date'].unique()
149
+ new_dates = df['trade_date'].unique()
150
+
151
+ # 只保留新数据中没有的旧日期
152
+ dates_to_keep = set(existing_dates) - set(new_dates)
153
+ if dates_to_keep:
154
+ existing_df = existing_df[existing_df['trade_date'].isin(dates_to_keep)]
155
+ else:
156
+ existing_df = pd.DataFrame()
157
+
158
+ # 合并数据
159
+ if not existing_df.empty:
160
+ combined_df = pd.concat([existing_df, df], ignore_index=True)
161
+ else:
162
+ combined_df = df
163
+
164
+ # 去重(基于 trade_date 和 ts_code,保留后出现的)
165
+ combined_df = combined_df.drop_duplicates(
166
+ subset=['trade_date', 'ts_code'],
167
+ keep='last'
168
+ )
169
+
170
+ # 排序
171
+ combined_df = combined_df.sort_values(['trade_date', 'ts_code'])
172
+
173
+ # 保存
174
+ combined_df.to_parquet(self.cache_file, compression='snappy', index=False)
175
+
176
+ old_len = len(existing_df) if dates_to_keep else 0
177
+ new_len = len(combined_df)
178
+ added_rows = new_len - old_len
179
+
180
+ self.logger.info(
181
+ f"宽表数据已增量更新: {self.cache_file} "
182
+ f"(原有: {old_len}, 新增: {added_rows}, 总计: {new_len})"
183
+ )
184
+ except Exception as e:
185
+ self.logger.warning(f"增量更新失败: {e},执行覆盖保存")
186
+ df.to_parquet(self.cache_file, compression='snappy', index=False)
187
+ self.logger.info(f"宽表数据已保存: {self.cache_file}")
188
+ else:
189
+ # 首次保存(先去重)
190
+ df = df.drop_duplicates(subset=['trade_date', 'ts_code'], keep='last')
191
+ df.to_parquet(self.cache_file, compression='snappy', index=False)
192
+ self.logger.info(f"宽表数据已保存: {self.cache_file}")
193
+
194
+ self.logger.info(f"数据范围: {df['trade_date'].min()} ~ {df['trade_date'].max()}")
195
+ self.logger.info(f"股票数量: {df['ts_code'].nunique()}")
@@ -0,0 +1,46 @@
1
+ """
2
+ 数据中心通用schemas模块
3
+ """
4
+ from .base import BaseResponse, PaginatedResponse, PaginationInfo
5
+ from .results import ResultType, BaseResult, TableResult, PageResult, DictResult, SingleResult
6
+ from .a_stock import AStock
7
+ from .hk_stock import HKStock
8
+ from .index_basic import IndexBasic
9
+ from .index_company import IndexCompany
10
+ from .margin_account import MarginAccount
11
+ from .margin_analysis import MarginAnalysis
12
+ from .margin_detail import MarginDetail
13
+
14
+ from .hs_industry import HSIndustry, HSIndustryCategory
15
+ from .hs_industry_company import HSIndustryCompany
16
+ from .sw_industry import SWIndustry
17
+ from .sw_industry_company import SWIndustryCompany
18
+ from .index_daily import IndexDaily
19
+ from .sw_index_daily import SWIndexDaily
20
+
21
+ __all__ = [
22
+ "BaseResponse",
23
+ "PaginatedResponse",
24
+ "PaginationInfo",
25
+ "ResultType",
26
+ "BaseResult",
27
+ "TableResult",
28
+ "PageResult",
29
+ "DictResult",
30
+ "SingleResult",
31
+ "AStock",
32
+ "HKStock",
33
+ "IndexBasic",
34
+ "IndexCompany",
35
+ "MarginAccount",
36
+ "MarginAnalysis",
37
+ "MarginDetail",
38
+
39
+ "HSIndustry",
40
+ "HSIndustryCategory",
41
+ "HSIndustryCompany",
42
+ "SWIndustry",
43
+ "SWIndustryCompany",
44
+ "IndexDaily",
45
+ "SWIndexDaily",
46
+ ]
@@ -0,0 +1,24 @@
1
+ """
2
+ A股相关的Pydantic模型
3
+ """
4
+ from pydantic import BaseModel, Field
5
+ from typing import Optional
6
+ from datetime import datetime
7
+
8
+ class AStockBase(BaseModel):
9
+ stock_code: str = Field(..., description="股票代码")
10
+ stock_name: str = Field(..., description="股票名称")
11
+ exchange: str = Field(..., description="交易所(SH/SZ/BJ)")
12
+ list_status: Optional[str] = Field(None, description="上市状态(L上市/D退市/P暂停上市)")
13
+ list_date: Optional[datetime] = Field(None, description="上市日期")
14
+ curr_type: Optional[str] = Field(None, description="交易货币(CNY)")
15
+ market: Optional[str] = Field(None, description="市场类型(主板/创业板/科创板/北交所)")
16
+ ric_code: Optional[str] = Field(None, description="路透代码")
17
+
18
+ class AStock(AStockBase):
19
+ id: int
20
+ created_at: datetime
21
+ updated_at: datetime
22
+
23
+ class Config:
24
+ from_attributes = True