feat: enrich daily bar schema with valuation/status fields + VWAP

Batch download now pulls baostock's preclose, turn, pctChg, tradestatus,
isST, and peTTM/pbMRQ/psTTM/pcfNcfTTM on top of OHLCV+amount, plus a
derived daily VWAP (amount/volume). VWAP is raw-price scale and not
comparable with adjusted OHLC under qfq/hfq — documented in the schema.

Richer fields live only in the batch path (download_daily_batch ->
download_universe); single-symbol download_daily keeps the legacy
8-column schema that test_downloader.py pins. Also flags intraday/L1-L2
microstructure data as a future phase in the README roadmap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Yuxuan Yan
2026-06-09 14:25:38 +08:00
parent 419114b87b
commit de43444ad4
4 changed files with 57 additions and 17 deletions
+27 -6
View File
@@ -9,8 +9,27 @@ logger = logging.getLogger(__name__)
# Map the adjust argument to baostock's adjustflag codes.
_BAOSTOCK_ADJUST = {"qfq": "2", "hfq": "1", "": "3", "none": "3"}
_BAOSTOCK_FIELDS = "date,open,high,low,close,volume,amount"
_OHLCV = ["open", "high", "low", "close", "volume", "amount"]
# Richer field set requested by the batch downloader. On top of OHLCV+amount we
# pull baostock's preclose, turnover rate, daily % change, trade/ST status, and
# the four valuation ratios, then derive a daily VWAP (amount / volume).
_BATCH_FIELDS = (
"date,open,high,low,close,preclose,volume,amount,turn,pctChg,"
"tradestatus,isST,peTTM,pbMRQ,psTTM,pcfNcfTTM"
)
# Every batch field except ``date`` is numeric (flags included: 0/1 strings).
_BATCH_NUMERIC = [
"open", "high", "low", "close", "preclose", "volume", "amount",
"turn", "pctChg", "tradestatus", "isST",
"peTTM", "pbMRQ", "psTTM", "pcfNcfTTM",
]
# Output column order; ``vwap`` is derived (inserted right after ``amount``).
_BATCH_COLUMNS = [
"symbol", "date",
"open", "high", "low", "close", "preclose", "volume", "amount", "vwap",
"turn", "pctChg", "tradestatus", "isST",
"peTTM", "pbMRQ", "psTTM", "pcfNcfTTM",
]
class _SessionLost(Exception):
@@ -163,7 +182,7 @@ def download_daily_batch(
"""One baostock query; returns df, or None (no data), or raises _SessionLost."""
code = f"{symbol[:2]}.{symbol[2:]}"
rs = bs.query_history_k_data_plus(
code=code, fields=_BAOSTOCK_FIELDS,
code=code, fields=_BATCH_FIELDS,
start_date=start, end_date=end, frequency="d", adjustflag=flag,
)
if rs.error_code != "0":
@@ -176,12 +195,14 @@ def download_daily_batch(
rows.append(rs.get_row_data())
if not rows:
return None
df = pd.DataFrame(rows, columns=["date", *_OHLCV])
df = pd.DataFrame(rows, columns=["date", *_BATCH_NUMERIC])
# Suspended-trading days come back as empty strings; coerce to NaN
# rather than crashing the whole symbol.
df[_OHLCV] = df[_OHLCV].apply(pd.to_numeric, errors="coerce")
df[_BATCH_NUMERIC] = df[_BATCH_NUMERIC].apply(pd.to_numeric, errors="coerce")
# Daily VWAP = turnover (yuan) / shares; NaN when no volume (suspended).
df["vwap"] = (df["amount"] / df["volume"]).where(df["volume"] > 0)
df["symbol"] = symbol
return df[["symbol", "date", *_OHLCV]]
return df[_BATCH_COLUMNS]
bs.login()
try: