rewrite the strategy below in a way that :
1- in trade opening it opens trade in bingx using bingx api 
2- is tps and sl and break even it uses the bingx api as well
## the best


import pandas as pd
import numpy as np
from dataclasses import dataclass
from typing import List, Optional

@dataclass
class StrategySettings:
    sensitivity: float = 18.0
    risk_percent: float = 1.0
    break_even_target: str = "1"  # Options: "1", "2", "3", "WITHOUT"
    tp1_percent: float = 1.0
    tp1_percent_fix: float = 40.0
    tp2_percent: float = 2.0
    tp2_percent_fix: float = 30.0
    tp3_percent: float = 3.0
    tp3_percent_fix: float = 20.0
    tp4_percent: float = 4.0
    tp4_percent_fix: float = 10.0
    fixed_stop: bool = False
    sl_percent: float = 0.0

@dataclass
class Trade:
    start_bar_index: int
    side: str  # "LONG" or "SHORT"
    entry_price: float
    sl_price: float
    tp1_price: float
    tp1_percent_fix: float
    tp2_price: float
    tp2_percent_fix: float
    tp3_price: float
    tp3_percent_fix: float
    tp4_price: float
    tp4_percent_fix: float
    break_even_price: float
    risk_percent: float
    open_date: pd.Timestamp
    close_date: Optional[pd.Timestamp] = None
    is_closed: bool = False
    sl_hit: bool = False
    tp1_hit: bool = False
    tp2_hit: bool = False
    tp3_hit: bool = False
    tp4_hit: bool = False
    can_break_even: bool = False
    profit: float = 0.0
    position_size_left: float = 100.0
    force_closed: bool = False

class TradingStrategy:
    def __init__(self, data: pd.DataFrame, settings: StrategySettings):
        # Reset index without dropping to retain 'datetime' as a column
        self.data = data.reset_index(drop=False)

        # Verify 'datetime' exists
        if 'datetime' not in self.data.columns:
            raise ValueError("DataFrame must contain a 'datetime' column.")

        self.settings = settings
        self.trades: List[Trade] = []
        self.total_profit = 0.0
        self.dep = settings.risk_percent * 100  # Initial deposit can be adjusted as needed
        self.trade_count = 0
        self.profit_trades = 0
        self.loss_trades = 0
        self.win_streak = 0
        self.loss_streak = 0
        self.current_win_streak = 0
        self.current_loss_streak = 0
        self.first_trade_date = None

        # Calculate trend lines with Fibonacci levels
        self.data['high_line'] = self.data['high'].rolling(window=int(self.settings.sensitivity)).max()
        self.data['low_line'] = self.data['low'].rolling(window=int(self.settings.sensitivity)).min()
        self.data['channel_range'] = self.data['high_line'] - self.data['low_line']
        self.data['fib_236'] = self.data['high_line'] - self.data['channel_range'] * 0.236
        self.data['fib_382'] = self.data['high_line'] - self.data['channel_range'] * 0.382
        self.data['fib_5'] = self.data['high_line'] - self.data['channel_range'] * 0.5
        self.data['fib_618'] = self.data['high_line'] - self.data['channel_range'] * 0.618
        self.data['fib_786'] = self.data['high_line'] - self.data['channel_range'] * 0.786
        self.data['trend_line'] = self.data['fib_5']  # Using fib_5 as the main trend line

        # Initialize trend states
        self.is_long_trend = False
        self.is_short_trend = False
        self.last_trend = None  # Keeps track of the last trend to prevent rapid switching

    def calc_rr(self, entry_price: float, sl_price: float, tp_price: float) -> float:
        if entry_price > sl_price:
            return (tp_price - entry_price) / (entry_price - sl_price)
        else:
            return (entry_price - tp_price) / (sl_price - entry_price)

    def run_strategy(self):
        for idx in range(len(self.data)):
            current_bar = self.data.iloc[idx]
            close = current_bar['close']
            date = current_bar['datetime']
            trend_line = current_bar['trend_line']
            fib_236 = current_bar['fib_236']
            fib_786 = current_bar['fib_786']

            if not np.isnan(trend_line):
                can_long = close >= trend_line and close >= fib_236
                can_short = close <= trend_line and close <= fib_786

                # Prevent trend switching too frequently
                new_trend = None
                if can_long and self.last_trend != "LONG":
                    new_trend = "LONG"
                elif can_short and self.last_trend != "SHORT":
                    new_trend = "SHORT"

                # Update trend only if there's a clear change
                if new_trend:
                    if new_trend == "LONG" and not self.is_short_trend:
                        self.is_long_trend = True
                        self.is_short_trend = False
                        self.last_trend = "LONG"
                    elif new_trend == "SHORT" and not self.is_long_trend:
                        self.is_short_trend = True
                        self.is_long_trend = False
                        self.last_trend = "SHORT"

            # Manage existing trades
            self.manage_trades(idx, current_bar)

            # Check for new trade entry
            self.check_entry(idx, current_bar)

    def manage_trades(self, idx, current_bar: pd.Series):
        for trade in self.trades:
            if not trade.is_closed:
                if trade.side == "LONG":
                    self.manage_long_trade(trade, idx, current_bar)
                elif trade.side == "SHORT":
                    self.manage_short_trade(trade, idx, current_bar)

    def manage_long_trade(self, trade: Trade, idx: int, current_bar: pd.Series):
        high = current_bar['high']
        low = current_bar['low']
        date = current_bar['datetime']

        # Handle Take Profits
        if not trade.tp1_hit and high >= trade.tp1_price:
            self.hit_tp(trade, 'tp1', trade.tp1_price, self.settings.tp1_percent_fix, idx, date)

        if not trade.tp2_hit and high >= trade.tp2_price:
            self.hit_tp(trade, 'tp2', trade.tp2_price, self.settings.tp2_percent_fix, idx, date)
            trade.can_break_even = True  # Enable BE once TP2 is hit

        if not trade.tp3_hit and high >= trade.tp3_price:
            self.hit_tp(trade, 'tp3', trade.tp3_price, self.settings.tp3_percent_fix, idx, date)

        if not trade.tp4_hit and high >= trade.tp4_price:
            self.hit_tp(trade, 'tp4', trade.tp4_price, self.settings.tp4_percent_fix, idx, date)
            trade.is_closed = True  # Close the trade after TP4

        # Handle Stop Loss
        if low <= trade.sl_price and not trade.can_break_even:
            self.hit_sl(trade, idx, date)

        # Handle Break-Even
        if trade.can_break_even and low <= trade.entry_price:
            self.hit_be(trade, idx, date)

    def manage_short_trade(self, trade: Trade, idx: int, current_bar: pd.Series):
        high = current_bar['high']
        low = current_bar['low']
        date = current_bar['datetime']

        # Handle Take Profits
        if not trade.tp1_hit and low <= trade.tp1_price:
            self.hit_tp(trade, 'tp1', trade.tp1_price, self.settings.tp1_percent_fix, idx, date)

        if not trade.tp2_hit and low <= trade.tp2_price:
            self.hit_tp(trade, 'tp2', trade.tp2_price, self.settings.tp2_percent_fix, idx, date)
            trade.can_break_even = True  # Enable BE once TP2 is hit

        if not trade.tp3_hit and low <= trade.tp3_price:
            self.hit_tp(trade, 'tp3', trade.tp3_price, self.settings.tp3_percent_fix, idx, date)

        if not trade.tp4_hit and low <= trade.tp4_price:
            self.hit_tp(trade, 'tp4', trade.tp4_price, self.settings.tp4_percent_fix, idx, date)
            trade.is_closed = True  # Close the trade after TP4

        # Handle Stop Loss
        if high >= trade.sl_price and not trade.can_break_even:
            self.hit_sl(trade, idx, date)

        # Handle Break-Even
        if trade.can_break_even and high >= trade.entry_price:
            self.hit_be(trade, idx, date)

    def hit_tp(self, trade: Trade, tp_label: str, tp_price: float, tp_percent_fix: float, idx: int, date: pd.Timestamp):
        setattr(trade, f"{tp_label}_hit", True)
        trade.position_size_left -= tp_percent_fix
        profit = self.calc_rr(trade.entry_price, trade.sl_price, tp_price) * (tp_percent_fix / 100) * trade.risk_percent
        trade.profit += profit
        self.total_profit += profit
        print(f"{tp_label.upper()} Hit on {date}: +{profit:.2f}%")

    def hit_sl(self, trade: Trade, idx: int, date: pd.Timestamp):
        trade.sl_hit = True
        trade.is_closed = True
        trade.close_date = date
        trade.profit -= self.settings.risk_percent * (trade.position_size_left / 100)
        self.total_profit += trade.profit
        self.loss_trades += 1
        self.current_loss_streak += 1
        self.current_win_streak = 0
        if self.current_loss_streak > self.loss_streak:
            self.loss_streak = self.current_loss_streak
        print(f"Stop Loss Hit on {date} for trade started on {trade.open_date}")

    def hit_be(self, trade: Trade, idx: int, date: pd.Timestamp):
        trade.is_closed = True
        trade.close_date = date
        # Assume no profit or loss on BE
        trade.profit += 0.0
        self.total_profit += trade.profit
        if trade.profit >= 0:
            self.profit_trades += 1
            self.current_win_streak += 1
            self.current_loss_streak = 0
            if self.current_win_streak > self.win_streak:
                self.win_streak = self.current_win_streak
        else:
            self.loss_trades += 1
            self.current_loss_streak += 1
            self.current_win_streak = 0
            if self.current_loss_streak > self.loss_streak:
                self.loss_streak = self.current_loss_streak
        print(f"Break-Even triggered on {date} for trade started on {trade.open_date}")

    def check_entry(self, idx: int, current_bar: pd.Series):
        close = current_bar['close']
        date = current_bar['datetime']

        # Prevent both long and short trades from being open simultaneously
        active_long = any(trade.side == "LONG" and not trade.is_closed for trade in self.trades)
        active_short = any(trade.side == "SHORT" and not trade.is_closed for trade in self.trades)

        # Long Entry Condition
        if self.is_long_trend and not active_long and not active_short:
            self.open_trade(idx, current_bar, side="LONG")

        # Short Entry Condition
        elif self.is_short_trend and not active_short and not active_long:
            self.open_trade(idx, current_bar, side="SHORT")

    def open_trade(self, idx: int, current_bar: pd.Series, side: str):
        entry_price = current_bar['close']
        date = current_bar['datetime']

        if side == "LONG":
            if self.settings.fixed_stop:
                sl_price = entry_price * (1 - self.settings.sl_percent / 100)
            else:
                sl_price = current_bar['fib_786'] * (1 - self.settings.sl_percent / 100)
            tp1_price = entry_price * (1 + self.settings.tp1_percent / 100)
            tp2_price = entry_price * (1 + self.settings.tp2_percent / 100)
            tp3_price = entry_price * (1 + self.settings.tp3_percent / 100)
            tp4_price = entry_price * (1 + self.settings.tp4_percent / 100)
            # Determine break-even price based on target
            if self.settings.break_even_target == "1":
                break_even_price = tp1_price
            elif self.settings.break_even_target == "2":
                break_even_price = tp2_price
            elif self.settings.break_even_target == "3":
                break_even_price = tp3_price
            else:
                break_even_price = tp4_price
        else:  # SHORT
            if self.settings.fixed_stop:
                sl_price = entry_price * (1 + self.settings.sl_percent / 100)
            else:
                sl_price = current_bar['fib_236'] * (1 + self.settings.sl_percent / 100)
            tp1_price = entry_price * (1 - self.settings.tp1_percent / 100)
            tp2_price = entry_price * (1 - self.settings.tp2_percent / 100)
            tp3_price = entry_price * (1 - self.settings.tp3_percent / 100)
            tp4_price = entry_price * (1 - self.settings.tp4_percent / 100)
            # Determine break-even price based on target
            if self.settings.break_even_target == "1":
                break_even_price = tp1_price
            elif self.settings.break_even_target == "2":
                break_even_price = tp2_price
            elif self.settings.break_even_target == "3":
                break_even_price = tp3_price
            else:
                break_even_price = tp4_price

        new_trade = Trade(
            start_bar_index=idx,
            side=side,
            entry_price=round(entry_price, 2),
            sl_price=round(sl_price, 2),
            tp1_price=round(tp1_price, 2),
            tp1_percent_fix=self.settings.tp1_percent_fix,
            tp2_price=round(tp2_price, 2),
            tp2_percent_fix=self.settings.tp2_percent_fix,
            tp3_price=round(tp3_price, 2),
            tp3_percent_fix=self.settings.tp3_percent_fix,
            tp4_price=round(tp4_price, 2),
            tp4_percent_fix=self.settings.tp4_percent_fix,
            break_even_price=round(break_even_price, 2),
            risk_percent=self.settings.risk_percent,
            open_date=date
        )
        self.trades.append(new_trade)
        self.trade_count += 1
        if self.first_trade_date is None:
            self.first_trade_date = date
        print(f"New {side} trade opened on {date} at Entry Price: {entry_price:.2f}")

    def get_performance_summary(self):
        win_trades = [trade for trade in self.trades if trade.profit > 0]
        loss_trades = [trade for trade in self.trades if trade.profit <= 0]
        win_rate = (len(win_trades) / len(self.trades) * 100) if self.trades else 0
        avg_win = sum(trade.profit for trade in win_trades) / len(win_trades) if win_trades else 0
        avg_loss = sum(trade.profit for trade in loss_trades) / len(loss_trades) if loss_trades else 0

        return {
            "Total Trades": len(self.trades),
            "Total Profit (%)": round(self.total_profit, 2),
            "Win Rate (%)": round(win_rate, 2),
            "Average Profit per Win (%)": round(avg_win, 2),
            "Average Loss per Loss (%)": round(avg_loss, 2),
            "Current Deposit": round(self.dep, 2)
        }


# Example usage
if __name__ == "__main__":
    # Replace `get_data` with your actual data source
    data = get_data("ICPUSDT.P")
    print(data.head())
    # Reset index without dropping to keep 'datetime' as a column
    data = data.reset_index(drop=False)

    # Ensure lowercase column names for consistency
    data.columns = [col.lower() for col in data.columns]

    # Initialize strategy settings
    settings = StrategySettings(
        sensitivity=17,
        risk_percent=2.5,
        break_even_target="2",
        tp1_percent=1.0,
        tp1_percent_fix=40.0,
        tp2_percent=2.0,
        tp2_percent_fix=30.0,
        tp3_percent=3.0,
        tp3_percent_fix=20.0,
        tp4_percent=4.0,
        tp4_percent_fix=10.0,
        fixed_stop=True,  # Set to True if using fixed SL
        sl_percent=1.0  # Percentage for SL if not fixed
    )

    # Initialize and run the strategy
    strategy = TradingStrategy(data, settings)
    strategy.run_strategy()

    # Print performance summary
    print("\n=== Performance Summary ===")
    summary = strategy.get_performance_summary()
    for key, value in summary.items():
        print(f"{key}: {value}")