EJ MORDO POWIEDZ MI JEDNA RZECZ BO SKORO MAM TUTAJ FUNKCJE KTORA ODCZYTUJE DANE Z OTWARTEJ POZYCJI TYPU ENTRY SL ORAZ TP TO SKAD POBIERAM DANE CZASU ZAMKNIECIA POZYCJI? SKORO PRZY OTWARCIU PRZECIEZ JESZCZE NIE MA TYCH DANYCH?

    def load_closed_positions_from_csv(self, filepath: str):
        if not os.path.exists(filepath):
            self.closed_positions_table.setRowCount(0)
            return

        max_retries = 5
        for attempt in range(max_retries):
            try:
                with open(filepath, "rb") as f:
                    content = f.read()
                    content_hash = hash(content)

                if hasattr(self, "last_closed_positions_hash") and self.last_closed_positions_hash == content_hash:
                    return
                self.last_closed_positions_hash = content_hash

                decoded_content = content.decode("utf-8", errors="ignore")
                sample = decoded_content[:1024]
                csvfile = io.StringIO(decoded_content)

                csvfile = io.StringIO(decoded_content)

                # 👇 Wymuszamy obsługę pliku rozdzielanego tabulatorem
                dialect = csv.excel_tab

                reader = csv.reader(csvfile, dialect)
                next(reader, None)  # nagłówek

                new_tickets = set()
                self.closed_positions_table.setRowCount(0)

                def safe_float(s):
                    try:
                        return float(s.strip().replace('\xa0', '').replace(',', '.'))
                    except Exception:
                        return None

                def determine_close_reason(row_data):
                    global closed_by_ts_candidates  # Dostęp do globalnego setu

                    try:
                        ticket = row_data[0]
                        close_price = safe_float(row_data[6])
                        sl = safe_float(row_data[7])
                        tp = safe_float(row_data[8])

                        if close_price is None or close_price < 1e-6:
                            return None

                        # Ustawiamy bardziej realistyczną tolerancję na poślizg (np. 5 punktów)
                        TOLERANCE = 5e-4

                        # 1. Najpierw sprawdzamy, czy to nasz kandydat z Trailing Stopa
                        # Warunek sl > 0 jest ważny, żeby nie sprawdzać pozycji bez SL
                        if ticket in closed_by_ts_candidates and sl is not None and sl > 0 and abs(
                                close_price - sl) < TOLERANCE:
                            closed_by_ts_candidates.remove(ticket)
                            return "Trailing Stop"

                        # 2. Sprawdzamy standardowy SL
                        # Dodajemy warunek sl > 0, aby ignorować puste/zerowe wartości SL
                        if sl is not None and sl > 0 and abs(close_price - sl) < TOLERANCE:
                            return "SL"

                        # 3. Sprawdzamy standardowy TP
                        # Dodajemy warunek tp > 0
                        elif tp is not None and tp > 0 and abs(close_price - tp) < TOLERANCE:
                            return "TP"

                        # 4. Jeśli żaden z powyższych, to było zamknięcie ręczne
                        else:
                            # Jeśli ticket był kandydatem na TS, ale warunki nie pasowały, usuwamy go
                            if ticket in closed_by_ts_candidates:
                                closed_by_ts_candidates.remove(ticket)
                            return "ręcznie"

                    except Exception as e:
                        print(f"[ERROR] determine_close_reason failed: {e}")
                        return "Unknown"

                if not hasattr(self, "tickets_status"):
                    self.tickets_status = {}

                # Sprawdź wiek pliku — jeśli bardzo świeży, to nie suppressuj
                file_age = time.time() - os.path.getmtime(filepath)
                suppress_initial_notifications = file_age > 5  # jeśli starszy niż 5 sekund → uznaj za stare pozycje

                tickets_status_changed = False

                for row_data in reader:
                    if len(row_data) < 11:
                        print(f"⚠️ Pominięto niekompletny wiersz: {row_data}")
                        continue
                    ticket = row_data[0]
                    new_tickets.add(ticket)

                    row = self.closed_positions_table.rowCount()
                    self.closed_positions_table.insertRow(row)

                    ordered_data = [
                        row_data[0],  # Ticket
                        row_data[1],  # Entry time
                        row_data[2],  # Symbol
                        row_data[3],  # Type
                        row_data[4],  # Volume
                        row_data[5],  # Entry price
                        row_data[6],  # Close price
                        row_data[7],  # SL
                        row_data[8],  # TP
                        row_data[9],  # Close time
                        row_data[10],  # Profit
                    ]

                    for col, data in enumerate(ordered_data):
                        item = QTableWidgetItem(data)
                        item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)

                        if col == 10:  # Profit column
                            try:
                                profit_value = float(data)
                                if profit_value < 0:
                                    item.setBackground(QColor(200, 100, 100))
                                elif profit_value > 0:
                                    item.setBackground(QColor(100, 150, 100))
                            except ValueError:
                                pass

                        self.closed_positions_table.setItem(row, col, item)

                    close_price = safe_float(row_data[6])
                    status = self.tickets_status.get(ticket, None)

                    if status is None:
                        # Nowy ticket – uznaj go za widziany tylko jeśli suppressujemy
                        self.tickets_status[ticket] = suppress_initial_notifications
                        tickets_status_changed = True

                    elif status is False and close_price is not None and close_price > 0:
                        # Pojawiła się nowa zamknięta pozycja
                        reason = determine_close_reason(row_data)
                        symbol = row_data[2]
                        profit = row_data[10]

                        try:
                            profit_value = float(profit)
                            if profit_value < 0:
                                emoji = "📉"
                            elif profit_value > 0:
                                emoji = "📈"
                            else:
                                emoji = "🔁"
                        except Exception:
                            emoji = "🔒"

                        message = f"{emoji} Pozycja [{ticket}] zamknięta {reason} (zysk: {profit})\n"
                        logger.info(message)
                        log_console.scroll_to_bottom()
                        notify("TorekMarketKiller", message)

                        self.tickets_status[ticket] = True
                        tickets_status_changed = True

                # Usuwamy tickety, których nie ma już w pliku
                for saved_ticket in list(self.tickets_status.keys()):
                    if saved_ticket not in new_tickets:
                        del self.tickets_status[saved_ticket]
                        tickets_status_changed = True

                if tickets_status_changed:
                    self.save_seen_tickets(self.tickets_status)

                break

            except PermissionError:
                if attempt < max_retries - 1:
                    time.sleep(0.4)
                else:
                    print(f"❌ Nie udało się odczytać pliku {filepath} po kilku próbach.")