# source: https://raw.githubusercontent.com/aabudeev/freqtrade/b0d5d65518bb013d85845084593a2198ad69e303/user_data/strategies/SignalOnlyStrategy.py
# pragma pylint: disable=missing-docstring
"""Strictly Signal-based Strategy. No automated TA entries."""

from pandas import DataFrame
from datetime import datetime
import logging

logger = logging.getLogger(__name__)

from freqtrade.strategy import IStrategy
from freqtrade.persistence import Trade
from freqtrade.signals.queue_store import SignalQueueStore


class Github_aabudeev_freqtrade__SignalOnlyStrategy__20260502_182920(IStrategy):
    """
    Strategy for executing external signals ONLY.
    Entries are made via SignalWorker (Telegram/API).
    """

    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.signal_store = SignalQueueStore("/freqtrade/user_data/signals.db")

    INTERFACE_VERSION = 3
    can_short: bool = True

    minimal_roi = {"0": 10.0}  # Effectively disabled
    stoploss = -0.99           # Effectively disabled
    
    # TRAILING STOP DISABLED
    trailing_stop = False
    use_custom_stoploss = False
    process_only_new_candles = False
    use_exit_signal = False
    startup_candle_count = 20

    order_types = {
        "entry": "market",
        "exit": "limit",
        "stoploss": "market",
        "stoploss_on_exchange": True,
    }
    order_time_in_force = {"entry": "GTC", "exit": "GTC"}

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str,
                 **kwargs) -> float:
        settings = self.signal_store.get_settings()
        lev = float(settings.get('signal_strategy_leverage', 50.0))
        return min(lev, max_leverage)

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # No indicators for signal strategy
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Entries only via SignalWorker
        dataframe.loc[:, 'enter_long'] = 0
        dataframe.loc[:, 'enter_short'] = 0
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, "exit_long"] = 0
        dataframe.loc[:, "exit_short"] = 0
        return dataframe

    def bot_loop_start(self, **kwargs) -> None:
        """
        Called at the start of each bot iteration.
        Used to reconcile stoploss orders for BingX (Freqtrade V3 schema).
        """
        if self.config['exchange']['name'] != 'bingx':
            return

        try:
            from freqtrade.persistence import Trade, Order
            from datetime import datetime, timezone, timedelta
            open_trades = Trade.get_trades([Trade.is_open.is_(True)]).all()
            
            for trade in open_trades:
                # Protection: skip very new trades (less than 30s old)
                # to avoid race condition with SignalWorker initial setup
                # Ensure we compare aware datetimes
                trade_open_date = trade.open_date
                if trade_open_date.tzinfo is None:
                    trade_open_date = trade_open_date.replace(tzinfo=timezone.utc)
                
                if trade_open_date > datetime.now(timezone.utc) - timedelta(seconds=30):
                    continue

                # Check if we already have an open stoploss order in our database
                has_sl = any(o.ft_order_side == 'stoploss' and o.ft_is_open for o in trade.orders)
                
                if not has_sl and trade.stop_loss:
                    try:
                        if hasattr(self.dp.exchange, '_api'):
                            symbol = trade.pair.replace("/", "").replace(":USDT", "USDT")
                            # Fetch open orders from BingX V2 Swap API
                            open_orders = self.dp.exchange._api.swapV2PrivateGetTradeOpenOrders({"symbol": symbol})
                            if open_orders and 'data' in open_orders:
                                for o in open_orders['data']:
                                    order_side = o.get('side', '').lower()
                                    target_side = 'sell' if not trade.is_short else 'buy'
                                    
                                    # Identify Stop orders
                                    if order_side == target_side and o.get('type') in ('STOP', 'STOP_MARKET'):
                                        o_price = float(o.get('stopPrice') or o.get('price') or 0)
                                        if o_price > 0 and abs(o_price - trade.stop_loss) / trade.stop_loss < 0.001:
                                            logger.info(f"BINGX RECONCILE: Found existing SL order {o['orderId']} for {trade.pair}. Registering.")
                                            
                                            # Create Order object in database to stop Freqtrade from placing a new one
                                            new_order = Order(
                                                ft_trade_id=trade.id,
                                                ft_pair=trade.pair,
                                                ft_is_open=True,
                                                ft_order_side='stoploss',
                                                order_id=str(o['orderId']),
                                                status='open',
                                                symbol=trade.pair,
                                                order_type='stoploss',
                                                side=target_side,
                                                amount=trade.amount,
                                                filled=0.0,
                                                remaining=trade.amount,
                                                order_date=datetime.now()
                                            )
                                            Trade.session.add(new_order)
                                            Trade.commit()
                                            break
                    except Exception as e_rec:
                        logger.debug(f"BingX SL reconciliation failed for {trade.pair}: {e_rec}")
        except Exception as e:
            logger.error(f"Error in bot_loop_start: {e}")

    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
                    current_profit: float, **kwargs) -> str | bool | None:
        # Take profit from signal
        signal_tp = trade.get_custom_data("signal_tp")
        if signal_tp is not None:
            tp_price = float(signal_tp)
            if not trade.is_short:
                if current_rate >= tp_price:
                    return f"signal_tp_{tp_price}"
            else:
                if current_rate <= tp_price:
                    return f"signal_tp_{tp_price}"
        return None
