#!/usr/bin/env python3
"""
Pure Regime-Based Strategy Backtest
Trades based on regime transitions ONLY - no ML models (they collapsed to constants).

Strategy:
- Enter LONG when regime transitions to bullish (confirmed by duration)
- Enter SHORT when regime transitions to bearish (confirmed by duration)
- Exit to FLAT when regime changes to ranging/scalper/volatile
- Stop loss and take profit rules
"""

import numpy as np
import pandas as pd
from pathlib import Path
from datetime import datetime
from dataclasses import dataclass
from typing import List, Tuple

DATA_FILE = Path('/var/www/html/bestrading.cuttalo.com/scripts/prices_BTC_EUR_2025_full.csv')

@dataclass
class TradeConfig:
    initial_capital: float = 10000
    fee_rate: float = 0.002  # 0.2% per trade (Kraken taker)
    slippage: float = 0.0003  # 0.03%
    position_size_pct: float = 0.20  # 20% of capital per trade
    stop_loss_pct: float = 0.025  # 2.5% stop loss
    take_profit_pct: float = 0.05  # 5% take profit
    min_regime_confirm: int = 60  # Minutes regime must persist
    min_hold_time: int = 60  # Minimum hold time in minutes
    cooldown_after_trade: int = 120  # Minutes before next trade

def compute_features(prices: np.ndarray, idx: int) -> dict:
    """Compute market features at index."""
    if idx < 60:
        return None

    features = {}

    # Returns
    for period in [5, 10, 20, 60]:
        features[f'ret{period}'] = (prices[idx] - prices[idx - period]) / prices[idx - period] if idx >= period else 0

    # Volatility
    price_window = prices[max(0, idx-30):idx+1]
    if len(price_window) > 1:
        ret1 = np.diff(price_window) / price_window[:-1]
        features['vol10'] = np.std(ret1[-10:]) if len(ret1) >= 10 else np.std(ret1)
        features['vol30'] = np.std(ret1)
    else:
        features['vol10'] = features['vol30'] = 0

    # Moving averages
    for period in [10, 30, 60]:
        if idx >= period:
            ma = np.mean(prices[idx-period:idx])
            features[f'ma{period}_ratio'] = (prices[idx] - ma) / ma
        else:
            features[f'ma{period}_ratio'] = 0

    return features

def detect_regime(features: dict) -> str:
    """Detect market regime with tuned thresholds."""
    if features is None:
        return 'scalper'

    ret20 = features['ret20']
    ret60 = features['ret60']
    vol30 = features['vol30']

    # Wider thresholds for clearer signals
    if vol30 > 0.015:
        return 'volatile'
    elif ret20 > 0.004 and ret60 > 0.003:
        return 'bullish'
    elif ret20 < -0.004 and ret60 < -0.003:
        return 'bearish'
    elif abs(ret60) < 0.001 and vol30 < 0.003:
        return 'ranging'
    else:
        return 'scalper'

def calculate_regime_strength(features: dict, regime: str) -> float:
    """Calculate how strong the regime signal is (0-1)."""
    if regime == 'bullish':
        return min(1.0, (features['ret20'] + features['ret60']) / 0.02)
    elif regime == 'bearish':
        return min(1.0, abs(features['ret20'] + features['ret60']) / 0.02)
    elif regime == 'volatile':
        return min(1.0, features['vol30'] / 0.03)
    else:
        return 0.5

def run_backtest(config: TradeConfig):
    print("=" * 70)
    print("PURE REGIME-BASED STRATEGY")
    print("=" * 70)
    print(f"  Position Size: {config.position_size_pct*100:.0f}% of capital")
    print(f"  Stop Loss: {config.stop_loss_pct*100:.1f}%")
    print(f"  Take Profit: {config.take_profit_pct*100:.1f}%")
    print(f"  Fees: {config.fee_rate*100:.2f}% per trade")

    # Load data
    df = pd.read_csv(DATA_FILE)
    prices = df['close'].values.astype(np.float32)
    timestamps = df['timestamp'].values

    print(f"\n  Data: {len(prices):,} candles")
    print(f"  Period: {datetime.fromtimestamp(timestamps[0])} - {datetime.fromtimestamp(timestamps[-1])}")
    print(f"  BTC Range: €{prices.min():.0f} - €{prices.max():.0f}")

    # State
    capital = config.initial_capital
    position = 0  # -1 short, 0 flat, 1 long
    btc_held = 0.0
    entry_price = 0.0
    entry_idx = 0

    current_regime = 'scalper'
    regime_start_idx = 61
    last_trade_idx = 0

    trades = []
    equity_curve = []
    regime_history = []

    # Stats
    regime_counts = {'bullish': 0, 'bearish': 0, 'ranging': 0, 'volatile': 0, 'scalper': 0}

    for i in range(61, len(prices)):
        price = prices[i]
        features = compute_features(prices, i)

        if features is None:
            continue

        regime = detect_regime(features)
        regime_counts[regime] += 1

        # Track regime changes
        if regime != current_regime:
            regime_history.append((i, current_regime, regime))
            current_regime = regime
            regime_start_idx = i

        time_in_regime = i - regime_start_idx
        time_since_trade = i - last_trade_idx
        time_in_position = i - entry_idx if position != 0 else 0

        action = None

        # ENTRY LOGIC
        if position == 0 and time_since_trade >= config.cooldown_after_trade:
            # Long entry on confirmed bullish
            if regime == 'bullish' and time_in_regime >= config.min_regime_confirm:
                strength = calculate_regime_strength(features, regime)
                if strength > 0.5:
                    action = 'LONG'

            # Short entry on confirmed bearish
            elif regime == 'bearish' and time_in_regime >= config.min_regime_confirm:
                strength = calculate_regime_strength(features, regime)
                if strength > 0.5:
                    action = 'SHORT'

        # EXIT LOGIC
        elif position != 0:
            pnl_pct = (price - entry_price) / entry_price if position == 1 else (entry_price - price) / entry_price

            # Stop loss
            if pnl_pct < -config.stop_loss_pct:
                action = 'STOP_LOSS'

            # Take profit
            elif pnl_pct > config.take_profit_pct:
                action = 'TAKE_PROFIT'

            # Regime change exit (after min hold)
            elif time_in_position >= config.min_hold_time:
                # Exit long if no longer bullish
                if position == 1 and regime in ['bearish', 'volatile'] and time_in_regime >= 30:
                    action = 'REGIME_EXIT'
                # Exit short if no longer bearish
                elif position == -1 and regime in ['bullish', 'volatile'] and time_in_regime >= 30:
                    action = 'REGIME_EXIT'

        # Execute trades
        if action == 'LONG':
            trade_value = capital * config.position_size_pct
            fee = trade_value * config.fee_rate
            btc_amount = (trade_value - fee) / (price * (1 + config.slippage))

            capital -= trade_value
            btc_held = btc_amount
            position = 1
            entry_price = price * (1 + config.slippage)
            entry_idx = i
            last_trade_idx = i

            trades.append({
                'time': timestamps[i],
                'action': 'LONG',
                'price': price,
                'btc': btc_amount,
                'regime': regime
            })

        elif action == 'SHORT':
            # Simulate short with margin (track entry price for PnL)
            trade_value = capital * config.position_size_pct
            fee = trade_value * config.fee_rate

            capital -= fee  # Fee for opening
            position = -1
            entry_price = price * (1 - config.slippage)  # Entry at worse price
            entry_idx = i
            last_trade_idx = i
            btc_held = trade_value / entry_price  # Amount we're short

            trades.append({
                'time': timestamps[i],
                'action': 'SHORT',
                'price': price,
                'btc': btc_held,
                'regime': regime
            })

        elif action in ['STOP_LOSS', 'TAKE_PROFIT', 'REGIME_EXIT']:
            if position == 1:  # Close long
                proceeds = btc_held * price * (1 - config.slippage)
                fee = proceeds * config.fee_rate
                pnl = proceeds - fee - (btc_held * entry_price)
                capital += proceeds - fee
            else:  # Close short
                # Short profit = (entry_price - exit_price) * btc_amount
                exit_price = price * (1 + config.slippage)
                pnl_per_btc = entry_price - exit_price
                gross_pnl = pnl_per_btc * btc_held
                fee = abs(btc_held * exit_price) * config.fee_rate
                pnl = gross_pnl - fee
                capital += capital * config.position_size_pct + pnl  # Return margin + PnL

            trades.append({
                'time': timestamps[i],
                'action': action,
                'price': price,
                'pnl': pnl,
                'regime': regime
            })

            position = 0
            btc_held = 0
            entry_price = 0
            entry_idx = 0
            last_trade_idx = i

        # Track equity
        if position == 0:
            equity = capital
        elif position == 1:
            equity = capital + btc_held * price
        else:  # Short position
            current_price = price
            unrealized_pnl = (entry_price - current_price) * btc_held
            equity = capital + capital * config.position_size_pct + unrealized_pnl

        equity_curve.append((timestamps[i], equity))

        if i % 100000 == 0:
            print(f"  Progress: {i:,}/{len(prices):,} | Equity: €{equity:,.0f} | Trades: {len(trades)}")

    # Close any open position
    if position != 0:
        if position == 1:
            proceeds = btc_held * prices[-1] * (1 - config.slippage)
            fee = proceeds * config.fee_rate
            pnl = proceeds - fee - (btc_held * entry_price)
            capital += proceeds - fee
        else:
            exit_price = prices[-1] * (1 + config.slippage)
            pnl_per_btc = entry_price - exit_price
            gross_pnl = pnl_per_btc * btc_held
            fee = abs(btc_held * exit_price) * config.fee_rate
            pnl = gross_pnl - fee
            capital += capital * config.position_size_pct + pnl

        trades.append({
            'time': timestamps[-1],
            'action': 'FINAL_CLOSE',
            'price': prices[-1],
            'pnl': pnl
        })

    final_equity = capital

    # Metrics
    total_return = (final_equity - config.initial_capital) / config.initial_capital * 100

    if equity_curve:
        max_equity = max(e[1] for e in equity_curve)
        max_drawdown = min((e[1] - max_equity) / max_equity for e in equity_curve) * 100
    else:
        max_drawdown = 0

    # Trade analysis
    closed_trades = [t for t in trades if 'pnl' in t]
    winning = [t for t in closed_trades if t['pnl'] > 0]
    losing = [t for t in closed_trades if t['pnl'] <= 0]

    win_rate = len(winning) / len(closed_trades) * 100 if closed_trades else 0
    avg_win = np.mean([t['pnl'] for t in winning]) if winning else 0
    avg_loss = np.mean([t['pnl'] for t in losing]) if losing else 0

    # Compare to BTC
    btc_return = (prices[-1] - prices[0]) / prices[0] * 100

    # Print results
    print("\n" + "=" * 70)
    print("RESULTS")
    print("=" * 70)

    print(f"""
    Initial Capital:      €{config.initial_capital:,.0f}
    Final Capital:        €{final_equity:,.0f}
    ─────────────────────────────────────────
    Total Return:         {total_return:+.2f}%
    Max Drawdown:         {max_drawdown:.2f}%
    ─────────────────────────────────────────
    Total Trades:         {len(closed_trades)}
    Winning:              {len(winning)} ({win_rate:.1f}%)
    Losing:               {len(losing)}
    Avg Win:              €{avg_win:+.2f}
    Avg Loss:             €{avg_loss:+.2f}
    ─────────────────────────────────────────
    BTC Buy & Hold:       {btc_return:+.2f}%
    Strategy Alpha:       {total_return - btc_return:+.2f}%
    ─────────────────────────────────────────
    """)

    # Regime stats
    print("    Regime Distribution:")
    for r, c in sorted(regime_counts.items(), key=lambda x: -x[1]):
        pct = c / sum(regime_counts.values()) * 100
        print(f"       {r:12s}: {c:6,} ({pct:5.1f}%)")

    # Trade history
    if trades:
        print("\n    Trade History:")
        for t in trades:
            dt = datetime.fromtimestamp(t['time'])
            pnl_str = f"PnL: €{t['pnl']:+.2f}" if 'pnl' in t else f"BTC: {t.get('btc', 0):.4f}"
            regime_str = t.get('regime', '')
            print(f"       {dt.strftime('%Y-%m-%d %H:%M')} | {t['action']:14s} | €{t['price']:,.0f} | {pnl_str:14s} | {regime_str}")

    return {
        'total_return': total_return,
        'max_drawdown': max_drawdown,
        'trades': len(closed_trades),
        'win_rate': win_rate,
        'alpha': total_return - btc_return
    }

if __name__ == '__main__':
    # Test with default config
    config = TradeConfig()
    results = run_backtest(config)

    # Try with more aggressive settings
    print("\n\n" + "=" * 70)
    print("TESTING ALTERNATIVE CONFIGURATIONS")
    print("=" * 70)

    configs = [
        ("Conservative", TradeConfig(position_size_pct=0.10, stop_loss_pct=0.015, take_profit_pct=0.03)),
        ("Aggressive", TradeConfig(position_size_pct=0.30, stop_loss_pct=0.03, take_profit_pct=0.08)),
        ("Quick Trades", TradeConfig(position_size_pct=0.15, stop_loss_pct=0.02, take_profit_pct=0.04, min_hold_time=30, cooldown_after_trade=60)),
    ]

    for name, cfg in configs:
        print(f"\n--- {name} ---")
        r = run_backtest(cfg)
        print(f"    Return: {r['total_return']:+.2f}% | DD: {r['max_drawdown']:.2f}% | Trades: {r['trades']} | WR: {r['win_rate']:.1f}% | Alpha: {r['alpha']:+.2f}%")
