COTexplorer/frontend/index.html
Greg 90c2ae3f35 Expose disaggregated COT data in the UI (wheat focus)
The disagg dataset (2019–2026, 468 markets) was in the DB but invisible.
This wires it into every layer of the app:

Backend:
- models.py: add has_disagg to CommodityMeta; add DisaggPositionPoint,
  DisaggHistoryResponse, DisaggScreenerRow models
- commodities.py: join disagg_reports to populate has_disagg flag and
  correct first/last dates; HAVING filter removes markets with no data
- disagg.py (new): /api/disagg/{code}/history, /api/disagg/screener,
  /api/disagg/{code}/net-position-percentile, /api/disagg/compare
- main.py: register disagg router

Frontend:
- Metric selector shows Disaggregated optgroup (Managed Money, Prod/Merchant,
  Swap Dealer, Other Rept) when market has has_disagg=true, hides Legacy group
- Detail view auto-switches to disagg endpoint and defaults to m_money_net
  for disagg markets; shows green "Disaggregated" badge
- Screener always uses disagg endpoint (Managed Money percentile rank)
- Compare uses /api/disagg/compare for disagg metrics
- style.css: add .badge-disagg green variant

Result: wheat markets (SRW, HRW, HRSpring, Black Sea) now show 7 years of
disaggregated positioning data with Managed Money as the default metric.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 18:22:16 +01:00

214 lines
8.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CFTC COT Explorer</title>
<link rel="stylesheet" href="/static/style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2/dist/chart.umd.min.js"></script>
</head>
<body>
<header>
<div class="header-left">
<span class="logo">COT Explorer</span>
<select id="exchangeFilter">
<option value="">All Exchanges</option>
</select>
</div>
<nav>
<button class="tab-btn active" data-view="detail">Market Detail</button>
<button class="tab-btn" data-view="screener">Screener</button>
<button class="tab-btn" data-view="compare">Compare</button>
</nav>
</header>
<main>
<!-- ======== DETAIL VIEW ======== -->
<div id="view-detail" class="view active">
<aside id="market-sidebar">
<input type="text" id="marketSearch" placeholder="Search markets..." autocomplete="off">
<div id="market-tree"></div>
</aside>
<section id="detail-main">
<div id="detail-placeholder" class="placeholder">
<p>Select a market from the left panel to begin exploring.</p>
</div>
<div id="detail-content" style="display:none">
<div id="detail-header">
<h2 id="detail-title"></h2>
<span id="detail-exchange" class="badge"></span>
<span id="detail-report-type" class="badge badge-disagg" style="display:none">Disaggregated</span>
<span id="detail-unit" class="unit"></span>
</div>
<div class="chart-controls">
<div class="control-group">
<label>Range</label>
<div class="btn-group" id="rangeGroup">
<button class="range-btn" data-range="1Y">1Y</button>
<button class="range-btn active" data-range="3Y">3Y</button>
<button class="range-btn" data-range="5Y">5Y</button>
<button class="range-btn" data-range="Max">Max</button>
</div>
</div>
<div class="control-group">
<label>Row type</label>
<div class="btn-group" id="rowTypeGroup">
<button class="rt-btn active" data-rt="All">All</button>
<button class="rt-btn" data-rt="Old">Old</button>
<button class="rt-btn" data-rt="Other">Other</button>
</div>
</div>
<div class="control-group">
<label>Metric</label>
<select id="metricSelect">
<optgroup label="Disaggregated" id="metricDisaggGroup">
<option value="m_money_net">Managed Money Net</option>
<option value="m_money_long">Mgd Money Long</option>
<option value="m_money_short">Mgd Money Short</option>
<option value="prod_merc_net">Prod/Merchant Net</option>
<option value="swap_net">Swap Dealer Net</option>
<option value="other_rept_net">Other Rept Net</option>
<option value="open_interest">Open Interest</option>
<option value="pct_m_money_long">% Mgd Money Long</option>
<option value="pct_m_money_short">% Mgd Money Short</option>
</optgroup>
<optgroup label="Legacy COT" id="metricLegacyGroup">
<option value="noncomm_net">Non-Comm Net</option>
<option value="noncomm_long">Non-Comm Long</option>
<option value="noncomm_short">Non-Comm Short</option>
<option value="comm_net">Commercial Net</option>
<option value="open_interest">Open Interest</option>
<option value="pct_noncomm_long">% Non-Comm Long</option>
<option value="pct_noncomm_short">% Non-Comm Short</option>
</optgroup>
</select>
</div>
<div class="control-group">
<label>Overlay</label>
<label class="checkbox-label">
<input type="checkbox" id="overlayOI" checked> Open Interest
</label>
</div>
</div>
<div class="chart-wrapper">
<canvas id="detailChart"></canvas>
</div>
<div id="stats-bar">
<div class="stat-item">
<span class="stat-label">Current</span>
<span class="stat-value" id="statCurrent"></span>
</div>
<div class="stat-item">
<span class="stat-label">Period High</span>
<span class="stat-value" id="statMax"></span>
</div>
<div class="stat-item">
<span class="stat-label">Period Low</span>
<span class="stat-value" id="statMin"></span>
</div>
<div class="stat-item">
<span class="stat-label">3Y Percentile</span>
<span class="stat-value" id="statPctile"></span>
</div>
<div class="stat-item">
<span class="stat-label">Week Change</span>
<span class="stat-value" id="statChange"></span>
</div>
<div class="stat-item">
<span class="stat-label" id="statNetLabel">Net</span>
<span class="stat-value" id="statNet"></span>
</div>
</div>
</div>
</section>
</div>
<!-- ======== SCREENER VIEW ======== -->
<div id="view-screener" class="view" style="display:none">
<div class="screener-toolbar">
<h2>Market Screener</h2>
<div class="screener-filters">
<select id="screenerExchange">
<option value="">All Exchanges</option>
</select>
<select id="screenerDirection">
<option value="">All directions</option>
<option value="long">Extreme long (≥70th pctile)</option>
<option value="short">Extreme short (≤30th pctile)</option>
</select>
<select id="screenerLookback">
<option value="52">1-year lookback</option>
<option value="156" selected>3-year lookback</option>
<option value="260">5-year lookback</option>
</select>
<button id="screenerRefresh" class="btn-primary">Refresh</button>
</div>
</div>
<div class="table-wrapper">
<table id="screenerTable">
<thead>
<tr>
<th>Market</th>
<th>Exchange</th>
<th class="num">Net Position</th>
<th class="num">Open Interest</th>
<th class="num">Pctile Rank</th>
<th class="num">Wk Chg Long</th>
<th class="num">Wk Chg Short</th>
</tr>
</thead>
<tbody id="screenerBody"></tbody>
</table>
</div>
</div>
<!-- ======== COMPARE VIEW ======== -->
<div id="view-compare" class="view" style="display:none">
<div class="compare-toolbar">
<h2>Compare Markets</h2>
<div class="compare-controls">
<div class="compare-search-wrap">
<input type="text" id="compareSearch" placeholder="Add market..." autocomplete="off">
<div id="compareDropdown" class="autocomplete-dropdown" style="display:none"></div>
</div>
<div class="control-group">
<label>Metric</label>
<select id="compareMetric">
<optgroup label="Disaggregated">
<option value="m_money_net">Managed Money Net</option>
<option value="prod_merc_net">Prod/Merchant Net</option>
<option value="swap_net">Swap Dealer Net</option>
<option value="open_interest">Open Interest</option>
</optgroup>
<optgroup label="Legacy COT">
<option value="noncomm_net">Non-Comm Net</option>
<option value="comm_net">Commercial Net</option>
<option value="pct_noncomm_long">% Non-Comm Long</option>
</optgroup>
</select>
</div>
<div class="control-group">
<label>Range</label>
<select id="compareRange">
<option value="1Y">1 Year</option>
<option value="3Y" selected>3 Years</option>
<option value="5Y">5 Years</option>
<option value="Max">All</option>
</select>
</div>
</div>
<div id="compareTags"></div>
</div>
<div class="chart-wrapper" id="compareChartWrap">
<div class="placeholder" id="comparePlaceholder">
<p>Search for markets above to add them to the comparison chart.</p>
</div>
<canvas id="compareChart" style="display:none"></canvas>
</div>
</div>
</main>
<script type="module" src="/static/app.js"></script>
</body>
</html>