#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
创业板50指数真实数据回测 - 高收益优化版
"""

import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')

# 加载真实数据
def load_real_data():
    """加载创业板50指数真实数据 - cyb50_baostock.csv"""
    df = pd.read_csv('cyb50_baostock.csv')
    df['date'] = pd.to_datetime(df['date'])
    df = df.set_index('date').sort_index()
    
    # 转换数据类型
    for col in ['open', 'high', 'low', 'close', 'volume']:
        df[col] = pd.to_numeric(df[col], errors='coerce')
    
    print(f"真实数据加载成功: {df.index[0].date()} ~ {df.index[-1].date()}")
    return df

# ==================== 高性能策略 ====================

class HighPerformanceStrategy:
    """
    高收益策略：趋势跟踪 + 动量加速 + 智能止盈
    """
    
    def __init__(self, params=None):
        self.params = params or {
            'fast_ma': 5,       # 超短均线，快速响应
            'slow_ma': 20,      # 月均线
            'trend_ma': 60,     # 季均线
            'momentum_period': 10,
            'volatility_period': 20,
            'max_position': 1.0,
            'profit_take': 0.15,    # 15%止盈
            'trailing_stop': 0.08,  # 8%移动止损
        }
        # 确保所有参数都有默认值
        default_params = {
            'fast_ma': 5,
            'slow_ma': 20,
            'trend_ma': 60,
            'momentum_period': 10,
            'volatility_period': 20,
            'max_position': 1.0,
            'profit_take': 0.15,
            'trailing_stop': 0.08,
        }
        if params:
            for key, val in default_params.items():
                if key not in params:
                    self.params[key] = val
        self.position = 0
        self.entry_price = None
        self.max_price = None
    
    def generate_signal(self, data):
        """生成交易信号"""
        close = data['close']
        p = self.params
        
        # 计算指标
        ma_fast = close.rolling(p['fast_ma']).mean().iloc[-1]
        ma_slow = close.rolling(p['slow_ma']).mean().iloc[-1]
        ma_trend = close.rolling(p['trend_ma']).mean().iloc[-1]
        
        # 动量
        momentum = (close.iloc[-1] / close.iloc[-p['momentum_period']] - 1) * 100
        
        # 波动率
        returns = close.pct_change()
        vol = returns.rolling(p['volatility_period']).std().iloc[-1] * np.sqrt(252) * 100
        
        curr_price = close.iloc[-1]
        
        # 趋势强度
        trend_strong = (curr_price > ma_fast) and (ma_fast > ma_slow) and (ma_slow > ma_trend)
        trend_weak = (curr_price < ma_fast) and (ma_fast < ma_slow)
        
        # 信号生成
        if trend_strong and momentum > 2:
            # 强势上涨，满仓
            target_pos = p['max_position']
            state = "STRONG_UP"
        elif trend_strong and momentum > 0:
            # 趋势向上但动量一般，80%仓位
            target_pos = p['max_position'] * 0.8
            state = "UP"
        elif trend_weak or momentum < -3:
            # 趋势转弱，空仓
            target_pos = 0
            state = "DOWN"
        else:
            # 震荡，50%仓位
            target_pos = p['max_position'] * 0.5
            state = "OSCILLATE"
        
        # 移动止盈
        if self.position > 0 and self.max_price:
            current_return = (curr_price - self.entry_price) / self.entry_price
            
            # 更新最高价
            if curr_price > self.max_price:
                self.max_price = curr_price
            
            # 移动止损：从最高点回撤8%离场
            drawdown_from_peak = (curr_price - self.max_price) / self.max_price
            if drawdown_from_peak < -p['trailing_stop']:
                target_pos = 0
                state = "TRAILING_STOP"
            
            # 固定止盈15%
            elif current_return > p['profit_take']:
                target_pos = 0.5  # 减半仓，锁定利润
                state = "PROFIT_TAKE"
        
        # 更新状态
        if target_pos > 0 and self.position == 0:
            self.entry_price = curr_price
            self.max_price = curr_price
        elif target_pos == 0:
            self.entry_price = None
            self.max_price = None
        
        self.position = target_pos
        return target_pos, state

# ==================== 回测引擎 ====================

def backtest(data, strategy, start_date=None, end_date=None, warmup=60):
    """回测引擎"""
    if start_date:
        data = data[data.index >= start_date]
    if end_date:
        data = data[data.index <= end_date]
    
    results = []
    nav = 1.0
    
    for i in range(warmup, len(data)):
        curr_data = data.iloc[:i+1]
        position, state = strategy.generate_signal(curr_data)
        
        if i > warmup:
            daily_return = data['close'].iloc[i] / data['close'].iloc[i-1] - 1
            strategy_return = daily_return * results[-1]['position'] if results else 0
            nav *= (1 + strategy_return)
        
        results.append({
            'date': data.index[i],
            'position': position,
            'nav': nav,
            'state': state,
            'close': data['close'].iloc[i]
        })
    
    df = pd.DataFrame(results).set_index('date')
    df['index_nav'] = df['close'] / df['close'].iloc[0]
    
    metrics = calculate_metrics(df['nav'], df['index_nav'])
    return df, metrics

def calculate_metrics(strategy_nav, index_nav):
    """计算绩效指标"""
    s_returns = strategy_nav.pct_change().dropna()
    
    total_return = strategy_nav.iloc[-1] - 1
    days = len(strategy_nav)
    annual_return = (1 + total_return) ** (252 / days) - 1
    
    index_return = index_nav.iloc[-1] - 1
    index_annual = (1 + index_return) ** (252 / days) - 1
    
    running_max = strategy_nav.expanding().max()
    max_dd = ((strategy_nav - running_max) / running_max).min()
    
    volatility = s_returns.std() * np.sqrt(252)
    sharpe = (annual_return - 0.03) / volatility if volatility > 0 else 0
    calmar = annual_return / abs(max_dd) if max_dd != 0 else 0
    win_rate = (s_returns > 0).mean()
    
    return {
        'annual_return': annual_return,
        'index_annual': index_annual,
        'excess_annual': annual_return - index_annual,
        'max_drawdown': max_dd,
        'sharpe': sharpe,
        'calmar': calmar,
        'win_rate': win_rate,
        'total_return': total_return,
        'index_return': index_return
    }

def plot_results(results, title, filename):
    """绘制回测图表"""
    fig, axes = plt.subplots(3, 1, figsize=(14, 10))
    
    ax1 = axes[0]
    ax1.plot(results.index, results['nav'], label='Strategy', linewidth=2, color='red')
    ax1.plot(results.index, results['index_nav'], label='Index', linewidth=1, color='gray', alpha=0.7)
    ax1.set_title(f'{title}', fontsize=14)
    ax1.set_ylabel('NAV')
    ax1.legend()
    ax1.grid(True, alpha=0.3)
    
    ax2 = axes[1]
    colors = {'STRONG_UP': 'green', 'UP': 'lightgreen', 'DOWN': 'red', 
              'OSCILLATE': 'yellow', 'TRAILING_STOP': 'orange', 'PROFIT_TAKE': 'blue'}
    pos_colors = [colors.get(s, 'gray') for s in results['state']]
    ax2.fill_between(results.index, 0, results['position'], alpha=0.5, color='green')
    ax2.set_ylabel('Position')
    ax2.set_ylim(0, 1.1)
    ax2.grid(True, alpha=0.3)
    
    ax3 = axes[2]
    running_max = results['nav'].expanding().max()
    drawdown = (results['nav'] - running_max) / running_max
    ax3.fill_between(results.index, drawdown, 0, alpha=0.3, color='red')
    ax3.set_ylabel('Drawdown')
    ax3.set_xlabel('Date')
    ax3.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig(filename, dpi=150, bbox_inches='tight')
    print(f"  图表已保存: {filename}")

# ==================== 主程序 ====================

def main():
    print("="*70)
    print("创业板50指数高收益策略回测")
    print("="*70)
    
    # 加载真实数据
    print("\n[1] 加载真实数据...")
    data = load_real_data()
    
    print(f"    数据区间: {data.index[0].date()} ~ {data.index[-1].date()}")
    
    # 训练阶段
    print("\n[2] 训练阶段 (2018-2023) - 优化参数...")
    
    # 测试多组参数，找最优
    best_params = None
    best_score = -999
    
    test_configs = [
        {'fast_ma': 5, 'slow_ma': 20, 'profit_take': 0.15, 'trailing_stop': 0.08},
        {'fast_ma': 3, 'slow_ma': 15, 'profit_take': 0.12, 'trailing_stop': 0.06},
        {'fast_ma': 10, 'slow_ma': 30, 'profit_take': 0.20, 'trailing_stop': 0.10},
    ]
    
    for cfg in test_configs:
        strategy = HighPerformanceStrategy(cfg)
        results, metrics = backtest(data, strategy, start_date='2018-01-01', end_date='2023-12-31')
        
        # 评分：收益优先
        score = metrics['annual_return'] * 0.5 + metrics['calmar'] * 0.3 + metrics['sharpe'] * 0.2
        
        print(f"\n  参数: {cfg}")
        print(f"  年化: {metrics['annual_return']*100:.1f}%, 回撤: {metrics['max_drawdown']*100:.1f}%, 评分: {score:.2f}")
        
        if score > best_score and metrics['max_drawdown'] > -0.40:
            best_score = score
            best_params = cfg
    
    print(f"\n  最优参数: {best_params}")
    
    # 用最优参数重新回测训练集
    strategy = HighPerformanceStrategy(best_params)
    train_results, train_metrics = backtest(data, strategy, start_date='2018-01-01', end_date='2023-12-31')
    
    print(f"\n  训练集最终表现:")
    print(f"  ┌─────────────────────────────────────┐")
    print(f"  │ 策略年化收益: {train_metrics['annual_return']*100:>8.2f}%      │")
    print(f"  │ 指数年化收益: {train_metrics['index_annual']*100:>8.2f}%      │")
    print(f"  │ 超额收益:     {train_metrics['excess_annual']*100:>8.2f}%      │")
    print(f"  │ 最大回撤:     {train_metrics['max_drawdown']*100:>8.2f}%      │")
    print(f"  │ 夏普比率:     {train_metrics['sharpe']:>8.2f}          │")
    print(f"  │ 卡玛比率:     {train_metrics['calmar']:>8.2f}          │")
    print(f"  │ 胜率:         {train_metrics['win_rate']*100:>8.1f}%        │")
    print(f"  └─────────────────────────────────────┘")
    
    plot_results(train_results, "Training Set (2018-2023)", "train_high_perf.png")
    
    # 验证阶段
    print(f"\n[3] 验证阶段 (2024-2025) - 样本外测试...")
    strategy_val = HighPerformanceStrategy(best_params)
    val_results, val_metrics = backtest(data, strategy_val, start_date='2024-01-01', end_date='2025-12-31')
    
    print(f"\n  验证集最终表现:")
    print(f"  ┌─────────────────────────────────────┐")
    print(f"  │ 策略年化收益: {val_metrics['annual_return']*100:>8.2f}%      │")
    print(f"  │ 指数年化收益: {val_metrics['index_annual']*100:>8.2f}%      │")
    print(f"  │ 超额收益:     {val_metrics['excess_annual']*100:>8.2f}%      │")
    print(f"  │ 最大回撤:     {val_metrics['max_drawdown']*100:>8.2f}%      │")
    print(f"  │ 夏普比率:     {val_metrics['sharpe']:>8.2f}          │")
    print(f"  │ 卡玛比率:     {val_metrics['calmar']:>8.2f}          │")
    print(f"  └─────────────────────────────────────┘")
    
    plot_results(val_results, "Validation Set (2024-2025)", "val_high_perf.png")
    
    # 过拟合检测
    print(f"\n[4] 过拟合检测:")
    return_decay = (train_metrics['annual_return'] - val_metrics['annual_return']) / train_metrics['annual_return'] if train_metrics['annual_return'] != 0 else 0
    print(f"    年化收益衰减: {return_decay*100:.1f}%")
    if return_decay > 0.5:
        print("    ⚠️  策略在验证集表现下降明显")
    else:
        print("    ✓ 策略稳健性良好")
    
    print("\n" + "="*70)
    print("回测完成！")
    print("="*70)

if __name__ == "__main__":
    main()
