"""
combined_ftmo_mc_v1.py
=======================
FTMO Monte Carlo simulation for the combined EURUSD + USDJPY portfolio.
Mirrors the EURUSD-only FTMO simulation but tests three scenarios:
  1. EURUSD alone
  2. USDJPY alone
  3. Combined portfolio (both running simultaneously)

For each scenario:
  - 5,000 Monte Carlo simulations over the full 23-year trade sequence
  - FTMO 1-Step Challenge: 10% profit target, 5% daily DD, 10% total DD
  - Pass rate, breach rate, avg time to pass, median time, P25/P75
  - Rolling 12-month pass rate (does it stay consistent over 20+ years?)
  - Side-by-side comparison table

DATA REQUIRED:
  data/processed/trades_real_costs.csv         (EURUSD — from real_world_costs_v1.py)
  data/processed/usdjpy_trades_real_costs.csv  (USDJPY — from usdjpy_real_costs_v1.py)

Run from project root:
  python src/research/combined_ftmo_mc_v1.py
"""

import pandas as pd
import numpy as np
from pathlib import Path
import sys
import warnings
warnings.filterwarnings('ignore')

BASE_PATH = Path(__file__).resolve().parents[2]
PROC_PATH = BASE_PATH / "data" / "processed"
OUT_PATH  = BASE_PATH / "data" / "output"
OUT_PATH.mkdir(parents=True, exist_ok=True)

if str(BASE_PATH / "src") not in sys.path:
    sys.path.insert(0, str(BASE_PATH / "src"))

# ── FTMO 1-Step Challenge parameters ─────────────────────────────────────────
ACCOUNT        = 100_000
PROFIT_TARGET  = 0.10    # 10%
DAILY_DD_LIMIT = 0.05    # 5%  ($5,000 on $100k)
TOTAL_DD_LIMIT = 0.10    # 10% ($10,000 on $100k)
N_SIMS         = 5000
SEED           = 42

# ── Risk parameters ───────────────────────────────────────────────────────────
EU_NOTIONAL = 300_000
UJ_NOTIONAL = 300_000


def load_trades():
    """Load both trade files and return as separate DataFrames."""
    # EURUSD
    eu_path = next((p for p in [
        PROC_PATH / "trades_real_costs.csv",
        PROC_PATH / "trades" / "trades_real_costs.csv",
    ] if p.exists()), None)
    if eu_path is None:
        print("ERROR: trades_real_costs.csv not found"); sys.exit(1)
    eu = pd.read_csv(eu_path)
    eu['entry_time'] = pd.to_datetime(eu['entry_time'])

    # USDJPY
    uj_path = PROC_PATH / "usdjpy_trades_real_costs.csv"
    if not uj_path.exists():
        print("ERROR: usdjpy_trades_real_costs.csv not found"); sys.exit(1)
    uj = pd.read_csv(uj_path)
    uj['entry_time'] = pd.to_datetime(uj['entry_time'])

    # Use real P&L column — handle different column names
    eu_pnl_col = 'dollar_pnl_real' if 'dollar_pnl_real' in eu.columns else 'pnl_real'
    uj_pnl_col = 'pnl_real'

    print(f"  EURUSD: {len(eu):,} trades  ({eu['entry_time'].min().year}–"
          f"{eu['entry_time'].max().year})  "
          f"WR={((eu[eu_pnl_col]>0).mean()*100):.1f}%")
    print(f"  USDJPY: {len(uj):,} trades  ({uj['entry_time'].min().year}–"
          f"{uj['entry_time'].max().year})  "
          f"WR={((uj[uj_pnl_col]>0).mean()*100):.1f}%")

    return eu, eu_pnl_col, uj, uj_pnl_col


def build_combined_pnl(eu, eu_col, uj, uj_col):
    """
    Merge EU and UJ trades into a single chronological P&L stream.
    Trades from both models appear in date order. No netting — each
    trade is independent, simultaneous positions are allowed.
    """
    eu_stream = eu[['entry_time', eu_col]].rename(
        columns={eu_col: 'pnl', 'entry_time': 'ts'})
    eu_stream['pair'] = 'EURUSD'

    uj_stream = uj[['entry_time', uj_col]].rename(
        columns={uj_col: 'pnl', 'entry_time': 'ts'})
    uj_stream['pair'] = 'USDJPY'

    combined = pd.concat([eu_stream, uj_stream]).sort_values('ts').reset_index(drop=True)
    return combined


def run_ftmo_mc(pnl_array, label, n_sims=N_SIMS, seed=SEED,
                account=ACCOUNT, pt=PROFIT_TARGET,
                daily_lim=DAILY_DD_LIMIT, total_lim=TOTAL_DD_LIMIT):
    """
    Monte Carlo FTMO 1-Step Challenge simulation.

    Method: resample the full trade sequence with replacement,
    then walk through trades accumulating equity. Track:
      - profit target hit (with DD limits intact)
      - daily DD breach (any single trade loss > daily_lim × account)
      - total DD breach (running DD from peak > total_lim × account)
      - months to pass (approximate: 182 EU trades/yr → ~0.066 yrs/trade,
        or use actual date-weighted estimate)
    """
    np.random.seed(seed)
    pnl  = np.array(pnl_array, dtype=float)
    n    = len(pnl)
    pt_usd = account * pt
    dd_d = account * daily_lim
    dd_t = account * total_lim

    passes       = 0
    daily_b      = 0
    total_b      = 0
    no_profit    = 0
    pass_trades  = []   # number of trades when target hit
    pass_months  = []   # approximate months

    # Estimate trades per month from actual data length
    # Full history is ~23 years → use as calibration
    trades_per_month = n / (23 * 12)

    for _ in range(n_sims):
        sim  = np.random.choice(pnl, size=n, replace=True)
        eq   = account
        peak = account
        hit_daily = hit_total = hit_profit = False
        t_pass = None

        for i, trade_pnl in enumerate(sim):
            eq   += trade_pnl
            peak  = max(peak, eq)
            dd_running = peak - eq  # absolute drawdown from peak

            # Daily DD: treat each trade as one trading period
            if trade_pnl < -dd_d:
                hit_daily = True; break
            # Total DD
            if dd_running > dd_t:
                hit_total = True; break
            # Profit target
            if eq >= account + pt_usd and not hit_profit:
                hit_profit = True
                t_pass     = i + 1
                break

        if hit_daily:
            daily_b += 1
        elif hit_total:
            total_b += 1
        elif hit_profit:
            passes     += 1
            pass_trades.append(t_pass)
            pass_months.append(t_pass / trades_per_month)
        else:
            no_profit += 1

    pass_rate   = passes / n_sims * 100
    daily_rate  = daily_b / n_sims * 100
    total_rate  = total_b / n_sims * 100
    noprofit_rt = no_profit / n_sims * 100

    if pass_months:
        avg_months = np.mean(pass_months)
        med_months = np.median(pass_months)
        p25_months = np.percentile(pass_months, 25)
        p75_months = np.percentile(pass_months, 75)
        min_months = np.min(pass_months)
    else:
        avg_months = med_months = p25_months = p75_months = min_months = 0.0

    return {
        'label':       label,
        'n_trades':    n,
        'pass_rate':   round(pass_rate, 2),
        'daily_b':     round(daily_rate, 2),
        'total_b':     round(total_rate, 2),
        'no_profit':   round(noprofit_rt, 2),
        'avg_months':  round(avg_months, 2),
        'med_months':  round(med_months, 2),
        'p25_months':  round(p25_months, 2),
        'p75_months':  round(p75_months, 2),
        'min_months':  round(min_months, 2),
    }


def rolling_pass_rate(pnl_array, label, window_years=3,
                      n_sims=500, account=ACCOUNT,
                      pt=PROFIT_TARGET, daily_lim=DAILY_DD_LIMIT,
                      total_lim=TOTAL_DD_LIMIT):
    """
    Compute FTMO pass rate using only trades from each rolling 3-year window.
    Shows whether the edge is stable across different market regimes.
    """
    pnl  = np.array(pnl_array, dtype=float)
    n    = len(pnl)
    # Assume uniform time distribution for simplicity
    trades_per_yr = n / 23
    window_n      = int(trades_per_yr * window_years)

    results = []
    step    = max(int(trades_per_yr * 0.5), 1)  # 6-month step

    for start in range(0, n - window_n, step):
        end     = start + window_n
        window  = pnl[start:end]
        year_approx = 2003 + start / trades_per_yr

        r = run_ftmo_mc(window, label, n_sims=n_sims,
                        account=account, pt=pt,
                        daily_lim=daily_lim, total_lim=total_lim)
        results.append({
            'year':      round(year_approx, 1),
            'pass_rate': r['pass_rate'],
            'daily_b':   r['daily_b'],
            'total_b':   r['total_b'],
        })

    return pd.DataFrame(results)


def sharpe(pnl, per_yr):
    p   = np.array(pnl, dtype=float)
    exc = p / 100_000 - 0.04 / per_yr
    return round(exc.mean() / exc.std() * np.sqrt(per_yr), 2) if exc.std() > 0 else 0


def print_results(r):
    print(f"\n  {'─'*62}")
    print(f"  {r['label']}")
    print(f"  {'─'*62}")
    print(f"  Trades in simulation : {r['n_trades']:,}")
    print(f"  Pass rate            : {r['pass_rate']:>6.2f}%")
    print(f"  Daily DD breach      : {r['daily_b']:>6.2f}%")
    print(f"  Total DD breach      : {r['total_b']:>6.2f}%")
    print(f"  No profit (timeout)  : {r['no_profit']:>6.2f}%")
    print(f"  Time to pass:")
    print(f"    Average            : {r['avg_months']:>5.1f} months")
    print(f"    Median             : {r['med_months']:>5.1f} months")
    print(f"    P25 / P75          : {r['p25_months']:.1f} / {r['p75_months']:.1f} months")
    print(f"    Fastest (min)      : {r['min_months']:.1f} months")


def main():
    print("="*72)
    print("COMBINED FTMO MONTE CARLO — EURUSD + USDJPY")
    print(f"FTMO 1-Step: Target +10% | Daily DD 5% | Total DD 10%")
    print(f"Account: $100k | Simulations: {N_SIMS:,}")
    print("="*72)

    print("\nLoading trade data...")
    eu, eu_col, uj, uj_col = load_trades()

    eu_pnl  = eu[eu_col].values
    uj_pnl  = uj[uj_col].values
    combined = build_combined_pnl(eu, eu_col, uj, uj_col)
    co_pnl  = combined['pnl'].values

    # Quick stats
    eu_years = (eu['entry_time'].max() - eu['entry_time'].min()).days / 365.25
    uj_years = (uj['entry_time'].max() - uj['entry_time'].min()).days / 365.25
    co_years = max(eu_years, uj_years)

    eu_sh = sharpe(eu_pnl, len(eu_pnl)/eu_years)
    uj_sh = sharpe(uj_pnl, len(uj_pnl)/uj_years)
    co_sh = sharpe(co_pnl, len(co_pnl)/co_years)

    print(f"\n  Portfolio summary:")
    print(f"  {'':30}{'EURUSD':>10}{'USDJPY':>10}{'Combined':>10}")
    print(f"  {'─'*60}")
    print(f"  {'Trades':30}{len(eu_pnl):>10,}{len(uj_pnl):>10,}{len(co_pnl):>10,}")
    print(f"  {'Trades/yr':30}{len(eu_pnl)/eu_years:>10.0f}"
          f"{len(uj_pnl)/uj_years:>10.0f}"
          f"{len(co_pnl)/co_years:>10.0f}")
    print(f"  {'Sharpe (real costs)':30}{eu_sh:>10.2f}{uj_sh:>10.2f}{co_sh:>10.2f}")
    print(f"  {'Total P&L':30}"
          f"${eu_pnl.sum():>9,.0f}"
          f"${uj_pnl.sum():>9,.0f}"
          f"${co_pnl.sum():>9,.0f}")
    print(f"  {'Win Rate %':30}"
          f"{(eu_pnl>0).mean()*100:>10.1f}"
          f"{(uj_pnl>0).mean()*100:>10.1f}"
          f"{(co_pnl>0).mean()*100:>10.1f}")

    # ── Run FTMO MC for each scenario ─────────────────────────────────────────
    print(f"\n{'='*72}")
    print(f"FTMO 1-STEP MONTE CARLO — {N_SIMS:,} SIMULATIONS EACH")
    print(f"{'='*72}")

    print("\nRunning EURUSD...", end=" ", flush=True)
    r_eu = run_ftmo_mc(eu_pnl, "EURUSD alone")
    print("done")

    print("Running USDJPY...", end=" ", flush=True)
    r_uj = run_ftmo_mc(uj_pnl, "USDJPY alone")
    print("done")

    print("Running Combined...", end=" ", flush=True)
    r_co = run_ftmo_mc(co_pnl, "Combined Portfolio")
    print("done")

    print_results(r_eu)
    print_results(r_uj)
    print_results(r_co)

    # ── Side-by-side summary ──────────────────────────────────────────────────
    print(f"\n{'='*72}")
    print("SIDE-BY-SIDE SUMMARY")
    print(f"{'='*72}")
    print(f"\n  {'Metric':<30}{'EURUSD':>12}{'USDJPY':>12}{'Combined':>12}")
    print(f"  {'─'*66}")

    rows = [
        ('Pass rate %',       r_eu['pass_rate'],  r_uj['pass_rate'],  r_co['pass_rate']),
        ('Daily DD breach %', r_eu['daily_b'],    r_uj['daily_b'],    r_co['daily_b']),
        ('Total DD breach %', r_eu['total_b'],    r_uj['total_b'],    r_co['total_b']),
        ('No profit %',       r_eu['no_profit'],  r_uj['no_profit'],  r_co['no_profit']),
        ('Avg months',        r_eu['avg_months'], r_uj['avg_months'], r_co['avg_months']),
        ('Median months',     r_eu['med_months'], r_uj['med_months'], r_co['med_months']),
        ('P25 months',        r_eu['p25_months'], r_uj['p25_months'], r_co['p25_months']),
        ('P75 months',        r_eu['p75_months'], r_uj['p75_months'], r_co['p75_months']),
        ('Fastest (months)',  r_eu['min_months'], r_uj['min_months'], r_co['min_months']),
    ]

    for label, v_eu, v_uj, v_co in rows:
        best = max(v_eu, v_uj, v_co) if 'pass' in label.lower() \
               else min(v_eu, v_uj, v_co)
        def fmt(v, is_best):
            s = f"{v:.2f}"
            return f"{s} ◄" if is_best else f"{s}   "
        print(f"  {label:<30}{fmt(v_eu, v_eu==best):>12}"
              f"{fmt(v_uj, v_uj==best):>12}"
              f"{fmt(v_co, v_co==best):>12}")

    # ── Rolling pass rate ─────────────────────────────────────────────────────
    print(f"\n{'='*72}")
    print("ROLLING 3-YEAR PASS RATE — Is the edge stable across regimes?")
    print(f"{'='*72}")
    print(f"  (500 sims per window, 6-month step)")

    print("\n  Running rolling windows...", end=" ", flush=True)
    roll_eu = rolling_pass_rate(eu_pnl,  "EURUSD")
    roll_uj = rolling_pass_rate(uj_pnl,  "USDJPY")
    roll_co = rolling_pass_rate(co_pnl,  "Combined")
    print("done")

    # Merge on approximate year
    roll_eu = roll_eu.rename(columns={
        'pass_rate':'eu_pass','daily_b':'eu_daily','total_b':'eu_total'})
    roll_uj = roll_uj.rename(columns={
        'pass_rate':'uj_pass','daily_b':'uj_daily','total_b':'uj_total'})
    roll_co = roll_co.rename(columns={
        'pass_rate':'co_pass','daily_b':'co_daily','total_b':'co_total'})

    roll = roll_eu.merge(
        roll_uj[['year','uj_pass','uj_daily','uj_total']],
        on='year', how='outer'
    ).merge(
        roll_co[['year','co_pass','co_daily','co_total']],
        on='year', how='outer'
    ).sort_values('year').reset_index(drop=True)

    print(f"\n  {'Window':>8}{'EU Pass%':>10}{'UJ Pass%':>10}{'Co Pass%':>10}"
          f"{'EU Daily%':>11}{'Co Daily%':>11}")
    print(f"  {'─'*60}")

    for _, row in roll.iterrows():
        eu_p  = f"{row['eu_pass']:.1f}%" if pd.notna(row.get('eu_pass')) else "   —"
        uj_p  = f"{row['uj_pass']:.1f}%" if pd.notna(row.get('uj_pass')) else "   —"
        co_p  = f"{row['co_pass']:.1f}%" if pd.notna(row.get('co_pass')) else "   —"
        eu_d  = f"{row['eu_daily']:.1f}%" if pd.notna(row.get('eu_daily')) else "   —"
        co_d  = f"{row['co_daily']:.1f}%" if pd.notna(row.get('co_daily')) else "   —"
        print(f"  {row['year']:>8.1f}{eu_p:>10}{uj_p:>10}{co_p:>10}"
              f"{eu_d:>11}{co_d:>11}")

    min_eu = roll['eu_pass'].min() if 'eu_pass' in roll else 0
    min_co = roll['co_pass'].min() if 'co_pass' in roll else 0
    avg_eu = roll['eu_pass'].mean() if 'eu_pass' in roll else 0
    avg_co = roll['co_pass'].mean() if 'co_pass' in roll else 0

    print(f"\n  EURUSD rolling: min={min_eu:.1f}%  avg={avg_eu:.1f}%")
    print(f"  Combined rolling: min={min_co:.1f}%  avg={avg_co:.1f}%")

    # ── Overlap analysis ──────────────────────────────────────────────────────
    print(f"\n{'='*72}")
    print("PORTFOLIO CORRELATION ANALYSIS")
    print(f"{'='*72}")

    eu_dated = eu[['entry_time', eu_col]].copy()
    eu_dated['date'] = eu_dated['entry_time'].dt.date
    uj_dated = uj[['entry_time', uj_col]].copy()
    uj_dated['date'] = uj_dated['entry_time'].dt.date

    eu_daily = eu_dated.groupby('date')[eu_col].sum()
    uj_daily = uj_dated.groupby('date')[uj_col].sum()
    both     = eu_daily.align(uj_daily, join='inner', fill_value=0)
    corr     = pd.Series(both[0].values).corr(pd.Series(both[1].values))

    # Days with simultaneous positions
    eu_days  = set(eu_dated['date'])
    uj_days  = set(uj_dated['date'])
    overlap  = eu_days & uj_days
    overlap_pct = len(overlap) / len(eu_days | uj_days) * 100

    print(f"\n  Daily P&L correlation:       {corr:.4f}")
    print(f"  Days with overlap:           {len(overlap):,} / {len(eu_days|uj_days):,}"
          f"  ({overlap_pct:.1f}%)")
    print(f"  EURUSD trading days:         {len(eu_days):,}")
    print(f"  USDJPY trading days:         {len(uj_days):,}")

    if abs(corr) < 0.10:
        corr_verdict = "UNCORRELATED — models operate independently ✅"
    elif abs(corr) < 0.25:
        corr_verdict = "LOW CORRELATION — minimal portfolio risk ✅"
    else:
        corr_verdict = "NOTABLE CORRELATION — monitor combined DD carefully ⚠️"

    print(f"\n  {corr_verdict}")

    # ── Concurrent max DD ─────────────────────────────────────────────────────
    eu_max_dd = abs(pd.Series(100_000 + np.cumsum(eu_pnl)) \
        .pipe(lambda eq: ((eq - eq.cummax()) / eq.cummax() * 100)).min())
    uj_max_dd = abs(pd.Series(100_000 + np.cumsum(uj_pnl)) \
        .pipe(lambda eq: ((eq - eq.cummax()) / eq.cummax() * 100)).min())
    co_max_dd = abs(pd.Series(100_000 + np.cumsum(co_pnl)) \
        .pipe(lambda eq: ((eq - eq.cummax()) / eq.cummax() * 100)).min())

    print(f"\n  Max historical DD (real costs):")
    print(f"    EURUSD:    {eu_max_dd:.2f}%")
    print(f"    USDJPY:    {uj_max_dd:.2f}%")
    print(f"    Combined:  {co_max_dd:.2f}%")

    if co_max_dd < 5.0:
        dd_verdict = "Combined DD well within FTMO limits ✅"
    elif co_max_dd < 8.0:
        dd_verdict = "Combined DD acceptable — monitor closely ✅"
    else:
        dd_verdict = "Combined DD approaching FTMO limit — reduce risk ⚠️"
    print(f"    → {dd_verdict}")

    # ── Final verdict ─────────────────────────────────────────────────────────
    print(f"\n{'='*72}")
    print("FINAL VERDICT")
    print(f"{'='*72}")

    print(f"""
  EURUSD alone:    {r_eu['pass_rate']:.1f}% pass rate  |  {r_eu['med_months']:.1f} month median
  USDJPY alone:    {r_uj['pass_rate']:.1f}% pass rate  |  {r_uj['med_months']:.1f} month median
  Combined:        {r_co['pass_rate']:.1f}% pass rate  |  {r_co['med_months']:.1f} month median
""")

    if r_co['pass_rate'] >= 95:
        verdict = "COMBINED PORTFOLIO READY FOR FTMO ✅✅"
        detail  = (f"Pass rate {r_co['pass_rate']:.1f}% with median "
                   f"{r_co['med_months']:.1f} months to challenge completion.\n"
                   f"  Combined portfolio reduces time-to-profit vs either model alone.\n"
                   f"  Run both models on the same account.")
    elif r_co['pass_rate'] >= 85:
        verdict = "COMBINED PORTFOLIO READY — STRONG PASS RATE ✅"
        detail  = (f"Pass rate {r_co['pass_rate']:.1f}%. "
                   f"Consider separate accounts if DD concern arises.")
    else:
        verdict = "REVIEW RISK SIZING — PASS RATE BELOW TARGET ⚠️"
        detail  = "Check position sizing or run on separate accounts."

    print(f"  VERDICT: {verdict}")
    print(f"  {detail}")

    # ── Save summary ──────────────────────────────────────────────────────────
    summary = pd.DataFrame([r_eu, r_uj, r_co])
    out     = PROC_PATH / "combined_ftmo_results.csv"
    summary.to_csv(out, index=False)
    print(f"\n  Results saved: {out.name}")


if __name__ == "__main__":
    main()
