// Documentation page — comprehensive reference sourced from spec v3.0
const { useState: udS, useEffect: udE, useRef: udR } = React;

const DOCS_SECTIONS = [
  { id: "overview",     label: "Overview",         icon: "01" },
  { id: "architecture", label: "Architecture",     icon: "02" },
  { id: "flow",         label: "Trading flow",     icon: "03" },
  { id: "strategy",     label: "Strategy & signals", icon: "04" },
  { id: "webhook",      label: "Webhook payload",  icon: "05" },
  { id: "execution",    label: "Order execution",  icon: "06" },
  { id: "exchanges",    label: "Exchanges",        icon: "07" },
  { id: "risk",         label: "Risk management",  icon: "08" },
  { id: "alerts",       label: "Telegram alerts",  icon: "09" },
  { id: "api",          label: "API endpoints",    icon: "10" },
  { id: "schema",       label: "Database schema",  icon: "11" },
  { id: "env",          label: "Environment vars", icon: "12" },
  { id: "stack",        label: "Tech stack",       icon: "13" },
  { id: "structure",    label: "Folder structure", icon: "14" },
  { id: "security",     label: "Security",         icon: "15" },
  { id: "recovery",     label: "State recovery",   icon: "16" },
  { id: "setup",        label: "Setup & deploy",   icon: "17" },
];

function DocsPage({ accent, activeExchange = "binance" }) {
  const [active, setActive] = udS("overview");

  // Scroll-spy via IntersectionObserver
  udE(() => {
    const els = DOCS_SECTIONS.map((s) => document.getElementById("docs-" + s.id)).filter(Boolean);
    if (!els.length) return;
    const obs = new IntersectionObserver((entries) => {
      const top = entries
        .filter((e) => e.isIntersecting)
        .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
      if (top) setActive(top.target.id.replace("docs-", ""));
    }, { rootMargin: "-120px 0px -60% 0px" });
    els.forEach((el) => obs.observe(el));
    return () => obs.disconnect();
  }, []);

  const goto = (id) => {
    const el = document.getElementById("docs-" + id);
    if (el) window.scrollTo({ top: el.offsetTop - 100, behavior: "smooth" });
    setActive(id);
  };

  return (
    <div style={{ display: "grid", gridTemplateColumns: "240px minmax(0, 1fr)", gap: "var(--row-gap)", alignItems: "flex-start" }}>
      {/* Sticky TOC */}
      <aside style={{
        position: "sticky", top: 88,
        background: "linear-gradient(180deg, rgba(20,26,42,0.6), rgba(15,19,32,0.6))",
        border: "1px solid var(--line)", borderRadius: 14, padding: 12,
        display: "flex", flexDirection: "column", gap: 1,
        maxHeight: "calc(100vh - 110px)", overflowY: "auto",
      }}>
        <div style={{ fontSize: 10, color: "var(--text-3)", letterSpacing: 0.6, textTransform: "uppercase", padding: "6px 10px 10px" }}>On this page</div>
        {DOCS_SECTIONS.map((s) => {
          const isActive = s.id === active;
          return (
            <button key={s.id} onClick={() => goto(s.id)} className="focus-ring"
              style={{
                display: "flex", alignItems: "center", gap: 10, padding: "8px 10px",
                borderRadius: 7, border: 0, textAlign: "left",
                background: isActive ? "var(--accent-soft)" : "transparent",
                color: isActive ? "var(--text)" : "var(--text-2)",
                fontSize: 12.5, cursor: "pointer", transition: "all .15s",
              }}
              onMouseEnter={(e) => { if (!isActive) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
              onMouseLeave={(e) => { if (!isActive) e.currentTarget.style.background = "transparent"; }}>
              <code className="mono" style={{ fontSize: 10, color: isActive ? "var(--accent)" : "var(--text-3)", letterSpacing: 0.4 }}>{s.icon}</code>
              <span style={{ flex: 1 }}>{s.label}</span>
              {isActive && <span style={{ width: 4, height: 14, background: "var(--accent)", borderRadius: 2 }} />}
            </button>
          );
        })}
        <div style={{ marginTop: 8, padding: "10px", borderTop: "1px solid var(--line)" }}>
          <div className="mono" style={{ fontSize: 10, color: "var(--text-3)" }}>spec v3.0 · MVP V1.1</div>
          <div className="mono" style={{ fontSize: 10, color: "var(--text-3)", marginTop: 2 }}>Last edit · 2026-05-15</div>
        </div>
      </aside>

      {/* Content */}
      <div style={{ display: "flex", flexDirection: "column", gap: 28, minWidth: 0 }}>
        <DocsHero />
        <DocOverview />
        <DocArchitecture />
        <DocFlow />
        <DocStrategy />
        <DocWebhook />
        <DocExecution activeExchange={activeExchange} />
        <DocExchanges />
        <DocRisk />
        <DocAlerts />
        <DocAPI />
        <DocSchema />
        <DocEnv />
        <DocStack />
        <DocStructure />
        <DocSecurity />
        <DocRecovery />
        <DocSetup />
      </div>
    </div>
  );
}

// =========================================================
// Helpers
// =========================================================
function DocSection({ id, eyebrow, title, lede, children }) {
  return (
    <section id={"docs-" + id} style={{ scrollMarginTop: 100 }}>
      <div style={{ display: "flex", alignItems: "baseline", gap: 12, marginBottom: 6 }}>
        <code className="mono" style={{ fontSize: 11, color: "var(--text-3)", letterSpacing: 0.5 }}>{eyebrow}</code>
      </div>
      <h2 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: -0.4 }}>{title}</h2>
      {lede && <p style={{ margin: "10px 0 22px", fontSize: 14, color: "var(--text-2)", lineHeight: 1.6, maxWidth: 760 }}>{lede}</p>}
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>{children}</div>
    </section>
  );
}

function Prose({ children }) {
  return <p style={{ margin: 0, fontSize: 13.5, color: "var(--text-2)", lineHeight: 1.65, maxWidth: 760 }}>{children}</p>;
}

function Callout({ kind = "info", title, children }) {
  const cfg = {
    info: { bg: "rgba(96,165,250,0.07)", bd: "rgba(96,165,250,0.25)", c: "#93C5FD", Icon: IconAlert },
    warn: { bg: "rgba(245,166,35,0.08)", bd: "rgba(245,166,35,0.25)", c: "#FBBF24", Icon: IconAlert },
    danger: { bg: "rgba(242,63,92,0.08)", bd: "rgba(242,63,92,0.25)", c: "#FCA5A5", Icon: IconAlert },
    success: { bg: "rgba(25,229,166,0.07)", bd: "rgba(25,229,166,0.25)", c: "var(--profit)", Icon: IconCheck },
  }[kind];
  return (
    <div style={{ padding: 14, borderRadius: 10, background: cfg.bg, border: "1px solid " + cfg.bd, display: "flex", gap: 12, alignItems: "flex-start" }}>
      <cfg.Icon size={16} style={{ color: cfg.c, marginTop: 1, flex: "0 0 16px" }} />
      <div style={{ minWidth: 0, flex: 1 }}>
        {title && <div style={{ fontSize: 13, fontWeight: 600, color: cfg.c, marginBottom: 4 }}>{title}</div>}
        <div style={{ fontSize: 12.5, color: "var(--text-2)", lineHeight: 1.6 }}>{children}</div>
      </div>
    </div>
  );
}

function CodeBox({ lang, children }) {
  return (
    <div style={{ borderRadius: 10, overflow: "hidden", border: "1px solid var(--line)" }}>
      {lang && (
        <div style={{ padding: "7px 14px", background: "rgba(255,255,255,0.03)", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: "var(--accent)" }} />
          <code className="mono" style={{ fontSize: 10.5, color: "var(--text-3)", letterSpacing: 0.4, textTransform: "uppercase" }}>{lang}</code>
        </div>
      )}
      <pre style={{
        margin: 0, padding: "14px 18px",
        background: "#06080F",
        fontFamily: "JetBrains Mono", fontSize: 12, lineHeight: 1.7,
        color: "var(--text-2)", overflow: "auto", whiteSpace: "pre",
      }}>{children}</pre>
    </div>
  );
}

function DefList({ items }) {
  return (
    <dl style={{ margin: 0, display: "grid", gridTemplateColumns: "180px 1fr", gap: "12px 24px", fontSize: 13 }}>
      {items.map((it, i) => (
        <React.Fragment key={i}>
          <dt style={{ color: "var(--text-3)", paddingTop: 1 }}>{it.k}</dt>
          <dd style={{ margin: 0, color: "var(--text)", lineHeight: 1.55 }}>{it.v}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

// =========================================================
// Hero
// =========================================================
function DocsHero() {
  return (
    <div style={{
      padding: "32px 36px", borderRadius: 16,
      background: "linear-gradient(135deg, rgba(0,212,170,0.10), rgba(124,92,255,0.08))",
      border: "1px solid var(--line)", position: "relative", overflow: "hidden",
    }}>
      <div style={{
        position: "absolute", inset: 0, opacity: 0.06, pointerEvents: "none",
        backgroundImage:
          "linear-gradient(rgba(255,255,255,0.4) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.4) 1px, transparent 1px)",
        backgroundSize: "32px 32px",
      }} />
      <div style={{ position: "relative" }}>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "5px 11px", borderRadius: 999, background: "rgba(0,212,170,0.12)", color: "var(--accent)", fontSize: 11, fontWeight: 600, letterSpacing: 0.5, textTransform: "uppercase", marginBottom: 14 }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: "currentColor" }} />
          Documentation · v3.0 spec · MVP V1.1
        </div>
        <h1 style={{ margin: 0, fontSize: 34, fontWeight: 600, letterSpacing: -0.6 }}>Auto-Trader — reference</h1>
        <p style={{ margin: "10px 0 18px", fontSize: 15, color: "var(--text-2)", lineHeight: 1.6, maxWidth: 720 }}>
          Production-ready automated crypto trading bot. Receives signals from TradingView via webhook, validates through risk management,
          executes on <strong>Binance or Kraken</strong> (one active per deploy), and reports via Telegram.
        </p>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
          <Badge variant="accent">FastAPI · Python 3.11+</Badge>
          <Badge variant="info">CCXT async</Badge>
          <Badge>SQLite WAL</Badge>
          <Badge>Docker</Badge>
          <Badge variant="warn">Testnet default</Badge>
          <Badge variant="profit">Dubai region</Badge>
        </div>
      </div>
    </div>
  );
}

// =========================================================
// Sections
// =========================================================
function DocOverview() {
  return (
    <DocSection id="overview" eyebrow="§ 01" title="Overview"
      lede="A single-pair, signal-driven bot built around hard risk controls. Indicators run in TradingView (Pine v5); the bot's only job is to receive validated signals and execute them safely.">
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12 }}>
        <PrincipleCard n="01" t="Risk before alpha"  d="Daily loss cap, drawdown kill switch, max consecutive losses. The bot pauses itself before it can blow up." />
        <PrincipleCard n="02" t="Defaults to safe"   d="TRADING_MODE defaults to testnet. Live mode requires explicit confirmation and 7+ days in test." />
        <PrincipleCard n="03" t="No orphan positions" d="State reconciler compares DB vs exchange on every startup. Halts and alerts on mismatch." />
      </div>
      <DefList items={[
        { k: "Project name", v: <span>AI Crypto Trading Platform</span> },
        { k: "Repository", v: <code className="mono" style={{ color: "var(--accent)" }}>aminbassam/Auto-Trader</code> },
        { k: "Deployment", v: "Dubai · Hetzner / DigitalOcean / Vultr" },
        { k: "Exchanges", v: "Binance · Kraken (single-active per deploy)" },
        { k: "Pair", v: <code className="mono">BTC/USDT</code> },
        { k: "Timeframe", v: "5-minute candles" },
        { k: "Target uptime", v: "99.9%" },
        { k: "Signal→fill", v: "< 2s" },
      ]} />
    </DocSection>
  );
}

function PrincipleCard({ n, t, d }) {
  return (
    <div style={{ padding: 16, borderRadius: 12, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 8 }}>
      <code className="mono" style={{ fontSize: 11, color: "var(--accent)", letterSpacing: 0.5 }}>{n}</code>
      <div style={{ fontSize: 14, fontWeight: 600 }}>{t}</div>
      <div style={{ fontSize: 12.5, color: "var(--text-2)", lineHeight: 1.55 }}>{d}</div>
    </div>
  );
}

function DocArchitecture() {
  const layers = [
    { name: "TradingView", role: "Strategy + indicator calculation (Pine v5)", color: "#2962FF" },
    { name: "Webhook (FastAPI)", role: "POST /webhook · secret validation · 10/min rate-limit", color: "#60A5FA" },
    { name: "Signal handler", role: "Pydantic parse · 60s dedup by signal_id", color: "#A78BFA" },
    { name: "Risk manager", role: "Pre-trade VETO · position sizing · circuit breaker", color: "var(--accent)" },
    { name: "Exchange factory", role: "Reads ACTIVE_EXCHANGE · returns BinanceClient or KrakenClient", color: "var(--profit)" },
    { name: "CCXT async", role: "Spot orders + exchange-side stop loss · retry x3", color: "#F472B6" },
    { name: "SQLite + Telegram", role: "Trade log (WAL) · alerts + /commands", color: "var(--warn)" },
  ];
  return (
    <DocSection id="architecture" eyebrow="§ 02" title="Architecture"
      lede="Layered with strict boundaries. The factory pattern keeps the rest of the codebase from referencing Binance or Kraken directly — only the abstract interface.">
      <div style={{ padding: 14, borderRadius: 12, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)" }}>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {layers.map((l, i) => (
            <React.Fragment key={l.name}>
              <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
                <span style={{ width: 28, height: 28, borderRadius: 7, background: l.color, color: "#06150F", display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "JetBrains Mono", fontSize: 11, fontWeight: 700 }}>{String(i + 1).padStart(2, "0")}</span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600 }}>{l.name}</div>
                  <div style={{ fontSize: 12, color: "var(--text-3)", marginTop: 2 }}>{l.role}</div>
                </div>
              </div>
              {i < layers.length - 1 && (
                <div style={{ paddingLeft: 14, color: "var(--text-3)", fontSize: 13 }}>↓</div>
              )}
            </React.Fragment>
          ))}
        </div>
      </div>
      <CodeBox lang="data flow">
{`TradingView Pine Script
   │  (alert fires)
   ▼
HTTPS POST → FastAPI /webhook    [X-Webhook-Secret header]
   │  (Pydantic parse)
   ▼
SignalHandler.dedup(signal_id, 60s window)
   │
   ▼
RiskManager.validate(signal)     [7 pre-trade checks]
   │  (pass)
   ▼
ExchangeFactory.get_client()     [reads ACTIVE_EXCHANGE]
   │
   ▼
ExchangeClient.place_market_order(symbol, side, qty)
ExchangeClient.place_stop_loss()  [OCO or conditional close]
   │
   ▼
SQLite trade log  +  Telegram notify`}
      </CodeBox>
    </DocSection>
  );
}

function DocFlow() {
  return (
    <DocSection id="flow" eyebrow="§ 03" title="Trading flow"
      lede="Every signal travels the same path. Any check can veto. If any veto fires, the trade is logged as rejected with a reason and a Telegram alert is sent.">
      <ol style={{ margin: 0, paddingLeft: 18, fontSize: 13.5, color: "var(--text-2)", lineHeight: 1.7 }}>
        <li><strong style={{ color: "var(--text)" }}>Webhook arrives.</strong> POST /webhook with X-Webhook-Secret. Rate-limited at 10/min.</li>
        <li><strong style={{ color: "var(--text)" }}>Payload validates.</strong> Pydantic checks signal, symbol, price, signal_id. Bad payload → 422.</li>
        <li><strong style={{ color: "var(--text)" }}>Deduplication.</strong> If same signal_id has been seen in the last 60s, drop silently and log <code className="mono">signal_deduplicated</code>.</li>
        <li><strong style={{ color: "var(--text)" }}>Pre-trade risk checks.</strong> Seven gates — see Risk management.</li>
        <li><strong style={{ color: "var(--text)" }}>Position sizing.</strong> <code className="mono">size = (balance × risk_pct) ÷ stop_pct</code>. Below min notional → reject.</li>
        <li><strong style={{ color: "var(--text)" }}>Exchange dispatch.</strong> Factory returns BinanceClient or KrakenClient based on ACTIVE_EXCHANGE.</li>
        <li><strong style={{ color: "var(--text)" }}>Market order placed.</strong> With retry x3 (exponential backoff) on transient errors.</li>
        <li><strong style={{ color: "var(--text)" }}>Stop loss attached.</strong> OCO (Binance) or conditional close (Kraken). Exchange-side, survives crash.</li>
        <li><strong style={{ color: "var(--text)" }}>Persist + notify.</strong> SQLite WAL write, Telegram message, daily P&L counter incremented.</li>
      </ol>
      <Callout kind="warn" title="One position at a time">
        Max concurrent positions is locked at 1 in MVP. Additional signals are rejected with reason <code className="mono">position_already_open</code> while a trade is live. Pyramiding ships in v2.
      </Callout>
    </DocSection>
  );
}

function DocStrategy() {
  return (
    <DocSection id="strategy" eyebrow="§ 04" title="Strategy & signals"
      lede="The bot does not compute indicators. All strategy logic lives in TradingView Pine Script v5. The bot's contract is just the JSON shape of incoming alerts.">
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        <div style={{ padding: 16, borderRadius: 12, background: "rgba(25,229,166,0.04)", border: "1px solid rgba(25,229,166,0.18)" }}>
          <div style={{ fontSize: 11, color: "var(--profit)", letterSpacing: 0.5, textTransform: "uppercase", marginBottom: 8 }}>BUY signal</div>
          <div style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.7 }}>
            EMA 9 crosses <strong style={{ color: "var(--text)" }}>above</strong> EMA 21<br/>
            <span style={{ color: "var(--text-3)" }}>AND</span><br/>
            RSI(14) <strong style={{ color: "var(--text)" }}>&gt; 50</strong>
          </div>
          <div className="mono" style={{ fontSize: 11, color: "var(--text-3)", marginTop: 10 }}>action: open LONG</div>
        </div>
        <div style={{ padding: 16, borderRadius: 12, background: "rgba(242,63,92,0.04)", border: "1px solid rgba(242,63,92,0.18)" }}>
          <div style={{ fontSize: 11, color: "var(--loss)", letterSpacing: 0.5, textTransform: "uppercase", marginBottom: 8 }}>SELL signal</div>
          <div style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.7 }}>
            EMA 9 crosses <strong style={{ color: "var(--text)" }}>below</strong> EMA 21<br/>
            <span style={{ color: "var(--text-3)" }}>AND</span><br/>
            RSI(14) <strong style={{ color: "var(--text)" }}>&lt; 50</strong>
          </div>
          <div className="mono" style={{ fontSize: 11, color: "var(--text-3)", marginTop: 10 }}>action: close position</div>
        </div>
      </div>
      <DefList items={[
        { k: "Source", v: "TradingView Pine Script v5" },
        { k: "Timeframe", v: <code className="mono">5m</code> },
        { k: "Pair", v: <code className="mono">BTC/USDT</code> },
        { k: "Indicators", v: "EMA(9), EMA(21), RSI(14)" },
        { k: "Take profit", v: <span>Deferred for MVP — exit via reverse signal or stop loss only. <Badge variant="info">v2</Badge></span> },
      ]} />
    </DocSection>
  );
}

function DocWebhook() {
  return (
    <DocSection id="webhook" eyebrow="§ 05" title="Webhook payload"
      lede="TradingView fires alerts at the bot's webhook endpoint. Paste this JSON into the alert's 'Message' field (use TradingView placeholders for live values).">
      <CodeBox lang="application/json">
{`{
  "signal":    "BUY",
  "symbol":    "BTC/USDT",
  "price":     {{close}},
  "timestamp": {{timenow}},
  "strategy":  "ema_crossover",
  "signal_id": {{strategy.order.id}}
}`}
      </CodeBox>
      <DefList items={[
        { k: "signal", v: <span><code className="mono">"BUY"</code> or <code className="mono">"SELL"</code></span> },
        { k: "symbol", v: <span>CCXT-normalised, e.g. <code className="mono">"BTC/USDT"</code></span> },
        { k: "price", v: "Numeric. Used for logging/audit; bot uses live exchange price for sizing." },
        { k: "timestamp", v: "TradingView server time at alert fire." },
        { k: "strategy", v: "Free-form identifier — logged for analytics." },
        { k: "signal_id", v: <span><strong>REQUIRED for dedup.</strong> Must be unique per signal — bot drops same-id within 60s.</span> },
      ]} />
      <Callout kind="info" title="Required header">
        Every request must include <code className="mono">X-Webhook-Secret: $WEBHOOK_SECRET</code>. Missing or wrong → 401. Rotate the secret periodically.
      </Callout>
    </DocSection>
  );
}

function DocExecution({ activeExchange }) {
  const ex = EXCHANGES[activeExchange] || EXCHANGES.binance;
  return (
    <DocSection id="execution" eyebrow="§ 06" title="Order execution"
      lede="Market orders only in MVP. Position size is fixed-fraction, calculated from account equity and stop-loss distance. Limit orders ship in v2.">
      <CodeBox lang="position sizing">
{`position_size = (account_balance × risk_pct) ÷ stop_loss_distance_pct

# Example with $10,000 equity, 1% risk, 2% stop:
position_size = (10000 × 0.01) ÷ 0.02
              = $5,000 notional
              ≈ 0.078 BTC at $64,210

# Risk per trade (USD)
$ at risk = account_balance × risk_pct = $100`}
      </CodeBox>
      <Callout kind="success" title={"Stop loss on " + ex.name}>
        {ex.oco
          ? <>Binance supports <strong>OCO orders</strong> natively. The stop-loss leg is placed exchange-side at order entry — it survives bot crashes, restarts, or network loss.</>
          : <>Kraken has no OCO. The bot attaches a <strong>conditional close</strong> order to the opening order via the <code className="mono">close</code> parameter — Kraken's native equivalent. Same survives-crash guarantee.</>}
      </Callout>
      <DefList items={[
        { k: "Order type", v: "MARKET" },
        { k: "Position mode", v: "one-way" },
        { k: "Max concurrent", v: "1 position" },
        { k: "Risk per trade", v: "1.0% of equity (env: RISK_PER_TRADE_PCT)" },
        { k: "Stop loss", v: "2.0% (env: STOP_LOSS_PCT)" },
        { k: "Min notional", v: <span>$10 ({ex.name}) — queried at startup, cached</span> },
        { k: "Retry policy", v: "3 retries · exponential backoff on transient errors" },
      ]} />
    </DocSection>
  );
}

function DocExchanges() {
  return (
    <DocSection id="exchanges" eyebrow="§ 07" title="Exchanges"
      lede="Two venues supported in v1.1, one active per deploy. The exchange factory reads ACTIVE_EXCHANGE at boot and returns the matching client. All callers use the abstract interface — they don't know which venue they're talking to.">
      <CodeBox lang="abstract interface">
{`class AbstractExchangeClient(ABC):
    @abstractmethod async def connect(): ...
    @abstractmethod async def disconnect(): ...
    @abstractmethod async def get_balance(): ...
    @abstractmethod async def get_ticker(symbol): ...
    @abstractmethod async def place_market_order(symbol, side, qty): ...
    @abstractmethod async def place_oco_order(symbol, side, qty, stop_price): ...
    @abstractmethod async def get_open_positions(): ...
    @abstractmethod async def cancel_order(order_id): ...

class BinanceClient(AbstractExchangeClient):  # spot · OCO · testnet
class KrakenClient(AbstractExchangeClient):   # spot · conditional close · paper`}
      </CodeBox>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        {["binance", "kraken"].map((k) => {
          const ex = EXCHANGES[k];
          return (
            <div key={k} style={{ padding: 16, borderRadius: 12, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 14 }}>
                <ex.Icon size={28} />
                <div>
                  <div style={{ fontSize: 15, fontWeight: 600 }}>{ex.name}</div>
                  <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 2 }}>{ex.full}</div>
                </div>
              </div>
              <DefList items={[
                { k: "CCXT id", v: <code className="mono">{ex.ccxtId}</code> },
                { k: "Native symbol", v: <code className="mono">{ex.nativeSymbol}</code> },
                { k: "Maker fee", v: <code className="mono">{ex.fees.maker.toFixed(2)}%</code> },
                { k: "Taker fee", v: <code className="mono">{ex.fees.taker.toFixed(2)}%</code> },
                { k: "Rate limit", v: <span className="mono" style={{ fontSize: 11 }}>{ex.rateLimit}</span> },
                { k: "Stop loss", v: ex.stopLoss + " — " + ex.stopLossNote },
                { k: "Testnet", v: ex.testnet ? "Available · " + ex.testnetUrl : "None — use TRADING_MODE=paper" },
                { k: "Quirk", v: ex.quirks },
              ]} />
            </div>
          );
        })}
      </div>
      <Callout kind="warn" title="Kraken-specific notes">
        Kraken has <strong>no public testnet</strong> — the bot ships with a <code className="mono">paper</code> mode that simulates fills against live order-book prices.
        Kraken uses XBT for Bitcoin internally; CCXT normalises everything to BTC. Auth is nonce-based (CCXT handles it).
        Rate limit is much stricter than Binance — CCXT's <code className="mono">enableRateLimit</code> is on by default, with extra delay between calls.
      </Callout>
    </DocSection>
  );
}

function DocRisk() {
  const checks = [
    { n: 1, t: "Max concurrent positions", d: "Reject if a position is already open (MVP cap = 1)." },
    { n: 2, t: "Daily loss limit",         d: "Reject if cumulative P&L since 00:00 UTC ≤ −3%. Trips circuit breaker." },
    { n: 3, t: "Duplicate signal",         d: "Reject if signal_id seen in last 60s." },
    { n: 4, t: "Min account balance",      d: "Reject if balance < $50." },
    { n: 5, t: "Active exchange reachable", d: "Health-check ACTIVE_EXCHANGE before every order." },
    { n: 6, t: "Min notional ($10)",       d: "Position size below exchange minimum → reject. Varies per venue." },
    { n: 7, t: "Pair available",           d: "Reject if symbol not in active exchange's catalogue." },
  ];
  return (
    <DocSection id="risk" eyebrow="§ 08" title="Risk management"
      lede="Risk has veto power over every trade. Seven gates run on every signal. The circuit breaker is armed by default and trips on daily loss limit OR max consecutive losses.">
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 10 }}>
        {checks.map((c) => (
          <div key={c.n} style={{ padding: 12, borderRadius: 10, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)", display: "flex", gap: 12 }}>
            <div style={{ width: 28, height: 28, borderRadius: 7, background: "var(--accent-soft)", color: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", flex: "0 0 28px", fontFamily: "JetBrains Mono", fontSize: 12, fontWeight: 700 }}>{c.n}</div>
            <div>
              <div style={{ fontSize: 13, fontWeight: 600 }}>{c.t}</div>
              <div style={{ fontSize: 12, color: "var(--text-3)", marginTop: 3, lineHeight: 1.5 }}>{c.d}</div>
            </div>
          </div>
        ))}
      </div>
      <DefList items={[
        { k: "Risk per trade", v: "1.0%" },
        { k: "Max daily loss", v: "3.0% — halts until 00:00 UTC reset" },
        { k: "Max drawdown", v: <span>15.0% — <strong style={{ color: "var(--loss)" }}>permanent halt</strong>, requires manual restart</span> },
        { k: "Max consecutive losses", v: "5 → auto-pause" },
        { k: "Auto-resume", v: <span>OFF — require manual <code className="mono">/resume</code> via Telegram</span> },
      ]} />
      <Callout kind="danger" title="Kill switch math">
        At 1% risk per trade and a 2% stop, you can lose ~50 consecutive trades before hitting the 15% drawdown kill switch. Robust against noise-driven false signals.
      </Callout>
    </DocSection>
  );
}

function DocAlerts() {
  const events = [
    ["trade_opened",        "🟢 OPENED: {side} {symbol} @ {price} | Size: {size} | SL: {stop_loss}"],
    ["trade_closed",        "🔴 CLOSED: {symbol} @ {price} | P&L: {pnl} ({pnl_pct}%)"],
    ["stop_loss_triggered", "⛔ STOP LOSS: {symbol} @ {price} | Loss: {loss}"],
    ["daily_limit_hit",     "🚨 DAILY LOSS LIMIT HIT: −{daily_loss}% | Trading halted"],
    ["signal_rejected",     "⚠️ SIGNAL REJECTED: {reason}"],
    ["error",               "❌ ERROR: {error_type} — {message}"],
    ["daily_summary",       "📊 DAILY SUMMARY: Trades: {count} | P&L: {pnl} | Win rate: {win_rate}%"],
    ["bot_startup",         "🤖 BOT STARTED: Mode: {mode} | Exchange: {exchange} | Balance: {balance}"],
  ];
  const cmds = [
    ["/status",  "Bot state, open positions, daily P&L"],
    ["/balance", "Current account balance from exchange"],
    ["/today",   "Today's trade summary"],
    ["/stop",    "Emergency halt — closes positions, stops trading"],
    ["/resume",  "Resume trading after manual halt"],
  ];
  return (
    <DocSection id="alerts" eyebrow="§ 09" title="Telegram alerts"
      lede="All bot ↔ human communication runs through Telegram. Outbound events fire on state changes. Inbound /commands let you query and control the bot from your phone.">
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        <div>
          <div style={{ fontSize: 11, color: "var(--text-3)", letterSpacing: 0.5, textTransform: "uppercase", marginBottom: 10 }}>Event templates</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {events.map(([e, tmpl]) => (
              <div key={e} style={{ padding: "9px 12px", borderRadius: 8, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)" }}>
                <code className="mono" style={{ fontSize: 11, color: "var(--accent)" }}>{e}</code>
                <div className="mono" style={{ fontSize: 11, color: "var(--text-3)", marginTop: 4, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{tmpl}</div>
              </div>
            ))}
          </div>
        </div>
        <div>
          <div style={{ fontSize: 11, color: "var(--text-3)", letterSpacing: 0.5, textTransform: "uppercase", marginBottom: 10 }}>Commands</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {cmds.map(([c, d]) => (
              <div key={c} style={{ padding: "9px 12px", borderRadius: 8, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 10 }}>
                <code className="mono" style={{ fontSize: 11.5, color: "var(--accent)", fontWeight: 600 }}>{c}</code>
                <span style={{ fontSize: 12, color: "var(--text-2)" }}>{d}</span>
              </div>
            ))}
          </div>
          <Callout kind="warn">
            Only the configured <code className="mono">TELEGRAM_CHAT_ID</code> can execute commands. All others are silently ignored.
          </Callout>
        </div>
      </div>
      <div style={{ fontSize: 12, color: "var(--text-3)" }}>Daily summary scheduled at <code className="mono">23:59 UTC</code>.</div>
    </DocSection>
  );
}

function DocAPI() {
  return (
    <DocSection id="api" eyebrow="§ 10" title="API endpoints"
      lede="Three HTTP endpoints. /webhook is the only externally-facing route. /health is consumed by Docker and uptime monitors. /status is internal.">
      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        <EndpointRow method="POST" path="/webhook" auth="X-Webhook-Secret header" desc="Receives TradingView alerts. Returns 200 with trade_id, or 4xx with rejection reason." />
        <EndpointRow method="GET"  path="/health"  auth="None (or basic token)"   desc="Health check for monitors. Returns active_exchange, exchange_connected, open_positions, daily_pnl_pct, trading_mode." />
        <EndpointRow method="GET"  path="/status"  auth="Internal only"            desc="Detailed bot status — used by the Telegram /status command." />
      </div>
      <CodeBox lang="GET /health → 200">
{`{
  "status":             "ok",            // ok | degraded | halted
  "uptime_seconds":     184302,
  "active_exchange":    "binance",       // binance | kraken
  "exchange_connected": true,
  "last_signal_time":   "2026-05-15T08:14:32Z",
  "open_positions":     1,
  "daily_pnl_pct":      1.13,
  "trading_mode":       "testnet"        // testnet | live | paper
}`}
      </CodeBox>
    </DocSection>
  );
}

function EndpointRow({ method, path, auth, desc }) {
  const c = method === "POST" ? "var(--accent)" : "#60A5FA";
  return (
    <div style={{ padding: 14, borderRadius: 10, background: "rgba(255,255,255,0.02)", border: "1px solid var(--line)" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <code className="mono" style={{ padding: "3px 9px", borderRadius: 5, background: "rgba(255,255,255,0.04)", color: c, fontWeight: 700, fontSize: 11, letterSpacing: 0.5 }}>{method}</code>
        <code className="mono" style={{ fontSize: 13.5, color: "var(--text)", fontWeight: 500 }}>{path}</code>
        <span style={{ marginLeft: "auto", fontSize: 11, color: "var(--text-3)" }}>auth: {auth}</span>
      </div>
      <div style={{ fontSize: 12.5, color: "var(--text-2)", marginTop: 8, lineHeight: 1.5 }}>{desc}</div>
    </div>
  );
}

function DocSchema() {
  return (
    <DocSection id="schema" eyebrow="§ 11" title="Database schema"
      lede="SQLite with WAL mode. Three tables. SQLAlchemy 2.0 async ORM. Migrates to Postgres in v2.">
      <SchemaTable name="trades" cols={[
        ["id",               "INTEGER PK AUTOINC"],
        ["signal_id",        "TEXT UNIQUE NOT NULL"],
        ["exchange",         "TEXT NOT NULL  -- binance | kraken"],
        ["symbol",           "TEXT NOT NULL"],
        ["side",             "TEXT NOT NULL  -- BUY | SELL"],
        ["entry_price",      "REAL"],
        ["exit_price",       "REAL"],
        ["quantity",         "REAL"],
        ["stop_loss_price",  "REAL"],
        ["status",           "TEXT  -- open | closed | stopped_out | cancelled"],
        ["pnl",              "REAL"],
        ["pnl_pct",          "REAL"],
        ["exchange_order_id","TEXT"],
        ["opened_at",        "TIMESTAMP"],
        ["closed_at",        "TIMESTAMP"],
        ["close_reason",     "TEXT  -- signal | stop_loss | manual | circuit_breaker"],
      ]} />
      <SchemaTable name="signals" cols={[
        ["id",               "INTEGER PK AUTOINC"],
        ["signal_id",        "TEXT NOT NULL"],
        ["payload",          "TEXT  -- raw JSON"],
        ["action",           "TEXT  -- accepted | rejected | deduplicated"],
        ["rejection_reason", "TEXT"],
        ["received_at",      "TIMESTAMP"],
      ]} />
      <SchemaTable name="daily_stats" cols={[
        ["date",                  "TEXT PRIMARY KEY"],
        ["trades_count",          "INTEGER"],
        ["wins",                  "INTEGER"],
        ["losses",                "INTEGER"],
        ["total_pnl",             "REAL"],
        ["total_pnl_pct",         "REAL"],
        ["max_drawdown_pct",      "REAL"],
        ["daily_loss_limit_hit",  "BOOLEAN"],
      ]} />
    </DocSection>
  );
}

function SchemaTable({ name, cols }) {
  return (
    <div style={{ borderRadius: 10, overflow: "hidden", border: "1px solid var(--line)" }}>
      <div style={{ padding: "10px 14px", background: "rgba(255,255,255,0.03)", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 10 }}>
        <code className="mono" style={{ fontSize: 13, color: "var(--accent)", fontWeight: 600 }}>{name}</code>
        <span style={{ fontSize: 11, color: "var(--text-3)" }}>{cols.length} columns</span>
      </div>
      <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
        <tbody>
          {cols.map(([k, t], i) => (
            <tr key={k} style={{ borderTop: i ? "1px solid var(--line)" : 0 }}>
              <td style={{ padding: "8px 14px", width: 200 }}><code className="mono" style={{ color: "var(--text)" }}>{k}</code></td>
              <td style={{ padding: "8px 14px" }}><code className="mono" style={{ color: "var(--text-3)", fontSize: 11.5 }}>{t}</code></td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

function DocEnv() {
  const groups = [
    {
      title: "Required core", items: [
        ["ACTIVE_EXCHANGE",    "binance | kraken — picks ExchangeClient at boot"],
        ["TRADING_MODE",       "testnet | live | paper — paper required for Kraken"],
        ["TELEGRAM_BOT_TOKEN", "From @BotFather"],
        ["TELEGRAM_CHAT_ID",   "Your personal chat ID"],
        ["WEBHOOK_SECRET",     "Random string passed in X-Webhook-Secret"],
      ],
    },
    {
      title: "Exchange credentials (only the active one is read)", items: [
        ["BINANCE_API_KEY",    "Required when ACTIVE_EXCHANGE=binance"],
        ["BINANCE_API_SECRET", "Treat as password"],
        ["KRAKEN_API_KEY",     "Required when ACTIVE_EXCHANGE=kraken"],
        ["KRAKEN_API_SECRET",  "Base64-encoded private key"],
      ],
    },
    {
      title: "Optional tuning (defaults applied)", items: [
        ["TRADING_PAIR",          "default: BTC/USDT"],
        ["LOG_LEVEL",             "default: INFO"],
        ["RISK_PER_TRADE_PCT",    "default: 1.0"],
        ["MAX_DAILY_LOSS_PCT",    "default: 3.0"],
        ["MAX_DRAWDOWN_PCT",      "default: 15.0"],
        ["STOP_LOSS_PCT",         "default: 2.0"],
        ["MAX_CONSECUTIVE_LOSSES","default: 5"],
        ["HOST",                  "default: 0.0.0.0"],
        ["PORT",                  "default: 8000"],
      ],
    },
  ];
  return (
    <DocSection id="env" eyebrow="§ 12" title="Environment variables"
      lede="Loaded from .env via pydantic-settings. Never hardcoded. Never logged. .env is gitignored; .env.example is committed.">
      {groups.map((g) => (
        <div key={g.title}>
          <div style={{ fontSize: 12, color: "var(--text-3)", letterSpacing: 0.4, textTransform: "uppercase", marginBottom: 8, fontWeight: 500 }}>{g.title}</div>
          <div style={{ borderRadius: 10, overflow: "hidden", border: "1px solid var(--line)" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
              <tbody>
                {g.items.map(([k, d], i) => (
                  <tr key={k} style={{ borderTop: i ? "1px solid var(--line)" : 0 }}>
                    <td style={{ padding: "9px 14px", width: 260 }}><code className="mono" style={{ color: "var(--accent)" }}>{k}</code></td>
                    <td style={{ padding: "9px 14px", color: "var(--text-2)" }}>{d}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      ))}
    </DocSection>
  );
}

function DocStack() {
  const stack = [
    ["Language",          "Python 3.11+"],
    ["Framework",         "FastAPI"],
    ["Async server",      "Uvicorn"],
    ["Exchange library",  "ccxt (async mode)"],
    ["Database",          "SQLite with WAL mode"],
    ["ORM",               "SQLAlchemy 2.0 (async)"],
    ["Validation",        "Pydantic v2"],
    ["Config",            "pydantic-settings (.env loader)"],
    ["Alerts",            "python-telegram-bot (async)"],
    ["Rate limiting",     "slowapi"],
    ["Deployment",        "Docker · docker-compose"],
    ["Hosting",           "VPS (DigitalOcean, Hetzner, or Vultr) · Frankfurt or Dubai"],
    ["Charts / signals",  "TradingView Pine Script v5"],
  ];
  return (
    <DocSection id="stack" eyebrow="§ 13" title="Tech stack"
      lede="Pinned, async-first Python. Every exchange call is awaited; every webhook is non-blocking.">
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 0, borderRadius: 10, overflow: "hidden", border: "1px solid var(--line)" }}>
        {stack.map(([k, v], i) => (
          <div key={k} style={{
            padding: "10px 14px", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12,
            borderTop: i > 1 ? "1px solid var(--line)" : 0,
            borderLeft: i % 2 === 1 ? "1px solid var(--line)" : 0,
            background: i % 2 === 0 ? "rgba(255,255,255,0.012)" : "transparent",
          }}>
            <span style={{ fontSize: 12.5, color: "var(--text-3)" }}>{k}</span>
            <span className="mono" style={{ fontSize: 12.5, color: "var(--text)", fontWeight: 500 }}>{v}</span>
          </div>
        ))}
      </div>
    </DocSection>
  );
}

function DocStructure() {
  return (
    <DocSection id="structure" eyebrow="§ 14" title="Folder structure"
      lede="Strict module boundaries. Risk has veto power. Exchange clients are interchangeable behind the factory. Tests live alongside the modules they protect.">
      <CodeBox lang="tree">
{`Auto-Trader/
├── main.py                  — FastAPI app entry · Uvicorn runner
├── requirements.txt         — Pinned deps
├── Dockerfile
├── docker-compose.yml
├── .env.example             — Template (committed)
├── .env                     — Secrets (gitignored)
├── .gitignore
├── README.md
│
├── app/
│   ├── config.py            — Pydantic settings, loads .env
│   ├── models.py            — Trade, Signal, DailyStats
│   └── database.py          — Async engine, WAL init
│
├── app/api/
│   ├── webhook.py           — POST /webhook · dedup · validation
│   └── health.py            — GET /health
│
├── app/strategy/
│   ├── signal_handler.py    — Parse · validate · deduplicate
│   └── signal_models.py     — Pydantic payload models
│
├── app/execution/           — ⭐ factory pattern (v3)
│   ├── base_client.py       — AbstractExchangeClient ABC
│   ├── exchange_factory.py  — Reads ACTIVE_EXCHANGE → client
│   ├── binance_client.py    — OCO orders · testnet support
│   ├── kraken_client.py     — Conditional close · paper mode
│   └── order_manager.py     — Exchange-agnostic dispatch
│
├── app/risk/                — ⭐ VETO power
│   ├── risk_manager.py      — Pre-trade checks · sizing
│   └── circuit_breaker.py   — Daily loss · drawdown · cooldown
│
├── app/alerts/
│   ├── telegram_bot.py      — Notifications · /commands
│   └── templates.py
│
├── app/recovery/
│   └── state_reconciler.py  — DB ↔ exchange on boot
│
├── tests/
│   ├── test_risk_manager.py
│   ├── test_exchange_factory.py
│   ├── test_binance_client.py
│   ├── test_kraken_client.py
│   ├── test_signal_handler.py
│   ├── test_webhook.py
│   └── conftest.py          — Mock exchanges (both)
│
└── logs/                    — Runtime logs (gitignored)`}
      </CodeBox>
    </DocSection>
  );
}

function DocSecurity() {
  return (
    <DocSection id="security" eyebrow="§ 15" title="Security"
      lede="API keys never touch code or DB. The bot's exchange key must have Withdraw permission DISABLED — verified on startup.">
      <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 10 }}>
        <SecRow t="Encrypted secrets"   d="Loaded from .env via pydantic-settings · never hardcoded · never in logs" />
        <SecRow t="IP whitelisting"     d="Configure in exchange API settings. Restrict to VPS IP only." />
        <SecRow t="Disable withdrawals" d="API key must have Trade permission only. The bot probes this on save and refuses to start if Withdraw is on." />
        <SecRow t="Webhook secret"      d="Every /webhook request must include X-Webhook-Secret. Missing or wrong → 401. Rotate periodically." />
        <SecRow t="Rate limiting"       d="SlowAPI · /webhook 10/min · /health 30/min" />
        <SecRow t="Audit log"           d="All sensitive config changes appended to /var/log/audit.log" />
      </ul>
    </DocSection>
  );
}

function SecRow({ t, d }) {
  return (
    <li style={{ display: "flex", gap: 14, padding: 12, borderRadius: 10, background: "rgba(25,229,166,0.04)", border: "1px solid rgba(25,229,166,0.18)" }}>
      <div style={{ width: 26, height: 26, borderRadius: 6, background: "rgba(25,229,166,0.14)", color: "var(--profit)", display: "flex", alignItems: "center", justifyContent: "center", flex: "0 0 26px" }}>
        <IconCheck size={14} />
      </div>
      <div style={{ minWidth: 0, flex: 1 }}>
        <div style={{ fontSize: 13, fontWeight: 600 }}>{t}</div>
        <div style={{ fontSize: 12, color: "var(--text-2)", marginTop: 3, lineHeight: 1.55 }}>{d}</div>
      </div>
    </li>
  );
}

function DocRecovery() {
  return (
    <DocSection id="recovery" eyebrow="§ 16" title="State recovery"
      lede="On every boot, the reconciler compares the SQLite trade log with the exchange's open positions. Orphans halt the bot and notify — never auto-closed.">
      <ol style={{ margin: 0, paddingLeft: 18, fontSize: 13.5, color: "var(--text-2)", lineHeight: 1.8 }}>
        <li>Read <code className="mono">ACTIVE_EXCHANGE</code> from config; instantiate that client.</li>
        <li>Query open positions via <code className="mono">get_open_positions()</code>.</li>
        <li>Cross-reference with last-known DB state.</li>
        <li><strong style={{ color: "var(--text)" }}>If orphan position found</strong> → log WARNING, send Telegram alert, halt. User decides.</li>
        <li>Load daily P&L from DB so the daily-loss limit picks up where it left off.</li>
        <li>Verify the trading pair is still available on the active exchange.</li>
        <li>Resume normal operation.</li>
      </ol>
      <Callout kind="danger" title="Zero orphan tolerance">
        Success metric: 100% of untracked positions are caught on boot. The bot will not auto-close — humans decide whether to keep, modify, or exit an orphan position.
      </Callout>
    </DocSection>
  );
}

function DocSetup() {
  return (
    <DocSection id="setup" eyebrow="§ 17" title="Setup & deploy"
      lede="Three steps from clone to first signal. Always start in testnet (or paper for Kraken).">
      <ol style={{ margin: 0, paddingLeft: 18, fontSize: 13.5, color: "var(--text-2)", lineHeight: 1.8, marginBottom: 14 }}>
        <li>Clone the repo and copy <code className="mono">.env.example</code> → <code className="mono">.env</code>.</li>
        <li>Fill in <code className="mono">ACTIVE_EXCHANGE</code>, the matching API credentials, Telegram tokens, and webhook secret.</li>
        <li>Run <code className="mono">docker-compose up -d</code>. The bot starts in testnet/paper by default.</li>
      </ol>
      <CodeBox lang="bash">
{`# 1. Clone
git clone git@github.com:aminbassam/Auto-Trader.git
cd Auto-Trader

# 2. Configure
cp .env.example .env
$EDITOR .env          # set ACTIVE_EXCHANGE, keys, Telegram, secret

# 3. Boot (testnet by default)
docker-compose up -d

# 4. Verify
curl http://localhost:8000/health | jq
# → "status": "ok", "active_exchange": "binance", "trading_mode": "testnet"

# 5. Point TradingView at it
#    Webhook URL:  https://your-vps/webhook
#    Header:       X-Webhook-Secret: $WEBHOOK_SECRET

# 6. Run 7+ days in testnet before flipping TRADING_MODE=live`}
      </CodeBox>
      <Callout kind="success" title="Then what?">
        Watch the dashboard, run a backtest in the Backtester tab (v2 preview), and tune your sliders in Settings → Risk. The Telegram bot will alert on every state change. Ship live only after a clean week in testnet.
      </Callout>
    </DocSection>
  );
}

Object.assign(window, { DocsPage });
