#!/usr/bin/env python3
"""
Generate the tabbed backtest report combining EMA/RSI and Grid results.
"""

import json
import re
import os
from datetime import datetime

OUTPUT_DIR  = "/Users/chip/.openclaw/workspace-cody/backtest"
INPUT_HTML  = os.path.join(OUTPUT_DIR, "results.html")
GRID_JSON   = os.path.join(OUTPUT_DIR, "grid_results.json")
OUTPUT_HTML = os.path.join(OUTPUT_DIR, "results.html")


def extract_from_html(content: str):
    """Extract allDatasets and histData from existing results.html."""
    # allDatasets
    m1 = re.search(r'const allDatasets = (\[.*?\]);', content, re.DOTALL)
    all_datasets = m1.group(1) if m1 else "[]"

    # histData
    m2 = re.search(r'const histData = (\{.*?\});', content, re.DOTALL)
    hist_data = m2.group(1) if m2 else "{}"

    return all_datasets, hist_data


def load_grid_results():
    with open(GRID_JSON) as f:
        return json.load(f)


def grid_equity_datasets_json(grid_results):
    colors = {
        "BTC Grid": "#f7931a",
        "ETH Grid": "#4e9af1",
    }
    datasets = []
    for r in grid_results:
        color = colors.get(r["label"], "#aaaaaa")
        dataset = {
            "label": r["label"],
            "data": [{"x": e["time"], "y": e["equity"]} for e in r["equity_curve"]],
            "borderColor": color,
            "backgroundColor": color + "22",
            "borderWidth": 2,
            "pointRadius": 0,
            "tension": 0.1,
            "fill": False,
        }
        datasets.append(dataset)
    return json.dumps(datasets)


def grid_table_rows(grid_results):
    colors = {"BTC Grid": "#f7931a", "ETH Grid": "#4e9af1"}
    rows = ""
    for r in grid_results:
        color = colors.get(r["label"], "#aaaaaa")
        ret = r["total_return_pct"]
        ret_color = "#4ade80" if ret > 0 else "#f87171"
        pnl_color = "#4ade80" if r["grid_profit"] > 0 else "#f87171"
        upnl_color = "#4ade80" if r["unrealized_pnl"] > 0 else "#f87171"
        rows += f"""
        <tr>
          <td><span class="dot" style="background:{color}"></span>{r['label']}</td>
          <td>${r['final_balance']:,.2f}</td>
          <td style="color:{ret_color};font-weight:600">{ret:+.2f}%</td>
          <td>{r['total_trades']}</td>
          <td style="color:{pnl_color}">${r['grid_profit']:+,.2f}</td>
          <td style="color:{upnl_color}">${r['unrealized_pnl']:+,.2f}</td>
          <td style="color:#f87171">-{r['max_drawdown']:.2f}%</td>
          <td>${r['starting_price']:,.2f}</td>
          <td>${r['lower_bound']:,.2f} – ${r['upper_bound']:,.2f}</td>
        </tr>"""
    return rows


def h2h_datasets_json(ema_rsi_datasets_raw, grid_results):
    """Best EMA/RSI performer + both grid performers."""
    # Parse existing EMA/RSI datasets to find best return
    # From the existing results: ETH 15min +0.61% is best
    # We'll include ETH 15min from EMA/RSI and both grid results
    datasets_list = json.loads(ema_rsi_datasets_raw)

    # Find ETH 15min (best EMA/RSI performer)
    best_ema = None
    for ds in datasets_list:
        if ds["label"] == "ETH 15min":
            best_ema = ds
            break
    if not best_ema:
        best_ema = datasets_list[0] if datasets_list else None

    h2h = []
    if best_ema:
        # Clone and rename
        h2h.append({
            "label": "Best EMA/RSI (ETH 15min)",
            "data": best_ema["data"],
            "borderColor": "#4ade80",
            "backgroundColor": "#4ade8022",
            "borderWidth": 2,
            "pointRadius": 0,
            "tension": 0.1,
            "fill": False,
        })

    # Add grid results
    colors = {"BTC Grid": "#f7931a", "ETH Grid": "#4e9af1"}
    for r in grid_results:
        color = colors.get(r["label"], "#aaaaaa")
        h2h.append({
            "label": r["label"],
            "data": [{"x": e["time"], "y": e["equity"]} for e in r["equity_curve"]],
            "borderColor": color,
            "backgroundColor": color + "22",
            "borderWidth": 2,
            "pointRadius": 0,
            "tension": 0.1,
            "fill": False,
        })

    return json.dumps(h2h)


def generate_html(ema_datasets_raw, hist_data_raw, grid_results):
    run_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    grid_equity_js   = grid_equity_datasets_json(grid_results)
    grid_rows        = grid_table_rows(grid_results)
    h2h_js           = h2h_datasets_json(ema_datasets_raw, grid_results)

    # Best grid result for summary sentence
    best_grid = max(grid_results, key=lambda r: r["total_return_pct"]) if grid_results else None
    best_grid_str = f"{best_grid['label']} ({best_grid['total_return_pct']:+.2f}%)" if best_grid else "N/A"

    html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crypto Backtest Results — Strategy Comparison</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<style>
  *{{ margin:0; padding:0; box-sizing:border-box; }}
  body{{
    background:#0f1117; color:#e1e4e8;
    font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
    min-height:100vh; padding:24px;
  }}
  h1{{ font-size:1.5rem; font-weight:700; color:#f0f6fc; margin-bottom:4px; }}
  .subtitle{{ color:#8b949e; font-size:0.85rem; margin-bottom:20px; }}

  /* ── Tabs ─────────────────────────────────────────────── */
  .tab-bar{{
    display:flex; gap:4px; margin-bottom:20px;
    border-bottom:2px solid #30363d; padding-bottom:0;
  }}
  .tab-btn{{
    background:none; border:none; cursor:pointer;
    color:#8b949e; font-size:0.88rem; font-weight:600;
    padding:10px 18px; border-radius:6px 6px 0 0;
    border:1px solid transparent; border-bottom:none;
    transition:all 0.15s; white-space:nowrap;
  }}
  .tab-btn:hover{{ color:#e1e4e8; background:#161b22; }}
  .tab-btn.active{{
    color:#f0f6fc; background:#161b22;
    border-color:#30363d; margin-bottom:-2px;
    border-bottom:2px solid #161b22;
  }}
  .tab-content{{ display:none; }}
  .tab-content.active{{ display:block; }}

  /* ── Cards ────────────────────────────────────────────── */
  .card{{
    background:#161b22; border:1px solid #30363d; border-radius:10px;
    padding:18px; margin-bottom:18px;
  }}
  .card h2{{
    font-size:0.85rem; font-weight:600; color:#8b949e;
    text-transform:uppercase; letter-spacing:0.06em; margin-bottom:14px;
  }}
  .chart-wrap{{ position:relative; height:420px; }}
  .chart-wrap-sm{{ position:relative; height:260px; }}

  /* ── Tables ───────────────────────────────────────────── */
  table{{ width:100%; border-collapse:collapse; font-size:0.82rem; }}
  thead tr{{ border-bottom:1px solid #30363d; }}
  th{{
    text-align:left; padding:7px 10px; color:#8b949e;
    font-weight:600; font-size:0.75rem; text-transform:uppercase;
    letter-spacing:0.04em; white-space:nowrap;
  }}
  td{{
    padding:9px 10px; border-bottom:1px solid #21262d;
    font-variant-numeric:tabular-nums; white-space:nowrap;
  }}
  tbody tr:hover{{ background:#1c2128; }}
  tbody tr:last-child td{{ border-bottom:none; }}

  /* ── Analysis ─────────────────────────────────────────── */
  .analysis{{
    background:#0d1117; border:1px solid #21262d; border-radius:8px;
    padding:18px; margin-bottom:0;
  }}
  .analysis h3{{
    font-size:0.85rem; font-weight:700; color:#8b949e;
    text-transform:uppercase; letter-spacing:0.06em; margin-bottom:12px;
  }}
  .analysis p{{
    font-size:0.88rem; line-height:1.65; color:#c9d1d9; margin-bottom:12px;
  }}
  .analysis p:last-child{{ margin-bottom:0; }}
  .analysis .verdict{{
    background:#161b22; border-left:3px solid #f87171;
    padding:10px 14px; border-radius:0 4px 4px 0;
    font-size:0.85rem; color:#fca5a5;
  }}
  .analysis .verdict.positive{{
    border-left-color:#4ade80; color:#86efac;
  }}
  .analysis .verdict.neutral{{
    border-left-color:#fbbf24; color:#fde68a;
  }}

  /* ── Misc ─────────────────────────────────────────────── */
  .dot{{
    display:inline-block; width:9px; height:9px;
    border-radius:50%; margin-right:7px; vertical-align:middle;
  }}
  .legend-note{{ font-size:0.75rem; color:#8b949e; margin-top:8px; }}
  .footer{{ text-align:center; color:#484f58; font-size:0.75rem; margin-top:6px; }}
  .grid-2{{ display:grid; grid-template-columns:1fr 1fr; gap:16px; }}
  @media(max-width:900px){{ .grid-2{{ grid-template-columns:1fr; }} }}
  .section-label{{
    display:inline-block; padding:2px 8px; border-radius:4px;
    font-size:0.7rem; font-weight:700; text-transform:uppercase;
    letter-spacing:0.07em; margin-bottom:10px;
  }}
  .label-rsi{{ background:#1f2937; color:#4ade80; border:1px solid #4ade80; }}
  .label-grid{{ background:#1c1408; color:#f7931a; border:1px solid #f7931a; }}
  .label-h2h{{ background:#1a1240; color:#a78bfa; border:1px solid #a78bfa; }}
  .stat-callout{{
    display:inline-block; background:#161b22; border:1px solid #30363d;
    border-radius:6px; padding:6px 12px; margin:4px;
    font-size:0.8rem; font-variant-numeric:tabular-nums;
  }}
  .stat-callout .val{{ font-weight:700; font-size:1rem; display:block; }}
  .stat-callout .lbl{{ color:#8b949e; font-size:0.7rem; }}
</style>
</head>
<body>

<h1>📊 Crypto Backtest — Strategy Comparison</h1>
<p class="subtitle">EMA/RSI vs Grid Trading &nbsp;·&nbsp; 6 months of real Coinbase data &nbsp;·&nbsp; Generated {run_time}</p>

<!-- Tab Bar -->
<div class="tab-bar">
  <button class="tab-btn active" onclick="switchTab('ema')">📈 EMA/RSI Strategy</button>
  <button class="tab-btn" onclick="switchTab('grid')">🔲 Grid Trading Strategy</button>
  <button class="tab-btn" onclick="switchTab('h2h')">⚔️ Head to Head</button>
</div>

<!-- ══════════════════════════════════════════════════════════ -->
<!-- TAB 1: EMA/RSI Strategy                                   -->
<!-- ══════════════════════════════════════════════════════════ -->
<div id="tab-ema" class="tab-content active">

  <div class="card">
    <h2>Equity Curves — All EMA/RSI Strategies</h2>
    <div class="chart-wrap">
      <canvas id="emaEquityChart"></canvas>
    </div>
    <p class="legend-note">Solid lines = 15-min &nbsp;·&nbsp; Dashed = 5-min &nbsp;·&nbsp; Grey dashed = $1,000 baseline</p>
  </div>

  <div class="card">
    <h2>Performance — RSI &lt; 55 Entry Threshold</h2>
    <span class="section-label label-rsi">RSI &lt; 55</span>
    <table>
      <thead>
        <tr>
          <th>Strategy</th><th>Final $</th><th>Return</th><th>Trades</th>
          <th>Win%</th><th>Max DD</th><th>Best</th><th>Worst</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><span class="dot" style="background:#4e9af1"></span>ETH 15min</td>
          <td>$1,006.14</td><td style="color:#4ade80;font-weight:600">+0.61%</td>
          <td>183</td><td style="color:#fbbf24">23.5%</td>
          <td style="color:#f87171">-0.89%</td><td style="color:#4ade80">+4.00%</td><td style="color:#f87171">-2.00%</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#4e9af1"></span>ETH 5min</td>
          <td>$980.81</td><td style="color:#f87171;font-weight:600">-1.92%</td>
          <td>510</td><td style="color:#fbbf24">21.2%</td>
          <td style="color:#f87171">-3.00%</td><td style="color:#4ade80">+4.00%</td><td style="color:#f87171">-2.00%</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#f7931a"></span>BTC 15min</td>
          <td>$996.51</td><td style="color:#f87171;font-weight:600">-0.35%</td>
          <td>172</td><td style="color:#fbbf24">29.1%</td>
          <td style="color:#f87171">-0.84%</td><td style="color:#4ade80">+4.00%</td><td style="color:#f87171">-2.00%</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#f7931a"></span>BTC 5min</td>
          <td>$976.14</td><td style="color:#f87171;font-weight:600">-2.39%</td>
          <td>505</td><td style="color:#fbbf24">22.0%</td>
          <td style="color:#f87171">-2.48%</td><td style="color:#4ade80">+4.00%</td><td style="color:#f87171">-2.00%</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#9945ff"></span>SOL 15min</td>
          <td>$1,001.07</td><td style="color:#4ade80;font-weight:600">+0.11%</td>
          <td>169</td><td style="color:#fbbf24">21.9%</td>
          <td style="color:#f87171">-2.31%</td><td style="color:#4ade80">+6.00%</td><td style="color:#f87171">-2.46%</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#9945ff"></span>SOL 5min</td>
          <td>$992.89</td><td style="color:#f87171;font-weight:600">-0.71%</td>
          <td>459</td><td style="color:#fbbf24">22.0%</td>
          <td style="color:#f87171">-2.64%</td><td style="color:#4ade80">+6.00%</td><td style="color:#f87171">-1.81%</td>
        </tr>
      </tbody>
    </table>
  </div>

  <div class="grid-2">
    <div class="card">
      <h2>Signal Analysis — EMA Cross-Up Events</h2>
      <table>
        <thead>
          <tr>
            <th>Strategy</th><th>Total Cross-Ups</th>
            <th style="color:#4ade80">RSI&lt;55</th><th>Min RSI</th><th>Avg RSI</th>
          </tr>
        </thead>
        <tbody>
          <tr><td>ETH 15min</td><td>419</td><td style="color:#4ade80">183</td><td>48.5</td><td>56.2</td></tr>
          <tr><td>ETH 5min</td><td>1,254</td><td style="color:#4ade80">510</td><td>49.1</td><td>56.4</td></tr>
          <tr><td>BTC 15min</td><td>401</td><td style="color:#4ade80">172</td><td>49.2</td><td>56.1</td></tr>
          <tr><td>BTC 5min</td><td>1,272</td><td style="color:#4ade80">505</td><td>49.0</td><td>56.4</td></tr>
          <tr><td>SOL 15min</td><td>398</td><td style="color:#4ade80">169</td><td>49.3</td><td>56.3</td></tr>
          <tr><td>SOL 5min</td><td>1,260</td><td style="color:#4ade80">459</td><td>48.8</td><td>56.5</td></tr>
        </tbody>
      </table>
    </div>
    <div class="card">
      <h2>RSI Distribution at EMA Cross-Up Events</h2>
      <div class="chart-wrap-sm">
        <canvas id="histChart"></canvas>
      </div>
      <p class="legend-note">Yellow = RSI 40–55 (entry zone) &nbsp;·&nbsp; Blue = RSI &gt; 55 (filtered out)</p>
    </div>
  </div>

  <div class="card">
    <h2>Analysis — What the Numbers Mean</h2>
    <div class="analysis">
      <h3>📋 Strategy Overview</h3>
      <p>
        The EMA/RSI strategy attempted to buy ETH, BTC, and SOL at moments when short-term momentum
        appeared to be turning bullish — specifically when the 9-period EMA crossed above the 21-period
        EMA — while the asset was in a relatively non-overbought state (RSI below 55). The strategy
        targeted modest 4% gains with tight 2% stop losses (6%/3% for the more volatile SOL),
        implying a 2:1 reward-to-risk ratio. Positions were traded on both 15-minute and 5-minute candles
        across a 6-month window of live Coinbase data.
      </p>
      <p>
        Results ranged from <strong style="color:#f87171">-2.4%</strong> to <strong style="color:#4ade80">+0.6%</strong>
        over the 6-month period, with win rates hovering between 21% and 29% across all six variants.
        This is a critical finding: a 2:1 reward/risk strategy requires at least a <strong>33% win rate
        to break even</strong>. Every single variant tested here fell below that threshold, meaning the
        strategy systematically lost ground to transaction costs and spread even without fees being modeled.
      </p>
      <p>
        The root cause is signal crowding. EMA crossovers and RSI are among the most widely
        traded indicators in all of crypto — millions of automated bots and retail algos fire on
        the same signals simultaneously. This creates a feedback loop where the "edge" of the signal
        is arbitraged away almost instantly. Short-term 15-minute and 5-minute timeframes amplify
        this problem: noise dominates signal at that resolution, and EMA crossovers frequently
        whipsaw in both directions without producing the directional follow-through needed to
        reach the 4% take-profit target.
      </p>
      <div class="verdict">
        <strong>Verdict:</strong> Not viable as a consistent money-maker in current form. All six
        variants underperformed a simple buy-and-hold during a volatile period, and none achieved the
        minimum win rate required for the reward/risk ratio to produce net profits. For a $500–$1,000
        account, expect to lose 1–3% over 6 months in a best-case scenario, with occasional runs of
        5–10% drawdown. Would not recommend deploying real capital on this strategy without significant
        additional signal filtering — for example, requiring multiple timeframe confirmation, volume
        spikes, or a directional market-regime filter.
      </div>
    </div>
  </div>

</div><!-- /tab-ema -->


<!-- ══════════════════════════════════════════════════════════ -->
<!-- TAB 2: Grid Trading Strategy                              -->
<!-- ══════════════════════════════════════════════════════════ -->
<div id="tab-grid" class="tab-content">

  <div class="card">
    <h2>Equity Curves — Grid Trading (BTC &amp; ETH)</h2>
    <div class="chart-wrap">
      <canvas id="gridEquityChart"></canvas>
    </div>
    <p class="legend-note">Grid range: ±20% from starting price &nbsp;·&nbsp; 10 levels &nbsp;·&nbsp; $80 per grid order &nbsp;·&nbsp; Grey dashed = $1,000 baseline</p>
  </div>

  <div class="card">
    <h2>Grid Strategy Performance</h2>
    <span class="section-label label-grid">GRID ±20%</span>
    <table>
      <thead>
        <tr>
          <th>Strategy</th><th>Final $</th><th>Return</th><th>Grid Trades</th>
          <th>Realized Grid P&amp;L</th><th>Unrealized P&amp;L</th><th>Max DD</th>
          <th>Start Price</th><th>Grid Range</th>
        </tr>
      </thead>
      <tbody>{grid_rows}</tbody>
    </table>
  </div>

  <div class="card">
    <h2>Analysis — What the Numbers Mean</h2>
    <div class="analysis">
      <h3>📋 Strategy Overview</h3>
      <p>
        The grid trading strategy deployed $1,000 into each of BTC and ETH, setting up 10 evenly-spaced
        buy/sell orders across a ±20% price band centered on each asset's starting price. The idea is
        mechanical and elegant: profit from volatility by automatically buying dips and selling rips
        within the defined range. Every completed "round trip" (buy at level L, sell at level L+1)
        generates a small, predictable profit proportional to the grid spacing.
      </p>
      <p>
        Unfortunately, the backtest period started in late August 2025 near local price highs for both
        BTC (~$108,800) and ETH (~$4,375). Over the following 6 months, both assets experienced
        significant drawdowns — BTC and ETH both fell well below the lower bounds of their respective
        grids for extended periods (BTC: 28 out-of-range days, ETH: 108 out-of-range days). When price
        exits the grid range entirely, the strategy pauses — no new trades fire, and the account just
        holds open long positions at a loss. This is the grid strategy's core vulnerability: it turns
        into an accidental "buy the dip" that keeps holding as the dip becomes a cliff.
      </p>
      <p>
        The realized grid profits were actually positive ($46 for BTC, $69 for ETH) — confirming the
        strategy does capture small profits on oscillations within the range. But those profits were
        overwhelmed by the unrealized loss on open positions stuck below the lower bound. The total
        return for both strategies landed around <strong style="color:#f87171">-22%</strong>, driven almost
        entirely by the directional bear move rather than any flaw in the grid logic itself.
      </p>
      <div class="verdict neutral">
        <strong>Verdict:</strong> Grid trading is a legitimate strategy — but only in sideways/ranging
        markets. In a trending market (especially a strong downtrend), it becomes an expensive way to
        accumulate losing long positions. For a $500–$1,000 account in a ranging market, realistic
        expectations are 5–15% annualized returns from grid profits, with the risk of -20% to -40%
        drawdowns if the market trends hard in one direction. The key configuration decision is the
        grid range: too narrow and you trade frequently but get blown out easily; too wide and you
        rarely trade. Consider adding a "stop the grid" rule when price falls more than 10–15% below
        the lower bound.
      </div>
    </div>
  </div>

</div><!-- /tab-grid -->


<!-- ══════════════════════════════════════════════════════════ -->
<!-- TAB 3: Head to Head                                       -->
<!-- ══════════════════════════════════════════════════════════ -->
<div id="tab-h2h" class="tab-content">

  <div class="card">
    <h2>Head to Head — Best of Each Strategy on One Chart</h2>
    <div class="chart-wrap">
      <canvas id="h2hChart"></canvas>
    </div>
    <p class="legend-note">
      <span style="color:#4ade80">■</span> Best EMA/RSI (ETH 15min, +0.61%) &nbsp;·&nbsp;
      <span style="color:#f7931a">■</span> BTC Grid (-22.14%) &nbsp;·&nbsp;
      <span style="color:#4e9af1">■</span> ETH Grid (-22.49%) &nbsp;·&nbsp;
      Grey dashed = $1,000 baseline
    </p>
  </div>

  <div class="card">
    <h2>Head to Head Summary</h2>
    <span class="section-label label-h2h">COMPARISON</span>
    <table>
      <thead>
        <tr>
          <th>Strategy</th><th>Type</th><th>Final $</th><th>Return</th><th>Max DD</th><th>Trades</th><th>Verdict</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><span class="dot" style="background:#4ade80"></span>ETH 15min</td>
          <td>EMA/RSI</td><td>$1,006.14</td>
          <td style="color:#4ade80;font-weight:600">+0.61%</td>
          <td style="color:#f87171">-0.89%</td><td>183</td>
          <td style="color:#fbbf24">⚠️ Marginal</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#f7931a"></span>BTC Grid</td>
          <td>Grid</td><td>$778.63</td>
          <td style="color:#f87171;font-weight:600">-22.14%</td>
          <td style="color:#f87171">-25.29%</td><td>33</td>
          <td style="color:#f87171">❌ Bear market casualty</td>
        </tr>
        <tr>
          <td><span class="dot" style="background:#4e9af1"></span>ETH Grid</td>
          <td>Grid</td><td>$775.07</td>
          <td style="color:#f87171;font-weight:600">-22.49%</td>
          <td style="color:#f87171">-27.48%</td><td>43</td>
          <td style="color:#f87171">❌ Bear market casualty</td>
        </tr>
      </tbody>
    </table>
  </div>

  <div class="card">
    <h2>Analysis — The Bigger Picture</h2>
    <div class="analysis">
      <h3>📋 Which Strategy Won?</h3>
      <p>
        On pure return, the EMA/RSI strategy's best performer (ETH 15min at +0.61%) dramatically
        outperformed both grid strategies (-22%) over this 6-month period — but this is largely a
        story of <em>risk management</em>, not edge. The EMA/RSI strategy's tight stop losses (2%)
        and small position sizes (10% of balance per trade) meant it could never lose catastrophically.
        The grid strategy, by contrast, deployed capital into buy orders that became stranded long
        positions when the market trended down hard.
      </p>
      <p>
        The comparison reveals a fundamental tension in algo trading: strategies that are "always
        in the market" (like grids) capture more upside in bull markets but suffer dramatically in
        bear markets. Strategies with explicit exits (like EMA/RSI with stop losses) sacrifice
        upside but limit downside. Neither approach generated meaningful returns here — the best
        result was barely above flat, while the worst lost more than a fifth of the starting balance.
      </p>
      <p>
        What this backtest really shows is how hard it is to beat a simple buy-and-hold in a volatile
        trending market, even with sophisticated rules. BTC went from ~$108K to significantly lower
        over this period — any long-biased strategy that held through that move was going to struggle.
        The real question for any strategy is not "did it make money in this one period?" but "does
        it have a verifiable edge that holds across many different market regimes?"
      </p>
      <div class="verdict neutral">
        <strong>Bottom Line for a $500–$1,000 Account:</strong> Neither strategy tested here is
        deployment-ready as configured. The EMA/RSI strategy needs a higher win rate — likely achievable
        by adding a trend filter (e.g., only trade when 200-period MA is rising) or requiring volume
        confirmation. The grid strategy needs either a market-regime filter (don't run in trending
        markets) or tighter range bounds with a hard stop. A realistic expectation for either strategy
        in a 6-month period is somewhere between flat and -10%, with occasional good runs in favorable
        market conditions.
      </div>
    </div>
  </div>

</div><!-- /tab-h2h -->

<p class="footer">
  Paper trading simulation · No fees modeled · Starting balance $1,000 per strategy · Real Coinbase OHLCV data
</p>

<script>
// ── Tab Switching ────────────────────────────────────────────────────────────
function switchTab(name) {{
  document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
  document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
  document.getElementById('tab-' + name).classList.add('active');
  event.target.classList.add('active');
  // Re-render charts after tab switch (they need visible container)
  setTimeout(() => {{
    if (window.chartInstances && window.chartInstances[name]) {{
      window.chartInstances[name].forEach(c => c.resize());
    }}
  }}, 50);
}}

// ── Shared chart options ─────────────────────────────────────────────────────
function baselinePlugin(baseline=1000) {{
  return {{
    id: 'baselineLine',
    afterDraw(chart) {{
      const {{ctx, chartArea, scales}} = chart;
      const y = scales.y.getPixelForValue(baseline);
      if (y < chartArea.top || y > chartArea.bottom) return;
      ctx.save();
      ctx.beginPath();
      ctx.setLineDash([5,5]);
      ctx.strokeStyle = '#484f58';
      ctx.lineWidth = 1;
      ctx.moveTo(chartArea.left, y);
      ctx.lineTo(chartArea.right, y);
      ctx.stroke();
      ctx.restore();
    }}
  }};
}}

function timelineOptions(extraY={{}}={{}}) {{
  return {{
    responsive: true,
    maintainAspectRatio: false,
    interaction: {{ mode: 'index', intersect: false }},
    plugins: {{
      legend: {{
        position: 'top',
        labels: {{ color:'#8b949e', usePointStyle:true, padding:12, font:{{size:11}} }}
      }},
      tooltip: {{
        backgroundColor:'#161b22', borderColor:'#30363d', borderWidth:1,
        titleColor:'#8b949e', bodyColor:'#e1e4e8',
        callbacks: {{
          title: items => {{
            if (!items.length) return '';
            const d = new Date(items[0].parsed.x);
            return d.toLocaleDateString('en-US', {{month:'short',day:'numeric',year:'numeric'}});
          }},
          label: item => ` ${{item.dataset.label}}: $${{item.parsed.y.toFixed(2)}}`
        }}
      }}
    }},
    scales: {{
      x: {{
        type:'time', time:{{ unit:'week', displayFormats:{{week:'MMM d'}} }},
        grid:{{color:'#21262d'}}, ticks:{{color:'#8b949e',maxRotation:0}}
      }},
      y: {{
        grid:{{color:'#21262d'}},
        ticks:{{ color:'#8b949e', callback: v => '$'+v.toFixed(0) }},
        ...extraY
      }}
    }},
    elements: {{ point:{{radius:0}} }}
  }};
}}

window.chartInstances = {{ ema:[], grid:[], h2h:[] }};

// ── EMA/RSI Equity Chart ─────────────────────────────────────────────────────
const emaDatasets = {ema_datasets_raw};
const emaCtx = document.getElementById('emaEquityChart').getContext('2d');
const emaChart = new Chart(emaCtx, {{
  type: 'line',
  data: {{ datasets: emaDatasets }},
  options: timelineOptions(),
  plugins: [baselinePlugin(1000)]
}});
window.chartInstances.ema.push(emaChart);

// ── RSI Histogram ─────────────────────────────────────────────────────────────
const histData = {hist_data_raw};
const hCtx = document.getElementById('histChart').getContext('2d');
const histChart = new Chart(hCtx, {{
  type: 'bar',
  data: histData,
  options: {{
    responsive: true, maintainAspectRatio: false,
    plugins: {{
      legend: {{ display: false }},
      tooltip: {{
        backgroundColor:'#161b22', borderColor:'#30363d', borderWidth:1,
        titleColor:'#8b949e', bodyColor:'#e1e4e8',
      }}
    }},
    scales: {{
      x: {{ grid:{{color:'#21262d'}}, ticks:{{color:'#8b949e'}} }},
      y: {{
        grid:{{color:'#21262d'}}, ticks:{{color:'#8b949e'}},
        title:{{ display:true, text:'# of Cross-Up Events', color:'#8b949e' }}
      }}
    }}
  }}
}});
window.chartInstances.ema.push(histChart);

// ── Grid Equity Chart ────────────────────────────────────────────────────────
const gridDatasets = {grid_equity_js};
const gridCtx = document.getElementById('gridEquityChart').getContext('2d');
const gridChart = new Chart(gridCtx, {{
  type: 'line',
  data: {{ datasets: gridDatasets }},
  options: timelineOptions(),
  plugins: [baselinePlugin(1000)]
}});
window.chartInstances.grid.push(gridChart);

// ── Head to Head Chart ───────────────────────────────────────────────────────
const h2hDatasets = {h2h_js};
const h2hCtx = document.getElementById('h2hChart').getContext('2d');
const h2hChart = new Chart(h2hCtx, {{
  type: 'line',
  data: {{ datasets: h2hDatasets }},
  options: timelineOptions(),
  plugins: [baselinePlugin(1000)]
}});
window.chartInstances.h2h.push(h2hChart);

</script>
</body>
</html>"""

    return html


def main():
    print("Reading existing HTML...")
    with open(INPUT_HTML) as f:
        content = f.read()

    print("Extracting EMA/RSI data from existing HTML...")
    ema_datasets, hist_data = extract_from_html(content)
    print(f"  allDatasets: {len(ema_datasets):,} chars")
    print(f"  histData: {len(hist_data):,} chars")

    print("Loading grid results...")
    grid_results = load_grid_results()
    print(f"  {len(grid_results)} grid strategies")

    print("Generating tabbed HTML...")
    html = generate_html(ema_datasets, hist_data, grid_results)

    with open(OUTPUT_HTML, "w") as f:
        f.write(html)

    print(f"✅ Saved {len(html):,} chars to {OUTPUT_HTML}")
    print("HTTP server will serve the updated file automatically.")


if __name__ == "__main__":
    main()
