betrachte die gesamtlogik und etwaige überschneidung, insbesondere was die berechnung des unrealized pnl angeht:


# Function to write headers to CSV files if they don't exist or are empty
def initialize_logs():
    try:
        # Check and write header for orders_log_file
        if not os.path.exists(orders_log_file) or os.path.getsize(orders_log_file) == 0:
            with open(orders_log_file, mode='w', newline='') as file:
                writer = csv.writer(file)
                writer.writerow(['Timestamp', 'MagicNumber', 'TradeID', 'Action', 'Quantity', 'Symbol', 'Status', 'OrderID', 'FillPrice', 'Commission', 'OrderType', 'TIF', 'Exchange'])

        # Check and write header for positions_log_file
        if not os.path.exists(positions_log_file) or os.path.getsize(positions_log_file) == 0:
            with open(positions_log_file, mode='w', newline='') as file:
                writer = csv.writer(file)
                writer.writerow(['Timestamp', 'MagicNumber', 'Account', 'Symbol', 'Quantity', 'AverageCost','RealizedPnL'])
    except Exception as e:
        logging.critical(f"Error initializing logs: {e}", exc_info=True)

# Function to disconnect all clients before starting
def disconnect_all_clients():
    try:
        ib = IB()
        for client_id in range(client_lowest, client_highest):  # Adjust range if needed
            try:
                ib.connect('127.0.0.1', 7497, clientId=client_id, timeout=30)
                if ib.isConnected():
                    logging.getLogger().trading(f"Disconnecting clientId {client_id}")
                    ib.disconnect()
                logging.info("Kritische Funktion erfolgreich ausgeführt.")
            except Exception as e:
                logging.critical(f"Error disconnecting clientId {client_id}: {e}", exc_info=True)
                logging.critical(f"Fehler in kritischer Funktion: {e}")
                continue
    except Exception as e:
        logging.critical(f"Failed to disconnect all clients: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")

# Function to check connection to the IB server
def check_connection():
    try:
        ib = IB()
        with ib.connect('127.0.0.1', 7497, clientId=client_lowest, timeout=30):
            return True
    except Exception as e:
        logging.critical(f"IB Server nicht erreichbar {e}")
        return False
    
# Function to generate a unique magic number for each trade
def generate_magic_number():
    return str(uuid.uuid4())

# Function to load the latest 800 price points and include the current day's price
def add_latest_prices(stock_data, tickers):
    try:
        # Calculate date range to fetch historical data
        end_date = datetime.today().strftime('%Y-%m-%d')
        start_date = (datetime.today() - timedelta(days=800 * 1.5)).strftime('%Y-%m-%d')  # Estimate, considering weekends

        # Fetch historical adjusted close prices
        data = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
        data = data.tail(800)

        # Fetch the current day's price for each ticker
        for ticker in tickers:
            ticker_obj = yf.Ticker(ticker)
            todays_data = ticker_obj.history(period="1d")
            if not todays_data.empty:
                # Append today's price if it exists
                today_price = todays_data['Close'].iloc[-1]
                data.loc[datetime.today().strftime('%Y-%m-%d'), ticker] = today_price

        # Reset index and clean up column names
        stock_data = data.reset_index()
        stock_data.columns = ['Date'] + tickers
        stock_data.set_index('Date', inplace=True)

        logging.info("Kritische Funktion erfolgreich ausgeführt.")
        return stock_data
    except Exception as e:
        logging.critical(f"Error loading prices: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")
        return stock_data

# Function to update the stock data and perform trading
def update_and_trade():
    global stock_data, last_run
    
    now = datetime.now()
    logging.getLogger().trading("####################################################################################################################################################")
    logging.getLogger().trading(f"######################################  {now.strftime('%Y-%m-%d %H:%M:%S')} #######################################################################")
    logging.getLogger().trading("####################################################################################################################################################")

    try:
        # Ensure the task is run once per day FEHLER!!! last run wird nirgends gespeichert
        if last_run is not None and last_run.date() == datetime.today().date():
            logging.getLogger().trading("The task has already been run today. Skipping this execution.")
            return

        # Skip execution on weekends
        if datetime.today().weekday() >= 6:
            logging.getLogger().trading("Es ist Wochenende.. Skipping this execution.")
            return

        # Disconnect all clients before starting
        disconnect_all_clients()
        logging.getLogger().trading(f"Starting update and trade at {datetime.now()}")

        tickers = [symbol1, symbol2]
        updated_stock_data = add_latest_prices(stock_data, tickers)
        print(updated_stock_data)
        updated_stock_data = updated_stock_data.diff().cumsum()
        print(updated_stock_data)
        logging.getLogger().trading("Updated until today stock data:")
        logging.getLogger().trading(updated_stock_data)

        data1 = updated_stock_data[symbol1].ffill().fillna(0).values
        data2 = updated_stock_data[symbol2].ffill().fillna(0).values

        try:
            with pm.Model() as model:
                stock1 = data1
                stock2 = data2

                beta_sigma = pm.Exponential('beta_sigma', 50.)
                init_dist = pm.Normal.dist(0, 1)
                beta = pm.GaussianRandomWalk('beta', sigma=beta_sigma, shape=data1.shape[0], init_dist=init_dist)

                stock2_regression = beta * stock1

                sd = pm.HalfNormal('sd', sigma=.25)
                likelihood = pm.Normal('y', mu=stock2_regression, sigma=sd, observed=stock2)

            logging.getLogger().trading("Starting model fitting...")
            with model:
                trace = pm.sample(2000, tune=1000, cores=1, progressbar=True)

            logging.getLogger().trading("Model fitting completed.")

            rolling_beta = trace.posterior['beta'].values[1].T.mean(axis=1)
            leverage = strategy.getStrategyPortfolioWeightsLive(rolling_beta, symbol1, symbol2, updated_stock_data)
            logging.getLogger().trading("Leverage calculated:")
            logging.getLogger().trading(leverage)
            logging.info("Kritische Funktion erfolgreich ausgeführt.")

        except Exception as e:
            logging.critical(f"Error during model fitting or leverage calculation: {e}", exc_info=True)
            logging.critical(f"Fehler in kritischer Funktion: {e}")

        stock_data = updated_stock_data
        stock_data.to_pickle('stock_data.pkl')

        logging.getLogger().trading("Update and trade process completed and data saved.")
        last_run = datetime.now()
        logging.getLogger().trading("Tasks for today executed, waiting for the next day.")

        # After updating and trading, check and update order fills
        check_and_update_order_fills()

        # Calculate and plot trade equity
        save_pnl_csv_and_plot(stock_data, orders_log_file)       
        logging.info("Kritische Funktion erfolgreich ausgeführt.")
        
    except Exception as e:
        logging.critical(f"Error in update_and_trade: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")

# Function to dynamically find a unique client ID
def get_unique_client_id():
    base_client_id = client_lowest
    max_attempts = client_highest  # Increase attempts to find a free ID
    for attempt in range(max_attempts):
        client_id = base_client_id + attempt
        try:
            ib = IB()
            ib.connect('127.0.0.1', 7497, clientId=client_id, timeout=30)
            if ib.isConnected():
                logging.getLogger().trading(f"Successfully connected with clientId {client_id}")
                ib.disconnect()
                logging.info("Kritische Funktion erfolgreich ausgeführt.")
                return client_id
        except Exception as e:
            logging.critical(f"Client ID {client_id} is in use or connection failed: {e}. Trying next...", exc_info=True)
            logging.critical(f"Fehler in kritischer Funktion: {e}")
            continue
    raise RuntimeError("Failed to find a unique client ID after multiple attempts.")

# Function to place a market order using the IB API and log order placement
def place_market_order(action, quantity, stock_data, symbol="", exchange='SMART', currency='EUR' ):
    ib = IB()
    try:
        client_id = get_unique_client_id()
        try:
            # Attempt to connect with a timeout
            ib.connect('127.0.0.1', 7497, clientId=client_id, timeout=30)
            logging.info("Connected successfully")

        except Exception as e:
            # Log any exception that occurs during connection
            logging.critical(f"Error connecting to API: {e}")

        contract = Stock(symbol, exchange, currency)
        order = MarketOrder(action, quantity)
        trade = ib.placeOrder(contract, order)

        # Log the initial order placement for debugging purposes
        logging.getLogger().trading(f"Order {trade.order.orderId} for {symbol} placed, waiting for fill...")

        # Wait until the order is filled, with a timeout to prevent indefinite waiting
        timeout = 60  # Maximum time to wait in seconds
        start_time = time.time()

        while trade.orderStatus.status != 'Filled':
            ib.sleep(1)  # Check every second
            ib.waitOnUpdate(timeout=1)  # Wait for updates
            if time.time() - start_time > timeout:
                logging.warning(f"Order {trade.order.orderId} for {symbol} not filled within {timeout} seconds.{e}")
                break

        # Generate a unique magic number for the trade
        magic_number = generate_magic_number()

        # Once filled, log the order details
        if trade.orderStatus.status == 'Filled':
            trade_id = trade.order.orderId
            logging.getLogger().trading(f"Order {trade_id} filled successfully.")

            orders_df = pd.read_csv(orders_log_file)
            account_id = '1101'  # Replace with actual account retrieval
           

            # Umwandeln in ein pandas DataFrame
            realized_pnl = calculate_realized_pnl(symbol,stock_data,orders_df,positions_log_file)

            log_order(
                timestamp=datetime.now(),
                magic_number=magic_number,
                trade_id=trade_id,
                action=action,
                quantity=quantity,
                symbol=symbol,
                status="Filled",
                order_id=trade_id,
                fill_price=trade.orderStatus.avgFillPrice,
                commission=sum(fill.commissionReport.commission for fill in trade.fills),
                order_type='Market',
                tif='DAY',
                exchange=exchange
            )
            # Call log_position with positional arguments
            log_position(
                timestamp=datetime.now(),
                magic_number=magic_number,
                account_id=account_id,
                symbol=symbol,
                quantity=quantity,
                avg_fill_price=trade.orderStatus.avgFillPrice,
                realized_pnl=realized_pnl
            )
        else:
            # Log as failed if not filled within the timeout period
            trade_id = trade.order.orderId if trade.order.orderId else "Unknown"
            logging.critical(f"Order {trade_id} failed to fill within the specified time.{e}")
            log_order(
                timestamp=datetime.now(),
                magic_number=magic_number,
                trade_id=trade_id,
                action=action,
                quantity=quantity,
                symbol=symbol,
                status="Failed2",
                order_id=trade_id,
                fill_price=None,
                commission=None,
                order_type='Market',
                tif='DAY',
                exchange=exchange
            )
        logging.info("Kritische Funktion erfolgreich ausgeführt.")

    except Exception as e:
        # Improved logging to capture and display exception details
        logging.critical(f"Error placing market order: {e}", exc_info=True)
        # Use a specific trade ID if available; otherwise, note the action failed without an order ID.
        magic_number = generate_magic_number()
        trade_id = trade.order.orderId if 'trade' in locals() and trade.order.orderId else "Unknown"
        log_order(
            timestamp=datetime.now(),
            magic_number=magic_number,
            trade_id=trade_id,
            action=action,
            quantity=quantity,
            symbol=symbol,
            status="Failed1",
            order_id=trade_id,
            fill_price=None,
            commission=None,
            order_type='Market',
            tif='DAY',
            exchange=exchange
        )
        logging.critical(f"Fehler in kritischer Funktion: {e}")

    finally:
        # Ensure disconnection happens even if an error occurs
        if ib.isConnected():
            ib.disconnect()

def log_order(timestamp, magic_number, trade_id, action, quantity, symbol, status, order_id, fill_price, commission, order_type='Market', tif='DAY', exchange='SMART'):
    try:
        with open(orders_log_file, mode='a', newline='') as file:
            writer = csv.writer(file)
            writer.writerow([
                timestamp,  # Timestamp
                magic_number,  # Magic Number
                trade_id,  # Trade ID
                action,  # Action (Buy/Sell)
                quantity,  # Quantity
                symbol,  # Symbol
                status,  # Order Status
                order_id,  # Order ID
                fill_price,  # Fill Price
                commission,  # Commission
                order_type,  # Order Type
                tif,  # Time in Force
                exchange  # Exchange
            ])
        logging.getLogger().trading(f"Order logged: {action} {quantity} {symbol} with status {status}")
        logging.info("Kritische Funktion erfolgreich ausgeführt.")
    except Exception as e:
        logging.critical(f"Error logging order: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")

def log_position(timestamp, magic_number, account_id, symbol, quantity, avg_fill_price, realized_pnl):
    try:
        with open(positions_log_file, mode='a', newline='') as file:
            writer = csv.writer(file)
            writer.writerow([
                timestamp,  # Timestamp
                magic_number,  # Magic Number
                account_id,  # Account ID
                symbol,  # Symbol
                quantity,  # Quantity
                avg_fill_price,  # Average Fill Price
                realized_pnl  # Realized P&L
            ])
        logging.getLogger().trading(f"Position logged: Account {account_id}, Symbol {symbol}, Quantity {quantity}, Avg Fill Price {avg_fill_price}")
        logging.info("Kritische Funktion erfolgreich ausgeführt.")
    except Exception as e:
        logging.critical(f"Error logging position: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")

def calculate_realized_pnl(symbol, stock_data, orders_df, positions_log_file, initial_cash=1000):
    try:
        positions_df = pd.read_csv(positions_log_file)
        positions_df['Timestamp'] = pd.to_datetime(positions_df['Timestamp'])
        positions_df['Date'] = positions_df['Timestamp'].dt.date

        # Wir interessieren uns nur für das spezielle Symbol
        orders_df = orders_df[orders_df['Symbol'] == symbol]
        portfolio = 0  # Anzahl der Aktien für das spezifische Symbol
        cost_basis = 0.0  # Einstandspreis des Symbols
        cash = initial_cash
        cumulative_realized_pnl = 0  # Realized PnL muss kumuliert werden

        orders_df['Timestamp'] = pd.to_datetime(orders_df['Timestamp'])
        orders_df = orders_df.sort_values('Timestamp')
        orders_df['Date'] = orders_df['Timestamp'].dt.date

        stock_data = stock_data.copy()
        stock_data.index = pd.to_datetime(stock_data.index).date

        for current_date, prices in stock_data.iterrows():
            todays_orders = orders_df[orders_df['Date'] == current_date]

            for _, order in todays_orders.iterrows():
                action = order['Action'].upper()
                quantity = order['Quantity']
                fill_price = order['FillPrice']

                if action == 'BUY':
                    if portfolio < 0:  # Schließt eine Short-Position
                        qty_to_close = min(abs(portfolio), quantity)
                        realized_pnl = (cost_basis - fill_price) * qty_to_close
                        cash += fill_price * qty_to_close
                        portfolio += qty_to_close
                        cumulative_realized_pnl += realized_pnl
                        quantity -= qty_to_close

                    if quantity > 0:  # Eröffnet oder erhöht eine Long-Position
                        new_cost_basis = (cost_basis * portfolio + fill_price * quantity) / (portfolio + quantity)
                        portfolio += quantity
                        cost_basis = new_cost_basis
                        cash -= fill_price * quantity

                elif action == 'SELL':
                    if portfolio > 0:  # Schließt eine Long-Position
                        qty_to_close = min(portfolio, quantity)
                        realized_pnl = (fill_price - cost_basis) * qty_to_close
                        cash += fill_price * qty_to_close
                        portfolio -= qty_to_close
                        cumulative_realized_pnl += realized_pnl
                        quantity -= qty_to_close

                    if quantity > 0:  # Eröffnet oder erhöht eine Short-Position
                        new_cost_basis = (cost_basis * abs(portfolio) + fill_price * quantity) / (abs(portfolio) + quantity)
                        portfolio -= quantity
                        cost_basis = new_cost_basis
                        cash += fill_price * quantity

        return cumulative_realized_pnl

    except Exception as e:
        logging.critical(f"Error calculating realized PnL for {symbol}: {e}", exc_info=True)
        return 0


# Function to check and update order fills
def check_and_update_order_fills():
    try:
        client_id = get_unique_client_id()
        ib = IB()
        try:
            # Attempt to connect with a timeout
            ib.connect('127.0.0.1', 7497, clientId=client_id, timeout=30)
            logging.info("Connected successfully")

        except Exception as e:
            # Log any exception that occurs during connection
            logging.critical(f"Error connecting to API: {e}")

        # Load current orders from the log
        orders_df = pd.read_csv(orders_log_file)

        # Iterate through each order to check and update fills
        for index, row in orders_df.iterrows():
            if row['Status'] == 'Placed':
                # Check for filled orders
                trades = ib.openTrades()
                for trade in trades:
                    if trade.order.orderId == row['OrderID'] and trade.orderStatus.status == 'Filled':
                        # Update the order log with fill details
                        orders_df.at[index, 'Status'] = 'Filled'
                        orders_df.at[index, 'FillPrice'] = trade.orderStatus.avgFillPrice
                        orders_df.at[index, 'Commission'] = sum(fill.commissionReport.commission for fill in trade.fills)
                        logging.getLogger().trading(f"Order {trade.order.orderId} updated with fill details.")

        # Save the updated orders log
        orders_df.to_csv(orders_log_file, index=False)

        ib.disconnect()
        logging.info("Kritische Funktion erfolgreich ausgeführt.")
    except Exception as e:
        logging.critical(f"Error checking and updating order fills: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")

class TradingStrategy:
    def __init__(self):
        self.trading = self.load_trading_state()  # Load the state on initialization
        self.trading_start = self.load_trading_start()  # Load the trading start value

    def load_trading_state(self):
        """Load the trading state from a file."""
        if os.path.exists('trading_state.txt'):
            with open('trading_state.txt', 'r') as file:
                return file.read().strip()
        return "not"  # Default state if file does not exist

    def save_trading_state(self):
        """Save the trading state to a file."""
        with open('trading_state.txt', 'w') as file:
            file.write(self.trading)

    def load_trading_start(self):
        """Load the trading start value from a file."""
        if os.path.exists('trading_start.txt'):
            with open('trading_start.txt', 'r') as file:
                return float(file.read().strip())  # Convert to float if necessary
        return 0  # Default value if file does not exist

    def save_trading_start(self):
        """Save the trading start value to a file."""
        with open('trading_start.txt', 'w') as file:
            file.write(str(self.trading_start))

    # Function to determine strategy portfolio weights and execute live trades
    def getStrategyPortfolioWeightsLive(
        self, rolling_beta, stock_name1, stock_name2, data, 
        smoothing_window=15
    ):
        data1 = data[stock_name1].ffill().fillna(0).values
        data2 = data[stock_name2].ffill().fillna(0).values

        fixed_beta = rolling_beta[smoothing_window]
        signal = fixed_beta * data1 - data2
        smoothed_signal = pd.Series(signal).rolling(smoothing_window).mean()
        d_smoothed_signal = smoothed_signal.diff()

        # Only consider the last index
        i = data1.shape[0] - 1
        leverage = pd.DataFrame(
            np.zeros_like(data), 
            columns=data.columns, 
            index=data.index
        )
        leverage.iloc[i, :] = leverage.iloc[i - 1, :]

        try:
            # Check current positions
            current_positions = get_current_positions([stock_name1, stock_name2])
            # Log the current trading status
            logging.getLogger().trading(f"Current trading status: {self.trading}")  
            
            if self.trading == "not":
                logging.getLogger().trading("trading == not:")
                fixed_beta = rolling_beta[i]
                signal = fixed_beta * data1 - data2
                smoothed_signal = pd.Series(signal).rolling(smoothing_window).mean()
                d_smoothed_signal = smoothed_signal.diff()

                logging.getLogger().trading(f"smoothed_signal[i]: {smoothed_signal[i]}")
                logging.getLogger().trading(f"d_smoothed_signal[i]: {d_smoothed_signal[i]}")
                
                if smoothed_signal[i] > 0 and d_smoothed_signal[i] < 0:
                    # Place short trade if no open positions for stock_name1 or stock_name2
                    if current_positions[stock_name1] == 0 and current_positions[stock_name2] == 0:
                        leverage.iloc[i, 0] = -fixed_beta / (abs(fixed_beta) + 1)
                        leverage.iloc[i, 1] = 1 / (abs(fixed_beta) + 1)
                        self.trading = "short"
                        self.trading_start = smoothed_signal[i]
                        self.save_trading_state()  # Save the new state
                        self.save_trading_start()  # Save the trading start value
                        logging.getLogger().trading(f"Placing short trade: SELL {stock_name1} --- BUY {stock_name2}")
                        place_market_order('SELL', quantity_1, stock_name1,stock_data=data)
                        place_market_order('BUY', quantity_2, stock_name2,stock_data=data)
                    else:
                        logging.getLogger().trading("Already existing positions for short. No Trade Execution today.")

                elif smoothed_signal[i] < 0 and d_smoothed_signal[i] > 0:
                    # Place long trade if no open positions for stock_name1 or stock_name2
                    if current_positions[stock_name1] == 0 and current_positions[stock_name2] == 0:
                        leverage.iloc[i, 0] = fixed_beta / (abs(fixed_beta) + 1)
                        leverage.iloc[i, 1] = -1 / (abs(fixed_beta) + 1)
                        self.trading = "long"
                        self.trading_start = smoothed_signal[i]
                        self.save_trading_state()  # Save the new state
                        self.save_trading_start()  # Save the trading start value
                        logging.getLogger().trading(f"Placing long trade: BUY {stock_name1} --- SELL {stock_name2}")
                        place_market_order('BUY', quantity_1, stock_name1,stock_data=data)
                        place_market_order('SELL', quantity_2, stock_name2,stock_data=data)
                    else:
                        logging.getLogger().trading("Already existing positions for long. No Trade Execution today.")
                else:
                    logging.getLogger().trading(
                        f"No trade executed as both signals are either (+) or (-): "
                        f"smoothed_signal[i]: {smoothed_signal[i]} and "
                        f"d_smoothed_signal[i]: {d_smoothed_signal[i]}"
                    )
            
            elif self.trading == "long":
                logging.getLogger().trading("trading == long:")
                # Close long position if conditions are met
                if (
                    smoothed_signal[i] < self.trading_start 
                    or (
                        smoothed_signal[i] > 0 
                        and check_order_placed([stock_name1, stock_name2]) # Potential error here
                    )
                ):
                    # Close long trade only if there is an open position for stock_name1 or stock_name2
                    if current_positions[stock_name1] > 0 and current_positions[stock_name2] < 0:
                        leverage.iloc[i, 0] = 0
                        leverage.iloc[i, 1] = 0
                        self.trading = "not"
                        self.save_trading_state()  # Save the new state
                        self.save_trading_start()  # Save the trading start value
                        logging.getLogger().trading(f"Closing long trade: SELL {stock_name1} --and-- BUY {stock_name2}")
                        place_market_order('SELL', quantity_1, stock_name1,stock_data=data)
                        place_market_order('BUY', quantity_2, stock_name2,stock_data=data)
                else:
                    logging.getLogger().trading("Conditions for closing long trade not met."
                                                f" smoothed_signal[i]: {smoothed_signal[i]} >= "
                                                f" self.trading_start: {self.trading_start}"
                                            )
                    logging.getLogger().trading("Keep this f*ing Trade running")

            elif self.trading == "short":
                logging.getLogger().trading("trading == short:")
                # Close short position if conditions are met
                if (
                    smoothed_signal[i] > self.trading_start 
                    or (
                        smoothed_signal[i] < 0 
                        and check_order_placed([stock_name1, stock_name2])
                    )
                ):
                    # Close short trade only if there is an open position for stock_name1 or stock_name2
                    if current_positions[stock_name1] < 0 and current_positions[stock_name2] > 0:
                        leverage.iloc[i, 0] = 0
                        leverage.iloc[i, 1] = 0
                        self.trading = "not"
                        self.save_trading_state()  # Save the new state
                        self.save_trading_start()  # Save the trading start value
                        logging.getLogger().trading(f"Closing short trade: BUY {stock_name1} --and-- SELL {stock_name2}")
                        place_market_order('BUY', quantity_1, stock_name1,stock_data=data)
                        place_market_order('SELL', quantity_2, stock_name2,stock_data=data)
                else:
                    logging.getLogger().trading("Conditions for closing short trade not met."
                                                f" smoothed_signal[i]: {smoothed_signal[i]} <= "
                                                f" self.trading_start: {self.trading_start}"
                                            )
                    logging.getLogger().trading("Keep this f*ing Trade running")


            else:
                logging.critical("None of the trading options (long/short/not) matched.")
            logging.info("Kritische Funktion erfolgreich ausgeführt.")

        except Exception as e:
            logging.critical(f"Error during trading strategy execution: {e}", exc_info=True)
            logging.critical(f"Fehler in kritischer Funktion: {e}")

        return leverage
strategy = TradingStrategy()

def get_current_positions(tickers):
    """
    This function retrieves the current open positions for the given tickers.
    Returns a dictionary with the ticker as the key and the position as the value.
    """
    try:
        ib = IB()
        try:
            # Attempt to connect with a timeout
            ib.connect('127.0.0.1', 7497, clientId=get_unique_client_id(), timeout=30)
            logging.info("Connected successfully")

        except Exception as e:
            # Log any exception that occurs during connection
            logging.critical(f"Error connecting to API: {e}")

        
        positions = {ticker: 0 for ticker in tickers}
        for position in ib.positions():
            if position.contract.symbol in tickers:
                positions[position.contract.symbol] = position.position

        ib.disconnect()
        logging.info("Kritische Funktion erfolgreich ausgeführt.")
        return positions

    except Exception as e:
        logging.critical(f"Error retrieving current positions: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")
        return {ticker: 0 for ticker in tickers}

def check_order_placed(tickers):
    """
    This function checks if there are any open orders for the given tickers.
    Returns True if there are open orders, otherwise False.
    """
    try:
        ib = IB()
        try:
            # Attempt to connect with a timeout
            ib.connect('127.0.0.1', 7497, clientId=get_unique_client_id(), timeout=30)
            logging.info("Connected successfully")
            
        except Exception as e:
            # Log any exception that occurs during connection
            logging.critical(f"Error connecting to API: {e}")

        open_orders = ib.openOrders()
        for order in open_orders:
            if order.contract.symbol in tickers:
                ib.disconnect()
                return True

        ib.disconnect()
        logging.info("Kritische Funktion erfolgreich ausgeführt.")
        return False

    except Exception as e:
        logging.critical(f"Error checking open orders: {e}", exc_info=True)
        logging.critical(f"Fehler in kritischer Funktion: {e}")
        return False

def calculate_portfolio_realized_pnl(stock_data, orders_df, initial_cash=1000):
    """
    Berechnet den Realized PnL aus den gegebenen Orders und Aktienkursen.

    Parameters:
    - stock_data: pd.DataFrame, DataFrame mit Daten als Index (Datum) und Symbolen als Spalten, die tägliche Preise enthalten.
    - orders_df: pd.DataFrame, DataFrame mit Orders, Spalten sollten 'Timestamp', 'Action', 'Symbol', 'Quantity', 'FillPrice' enthalten.
    - initial_cash: float, Startkapital für die Portfolioberechnung.

    Returns:
    - realized_pnl_df: pd.DataFrame, DataFrame mit Datum als Index und einer Spalte 'RealizedPnL'.
    """
    try:
        # Vorbereiten der Orders
        symbols = orders_df['Symbol'].unique()
        portfolio = {symbol: 0 for symbol in symbols}  # Anzahl der Aktien im Portfolio
        cost_basis = {symbol: 0.0 for symbol in symbols}  # Einstandspreis pro Symbol
        cash = initial_cash
        cumulative_realized_pnl = 0  # Kumuliertes Realized PnL

        orders_df['Timestamp'] = pd.to_datetime(orders_df['Timestamp'])
        orders_df = orders_df.sort_values('Timestamp')
        orders_df['Date'] = orders_df['Timestamp'].dt.date

        # Vorbereiten der Aktienkurse
        stock_data = stock_data.copy()
        stock_data.index = pd.to_datetime(stock_data.index).date

        # Liste zur Speicherung der täglichen Realized PnL-Werte
        realized_pnl_values = []

        for current_date, prices in stock_data.iterrows():
            # Verarbeitung der heutigen Orders
            todays_orders = orders_df[orders_df['Date'] == current_date]
            for _, order in todays_orders.iterrows():
                action = order['Action'].upper()
                symbol = order['Symbol']
                quantity = order['Quantity']
                fill_price = order['FillPrice']

                if action == 'BUY':
                    if portfolio[symbol] < 0:  # Schließen einer Short-Position
                        qty_to_close = min(abs(portfolio[symbol]), quantity)
                        realized_pnl = (cost_basis[symbol] - fill_price) * qty_to_close
                        cash += fill_price * qty_to_close
                        portfolio[symbol] += qty_to_close
                        cumulative_realized_pnl += realized_pnl
                        quantity -= qty_to_close
                    if quantity > 0:  # Eröffnung oder Erhöhung einer Long-Position
                        new_cost_basis = (
                            (cost_basis[symbol] * portfolio[symbol] + fill_price * quantity) 
                            / (portfolio[symbol] + quantity)
                        ) if (portfolio[symbol] + quantity) != 0 else 0
                        portfolio[symbol] += quantity
                        cost_basis[symbol] = new_cost_basis
                        cash -= fill_price * quantity

                elif action == 'SELL':
                    if portfolio[symbol] > 0:  # Schließen einer Long-Position
                        qty_to_close = min(portfolio[symbol], quantity)
                        realized_pnl = (fill_price - cost_basis[symbol]) * qty_to_close
                        cash += fill_price * qty_to_close
                        portfolio[symbol] -= qty_to_close
                        cumulative_realized_pnl += realized_pnl
                        quantity -= qty_to_close
                    if quantity > 0:  # Eröffnung oder Erhöhung einer Short-Position
                        new_cost_basis = (
                            (cost_basis[symbol] * abs(portfolio[symbol]) + fill_price * quantity) 
                            / (abs(portfolio[symbol]) + quantity)
                        ) if (abs(portfolio[symbol]) + quantity) != 0 else 0
                        portfolio[symbol] -= quantity
                        cost_basis[symbol] = new_cost_basis
                        cash += fill_price * quantity

            # Tageswerte speichern
            realized_pnl_values.append((current_date, cumulative_realized_pnl))

        # DataFrame für Realized PnL erstellen
        realized_pnl_df = pd.DataFrame(realized_pnl_values, columns=['Date', 'RealizedPnL']).set_index('Date')

        logging.info("Realized PnL calculation completed successfully.")
        return realized_pnl_df

    except Exception as e:
        logging.critical(f"Error calculating Realized PnL: {e}", exc_info=True)
        return pd.DataFrame(columns=['Date', 'RealizedPnL']).set_index('Date')

def save_pnl_csv_and_plot(stock_data, orders_log_file, output_csv='realized_pnl_output.csv', output_plot='realized_pnl_plot.png', initial_cash=1000):
    """
    Berechnet den Realized PnL, speichert ihn in einer CSV-Datei und erstellt ein Plot.

    Parameters:
    - stock_data: pd.DataFrame, DataFrame mit Daten als Index (Datum) und Symbolen als Spalten, die tägliche Preise enthalten.
    - orders_log_file: str, Pfad zur CSV-Datei, die die Orders enthält.
    - output_csv: str, Dateiname zum Speichern der PnL-Daten als CSV.
    - output_plot: str, Dateiname zum Speichern des PnL-Plots.
    - initial_cash: float, Startkapital für die Portfolioberechnung.
    """
    try:
        # Load the orders
        orders_df = pd.read_csv(orders_log_file)

        # Check if necessary columns are present in the orders CSV
        required_columns = {'Timestamp', 'Action', 'Symbol', 'Quantity', 'FillPrice'}
        if not required_columns.issubset(orders_df.columns):
            missing = required_columns - set(orders_df.columns)
            raise ValueError(f"The orders CSV is missing the following required columns: {missing}")

        # Calculate the Realized PnL
        realized_pnl_df = calculate_portfolio_realized_pnl(stock_data, orders_df, initial_cash=initial_cash)

        # Datum des ersten Trades ermitteln
        orders_df['Timestamp'] = pd.to_datetime(orders_df['Timestamp'])
        orders_df['Date'] = orders_df['Timestamp'].dt.date
        first_trade_date = orders_df['Date'].min()

        # PnL-Daten ab dem ersten Trade filtern
        realized_pnl_df = realized_pnl_df[realized_pnl_df.index >= first_trade_date]

        # Save the Realized PnL DataFrame as CSV
        realized_pnl_df.to_csv(output_csv)
        logging.info(f"Realized PnL successfully saved as {output_csv}.")

        # Plotting Realized PnL
        plt.figure(figsize=(12, 6))
        plt.plot(realized_pnl_df.index, realized_pnl_df['RealizedPnL'], label='Realized PnL', color='green')
        plt.title('Realized PnL')
        plt.xlabel('Date')
        plt.ylabel('PnL (€)')
        plt.grid(True)
        plt.legend()
        plt.tight_layout()  # Optimize layout
        plt.savefig(output_plot)
        plt.close()  # Close the figure to free memory
        logging.info(f"Realized PnL plot successfully saved as {output_plot}.")

    except FileNotFoundError as fnf_error:
        logging.critical(f"File not found: {fnf_error}")
    except pd.errors.EmptyDataError:
        logging.critical("The CSV file is empty.")
    except pd.errors.ParserError:
        logging.critical("Error parsing the CSV file.")
    except ValueError as ve:
        logging.critical(f"Value error: {ve}")
    except Exception as e:
        logging.critical(f"Error saving Realized PnL CSV and plot: {e}", exc_info=True)

def main():
    scheduled_time = "15:51"  # Geplante Zeit definieren
    last_logged_time = None
    CHECK_INTERVAL = 5

    # Logs initialisieren
    initialize_logs()

    if not development_mode:
        print(f"Trading {symbol1} and {symbol2}")
        schedule.every().day.at(scheduled_time).do(update_and_trade)
        logging.getLogger().trading("#######################################################################################################################################################################")
        logging.getLogger().trading(f"######################################  Scheduler started. Waiting for the scheduled time {scheduled_time}... ########################################################")
        logging.getLogger().trading("#######################################################################################################################################################################")

    # Während im Produktionsmodus
    while not development_mode:
        try:
            # Überprüfen, ob der IB-Server aktiv ist
            if check_connection():
                current_time = datetime.now()

                # Log info nur, wenn eine Stunde seit dem letzten Log vergangen ist
                if last_logged_time is None or current_time >= last_logged_time + timedelta(minutes=20):
                    logging.info("IB server is alive.")
                    last_logged_time = current_time  # Update last logged time

                # Die geplante Aufgabe ausführen
                schedule.run_pending()  

            time.sleep(CHECK_INTERVAL)  # Alle CHECK_INTERVAL Sekunden überprüfen

        except Exception as e:
            logging.critical(f"IB Server is dead! {e}")  # Logge die Ausnahme

    # Entwicklungmodus Logik
    if development_mode:
        logging.getLogger().trading("########################################################################################################################################################################")
        logging.getLogger().trading(f"######################################  Script started in DEVELOPMENT-MODE... #########################################################################################")
        logging.getLogger().trading("########################################################################################################################################################################")
        update_and_trade()  # Manuelles Update und Handel für den Entwicklungsmodus

if __name__ == "__main__":
    
    start_time = time.time() 
    main()
    end_time = time.time()  # Endzeit erfassen

    elapsed_time = end_time - start_time  # Laufzeit berechnen
    print(f"Programmlaufzeit: {elapsed_time:.5f} Sekunden")