#!/usr/bin/env python3
"""chk_preclose_repair_audit.py — pre_close 列体检 + "由因子精确重算"可行性评估(只读)。

派生式(不变根推导, 无未来函数):
    pre_close_t = raw_close_{t-1} × (adj_factor_{t-1} / adj_factor_t)
非除权日 因子比=1 → pre_close = 前一日 raw_close; 除权日 → pre_close = 除权参考价。
自洽判据: |(raw_close_t/pre_close_t - 1)*100 - pct_chg_t| < 0.05pp(与等效的 B 判据同源)。

用法: python code/chk_preclose_repair_audit.py [--by-year] [--top 20]
"""
import sys, glob
from collections import Counter
import numpy as np
import pandas as pd

BASE = '/Users/xpresso/zt_app/backtest_zt_full'
TOP = 20
if '--top' in sys.argv:
    TOP = int(sys.argv[sys.argv.index('--top') + 1])
TOL = 0.05
tot = mismatched = same = 0
stored_bad = derived_bad = both_bad = 0
by_year = Counter(); states = Counter()
worst = []
first_row = 0
for f in sorted(glob.glob(f'{BASE}/daily_*/*.csv')):
    d = pd.read_csv(f, dtype={'trade_date': str}).sort_values('trade_date').reset_index(drop=True)
    if len(d) < 2:
        continue
    rc = d['raw_close'].values; pc = d['pre_close'].values; pct = d['pct_chg'].values
    fac = d['adj_factor'].values
    prev = np.roll(rc, 1); fprev = np.roll(fac, 1)
    prev[0] = np.nan; fprev[0] = np.nan
    der = prev * (fprev / fac)
    ok = np.isfinite(prev) * np.isfinite(pct) * np.isfinite(der) * (der > 0)
    first_row += int((~np.isfinite(prev)).sum())
    tot += int(ok.sum())
    mism = np.logical_and(ok, np.abs(der - pc) > 0.005)          # 0.5 分钱
    mismatched += int(mism.sum()); same += int(ok.sum() - mism.sum())
    s_ok = np.logical_and(ok, np.abs((rc / pc - 1) * 100 - pct) < TOL)
    d_ok = np.logical_and(ok, np.abs((rc / der - 1) * 100 - pct) < TOL)
    stored_bad += int(np.logical_and(ok, np.logical_not(s_ok)).sum())
    derived_bad += int(np.logical_and(ok, np.logical_not(d_ok)).sum())
    both_bad += int(np.logical_and(np.logical_and(ok, np.logical_not(s_ok)), np.logical_not(d_ok)).sum())
    for i in np.where(mism)[0]:
        by_year[d['trade_date'].values[i][:4]] += 1
        states[(bool(s_ok[i]), bool(d_ok[i]))] += 1
        worst.append((float(np.abs(der[i] - pc[i]) / max(pc[i], 1e-9)), f.split('/')[-1][:-4],
                      d['trade_date'].values[i], round(float(pc[i]), 3), round(float(der[i]), 3),
                      round(float(rc[i]), 3), round(float(pct[i]), 4)))
print(f'可判行 {tot} | 首行(无前值, 不可派生) {first_row}')
print(f'store pre_close 与派生式一致(≤0.005元) {same} ({same/max(tot,1)*100:.2f}%) | '
      f'不一致 {mismatched} ({mismatched/max(tot,1)*100:.2f}%)')
print(f'自洽率: 用 store pre_close 不自洽 {stored_bad} ({stored_bad/max(tot,1)*100:.2f}%) | '
      f'用派生 pre_close 不自洽 {derived_bad} ({derived_bad/max(tot,1)*100:.3f}%) | 两者都不自洽 {both_bad}')
print('不一致行的归属 (store自洽?, 派生自洽?):', dict(states))
if '--by-year' in sys.argv:
    print('不一致按年:', dict(sorted(by_year.items())))
worst.sort(reverse=True)
print(f'偏差最大的 {TOP} 行: 代码 日期 store pre_close | 派生 pre_close | raw_close | pct_chg')
for w in worst[:TOP]:
    print(w[1:])