Files
Chan/web/templates/index.html
T
jackyu66gitandCursor 8c165f11cd fix(web): 未完成笔/线段终点对齐图表最新 K 线
各周期使用对应 kline 数据,终点时间 snap 到 candles,优先使用分析 end_price。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 00:40:41 +08:00

1613 lines
66 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LVVXH3TL04"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LVVXH3TL04');
</script>
<title>缠论分析系统</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
<link href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.min.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- TradingView Widget BEGIN -->
<script src="https://cdn.jsdelivr.net/npm/lightweight-charts@4.0.1/dist/lightweight-charts.standalone.production.js"></script>
<script defer src="{{ url_for('static', filename='js/indicators.js') }}?v=20260809i"></script>
<script defer src="{{ url_for('static', filename='js/charts.js') }}?v=20260809i"></script>
<!-- TradingView Widget END -->
<script>
window.AVAILABLE_TIMEFRAMES = JSON.parse('{{ timeframe_keys_json | safe }}');
window.DEFAULT_MAIN_TIMEFRAME = "{{ default_main_timeframe }}";
window.DEFAULT_ELEMENT_TIMEFRAME = "{{ default_element_timeframe }}";
window.DEFAULT_SUB_SUB_TIMEFRAME = "{{ default_sub_sub_timeframe }}";
window.timeframeToMs = function(tf) {
if (!tf) return null;
var unit = tf.slice(-1);
var value = parseInt(tf.slice(0, -1), 10);
if (isNaN(value)) return null;
var unitMap = {
m: 60 * 1000,
h: 60 * 60 * 1000,
d: 24 * 60 * 60 * 1000,
w: 7 * 24 * 60 * 60 * 1000,
M: 30 * 24 * 60 * 60 * 1000
};
return unitMap[unit] ? value * unitMap[unit] : null;
};
</script>
<style>
body {
font-family: "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
margin: 0;
padding: 10px;
background-color: #f8f9fa;
color: #333;
}
.container {
max-width: 100%;
margin: 0 auto;
background-color: white;
padding: 5px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.header {
margin-bottom: 0px;
padding-bottom: 0px;
}
.controls {
margin-bottom: 10px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.form-label {
font-weight: 500;
margin-bottom: 6px;
color: #495057;
}
/*主图大小*/
.chart-container {
width: 100%;
height: 1200px;
margin-top: 10px;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 10px;
background-color: #fff;
position: relative;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
margin-bottom: 20px; /* 增加下边距,防止被下方数据面板遮挡 */
}
#tradingview_chart {
width: 100%;
height: 100%;
position: relative;
z-index: 1; /* 确保图表在数据面板之上 */
}
/* 挂在主图容器内:左下角 = K线主图左下,而非整图(含副图)底边 */
.wyckoff-cycle-summary {
position: absolute;
left: 8px;
bottom: 28px; /* 略抬高,避开主图时间轴 */
top: auto;
right: auto;
z-index: 1100;
min-width: 200px;
max-width: 300px;
max-height: calc(100% - 36px);
overflow-y: auto;
padding: 8px 10px;
background: rgba(255, 255, 255, 0.94);
border: 1px solid #d0d7de;
border-radius: 6px;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
font-size: 12px;
line-height: 1.45;
color: #24292f;
display: none;
pointer-events: auto;
}
.wyckoff-cycle-summary .wcs-block {
padding: 6px 0;
}
.wyckoff-cycle-summary .wcs-block + .wcs-block {
border-top: 1px solid #eaeef2;
margin-top: 6px;
padding-top: 8px;
}
.wyckoff-cycle-summary .wcs-block.wcs-main { border-left: 3px solid #3498db; padding-left: 8px; }
.wyckoff-cycle-summary .wcs-block.wcs-element { border-left: 3px solid #e67e22; padding-left: 8px; }
.wyckoff-cycle-summary .wcs-block.wcs-subsub { border-left: 3px solid #27ae60; padding-left: 8px; }
.wyckoff-cycle-summary .wcs-title {
font-weight: 650;
font-size: 13px;
margin-bottom: 6px;
letter-spacing: 0.02em;
}
.wyckoff-cycle-summary .wcs-row {
display: flex;
justify-content: space-between;
gap: 8px;
margin: 2px 0;
}
.wyckoff-cycle-summary .wcs-k {
color: #656d76;
flex-shrink: 0;
}
.wyckoff-cycle-summary .wcs-v {
text-align: right;
font-variant-numeric: tabular-nums;
}
.wyckoff-cycle-summary .wcs-active {
margin-top: 2px;
padding: 6px 0 4px;
border-top: 1px solid #eaeef2;
}
.wyckoff-cycle-summary .wcs-prev {
margin-top: 6px;
padding-top: 6px;
border-top: 1px dashed #eaeef2;
color: #656d76;
font-size: 11px;
}
.wyckoff-cycle-summary .wcs-badge {
display: inline-block;
padding: 1px 6px;
border-radius: 3px;
background: #ddf4ff;
color: #0969da;
font-weight: 600;
font-size: 11px;
}
.chart-options {
position: absolute;
top: 10px;
right: 10px;
z-index: 100;
background-color: rgba(255, 255, 255, 0.9);
padding: 12px;
border-radius: 8px;
font-size: 13px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
.data-container {
margin-top: 30px; /* 增加上边距,避免与图表重叠 */
position: relative;
z-index: 10;
background-color: white;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
clear: both; /* 清除浮动 */
}
.nav-tabs {
margin-bottom: 10px;
position: relative;
z-index: 20;
background-color: white;
border-radius: 8px 8px 0 0;
padding: 10px 10px 0 10px;
}
.nav-tabs .nav-link {
border-radius: 6px 6px 0 0;
margin-right: 5px;
font-weight: 500;
transition: all 0.2s ease;
}
.nav-tabs .nav-link:hover {
background-color: #f8f9fa;
border-color: #dee2e6;
}
.nav-tabs .nav-link.active {
background-color: #0d6efd;
color: white;
border-color: #0d6efd;
}
.table-container {
overflow-x: auto;
}
.btn-primary {
background-color: #0d6efd;
border-color: #0d6efd;
color: white;
padding: 8px 16px;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary:hover {
background-color: #0a58ca;
border-color: #0a53be;
}
#loadingIndicator {
display: none;
text-align: center;
padding: 20px;
font-size: 18px;
color: #666;
}
/* 方向列颜色 */
.direction-up {
color: #dc3545;
font-weight: bold;
}
.direction-down {
color: #28a745;
font-weight: bold;
}
/* MACD列颜色 */
.positive {
color: #dc3545;
}
.negative {
color: #28a745;
}
/* 自定义控制面板 */
.form-check-label {
cursor: pointer;
user-select: none;
}
/* 增加表单元素间距 */
.g-3 {
--bs-gutter-y: 1rem;
}
/* 买卖点相关样式已移除 */
/* 主次周期元素样式区分 */
.main-bi {
color: #dc3545;
font-weight: bold;
}
.element-bi {
color: #9c27b0;
font-weight: bold;
}
.main-seg {
color: #FF6B6B;
font-weight: bold;
}
.element-seg {
color: #673ab7;
font-weight: bold;
}
.main-zs {
color: #F1C40F;
font-weight: bold;
}
.element-zs {
color: #3f51b5;
font-weight: bold;
}
/* 加载指示器旋转动画 */
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-spinner {
display: inline-block;
width: 18px;
height: 18px;
border: 2px solid rgba(0, 0, 0, 0.1);
border-left-color: #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 8px;
vertical-align: middle;
}
/* 数据回放相关样式 */
#replayStatus {
min-width: 120px;
}
#replayProgress {
border-radius: 4px;
overflow: hidden;
}
.crosshair-tooltip {
position: absolute;
background: rgba(40, 40, 40, 0.9);
color: white;
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
z-index: 1000;
pointer-events: none;
max-width: 300px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
display: none;
}
/* 主次周期元素样式区分 */
/* 均线系统样式 */
.ma-panel {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 8px;
max-width: 300px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.ma-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 0;
font-size: 12px;
border-bottom: 1px solid #f0f0f0;
}
.ma-item:last-child {
border-bottom: none;
}
.ma-info {
display: flex;
align-items: center;
gap: 8px;
}
.ma-label {
font-weight: 500;
}
.ma-value {
color: #666;
}
.ma-actions {
display: flex;
gap: 4px;
}
.ma-action-btn {
background: none;
border: none;
cursor: pointer;
padding: 2px 4px;
border-radius: 3px;
font-size: 11px;
color: #666;
}
.ma-action-btn:hover {
background-color: #f0f0f0;
color: #333;
}
.ma-color-indicator {
width: 12px;
height: 2px;
border-radius: 1px;
margin-right: 4px;
}
.indicator-dropdown {
position: absolute;
top: 10px;
right: 120px;
z-index: 100;
}
.add-indicator-btn {
background-color: #0d6efd;
color: white;
border: none;
padding: 8px 12px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: flex;
align-items: center;
gap: 5px;
}
.add-indicator-btn:hover {
background-color: #0a58ca;
}
.add-indicator-btn:focus {
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
min-width: 160px;
padding: 0.5rem 0;
margin: 0.125rem 0 0;
font-size: 0.875rem;
color: #212529;
text-align: left;
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0,0,0,.15);
border-radius: 0.375rem;
box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15);
}
.dropdown-menu.show {
display: block;
}
.dropdown-item {
display: block;
width: 100%;
padding: 0.375rem 1rem;
clear: both;
font-weight: 400;
color: #212529;
text-align: inherit;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border: 0;
cursor: pointer;
}
.dropdown-item:hover,
.dropdown-item:focus {
background-color: #f8f9fa;
color: #16181b;
}
.dropdown-item i {
margin-right: 8px;
}
.indicator-panel {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 8px;
max-width: 350px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.indicator-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 0;
font-size: 12px;
border-bottom: 1px solid #f0f0f0;
}
.indicator-item:last-child {
border-bottom: none;
}
.indicator-info {
display: flex;
align-items: center;
gap: 8px;
}
.indicator-label {
font-weight: 500;
}
.indicator-value {
color: #666;
}
.indicator-actions {
display: flex;
gap: 4px;
}
.indicator-action-btn {
background: none;
border: none;
cursor: pointer;
padding: 2px 4px;
border-radius: 3px;
font-size: 11px;
color: #666;
}
.indicator-action-btn:hover {
background-color: #f0f0f0;
color: #333;
}
.indicator-color-indicator {
width: 12px;
height: 2px;
border-radius: 1px;
margin-right: 4px;
}
/* 配置弹窗样式 */
.ma-config-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 10000;
display: none;
justify-content: center;
align-items: center;
}
.ma-config-content {
background: white;
border-radius: 8px;
padding: 20px;
width: 400px;
max-width: 90vw;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}
.ma-config-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 20px;
color: #333;
}
.ma-config-form {
display: grid;
gap: 16px;
}
.ma-config-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.ma-config-label {
font-size: 14px;
font-weight: 500;
color: #555;
}
.ma-config-input, .ma-config-select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.ma-config-input:focus, .ma-config-select:focus {
outline: none;
border-color: #0d6efd;
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
}
.ma-config-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
margin-top: 20px;
}
.ma-config-btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
.ma-config-btn-primary {
background-color: #0d6efd;
color: white;
}
.ma-config-btn-primary:hover {
background-color: #0a58ca;
}
.ma-config-btn-secondary {
background-color: #6c757d;
color: white;
}
.ma-config-btn-secondary:hover {
background-color: #5c636a;
}
.ma-line-preview {
width: 100%;
height: 30px;
border: 1px solid #ddd;
border-radius: 4px;
margin-top: 8px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f8f9fa;
}
.ma-line-preview svg {
width: 80%;
height: 20px;
}
/* 布林带面板样式 */
.bb-panel {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
min-width: 200px;
max-width: 300px;
z-index: 50;
}
.bb-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid #f0f0f0;
font-size: 12px;
}
.bb-item:last-child {
border-bottom: none;
}
.bb-info {
flex: 1;
margin-right: 10px;
}
.bb-label {
font-weight: 600;
color: #333;
}
.bb-value {
color: #666;
font-size: 11px;
}
.bb-actions {
display: flex;
gap: 6px;
}
.bb-action-btn {
width: 20px;
height: 20px;
border: none;
background: #f8f9fa;
border-radius: 3px;
cursor: pointer;
font-size: 10px;
color: #666;
}
.bb-action-btn:hover {
background: #e9ecef;
color: #333;
}
.bb-color-indicator {
width: 12px;
height: 12px;
border-radius: 2px;
margin-right: 4px;
display: inline-block;
}
.bb-config-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.bb-config-content {
background: white;
border-radius: 8px;
padding: 16px;
width: 400px;
max-width: 90vw;
max-height: 85vh;
overflow-y: auto;
}
.bb-config-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
color: #333;
}
.bb-config-form {
display: flex;
flex-direction: column;
gap: 16px;
}
.bb-config-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.bb-config-label {
font-weight: 500;
color: #555;
font-size: 14px;
}
.bb-config-input, .bb-config-select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.2s;
}
.bb-config-input:focus, .bb-config-select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.bb-config-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
margin-top: 20px;
}
.bb-config-btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.bb-config-btn-primary {
background: #667eea;
color: white;
}
.bb-config-btn-primary:hover {
background: #5a67d8;
}
.bb-config-btn-secondary {
background: #f8f9fa;
color: #6c757d;
}
.bb-config-btn-secondary:hover {
background: #e9ecef;
}
.bb-line-preview {
height: 30px;
border: 1px solid #ddd;
border-radius: 4px;
margin-top: 8px;
background: linear-gradient(to right, #f8f9fa, #fff);
position: relative;
}
.bb-line-preview svg {
width: 100%;
height: 100%;
}
.macd-config-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 10000;
display: none;
justify-content: center;
align-items: center;
}
.macd-config-content {
background: white;
border-radius: 8px;
padding: 20px;
width: 360px;
max-width: 90vw;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}
.macd-config-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 20px;
color: #333;
}
.macd-config-form {
display: grid;
gap: 14px;
}
.macd-config-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.macd-config-label {
font-size: 14px;
font-weight: 500;
color: #555;
}
.macd-config-input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.macd-config-input:focus {
outline: none;
border-color: #0d6efd;
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
}
.macd-config-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
}
.macd-config-btn {
padding: 8px 18px;
border: none;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.macd-config-btn-primary {
background: #0d6efd;
color: white;
}
.macd-config-btn-primary:hover {
background: #0b5ed7;
}
.macd-config-btn-secondary {
background: #f8f9fa;
color: #6c757d;
}
.macd-config-btn-secondary:hover {
background: #e9ecef;
}
.macd-config-hint {
font-size: 12px;
color: #999;
margin-top: 2px;
}
</style>
</head>
<body>
<div class="container">
<div class="controls">
<div class="row g-1 d-flex align-items-end">
<div class="col-md-2">
<label for="dataSource" class="form-label">数据源:</label>
<select id="dataSource" class="form-select">
<option value="crypto" selected>加密货币</option>
<option value="a_stock">A股</option>
</select>
</div>
<div class="col-md-2" id="cryptoSymbolContainer">
<label for="symbol" class="form-label">交易对:</label>
<select id="symbol" class="form-select">
{% for symbol in symbols %}
<option value="{{ symbol }}" {% if symbol == default_symbol %}selected{% endif %}>{{ symbol }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2" id="astockSymbolContainer" style="display:none;">
<label for="astockSymbol" class="form-label">A股股票:</label>
<select id="astockSymbol" class="form-select">
{% for stock in a_stock_symbols %}
<option value="{{ stock.symbol }}" {% if stock.symbol == '000001' %}selected{% endif %}>{{ stock.symbol }} - {{ stock.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-1">
<label for="timezone" class="form-label">时区:</label>
<select id="timezone" class="form-select">
<option value="UTC">UTC</option>
<option value="Asia/Shanghai" selected>Asia/Shanghai (UTC+8)</option>
<option value="America/New_York">America/New_York (UTC-4/5)</option>
<option value="Europe/London">Europe/London (UTC+0/1)</option>
<option value="Europe/Berlin">Europe/Berlin (UTC+1/2)</option>
<option value="Asia/Tokyo">Asia/Tokyo (UTC+9)</option>
</select>
</div>
<div class="col-md-2">
<label for="start_time" class="form-label">开始时间:</label>
<input type="datetime-local" id="start_time" class="form-control">
</div>
<div class="col-md-2">
<label for="end_time" class="form-label">结束时间:</label>
<input type="datetime-local" id="end_time" class="form-control">
</div>
<div class="col-md-1">
<button class="btn btn-primary w-100" onclick="analyzeChart()" style="padding: 8px 6px; font-size: 14px;" title="按开始/结束时间拉取并分析(与自动刷新无关)">
分析
</button>
</div>
</div>
<div class="row mt-3">
<div class="col-md-16">
<div class="d-flex align-items-center">
<label class="form-label me-3 mb-0">基础显示:</label>
<div class="form-check form-check-inline">
<select id="klineType" class="form-select form-select-sm me-2" style="width: 120px;">
<option value="candlestick" selected>蜡烛线</option>
<option value="bar">柱条</option>
<option value="line">折线</option>
<option value="area">面积</option>
<option value="baseline">基线</option>
<option value="renko">砖型</option>
<option value="heikin">平均K</option>
<option value="klc">KLC</option>
</select>
<!-- 为兼容现有逻辑,保留隐藏的原始K线开关,与下拉同步 -->
<input class="form-check-input" type="checkbox" id="showOriginalKline" checked style="display:none;">
</div>
<!-- 添加K线周期切换 -->
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="klinePeriod" id="mainPeriodKline" checked>
<label class="form-check-label" for="mainPeriodKline">主周期</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="klinePeriod" id="elementPeriodKline">
<label class="form-check-label" for="elementPeriodKline">小周期</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="klinePeriod" id="subSubPeriodKline">
<label class="form-check-label" for="subSubPeriodKline">次次周期</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMacd" checked>
<label class="form-check-label" for="showMacd">ChanMACD</label>
<button class="btn btn-sm btn-outline-secondary ms-1 p-0" onclick="showMacdConfig()" style="width:22px;height:22px;line-height:1;font-size:12px;" title="MACD参数设置"></button>
</div>
<div class="d-flex align-items-center mb-2">
<label for="refreshInterval" class="form-label me-2 mb-0">自动刷新:</label>
<select id="refreshInterval" class="form-select form-select-sm me-2" style="width: 80px;">
<option value="0.0833" selected>5秒</option>
<option value="0.1667">10秒</option>
<option value="0.25">15秒</option>
<option value="0.5">30秒</option>
<option value="1">1分钟</option>
<option value="2">2分钟</option>
<option value="3">3分钟</option>
<option value="5">5分钟</option>
<option value="10">10分钟</option>
</select>
<div class="form-check form-check-inline me-2">
<input class="form-check-input" type="checkbox" id="autoRefresh">
<label class="form-check-label" for="autoRefresh">启用</label>
</div>
<div class="form-check form-check-inline me-2">
<input class="form-check-input" type="checkbox" id="showMainStructureZone">
<label class="form-check-label" for="showMainStructureZone">结构区</label>
</div>
<input type="number" id="zoneKlLines" class="form-control form-control-sm" value="1000" min="100" max="5000" step="100" style="width:80px;" title="结构区K线数量">
<span id="nextRefreshTime" class="text-muted" style="display:none;font-size:0.85rem;"></span>
<div id="refreshLoadingSpinner" class="loading-spinner ms-2" style="display:none;"></div>
</div>
</div>
<div class="d-flex align-items-center mt-1">
<label class="form-label me-0 mb-0">主周期:</label>
<div class="form-check form-check-inline">
<select id="timeframe" class="form-select form-select-sm me-2" style="width: 100px;">
{% for value, label in timeframes.items() %}
<option value="{{ value }}" {% if value == default_main_timeframe %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainBi" checked>
<label class="form-check-label" for="showMainBi"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainSeg">
<label class="form-check-label" for="showMainSeg">线段</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainZs">
<label class="form-check-label" for="showMainZs">SEG中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainBiZs">
<label class="form-check-label" for="showMainBiZs">BI中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showKlcFxType" checked>
<label class="form-check-label" for="showKlcFxType">KLC分型</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainTrend">
<label class="form-check-label" for="showMainTrend">Trend</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="toggleUOnMain">
<label class="form-check-label" for="toggleUOnMain">显示U</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainBsp">
<label class="form-check-label" for="showMainBsp">买卖点</label>
</div>
<div class="form-check form-check-inline ms-2">
<input class="form-check-input" type="checkbox" id="showMainWrRange">
<label class="form-check-label" for="showMainWrRange">区间</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainWrPhases">
<label class="form-check-label" for="showMainWrPhases">阶段</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainWrEvents">
<label class="form-check-label" for="showMainWrEvents">时间</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainWrVP">
<label class="form-check-label" for="showMainWrVP">VP</label>
</div>
</div>
<div class="d-flex align-items-center mt-1">
<label class="form-label me-0 mb-0">次周期:</label>
<div class="form-check form-check-inline">
<select id="elementTimeframe" class="form-select form-select-sm me-2" style="width: 100px;">
{% for value, label in timeframes.items() %}
<option value="{{ value }}" {% if value == default_element_timeframe %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementBi">
<label class="form-check-label" for="showElementBi"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementSeg">
<label class="form-check-label" for="showElementSeg">线段</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementZs">
<label class="form-check-label" for="showElementZs">SEG中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementBiZs">
<label class="form-check-label" for="showElementBiZs">BI中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementKlcFxType">
<label class="form-check-label" for="showElementKlcFxType">KLC分型</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementTrend">
<label class="form-check-label" for="showElementTrend">Trend</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="toggleUOnElement">
<label class="form-check-label" for="toggleUOnElement">显示U</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementBsp">
<label class="form-check-label" for="showElementBsp">买卖点</label>
</div>
<div class="form-check form-check-inline ms-2">
<input class="form-check-input" type="checkbox" id="showElementWrRange">
<label class="form-check-label" for="showElementWrRange">区间</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementWrPhases">
<label class="form-check-label" for="showElementWrPhases">阶段</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementWrEvents">
<label class="form-check-label" for="showElementWrEvents">时间</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementWrVP">
<label class="form-check-label" for="showElementWrVP">VP</label>
</div>
</div>
<div class="d-flex align-items-center mt-1">
<label class="form-label me-0 mb-0">次次周期:</label>
<div class="form-check form-check-inline">
<select id="subSubTimeframe" class="form-select form-select-sm me-2" style="width: 100px;">
{% for value, label in timeframes.items() %}
<option value="{{ value }}" {% if value == default_sub_sub_timeframe %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubBi">
<label class="form-check-label" for="showSubSubBi"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubSeg">
<label class="form-check-label" for="showSubSubSeg">线段</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubZs">
<label class="form-check-label" for="showSubSubZs">SEG中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubBiZs">
<label class="form-check-label" for="showSubSubBiZs">BI中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubKlcFxType">
<label class="form-check-label" for="showSubSubKlcFxType">KLC分型</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubTrend">
<label class="form-check-label" for="showSubSubTrend">Trend</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="toggleUOnSubSub">
<label class="form-check-label" for="toggleUOnSubSub">显示U</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubBsp">
<label class="form-check-label" for="showSubSubBsp">买卖点</label>
</div>
<div class="form-check form-check-inline ms-2">
<input class="form-check-input" type="checkbox" id="showSubSubWrRange">
<label class="form-check-label" for="showSubSubWrRange">区间</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubWrPhases">
<label class="form-check-label" for="showSubSubWrPhases">阶段</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubWrEvents">
<label class="form-check-label" for="showSubSubWrEvents">时间</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showSubSubWrVP">
<label class="form-check-label" for="showSubSubWrVP">VP</label>
</div>
</div>
</div>
</div>
</div>
<div id="loadingIndicator" style="display:none;"></div>
<div class="chart-container">
<div id="tradingview_chart"></div>
<div id="wyckoffCycleSummary" class="wyckoff-cycle-summary" aria-live="polite"></div>
<!-- 技术指标下拉菜单 -->
<div class="indicator-dropdown dropdown">
<button class="add-indicator-btn dropdown-toggle" type="button" id="indicatorDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-plus-lg"></i> 添加指标
</button>
<ul class="dropdown-menu" aria-labelledby="indicatorDropdown">
<li><a class="dropdown-item" href="#" onclick="showMAConfig(); return false;">
<i class="bi bi-graph-up"></i> 添加均线
</a></li>
<li><a class="dropdown-item" href="#" onclick="showBBConfig(); return false;">
<i class="bi bi-arrows-expand"></i> 添加布林带
</a></li>
</ul>
</div>
<!-- 技术指标信息面板 -->
<div id="indicatorPanel" class="indicator-panel" style="display: none;"></div>
</div>
<div class="data-container">
<ul class="nav nav-tabs" id="dataTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="kline-tab" data-bs-toggle="tab" data-bs-target="#kline" type="button" role="tab">K线数据</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="bi-tab" data-bs-toggle="tab" data-bs-target="#bi" type="button" role="tab"></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="seg-tab" data-bs-toggle="tab" data-bs-target="#seg" type="button" role="tab">线段</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="zs-tab" data-bs-toggle="tab" data-bs-target="#zs" type="button" role="tab">中枢</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="macd-tab" data-bs-toggle="tab" data-bs-target="#macd" type="button" role="tab">MACD</button>
</li>
<li class="nav-item" role="presentation">
</li>
</ul>
<div class="data-source-info alert alert-info py-2 mt-1 mb-2" style="display:none;">
<small id="dataSourceText"></small>
</div>
<div class="tab-content" id="dataTabsContent">
<div class="tab-pane fade show active" id="kline" role="tabpanel">
<div class="table-container">
<table id="klineTable" class="display compact" style="width:100%">
<thead>
<tr>
<th>时间</th>
<th>开盘价</th>
<th>最高价</th>
<th>最低价</th>
<th>收盘价</th>
<th>成交量</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="tab-pane fade" id="bi" role="tabpanel">
<div class="table-container">
<table id="biTable" class="display compact" style="width:100%">
<thead>
<tr>
<th>起始时间</th>
<th>结束时间</th>
<th>确认时间</th>
<th>起始价格</th>
<th>结束价格</th>
<th>方向</th>
<th>MACD背离值</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="tab-pane fade" id="seg" role="tabpanel">
<div class="table-container">
<table id="segTable" class="display compact" style="width:100%">
<thead>
<tr>
<th>起始时间</th>
<th>结束时间</th>
<th>确认时间</th>
<th>起始价格</th>
<th>结束价格</th>
<th>方向</th>
<th>状态</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="tab-pane fade" id="zs" role="tabpanel">
<div class="table-container">
<h5>已完成中枢</h5>
<table id="zsTable" class="display compact" style="width:100%">
<thead>
<tr>
<th>起始时间</th>
<th>结束时间</th>
<th>中枢上沿(ZG)</th>
<th>中枢下沿(ZD)</th>
</tr>
</thead>
<tbody></tbody>
</table>
<h5 class="mt-4">未完成中枢</h5>
<table id="uncompletedZsTable" class="display compact" style="width:100%">
<thead>
<tr>
<th>起始时间</th>
<th>中枢上沿(ZG)</th>
<th>中枢下沿(ZD)</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="tab-pane fade" id="macd" role="tabpanel">
<div class="table-container">
<table id="macdTable" class="display compact" style="width:100%">
<thead>
<tr>
<th>时间</th>
<th>收盘价</th>
<th>MACD</th>
<th>信号线</th>
<th>直方图</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script defer src="{{ url_for('static', filename='js/app/api_client.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/state.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/trend.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/macd_ui.js') }}?v=20260809t"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_format.js') }}?v=20260810a"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_view.js') }}?v=20260810a"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv_lifecycle.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv_shell.js') }}?v=20260809j"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv_indicators.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv_chan.js') }}?v=20260810d"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv_overlays.js') }}?v=20260810c"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv_finalize.js') }}?v=20260810a"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tv.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_sync.js') }}?v=20260809z"></script>
<script defer src="{{ url_for('static', filename='js/app/chart_tables.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/ui.js') }}?v=20260810a"></script>
<script defer src="{{ url_for('static', filename='js/app/overlays.js') }}?v=20260808i"></script>
<script defer src="{{ url_for('static', filename='js/app/main.js') }}?v=20260808i"></script>
<!-- 均线配置弹窗 -->
<div id="maConfigModal" class="ma-config-modal">
<div class="ma-config-content">
<div class="ma-config-title">添加均线</div>
<div class="ma-config-form">
<div class="ma-config-group">
<label class="ma-config-label">类型</label>
<select id="maType" class="ma-config-select">
<option value="SMA">SMA (简单移动平均)</option>
<option value="EMA">EMA (指数移动平均)</option>
<option value="WMA">WMA (加权移动平均)</option>
</select>
</div>
<div class="ma-config-group">
<label class="ma-config-label">长度</label>
<input type="number" id="maLength" class="ma-config-input" value="20" min="1" max="200">
</div>
<div class="ma-config-group">
<label class="ma-config-label">数据来源</label>
<select id="maSource" class="ma-config-select">
<option value="close">收盘价</option>
<option value="open">开盘价</option>
<option value="high">最高价</option>
<option value="low">最低价</option>
<option value="hl2">最高最低平均价 (HL2)</option>
<option value="hlc3">典型价 (HLC3)</option>
<option value="ohlc4">平均价 (OHLC4)</option>
</select>
</div>
<div class="ma-config-group">
<label class="ma-config-label">平滑线类型</label>
<select id="maSmoothType" class="ma-config-select">
<option value="none">无平滑</option>
<option value="SMA">SMA</option>
<option value="EMA">EMA</option>
<option value="WMA">WMA</option>
</select>
</div>
<div class="ma-config-group">
<label class="ma-config-label">平滑长度</label>
<input type="number" id="maSmoothLength" class="ma-config-input" value="3" min="1" max="50" disabled>
</div>
<div class="ma-config-group">
<label class="ma-config-label">线条粗细</label>
<select id="maLineWidth" class="ma-config-select">
<option value="1">细线 (1px)</option>
<option value="2" selected>普通 (2px)</option>
<option value="3">粗线 (3px)</option>
<option value="4">很粗 (4px)</option>
<option value="5">超粗 (5px)</option>
</select>
</div>
<div class="ma-config-group">
<label class="ma-config-label">线条样式</label>
<select id="maLineStyle" class="ma-config-select">
<option value="0">实线</option>
<option value="1">点线</option>
<option value="2">虚线</option>
<option value="3">大虚线</option>
</select>
</div>
<div class="ma-config-group">
<label class="ma-config-label">颜色</label>
<input type="color" id="maColor" class="ma-config-input" value="#2962FF">
</div>
<div class="ma-config-group">
<label class="ma-config-label">预览</label>
<div id="maLinePreview" class="ma-line-preview">
<svg id="previewSvg">
<line id="previewLine" x1="10%" y1="50%" x2="90%" y2="50%" stroke="#2962FF" stroke-width="2"/>
</svg>
</div>
</div>
</div>
<div class="ma-config-actions">
<button class="ma-config-btn ma-config-btn-secondary" onclick="hideMAConfig()">取消</button>
<button class="ma-config-btn ma-config-btn-primary" id="maConfigSubmitBtn">添加</button>
</div>
</div>
</div>
<!-- 布林带配置弹窗 -->
<div id="bbConfigModal" class="bb-config-modal">
<div class="bb-config-content">
<div class="bb-config-title">添加布林带</div>
<div class="bb-config-form">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="bb-config-group">
<label class="bb-config-label">类型</label>
<select id="bbType" class="bb-config-select">
<option value="Bollinger Bands">BB</option>
</select>
</div>
<div class="bb-config-group">
<label class="bb-config-label">长度</label>
<input type="number" id="bbLength" class="bb-config-input" value="20" min="1" max="200">
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="bb-config-group">
<label class="bb-config-label">上轨倍数</label>
<input type="number" id="bbUpperMultiplier" class="bb-config-input" value="2" min="0.1" max="10" step="0.1">
</div>
<div class="bb-config-group">
<label class="bb-config-label">下轨倍数</label>
<input type="number" id="bbLowerMultiplier" class="bb-config-input" value="2" min="0.1" max="10" step="0.1">
</div>
</div>
<div class="bb-config-group">
<label class="bb-config-label">数据来源</label>
<select id="bbSource" class="bb-config-select">
<option value="close">收盘价</option>
<option value="open">开盘价</option>
<option value="high">最高价</option>
<option value="low">最低价</option>
<option value="hl2">最高最低平均价 (HL2)</option>
<option value="hlc3">典型价 (HLC3)</option>
<option value="ohlc4">平均价 (OHLC4)</option>
</select>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="bb-config-group">
<label class="bb-config-label">线条粗细</label>
<select id="bbLineWidth" class="bb-config-select">
<option value="1">细线 (1px)</option>
<option value="2" selected>普通 (2px)</option>
<option value="3">粗线 (3px)</option>
</select>
</div>
<div class="bb-config-group">
<label class="bb-config-label">线条样式</label>
<select id="bbLineStyle" class="bb-config-select">
<option value="0">实线</option>
<option value="1">点线</option>
<option value="2">虚线</option>
<option value="3">大虚线</option>
</select>
</div>
</div>
<div class="bb-config-group">
<label class="bb-config-label">颜色配置</label>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;">
<div>
<label style="font-size: 11px; color: #666;">上轨</label>
<input type="color" id="bbUpperColor" class="bb-config-input" value="#ff6b6b" style="height: 30px;">
</div>
<div>
<label style="font-size: 11px; color: #666;">中轨</label>
<input type="color" id="bbMiddleColor" class="bb-config-input" value="#667eea" style="height: 30px;">
</div>
<div>
<label style="font-size: 11px; color: #666;">下轨</label>
<input type="color" id="bbLowerColor" class="bb-config-input" value="#4ecdc4" style="height: 30px;">
</div>
</div>
</div>
<div class="bb-config-group">
<label class="bb-config-label">预览</label>
<div id="bbLinePreview" class="bb-line-preview">
<svg width="100%" height="30">
<line id="bbPreviewUpper" x1="10%" y1="25%" x2="90%" y2="25%" stroke="#ff6b6b" stroke-width="2"/>
<line id="bbPreviewMiddle" x1="10%" y1="50%" x2="90%" y2="50%" stroke="#667eea" stroke-width="2"/>
<line id="bbPreviewLower" x1="10%" y1="75%" x2="90%" y2="75%" stroke="#4ecdc4" stroke-width="2"/>
</svg>
</div>
</div>
</div>
<div class="bb-config-actions">
<button class="bb-config-btn bb-config-btn-secondary" onclick="hideBBConfig()">取消</button>
<button class="bb-config-btn bb-config-btn-primary" id="bbConfigSubmitBtn">添加</button>
</div>
</div>
</div>
<!-- MACD参数配置弹窗 -->
<div id="macdConfigModal" class="macd-config-modal">
<div class="macd-config-content">
<div class="macd-config-title">ChanMACD 参数设置</div>
<div class="macd-config-form">
<div class="macd-config-group">
<label class="macd-config-label">快线周期 (Fast)</label>
<input type="number" id="macdFastPeriod" class="macd-config-input" value="13" min="2" max="200">
<div class="macd-config-hint">默认: 13</div>
</div>
<div class="macd-config-group">
<label class="macd-config-label">慢线周期 (Slow)</label>
<input type="number" id="macdSlowPeriod" class="macd-config-input" value="26" min="2" max="500">
<div class="macd-config-hint">默认: 26</div>
</div>
<div class="macd-config-group">
<label class="macd-config-label">信号线周期 (Signal)</label>
<input type="number" id="macdSignalPeriod" class="macd-config-input" value="9" min="2" max="200">
<div class="macd-config-hint">默认: 9</div>
</div>
</div>
<div class="macd-config-actions">
<button class="macd-config-btn macd-config-btn-secondary" onclick="hideMacdConfig()">取消</button>
<button class="macd-config-btn macd-config-btn-secondary" onclick="resetMacdConfig()">恢复默认</button>
<button class="macd-config-btn macd-config-btn-primary" onclick="saveMacdConfig()">应用</button>
</div>
</div>
</div>
</body>
</html>