"""Reproduce one preregistered central setting. Install finlab, pandas, numpy.

Usage: python strategy.py [--design DESIGN]
Writes the FinLab HTML report and common-window data.csv / metrics.json next to this file.
Costs are sim() defaults. No upload. Data snapshot through 2026-11-09.
The complete 11-design / 99-setting study is in research-bundle.zip.
"""
import argparse
import copy
import json
from pathlib import Path
import numpy as np
import pandas as pd
import finlab
from finlab import data
from finlab.backtest import sim

START, END = "2018-02-01", "2026-11-09"
CHOICES = ("bullish_divergence", "bearish_exclusion")
OUT = Path(__file__).resolve().parent


def main():
    parser = argparse.ArgumentParser(__doc__)
    parser.add_argument("--design", choices=CHOICES, default=CHOICES[0])
    design = parser.parse_args().design
    finlab.login()
    data.set_market("tw")
    data.truncate_end = END
    prices = pd.DataFrame(data.get("etl:adj_close"))
    stocks = [s for s in prices.columns if len(s) == 4 and s.isdigit() and not s.startswith(("0", "91"))]
    days = prices.index[(prices.index >= "2016-01-01") & (prices.index <= END)]
    close = prices.reindex(index=days, columns=stocks)
    volume = pd.DataFrame(data.get("price:成交股數")).reindex(index=days, columns=stocks)
    amount = pd.DataFrame(data.get("price:成交金額")).reindex(index=days, columns=stocks).rolling(20).mean()
    eligible = (amount > 20_000_000) & close.notna() & (volume > 0)
    volume_ratio = volume / volume.shift(1).rolling(20).mean()
    if design == "bullish_divergence":
        signal = (close < close.shift(1).rolling(40).min()) & (volume_ratio < 0.8)
        score = amount.where(eligible & signal)
    else:
        signal = (close > close.shift(1).rolling(40).max()) & (volume_ratio < 0.8) & eligible
        score = amount.where(eligible)
    position = (score.rank(axis=1, ascending=False, method="first") <= 30).astype(float) / 30
    if design == "bearish_exclusion":
        position = position.where(~signal, 0.0)
    frequency = "W-FRI"
    selected = position.groupby(position.index.to_period(frequency)).tail(1).loc["2017-12-01":END]
    report = sim(selected, resample=selected.index, upload=False, end_date=END)
    equity = report.creturn.reindex(days).ffill().fillna(1.0).loc[START:END]
    benchmark = prices["0050"].reindex(equity.index)
    result = pd.DataFrame({design: equity / equity.iloc[0], "0050": benchmark / benchmark.iloc[0]})
    result.rename_axis("date").to_csv(OUT / "data.csv")
    metrics = {"run_date": str(pd.Timestamp.now(tz="Asia/Taipei").date()),
               "finlab_version": finlab.__version__, "window": [START, END]}
    for name, curve in result.items():
        daily = curve.pct_change().dropna()
        years = (curve.index[-1] - curve.index[0]).days / 365.25
        metrics[name] = {"cagr": float(curve.iloc[-1] ** (1 / years) - 1),
                         "daily_sharpe": float(daily.mean() / daily.std() * np.sqrt(252)),
                         "mdd": float((curve / curve.cummax() - 1).min())}
    (OUT / "metrics.json").write_text(json.dumps(metrics, indent=2))
    # Same FinLab report, displayed over the comparison window, including its initial cash.
    exported = copy.copy(report)
    exported.creturn = result[design]
    exported.daily_creturn = result[design]
    exported.benchmark = result["0050"]
    exported.daily_benchmark = result["0050"]
    exported.position = report.position.loc[START:END]
    trades = report.trades
    exported.trades = trades[trades["exit_date"].isna() | (trades["exit_date"] >= pd.Timestamp(START))].copy()
    exported.__dict__.pop("metrics", None)
    exported._get_stats.cache_clear()
    exported.to_html(str(OUT / (design + "_report.html")))
    print(json.dumps(metrics, indent=2))


if __name__ == "__main__":
    main()
