# source: https://raw.githubusercontent.com/houston2394/ai-income-engine/5968a8cae5e5de74cdae0e311fb1e2b9fcb25241/trading/strategies/grid_strategy.py
"""
Grid Trading Strategy for Freqtrade
===================================
A grid trading bot that places buy and sell orders at regular price intervals.

Risk Rules (Moderate):
- Max position size: 2% of portfolio per trade
- Max drawdown: 15% - stop trading if reached
- Stop loss: Hard stop at 15% drawdown
"""

from freqtrade.strategy import IStrategy
from pandas import DataFrame
import pandas as pd
from typing import Dict
import logging

logger = logging.getLogger(__name__)


class Github_houston2394_ai_income_engine__grid_strategy__20260226_063723(IStrategy):
    """
    Grid Trading Strategy
    - Places orders at grid levels
    - Profits from price oscillations
    - Lower risk than directional trading
    """

    # Strategy parameters
    timeframe = "15m"
    minimal_roi = {
        "0": 0.01,  # 1% profit target
    }
    stoploss = -0.15  # 15% stop loss (matches drawdown limit)

    # Grid parameters
    grid_levels = 10  # Number of grid levels
    grid_spacing_pct = 0.005  # 0.5% between each grid level

    def __init__(self, config: dict):
        super().__init__(config)
        self.grid_levels = config.get("grid_levels", self.grid_levels)
        self.grid_spacing_pct = config.get("grid_spacing_pct", self.grid_spacing_pct)
        self.entry_grid = []
        self.exit_grid = []
        self.base_price = None
        self.total_trades = 0
        self.initial_balance = config.get("dry_run_wallet_balance", 1000)
        self.current_drawdown = 0

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Populate indicators for the strategy"""
        # Calculate grid levels based on recent price
        if self.base_price is None and len(dataframe) > 0:
            self.base_price = dataframe["close"].iloc[-1]

        # Simple moving averages for trend detection
        dataframe["sma_20"] = dataframe["close"].rolling(window=20).mean()
        dataframe["sma_50"] = dataframe["close"].rolling(window=50).mean()

        # Price volatility
        dataframe["volatility"] = dataframe["close"].rolling(window=20).std()

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Populate entry signals"""
        # Grid entry: buy when price drops to grid level
        if len(dataframe) > 0:
            current_price = dataframe["close"].iloc[-1]

            # Calculate grid entry points (buy levels)
            for i in range(1, self.grid_levels + 1):
                entry_price = self.base_price * (1 - (i * self.grid_spacing_pct))

                if current_price <= entry_price:
                    dataframe.loc[dataframe.index[-1], "enter_long"] = 1
                    break

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Populate exit signals"""
        # Grid exit: sell when price rises to upper grid level
        if len(dataframe) > 0:
            current_price = dataframe["close"].iloc[-1]

            # Calculate grid exit points (sell levels)
            for i in range(1, self.grid_levels + 1):
                exit_price = self.base_price * (1 + (i * self.grid_spacing_pct))

                if current_price >= exit_price:
                    dataframe.loc[dataframe.index[-1], "exit_long"] = 1
                    break

        return dataframe

    def check_entry(self, pair: str, dataframe: DataFrame) -> bool:
        """Custom entry check with risk management"""
        if self.current_drawdown >= 15:
            logger.warning(
                f"Max drawdown reached ({self.current_drawdown}%). Stopping entries."
            )
            return False

        # Check position size limit (2% max)
        if self.config.get("dry_run", True):
            position_value = self.wallets.get_free_balance(pair.replace("/", ""))
            portfolio_value = sum(w.free for w in self.wallets.values())

            if portfolio_value > 0:
                position_pct = (position_value / portfolio_value) * 100
                if position_pct >= 2:
                    logger.info(
                        f"Max position size reached ({position_pct}%). Skipping entry."
                    )
                    return False

        return True

    def check_exit(self, pair: str, dataframe: DataFrame, trade) -> bool:
        """Custom exit check with risk management"""
        # Update drawdown
        if self.config.get("dry_run", True):
            current_balance = self.wallets.get_total()
            drawdown = (
                (self.initial_balance - current_balance) / self.initial_balance
            ) * 100
            self.current_drawdown = max(drawdown, self.current_drawdown)

        return True

    def leverage(
        self,
        pair: str,
        current_price: float,
        proposed_leverage: float,
        max_leverage: float,
        entry_price: float,
        side: str,
        **kwargs,
    ) -> float:
        """No leverage - keep it at 1x"""
        return 1.0

    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time,
        entry_price,
        **kwargs,
    ) -> bool:
        """Confirm trade entry with risk check"""
        # Final risk check before entry
        if self.current_drawdown >= 15:
            logger.warning("Rejecting entry due to max drawdown")
            return False

        return True

    def confirm_trade_exit(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        exit_reason: str,
        current_time,
        **kwargs,
    ) -> bool:
        """Confirm trade exit"""
        return True

    def get_valid_pair_list(self, whitelist: list = None) -> list:
        """Get valid trading pairs"""
        if whitelist is None:
            whitelist = ["BTC/USDT", "ETH/USDT", "BNB/USDT"]
        return whitelist
