#!/usr/bin/env python3
"""
Optimized Regime-Based Strategy with Parameter Search
Goal: Find the best threshold/parameter combination for maximum profitability.
"""

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

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

@dataclass
class Config:
    # Position sizing
    position_size_pct: float = 0.15
    stop_loss_pct: float = 0.02
    take_profit_pct: float = 0.04
    fee_rate: float = 0.002
    slippage: float = 0.0003

    # Regime detection thresholds
    bullish_ret20: float = 0.002
    bullish_ret60: float = 0.001
    bearish_ret20: float = -0.002
    bearish_ret60: float = -0.001
    volatile_vol: float = 0.015

    # Timing
    min_regime_confirm: int = 30
    min_hold_time: int = 30
    cooldown: int = 60

def compute_features(prices: np.ndarray, idx: int) -> dict:
    if idx < 60:
        return None
    features = {}
    for period in [5, 10, 20, 60]:
        features[f'ret{period}'] = (prices[idx] - prices[idx - period]) / prices[idx - period] if idx >= period else 0
    price_window = prices[max(0, idx-30):idx+1]
    if len(price_window) > 1:
        ret1 = np.diff(price_window) / price_window[:-1]
        features['vol30'] = np.std(ret1)
    else:
        features['vol30'] = 0
    return features

def detect_regime(features: dict, cfg: Config) -> str:
    if features is None:
        return 'scalper'
    ret20 = features['ret20']
    ret60 = features['ret60']
    vol30 = features['vol30']

    if vol30 > cfg.volatile_vol:
        return 'volatile'
    elif ret20 > cfg.bullish_ret20 and ret60 > cfg.bullish_ret60:
        return 'bullish'
    elif ret20 < cfg.bearish_ret20 and ret60 < cfg.bearish_ret60:
        return 'bearish'
    else:
        return 'scalper'

def run_simulation(prices, timestamps, cfg: Config, verbose=False):
    capital = 10000
    position = 0
    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 = []

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

        regime = detect_regime(features, cfg)

        if regime != current_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
        if position == 0 and time_since_trade >= cfg.cooldown:
            if regime == 'bullish' and time_in_regime >= cfg.min_regime_confirm:
                action = 'LONG'
            elif regime == 'bearish' and time_in_regime >= cfg.min_regime_confirm:
                action = 'SHORT'

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

            if pnl_pct < -cfg.stop_loss_pct:
                action = 'STOP_LOSS'
            elif pnl_pct > cfg.take_profit_pct:
                action = 'TAKE_PROFIT'
            elif time_in_position >= cfg.min_hold_time:
                if position == 1 and regime in ['bearish', 'volatile'] and time_in_regime >= 15:
                    action = 'REGIME_EXIT'
                elif position == -1 and regime in ['bullish', 'volatile'] and time_in_regime >= 15:
                    action = 'REGIME_EXIT'

        # Execute
        if action == 'LONG':
            trade_value = capital * cfg.position_size_pct
            fee = trade_value * cfg.fee_rate
            btc_amount = (trade_value - fee) / (price * (1 + cfg.slippage))
            capital -= trade_value
            btc_held = btc_amount
            position = 1
            entry_price = price * (1 + cfg.slippage)
            entry_idx = i
            last_trade_idx = i
            trades.append({'action': 'LONG', 'time': i})

        elif action == 'SHORT':
            trade_value = capital * cfg.position_size_pct
            fee = trade_value * cfg.fee_rate
            capital -= fee
            position = -1
            entry_price = price * (1 - cfg.slippage)
            entry_idx = i
            last_trade_idx = i
            btc_held = trade_value / entry_price
            trades.append({'action': 'SHORT', 'time': i})

        elif action in ['STOP_LOSS', 'TAKE_PROFIT', 'REGIME_EXIT']:
            if position == 1:
                proceeds = btc_held * price * (1 - cfg.slippage)
                fee = proceeds * cfg.fee_rate
                pnl = proceeds - fee - (btc_held * entry_price)
                capital += proceeds - fee
            else:
                exit_price = price * (1 + cfg.slippage)
                pnl_per_btc = entry_price - exit_price
                gross_pnl = pnl_per_btc * btc_held
                fee = abs(btc_held * exit_price) * cfg.fee_rate
                pnl = gross_pnl - fee
                capital += capital * cfg.position_size_pct + pnl

            trades.append({'action': action, 'pnl': pnl, 'time': i})
            position = 0
            btc_held = 0
            entry_price = 0
            entry_idx = 0
            last_trade_idx = i

        # Equity
        if position == 0:
            equity = capital
        elif position == 1:
            equity = capital + btc_held * price
        else:
            unrealized_pnl = (entry_price - price) * btc_held
            equity = capital + capital * cfg.position_size_pct + unrealized_pnl

        equity_curve.append(equity)

    # Close final
    if position != 0:
        if position == 1:
            proceeds = btc_held * prices[-1] * (1 - cfg.slippage)
            fee = proceeds * cfg.fee_rate
            pnl = proceeds - fee - (btc_held * entry_price)
            capital += proceeds - fee
        else:
            exit_price = prices[-1] * (1 + cfg.slippage)
            pnl_per_btc = entry_price - exit_price
            gross_pnl = pnl_per_btc * btc_held
            fee = abs(btc_held * exit_price) * cfg.fee_rate
            pnl = gross_pnl - fee
            capital += capital * cfg.position_size_pct + pnl
        trades.append({'action': 'FINAL', 'pnl': pnl})

    # Metrics
    final_equity = capital
    total_return = (final_equity - 10000) / 10000 * 100

    if equity_curve:
        max_eq = max(equity_curve)
        max_dd = min((e - max_eq) / max_eq for e in equity_curve) * 100
    else:
        max_dd = 0

    closed = [t for t in trades if 'pnl' in t]
    winning = [t for t in closed if t['pnl'] > 0]
    win_rate = len(winning) / len(closed) * 100 if closed else 0

    return {
        'return': total_return,
        'max_dd': max_dd,
        'trades': len(closed),
        'win_rate': win_rate
    }

def main():
    print("Loading data...")
    df = pd.read_csv(DATA_FILE)
    prices = df['close'].values.astype(np.float32)
    timestamps = df['timestamp'].values

    btc_return = (prices[-1] - prices[0]) / prices[0] * 100
    print(f"Data: {len(prices):,} candles | BTC return: {btc_return:+.2f}%")

    print("\nGrid search for optimal parameters...")
    print("=" * 80)

    # Parameter grid
    bullish_thresholds = [0.001, 0.0015, 0.002, 0.003]
    bearish_thresholds = [-0.001, -0.0015, -0.002, -0.003]
    volatile_thresholds = [0.01, 0.012, 0.015, 0.02]
    position_sizes = [0.10, 0.15, 0.20, 0.25]
    stop_losses = [0.015, 0.02, 0.025, 0.03]
    take_profits = [0.03, 0.04, 0.05, 0.06]
    confirm_times = [15, 30, 60]

    results = []
    total_combinations = len(bullish_thresholds) * len(position_sizes) * len(stop_losses) * len(take_profits) * len(confirm_times)

    print(f"Testing {total_combinations} combinations...")

    count = 0
    for bull_ret in bullish_thresholds:
        for pos_size in position_sizes:
            for sl in stop_losses:
                for tp in take_profits:
                    for confirm in confirm_times:
                        cfg = Config(
                            position_size_pct=pos_size,
                            stop_loss_pct=sl,
                            take_profit_pct=tp,
                            bullish_ret20=bull_ret,
                            bullish_ret60=bull_ret * 0.5,
                            bearish_ret20=-bull_ret,
                            bearish_ret60=-bull_ret * 0.5,
                            min_regime_confirm=confirm,
                            min_hold_time=confirm
                        )

                        r = run_simulation(prices, timestamps, cfg)

                        # Score: high return, low drawdown, reasonable trades
                        score = r['return'] - abs(r['max_dd']) * 0.5
                        if r['trades'] < 2:
                            score -= 10  # Penalize too few trades

                        results.append({
                            'config': cfg,
                            'result': r,
                            'score': score
                        })

                        count += 1
                        if count % 100 == 0:
                            print(f"  Progress: {count}/{total_combinations}")

    # Sort by score
    results.sort(key=lambda x: x['score'], reverse=True)

    print("\n" + "=" * 80)
    print("TOP 10 CONFIGURATIONS")
    print("=" * 80)

    for i, r in enumerate(results[:10]):
        cfg = r['config']
        res = r['result']
        print(f"\n#{i+1} Score: {r['score']:.2f}")
        print(f"   Return: {res['return']:+.2f}% | MaxDD: {res['max_dd']:.2f}% | Trades: {res['trades']} | WinRate: {res['win_rate']:.1f}%")
        print(f"   Position: {cfg.position_size_pct*100:.0f}% | SL: {cfg.stop_loss_pct*100:.1f}% | TP: {cfg.take_profit_pct*100:.1f}%")
        print(f"   Bullish threshold: {cfg.bullish_ret20:.4f} | Confirm: {cfg.min_regime_confirm}min")

    # Run detailed backtest with best config
    best = results[0]
    print("\n" + "=" * 80)
    print("BEST CONFIGURATION DETAILS")
    print("=" * 80)

    cfg = best['config']
    print(f"""
Configuration:
  Position Size:     {cfg.position_size_pct*100:.0f}%
  Stop Loss:         {cfg.stop_loss_pct*100:.1f}%
  Take Profit:       {cfg.take_profit_pct*100:.1f}%
  Bullish Threshold: ret20 > {cfg.bullish_ret20:.4f}, ret60 > {cfg.bullish_ret60:.4f}
  Bearish Threshold: ret20 < {cfg.bearish_ret20:.4f}, ret60 < {cfg.bearish_ret60:.4f}
  Regime Confirm:    {cfg.min_regime_confirm} minutes
  Min Hold Time:     {cfg.min_hold_time} minutes
  Cooldown:          {cfg.cooldown} minutes

Results:
  Total Return:      {best['result']['return']:+.2f}%
  Max Drawdown:      {best['result']['max_dd']:.2f}%
  Total Trades:      {best['result']['trades']}
  Win Rate:          {best['result']['win_rate']:.1f}%
  Alpha vs BTC:      {best['result']['return'] - btc_return:+.2f}%
    """)

    # Save best config
    import json
    output = {
        'best_config': {
            'position_size_pct': cfg.position_size_pct,
            'stop_loss_pct': cfg.stop_loss_pct,
            'take_profit_pct': cfg.take_profit_pct,
            'bullish_ret20': cfg.bullish_ret20,
            'bullish_ret60': cfg.bullish_ret60,
            'bearish_ret20': cfg.bearish_ret20,
            'bearish_ret60': cfg.bearish_ret60,
            'min_regime_confirm': cfg.min_regime_confirm,
            'min_hold_time': cfg.min_hold_time,
            'cooldown': cfg.cooldown
        },
        'results': {
            'total_return': best['result']['return'],
            'max_drawdown': best['result']['max_dd'],
            'trades': best['result']['trades'],
            'win_rate': best['result']['win_rate'],
            'btc_return': btc_return,
            'alpha': best['result']['return'] - btc_return
        }
    }

    with open('/var/www/html/bestrading.cuttalo.com/models/btc_v6/best_regime_config.json', 'w') as f:
        json.dump(output, f, indent=2)

    print(f"Saved best config to best_regime_config.json")

if __name__ == '__main__':
    main()
