Files
easy-tdx/src/easy_tdx/offline/block.py
T
GitHubandClaude Opus 4.7 50491f9aae feat!: rename project from xmtdx to easy-tdx
- Package directory: src/xmtdx/ -> src/easy_tdx/
- Import path: from easy_tdx import ...
- pip install easy-tdx
- Add LICENSE (MIT) with upstream attribution (pytdx, xmtdx)
- Add NOTICE with detailed attribution
- Update all examples, tests, scripts, docs
- Bump version to 1.0.0

BREAKING CHANGE: import path changed from `xmtdx` to `easy_tdx`

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 23:21:21 +08:00

93 lines
2.7 KiB
Python

"""板块数据读取(.dat 文件和自定义板块目录)。"""
from dataclasses import dataclass, field
from pathlib import Path
from ..codec.block import parse_block_dat
from ..exceptions import TdxFileNotFoundError, TdxOfflineError
from ..models.finance import TdxBlock
@dataclass
class CustomerBlock:
"""自定义板块。"""
blockname: str
block_type: str
codes: list[str] = field(default_factory=list)
def read_block_dat(filepath: str | Path) -> list[TdxBlock]:
"""从本地 .dat 板块文件读取板块数据。
直接复用 codec/block.py 的 parse_block_dat()。
Args:
filepath: .dat 文件路径(如 block_zs.dat)。
Returns:
TdxBlock 列表。
"""
filepath = Path(filepath)
if not filepath.is_file():
raise TdxFileNotFoundError(f"板块数据文件不存在: {filepath}")
data = filepath.read_bytes()
return parse_block_dat(data, filename=filepath.name)
def read_customer_blocks(block_dir: str | Path) -> list[CustomerBlock]:
"""从通达信自定义板块目录读取板块数据。
目录结构:
blocknew.cfg — 板块索引(120 字节/条:50B 名称 + 70B 文件名)
*.blk — 板块内容(每行一个代码,首位为市场标识)
Args:
block_dir: 自定义板块目录路径。
Returns:
CustomerBlock 列表。
"""
block_dir = Path(block_dir)
if not block_dir.is_dir():
raise TdxOfflineError(f"自定义板块目录不存在: {block_dir}")
cfg_path = block_dir / "blocknew.cfg"
if not cfg_path.is_file():
raise TdxOfflineError(f"板块配置文件不存在: {cfg_path}")
cfg_data = cfg_path.read_bytes()
results: list[CustomerBlock] = []
pos = 0
while pos + 120 <= len(cfg_data):
name = cfg_data[pos : pos + 50].decode("gbk", errors="replace").rstrip("\x00")
name = name.split("\x00")[0]
blk_filename = cfg_data[pos + 50 : pos + 120].decode("gbk", errors="replace").rstrip("\x00")
blk_filename = blk_filename.split("\x00")[0]
pos += 120
if not blk_filename:
continue
blk_path = block_dir / f"{blk_filename}.blk"
if not blk_path.is_file():
continue
codes: list[str] = []
for line in blk_path.read_text(encoding="utf-8", errors="replace").splitlines():
line = line.strip()
if line and len(line) > 1:
codes.append(line[1:]) # 去掉首位的市场标识
if name:
results.append(
CustomerBlock(
blockname=name,
block_type=blk_filename,
codes=codes,
)
)
return results