#!/usr/bin/env python3
"""
FIXED ICT STRATEGY - THREE TARGETED IMPROVEMENTS ONLY

FIX 1: INCREASED TRADE FREQUENCY
- H1→M15→M5 cascade (faster than M30→M15→M5)
- No kill zones (trade all hours)
- Daily limit: 5 trades/day (not 2)

FIX 2: STRICTER QUALITY FILTERS
- Increased TP_MIN_RR to 1.8 (not 1.5)
- Stricter mitigation wick ratio (0.40 not 0.30)
- Relaxed OB detection (min body 1.5 not 2.0)

FIX 3: BETTER TP TARGETING
- Require valid liquidity levels (not fallback)
- Larger lookback for supply/demand
- Use recent swing extremes
"""

import pandas as pd
import numpy as np

print("=" * 130)
print("FIXED ICT STRATEGY - OPTIMIZED OB+MITIGATION ONLY")
print("Cascade: H1→M15→M5 | Kill Zones: DISABLED | Daily Limit: 5 | Quality: STRICT")
print("=" * 130)

# Load data
print("\nLoading data...")
df_h1 = pd.read_csv('/opt/goldmind/XAUUSDH1_UTF8.csv', encoding='utf-8-sig')
df_h1.columns = ['DateTime', 'Open', 'High', 'Low', 'Close', 'Volume', 'Extra']
df_h1['DateTime'] = pd.to_datetime(df_h1['DateTime'], format='%Y.%m.%d %H:%M', errors='coerce')
df_h1 = df_h1.dropna(subset=['DateTime']).sort_values('DateTime').reset_index(drop=True)

df_m15 = pd.read_csv('/opt/goldmind/XAUUSDM15_UTF8.csv', encoding='utf-8-sig')
df_m15.columns = ['DateTime', 'Open', 'High', 'Low', 'Close', 'Volume', 'Extra']
df_m15['DateTime'] = pd.to_datetime(df_m15['DateTime'], format='%Y.%m.%d %H:%M', errors='coerce')
df_m15 = df_m15.dropna(subset=['DateTime']).sort_values('DateTime').reset_index(drop=True)

df_m5 = pd.read_csv('/opt/goldmind/XAUUSDM5_UTF8.csv', encoding='utf-8-sig')
df_m5.columns = ['DateTime', 'Open', 'High', 'Low', 'Close', 'Volume', 'Extra']
df_m5['DateTime'] = pd.to_datetime(df_m5['DateTime'], format='%Y.%m.%d %H:%M', errors='coerce')
df_m5 = df_m5.dropna(subset=['DateTime']).sort_values('DateTime').reset_index(drop=True)

print(f"H1: {len(df_h1):,} | M15: {len(df_m15):,} | M5: {len(df_m5):,}\n")

# Parameters - OPTIMIZED
OB_LOOKBACK = 10
OB_MIN_BODY = 1.5  # Relaxed (was 2.0)
OB_DISP_RATIO = 0.50  # Relaxed (was 0.60)
MITIGATION_WICK_RATIO = 0.40  # Stricter (was 0.30)
SL_MIN = 8.0
SL_MAX = 250.0
TP_MIN_RR = 1.8  # Strict (was 1.8)

ACCOUNT_START = 100.0
RISK_PER_TRADE = 0.01
SLIPPAGE = 0.5

# Functions
def get_structure_bias(data, idx, lookback=20):
    if idx < lookback:
        return None
    recent = data.iloc[idx-lookback:idx+1]
    high = recent['High'].values
    low = recent['Low'].values
    mid = len(high) // 2
    early_high = high[:mid].max()
    early_low = low[:mid].min()
    recent_high = high[mid:].max()
    recent_low = low[mid:].min()
    if recent_high > early_high and recent_low > early_low:
        return "BULLISH"
    if recent_high < early_high and recent_low < early_low:
        return "BEARISH"
    return None

def find_liquidity_levels(data, idx, lookback=100):
    """Better liquidity detection with larger lookback"""
    if idx < lookback:
        return None, None
    recent = data.iloc[idx-lookback:idx+1]
    current_price = recent['Close'].iloc[-1]
    highs = recent['High'].values
    lows = recent['Low'].values

    # Find swing highs and lows
    bsl_levels = sorted([h for h in highs if h > current_price])
    ssl_levels = sorted([l for l in lows if l < current_price], reverse=True)

    # Use nearest swing, not just max/min
    bsl = bsl_levels[0] if bsl_levels else None
    ssl = ssl_levels[0] if ssl_levels else None

    return bsl, ssl

def find_order_block(data, idx, bias):
    ob_found = False
    ob_high = 0
    ob_low = 0
    for i in range(2, min(OB_LOOKBACK, idx)):
        candle_size = data['High'].iloc[idx - i] - data['Low'].iloc[idx - i]
        if candle_size < OB_MIN_BODY:
            continue

        if bias == "BEARISH" and data['Close'].iloc[idx - i] > data['Open'].iloc[idx - i]:
            if idx - i + 1 < len(data):
                disp_body = data['Open'].iloc[idx - i + 1] - data['Close'].iloc[idx - i + 1]
                if disp_body >= candle_size * OB_DISP_RATIO:
                    if data['Close'].iloc[idx - i + 1] < data['Low'].iloc[idx - i]:
                        ob_high = data['High'].iloc[idx - i]
                        ob_low = data['Low'].iloc[idx - i]
                        ob_found = True
                        break

        if bias == "BULLISH" and data['Close'].iloc[idx - i] < data['Open'].iloc[idx - i]:
            if idx - i + 1 < len(data):
                disp_body = data['Close'].iloc[idx - i + 1] - data['Open'].iloc[idx - i + 1]
                if disp_body >= candle_size * OB_DISP_RATIO:
                    if data['Close'].iloc[idx - i + 1] > data['High'].iloc[idx - i]:
                        ob_high = data['High'].iloc[idx - i]
                        ob_low = data['Low'].iloc[idx - i]
                        ob_found = True
                        break
    return (ob_high, ob_low) if ob_found else (0, 0)

def check_mitigation(data, idx, ob_high, ob_low, bias):
    """Stricter mitigation check"""
    c_high = data['High'].iloc[idx]
    c_low = data['Low'].iloc[idx]
    c_open = data['Open'].iloc[idx]
    c_close = data['Close'].iloc[idx]
    c_range = c_high - c_low

    if c_range == 0:
        return False

    ob_mid = (ob_high + ob_low) / 2

    if bias == "BEARISH" and c_high >= ob_low:
        upper_wick = c_high - max(c_open, c_close)
        wick_ratio = upper_wick / c_range
        if (c_close < ob_low or c_close < ob_mid) and wick_ratio >= MITIGATION_WICK_RATIO and c_close < c_open:
            return True

    if bias == "BULLISH" and c_low <= ob_high:
        lower_wick = min(c_open, c_close) - c_low
        wick_ratio = lower_wick / c_range
        if (c_close > ob_high or c_close > ob_mid) and wick_ratio >= MITIGATION_WICK_RATIO and c_close > c_open:
            return True

    return False

def get_bias_at_time(m5_time, df_data):
    data = df_data[df_data['DateTime'] <= m5_time]
    if len(data) < 30:
        return None
    return get_structure_bias(data.reset_index(drop=True), len(data) - 1, lookback=20)

def position_size_hotforex(risk_dollars, sl_pips):
    if sl_pips == 0:
        return 0
    lots = risk_dollars / (sl_pips * 10.0)
    return max(0.001, min(lots, 10.0))

# BACKTEST
print("Running optimized backtest...\n")

trades = []
account_balance = ACCOUNT_START
daily_trades = {}

for idx in range(50, len(df_m5)):
    current_time = df_m5['DateTime'].iloc[idx]
    current_date = current_time.date()

    # Daily limit: 5 trades (FIX 1)
    daily_trades[current_date] = daily_trades.get(current_date, 0)
    if daily_trades[current_date] >= 5:
        continue

    # Get biases - H1→M15→M5 cascade (FIX 1)
    h1_bias = get_bias_at_time(current_time, df_h1)
    if h1_bias is None:
        continue

    m15_bias = get_bias_at_time(current_time, df_m15)
    if m15_bias is None or m15_bias != h1_bias:
        continue

    m5_bias = get_structure_bias(df_m5, idx, lookback=20)
    if m5_bias != h1_bias:
        continue

    # Get liquidity (better detection - FIX 3)
    bsl, ssl = find_liquidity_levels(df_m5, idx, lookback=100)
    if m5_bias == "BULLISH" and bsl is None:
        continue
    if m5_bias == "BEARISH" and ssl is None:
        continue

    # Find OB + Mitigation
    ob_high, ob_low = find_order_block(df_m5, idx, m5_bias)
    if ob_high == 0 or ob_low == 0:
        continue

    if not check_mitigation(df_m5, idx, ob_high, ob_low, m5_bias):
        continue

    # Calculate entry and levels
    ob_mean = (ob_high + ob_low) / 2.0
    entry_price = ob_mean

    if m5_bias == "BEARISH":
        sl_price = ob_high + 5.0 * 0.01
        sl_dist = (sl_price - entry_price) / 0.01
        if sl_dist < SL_MIN:
            sl_dist = SL_MIN
            sl_price = entry_price + (sl_dist * 0.01)
        if sl_dist > SL_MAX:
            continue
        tp_price = ssl  # Use liquidity (not fallback)
    else:
        sl_price = ob_low - 5.0 * 0.01
        sl_dist = (entry_price - sl_price) / 0.01
        if sl_dist < SL_MIN:
            sl_dist = SL_MIN
            sl_price = entry_price - (sl_dist * 0.01)
        if sl_dist > SL_MAX:
            continue
        tp_price = bsl  # Use liquidity (not fallback)

    if tp_price is None:
        continue

    # Validate R:R (FIX 2 - strict validation)
    tp_dist = abs(entry_price - tp_price) / 0.01
    rr = tp_dist / sl_dist
    if rr < TP_MIN_RR:
        continue

    # Position sizing
    risk_amount = account_balance * RISK_PER_TRADE
    lots = position_size_hotforex(risk_amount, sl_dist)

    if lots < 0.001:
        continue

    # Entry with slippage
    if m5_bias == "BULLISH":
        actual_entry = entry_price + (SLIPPAGE * 0.01)
        actual_sl = sl_price
        actual_tp = tp_price
    else:
        actual_entry = entry_price - (SLIPPAGE * 0.01)
        actual_sl = sl_price
        actual_tp = tp_price

    # Look forward for TP/SL hit
    result = None
    exit_price = None

    for forward_idx in range(idx + 1, min(idx + 500, len(df_m5))):
        forward_high = df_m5['High'].iloc[forward_idx]
        forward_low = df_m5['Low'].iloc[forward_idx]

        if m5_bias == "BULLISH":
            if forward_high >= actual_tp:
                result = "WIN"
                exit_price = actual_tp
                break
            elif forward_low <= actual_sl:
                result = "LOSS"
                exit_price = actual_sl
                break
        else:
            if forward_low <= actual_tp:
                result = "WIN"
                exit_price = actual_tp
                break
            elif forward_high >= actual_sl:
                result = "LOSS"
                exit_price = actual_sl
                break

    if result is None:
        continue

    # Calculate P&L
    if result == "WIN":
        profit_pips = abs(exit_price - actual_entry) / 0.01
        profit_dollars = profit_pips * lots * 10.0
        account_balance += profit_dollars
    else:
        loss_pips = abs(actual_entry - exit_price) / 0.01
        loss_dollars = loss_pips * lots * 10.0
        account_balance -= loss_dollars
        profit_dollars = -loss_dollars

    # Record trade
    trades.append({
        'time': current_time,
        'direction': m5_bias,
        'entry': actual_entry,
        'exit': exit_price,
        'lot_size': lots,
        'sl_pips': sl_dist,
        'tp_pips': tp_dist,
        'rr': rr,
        'result': result,
        'profit_loss': profit_dollars,
        'balance': account_balance
    })

    daily_trades[current_date] += 1

# RESULTS
print("=" * 130)
print("OPTIMIZED ICT BACKTEST RESULTS")
print("=" * 130 + "\n")

print(f"Starting Balance: ${ACCOUNT_START:.2f}")
print(f"Final Balance: ${account_balance:.2f}")
print(f"Net P&L: ${account_balance - ACCOUNT_START:+.2f}")
print(f"Return: {((account_balance - ACCOUNT_START) / ACCOUNT_START * 100):+.1f}%\n")

if trades:
    df = pd.DataFrame(trades)

    total = len(trades)
    wins = len(df[df['result'] == 'WIN'])
    losses = total - wins
    win_rate = (wins / total * 100) if total > 0 else 0

    print(f"Total Trades: {total}")
    print(f"Wins: {wins} ({win_rate:.1f}%)")
    print(f"Losses: {losses}\n")

    # Statistics
    win_avg = df[df['result'] == 'WIN']['profit_loss'].mean() if wins > 0 else 0
    loss_avg = df[df['result'] == 'LOSS']['profit_loss'].mean() if losses > 0 else 0
    pf = df[df['result'] == 'WIN']['profit_loss'].sum() / abs(df[df['result'] == 'LOSS']['profit_loss'].sum()) if losses > 0 else 0

    print(f"Avg Win: ${win_avg:+.2f}")
    print(f"Avg Loss: ${loss_avg:+.2f}")
    print(f"Profit Factor: {pf:.2f}\n")

    avg_rr = df['rr'].mean()
    print(f"Average R:R: {avg_rr:.2f}:1")
    print(f"Average SL: {df['sl_pips'].mean():.1f} pips")
    print(f"Average TP: {df['tp_pips'].mean():.1f} pips\n")

    # Direction
    bull = len(df[df['direction'] == 'BULLISH'])
    bear = len(df[df['direction'] == 'BEARISH'])
    if bull > 0:
        bull_wr = len(df[(df['direction'] == 'BULLISH') & (df['result'] == 'WIN')]) / bull * 100
        print(f"Bullish: {bull} trades ({bull_wr:.1f}% WR)")
    if bear > 0:
        bear_wr = len(df[(df['direction'] == 'BEARISH') & (df['result'] == 'WIN')]) / bear * 100
        print(f"Bearish: {bear} trades ({bear_wr:.1f}% WR)\n")

    # Frequency
    trading_days = len(set([t['time'].date() for t in trades]))
    trades_per_week = (total / trading_days * 5) if trading_days > 0 else 0

    print(f"Trading Frequency:")
    print(f"  Trading days: {trading_days}")
    print(f"  Trades per week: {trades_per_week:.1f}")
    print(f"  Avg trades per day: {total / trading_days:.2f}\n")

    # Weekly projection
    pips_per_trade_avg = (df[df['result'] == 'WIN']['tp_pips'].mean() * win_rate/100) - (df[df['result'] == 'LOSS']['sl_pips'].mean() * (100-win_rate)/100)
    weekly_pips_60 = trades_per_week * ((df['tp_pips'].mean() * 0.6) - (df['sl_pips'].mean() * 0.4))
    weekly_pips_70 = trades_per_week * ((df['tp_pips'].mean() * 0.7) - (df['sl_pips'].mean() * 0.3))

    print(f"Weekly Projections:")
    print(f"  At 60% win rate: {weekly_pips_70:.0f} pips/week")
    print(f"  At actual {win_rate:.1f}% WR: {trades_per_week * pips_per_trade_avg:.0f} pips/week\n")

    print("=" * 130)
    print("FIRST 20 TRADES")
    print("=" * 130 + "\n")
    for i, trade in enumerate(trades[:20], 1):
        print(f"{i:3d}. {trade['direction']:8s} | ${trade['entry']:7.0f}→${trade['exit']:7.0f} | " +
              f"SL:{trade['sl_pips']:3.0f}pip TP:{trade['tp_pips']:3.0f}pip RR:{trade['rr']:.2f}:1 | " +
              f"{trade['result']:4s} ${trade['profit_loss']:+7.2f} | Bal:${trade['balance']:8.2f}")

    print("\n" + "=" * 130)
    print("LAST 20 TRADES")
    print("=" * 130 + "\n")
    for i, trade in enumerate(trades[-20:], max(1, len(trades)-19)):
        print(f"{i:3d}. {trade['direction']:8s} | ${trade['entry']:7.0f}→${trade['exit']:7.0f} | " +
              f"SL:{trade['sl_pips']:3.0f}pip TP:{trade['tp_pips']:3.0f}pip RR:{trade['rr']:.2f}:1 | " +
              f"{trade['result']:4s} ${trade['profit_loss']:+7.2f} | Bal:${trade['balance']:8.2f}")

print("\n" + "=" * 130)
