kitetdx 0.1.0__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.
kitetdx-0.1.0/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2017, mootdx
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
kitetdx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: kitetdx
3
+ Version: 0.1.0
4
+ Summary: A custom wrapper and extension for mootdx.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Kiteflyingee
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: Development Status :: 2 - Pre-Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Natural Language :: English
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3.6
22
+ Classifier: Programming Language :: Python :: 3.7
23
+ Requires-Dist: click (>=8.1.3,<9.0.0)
24
+ Requires-Dist: httpx (>=0.25.0,<0.26.0)
25
+ Requires-Dist: mini-racer (>=0.12.0,<0.13.0)
26
+ Requires-Dist: prettytable (>=3.5.0,<4.0.0)
27
+ Requires-Dist: tdxpy (>=0.2.5,<0.3.0)
28
+ Requires-Dist: tenacity (>=8.1.0,<9.0.0)
29
+ Requires-Dist: tqdm
30
+ Requires-Dist: typing-extensions (>=4.5.0,<5.0.0)
31
+ Project-URL: Homepage, https://github.com/Kiteflyingee/kitetdx
32
+ Project-URL: Repository, https://github.com/Kiteflyingee/kitetdx
33
+ Description-Content-Type: text/markdown
34
+
35
+ # Kitetdx
36
+
37
+ **Kitetdx** 是一个基于 [mootdx](https://github.com/mootdx/mootdx) 的二次封装与扩展项目。它提供了一套统一且稳定的 API 用于访问金融数据,内置了定制化的 `Reader` 模块,并对 `Quotes` 进行了完整的封装。
38
+
39
+
40
+ ## 功能特性
41
+
42
+ - **定制化 Reader 模块**: 位于 `kitetdx.reader`,针对特定项目需求重写了数据读取逻辑(如概念板块解析),完全独立于 `mootdx` 的 reader 实现。
43
+ - **统一 API 接口**: 对 `Quotes` 等模块进行了显式封装,提供了完整的文档注释,确保用户代码与底层实现解耦。
44
+ - **可扩展架构**: 设计上允许未来替换底层实现(如从 `mootdx` 切换到 `tushare` 或自研协议),而无需修改用户侧代码。
45
+
46
+ ## 安装指南
47
+
48
+ ```bash
49
+ pip install kitetdx
50
+ ```
51
+
52
+ ## 使用说明
53
+
54
+ ### 离线数据读取 (定制实现)
55
+
56
+ `kitetdx` 的 `Reader` 模块提供了增强的离线数据读取功能。
57
+
58
+ ```python
59
+ from kitetdx import Reader
60
+
61
+ # 初始化 Reader,指定通达信安装目录
62
+ reader = Reader.factory(market='std', tdxdir='/path/to/tdx')
63
+
64
+ # 读取日线数据
65
+ df = reader.daily(symbol='600036')
66
+ print(df)
67
+
68
+ # 读取板块数据 (定制逻辑)
69
+ concepts = reader.block()
70
+ for concept in concepts:
71
+ print(f"概念: {concept.concept_name}, 股票数: {len(concept.stocks)}")
72
+ ```
73
+
74
+ ### 在线行情 (封装 Mootdx)
75
+
76
+ `Quotes` 模块封装了 `mootdx.quotes`,提供了一致的 API。
77
+
78
+ ```python
79
+ from kitetdx import Quotes
80
+
81
+ # 初始化行情客户端
82
+ client = Quotes.factory(market='std', multithread=True, heartbeat=True)
83
+
84
+ # 获取实时 K 线
85
+ df = client.bars(symbol='600036', frequency=9, offset=10)
86
+ print(df)
87
+
88
+ # 获取实时分时
89
+ df = client.minute(symbol='000001')
90
+ print(df)
91
+ ```
92
+
93
+ ## 架构说明
94
+
95
+ - **`kitetdx.reader`**: 定制实现,不依赖 `mootdx.reader`。
96
+ - **`kitetdx.quotes`**: `mootdx.quotes` 的完整封装。
97
+ - **`kitetdx.entities`**: 统一的数据实体定义(如 `Stock`, `Concept`)。
98
+
99
+ ## 致谢
100
+
101
+ 本项目基于 [mootdx](https://github.com/mootdx/mootdx) 构建,感谢原作者的卓越工作。
102
+
@@ -0,0 +1,67 @@
1
+ # Kitetdx
2
+
3
+ **Kitetdx** 是一个基于 [mootdx](https://github.com/mootdx/mootdx) 的二次封装与扩展项目。它提供了一套统一且稳定的 API 用于访问金融数据,内置了定制化的 `Reader` 模块,并对 `Quotes` 进行了完整的封装。
4
+
5
+
6
+ ## 功能特性
7
+
8
+ - **定制化 Reader 模块**: 位于 `kitetdx.reader`,针对特定项目需求重写了数据读取逻辑(如概念板块解析),完全独立于 `mootdx` 的 reader 实现。
9
+ - **统一 API 接口**: 对 `Quotes` 等模块进行了显式封装,提供了完整的文档注释,确保用户代码与底层实现解耦。
10
+ - **可扩展架构**: 设计上允许未来替换底层实现(如从 `mootdx` 切换到 `tushare` 或自研协议),而无需修改用户侧代码。
11
+
12
+ ## 安装指南
13
+
14
+ ```bash
15
+ pip install kitetdx
16
+ ```
17
+
18
+ ## 使用说明
19
+
20
+ ### 离线数据读取 (定制实现)
21
+
22
+ `kitetdx` 的 `Reader` 模块提供了增强的离线数据读取功能。
23
+
24
+ ```python
25
+ from kitetdx import Reader
26
+
27
+ # 初始化 Reader,指定通达信安装目录
28
+ reader = Reader.factory(market='std', tdxdir='/path/to/tdx')
29
+
30
+ # 读取日线数据
31
+ df = reader.daily(symbol='600036')
32
+ print(df)
33
+
34
+ # 读取板块数据 (定制逻辑)
35
+ concepts = reader.block()
36
+ for concept in concepts:
37
+ print(f"概念: {concept.concept_name}, 股票数: {len(concept.stocks)}")
38
+ ```
39
+
40
+ ### 在线行情 (封装 Mootdx)
41
+
42
+ `Quotes` 模块封装了 `mootdx.quotes`,提供了一致的 API。
43
+
44
+ ```python
45
+ from kitetdx import Quotes
46
+
47
+ # 初始化行情客户端
48
+ client = Quotes.factory(market='std', multithread=True, heartbeat=True)
49
+
50
+ # 获取实时 K 线
51
+ df = client.bars(symbol='600036', frequency=9, offset=10)
52
+ print(df)
53
+
54
+ # 获取实时分时
55
+ df = client.minute(symbol='000001')
56
+ print(df)
57
+ ```
58
+
59
+ ## 架构说明
60
+
61
+ - **`kitetdx.reader`**: 定制实现,不依赖 `mootdx.reader`。
62
+ - **`kitetdx.quotes`**: `mootdx.quotes` 的完整封装。
63
+ - **`kitetdx.entities`**: 统一的数据实体定义(如 `Stock`, `Concept`)。
64
+
65
+ ## 致谢
66
+
67
+ 本项目基于 [mootdx](https://github.com/mootdx/mootdx) 构建,感谢原作者的卓越工作。
@@ -0,0 +1,5 @@
1
+ from .quotes import Quotes
2
+ from .reader import Reader
3
+ from .affair import Affair
4
+
5
+ __all__ = ['Quotes', 'Reader', 'Affair']
@@ -0,0 +1,39 @@
1
+ from mootdx.affair import Affair as MooAffair
2
+
3
+ class Affair(object):
4
+ """
5
+ Kitetdx Affair Module
6
+
7
+ Wraps mootdx.affair.Affair to provide financial data access.
8
+ """
9
+
10
+ @staticmethod
11
+ def files():
12
+ """
13
+ 获取远程文件列表
14
+
15
+ :return: list
16
+ """
17
+ return MooAffair.files()
18
+
19
+ @staticmethod
20
+ def fetch(downdir='tmp', filename=''):
21
+ """
22
+ 下载财务文件
23
+
24
+ :param downdir: 下载目录
25
+ :param filename: 文件名
26
+ :return: bool
27
+ """
28
+ return MooAffair.fetch(downdir=downdir, filename=filename)
29
+
30
+ @staticmethod
31
+ def parse(downdir='tmp', filename=''):
32
+ """
33
+ 解析财务文件
34
+
35
+ :param downdir: 下载目录
36
+ :param filename: 文件名 (可选,如果不指定则解析目录下所有)
37
+ :return: pd.DataFrame or None
38
+ """
39
+ return MooAffair.parse(downdir=downdir, filename=filename)
@@ -0,0 +1,27 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Optional
3
+
4
+ @dataclass
5
+ class Stock:
6
+ exchange: str # '0','1','2'
7
+ stock_code: str
8
+ stock_name: Optional[str] = None
9
+
10
+ @property
11
+ def full_code(self):
12
+ exchange_map = {'0': 'sz', '1': 'sh', '2': 'bj'}
13
+ return f"{exchange_map.get(self.exchange, '')}{self.stock_code}"
14
+
15
+
16
+ @dataclass
17
+ class Concept:
18
+ concept_type: str # 'GN','FG','ZS'
19
+ concept_name: str
20
+ concept_code: str
21
+ stocks: List[Stock]
22
+
23
+ def __init__(self, concept_type: str, concept_name: str, concept_code: str):
24
+ self.concept_type = concept_type
25
+ self.concept_name = concept_name
26
+ self.concept_code = concept_code
27
+ self.stocks = []
@@ -0,0 +1,169 @@
1
+ from mootdx.quotes import Quotes as MooQuotes
2
+ from mootdx.consts import MARKET_SH
3
+
4
+ class Quotes(object):
5
+ """
6
+ Kitetdx Quotes Module
7
+
8
+ Wraps mootdx.quotes.Quotes to provide a unified and documented API.
9
+ """
10
+
11
+ @staticmethod
12
+ def factory(market='std', **kwargs):
13
+ """
14
+ Quotes Factory Method
15
+
16
+ :param market: std (Standard Market), ext (Extended Market)
17
+ :param kwargs: Variable arguments
18
+ :return: Quotes object
19
+ """
20
+ return MooQuotes.factory(market=market, **kwargs)
21
+
22
+ def __init__(self, **kwargs):
23
+ self._client = MooQuotes.factory(**kwargs)
24
+
25
+ def bars(self, symbol='000001', frequency=9, start=0, offset=800, **kwargs):
26
+ """
27
+ 获取实时日K线数据
28
+
29
+ :param symbol: 股票代码
30
+ :param frequency: 数据频次 9=日线
31
+ :param start: 开始位置
32
+ :param offset: 每次获取条数
33
+ :return: pd.DataFrame or None
34
+ """
35
+ return self._client.bars(symbol=symbol, frequency=frequency, start=start, offset=offset, **kwargs)
36
+
37
+ def index_bars(self, symbol='000001', frequency=9, start=0, offset=800, **kwargs):
38
+ """
39
+ 获取指数K线数据
40
+
41
+ :param symbol: 股票代码
42
+ :param frequency: 数据频次
43
+ :param start: 开始位置
44
+ :param offset: 获取数量
45
+ :return: pd.DataFrame or None
46
+ """
47
+ return self._client.index_bars(symbol=symbol, frequency=frequency, start=start, offset=offset, **kwargs)
48
+
49
+ def minute(self, symbol=None, **kwargs):
50
+ """
51
+ 获取实时分时数据
52
+
53
+ :param symbol: 股票代码
54
+ :return: pd.DataFrame
55
+ """
56
+ return self._client.minute(symbol=symbol, **kwargs)
57
+
58
+ def minutes(self, symbol=None, date='20191023', **kwargs):
59
+ """
60
+ 分时历史数据
61
+
62
+ :param symbol: 股票代码
63
+ :param date: 查询日期
64
+ :return: pd.DataFrame or None
65
+ """
66
+ return self._client.minutes(symbol=symbol, date=date, **kwargs)
67
+
68
+ def transaction(self, symbol='', start=0, offset=800, **kwargs):
69
+ """
70
+ 查询分笔成交
71
+
72
+ :param symbol: 股票代码
73
+ :param start: 起始位置
74
+ :param offset: 结束位置
75
+ :return: pd.DataFrame or None
76
+ """
77
+ return self._client.transaction(symbol=symbol, start=start, offset=offset, **kwargs)
78
+
79
+ def transactions(self, symbol='', start=0, offset=800, date='20170209', **kwargs):
80
+ """
81
+ 查询历史分笔成交
82
+
83
+ :param symbol: 股票代码
84
+ :param start: 起始位置
85
+ :param offset: 获取数量
86
+ :param date: 查询日期
87
+ :return: pd.DataFrame or None
88
+ """
89
+ return self._client.transactions(symbol=symbol, start=start, offset=offset, date=date, **kwargs)
90
+
91
+ def F10(self, symbol='', name=''):
92
+ """
93
+ 读取公司信息详情
94
+
95
+ :param name: 公司 F10 标题
96
+ :param symbol: 股票代码
97
+ :return: pd.DataFrame or None
98
+ """
99
+ return self._client.F10(symbol=symbol, name=name)
100
+
101
+ def finance(self, symbol='000001', **kwargs):
102
+ """
103
+ 读取财务信息
104
+
105
+ :param symbol: 股票代码
106
+ :return: pd.DataFrame
107
+ """
108
+ return self._client.finance(symbol=symbol, **kwargs)
109
+
110
+ def k(self, symbol='', begin=None, end=None, **kwargs):
111
+ """
112
+ 读取k线信息
113
+
114
+ :param symbol: 股票代码
115
+ :param begin: 开始日期
116
+ :param end: 截止日期
117
+ :return: pd.DataFrame or None
118
+ """
119
+ return self._client.k(symbol=symbol, begin=begin, end=end, **kwargs)
120
+
121
+ def ohlc(self, **kwargs):
122
+ """
123
+ 读取k线信息 (Alias for k)
124
+ """
125
+ return self.k(**kwargs)
126
+
127
+ def block(self, tofile='block.dat', **kwargs):
128
+ """
129
+ 获取证券板块信息
130
+
131
+ :param tofile: 保存文件
132
+ :return: pd.DataFrame or None
133
+ """
134
+ return self._client.block(tofile=tofile, **kwargs)
135
+
136
+ def stock_count(self, market=MARKET_SH):
137
+ """
138
+ 获取市场股票数量
139
+
140
+ :param market: 股票市场代码 sh 上海, sz 深圳
141
+ :return: int
142
+ """
143
+ return self._client.stock_count(market=market)
144
+
145
+ def stocks(self, market=MARKET_SH):
146
+ """
147
+ 获取股票列表
148
+
149
+ :param market: 股票市场
150
+ :return: pd.DataFrame
151
+ """
152
+ return self._client.stocks(market=market)
153
+
154
+ def stock_all(self):
155
+ """
156
+ 获取所有股票列表
157
+
158
+ :return: pd.DataFrame
159
+ """
160
+ return self._client.stock_all()
161
+
162
+ def xdxr(self, symbol='', **kwargs):
163
+ """
164
+ 读取除权除息信息
165
+
166
+ :param symbol: 股票代码
167
+ :return: pd.DataFrame or None
168
+ """
169
+ return self._client.xdxr(symbol=symbol, **kwargs)
@@ -0,0 +1,297 @@
1
+ from abc import ABC
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+ from typing import List, Optional
5
+
6
+ import pandas as pd
7
+ from tdxpy.reader import TdxExHqDailyBarReader
8
+ from tdxpy.reader import TdxLCMinBarReader
9
+ from tdxpy.reader import TdxMinBarReader
10
+
11
+ from mootdx.contrib.compat import MooTdxDailyBarReader
12
+ from mootdx.utils import get_stock_market
13
+ # from mootdx.utils import read_data
14
+ from mootdx.utils import to_data
15
+ from mootdx.logger import logger
16
+
17
+ def read_data(file_path):
18
+ """
19
+ 读取文件内容
20
+ """
21
+ try:
22
+ with open(file_path, 'r', encoding='gbk') as f:
23
+ return f.read().strip().split('\n')
24
+ except FileNotFoundError:
25
+ logger.error(f"错误: 文件 {file_path} 不存在")
26
+ return None
27
+ except Exception as e:
28
+ logger.error(f"读取文件时出错: {e}")
29
+ return None
30
+
31
+
32
+ from kitetdx.entities import Stock, Concept
33
+
34
+
35
+ class Reader(object):
36
+ @staticmethod
37
+ def factory(market='std', **kwargs):
38
+ """
39
+ Reader 工厂方法
40
+
41
+ :param market: std 标准市场, ext 扩展市场
42
+ :param kwargs: 可变参数
43
+ :return:
44
+ """
45
+
46
+ if market == 'ext':
47
+ return ExtReader(**kwargs)
48
+
49
+ return StdReader(**kwargs)
50
+
51
+
52
+ class ReaderBase(ABC):
53
+ # 默认通达信安装目录
54
+ tdxdir = 'C:/new_tdx'
55
+
56
+ def __init__(self, tdxdir=None):
57
+ """
58
+ 构造函数
59
+
60
+ :param tdxdir: 通达信安装目录
61
+ """
62
+
63
+ if not Path(tdxdir).is_dir():
64
+ raise Exception('tdxdir 目录不存在')
65
+
66
+ self.tdxdir = tdxdir
67
+
68
+ def find_path(self, symbol=None, subdir='lday', suffix=None, **kwargs):
69
+ """
70
+ 自动匹配文件路径,辅助函数
71
+
72
+ :param symbol:
73
+ :param subdir:
74
+ :param suffix:
75
+ :return: pd.dataFrame or None
76
+ """
77
+
78
+ # 判断市场, 带#扩展市场
79
+ if '#' in symbol:
80
+ market = 'ds'
81
+ # 通达信特有的板块指数88****开头的日线数据放在 sh 文件夹下
82
+ elif symbol.startswith('88'):
83
+ market = 'sh'
84
+ else:
85
+ # 判断是sh还是sz
86
+ market = get_stock_market(symbol, True)
87
+
88
+ # 判断前缀(市场是sh和sz重置前缀)
89
+ if market.lower() in ['sh', 'sz', 'bj']:
90
+ symbol = market + symbol.lower().replace(market, '')
91
+
92
+ # 判断后缀
93
+ suffix = suffix if isinstance(suffix, list) else [suffix]
94
+
95
+ # 调试使用
96
+ if kwargs.get('debug'):
97
+ return market, symbol, suffix
98
+
99
+ # 遍历扩展名
100
+ for ex_ in suffix:
101
+ ex_ = ex_.strip('.')
102
+ vipdoc = Path(self.tdxdir) / 'vipdoc' / market / subdir / f'{symbol}.{ex_}'
103
+
104
+ if Path(vipdoc).exists():
105
+ return vipdoc
106
+
107
+ return None
108
+
109
+
110
+ class StdReader(ReaderBase):
111
+ """股票市场"""
112
+
113
+ def daily(self, symbol=None, **kwargs):
114
+ """
115
+ 获取日线数据
116
+
117
+ :param symbol: 证券代码
118
+ :return: pd.dataFrame or None
119
+ """
120
+ symbol = Path(symbol).stem
121
+ reader = MooTdxDailyBarReader()
122
+ vipdoc = self.find_path(symbol=symbol, subdir='lday', suffix='day')
123
+
124
+ result = reader.get_df(str(vipdoc)) if vipdoc else None
125
+ return to_data(result, symbol=symbol, **kwargs)
126
+
127
+ def minute(self, symbol=None, suffix=1, **kwargs): # noqa
128
+ """
129
+ 获取1, 5分钟线
130
+
131
+ :param suffix: 文件前缀
132
+ :param symbol: 证券代码
133
+ :return: pd.dataFrame or None
134
+ """
135
+ symbol = Path(symbol).stem
136
+ subdir = 'fzline' if str(suffix) == '5' else 'minline'
137
+ suffix = ['lc5', '5'] if str(suffix) == '5' else ['lc1', '1']
138
+ symbol = self.find_path(symbol, subdir=subdir, suffix=suffix)
139
+
140
+ if symbol is not None:
141
+ reader = TdxMinBarReader() if 'lc' not in symbol.suffix else TdxLCMinBarReader()
142
+ return reader.get_df(str(symbol))
143
+
144
+ return None
145
+
146
+ def fzline(self, symbol=None):
147
+ """
148
+ 分钟线数据
149
+
150
+ :param symbol: 自定义板块股票列表, 类型 list
151
+ :return: pd.dataFrame or Bool
152
+ """
153
+ return self.minute(symbol, suffix=5)
154
+
155
+ def block_new(self, name: str = None, symbol: list = None, group=False, **kwargs):
156
+ """
157
+ 自定义板块数据操作
158
+
159
+ :param name: 自定义板块名称
160
+ :param symbol: 自定义板块股票列表, 类型 list
161
+ :param group:
162
+ :return: pd.dataFrame or Bool
163
+ """
164
+ from mootdx.tools.customize import Customize
165
+
166
+ reader = Customize(tdxdir=self.tdxdir)
167
+
168
+ if symbol:
169
+ return reader.create(name=name, symbol=symbol, **kwargs)
170
+
171
+ return reader.search(name=name, group=group)
172
+
173
+ def block(self):
174
+ """
175
+ 获取板块数据
176
+ :return: List[Concept]
177
+ """
178
+ return self.parse_concept_data()
179
+
180
+ def parse_stock_mapping(self, file_path):
181
+ """
182
+ 解析股票代码-名称映射文件
183
+ 返回: {股票代码: 股票名称} 的字典
184
+ """
185
+ stock_mapping = {}
186
+
187
+ try:
188
+ lines = read_data(Path(self.tdxdir) / 'T0002' / 'hq_cache' / file_path)
189
+ if not lines:
190
+ return {}
191
+
192
+ for line_num, line in enumerate(lines):
193
+ line = line.strip()
194
+ if not line:
195
+ continue
196
+
197
+ # 解析格式: 000001|平安银行|平安保险,谢永林,冀光恒
198
+ parts = line.split('|')
199
+
200
+ if len(parts) < 2:
201
+ logger.warning(f"警告: 第{line_num}行格式不正确: {line}")
202
+ continue
203
+
204
+ stock_code = parts[0].strip()
205
+ stock_name = parts[1].strip()
206
+
207
+ stock_name = stock_name.replace(' ', '').replace(' ', '') # 全角和半角空格
208
+ stock_mapping[stock_code] = stock_name
209
+ return stock_mapping
210
+
211
+ except Exception as e:
212
+ logger.error(f"解析文件时出错: {e}")
213
+ return {}
214
+
215
+ def parse_concept_data(self) -> List[Concept]:
216
+ """
217
+ 解析原始数据格式
218
+ """
219
+ stock_mapping = self.parse_stock_mapping('infoharbor_ex.code')
220
+ concepts = []
221
+
222
+ current_concept = None
223
+ current_stocks = []
224
+
225
+ # 读取 GN/FG/ZS
226
+ gn_lines = read_data(Path(self.tdxdir) / 'T0002' / 'hq_cache' / 'infoharbor_block.dat')
227
+ if gn_lines:
228
+ for line in gn_lines:
229
+ if line.startswith('#'):
230
+ if current_concept:
231
+ current_concept.stocks = current_stocks
232
+ concepts.append(current_concept)
233
+ current_stocks = []
234
+
235
+ parts = line.strip('#').split(',')
236
+ concept_info = parts[0].split('_')
237
+
238
+ current_concept = Concept(
239
+ concept_type=concept_info[0],
240
+ concept_name=concept_info[1],
241
+ concept_code=parts[2]
242
+ )
243
+ else:
244
+ stock_items = line.split(',')
245
+ for item in stock_items:
246
+ if item and '#' in item:
247
+ exchange, code = item.split('#')
248
+ current_stocks.append(Stock(exchange=exchange, stock_code=code, stock_name=stock_mapping.get(code)))
249
+
250
+ # 添加最后一个概念
251
+ if current_concept:
252
+ current_concept.stocks = current_stocks
253
+ concepts.append(current_concept)
254
+
255
+ return concepts
256
+
257
+
258
+ class ExtReader(ReaderBase):
259
+ """扩展市场读取"""
260
+
261
+ def __init__(self, tdxdir=None):
262
+ super(ExtReader, self).__init__(tdxdir)
263
+ self.reader = TdxExHqDailyBarReader()
264
+
265
+ def daily(self, symbol=None):
266
+ """
267
+ 获取扩展市场日线数据
268
+
269
+ :return: pd.dataFrame or None
270
+ """
271
+
272
+ vipdoc = self.find_path(symbol=symbol, subdir='lday', suffix='day')
273
+ return self.reader.get_df(str(vipdoc)) if vipdoc else None
274
+
275
+ def minute(self, symbol=None):
276
+ """
277
+ 获取扩展市场分钟线数据
278
+
279
+ :return: pd.dataFrame or None
280
+ """
281
+
282
+ if not symbol:
283
+ return None
284
+
285
+ vipdoc = self.find_path(symbol=symbol, subdir='minline', suffix=['lc1', '1'])
286
+ return self.reader.get_df(str(vipdoc)) if vipdoc else None
287
+
288
+ def fzline(self, symbol=None):
289
+ """
290
+ 获取日线数据
291
+
292
+ :return: pd.dataFrame or None
293
+ """
294
+
295
+ vipdoc = self.find_path(symbol=symbol, subdir='fzline', suffix='lc5')
296
+ return self.reader.get_df(str(vipdoc)) if symbol else None
297
+
@@ -0,0 +1,82 @@
1
+ [tool.poetry]
2
+ name = "kitetdx"
3
+ version = "0.1.0"
4
+ description = "A custom wrapper and extension for mootdx."
5
+ authors = ["Kiteflyingee"]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ homepage = "https://github.com/Kiteflyingee/kitetdx"
9
+ repository = "https://github.com/Kiteflyingee/kitetdx"
10
+ classifiers = [
11
+ "Development Status :: 2 - Pre-Alpha",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Natural Language :: English",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.6",
17
+ "Programming Language :: Python :: 3.7",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ ]
23
+
24
+
25
+ [tool.poetry.scripts]
26
+ mootdx = "mootdx.__main__:entry"
27
+
28
+
29
+ [tool.poetry.dependencies]
30
+ python = "^3.8"
31
+ httpx = "^0.25.0"
32
+ tenacity = "^8.1.0"
33
+ tdxpy = "^0.2.5"
34
+ tqdm = "*"
35
+ prettytable = "^3.5.0"
36
+ click = "^8.1.3"
37
+ typing-extensions = "^4.5.0"
38
+ mini-racer = "^0.12.0"
39
+
40
+ [tool.poetry.group.test.dependencies]
41
+ pytest-cov = "^4.0.0"
42
+ freezegun = "^1.2.2"
43
+ pytest = "^7.3.1"
44
+
45
+ [build-system]
46
+ requires = ["poetry-core"]
47
+ build-backend = "poetry.core.masonry.api"
48
+
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = "tests"
52
+ addopts = "-p no:warnings"
53
+ log_cli = 0
54
+ log_cli_level = "DEBUG"
55
+
56
+
57
+ [tool.commitizen]
58
+ tag_format = "v$version"
59
+ annotated_tag = true
60
+ changelog_file = "docs/history.md"
61
+ changelog_incremental = true
62
+ update_changelog_on_bump = true
63
+ version_provider = "poetry"
64
+ version_files = [
65
+ "mootdx/__init__.py:__version__",
66
+ ]
67
+
68
+
69
+ [tool.poe.tasks]
70
+ venv = "poetry install --sync"
71
+ lock = "poetry lock --no-update"
72
+
73
+ test = "poetry run pytest"
74
+ lint = "ruff check ./mootdx/*.py"
75
+
76
+ dist = "poetry build -v"
77
+ bump = "cz bump --yes -ch -cc --increment"
78
+
79
+ sync = "make sync"
80
+ pypi = "poetry publish --build --skip-existing"
81
+
82
+ #{MAJOR,MINOR,PATCH}