mirror of
https://ghfast.top/https://github.com/aeroxw/easy_tdx_max.git
synced 2026-09-12 15:44:18 +08:00
- K-line: daily+ periods output 'date' only, minute periods output 'datetime' - Transactions (tick-by-tick): combine date param + hour/minute into 'datetime' - XdxrRecord, HistoricalFundFlow: year/month/day merged to 'date' - MinuteBar: rename unknown_1 to _unknown_1 (hidden from DataFrame) - MinuteBar: add datetime column computed from bar index (A-share 240-bar pattern) - get_minute_time_data: use history endpoint only (current-day endpoint broken in pytdx too) - Update all examples to reflect new DataFrame column names
112 lines
3.8 KiB
Python
112 lines
3.8 KiB
Python
"""Dataclass → DataFrame 转换工具。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import asdict, is_dataclass
|
||
from typing import Any
|
||
|
||
import pandas as pd
|
||
|
||
|
||
def _to_df(data: Any) -> pd.DataFrame:
|
||
"""将 list[dataclass] 或单个 dataclass 转为 DataFrame。
|
||
|
||
自动丢弃以 ``_`` 开头的内部字段(如 ``_raw``)。
|
||
仅处理 year/month/day(无 hour/minute)→ date 的合并;
|
||
SecurityBar 的完整 datetime 合并由调用方按周期决定。
|
||
"""
|
||
if isinstance(data, list):
|
||
if not data:
|
||
return pd.DataFrame()
|
||
rows = []
|
||
for item in data:
|
||
d = _clean_dict(item)
|
||
rows.append(d)
|
||
return pd.DataFrame(rows)
|
||
if is_dataclass(data) and not isinstance(data, type):
|
||
return pd.DataFrame([_clean_dict(data)])
|
||
raise TypeError(f"不支持转换为 DataFrame 的类型: {type(data)}")
|
||
|
||
|
||
def _clean_dict(item: Any) -> dict[str, Any]:
|
||
d = asdict(item)
|
||
d = {k: v for k, v in d.items() if not k.startswith("_")}
|
||
return _merge_datetime_fields(d)
|
||
|
||
|
||
def _merge_datetime_fields(d: dict[str, Any]) -> dict[str, Any]:
|
||
"""将仅含 year/month/day(无 hour/minute)的模型合并为 date 列。"""
|
||
if all(k in d for k in ("year", "month", "day")) and not all(
|
||
k in d for k in ("hour", "minute")
|
||
):
|
||
dt = pd.Timestamp(year=d["year"], month=d["month"], day=d["day"])
|
||
result: dict[str, Any] = {"date": dt}
|
||
result.update({k: v for k, v in d.items() if k not in {"year", "month", "day"}})
|
||
return result
|
||
return d
|
||
|
||
|
||
def _merge_bar_datetime(df: pd.DataFrame, daily_plus: bool) -> pd.DataFrame:
|
||
"""根据 K 线周期将 SecurityBar 的分散字段合并为 date 或 datetime。
|
||
|
||
Args:
|
||
daily_plus: True 表示日线及以上周期(DAY/WEEK/MONTH/YEAR),只保留 date;
|
||
False 表示分钟线(MIN_1/5/15/30/60),保留完整 datetime。
|
||
"""
|
||
if df.empty or "year" not in df.columns:
|
||
return df
|
||
date_str = (
|
||
df["year"].astype(str)
|
||
+ "-"
|
||
+ df["month"].astype(str).str.zfill(2)
|
||
+ "-"
|
||
+ df["day"].astype(str).str.zfill(2)
|
||
)
|
||
if daily_plus:
|
||
df.insert(0, "date", pd.to_datetime(date_str))
|
||
else:
|
||
full_str = (
|
||
date_str
|
||
+ " "
|
||
+ df["hour"].astype(str).str.zfill(2)
|
||
+ ":"
|
||
+ df["minute"].astype(str).str.zfill(2)
|
||
)
|
||
df.insert(0, "datetime", pd.to_datetime(full_str))
|
||
df.drop(columns=["year", "month", "day", "hour", "minute"], inplace=True)
|
||
return df
|
||
|
||
|
||
def _merge_txn_datetime(df: pd.DataFrame, date_int: int) -> pd.DataFrame:
|
||
"""将逐笔成交的 date + hour:minute 合并为 datetime 列。"""
|
||
if df.empty or "hour" not in df.columns:
|
||
return df
|
||
year = date_int // 10000
|
||
month = (date_int // 100) % 100
|
||
day = date_int % 100
|
||
base = pd.Timestamp(year=year, month=month, day=day)
|
||
offsets = pd.to_timedelta(df["hour"] * 3600 + df["minute"] * 60, unit="s")
|
||
df.insert(0, "datetime", base + offsets)
|
||
df.drop(columns=["hour", "minute"], inplace=True)
|
||
return df
|
||
|
||
|
||
def _add_minute_datetime(df: pd.DataFrame, date_int: int) -> pd.DataFrame:
|
||
"""为分时 DataFrame 添加 datetime 列(从 bar 索引计算时间)。
|
||
|
||
A 股分时 240 条:0-119 = 9:30~11:29(上午),120-239 = 13:00~14:59(下午)。
|
||
"""
|
||
if df.empty:
|
||
return df
|
||
year = date_int // 10000
|
||
month = (date_int // 100) % 100
|
||
day = date_int % 100
|
||
base = pd.Timestamp(year=year, month=month, day=day)
|
||
n = len(df)
|
||
morning = list(range(9 * 60 + 30, 9 * 60 + 30 + 120))
|
||
afternoon = list(range(13 * 60, 13 * 60 + 120))
|
||
all_minutes = (morning + afternoon)[:n]
|
||
offsets = pd.to_timedelta(all_minutes, unit="m")
|
||
df.insert(0, "datetime", base + offsets)
|
||
return df
|