#!/usr/bin/env python3
"""
Profitable Strategy - Based on what actually works
==================================================
RSI Mean Reversion + Momentum Filter
"""

import pandas as pd
import numpy as np
from datetime import datetime
from pathlib import Path
import json
from itertools import product

DATA_DIR = Path('/var/www/html/pippo.cuttalo.com/data')
RESULTS_DIR = Path('/var/www/html/pippo.cuttalo.com/optimization_results')

INITIAL_CAPITAL = 5000  # €5000 totale


def load_data():
    dfs = []
    for f in sorted(DATA_DIR.glob('prices_BTC_EUR_*.csv')):
        df = pd.read_csv(f)
        if df['timestamp'].dtype == 'int64' or str(df['timestamp'].iloc[0]).isdigit():
            ts = pd.to_numeric(df['timestamp'])
            if ts.iloc[0] > 1e12:
                df['timestamp'] = pd.to_datetime(ts, unit='ms')
            else:
                df['timestamp'] = pd.to_datetime(ts, unit='s')
        else:
            df['timestamp'] = pd.to_datetime(df['timestamp'])
        df = df[['timestamp', 'open', 'high', 'low', 'close', 'volume']]
        dfs.append(df)
    df = pd.concat(dfs, ignore_index=True)
    return df.sort_values('timestamp').drop_duplicates(subset='timestamp').reset_index(drop=True)


def calculate_indicators(df):
    """Calculate all needed indicators."""
    close = df['close']
    high = df['high']
    low = df['low']

    # RSI
    delta = close.diff()
    gain = (delta.where(delta > 0, 0)).rolling(14).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
    rs = gain / loss
    df['rsi'] = 100 - (100 / (1 + rs))

    # Bollinger Bands
    df['bb_mid'] = close.rolling(20).mean()
    df['bb_std'] = close.rolling(20).std()
    df['bb_upper'] = df['bb_mid'] + 2 * df['bb_std']
    df['bb_lower'] = df['bb_mid'] - 2 * df['bb_std']

    # Momentum (10 period return)
    df['momentum'] = close.pct_change(10)

    # ATR for volatility filter
    tr1 = high - low
    tr2 = abs(high - close.shift(1))
    tr3 = abs(low - close.shift(1))
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
    df['atr'] = tr.rolling(14).mean()
    df['atr_pct'] = df['atr'] / close

    # SMA trend filter
    df['sma_50'] = close.rolling(50).mean()
    df['sma_200'] = close.rolling(200).mean()

    return df


def backtest_rsi_strategy(df, params):
    """
    RSI Mean Reversion Strategy:
    - Buy when RSI < oversold AND price near BB lower
    - Sell when RSI > overbought OR take profit OR stop loss
    """
    close = df['close'].values
    rsi = df['rsi'].values
    bb_lower = df['bb_lower'].values
    bb_upper = df['bb_upper'].values
    bb_mid = df['bb_mid'].values
    momentum = df['momentum'].values
    sma_50 = df['sma_50'].values
    sma_200 = df['sma_200'].values

    n = len(close)

    capital = INITIAL_CAPITAL
    position = 0
    entry_price = 0
    position_size = 0

    trades = []
    equity = []

    rsi_oversold = params['rsi_oversold']
    rsi_overbought = params['rsi_overbought']
    stop_loss = params['stop_loss']
    take_profit = params['take_profit']
    trade_size = params['trade_size']
    use_trend_filter = params.get('use_trend_filter', False)

    for i in range(250, n):
        price = close[i]

        # Track equity
        if position > 0:
            current_equity = capital + position_size * price
        else:
            current_equity = capital
        equity.append({'timestamp': df['timestamp'].iloc[i], 'equity': current_equity})

        # Exit logic
        if position > 0:
            pnl_pct = (price - entry_price) / entry_price

            exit_signal = False
            exit_reason = None

            # Stop loss
            if pnl_pct <= -stop_loss:
                exit_signal = True
                exit_reason = 'stop_loss'
            # Take profit
            elif pnl_pct >= take_profit:
                exit_signal = True
                exit_reason = 'take_profit'
            # RSI overbought exit
            elif rsi[i] > rsi_overbought:
                exit_signal = True
                exit_reason = 'rsi_overbought'
            # Price at BB middle (mean reversion complete)
            elif price > bb_mid[i] and pnl_pct > 0.005:
                exit_signal = True
                exit_reason = 'mean_reversion'

            if exit_signal:
                pnl = position_size * (price - entry_price)
                capital += position_size * entry_price + pnl
                trades.append({
                    'entry_price': entry_price,
                    'exit_price': price,
                    'pnl': pnl,
                    'pnl_pct': pnl_pct,
                    'reason': exit_reason
                })
                position = 0
                position_size = 0

        # Entry logic (only long for now - mean reversion buy dips)
        if position == 0:
            entry_signal = False

            # Core condition: RSI oversold
            if rsi[i] < rsi_oversold:
                # Additional filter: price near or below BB lower
                if price <= bb_lower[i] * 1.01:
                    entry_signal = True

                # Or: strong oversold without BB requirement
                elif rsi[i] < rsi_oversold - 5:
                    entry_signal = True

            # Trend filter (optional): only buy in uptrend
            if use_trend_filter and entry_signal:
                if sma_50[i] < sma_200[i]:
                    entry_signal = False

            if entry_signal:
                trade_capital = capital * trade_size
                position_size = trade_capital / price
                capital -= trade_capital
                entry_price = price
                position = 1

    # Close any open position
    if position > 0:
        price = close[-1]
        pnl = position_size * (price - entry_price)
        capital += position_size * entry_price + pnl
        trades.append({
            'entry_price': entry_price,
            'exit_price': price,
            'pnl': pnl,
            'pnl_pct': (price - entry_price) / entry_price,
            'reason': 'end'
        })

    return calculate_metrics(trades, equity, capital)


def calculate_metrics(trades, equity, final_capital):
    """Calculate performance metrics."""
    if not trades:
        return None

    total_return = final_capital - INITIAL_CAPITAL
    total_return_pct = total_return / INITIAL_CAPITAL

    wins = [t for t in trades if t['pnl'] > 0]
    losses = [t for t in trades if t['pnl'] <= 0]

    win_rate = len(wins) / len(trades) if trades else 0

    equity_values = [e['equity'] for e in equity]
    if len(equity_values) > 1:
        returns = np.diff(equity_values) / np.array(equity_values[:-1])
        sharpe = returns.mean() / returns.std() * np.sqrt(525600) if returns.std() > 0 else 0

        cummax = np.maximum.accumulate(equity_values)
        drawdown = (np.array(equity_values) - cummax) / cummax
        max_dd = abs(min(drawdown))
    else:
        sharpe = 0
        max_dd = 0

    gross_profit = sum(t['pnl'] for t in wins) if wins else 0
    gross_loss = abs(sum(t['pnl'] for t in losses)) if losses else 1
    profit_factor = gross_profit / gross_loss if gross_loss > 0 else gross_profit

    # Analyze by exit reason
    by_reason = {}
    for t in trades:
        r = t['reason']
        if r not in by_reason:
            by_reason[r] = {'count': 0, 'pnl': 0, 'wins': 0}
        by_reason[r]['count'] += 1
        by_reason[r]['pnl'] += t['pnl']
        if t['pnl'] > 0:
            by_reason[r]['wins'] += 1

    return {
        'total_return': total_return,
        'total_return_pct': total_return_pct,
        'final_capital': final_capital,
        'num_trades': len(trades),
        'win_rate': win_rate,
        'sharpe': sharpe,
        'max_drawdown': max_dd,
        'profit_factor': profit_factor,
        'avg_win': np.mean([t['pnl'] for t in wins]) if wins else 0,
        'avg_loss': np.mean([t['pnl'] for t in losses]) if losses else 0,
        'by_reason': by_reason
    }


def optimize_parameters(df):
    """Find optimal parameters."""
    print("\n" + "="*60)
    print("OTTIMIZZAZIONE PARAMETRI")
    print("="*60)

    param_grid = {
        'rsi_oversold': [25, 30, 35],
        'rsi_overbought': [65, 70, 75],
        'stop_loss': [0.015, 0.02, 0.025],
        'take_profit': [0.02, 0.03, 0.04, 0.05],
        'trade_size': [0.2, 0.3, 0.4],
        'use_trend_filter': [False, True],
    }

    combinations = list(product(*param_grid.values()))
    param_names = list(param_grid.keys())

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

    results = []

    for i, combo in enumerate(combinations):
        params = dict(zip(param_names, combo))
        metrics = backtest_rsi_strategy(df, params)

        if metrics and metrics['num_trades'] > 20:
            results.append({**params, **metrics})

        if (i + 1) % 100 == 0:
            print(f"  Progress: {i+1}/{len(combinations)}")

    # Sort by a composite score
    for r in results:
        # Prioritize: return, win rate, low drawdown
        r['score'] = (
            r['total_return_pct'] * 0.4 +
            r['win_rate'] * 0.3 +
            min(r['sharpe'], 3) * 0.2 -
            r['max_drawdown'] * 0.1
        )

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

    return results


def main():
    print("="*60)
    print("PROFITABLE STRATEGY OPTIMIZATION")
    print("="*60)

    df = load_data()
    print(f"\nLoaded {len(df):,} candles")

    df = calculate_indicators(df)

    results = optimize_parameters(df)

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

    for i, r in enumerate(results[:10], 1):
        print(f"\n#{i} Score: {r['score']:.4f}")
        print(f"  RSI: {r['rsi_oversold']}/{r['rsi_overbought']} | "
              f"SL: {r['stop_loss']:.1%} | TP: {r['take_profit']:.1%} | "
              f"Size: {r['trade_size']:.0%} | Trend: {r['use_trend_filter']}")
        print(f"  Return: €{r['total_return']:.2f} ({r['total_return_pct']*100:+.2f}%) | "
              f"Final: €{r['final_capital']:.2f}")
        print(f"  Trades: {r['num_trades']} | Win Rate: {r['win_rate']*100:.1f}% | "
              f"Sharpe: {r['sharpe']:.2f}")
        print(f"  Max DD: {r['max_drawdown']*100:.1f}% | PF: {r['profit_factor']:.2f}")

    # Save best config
    if results:
        best = results[0]
        best_config = {
            'strategy': 'RSI_MEAN_REVERSION',
            'rsi_oversold': best['rsi_oversold'],
            'rsi_overbought': best['rsi_overbought'],
            'stop_loss': best['stop_loss'],
            'take_profit': best['take_profit'],
            'trade_size': best['trade_size'],
            'use_trend_filter': best['use_trend_filter'],
            'metrics': {
                'total_return_pct': best['total_return_pct'],
                'win_rate': best['win_rate'],
                'sharpe': best['sharpe'],
                'max_drawdown': best['max_drawdown'],
                'num_trades': best['num_trades'],
            }
        }

        with open(RESULTS_DIR / 'best_rsi_config.json', 'w') as f:
            json.dump(best_config, f, indent=2)

        print("\n" + "="*60)
        print("BEST CONFIGURATION SAVED")
        print("="*60)
        print(json.dumps(best_config, indent=2))

        # Detailed analysis of best config
        print("\n" + "="*60)
        print("ANALISI DETTAGLIATA MIGLIOR CONFIG")
        print("="*60)

        params = {k: best[k] for k in ['rsi_oversold', 'rsi_overbought', 'stop_loss', 'take_profit', 'trade_size', 'use_trend_filter']}
        metrics = backtest_rsi_strategy(df, params)

        print("\nExit reasons:")
        for reason, data in metrics['by_reason'].items():
            wr = data['wins'] / data['count'] * 100 if data['count'] > 0 else 0
            print(f"  {reason:20s}: {data['count']:4d} trades, €{data['pnl']:+8.2f}, WR: {wr:.1f}%")


if __name__ == '__main__':
    main()
