"""
trade_clustering_risk_v1.py  (v2 — FTMO realistic intraday simulation)
=======================================================================
The big one. FTMO cares about short-term survival, not long-term Sharpe.

Added: INTRADAY CLUSTERING SIMULATION
  - EURUSD holds up to 52h, USDJPY up to 24h
  - Both can be active simultaneously on the same day
  - Both hit stops with full slippage + worst-case sizing
  - Output: worst single day (realistic not theoretical)
            % of days within 80% of DD limit

Standard outputs:
  Max consecutive losses
  Worst 10-trade rolling window
  Worst calendar week / month
  FTMO survival: stops before daily DD breach
"""
import sys, warnings
from pathlib import Path
import numpy as np
import pandas as pd
warnings.filterwarnings('ignore')

BASE    = Path(__file__).resolve().parents[2]
EU_FILE = BASE / 'data/processed/trades/trades_real_costs.csv'
UJ_FILE = BASE / 'data/processed/usdjpy_trades_real_costs.csv'
ACCOUNT = 100_000

DAILY_2S  = 5_000   # 2-Step daily DD limit
TOTAL_2S  = 10_000  # 2-Step total DD limit
DAILY_1S  = 3_000   # 1-Step daily DD limit
TOTAL_1S  = 6_000   # 1-Step total DD limit

EU_SL_PCT = 0.0025  # 0.25%
UJ_SL_PCT = 0.0040  # 0.40%
EU_NOTIONAL_1X = 300_000
UJ_NOTIONAL_1X = 300_000

np.random.seed(42)

def load_trades(path, pair):
    df = pd.read_csv(path)
    df.columns = [c.lower().strip() for c in df.columns]
    for col in ['dollar_pnl_real','pnl_real','pnl','net_pnl']:
        if col in df.columns:
            df['pnl'] = pd.to_numeric(df[col], errors='coerce'); break
    for col in ['entry_time','date','entry_date']:
        if col in df.columns:
            df['date'] = pd.to_datetime(df[col], errors='coerce'); break
    df = df.dropna(subset=['pnl','date']).sort_values('date').reset_index(drop=True)
    if df['pnl'].abs().median() < 1.0: df['pnl'] *= ACCOUNT
    df['pair'] = pair
    df['win']  = df['pnl'] > 0
    df['week'] = df['date'].dt.to_period('W')
    df['month']= df['date'].dt.to_period('M')
    df['year'] = df['date'].dt.year
    return df

def consec_losses(arr):
    streaks=[]; cur=0
    for v in arr:
        if v<0: cur+=1
        else:
            if cur>0: streaks.append(cur)
            cur=0
    if cur>0: streaks.append(cur)
    return streaks

eu = load_trades(EU_FILE, 'EURUSD')
uj = load_trades(UJ_FILE, 'USDJPY')

print("=" * 72)
print("  TRADE CLUSTERING RISK v2  —  FTMO Realistic")
print("=" * 72)
print(f"""
  EURUSD: {len(eu):,} trades  WR={( eu['pnl']>0).mean():.1%}  
          Avg loss ${eu[eu['pnl']<0]['pnl'].mean():+.0f}
  USDJPY: {len(uj):,} trades  WR={( uj['pnl']>0).mean():.1%}  
          Avg loss ${uj[uj['pnl']<0]['pnl'].mean():+.0f}
  FTMO 2-Step: ${DAILY_2S:,}/day  ${TOTAL_2S:,} total
  FTMO 1-Step: ${DAILY_1S:,}/day  ${TOTAL_1S:,} total
""")

# ── 1. CONSECUTIVE LOSSES ────────────────────────────────────────────────────
print("─"*72)
print("  1. CONSECUTIVE LOSS STREAKS")
print("─"*72)

for pair, df in [('EURUSD', eu), ('USDJPY', uj)]:
    s = consec_losses(df['pnl'].values)
    if not s: continue
    max_s = max(s)
    avg_s = np.mean(s)
    worst_loss = 0; best_start = 0; cur=0; cur_loss=0
    for i,v in enumerate(df['pnl'].values):
        if v<0:
            cur+=1; cur_loss+=v
            if cur_loss < worst_loss:
                worst_loss=cur_loss; best_start=i-cur+1
        else:
            cur=0; cur_loss=0
    worst_seq = df['pnl'].values[best_start:best_start+max(s)][::-1]
    print(f"\n  {pair}:  Max streak={max_s}  Avg={avg_s:.1f}  "
          f"Worst streak $={worst_loss:+,.0f}")
    print(f"  Streaks ≥3: {sum(1 for x in s if x>=3)}  "
          f"≥5: {sum(1 for x in s if x>=5)}  "
          f"≥7: {sum(1 for x in s if x>=7)}  "
          f"≥10: {sum(1 for x in s if x>=10)}")
    print(f"  Worst streak cost: ${worst_loss:+,.0f}  "
          f"vs $10k total limit: {'✅ Safe' if abs(worst_loss)<TOTAL_2S else '⚠️ EXCEEDS'}")
    print(f"  Distribution: "+" | ".join(
          f"{l}:{sum(1 for x in s if x==l)}" for l in range(1,min(max_s+1,11))))

# ── 2. WORST 10-TRADE ROLLING WINDOW ────────────────────────────────────────
print("\n"+"─"*72)
print("  2. WORST 10-TRADE ROLLING WINDOW")
print("─"*72)

for pair, df in [('EURUSD', eu), ('USDJPY', uj)]:
    arr = df['pnl'].values
    windows = [sum(arr[i:i+10]) for i in range(len(arr)-9)]
    w_arr = np.array(windows)
    worst = min(windows)
    print(f"\n  {pair}:  Worst 10-trade=${worst:+,.0f}  "
          f"Best=${max(windows):+,.0f}  "
          f"Median=${np.median(w_arr):+,.0f}")
    print(f"  % of 10-windows negative: {(w_arr<0).mean()*100:.1f}%")
    print(f"  vs $10k total limit: {'✅ Safe' if abs(worst)<TOTAL_2S else '⚠️ EXCEEDS'}")
    for pct_l, pct_v in [('p1',1),('p5',5),('p10',10),('p25',25)]:
        print(f"    {pct_l}: ${np.percentile(w_arr,pct_v):+,.0f}", end='')
    print()

# ── 3. WORST WEEK + MONTH ────────────────────────────────────────────────────
print("\n"+"─"*72)
print("  3. WORST CALENDAR WEEK AND MONTH")
print("─"*72)

for pair, df in [('EURUSD', eu), ('USDJPY', uj)]:
    weekly  = df.groupby('week')['pnl'].sum()
    monthly = df.groupby('month')['pnl'].sum()
    print(f"\n  {pair}:")
    print(f"  Worst week:  {weekly.idxmin()}  = ${weekly.min():+,.0f}  "
          f"{'⚠️  >$5k daily' if abs(weekly.min())>DAILY_2S else '✅ within $5k/day'}")
    print(f"  Worst month: {monthly.idxmin()} = ${monthly.min():+,.0f}  "
          f"{'⚠️  >$10k total' if abs(monthly.min())>TOTAL_2S else '✅ within $10k total'}")
    print(f"  Neg weeks: {(weekly<0).sum()}/{len(weekly)} ({(weekly<0).mean()*100:.1f}%)  "
          f"Neg months: {(monthly<0).sum()}/{len(monthly)} ({(monthly<0).mean()*100:.1f}%)")
    print(f"  Top 3 worst weeks:")
    for wk, v in weekly.nsmallest(3).items():
        wk_trades = df[df['week']==wk]
        print(f"    {wk}  ${v:+,.0f}  ({len(wk_trades)} trades)")

# ── 4. FTMO SURVIVAL TABLE ───────────────────────────────────────────────────
print("\n"+"─"*72)
print("  4. CONSECUTIVE STOPS BEFORE FTMO DAILY LIMIT BREACH")
print("─"*72)

for pair, df, sl_pct, notional in [
    ('EURUSD', eu, EU_SL_PCT, EU_NOTIONAL_1X),
    ('USDJPY', uj, UJ_SL_PCT, UJ_NOTIONAL_1X),
]:
    avg_stop = abs(df[df['pnl']<0]['pnl'].mean())
    print(f"\n  {pair}  Avg stop loss: ${avg_stop:+.0f}")
    print(f"  {'Sizing':>8}  {'Loss/trade':>12}  "
          f"{'Stops→$5k breach':>18}  {'Stops→$3k breach':>18}")
    print(f"  {'─'*60}")
    for mult in [1.0, 1.5, 2.0]:
        loss = avg_stop * mult
        n_2s = DAILY_2S / loss
        n_1s = DAILY_1S / loss
        print(f"  {mult:.1f}×  base  ${loss:>12,.0f}  "
              f"{n_2s:>17.1f}  {n_1s:>17.1f}")

# ── 5. INTRADAY CLUSTERING SIMULATION (THE FTMO-REALISTIC TEST) ─────────────
print("\n"+"─"*72)
print("  5. INTRADAY CLUSTERING SIMULATION  (FTMO realistic — the key test)")
print("─"*72)
print("""
  Both models can have overlapping positions:
    EURUSD holds up to 52h — on any given day it may be in trade
    USDJPY holds up to 24h — could enter while EURUSD is also active
  
  Worst-case day: BOTH pairs in trade, BOTH hit stops, 2× scaling, + slippage
  Monte Carlo 50,000 simulations of day-level P&L combining both models.
""")

all_dates = pd.date_range('2003-01-01','2026-03-31',freq='B')
eu_d  = eu.groupby(eu['date'].dt.date)['pnl'].sum()
uj_d  = uj.groupby(uj['date'].dt.date)['pnl'].sum()
eu_d.index = pd.to_datetime(eu_d.index)
uj_d.index = pd.to_datetime(uj_d.index)
eu_daily = eu_d.reindex(all_dates, fill_value=0)
uj_daily = uj_d.reindex(all_dates, fill_value=0)
cb_daily = eu_daily + uj_daily

# Active days
eu_active = eu_daily[eu_daily != 0]
uj_active = uj_daily[uj_daily != 0]
cb_active = cb_daily[cb_daily != 0]

# Theoretical worst single days
eu_worst_theoretical  = -(EU_SL_PCT * EU_NOTIONAL_1X * 2.0 * 1.01)  # 2x + 1% slip
uj_worst_theoretical  = -(UJ_SL_PCT * UJ_NOTIONAL_1X * 2.0 * 1.01)
combined_worst_theory = eu_worst_theoretical + uj_worst_theoretical

# Actual worst days
print(f"  ACTUAL worst days from 23-year backtest:")
print(f"  {'Pair':>12}  {'Worst day':>12}  {'vs $5k daily':>14}  {'vs $3k (1-Step)':>16}")
print(f"  {'─'*60}")
for label, daily in [('EURUSD', eu_daily), ('USDJPY', uj_daily), ('Combined', cb_daily)]:
    worst = daily.min()
    f2 = '⚠️' if abs(worst) > DAILY_2S else '✅'
    f1 = '⚠️' if abs(worst) > DAILY_1S else '✅'
    print(f"  {label:>12}  {worst:>+12,.0f}  {f2} {abs(worst)/DAILY_2S*100:>6.1f}%  "
          f"{f1} {abs(worst)/DAILY_1S*100:>7.1f}%")

# Theoretical (formula-based)
print(f"\n  THEORETICAL worst-case (2× scaling + 1% slippage, both stop out):")
print(f"  EURUSD: ${eu_worst_theoretical:+,.0f}  "
      f"USDJPY: ${uj_worst_theoretical:+,.0f}  "
      f"Combined: ${combined_worst_theory:+,.0f}")
print(f"  vs $5k daily: {abs(combined_worst_theory)/DAILY_2S*100:.1f}%  "
      f"vs $3k 1-Step: {abs(combined_worst_theory)/DAILY_1S*100:.1f}%")

# Monte Carlo intraday simulation
print(f"\n  Monte Carlo intraday simulation (50,000 days):")
print(f"  Each simulated day: randomly draw EU daily P&L + UJ daily P&L")
print(f"  from their empirical distributions of ACTIVE trading days\n")

eu_draws = eu_active.values
uj_draws = uj_active.values
n_sim    = 50_000

combined_days = []
for _ in range(n_sim):
    # Both models active: draw one day from each
    eu_day = np.random.choice(eu_draws)
    uj_day = np.random.choice(uj_draws)
    combined_days.append(eu_day + uj_day)

combined_days = np.array(combined_days)
worst_sim     = combined_days.min()
pct_exceed_2s = (combined_days < -DAILY_2S).mean() * 100
pct_exceed_1s = (combined_days < -DAILY_1S).mean() * 100
pct_above_80p_2s = (combined_days < -(DAILY_2S * 0.8)).mean() * 100
pct_above_80p_1s = (combined_days < -(DAILY_1S * 0.8)).mean() * 100

print(f"  Combined day distribution (both active simultaneously):")
for pct_l, pct_v in [('p0.1 (worst)',0.1),('p1',1),('p5',5),('p10',10),
                      ('p25',25),('Median',50),('p75',75)]:
    print(f"    {pct_l:>14}: ${np.percentile(combined_days,pct_v):>+10,.0f}")
print()
print(f"  % of days exceeding $5k daily limit (2-Step): {pct_exceed_2s:.3f}%")
print(f"  % of days exceeding $3k daily limit (1-Step): {pct_exceed_1s:.3f}%")
print(f"  % of days within 80% of $5k limit (warning zone): {pct_above_80p_2s:.2f}%")
print(f"  % of days within 80% of $3k limit (warning zone): {pct_above_80p_1s:.2f}%")
print(f"\n  2-Step breach rate: {pct_exceed_2s:.3f}% of days = "
      f"~{pct_exceed_2s/100*252:.1f} days/year when both active")
print(f"  1-Step breach rate: {pct_exceed_1s:.3f}% of days = "
      f"~{pct_exceed_1s/100*252:.1f} days/year when both active")

# Historical: actual combined P&L on days BOTH pairs had active trades
both_active_mask = (eu_daily != 0) & (uj_daily != 0)
if both_active_mask.sum() > 10:
    both_active_days = cb_daily[both_active_mask]
    print(f"\n  ACTUAL historical days BOTH pairs active: {both_active_mask.sum()}")
    print(f"  Worst combined day (both active): ${both_active_days.min():+,.0f}")
    print(f"  % of those days negative: {(both_active_days<0).mean()*100:.1f}%")
    print(f"  vs $5k daily limit: "
          f"{'✅ Never breached' if abs(both_active_days.min())<DAILY_2S else '⚠️  Breach occurred'}")

# ── VERDICT ─────────────────────────────────────────────────────────────────
eu_streaks  = consec_losses(eu['pnl'].values)
uj_streaks  = consec_losses(uj['pnl'].values)
eu_worst10  = min(sum(eu['pnl'].values[i:i+10]) for i in range(len(eu)-9))
uj_worst10  = min(sum(uj['pnl'].values[i:i+10]) for i in range(len(uj)-9))
eu_worst_day= eu_daily.min()
uj_worst_day= uj_daily.min()
cb_worst_day= cb_daily.min()
cb_breach   = (cb_daily < -DAILY_2S).sum()

print(f"\n{'='*72}")
print("  VERDICT")
print(f"{'='*72}")
print(f"""
  EURUSD:
    Max consecutive losses: {max(eu_streaks) if eu_streaks else 0}
    Worst 10-trade window:  ${eu_worst10:+,.0f}
    Worst single day:       ${eu_worst_day:+,.0f}  {'✅' if abs(eu_worst_day)<DAILY_2S else '⚠️'}

  USDJPY:
    Max consecutive losses: {max(uj_streaks) if uj_streaks else 0}
    Worst 10-trade window:  ${uj_worst10:+,.0f}
    Worst single day:       ${uj_worst_day:+,.0f}  {'✅' if abs(uj_worst_day)<DAILY_2S else '⚠️'}

  Combined:
    Worst single day (actual backtest): ${cb_worst_day:+,.0f}  
    {'✅ Never breached 2-Step $5k daily limit' if cb_breach==0
     else f'⚠️  {cb_breach} days exceeded $5k limit'}
    
    MC intraday sim (both pairs active):
    2-Step daily breach rate: {pct_exceed_2s:.3f}%
    {'✅ < 0.5% breach rate — FTMO is safe' if pct_exceed_2s < 0.5
     else '⚠️  Breach rate elevated — review position sizing'}
    
    % of days in warning zone (>80% of daily limit):
    2-Step: {pct_above_80p_2s:.2f}%  1-Step: {pct_above_80p_1s:.2f}%

  FTMO reality check:
    The 100% pass rate claim is supported ONLY if:
    1. Combined worst day is within the daily limit ✅/⚠️ above
    2. MC breach rate is near-zero ✅/⚠️ above
    3. Worst 10-trade window is within total limit ✅/⚠️ above
    
    If all three pass → 100% FTMO pass rate is justified ✅
    If any fail → the 100% figure needs a caveat ⚠️
""")
