# source: https://raw.githubusercontent.com/joocy75-hash/TradingView-Strategy/430d56c6829ddca51cd5095ae96b326340687c0a/freqtrade/strategy_converter.py
#!/usr/bin/env python3
"""
전략 변환기: TradingView 전략 → Freqtrade 전략

검증된 전략을 Freqtrade 형식으로 자동 변환합니다.
"""

import re
import json
from typing import Dict, Any, Optional
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path


@dataclass
class StrategyInfo:
    """전략 정보"""
    name: str
    description: str
    timeframe: str = "1h"
    
    # 지표 파라미터
    indicators: Dict[str, Any] = None
    
    # 진입/청산 조건
    entry_conditions: list = None
    exit_conditions: list = None
    
    # 리스크 관리
    stoploss: float = -0.10  # -10%
    trailing_stop: bool = False
    trailing_stop_positive: float = 0.01
    
    # 백테스트 결과
    win_rate: float = 0.0
    profit_factor: float = 0.0
    max_drawdown: float = 0.0


class FreqtradeStrategyConverter:
    """
    Freqtrade 전략 변환기
    
    TradingView/Pine Script 전략을 Freqtrade Python 전략으로 변환합니다.
    """
    
    # Freqtrade 전략 템플릿
    STRATEGY_TEMPLATE = '''"""
{description}

Generated by TradingView Strategy Research Lab
Date: {date}
Original Strategy: {original_name}

Backtest Results:
- Win Rate: {win_rate:.1%}
- Profit Factor: {profit_factor:.2f}
- Max Drawdown: {max_drawdown:.1%}
"""

from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
import talib.abstract as ta


class Github_joocy75_hash_TradingView_Strategy__strategy_converter__20260201_085129(IStrategy):
    """
    {description}
    """
    
    # 전략 설정
    INTERFACE_VERSION = 3
    
    # 타임프레임
    timeframe = '{timeframe}'
    
    # 리스크 관리
    stoploss = {stoploss}
    trailing_stop = {trailing_stop}
    trailing_stop_positive = {trailing_stop_positive}
    trailing_stop_positive_offset = 0.02
    trailing_only_offset_is_reached = True
    
    # ROI 테이블 (시간별 목표 수익률)
    minimal_roi = {{
        "0": 0.10,    # 즉시 10% 수익 시 청산
        "30": 0.05,   # 30분 후 5% 수익 시 청산
        "60": 0.025,  # 1시간 후 2.5% 수익 시 청산
        "120": 0.01,  # 2시간 후 1% 수익 시 청산
    }}
    
    # 주문 설정
    order_types = {{
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': True
    }}
    
    # 최적화 가능한 파라미터
{parameters}
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        지표 계산
        """
{indicators}
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        진입 조건
        """
        dataframe.loc[
{entry_conditions}
            ,
            'enter_long'] = 1
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        청산 조건
        """
        dataframe.loc[
{exit_conditions}
            ,
            'exit_long'] = 1
        
        return dataframe
'''
    
    # 지표 변환 매핑
    INDICATOR_MAPPING = {
        'sma': 'ta.SMA(dataframe["close"], timeperiod={period})',
        'ema': 'ta.EMA(dataframe["close"], timeperiod={period})',
        'rsi': 'ta.RSI(dataframe["close"], timeperiod={period})',
        'macd': 'ta.MACD(dataframe["close"], fastperiod={fast}, slowperiod={slow}, signalperiod={signal})',
        'bb': 'ta.BBANDS(dataframe["close"], timeperiod={period}, nbdevup={std}, nbdevdn={std})',
        'atr': 'ta.ATR(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod={period})',
        'adx': 'ta.ADX(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod={period})',
        'stoch': 'ta.STOCH(dataframe["high"], dataframe["low"], dataframe["close"])',
        'cci': 'ta.CCI(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod={period})',
        'mfi': 'ta.MFI(dataframe["high"], dataframe["low"], dataframe["close"], dataframe["volume"], timeperiod={period})',
    }
    
    def __init__(self, output_dir: str = "freqtrade/user_data/strategies"):
        """
        Args:
            output_dir: 전략 파일 출력 디렉토리
        """
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
    
    def convert(self, strategy_info: StrategyInfo) -> str:
        """
        전략 변환
        
        Args:
            strategy_info: 전략 정보
            
        Returns:
            생성된 전략 파일 경로
        """
        # 클래스 이름 생성
        class_name = self._to_class_name(strategy_info.name)
        
        # 파라미터 생성
        parameters = self._generate_parameters(strategy_info.indicators or {})
        
        # 지표 코드 생성
        indicators = self._generate_indicators(strategy_info.indicators or {})
        
        # 진입 조건 생성
        entry_conditions = self._generate_conditions(
            strategy_info.entry_conditions or ["(dataframe['close'] > dataframe['sma_20'])"]
        )
        
        # 청산 조건 생성
        exit_conditions = self._generate_conditions(
            strategy_info.exit_conditions or ["(dataframe['close'] < dataframe['sma_20'])"]
        )
        
        # 템플릿 채우기
        strategy_code = self.STRATEGY_TEMPLATE.format(
            description=strategy_info.description,
            date=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            original_name=strategy_info.name,
            win_rate=strategy_info.win_rate,
            profit_factor=strategy_info.profit_factor,
            max_drawdown=strategy_info.max_drawdown,
            class_name=class_name,
            timeframe=strategy_info.timeframe,
            stoploss=strategy_info.stoploss,
            trailing_stop=str(strategy_info.trailing_stop),
            trailing_stop_positive=strategy_info.trailing_stop_positive,
            parameters=parameters,
            indicators=indicators,
            entry_conditions=entry_conditions,
            exit_conditions=exit_conditions,
        )
        
        # 파일 저장
        file_path = self.output_dir / f"Github_joocy75_hash_TradingView_Strategy__strategy_converter__20260201_085129.py"
        file_path.write_text(strategy_code)
        
        return str(file_path)
    
    def _to_class_name(self, name: str) -> str:
        """전략 이름을 클래스 이름으로 변환"""
        # 특수문자 제거, CamelCase로 변환
        words = re.sub(r'[^a-zA-Z0-9\s]', '', name).split()
        return ''.join(word.capitalize() for word in words) + 'Strategy'
    
    def _generate_parameters(self, indicators: Dict[str, Any]) -> str:
        """최적화 가능한 파라미터 생성"""
        params = []
        
        for name, config in indicators.items():
            if isinstance(config, dict):
                period = config.get('period', 14)
                params.append(
                    f"    {name}_period = IntParameter({max(5, period-10)}, {period+20}, default={period}, space='buy')"
                )
        
        return '\n'.join(params) if params else "    pass  # No optimizable parameters"
    
    def _generate_indicators(self, indicators: Dict[str, Any]) -> str:
        """지표 계산 코드 생성"""
        lines = []
        
        for name, config in indicators.items():
            indicator_type = config.get('type', name) if isinstance(config, dict) else name
            
            if indicator_type in self.INDICATOR_MAPPING:
                template = self.INDICATOR_MAPPING[indicator_type]
                
                if isinstance(config, dict):
                    code = template.format(**config)
                else:
                    code = template.format(period=14)
                
                lines.append(f"        dataframe['{name}'] = {code}")
        
        # 기본 지표 추가
        if not lines:
            lines = [
                "        # SMA",
                "        dataframe['sma_20'] = ta.SMA(dataframe['close'], timeperiod=20)",
                "        dataframe['sma_50'] = ta.SMA(dataframe['close'], timeperiod=50)",
                "",
                "        # RSI",
                "        dataframe['rsi'] = ta.RSI(dataframe['close'], timeperiod=14)",
                "",
                "        # Volume",
                "        dataframe['volume_mean'] = dataframe['volume'].rolling(20).mean()",
            ]
        
        return '\n'.join(lines)
    
    def _generate_conditions(self, conditions: list) -> str:
        """조건 코드 생성"""
        if not conditions:
            return "            (dataframe['volume'] > 0)"
        
        formatted = []
        for i, cond in enumerate(conditions):
            prefix = "            " if i == 0 else "            & "
            formatted.append(f"{prefix}{cond}")
        
        return '\n'.join(formatted)


# ============================================================
# 샘플 전략 생성
# ============================================================

def create_sample_strategies():
    """샘플 전략 생성"""
    converter = FreqtradeStrategyConverter()
    
    # 1. SMA 크로스오버 전략
    sma_strategy = StrategyInfo(
        name="SMA Crossover",
        description="Simple Moving Average Crossover Strategy - 20/50 SMA",
        timeframe="1h",
        indicators={
            'sma_fast': {'type': 'sma', 'period': 20},
            'sma_slow': {'type': 'sma', 'period': 50},
        },
        entry_conditions=[
            "(dataframe['sma_fast'] > dataframe['sma_slow'])",
            "(dataframe['sma_fast'].shift(1) <= dataframe['sma_slow'].shift(1))",
            "(dataframe['volume'] > dataframe['volume'].rolling(20).mean())",
        ],
        exit_conditions=[
            "(dataframe['sma_fast'] < dataframe['sma_slow'])",
            "(dataframe['sma_fast'].shift(1) >= dataframe['sma_slow'].shift(1))",
        ],
        stoploss=-0.05,
        trailing_stop=True,
        win_rate=0.55,
        profit_factor=1.8,
        max_drawdown=0.12,
    )
    
    # 2. RSI 전략
    rsi_strategy = StrategyInfo(
        name="RSI Oversold Bounce",
        description="RSI Oversold Bounce Strategy - Buy when RSI < 30",
        timeframe="1h",
        indicators={
            'rsi': {'type': 'rsi', 'period': 14},
            'sma': {'type': 'sma', 'period': 200},
        },
        entry_conditions=[
            "(dataframe['rsi'] < 30)",
            "(dataframe['rsi'].shift(1) >= 30)",
            "(dataframe['close'] > dataframe['sma'])",
        ],
        exit_conditions=[
            "(dataframe['rsi'] > 70)",
        ],
        stoploss=-0.03,
        trailing_stop=True,
        win_rate=0.60,
        profit_factor=2.1,
        max_drawdown=0.08,
    )
    
    # 3. 볼린저 밴드 전략
    bb_strategy = StrategyInfo(
        name="Bollinger Band Bounce",
        description="Bollinger Band Lower Band Bounce Strategy",
        timeframe="4h",
        indicators={
            'bb': {'type': 'bb', 'period': 20, 'std': 2},
            'rsi': {'type': 'rsi', 'period': 14},
        },
        entry_conditions=[
            "(dataframe['close'] < dataframe['bb_lowerband'])",
            "(dataframe['rsi'] < 40)",
        ],
        exit_conditions=[
            "(dataframe['close'] > dataframe['bb_middleband'])",
        ],
        stoploss=-0.04,
        trailing_stop=True,
        win_rate=0.58,
        profit_factor=1.9,
        max_drawdown=0.10,
    )
    
    # 전략 변환
    strategies = [sma_strategy, rsi_strategy, bb_strategy]
    
    for strategy in strategies:
        file_path = converter.convert(strategy)
        print(f"✅ 생성됨: {file_path}")
    
    return strategies


if __name__ == "__main__":
    print("Freqtrade 전략 변환기")
    print("=" * 50)
    
    create_sample_strategies()
    
    print()
    print("전략 파일이 freqtrade/user_data/strategies/ 에 생성되었습니다.")
    print("Freqtrade에서 사용하려면:")
    print("  freqtrade trade --strategy SmaCrossoverStrategy")
