FastAPI application that ingests CFTC Commitments of Traders data into SQLite and exposes it via a REST API with analytics endpoints (screener, percentile rank, concentration). Includes CLI for historical and weekly data ingestion, Docker setup, and a frontend. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
189 lines
7.0 KiB
HTML
189 lines
7.0 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-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">
|
|
<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>
|
|
</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>
|
|
</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">
|
|
<option value="noncomm_net">Non-Comm Net</option>
|
|
<option value="open_interest">Open Interest</option>
|
|
<option value="comm_net">Commercial Net</option>
|
|
<option value="pct_noncomm_long">% Non-Comm Long</option>
|
|
</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>
|