#!/usr/bin/env python3
"""chk_preclose_vs_tushare.py — 抽样核对: 库内 pre_close vs tushare pro.daily 原值 vs 派生式。

目的: 判定"脏"在库内(推算残留)还是在 tushare 原值; 并确认派生式
pre_close_t = raw_close_{t-1} × adj_factor_{t-1}/adj_factor_t 是否等于 tushare 原值。
用法: python code/chk_preclose_vs_tushare.py [ts_code:start:end ...]
"""
import sys, glob
import numpy as np
import pandas as pd
import tushare as ts

BASE = '/Users/xpresso/zt_app/backtest_zt_full'
CASES = [('000001.SZ', '20210104', '20210115'),
         ('000338.SZ', '20241016', '20241021'),
         ('603093.SH', '20260803', '20260812'),
         ('600309.SH', '20260909', '20260911')]
for a in sys.argv[1:]:
    c, s, e = a.split(':'); CASES.append((c, s, e))
pro = ts.pro_api()
for code, s, e in CASES:
    fp = [p for p in glob.glob(f'{BASE}/daily_*/*.csv') if p.endswith(f'/{code}.csv')]
    if not fp:
        print(f'{code}: 库内无文件'); continue
    d = pd.read_csv(fp[0], dtype={'trade_date': str}).sort_values('trade_date').reset_index(drop=True)
    rc = d['raw_close'].values; fac = d['adj_factor'].values
    der = np.roll(rc, 1) * (np.roll(fac, 1) / fac); der[0] = np.nan
    d['derived_pre_close'] = der
    lib = d[(d['trade_date'] >= s) & (d['trade_date'] <= e)][
        ['trade_date', 'raw_close', 'pre_close', 'derived_pre_close', 'pct_chg', 'adj_factor']]
    api = pro.daily(ts_code=code, start_date=s, end_date=e)[
        ['trade_date', 'close', 'pre_close', 'pct_chg']].rename(
        columns={'close': 'api_close', 'pre_close': 'api_pre_close', 'pct_chg': 'api_pct'})
    m = lib.merge(api, on='trade_date', how='outer').sort_values('trade_date')
    print(f'\n=== {code} {s}~{e} ({fp[0].split("daily_")[1].split("/")[0]}池) ===')
    print(m.to_string(index=False))
    chk = m.dropna(subset=['pre_close', 'api_pre_close'])
    if len(chk):
        print(f'  库 pre_close vs tushare 原值: 吻合 {int((chk["pre_close"]-chk["api_pre_close"]).abs().le(0.005).sum())}/{len(chk)}; '
              f'派生 vs tushare 原值: 吻合 {int((chk["derived_pre_close"]-chk["api_pre_close"]).abs().le(0.005).sum())}/{len(chk)}')