#!/usr/bin/env python3
"""
NeedfulDings Quant & Backtesting Workstation (PyQt6 Native Desktop & Remote GUI)
================================================================================
Hardware-accelerated 3-Pane Dynamic Financial Chart Renderer & Strategy Studio
capable of rendering all 12 Core Strategy Archetypes + all 42 Expert Advisors (EA-01 .. EA-42)
from the Neural Core Chiplet Cluster.

Supports:
  - Interactive 3-Pane QPainter Canvas:
      * Pane 1: OHLC Candlesticks / Heikin-Ashi / Clean Line, Volume Histogram,
                Dynamic Strategy Shaded Bands/Clouds (Ichimoku Kumo, Bollinger/Keltner,
                Donchian, VWAP Sigma, SMC OrderBlocks), Multi-Line & Step-Line Overlays,
                Entry-to-Exit Trade Spans, and BUY/SELL/SHORT/COVER/SL/TP Markers.
      * Pane 2: Synchronized Strategy Oscillator (RSI, OU Z-Score, Squeeze Momentum,
                Kalman Velocity, L2 Orderflow Imbalance, 42-EA Swarm Activation).
      * Pane 3: Strategy Equity vs. Buy-&-Hold Benchmark + Multi-Strategy Shootout
                Comparison Curves + Underwater Drawdown (%) Sub-Layer.
  - Mouse Wheel Zoom, Left-Click Drag Pan, Right-Click Reset View, Hover Crosshair HUD.
  - Trade-by-Trade Journal where clicking any trade focuses & highlights the span on chart.
  - Multi-Strategy Shootout Leaderboard across Core Archetypes & 42 EAs.
  - SIMD ML Hyperopt Integration & FIX 4.4 Micro-Slice Execution Bridge.
  - CLI Headless Verification Mode:
      QT_QPA_PLATFORM=offscreen python3 quant_workstation_qt.py --self-test --snapshot out.png
"""
import argparse
import json
import math
import os
import sys
import time
import urllib.request
from typing import Any, Dict, List, Optional, Tuple

from PyQt6.QtCore import QPointF, QRectF, Qt, QTimer, pyqtSignal
from PyQt6.QtGui import (
    QAction,
    QBrush,
    QColor,
    QFont,
    QLinearGradient,
    QPainter,
    QPainterPath,
    QPen,
    QPolygonF,
)
from PyQt6.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QDoubleSpinBox,
    QFileDialog,
    QFrame,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QHeaderView,
    QLabel,
    QMainWindow,
    QPushButton,
    QSplitter,
    QSpinBox,
    QStatusBar,
    QTabWidget,
    QTableWidget,
    QTableWidgetItem,
    QTextEdit,
    QTreeWidget,
    QTreeWidgetItem,
    QVBoxLayout,
    QWidget,
)

# Ensure local directory is in sys.path so universal_backtest_engine imports cleanly
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if SCRIPT_DIR not in sys.path:
    sys.path.insert(0, SCRIPT_DIR)
if "/root" not in sys.path:
    sys.path.insert(0, "/root")

from universal_backtest_engine import (
    CORE_STRATEGIES,
    FULL_42_EA_DEFINITIONS,
    UniversalBacktestEngine,
    universal_backtester,
)


def parse_hex_color(color_str: str, default_alpha: int = 255) -> QColor:
    """Parses #RRGGBB or rgba(r,g,b,a) into QColor."""
    if not color_str:
        return QColor(56, 189, 248, default_alpha)
    s = color_str.strip()
    if s.startswith("rgba(") and s.endswith(")"):
        parts = [p.strip() for p in s[5:-1].split(",")]
        if len(parts) == 4:
            r, g, b = int(float(parts[0])), int(float(parts[1])), int(float(parts[2]))
            a_float = float(parts[3])
            a = int(a_float * 255) if a_float <= 1.0 else int(a_float)
            return QColor(r, g, b, max(0, min(255, a)))
    c = QColor(s)
    if not c.isValid():
        c = QColor("#38bdf8")
    if default_alpha < 255:
        c.setAlpha(default_alpha)
    return c


class DynamicQuantChartWidget(QWidget):
    """
    Synchronized 3-Pane Hardware-Accelerated QPainter Financial Chart Studio.
    Renders any strategy's `visual_schema` without hardcoding indicator types.
    """

    def __init__(self, parent: Optional[QWidget] = None):
        super().__init__(parent)
        self.setMinimumSize(820, 520)
        self.setMouseTracking(True)

        self.result_data: Optional[Dict[str, Any]] = None
        self.comparison_data: Optional[Dict[str, Any]] = None
        self.chart_mode: str = "candles"  # "candles" | "heikin_ashi" | "line"

        # Viewport state (bar index range [view_start, view_end])
        self.view_start: int = 0
        self.view_end: int = 0

        # Mouse interaction state
        self.hover_pos: Optional[QPointF] = None
        self.hover_bar_idx: int = -1
        self.is_panning: bool = False
        self.pan_last_x: float = 0.0

        # Highlighted trade span (when clicking a row in the Trade Journal)
        self.highlighted_span: Optional[Dict[str, Any]] = None

        # Fonts
        self.font_title = QFont("DejaVu Sans Mono", 9, QFont.Weight.Bold)
        self.font_hud = QFont("DejaVu Sans Mono", 8)
        self.font_axis = QFont("DejaVu Sans Mono", 8)
        self.font_badge = QFont("DejaVu Sans Mono", 7, QFont.Weight.Bold)

    def set_backtest_result(self, data: Dict[str, Any]):
        self.result_data = data
        self.highlighted_span = None
        candles = data.get("candles", [])
        n = len(candles)
        self.view_start = 0
        self.view_end = max(n - 1, 1)
        self.hover_bar_idx = n - 1 if n > 0 else -1
        self.update()

    def set_comparison_result(self, cmp_data: Optional[Dict[str, Any]]):
        self.comparison_data = cmp_data
        self.update()

    def set_chart_mode(self, mode: str):
        self.chart_mode = mode
        self.update()

    def focus_trade_span(self, entry_step: int, exit_step: int, span_meta: Optional[Dict[str, Any]] = None):
        if not self.result_data:
            return
        candles = self.result_data.get("candles", [])
        n = len(candles)
        if n == 0:
            return
        center = (entry_step + exit_step) // 2
        span_len = max(exit_step - entry_step, 10)
        half_win = max(span_len * 2, 35)
        self.view_start = max(0, center - half_win)
        self.view_end = min(n - 1, center + half_win)
        self.highlighted_span = span_meta or {"entry_step": entry_step, "exit_step": exit_step}
        self.hover_bar_idx = exit_step
        self.update()

    def reset_zoom(self):
        if not self.result_data:
            return
        n = len(self.result_data.get("candles", []))
        self.view_start = 0
        self.view_end = max(n - 1, 1)
        self.highlighted_span = None
        self.update()

    # --- Mouse Events (Zoom, Pan, Crosshair) ---
    def wheelEvent(self, event):
        if not self.result_data:
            return
        candles = self.result_data.get("candles", [])
        n = len(candles)
        if n < 20:
            return
        delta = event.angleDelta().y()
        span = max(self.view_end - self.view_start, 20)
        factor = 0.82 if delta > 0 else 1.22
        new_span = max(25, min(n - 1, int(span * factor)))
        anchor = self.hover_bar_idx if (self.view_start <= self.hover_bar_idx <= self.view_end) else (self.view_start + self.view_end) // 2
        ratio = (anchor - self.view_start) / max(span, 1)
        new_start = int(anchor - ratio * new_span)
        new_end = new_start + new_span
        if new_start < 0:
            new_start = 0
            new_end = min(n - 1, new_span)
        if new_end >= n:
            new_end = n - 1
            new_start = max(0, new_end - new_span)
        self.view_start = new_start
        self.view_end = new_end
        self.update()

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.is_panning = True
            self.pan_last_x = event.position().x()
        elif event.button() == Qt.MouseButton.RightButton:
            self.reset_zoom()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.is_panning = False

    def mouseMoveEvent(self, event):
        pos = event.position()
        self.hover_pos = pos
        if not self.result_data:
            return
        candles = self.result_data.get("candles", [])
        n = len(candles)
        if n == 0:
            return

        left_pad = 14.0
        right_axis_w = 78.0
        plot_w = max(self.width() - left_pad - right_axis_w, 100.0)
        span = max(self.view_end - self.view_start, 1)

        if self.is_panning:
            dx = pos.x() - self.pan_last_x
            bars_shift = int(round(-dx / (plot_w / span)))
            if bars_shift != 0:
                new_start = max(0, min(n - 1 - span, self.view_start + bars_shift))
                self.view_start = new_start
                self.view_end = new_start + span
                self.pan_last_x = pos.x()

        rel_x = max(0.0, min(plot_w, pos.x() - left_pad))
        idx = self.view_start + int(round((rel_x / plot_w) * span))
        self.hover_bar_idx = max(self.view_start, min(self.view_end, idx))
        self.update()

    # --- Coordinate Helpers ---
    def _idx_to_x(self, idx: int, plot_rect: QRectF) -> float:
        span = max(self.view_end - self.view_start, 1)
        return plot_rect.left() + ((idx - self.view_start) / float(span)) * plot_rect.width()

    @staticmethod
    def _val_to_y(val: float, v_min: float, v_max: float, rect: QRectF) -> float:
        rng = max(v_max - v_min, 1e-12)
        norm = (val - v_min) / rng
        return rect.bottom() - norm * rect.height()

    # --- Main QPainter Render Pipeline ---
    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
        painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True)

        w = float(self.width())
        h = float(self.height())

        # Deep institutional dark background
        bg_grad = QLinearGradient(0, 0, 0, h)
        bg_grad.setColorAt(0.0, QColor("#050811"))
        bg_grad.setColorAt(1.0, QColor("#090d18"))
        painter.fillRect(QRectF(0, 0, w, h), bg_grad)

        if not self.result_data or not self.result_data.get("candles"):
            painter.setPen(QColor("#64748b"))
            painter.setFont(self.font_title)
            painter.drawText(QRectF(0, 0, w, h), Qt.AlignmentFlag.AlignCenter, "Lade Strategie & Marktdaten...")
            return

        left_pad = 12.0
        right_axis_w = 78.0
        top_pad = 8.0
        bottom_axis_h = 24.0
        gap = 8.0

        usable_h = h - top_pad - bottom_axis_h - 2 * gap
        p1_h = usable_h * 0.54
        p2_h = usable_h * 0.22
        p3_h = usable_h * 0.24
        plot_w = w - left_pad - right_axis_w

        rect_p1 = QRectF(left_pad, top_pad, plot_w, p1_h)
        rect_p2 = QRectF(left_pad, rect_p1.bottom() + gap, plot_w, p2_h)
        rect_p3 = QRectF(left_pad, rect_p2.bottom() + gap, plot_w, p3_h)

        self._draw_pane_frame(painter, rect_p1, right_axis_w)
        self._draw_pane_frame(painter, rect_p2, right_axis_w)
        self._draw_pane_frame(painter, rect_p3, right_axis_w)

        self._render_pane1_price_and_overlays(painter, rect_p1, right_axis_w)
        self._render_pane2_oscillator(painter, rect_p2, right_axis_w)
        self._render_pane3_equity_and_drawdown(painter, rect_p3, right_axis_w)
        self._render_time_axis(painter, QRectF(left_pad, rect_p3.bottom(), plot_w, bottom_axis_h))
        self._render_crosshair(painter, rect_p1, rect_p2, rect_p3)

    def _draw_pane_frame(self, painter: QPainter, rect: QRectF, axis_w: float):
        painter.setPen(QPen(QColor("#1e293b"), 1.0))
        painter.setBrush(QColor("#070b14"))
        painter.drawRect(rect)

        # Right price axis gutter
        axis_rect = QRectF(rect.right(), rect.top(), axis_w, rect.height())
        painter.fillRect(axis_rect, QColor("#04070e"))
        painter.drawRect(axis_rect)

        # Horizontal & vertical subtle grid lines
        grid_pen = QPen(QColor(30, 41, 59, 110), 1.0, Qt.PenStyle.DotLine)
        painter.setPen(grid_pen)
        for i in range(1, 5):
            y = rect.top() + (rect.height() * i / 5.0)
            painter.drawLine(QPointF(rect.left(), y), QPointF(rect.right(), y))
        for i in range(1, 8):
            x = rect.left() + (rect.width() * i / 8.0)
            painter.drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom()))

    def _render_right_axis_labels(
        self, painter: QPainter, rect: QRectF, axis_w: float, v_min: float, v_max: float, decimals: int = 2, suffix: str = ""
    ):
        painter.setFont(self.font_axis)
        painter.setPen(QColor("#94a3b8"))
        steps = 4
        for i in range(steps + 1):
            frac = i / float(steps)
            val = v_max - frac * (v_max - v_min)
            y = rect.top() + frac * rect.height()
            txt = f"{val:.{decimals}f}{suffix}"
            lbl_rect = QRectF(rect.right() + 4, y - 8, axis_w - 6, 16)
            painter.drawText(lbl_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, txt)

    # --- Pane 1: Price, Volume, Strategy Bands/Clouds, Overlays, Trade Spans & Signals ---
    def _render_pane1_price_and_overlays(self, painter: QPainter, rect: QRectF, axis_w: float):
        candles = self.result_data["candles"]
        schema = self.result_data.get("visual_schema", {})
        decimals = int(schema.get("decimals", 2))
        i0 = max(0, self.view_start)
        i1 = min(len(candles) - 1, self.view_end)
        if i1 <= i0:
            return

        # Compute min/max across visible candles, overlays, and bands
        v_min = float("inf")
        v_max = float("-inf")
        max_vol = 1.0

        for idx in range(i0, i1 + 1):
            c = candles[idx]
            v_min = min(v_min, float(c["low"]))
            v_max = max(v_max, float(c["high"]))
            max_vol = max(max_vol, float(c.get("volume", 1.0)))

        for ov in schema.get("overlays", []):
            data = ov.get("data", [])
            for idx in range(i0, min(i1 + 1, len(data))):
                val = data[idx]
                if val is not None:
                    v_min = min(v_min, float(val))
                    v_max = max(v_max, float(val))

        for bd in schema.get("bands", []):
            u_data = bd.get("upper", [])
            l_data = bd.get("lower", [])
            for idx in range(i0, min(i1 + 1, len(u_data), len(l_data))):
                if u_data[idx] is not None:
                    v_max = max(v_max, float(u_data[idx]))
                    v_min = min(v_min, float(u_data[idx]))
                if l_data[idx] is not None:
                    v_min = min(v_min, float(l_data[idx]))
                    v_max = max(v_max, float(l_data[idx]))

        if not math.isfinite(v_min) or not math.isfinite(v_max) or v_max <= v_min:
            v_min, v_max = 0.0, 100.0
        pad = (v_max - v_min) * 0.07
        v_min -= pad
        v_max += pad

        painter.save()
        painter.setClipRect(rect)

        # 0. Highlighted Trade Span Focus Banner (if user clicked a trade in the table)
        if self.highlighted_span:
            es = int(self.highlighted_span.get("entry_step", -1))
            xs = int(self.highlighted_span.get("exit_step", -1))
            if es >= 0 and xs >= es:
                x_start = self._idx_to_x(es, rect)
                x_end = self._idx_to_x(xs, rect)
                hl_color = QColor(16, 185, 129, 38) if self.highlighted_span.get("pnl", 0) >= 0 else QColor(244, 63, 94, 38)
                painter.fillRect(QRectF(x_start, rect.top(), max(x_end - x_start, 4.0), rect.height()), hl_color)
                painter.setPen(QPen(QColor("#fbbf24"), 1.5, Qt.PenStyle.DashLine))
                painter.drawRect(QRectF(x_start, rect.top() + 2, max(x_end - x_start, 4.0), rect.height() - 4))

        # 1. Volume Bars in bottom 18% of Pane 1
        span = max(i1 - i0, 1)
        bar_w = max(1.5, min(14.0, (rect.width() / float(span)) * 0.68))
        vol_h_max = rect.height() * 0.18
        for idx in range(i0, i1 + 1):
            c = candles[idx]
            x = self._idx_to_x(idx, rect)
            vol_ratio = float(c.get("volume", 0.0)) / max_vol
            vh = vol_ratio * vol_h_max
            is_bull = float(c["close"]) >= float(c["open"])
            v_col = QColor(16, 185, 129, 48) if is_bull else QColor(244, 63, 94, 48)
            painter.fillRect(QRectF(x - bar_w / 2.0, rect.bottom() - vh, bar_w, vh), v_col)

        # 2. Shaded Strategy Bands / Clouds (Ichimoku Kumo, Bollinger/Keltner, Donchian, VWAP Sigma)
        for bd in schema.get("bands", []):
            u_data = bd.get("upper", [])
            l_data = bd.get("lower", [])
            fill_col = parse_hex_color(bd.get("fill", "rgba(56, 189, 248, 0.10)"))
            poly = QPolygonF()
            lower_pts: List[QPointF] = []
            for idx in range(i0, min(i1 + 1, len(u_data), len(l_data))):
                u_val = u_data[idx]
                l_val = l_data[idx]
                if u_val is None or l_val is None:
                    continue
                x = self._idx_to_x(idx, rect)
                poly.append(QPointF(x, self._val_to_y(float(u_val), v_min, v_max, rect)))
                lower_pts.append(QPointF(x, self._val_to_y(float(l_val), v_min, v_max, rect)))
            for pt in reversed(lower_pts):
                poly.append(pt)
            if not poly.isEmpty():
                painter.setPen(Qt.PenStyle.NoPen)
                painter.setBrush(QBrush(fill_col))
                painter.drawPolygon(poly)

        # 3. Price Rendering: Candlesticks | Heikin-Ashi | Clean Line
        if self.chart_mode == "line":
            path = QPainterPath()
            started = False
            for idx in range(i0, i1 + 1):
                x = self._idx_to_x(idx, rect)
                y = self._val_to_y(float(candles[idx]["close"]), v_min, v_max, rect)
                if not started:
                    path.moveTo(x, y)
                    started = True
                else:
                    path.lineTo(x, y)
            painter.setPen(QPen(QColor("#e2e8f0"), 1.8))
            painter.setBrush(Qt.BrushStyle.NoBrush)
            painter.drawPath(path)
        else:
            ha_prev_open = float(candles[max(0, i0 - 1)]["open"])
            ha_prev_close = float(candles[max(0, i0 - 1)]["close"])
            for idx in range(i0, i1 + 1):
                c = candles[idx]
                o, hi, lo, cl = float(c["open"]), float(c["high"]), float(c["low"]), float(c["close"])
                if self.chart_mode == "heikin_ashi":
                    ha_close = (o + hi + lo + cl) / 4.0
                    ha_open = (ha_prev_open + ha_prev_close) / 2.0
                    ha_high = max(hi, ha_open, ha_close)
                    ha_low = min(lo, ha_open, ha_close)
                    ha_prev_open, ha_prev_close = ha_open, ha_close
                    o, hi, lo, cl = ha_open, ha_high, ha_low, ha_close

                is_bull = cl >= o
                col = QColor("#10b981") if is_bull else QColor("#f43f5e")
                x = self._idx_to_x(idx, rect)
                y_hi = self._val_to_y(hi, v_min, v_max, rect)
                y_lo = self._val_to_y(lo, v_min, v_max, rect)
                y_o = self._val_to_y(o, v_min, v_max, rect)
                y_c = self._val_to_y(cl, v_min, v_max, rect)

                painter.setPen(QPen(col, 1.1))
                painter.drawLine(QPointF(x, y_hi), QPointF(x, y_lo))
                body_top = min(y_o, y_c)
                body_h = max(abs(y_c - y_o), 1.4)
                painter.fillRect(QRectF(x - bar_w / 2.0, body_top, bar_w, body_h), col)

        # 4. Strategy Overlay Lines & Step-Stops (SuperTrend, Ichimoku, Bollinger, VWAP, Kalman)
        for ov in schema.get("overlays", []):
            data = ov.get("data", [])
            col = parse_hex_color(ov.get("color", "#38bdf8"))
            width = float(ov.get("width", 1.5))
            style_str = ov.get("style", "solid")
            pen_style = Qt.PenStyle.DashLine if style_str == "dashed" else Qt.PenStyle.SolidLine
            painter.setPen(QPen(col, width, pen_style))
            painter.setBrush(Qt.BrushStyle.NoBrush)

            path = QPainterPath()
            started = False
            prev_x, prev_y = 0.0, 0.0
            for idx in range(i0, min(i1 + 1, len(data))):
                val = data[idx]
                if val is None:
                    started = False
                    continue
                x = self._idx_to_x(idx, rect)
                y = self._val_to_y(float(val), v_min, v_max, rect)
                if not started:
                    path.moveTo(x, y)
                    started = True
                else:
                    if style_str == "step":
                        path.lineTo(x, prev_y)
                        path.lineTo(x, y)
                    else:
                        path.lineTo(x, y)
                prev_x, prev_y = x, y
            painter.drawPath(path)

        # 5. Dashed Entry-to-Exit Trade Spans
        for sp in schema.get("trade_spans", []):
            es = int(sp.get("entry_step", -1))
            xs = int(sp.get("exit_step", -1))
            if xs < i0 or es > i1:
                continue
            x1 = self._idx_to_x(es, rect)
            y1 = self._val_to_y(float(sp["entry_price"]), v_min, v_max, rect)
            x2 = self._idx_to_x(xs, rect)
            y2 = self._val_to_y(float(sp["exit_price"]), v_min, v_max, rect)
            win = float(sp.get("pnl", 0.0)) >= 0.0
            span_col = QColor("#34d399") if win else QColor("#fb7185")
            painter.setPen(QPen(span_col, 1.3, Qt.PenStyle.DashLine))
            painter.drawLine(QPointF(x1, y1), QPointF(x2, y2))

        # 6. BUY / SELL / SHORT / COVER / SL / TP Execution Markers
        painter.setFont(self.font_badge)
        for tr in self.result_data.get("trades", []):
            step = int(tr.get("step", -1))
            if step < i0 or step > i1:
                continue
            x = self._idx_to_x(step, rect)
            price = float(tr.get("price", 0.0))
            y = self._val_to_y(price, v_min, v_max, rect)
            ttype = tr.get("type", "BUY")
            reason = tr.get("reason", "")

            tri = QPolygonF()
            if ttype == "BUY":
                m_col = QColor("#10b981")
                tri.append(QPointF(x, y + 4))
                tri.append(QPointF(x - 5.5, y + 14))
                tri.append(QPointF(x + 5.5, y + 14))
                lbl = "B"
                ly = y + 24
            elif ttype == "SHORT":
                m_col = QColor("#f59e0b")
                tri.append(QPointF(x, y - 4))
                tri.append(QPointF(x - 5.5, y - 14))
                tri.append(QPointF(x + 5.5, y - 14))
                lbl = "SH"
                ly = y - 17
            else:
                is_tp = reason == "TAKE_PROFIT"
                is_sl = reason == "STOP_LOSS"
                m_col = QColor("#22d3ee") if is_tp else (QColor("#f43f5e") if is_sl else QColor("#eab308"))
                tri.append(QPointF(x, y - 4))
                tri.append(QPointF(x - 5.5, y - 14))
                tri.append(QPointF(x + 5.5, y - 14))
                lbl = "TP" if is_tp else ("SL" if is_sl else "S")
                ly = y - 17

            painter.setPen(QPen(QColor("#020617"), 1.0))
            painter.setBrush(QBrush(m_col))
            painter.drawPolygon(tri)
            painter.setPen(m_col)
            painter.drawText(QRectF(x - 12, ly - 6, 24, 12), Qt.AlignmentFlag.AlignCenter, lbl)

        painter.restore()
        self._render_right_axis_labels(painter, rect, axis_w, v_min, v_max, decimals)
        self._render_pane1_hud_legend(painter, rect)

    def _render_pane1_hud_legend(self, painter: QPainter, rect: QRectF):
        candles = self.result_data["candles"]
        schema = self.result_data.get("visual_schema", {})
        meta = self.result_data.get("strategy_meta", {})
        decimals = int(schema.get("decimals", 2))
        idx = self.hover_bar_idx if (0 <= self.hover_bar_idx < len(candles)) else len(candles) - 1
        c = candles[idx]

        # Translucent HUD pill (3 rows: Title, OHLCV, Indicator Chips)
        hud_rect = QRectF(rect.left() + 8, rect.top() + 6, min(rect.width() - 16, 860), 58)
        painter.fillRect(hud_rect, QColor(5, 8, 17, 215))
        painter.setPen(QPen(QColor("#1e293b"), 1.0))
        painter.drawRect(hud_rect)

        painter.setFont(self.font_title)
        sym = schema.get("symbol", "BTCUSDT")
        tf = schema.get("interval", "15m")
        sname = meta.get("name", schema.get("strategy_name", "Strategy"))
        cluster = meta.get("cluster", "CORE")
        painter.setPen(QColor("#38bdf8"))
        title_str = f"{sym} [{tf}]  •  {sname} ({cluster})"
        painter.drawText(QPointF(hud_rect.left() + 8, hud_rect.top() + 16), title_str)

        painter.setFont(self.font_hud)
        o, hi, lo, cl, vol = float(c["open"]), float(c["high"]), float(c["low"]), float(c["close"]), float(c.get("volume", 0.0))
        chg_pct = ((cl - o) / max(o, 1e-12)) * 100.0
        ohlc_col = QColor("#10b981") if cl >= o else QColor("#f43f5e")
        painter.setPen(ohlc_col)
        ohlc_str = (
            f"T: {c.get('time','')}   O: {o:.{decimals}f}   H: {hi:.{decimals}f}   "
            f"L: {lo:.{decimals}f}   C: {cl:.{decimals}f} ({chg_pct:+.2f}%)   Vol: {vol:,.0f}"
        )
        painter.drawText(QPointF(hud_rect.left() + 8, hud_rect.top() + 34), ohlc_str)

        # Overlay Legend Chips on third row
        x_cursor = hud_rect.left() + 8
        for ov in schema.get("overlays", [])[:5]:
            data = ov.get("data", [])
            val = data[idx] if idx < len(data) else None
            val_txt = f"{val:.{decimals}f}" if val is not None else "—"
            col = parse_hex_color(ov.get("color", "#38bdf8"))
            painter.setPen(col)
            chip = f"■ {ov.get('name','Ind')}: {val_txt}"
            painter.drawText(QPointF(x_cursor, hud_rect.top() + 50), chip)
            x_cursor += 170

    # --- Pane 2: Dynamic Strategy Oscillator ---
    def _render_pane2_oscillator(self, painter: QPainter, rect: QRectF, axis_w: float):
        schema = self.result_data.get("visual_schema", {})
        osc = schema.get("oscillator", {})
        series_list = osc.get("series", [])
        candles = self.result_data["candles"]
        i0 = max(0, self.view_start)
        i1 = min(len(candles) - 1, self.view_end)
        if i1 <= i0 or not series_list:
            return

        v_min = float("inf")
        v_max = float("-inf")
        if osc.get("min") is not None and osc.get("max") is not None:
            v_min = float(osc["min"])
            v_max = float(osc["max"])
        else:
            for s in series_list:
                data = s.get("data", [])
                for idx in range(i0, min(i1 + 1, len(data))):
                    if data[idx] is not None:
                        v_min = min(v_min, float(data[idx]))
                        v_max = max(v_max, float(data[idx]))
            for lv in osc.get("levels", []):
                v_min = min(v_min, float(lv["value"]))
                v_max = max(v_max, float(lv["value"]))
            if not math.isfinite(v_min) or not math.isfinite(v_max) or v_max <= v_min:
                v_min, v_max = -2.0, 2.0
            pad = (v_max - v_min) * 0.12
            v_min -= pad
            v_max += pad

        painter.save()
        painter.setClipRect(rect)

        # Horizontal Threshold Levels (e.g., RSI 30/70, Z-Score ±1.6σ, 0-line)
        for lv in osc.get("levels", []):
            lv_val = float(lv.get("value", 0.0))
            y = self._val_to_y(lv_val, v_min, v_max, rect)
            l_col = parse_hex_color(lv.get("color", "#475569"))
            st = Qt.PenStyle.DashLine if lv.get("style") == "dashed" else Qt.PenStyle.DotLine
            painter.setPen(QPen(l_col, 1.0, st))
            painter.drawLine(QPointF(rect.left(), y), QPointF(rect.right(), y))

        osc_type = osc.get("type", "line")
        span = max(i1 - i0, 1)
        bar_w = max(1.5, min(12.0, (rect.width() / float(span)) * 0.62))
        y_zero = self._val_to_y(0.0, v_min, v_max, rect)

        for s_idx, s in enumerate(series_list):
            data = s.get("data", [])
            base_col = parse_hex_color(s.get("color", "#a855f7"))
            if osc_type == "histogram" and s_idx == 0:
                for idx in range(i0, min(i1 + 1, len(data))):
                    val = data[idx]
                    if val is None:
                        continue
                    fval = float(val)
                    x = self._idx_to_x(idx, rect)
                    y = self._val_to_y(fval, v_min, v_max, rect)
                    h_col = QColor(16, 185, 129, 175) if fval >= 0 else QColor(244, 63, 94, 175)
                    top_y = min(y, y_zero)
                    box_h = max(abs(y - y_zero), 1.2)
                    painter.fillRect(QRectF(x - bar_w / 2.0, top_y, bar_w, box_h), h_col)
            else:
                path = QPainterPath()
                started = False
                for idx in range(i0, min(i1 + 1, len(data))):
                    val = data[idx]
                    if val is None:
                        started = False
                        continue
                    x = self._idx_to_x(idx, rect)
                    y = self._val_to_y(float(val), v_min, v_max, rect)
                    if not started:
                        path.moveTo(x, y)
                        started = True
                    else:
                        path.lineTo(x, y)
                painter.setPen(QPen(base_col, 1.6))
                painter.setBrush(Qt.BrushStyle.NoBrush)
                painter.drawPath(path)

        # Pane 2 Title & Live Value HUD
        painter.setFont(self.font_hud)
        painter.setPen(QColor("#c084fc"))
        idx_h = self.hover_bar_idx if (0 <= self.hover_bar_idx < len(candles)) else i1
        val_parts = []
        for s in series_list:
            d = s.get("data", [])
            v = d[idx_h] if idx_h < len(d) else None
            if v is not None:
                val_parts.append(f"{s.get('name','')}: {float(v):.2f}")
        osc_hdr = f"OSCILLATOR  •  {osc.get('name', 'Strategy Filter')}   |   " + "   ".join(val_parts)
        painter.drawText(QPointF(rect.left() + 8, rect.top() + 14), osc_hdr)

        painter.restore()
        self._render_right_axis_labels(painter, rect, axis_w, v_min, v_max, 2)

    # --- Pane 3: Strategy Equity vs Benchmark, Multi-Strategy Shootout & Underwater Drawdown ---
    def _render_pane3_equity_and_drawdown(self, painter: QPainter, rect: QRectF, axis_w: float):
        schema = self.result_data.get("visual_schema", {})
        eq_data = schema.get("strategy_equity", [])
        bnh_data = schema.get("benchmark_equity", [])
        dd_data = schema.get("drawdown_pct", [])
        candles = self.result_data["candles"]
        i0 = max(0, self.view_start)
        i1 = min(len(candles) - 1, self.view_end)
        if i1 <= i0 or not eq_data:
            return

        v_min = float("inf")
        v_max = float("-inf")
        for idx in range(i0, min(i1 + 1, len(eq_data), len(bnh_data))):
            if eq_data[idx] is not None:
                v_min = min(v_min, float(eq_data[idx]))
                v_max = max(v_max, float(eq_data[idx]))
            if bnh_data[idx] is not None:
                v_min = min(v_min, float(bnh_data[idx]))
                v_max = max(v_max, float(bnh_data[idx]))

        if self.comparison_data and self.comparison_data.get("equity_series"):
            for s in self.comparison_data["equity_series"]:
                s_eq = s.get("equity", [])
                for idx in range(i0, min(i1 + 1, len(s_eq))):
                    if s_eq[idx] is not None:
                        v_min = min(v_min, float(s_eq[idx]))
                        v_max = max(v_max, float(s_eq[idx]))

        if not math.isfinite(v_min) or not math.isfinite(v_max) or v_max <= v_min:
            v_min, v_max = 9500.0, 10500.0
        pad = (v_max - v_min) * 0.10
        v_min -= pad
        v_max += pad

        painter.save()
        painter.setClipRect(rect)

        # 1. Underwater Drawdown Fill (mapped to top 35% of Pane 3 inverted from top)
        if dd_data:
            min_dd = min((float(dd_data[i]) for i in range(i0, min(i1 + 1, len(dd_data))) if dd_data[i] is not None), default=-1.0)
            min_dd = min(min_dd, -1.0)
            dd_poly = QPolygonF()
            dd_poly.append(QPointF(self._idx_to_x(i0, rect), rect.bottom()))
            for idx in range(i0, min(i1 + 1, len(dd_data))):
                dd_val = float(dd_data[idx] or 0.0)
                dd_frac = min(1.0, abs(dd_val) / max(abs(min_dd), 1.0))
                y_dd = rect.bottom() - dd_frac * (rect.height() * 0.32)
                dd_poly.append(QPointF(self._idx_to_x(idx, rect), y_dd))
            dd_poly.append(QPointF(self._idx_to_x(i1, rect), rect.bottom()))
            painter.setPen(Qt.PenStyle.NoPen)
            painter.setBrush(QColor(244, 63, 94, 42))
            painter.drawPolygon(dd_poly)

        # 2. Buy & Hold Benchmark Curve (dashed slate)
        bnh_path = QPainterPath()
        started = False
        for idx in range(i0, min(i1 + 1, len(bnh_data))):
            if bnh_data[idx] is None:
                continue
            x = self._idx_to_x(idx, rect)
            y = self._val_to_y(float(bnh_data[idx]), v_min, v_max, rect)
            if not started:
                bnh_path.moveTo(x, y)
                started = True
            else:
                bnh_path.lineTo(x, y)
        painter.setPen(QPen(QColor("#64748b"), 1.3, Qt.PenStyle.DashLine))
        painter.setBrush(Qt.BrushStyle.NoBrush)
        painter.drawPath(bnh_path)

        # 3. Multi-Strategy Shootout Comparison Curves (if active)
        if self.comparison_data and self.comparison_data.get("equity_series"):
            for s in self.comparison_data["equity_series"]:
                s_eq = s.get("equity", [])
                s_col = parse_hex_color(s.get("color", "#38bdf8"))
                s_path = QPainterPath()
                started = False
                for idx in range(i0, min(i1 + 1, len(s_eq))):
                    if s_eq[idx] is None:
                        continue
                    x = self._idx_to_x(idx, rect)
                    y = self._val_to_y(float(s_eq[idx]), v_min, v_max, rect)
                    if not started:
                        s_path.moveTo(x, y)
                        started = True
                    else:
                        s_path.lineTo(x, y)
                painter.setPen(QPen(s_col, 1.4))
                painter.drawPath(s_path)

        # 4. Primary Strategy Equity Curve (bold emerald/cyan)
        eq_path = QPainterPath()
        started = False
        for idx in range(i0, min(i1 + 1, len(eq_data))):
            if eq_data[idx] is None:
                continue
            x = self._idx_to_x(idx, rect)
            y = self._val_to_y(float(eq_data[idx]), v_min, v_max, rect)
            if not started:
                eq_path.moveTo(x, y)
                started = True
            else:
                eq_path.lineTo(x, y)
        painter.setPen(QPen(QColor("#10b981"), 2.2))
        painter.drawPath(eq_path)

        # Pane 3 HUD Label
        idx_h = self.hover_bar_idx if (0 <= self.hover_bar_idx < len(eq_data)) else min(i1, len(eq_data) - 1)
        cur_eq = float(eq_data[idx_h] or 10000.0)
        cur_bnh = float(bnh_data[idx_h] or 10000.0) if idx_h < len(bnh_data) else 10000.0
        cur_dd = float(dd_data[idx_h] or 0.0) if idx_h < len(dd_data) else 0.0
        painter.setFont(self.font_hud)
        painter.setPen(QColor("#10b981"))
        p3_txt = f"PORTFOLIO EQUITY: ${cur_eq:,.2f}   |   BENCHMARK (B&H): ${cur_bnh:,.2f}   |   DRAWDOWN: {cur_dd:.2f}%"
        if self.comparison_data:
            p3_txt += f"   |   [MULTI-STRATEGY SHOOTOUT: {len(self.comparison_data.get('equity_series', []))} KURVEN AKTIV]"
        painter.drawText(QPointF(rect.left() + 8, rect.top() + 14), p3_txt)

        painter.restore()
        self._render_right_axis_labels(painter, rect, axis_w, v_min, v_max, 0, "$")

    def _render_time_axis(self, painter: QPainter, rect: QRectF):
        candles = self.result_data["candles"]
        i0 = max(0, self.view_start)
        i1 = min(len(candles) - 1, self.view_end)
        if i1 <= i0:
            return
        painter.setFont(self.font_axis)
        painter.setPen(QColor("#64748b"))
        ticks = 7
        for t in range(ticks + 1):
            frac = t / float(ticks)
            idx = int(round(i0 + frac * (i1 - i0)))
            idx = max(0, min(len(candles) - 1, idx))
            x = rect.left() + frac * rect.width()
            t_str = str(candles[idx].get("time", ""))[-11:]
            painter.drawText(QRectF(x - 42, rect.top() + 4, 84, 18), Qt.AlignmentFlag.AlignCenter, t_str)

    def _render_crosshair(self, painter: QPainter, r1: QRectF, r2: QRectF, r3: QRectF):
        if self.hover_bar_idx < self.view_start or self.hover_bar_idx > self.view_end:
            return
        x = self._idx_to_x(self.hover_bar_idx, r1)
        cross_pen = QPen(QColor(148, 163, 184, 130), 1.0, Qt.PenStyle.DashLine)
        painter.setPen(cross_pen)
        painter.drawLine(QPointF(x, r1.top()), QPointF(x, r3.bottom()))
        if self.hover_pos:
            y = self.hover_pos.y()
            for r in (r1, r2, r3):
                if r.top() <= y <= r.bottom():
                    painter.drawLine(QPointF(r.left(), y), QPointF(r.right(), y))


class QuantWorkstationWindow(QMainWindow):
    """
    Modern Native PyQt6 Quant & Backtesting Workstation Window.
    Unifies all 12 Core Strategy Archetypes + 42 Expert Advisors (EA-01 .. EA-42),
    Dynamic 3-Pane Charting, Institutional Tearsheet, Trade Journal, Multi-Strategy Shootout,
    SIMD ML Hyperopt, and FIX 4.4 Execution.
    """

    def __init__(self, api_base: str = "http://192.168.122.100:8090"):
        super().__init__()
        self.api_base = api_base.rstrip("/")
        self.engine: UniversalBacktestEngine = universal_backtester
        self.active_strategy_id: str = "ichimoku_cloud"
        self.last_result: Optional[Dict[str, Any]] = None

        self.setWindowTitle("NeedfulDings Quant & Backtesting Workstation v3.0 — 12 Archetypes & 42-EA Chiplet Studio (PyQt6)")
        self.resize(1620, 980)
        self._apply_dark_stylesheet()
        self._build_ui()
        self._populate_strategy_tree()
        self.run_single_backtest()

    def _apply_dark_stylesheet(self):
        self.setStyleSheet(
            """
            QMainWindow, QWidget {
                background-color: #060913;
                color: #e2e8f0;
                font-family: 'DejaVu Sans', 'Segoe UI', sans-serif;
                font-size: 12px;
            }
            QGroupBox {
                border: 1px solid #1e293b;
                border-radius: 6px;
                margin-top: 8px;
                padding-top: 8px;
                background-color: #0b101e;
                font-weight: bold;
                color: #38bdf8;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 4px;
            }
            QComboBox, QSpinBox, QDoubleSpinBox {
                background-color: #0f172a;
                border: 1px solid #334155;
                border-radius: 4px;
                padding: 4px 8px;
                color: #f8fafc;
                font-family: 'DejaVu Sans Mono', monospace;
            }
            QPushButton {
                background-color: #1e293b;
                border: 1px solid #334155;
                border-radius: 5px;
                padding: 6px 12px;
                color: #f8fafc;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #334155;
                border-color: #38bdf8;
            }
            QPushButton#btnPrimary {
                background-color: #0284c7;
                border-color: #38bdf8;
                color: #ffffff;
            }
            QPushButton#btnPrimary:hover {
                background-color: #0369a1;
            }
            QPushButton#btnShootout {
                background-color: #6d28d9;
                border-color: #a855f7;
                color: #ffffff;
            }
            QPushButton#btnHyperopt {
                background-color: #059669;
                border-color: #10b981;
                color: #ffffff;
            }
            QTreeWidget, QTableWidget {
                background-color: #070b14;
                alternate-background-color: #0b1120;
                border: 1px solid #1e293b;
                gridline-color: #1e293b;
                color: #e2e8f0;
                font-family: 'DejaVu Sans Mono', monospace;
                font-size: 11px;
            }
            QHeaderView::section {
                background-color: #0f172a;
                color: #94a3b8;
                border: 1px solid #1e293b;
                padding: 4px 6px;
                font-weight: bold;
            }
            QTabWidget::pane {
                border: 1px solid #1e293b;
                background-color: #080c17;
            }
            QTabBar::tab {
                background-color: #0f172a;
                color: #94a3b8;
                border: 1px solid #1e293b;
                padding: 6px 14px;
                margin-right: 2px;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
                font-weight: bold;
            }
            QTabBar::tab:selected {
                background-color: #1e293b;
                color: #38bdf8;
                border-bottom-color: #38bdf8;
            }
            """
        )

    def _build_ui(self):
        central = QWidget()
        self.setCentralWidget(central)
        root_layout = QVBoxLayout(central)
        root_layout.setContentsMargins(8, 8, 8, 8)
        root_layout.setSpacing(6)

        # 1. Top Command Ribbon
        ribbon = QFrame()
        ribbon.setStyleSheet("background-color: #0b1120; border: 1px solid #1e293b; border-radius: 6px;")
        rib_layout = QHBoxLayout(ribbon)
        rib_layout.setContentsMargins(10, 6, 10, 6)

        lbl_brand = QLabel("⚡ QUANT WORKSTATION v3.0")
        lbl_brand.setStyleSheet("font-size: 14px; font-weight: bold; color: #38bdf8; border: none;")
        rib_layout.addWidget(lbl_brand)
        rib_layout.addSpacing(12)

        rib_layout.addWidget(QLabel("Symbol:"))
        self.cmb_symbol = QComboBox()
        self.cmb_symbol.setEditable(True)
        self.cmb_symbol.setMinimumWidth(135)
        default_syms = [
            "SOLUSDT", "BTCUSDT", "ETHUSDT", "XLMUSDT", "SUIUSDT", "XRPUSDT",
            "DOGEUSDT", "AVAXUSDT", "NEARUSDT", "LINKUSDT", "PEPEUSDT", "BNBUSDT",
            "ADAUSDT", "FETUSDT", "RNDRUSDT", "ARBUSDT", "INJUSDT", "SEIUSDT",
        ]
        self.cmb_symbol.addItems(default_syms)
        self.cmb_symbol.currentTextChanged.connect(lambda _: self.run_single_backtest())
        rib_layout.addWidget(self.cmb_symbol)

        rib_layout.addWidget(QLabel("Timeframe:"))
        self.cmb_interval = QComboBox()
        self.cmb_interval.addItems(["1m", "5m", "15m", "1h", "4h", "1d"])
        self.cmb_interval.setCurrentText("15m")
        self.cmb_interval.currentTextChanged.connect(lambda _: self.run_single_backtest())
        rib_layout.addWidget(self.cmb_interval)

        rib_layout.addWidget(QLabel("Bars:"))
        self.cmb_bars = QComboBox()
        self.cmb_bars.addItems(["150", "250", "350", "500", "750"])
        self.cmb_bars.setCurrentText("250")
        self.cmb_bars.currentTextChanged.connect(lambda _: self.run_single_backtest())
        rib_layout.addWidget(self.cmb_bars)

        rib_layout.addWidget(QLabel("Chart Style:"))
        self.cmb_chart_mode = QComboBox()
        self.cmb_chart_mode.addItems(["Candlesticks", "Heikin-Ashi", "Clean Line"])
        self.cmb_chart_mode.currentTextChanged.connect(self._on_chart_mode_changed)
        rib_layout.addWidget(self.cmb_chart_mode)

        btn_reset_zoom = QPushButton("🔍 Reset View")
        btn_reset_zoom.clicked.connect(lambda: self.chart_widget.reset_zoom())
        rib_layout.addWidget(btn_reset_zoom)

        btn_Clear_cmp = QPushButton("🧹 Clear Shootout")
        btn_Clear_cmp.clicked.connect(lambda: self.chart_widget.set_comparison_result(None))
        rib_layout.addWidget(btn_Clear_cmp)

        btn_snap = QPushButton("📸 PNG Snapshot")
        btn_snap.clicked.connect(self._export_png_dialog)
        rib_layout.addWidget(btn_snap)

        rib_layout.addStretch(1)
        self.lbl_cluster_badge = QLabel("🟢 6-CHIPLET / 42-EA ENGINE READY  •  3,736 BINANCE SYMBOLS")
        self.lbl_cluster_badge.setStyleSheet("color: #10b981; font-family: 'DejaVu Sans Mono'; font-weight: bold; border: none;")
        rib_layout.addWidget(self.lbl_cluster_badge)
        root_layout.addWidget(ribbon)

        # 2. Main Horizontal Splitter: Left Strategy Dock | Right (3-Pane Chart + Bottom Analytics Tabs)
        main_split = QSplitter(Qt.Orientation.Horizontal)

        # --- Left Dock: Strategy Tree & Execution Parameters ---
        left_panel = QWidget()
        left_panel.setMinimumWidth(330)
        left_panel.setMaximumWidth(400)
        left_layout = QVBoxLayout(left_panel)
        left_layout.setContentsMargins(0, 0, 0, 0)

        grp_tree = QGroupBox("Strategie-Katalog (12 Archetypen + 42 EAs)")
        tree_layout = QVBoxLayout(grp_tree)
        self.tree_strategies = QTreeWidget()
        self.tree_strategies.setHeaderLabels(["Strategie / EA", "Cluster"])
        self.tree_strategies.setColumnWidth(0, 225)
        self.tree_strategies.itemClicked.connect(self._on_strategy_tree_clicked)
        tree_layout.addWidget(self.tree_strategies)
        left_layout.addWidget(grp_tree, stretch=3)

        grp_params = QGroupBox("Hyperparameter & Order-Risiko")
        p_grid = QGridLayout(grp_params)

        p_grid.addWidget(QLabel("Fast Period:"), 0, 0)
        self.spn_fast = QSpinBox()
        self.spn_fast.setRange(2, 120)
        self.spn_fast.setValue(9)
        p_grid.addWidget(self.spn_fast, 0, 1)

        p_grid.addWidget(QLabel("Slow Period:"), 1, 0)
        self.spn_slow = QSpinBox()
        self.spn_slow.setRange(5, 300)
        self.spn_slow.setValue(26)
        p_grid.addWidget(self.spn_slow, 1, 1)

        p_grid.addWidget(QLabel("Stop-Loss (%):"), 2, 0)
        self.spn_sl = QDoubleSpinBox()
        self.spn_sl.setRange(0.2, 25.0)
        self.spn_sl.setSingleStep(0.25)
        self.spn_sl.setValue(2.5)
        p_grid.addWidget(self.spn_sl, 2, 1)

        p_grid.addWidget(QLabel("Take-Profit (%):"), 3, 0)
        self.spn_tp = QDoubleSpinBox()
        self.spn_tp.setRange(0.5, 50.0)
        self.spn_tp.setSingleStep(0.5)
        self.spn_tp.setValue(6.5)
        p_grid.addWidget(self.spn_tp, 3, 1)

        p_grid.addWidget(QLabel("Fee + Slippage (bps):"), 4, 0)
        self.spn_fee = QDoubleSpinBox()
        self.spn_fee.setRange(0.0, 50.0)
        self.spn_fee.setSingleStep(1.0)
        self.spn_fee.setValue(7.5)
        p_grid.addWidget(self.spn_fee, 4, 1)

        self.chk_short = QCheckBox("Long/Short Modus erlauben (Perp/Margin)")
        self.chk_short.setChecked(True)
        p_grid.addWidget(self.chk_short, 5, 0, 1, 2)

        btn_run = QPushButton("▶ Strategie Backtesten & Rendern")
        btn_run.setObjectName("btnPrimary")
        btn_run.clicked.connect(self.run_single_backtest)
        p_grid.addWidget(btn_run, 6, 0, 1, 2)

        btn_shootout = QPushButton("📊 Multi-Strategy Shootout (8 Top-Modelle)")
        btn_shootout.setObjectName("btnShootout")
        btn_shootout.clicked.connect(self.run_multi_strategy_shootout)
        p_grid.addWidget(btn_shootout, 7, 0, 1, 2)

        btn_hyperopt = QPushButton("🧠 SIMD Grid Auto-Tune (Beste Parameter)")
        btn_hyperopt.setObjectName("btnHyperopt")
        btn_hyperopt.clicked.connect(self.run_local_simd_autotune)
        p_grid.addWidget(btn_hyperopt, 8, 0, 1, 2)

        btn_wfa = QPushButton("🔬 WFA · Monte-Carlo (1,000) · 2D Heatmap")
        btn_wfa.clicked.connect(self.run_validation_suite_qt)
        p_grid.addWidget(btn_wfa, 9, 0, 1, 2)

        btn_kruse = QPushButton("🌀 Kruse 32-D Regime & Auto-Execution")
        btn_kruse.clicked.connect(self.run_kruse_and_auto_execution_qt)
        p_grid.addWidget(btn_kruse, 10, 0, 1, 2)

        left_layout.addWidget(grp_params, stretch=2)
        main_split.addWidget(left_panel)

        # --- Right Panel: Vertical Splitter (Top 3-Pane Chart | Bottom Analytics Tabs) ---
        right_split = QSplitter(Qt.Orientation.Vertical)
        self.chart_widget = DynamicQuantChartWidget()
        right_split.addWidget(self.chart_widget)

        # Bottom Analytics Dock
        self.tabs_bottom = QTabWidget()

        # Tab 1: Institutional Tearsheet KPIs
        tab_kpi = QWidget()
        kpi_layout = QGridLayout(tab_kpi)
        self.kpi_labels: Dict[str, QLabel] = {}
        kpi_defs = [
            ("total_return_pct", "Total Return"),
            ("benchmark_return_pct", "Benchmark (B&H)"),
            ("alpha_pct", "Strategy Alpha"),
            ("sharpe_ratio", "Sharpe Ratio"),
            ("sortino_ratio", "Sortino Ratio"),
            ("calmar_ratio", "Calmar Ratio"),
            ("max_drawdown_pct", "Max Drawdown"),
            ("win_rate_pct", "Win Rate"),
            ("profit_factor", "Profit Factor"),
            ("expectancy_usd", "Expectancy / Trade"),
            ("closed_trades", "Closed Trades"),
            ("total_fees_usd", "Fees & Slippage"),
        ]
        for idx, (k_key, k_title) in enumerate(kpi_defs):
            card = QFrame()
            card.setStyleSheet("background-color: #0b1120; border: 1px solid #1e293b; border-radius: 6px; padding: 4px;")
            cl = QVBoxLayout(card)
            cl.setContentsMargins(8, 4, 8, 4)
            t_lbl = QLabel(k_title.upper())
            t_lbl.setStyleSheet("color: #64748b; font-size: 10px; font-weight: bold; border: none;")
            v_lbl = QLabel("—")
            v_lbl.setStyleSheet("color: #f8fafc; font-size: 15px; font-weight: bold; font-family: 'DejaVu Sans Mono'; border: none;")
            cl.addWidget(t_lbl)
            cl.addWidget(v_lbl)
            self.kpi_labels[k_key] = v_lbl
            kpi_layout.addWidget(card, idx // 6, idx % 6)
        self.tabs_bottom.addTab(tab_kpi, "📈 Institutional Tearsheet (12 KPIs)")

        # Tab 2: Interactive Trade-by-Trade Journal
        self.tbl_trades = QTableWidget(0, 9)
        self.tbl_trades.setHorizontalHeaderLabels([
            "Step", "Zeit (UTC)", "Typ", "Grund", "Entry Preis", "Exit/Exec Preis", "Menge", "Netto PnL ($)", "Rendite (%)"
        ])
        self.tbl_trades.setAlternatingRowColors(True)
        self.tbl_trades.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.tbl_trades.cellClicked.connect(self._on_trade_row_clicked)
        self.tabs_bottom.addTab(self.tbl_trades, "📋 Trade-by-Trade Journal (Klick fokussiert Chart)")

        # Tab 3: Multi-Strategy Shootout Leaderboard
        self.tbl_shootout = QTableWidget(0, 9)
        self.tbl_shootout.setHorizontalHeaderLabels([
            "Rang", "Strategie / EA", "Cluster", "Return (%)", "Alpha (%)", "Sharpe", "Sortino", "Max DD (%)", "Profit Factor"
        ])
        self.tbl_shootout.setAlternatingRowColors(True)
        self.tbl_shootout.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.tbl_shootout.cellClicked.connect(self._on_shootout_row_clicked)
        self.tabs_bottom.addTab(self.tbl_shootout, "🏆 Multi-Strategy Shootout Leaderboard")

        # Tab 4: Walk-Forward (4 Folds), Monte-Carlo (1,000 Paths) & 2D Heatmap
        tab_wfa = QWidget()
        wfa_layout = QHBoxLayout(tab_wfa)
        self.tbl_wfa = QTableWidget(0, 6)
        self.tbl_wfa.setHorizontalHeaderLabels(["Fold", "Opt Fast/Slow", "IS Return (%)", "OOS Return (%)", "OOS Sharpe", "WFE (%)"])
        self.tbl_wfa.setAlternatingRowColors(True)
        self.tbl_wfa.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        wfa_layout.addWidget(self.tbl_wfa, stretch=3)

        self.tbl_heatmap = QTableWidget(6, 6)
        self.tbl_heatmap.setAlternatingRowColors(False)
        self.tbl_heatmap.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        wfa_layout.addWidget(self.tbl_heatmap, stretch=2)

        self.lbl_mc_summary = QLabel("Klicke links auf '🔬 WFA · Monte-Carlo (1,000) · 2D Heatmap'")
        self.lbl_mc_summary.setWordWrap(True)
        self.lbl_mc_summary.setStyleSheet("background-color:#0b1120; border:1px solid #1e293b; border-radius:6px; padding:10px; font-family:'DejaVu Sans Mono'; color:#38bdf8;")
        wfa_layout.addWidget(self.lbl_mc_summary, stretch=2)
        self.tabs_bottom.addTab(tab_wfa, "🔬 Walk-Forward · Monte-Carlo (1,000) · 2D Heatmap")

        # Tab 5: Real-Time MT4/MT5 (.mq5) & PineScript v5 Transpiler
        tab_transpiler = QWidget()
        tr_layout = QHBoxLayout(tab_transpiler)
        self.txt_custom_script = QTextEdit()
        self.txt_custom_script.setStyleSheet("background-color:#050811; color:#38bdf8; font-family:'DejaVu Sans Mono'; font-size:11px; border:1px solid #1e293b;")
        self.txt_custom_script.setPlainText(
            '//@version=5\n'
            'strategy("Institutional SuperTrend + RSI Hybrid", overlay=true)\n'
            'fastLen = input.int(10, "Fast ATR Period")\n'
            'slowLen = input.int(26, "Slow Equilibrium")\n'
            '[stLine, dir] = ta.supertrend(2.8, fastLen)\n'
            'rsiVal = ta.rsi(close, 14)\n'
            'if dir < 0 and rsiVal < 70\n'
            '    strategy.entry("Long", strategy.long)\n'
            'if dir > 0\n'
            '    strategy.close("Long")\n'
        )
        tr_layout.addWidget(self.txt_custom_script, stretch=3)
        tr_right = QVBoxLayout()
        self.lbl_transpiler_ast = QLabel("SIMD Vector Transpiler AST bereit.\nUnterstützt MQL4/MQL5 (OnTick, iATR, iRSI, iBands) & PineScript v5.")
        self.lbl_transpiler_ast.setWordWrap(True)
        self.lbl_transpiler_ast.setStyleSheet("background-color:#0b1120; border:1px solid #1e293b; border-radius:6px; padding:10px; font-family:'DejaVu Sans Mono';")
        tr_right.addWidget(self.lbl_transpiler_ast, stretch=1)
        btn_compile_script = QPushButton("⚡ MQL/PineScript Kompilieren & im 3-Pane-Chart rendern")
        btn_compile_script.setObjectName("btnPrimary")
        btn_compile_script.clicked.connect(self.run_custom_script_transpiler_qt)
        tr_right.addWidget(btn_compile_script)
        tr_layout.addLayout(tr_right, stretch=2)
        self.tabs_bottom.addTab(tab_transpiler, "📜 MT4/MT5 & PineScript v5 Transpiler")

        # Tab 6: Kruse-Synergetik 32-D Market Regime & Auto-Execution Bridge
        tab_kruse = QWidget()
        kr_layout = QHBoxLayout(tab_kruse)
        self.lbl_kruse_info = QLabel("Lade Kruse-Synergetik 32-D Ordnungsparameter & Potenzialgebirge V(x) = -ln P(x)...")
        self.lbl_kruse_info.setWordWrap(True)
        self.lbl_kruse_info.setStyleSheet("background-color:#0b1120; border:1px solid #1e293b; border-radius:6px; padding:10px; font-family:'DejaVu Sans Mono'; color:#e2e8f0;")
        kr_layout.addWidget(self.lbl_kruse_info, stretch=3)
        self.tbl_kruse_weights = QTableWidget(6, 2)
        self.tbl_kruse_weights.setHorizontalHeaderLabels(["42-EA Chiplet Cluster", "Synergetisches Gewicht"])
        self.tbl_kruse_weights.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        kr_layout.addWidget(self.tbl_kruse_weights, stretch=2)
        self.tabs_bottom.addTab(tab_kruse, "🌀 Kruse-Synergetik 32-D & Auto-Execution")

        right_split.addWidget(self.tabs_bottom)
        right_split.setSizes([660, 230])
        main_split.addWidget(right_split)
        main_split.setSizes([355, 1245])
        root_layout.addWidget(main_split, stretch=1)

        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

    def _populate_strategy_tree(self):
        self.tree_strategies.clear()

        # Group 1: 12 Core Strategy Archetypes
        core_root = QTreeWidgetItem(self.tree_strategies, ["★ 12 Core Quant Archetypes", "CORE"])
        core_root.setExpanded(True)
        for s in CORE_STRATEGIES:
            item = QTreeWidgetItem(core_root, [s["name"], s["cluster"]])
            item.setData(0, Qt.ItemDataRole.UserRole, s["id"])
            if s["id"] == self.active_strategy_id:
                self.tree_strategies.setCurrentItem(item)

        # Group 2: 42 Expert Advisors grouped by their 6 Chiplets
        clusters: Dict[str, List[Dict[str, Any]]] = {}
        for ea in FULL_42_EA_DEFINITIONS:
            clusters.setdefault(ea["cluster"], []).append(ea)

        ea_root = QTreeWidgetItem(self.tree_strategies, ["🧠 42 Expert Advisors (6 Chiplets)", "SWARM"])
        ea_root.setExpanded(True)
        for c_name, ea_list in clusters.items():
            c_node = QTreeWidgetItem(ea_root, [f"Chiplet: {c_name} ({len(ea_list)} EAs)", c_name])
            c_node.setExpanded(False)
            for ea in ea_list:
                item = QTreeWidgetItem(c_node, [f"{ea['id']}: {ea['name']}", ea["archetype"]])
                item.setData(0, Qt.ItemDataRole.UserRole, ea["id"])

    def _on_strategy_tree_clicked(self, item: QTreeWidgetItem, column: int):
        strat_id = item.data(0, Qt.ItemDataRole.UserRole)
        if not strat_id:
            return
        self.active_strategy_id = str(strat_id)
        _, merged, _ = self.engine.resolve_strategy(self.active_strategy_id)
        self.spn_fast.setValue(int(merged.get("fast_period", 9)))
        self.spn_slow.setValue(int(merged.get("slow_period", 26)))
        self.run_single_backtest()

    def _on_chart_mode_changed(self, text: str):
        mode_map = {
            "Candlesticks": "candles",
            "Heikin-Ashi": "heikin_ashi",
            "Clean Line": "line",
        }
        self.chart_widget.set_chart_mode(mode_map.get(text, "candles"))

    def _collect_params(self) -> Dict[str, Any]:
        return {
            "fast_period": int(self.spn_fast.value()),
            "slow_period": int(self.spn_slow.value()),
            "stop_loss_pct": float(self.spn_sl.value()),
            "take_profit_pct": float(self.spn_tp.value()),
            "fee_bps": float(self.spn_fee.value()),
            "allow_short": bool(self.chk_short.isChecked()),
        }

    def run_single_backtest(self):
        symbol = self.cmb_symbol.currentText().strip().upper() or "SOLUSDT"
        interval = self.cmb_interval.currentText().strip() or "15m"
        limit = int(self.cmb_bars.currentText() or 250)
        params = self._collect_params()

        candles = self.engine.fetch_ohlc(symbol=symbol, interval=interval, limit=limit)
        res = self.engine.run_backtest(
            candles=candles,
            strategy_name=self.active_strategy_id,
            params=params,
            symbol=symbol,
            interval=interval,
        )
        self.last_result = res
        self.chart_widget.set_backtest_result(res)
        self._update_tearsheet_and_journal(res)
        meta = res.get("strategy_meta", {})
        m = res.get("metrics", {})
        self.status_bar.showMessage(
            f"[OK] {symbol} ({interval}, {len(candles)} Bars)  |  Strategie: {meta.get('name')}  |  "
            f"Return: {m.get('total_return_pct', 0):+.2f}%  |  Sharpe: {m.get('sharpe_ratio', 0):.2f}  |  "
            f"Win-Rate: {m.get('win_rate_pct', 0):.1f}%  |  Compute: {m.get('compute_time_ms', 0):.2f} ms"
        )

    def _update_tearsheet_and_journal(self, res: Dict[str, Any]):
        m = res.get("metrics", {})
        fmt_map = {
            "total_return_pct": (f"{m.get('total_return_pct', 0):+.2f}%", m.get("total_return_pct", 0) >= 0),
            "benchmark_return_pct": (f"{m.get('benchmark_return_pct', 0):+.2f}%", m.get("benchmark_return_pct", 0) >= 0),
            "alpha_pct": (f"{m.get('alpha_pct', 0):+.2f}%", m.get("alpha_pct", 0) >= 0),
            "sharpe_ratio": (f"{m.get('sharpe_ratio', 0):.2f}", m.get("sharpe_ratio", 0) >= 1.0),
            "sortino_ratio": (f"{m.get('sortino_ratio', 0):.2f}", m.get("sortino_ratio", 0) >= 1.2),
            "calmar_ratio": (f"{m.get('calmar_ratio', 0):.2f}", m.get("calmar_ratio", 0) >= 0.5),
            "max_drawdown_pct": (f"{m.get('max_drawdown_pct', 0):.2f}%", False),
            "win_rate_pct": (f"{m.get('win_rate_pct', 0):.1f}%", m.get("win_rate_pct", 0) >= 50.0),
            "profit_factor": (f"{m.get('profit_factor', 0):.2f}", m.get("profit_factor", 0) >= 1.2),
            "expectancy_usd": (f"${m.get('expectancy_usd', 0):+.2f}", m.get("expectancy_usd", 0) >= 0),
            "closed_trades": (f"{m.get('closed_trades', 0)} ({m.get('total_trades', 0)} Execs)", True),
            "total_fees_usd": (f"${m.get('total_fees_usd', 0):.2f}", None),
        }
        for k, lbl in self.kpi_labels.items():
            if k in fmt_map:
                txt, is_pos = fmt_map[k]
                lbl.setText(txt)
                col = "#10b981" if is_pos is True else ("#f43f5e" if is_pos is False else "#eab308")
                lbl.setStyleSheet(f"color: {col}; font-size: 15px; font-weight: bold; font-family: 'DejaVu Sans Mono'; border: none;")

        # Populate Trade-by-Trade Table
        trades = res.get("trades", [])
        self.tbl_trades.setRowCount(len(trades))
        for r_idx, tr in enumerate(trades):
            step = tr.get("step", 0)
            entry_step = tr.get("entry_step", step)
            ttype = tr.get("type", "")
            reason = tr.get("reason", "")
            ep = tr.get("entry_price", tr.get("price", 0.0))
            xp = tr.get("price", 0.0)
            amt = tr.get("amount", 0.0)
            pnl = tr.get("pnl")
            pnl_pct = tr.get("pnl_pct")

            vals = [
                str(step),
                str(tr.get("time", "")),
                ttype,
                reason,
                f"{float(ep):.4f}",
                f"{float(xp):.4f}",
                f"{float(amt):.4f}",
                f"${float(pnl):+.2f}" if pnl is not None else "—",
                f"{float(pnl_pct):+.2f}%" if pnl_pct is not None else "—",
            ]
            for c_idx, v in enumerate(vals):
                item = QTableWidgetItem(v)
                item.setData(Qt.ItemDataRole.UserRole, {"entry_step": entry_step, "exit_step": step, "pnl": pnl or 0.0})
                if c_idx in (2, 7, 8) and pnl is not None:
                    item.setForeground(QColor("#10b981") if float(pnl) >= 0 else QColor("#f43f5e"))
                self.tbl_trades.setItem(r_idx, c_idx, item)

    def _on_trade_row_clicked(self, row: int, col: int):
        item = self.tbl_trades.item(row, 0)
        if not item:
            return
        meta = item.data(Qt.ItemDataRole.UserRole)
        if isinstance(meta, dict):
            self.chart_widget.focus_trade_span(int(meta["entry_step"]), int(meta["exit_step"]), meta)

    def run_multi_strategy_shootout(self):
        symbol = self.cmb_symbol.currentText().strip().upper() or "SOLUSDT"
        interval = self.cmb_interval.currentText().strip() or "15m"
        limit = int(self.cmb_bars.currentText() or 250)
        params = self._collect_params()

        shootout_strats = [
            "neural_core_42ea_consensus",
            "ichimoku_cloud",
            "supertrend_atr",
            "bollinger_keltner_squeeze",
            "ornstein_uhlenbeck_zscore",
            "kalman_hull_trend",
            "EA-01",
            "EA-29",
        ]
        if self.active_strategy_id not in shootout_strats:
            shootout_strats[0] = self.active_strategy_id

        cmp_res = self.engine.run_multi_strategy_comparison(
            symbol=symbol,
            interval=interval,
            limit=limit,
            strategies=shootout_strats,
            params=params,
        )
        self.chart_widget.set_comparison_result(cmp_res)
        lb = cmp_res.get("leaderboard", [])
        self.tbl_shootout.setRowCount(len(lb))
        for r_idx, row in enumerate(lb):
            vals = [
                f"#{r_idx + 1}",
                f"{row['strategy_id']} — {row['name']}",
                row["cluster"],
                f"{row['total_return_pct']:+.2f}%",
                f"{row['alpha_pct']:+.2f}%",
                f"{row['sharpe_ratio']:.2f}",
                f"{row['sortino_ratio']:.2f}",
                f"{row['max_drawdown_pct']:.2f}%",
                f"{row['profit_factor']:.2f}",
            ]
            for c_idx, v in enumerate(vals):
                item = QTableWidgetItem(v)
                item.setData(Qt.ItemDataRole.UserRole, row["strategy_id"])
                if c_idx == 1:
                    item.setForeground(parse_hex_color(row["color"]))
                elif c_idx in (3, 4):
                    item.setForeground(QColor("#10b981") if row["total_return_pct"] >= 0 else QColor("#f43f5e"))
                self.tbl_shootout.setItem(r_idx, c_idx, item)

        self.tabs_bottom.setCurrentWidget(self.tbl_shootout)
        if lb:
            self.status_bar.showMessage(
                f"🏆 Shootout abgeschlossen ({len(lb)} Strategien auf {symbol} {interval}): "
                f"Platz #1: {lb[0]['name']} ({lb[0]['total_return_pct']:+.2f}%, Sharpe {lb[0]['sharpe_ratio']:.2f})"
            )

    def _on_shootout_row_clicked(self, row: int, col: int):
        item = self.tbl_shootout.item(row, 0)
        if not item:
            return
        strat_id = item.data(Qt.ItemDataRole.UserRole)
        if strat_id:
            self.active_strategy_id = str(strat_id)
            self.run_single_backtest()

    def run_local_simd_autotune(self):
        """Sweeps Fast/Slow grid for the active strategy to find the highest Sharpe/Return combination."""
        symbol = self.cmb_symbol.currentText().strip().upper() or "SOLUSDT"
        interval = self.cmb_interval.currentText().strip() or "15m"
        limit = int(self.cmb_bars.currentText() or 250)
        candles = self.engine.fetch_ohlc(symbol=symbol, interval=interval, limit=limit)
        base_params = self._collect_params()

        best_score = -1e9
        best_fast = base_params["fast_period"]
        best_slow = base_params["slow_period"]
        t0 = time.perf_counter()
        combos = 0
        for f_p in (5, 7, 9, 12, 14, 18):
            for s_p in (18, 21, 26, 34, 48, 60):
                if f_p >= s_p:
                    continue
                combos += 1
                p = dict(base_params, fast_period=f_p, slow_period=s_p)
                r = self.engine.run_backtest(candles, strategy_name=self.active_strategy_id, params=p, symbol=symbol, interval=interval)
                m = r["metrics"]
                score = m["total_return_pct"] * 0.65 + m["sharpe_ratio"] * 1.5 - abs(m["max_drawdown_pct"]) * 0.4
                if score > best_score:
                    best_score = score
                    best_fast = f_p
                    best_slow = s_p

        dt_ms = (time.perf_counter() - t0) * 1000.0
        self.spn_fast.setValue(best_fast)
        self.spn_slow.setValue(best_slow)
        self.run_single_backtest()
        self.status_bar.showMessage(
            f"🧠 SIMD Grid Auto-Tune ({combos} Matrizen in {dt_ms:.1f} ms): Optimal Fast={best_fast}, Slow={best_slow} für {self.active_strategy_id}"
        )

    def run_validation_suite_qt(self):
        """Runs 4-Fold Walk-Forward Optimization, 1,000-Path Monte Carlo & 2D Heatmap in PyQt6."""
        symbol = self.cmb_symbol.currentText().strip().upper() or "SOLUSDT"
        interval = self.cmb_interval.currentText().strip() or "15m"
        limit = int(self.cmb_bars.currentText() or 250)
        params = self._collect_params()

        val = self.engine.run_institutional_validation_suite(
            symbol=symbol,
            interval=interval,
            limit=limit,
            strategy_name=self.active_strategy_id,
            params=params,
            mc_paths=1000,
        )
        wf = val.get("walk_forward", {})
        folds = wf.get("folds", [])
        self.tbl_wfa.setRowCount(len(folds))
        for r_i, f in enumerate(folds):
            row_vals = [
                f"Fold #{f['fold']}",
                f"{f['best_fast']} / {f['best_slow']}",
                f"{f['is_return_pct']:+.2f}%",
                f"{f['oos_return_pct']:+.2f}%",
                f"{f['oos_sharpe']:.2f}",
                f"{f['wfe_pct']:.1f}%",
            ]
            for c_i, v in enumerate(row_vals):
                it = QTableWidgetItem(v)
                if c_i == 3:
                    it.setForeground(QColor("#10b981") if f["oos_return_pct"] >= 0 else QColor("#f43f5e"))
                self.tbl_wfa.setItem(r_i, c_i, it)

        hm = val.get("heatmap_2d", {})
        f_ax = hm.get("fast_axis", [])
        s_ax = hm.get("slow_axis", [])
        rmat = hm.get("return_matrix", [])
        self.tbl_heatmap.setRowCount(len(f_ax))
        self.tbl_heatmap.setColumnCount(len(s_ax))
        self.tbl_heatmap.setVerticalHeaderLabels([f"F={x}" for x in f_ax])
        self.tbl_heatmap.setHorizontalHeaderLabels([f"S={x}" for x in s_ax])
        for r_i, row in enumerate(rmat):
            for c_i, val_cell in enumerate(row):
                it = QTableWidgetItem(f"{val_cell:+.1f}%")
                it.setBackground(QColor(16, 185, 129, 75) if val_cell > 0 else QColor(244, 63, 94, 65))
                self.tbl_heatmap.setItem(r_i, c_i, it)

        mc = val.get("monte_carlo", {})
        opt = hm.get("optimal_cell", {})
        self.lbl_mc_summary.setText(
            f"🎲 MONTE-CARLO (1,000 PFADE)\n"
            f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
            f"• Median Terminal (p50): ${mc.get('median_terminal_equity', 10000):,.2f}\n"
            f"• Bull Quantil (p95):    ${mc.get('p95_terminal_equity', 10000):,.2f}\n"
            f"• Bear Quantil (p05):    ${mc.get('p05_terminal_equity', 10000):,.2f}\n"
            f"• Profit-Wahrsch.:       {mc.get('prob_profit_pct', 0):.1f}%\n"
            f"• Risk of Ruin (>15%DD): {mc.get('risk_of_ruin_15pct', 0):.2f}%\n"
            f"• Walk-Forward Verdict:  {wf.get('verdict')}\n"
            f"• Optimal Grid Cell:     F={opt.get('fast')}/S={opt.get('slow')} ({opt.get('return_pct', 0):+.2f}%)"
        )
        self.tabs_bottom.setCurrentIndex(3)
        self.status_bar.showMessage(
            f"🔬 Validation Suite ({symbol} {interval}): WFE={wf.get('wfe_efficiency_pct')}% | "
            f"MC Median=${mc.get('median_terminal_equity'):,.2f} | WinProb={mc.get('prob_profit_pct')}%"
        )

    def run_custom_script_transpiler_qt(self):
        """Compiles custom MT4/MT5 (.mq5) or PineScript v5 code in PyQt6 and renders it on the 3-Pane Chart."""
        symbol = self.cmb_symbol.currentText().strip().upper() or "SOLUSDT"
        interval = self.cmb_interval.currentText().strip() or "15m"
        limit = int(self.cmb_bars.currentText() or 250)
        code = self.txt_custom_script.toPlainText()
        params = self._collect_params()

        res = self.engine.transpile_and_backtest_custom_script(
            code=code,
            symbol=symbol,
            interval=interval,
            limit=limit,
            params=params,
        )
        self.last_result = res
        self.chart_widget.set_backtest_result(res)
        self._update_tearsheet_and_journal(res)
        ast = res.get("transpiler_ast", {})
        m = res.get("metrics", {})
        self.lbl_transpiler_ast.setText(
            f"✅ {ast.get('vectorized_status')} ({ast.get('language')})\n"
            f"• Titel: {ast.get('custom_title')}\n"
            f"• Archetyp: {ast.get('mapped_archetype')} (Fast={ast.get('extracted_fast_period')}, Slow={ast.get('extracted_slow_period')})\n"
            f"• Indikatoren: {', '.join(ast.get('detected_indicators', []))}\n"
            f"• Return: {m.get('total_return_pct', 0):+.2f}% | Sharpe: {m.get('sharpe_ratio', 0):.2f}"
        )
        self.status_bar.showMessage(
            f"📜 Custom {ast.get('language')} transpiliert & im 3-Pane-Chart gerendert: {ast.get('custom_title')} ({m.get('total_return_pct', 0):+.2f}%)"
        )

    def run_kruse_and_auto_execution_qt(self):
        """Computes Kruse Synergetik 32-D Market Regime and populates Chiplet Weights in PyQt6."""
        symbol = self.cmb_symbol.currentText().strip().upper() or "SOLUSDT"
        interval = self.cmb_interval.currentText().strip() or "15m"
        kr = self.engine.compute_kruse_synergetic_regime(symbol=symbol, interval=interval, limit=240)
        cl = kr.get("clusters_4", {})
        self.lbl_kruse_info.setText(
            f"🌀 KRUSE-SYNERGETIK 32-D MARKT-REGIME ({symbol} {interval})\n"
            f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
            f"• Aktuelles Attraktor-Tal: {kr.get('current_basin')}\n"
            f"• Ordnungsparameter (λ):   {kr.get('order_parameter_lambda'):+.4f}   |   Kritische Fluktuation: {kr.get('critical_fluctuation_index'):.4f}\n"
            f"• Processing Fluency:      {kr.get('processing_fluency_score'):.4f}   |   Kognitive Friktion:    {kr.get('cognitive_friction_index'):.4f}\n"
            f"• 4-Cluster-Profil:        Struktur={cl.get('STRUKTUR_ORDNUNG')} · Dynamik={cl.get('DYNAMIK_IMPULS')} · "
            f"Kohärenz={cl.get('KOHAERENZ_NETZWERK')} · Friktion={cl.get('FRIKTION_ENTROPIE')}"
        )
        rw = kr.get("recommended_chiplet_weights", {})
        self.tbl_kruse_weights.setRowCount(len(rw))
        for r_i, (k, v) in enumerate(rw.items()):
            self.tbl_kruse_weights.setItem(r_i, 0, QTableWidgetItem(k))
            self.tbl_kruse_weights.setItem(r_i, 1, QTableWidgetItem(f"{float(v) * 100:.1f}%"))
        self.tabs_bottom.setCurrentIndex(5)
        self.status_bar.showMessage(
            f"🌀 Kruse-Synergetik 32-D Regime ({symbol}): {kr.get('current_basin')} | λ={kr.get('order_parameter_lambda'):+.4f}"
        )

    def save_snapshot(self, path: str) -> bool:
        QApplication.processEvents()
        pix = self.grab()
        os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
        return pix.save(path, "PNG")

    def _export_png_dialog(self):
        default_path = f"/root/quant_backtest_qt/snapshot_{self.active_strategy_id}.png"
        path, _ = QFileDialog.getSaveFileName(self, "Chart Snapshot speichern", default_path, "PNG Images (*.png)")
        if path:
            self.save_snapshot(path)
            self.status_bar.showMessage(f"📸 Snapshot gespeichert: {path}")


def main():
    parser = argparse.ArgumentParser(description="NeedfulDings PyQt6 Quant & Backtesting Workstation")
    parser.add_argument("--symbol", default="SOLUSDT", help="Initial trading pair symbol")
    parser.add_argument("--strategy", default="ichimoku_cloud", help="Initial strategy or EA ID (e.g. ichimoku_cloud, EA-01)")
    parser.add_argument("--interval", default="15m", help="Timeframe interval (1m, 5m, 15m, 1h, 4h, 1d)")
    parser.add_argument("--self-test", action="store_true", help="Run automated verification of strategies & shootout then exit")
    parser.add_argument("--snapshot", default="", help="Save PNG screenshot of the workstation window to the given path")
    args = parser.parse_args()

    app = QApplication(sys.argv)
    win = QuantWorkstationWindow()
    win.cmb_symbol.setCurrentText(args.symbol.upper())
    win.cmb_interval.setCurrentText(args.interval)
    win.active_strategy_id = args.strategy
    win.run_single_backtest()
    win.show()

    if args.self_test:
        # Execute multi-strategy shootout + validation suite + Kruse 32-D so all tabs & 3 panes are populated
        win.run_validation_suite_qt()
        win.run_kruse_and_auto_execution_qt()
        win.run_multi_strategy_shootout()
        win.tabs_bottom.setCurrentIndex(3)  # Show Walk-Forward + 2D Heatmap + Monte-Carlo in snapshot
        QApplication.processEvents()
        if args.snapshot:
            ok = win.save_snapshot(args.snapshot)
            print(f"[SELF-TEST] Snapshot saved to {args.snapshot}: {ok}")
        print(
            f"[SELF-TEST] Workstation verified with strategy={win.active_strategy_id}, "
            f"symbol={win.cmb_symbol.currentText()}, candles={len(win.last_result.get('candles', []))}"
        )
        return 0

    if args.snapshot:
        QTimer.singleShot(400, lambda: win.save_snapshot(args.snapshot))

    return app.exec()


if __name__ == "__main__":
    sys.exit(main())
