修改排版,加入up,down,flat,unknown显示
This commit is contained in:
+46
-5
@@ -352,12 +352,13 @@ def analyze_chan(df, symbol=None, timeframe=None):
|
||||
|
||||
# 初始化多时间周期数据以获取EMA52
|
||||
ema52_dict = None
|
||||
if symbol and timeframe:
|
||||
# 先暂时不用这个功能,太慢了
|
||||
if symbol and timeframe and False:
|
||||
try:
|
||||
# 获取不同时间周期的数据用于初始化
|
||||
df_1h = get_kl_data(symbol, '1h', limit=800) if timeframe != '1h' else df
|
||||
df_1d = get_kl_data(symbol, '1d', limit=800) if timeframe != '1d' else df
|
||||
df_1M = get_kl_data(symbol, '1M', limit=800) if timeframe != '1M' else df
|
||||
df_1h = get_kl_data(symbol, '1h', limit=1500) if timeframe != '1h' else df
|
||||
df_1d = get_kl_data(symbol, '1d', limit=2000) if timeframe != '1d' else df
|
||||
df_1M = get_kl_data(symbol, '1M', limit=1500) if timeframe != '1M' else df
|
||||
|
||||
# 添加指标
|
||||
if df_1h is not None and len(df_1h) > 0:
|
||||
@@ -1659,6 +1660,24 @@ def analyze():
|
||||
else:
|
||||
replay_data = None
|
||||
|
||||
# 基于已有 KLC 列表生成趋势标记(不做额外计算)
|
||||
klc_trend = []
|
||||
try:
|
||||
for klc in analysis_result.get('klc_list', []):
|
||||
trend_val = getattr(klc, 'trend', None)
|
||||
t_obj = getattr(klc, 'end_time', None) or getattr(klc, 'start_time', None)
|
||||
if trend_val is None or t_obj is None:
|
||||
continue
|
||||
# 统一成字符串:UP/DOWN/FLAT/UNKNOWN
|
||||
trend_name = str(trend_val)
|
||||
if '.' in trend_name:
|
||||
trend_name = trend_name.split('.')[-1]
|
||||
time_str = format_time_safely(t_obj, client_tz)
|
||||
if time_str:
|
||||
klc_trend.append({'time': time_str, 'trend': trend_name})
|
||||
except Exception:
|
||||
klc_trend = []
|
||||
|
||||
# 添加主周期分析结果到返回数据
|
||||
result.update({
|
||||
'kline_data': clean_dataframe_for_json(df).to_dict('records'),
|
||||
@@ -1753,7 +1772,9 @@ def analyze():
|
||||
# 添加ChanMACD分析数据
|
||||
'chan_macd': serialize_chan_macd_data(analysis_result.get('chan_macd', {}), client_tz),
|
||||
# 添加多时间周期EMA52数据
|
||||
'ema52_dict': analysis_result.get('ema52_dict', {})
|
||||
'ema52_dict': analysis_result.get('ema52_dict', {}),
|
||||
# 直接输出KLC趋势标记(使用已有trend字段)
|
||||
'klc_trend': klc_trend
|
||||
})
|
||||
|
||||
# 如果生成了回放数据,添加到返回结果中
|
||||
@@ -1775,6 +1796,23 @@ def analyze():
|
||||
# 计算小周期MACD数据
|
||||
element_macd_data = calculate_macd(element_df)
|
||||
|
||||
# 组装小周期 KLC 趋势(仅提取已有 trend,不做重算)
|
||||
try:
|
||||
element_klc_trend = []
|
||||
for klc in element_analysis.get('klc_list', []):
|
||||
trend_val = getattr(klc, 'trend', None)
|
||||
t_obj = getattr(klc, 'end_time', None) or getattr(klc, 'start_time', None)
|
||||
if trend_val is None or t_obj is None:
|
||||
continue
|
||||
trend_name = str(trend_val)
|
||||
if '.' in trend_name:
|
||||
trend_name = trend_name.split('.')[-1]
|
||||
time_str = format_time_safely(t_obj, client_tz)
|
||||
if time_str:
|
||||
element_klc_trend.append({'time': time_str, 'trend': trend_name})
|
||||
except Exception:
|
||||
element_klc_trend = []
|
||||
|
||||
# 添加小周期分析结果到返回数据
|
||||
result['element_timeframe'] = element_timeframe
|
||||
result['element_macd'] = element_macd_data # 添加小周期MACD数据
|
||||
@@ -1880,6 +1918,9 @@ def analyze():
|
||||
# 添加次周期ChanMACD分析数据
|
||||
result['element_chan_macd'] = serialize_chan_macd_data(element_analysis.get('chan_macd', {}), client_tz)
|
||||
|
||||
# 添加小周期 KLC 趋势标记
|
||||
result['element_klc_trend'] = element_klc_trend
|
||||
|
||||
pass
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
+207
-122
@@ -801,8 +801,8 @@
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-1">
|
||||
<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>
|
||||
@@ -825,14 +825,6 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<label for="timeframe" class="form-label">时间周期:</label>
|
||||
<select id="timeframe" class="form-select">
|
||||
{% for value, label in timeframes.items() %}
|
||||
<option value="{{ value }}" {% if value == '5m' %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<label for="timezone" class="form-label">时区:</label>
|
||||
<select id="timezone" class="form-select">
|
||||
@@ -844,11 +836,11 @@
|
||||
<option value="Asia/Tokyo">Asia/Tokyo (UTC+9)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<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-3">
|
||||
<div class="col-md-2">
|
||||
<label for="end_time" class="form-label">结束时间:</label>
|
||||
<input type="datetime-local" id="end_time" class="form-control">
|
||||
</div>
|
||||
@@ -860,7 +852,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-8">
|
||||
<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">
|
||||
@@ -876,11 +868,41 @@
|
||||
<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>
|
||||
<i class="bi bi-info-circle" data-bs-toggle="tooltip" title="显示小周期K线,同时可以叠加大周期分型和笔段"></i>
|
||||
</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>
|
||||
</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">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" selected>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>
|
||||
<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-2">
|
||||
<label class="form-label me-3 mb-0">主周期:</label>
|
||||
<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 == '5m' %}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>
|
||||
@@ -897,33 +919,28 @@
|
||||
<input class="form-check-input" type="checkbox" id="showMainUncompletedZs">
|
||||
<label class="form-check-label" for="showMainUncompletedZs">未完成中枢</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="showMainMacdDiv">
|
||||
<label class="form-check-label" for="showMainMacdDiv">MACD背离</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="showKluFxType">
|
||||
<label class="form-check-label" for="showKluFxType">KLU分型</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="showMainBollinger">
|
||||
<label class="form-check-label" for="showMainBollinger">布林带</label>
|
||||
<input class="form-check-input" type="checkbox" id="showMainTrend" checked>
|
||||
<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" checked>
|
||||
<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="showMacd" checked>
|
||||
<label class="form-check-label" for="showMacd">ChanMACD</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mt-2">
|
||||
<label class="form-label me-3 mb-0">次周期:</label>
|
||||
<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 == '1m' %}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>
|
||||
@@ -940,21 +957,13 @@
|
||||
<input class="form-check-input" type="checkbox" id="showElementUncompletedZs">
|
||||
<label class="form-check-label" for="showElementUncompletedZs">未完成中枢</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="showElementMacdDiv">
|
||||
<label class="form-check-label" for="showElementMacdDiv">MACD背离</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="showElementKluFxType">
|
||||
<label class="form-check-label" for="showElementKluFxType">KLU分型</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="showElementBollinger">
|
||||
<label class="form-check-label" for="showElementBollinger">布林带</label>
|
||||
<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">
|
||||
@@ -962,64 +971,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<label for="elementTimeframe" class="form-label me-2 mb-0">次级别时间周期:</label>
|
||||
<select id="elementTimeframe" class="form-select form-select-sm me-2" style="width: 120px;">
|
||||
{% for value, label in timeframes.items() %}
|
||||
<option value="{{ value }}" {% if value == '1m' %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</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">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" selected>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>
|
||||
<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 class="d-flex align-items-center">
|
||||
<label for="replayInterval" class="form-label me-2 mb-0">数据回放:</label>
|
||||
<select id="replayInterval" class="form-select form-select-sm me-2" style="width: 80px;">
|
||||
<option value="0.5">0.5秒</option>
|
||||
<option value="1" selected>1秒</option>
|
||||
<option value="2">2秒</option>
|
||||
<option value="3">3秒</option>
|
||||
<option value="5">5秒</option>
|
||||
</select>
|
||||
<div class="btn-group me-2">
|
||||
<button id="startReplay" class="btn btn-sm btn-success">
|
||||
<i class="bi bi-play-fill"></i> 开始回放
|
||||
</button>
|
||||
<button id="pauseReplay" class="btn btn-sm btn-warning" style="display:none;">
|
||||
<i class="bi bi-pause-fill"></i> 暂停
|
||||
</button>
|
||||
<button id="stopReplay" class="btn btn-sm btn-danger" style="display:none;">
|
||||
<i class="bi bi-stop-fill"></i> 停止
|
||||
</button>
|
||||
</div>
|
||||
<span id="replayStatus" class="text-muted" style="font-size:0.85rem;"></span>
|
||||
<div id="replayProgress" class="progress ms-2" style="width: 80px; height: 8px; display: none;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2025,19 +1976,9 @@
|
||||
refreshChart(currentData);
|
||||
});
|
||||
|
||||
// 添加分型类型复选框变更事件
|
||||
$('#showKlcFxType').change(function() {
|
||||
refreshChartOnly();
|
||||
});
|
||||
|
||||
// 添加布林带显示变更事件
|
||||
$('#showMainBollinger').change(function() {
|
||||
updateChartDisplay();
|
||||
});
|
||||
|
||||
$('#showElementBollinger').change(function() {
|
||||
updateChartDisplay();
|
||||
});
|
||||
// Trend 显示开关
|
||||
$('#showMainTrend').change(function() { refreshChartOnly(); });
|
||||
$('#showElementTrend').change(function() { refreshChartOnly(); });
|
||||
|
||||
// 添加K线周期切换事件监听器
|
||||
$('input[name="klinePeriod"]').change(function() {
|
||||
@@ -3029,8 +2970,8 @@
|
||||
// 添加ChanMACD分析标注
|
||||
// 根据主/次周期开关与各自的“显示U”独立控制
|
||||
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
||||
const allowU = useElementPeriod ? (typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement)
|
||||
: (typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain);
|
||||
// 默认不显示,必须用户勾选对应复选框
|
||||
const allowU = useElementPeriod ? !!window.showUOnElement : !!window.showUOnMain;
|
||||
if (cm && allowU) {
|
||||
console.log('添加ChanMACD分析标注:', {
|
||||
segListLength: cm.seg_list ? cm.seg_list.length : 0,
|
||||
@@ -3088,7 +3029,7 @@
|
||||
);
|
||||
|
||||
// 主周期 U 标记(蓝/橙,与原样式一致)
|
||||
if ((typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain) && Array.isArray(mainCm.klu_list)) {
|
||||
if (window.showUOnMain && Array.isArray(mainCm.klu_list)) {
|
||||
mainCm.klu_list.forEach((item) => {
|
||||
if (!item || !item.time) return;
|
||||
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
||||
@@ -3110,7 +3051,7 @@
|
||||
}
|
||||
|
||||
// 次周期 U 标记(使用不同配色以区分)
|
||||
if ((typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement) && Array.isArray(elementCm.klu_list)) {
|
||||
if (window.showUOnElement && Array.isArray(elementCm.klu_list)) {
|
||||
elementCm.klu_list.forEach((item) => {
|
||||
if (!item || !item.time) return;
|
||||
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
||||
@@ -5488,12 +5429,86 @@
|
||||
window.fxMarkers = elementFxMarkers;
|
||||
}
|
||||
|
||||
// 合并主周期、小周期分型与 UnitTF 标记,统一设置到K线数据系列
|
||||
// 基于后端提供的 KLC 趋势生成标记(不进行任何计算)
|
||||
let klcTrendMarkers = [];
|
||||
try {
|
||||
if (currentData.klc_trend && currentData.klc_trend.length > 0) {
|
||||
console.log('KLC趋势点数量:', currentData.klc_trend.length, currentData.klc_trend.slice(0, 3));
|
||||
// 当前图表的bar时间集合(秒)用于对齐标记到最近的K线
|
||||
const seriesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||
const nearestTime = (target) => {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||
// 简单线性查找(数据量通常可接受),必要时可替换为二分
|
||||
let best = candles[0].time;
|
||||
let bestDiff = Math.abs(best - target);
|
||||
for (let i = 1; i < candles.length; i++) {
|
||||
const t = candles[i].time;
|
||||
const d = Math.abs(t - target);
|
||||
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||
}
|
||||
return best;
|
||||
};
|
||||
|
||||
klcTrendMarkers = currentData.klc_trend.map(t => {
|
||||
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||
let timeAligned = seriesTimes.has(ts) ? ts : nearestTime(ts);
|
||||
let marker = { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'square', size: 0.8 };
|
||||
if (trendRaw === 'UP') {
|
||||
marker = { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||
} else if (trendRaw === 'DOWN') {
|
||||
marker = { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||
} else if (trendRaw === 'FLAT') {
|
||||
marker = { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||
} else {
|
||||
// UNKNOWN 或其他
|
||||
marker = { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||
}
|
||||
return marker;
|
||||
});
|
||||
console.log('KLC趋势标记(对齐后)示例:', klcTrendMarkers.slice(0, 5));
|
||||
}
|
||||
} catch (e) {
|
||||
klcTrendMarkers = [];
|
||||
}
|
||||
// 暴露到全局以便调试或后续合并
|
||||
window.klcTrendMarkers = klcTrendMarkers;
|
||||
|
||||
// 无论当前显示主/小周期,只要勾选对应Trend,就叠加出来
|
||||
let trendMarkersToUse = [];
|
||||
if ($('#showMainTrend').is(':checked')) {
|
||||
trendMarkersToUse = trendMarkersToUse.concat(window.klcTrendMarkers || []);
|
||||
}
|
||||
if ($('#showElementTrend').is(':checked') && currentData.element_klc_trend) {
|
||||
const candlesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||
const nearestTime = (target) => {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||
let best = candles[0].time, bestDiff = Math.abs(best - target);
|
||||
for (let i = 1; i < candles.length; i++) {
|
||||
const t = candles[i].time, d = Math.abs(t - target);
|
||||
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||
}
|
||||
return best;
|
||||
};
|
||||
const elementMarkers = currentData.element_klc_trend.map(t => {
|
||||
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||
const timeAligned = candlesTimes.has(ts) ? ts : nearestTime(ts);
|
||||
if (trendRaw === 'UP') return { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||
if (trendRaw === 'DOWN') return { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||
if (trendRaw === 'FLAT') return { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||
return { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||
});
|
||||
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
||||
}
|
||||
|
||||
// 合并标记并设置
|
||||
const combinedMarkers = [
|
||||
...(window.mainFxMarkers || []),
|
||||
...allElementFxMarkers,
|
||||
...(window.kluDivMarkersMain || []),
|
||||
...(window.kluDivMarkersElement || [])
|
||||
...(window.kluDivMarkersElement || []),
|
||||
...trendMarkersToUse
|
||||
];
|
||||
if (combinedMarkers.length > 0) {
|
||||
console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)');
|
||||
@@ -5513,11 +5528,76 @@
|
||||
} else {
|
||||
console.log('绘制小周期分型标记 - 已禁用或无数据');
|
||||
|
||||
// 只设置主周期分型 + UnitTF 标记
|
||||
// 计算并缓存KLC趋势标记(即使未启用小周期分型,也应显示趋势)
|
||||
try {
|
||||
let klcTrendMarkers = [];
|
||||
if (currentData.klc_trend && currentData.klc_trend.length > 0) {
|
||||
console.log('KLC趋势点数量:', currentData.klc_trend.length, currentData.klc_trend.slice(0, 3));
|
||||
const seriesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||
const nearestTime = (target) => {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||
let best = candles[0].time;
|
||||
let bestDiff = Math.abs(best - target);
|
||||
for (let i = 1; i < candles.length; i++) {
|
||||
const t = candles[i].time;
|
||||
const d = Math.abs(t - target);
|
||||
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||
}
|
||||
return best;
|
||||
};
|
||||
klcTrendMarkers = currentData.klc_trend.map(t => {
|
||||
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||
const timeAligned = seriesTimes.has(ts) ? ts : nearestTime(ts);
|
||||
if (trendRaw === 'UP') {
|
||||
return { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||
} else if (trendRaw === 'DOWN') {
|
||||
return { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||
} else if (trendRaw === 'FLAT') {
|
||||
return { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||
} else {
|
||||
return { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||
}
|
||||
});
|
||||
console.log('KLC趋势标记(对齐后)示例:', klcTrendMarkers.slice(0, 5));
|
||||
}
|
||||
window.klcTrendMarkers = klcTrendMarkers;
|
||||
} catch (e) {
|
||||
window.klcTrendMarkers = [];
|
||||
}
|
||||
|
||||
// 与上方一致:勾选哪个Trend就显示哪个
|
||||
let trendMarkersToUse = [];
|
||||
if ($('#showMainTrend').is(':checked')) {
|
||||
trendMarkersToUse = trendMarkersToUse.concat(window.klcTrendMarkers || []);
|
||||
}
|
||||
if ($('#showElementTrend').is(':checked') && currentData.element_klc_trend) {
|
||||
const candlesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||
const nearestTime = (target) => {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||
let best = candles[0].time, bestDiff = Math.abs(best - target);
|
||||
for (let i = 1; i < candles.length; i++) {
|
||||
const t = candles[i].time, d = Math.abs(t - target);
|
||||
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||
}
|
||||
return best;
|
||||
};
|
||||
const elementMarkers = currentData.element_klc_trend.map(t => {
|
||||
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||
const timeAligned = candlesTimes.has(ts) ? ts : nearestTime(ts);
|
||||
if (trendRaw === 'UP') return { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||
if (trendRaw === 'DOWN') return { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||
if (trendRaw === 'FLAT') return { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||
return { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||
});
|
||||
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
||||
}
|
||||
const onlyMainAndU = [
|
||||
...(window.mainFxMarkers || []),
|
||||
...(window.kluDivMarkersMain || []),
|
||||
...(window.kluDivMarkersElement || [])
|
||||
...(window.kluDivMarkersElement || []),
|
||||
...trendMarkersToUse
|
||||
];
|
||||
if (onlyMainAndU.length > 0) {
|
||||
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')');
|
||||
@@ -5675,6 +5755,9 @@
|
||||
}
|
||||
|
||||
// 添加买卖点提示
|
||||
// 初始化 tooltip 与 U 显示状态
|
||||
window.showUOnMain = $('#toggleUOnMain').is(':checked');
|
||||
window.showUOnElement = $('#toggleUOnElement').is(':checked');
|
||||
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
|
||||
|
||||
// 显示买卖点
|
||||
@@ -5895,7 +5978,8 @@
|
||||
try {
|
||||
if (typeof clearChanMacdMarkers === 'function') clearChanMacdMarkers();
|
||||
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
||||
if (cm) {
|
||||
const allowU = useElementPeriod ? !!window.showUOnElement : !!window.showUOnMain;
|
||||
if (cm && allowU) {
|
||||
addAllChanMacdMarkers(
|
||||
cm.seg_list || [],
|
||||
cm.unittf_list || [],
|
||||
@@ -10279,9 +10363,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 保存到全局,供主图与分型一起统一合并绘制
|
||||
// 保存到全局,供主图与分型一起统一合并绘制(仅在开关开启时)
|
||||
console.log('DEBUG: U 标记数量:', signalMarkers.length);
|
||||
window.unittfMarkers = [...signalMarkers, ...boundaryMarkers];
|
||||
const allowUMerge = (window.showUOnMain && window.showUOnElement);
|
||||
window.unittfMarkers = allowUMerge ? [...signalMarkers, ...boundaryMarkers] : [];
|
||||
if (uTooltipMarkers.length > 0) {
|
||||
if (window.fxMarkers) {
|
||||
window.fxMarkers = [ ...window.fxMarkers, ...uTooltipMarkers ];
|
||||
|
||||
Reference in New Issue
Block a user