添加裸K形态,前端添加KLC显示

This commit is contained in:
jackyu66git
2025-10-29 01:16:50 +08:00
parent abe663e1a6
commit fe4193b153
8 changed files with 589 additions and 114 deletions
+26
View File
@@ -1167,6 +1167,18 @@ def analyze():
# 添加主周期分析结果到返回数据
result.update({
'kline_data': clean_dataframe_for_json(df).to_dict('records'),
'klc_list': [{
'date': klc.end_time if isinstance(klc.end_time, str) else klc.end_time.astimezone(client_tz).isoformat(),
'open': float(klc.open),
'high': float(klc.high),
'low': float(klc.low),
'close': float(klc.close),
'volume': float(klc.volume) if hasattr(klc, 'volume') else 0,
'direction': str(klc.dir).replace('Chan_KLINE_DIR.', ''),
'fx_type': str(klc.fx).replace('Chan_FX_TYPE.', ''),
'klc_fx_type': str(klc.klc_fx_type).replace('Chan_KLC_FX.', ''),
'trend': str(klc.trend).replace('Chan_PRICE_TREND.', '')
} for klc in analysis_result['klc_list'] if hasattr(klc, 'end_time') and klc.end_time],
'bi_list': [{
'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None,
@@ -1354,6 +1366,20 @@ def analyze():
# 添加小周期K线数据
result['element_kline_data'] = clean_dataframe_for_json(element_df).to_dict('records')
# 添加小周期KLC列表
result['element_klc_list'] = [{
'date': klc.end_time if isinstance(klc.end_time, str) else klc.end_time.astimezone(client_tz).isoformat(),
'open': float(klc.open),
'high': float(klc.high),
'low': float(klc.low),
'close': float(klc.close),
'volume': float(klc.volume) if hasattr(klc, 'volume') else 0,
'direction': str(klc.dir).replace('Chan_KLINE_DIR.', ''),
'fx_type': str(klc.fx).replace('Chan_FX_TYPE.', ''),
'klc_fx_type': str(klc.klc_fx_type).replace('Chan_KLC_FX.', ''),
'trend': str(klc.trend).replace('Chan_PRICE_TREND.', '')
} for klc in element_analysis['klc_list'] if hasattr(klc, 'end_time') and klc.end_time]
result['element_seg_list'] = [{
'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None,
+130 -94
View File
@@ -648,7 +648,6 @@
align-items: center;
z-index: 1000;
}
.bb-config-content {
background: white;
border-radius: 8px;
@@ -817,6 +816,7 @@
<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;">
@@ -2548,6 +2548,19 @@
const baseData = candles.map(c => ({ time: c.time, value: c.close }));
series.setData(baseData);
tvWidget.series.baselineSeries = series;
} else if (klineType === 'klc') {
// KLC显示模式 - 使用蜡烛线显示KLC数据
const series = mainChart.addCandlestickSeries({
upColor: '#dc3545',
downColor: '#28a745',
borderVisible: false,
wickUpColor: '#dc3545',
wickDownColor: '#28a745',
});
// 使用KLC数据创建蜡烛图
const klcCandles = buildKLCFromAnalysis(currentData);
series.setData(klcCandles);
tvWidget.series.klcSeries = series;
}
})();
@@ -2717,7 +2730,6 @@
tvWidget.series.signalLineSeries = signalLineSeries;
tvWidget.series.histogramSeries = histogramSeries;
}
// 添加ChanMACD图表
console.log('ChanMACD图表创建条件检查:', {
showMacd: showMacd,
@@ -2733,7 +2745,6 @@
if (typeof window.showUOnElement === 'undefined') {
window.showUOnElement = $('#toggleUOnElement').is(':checked');
}
if (showMacd && chanMacdChart && ((useElementPeriod && currentData.element_macd) || currentData.macd) && (useElementPeriod ? currentData.element_kline_data : currentData.kline_data)) {
console.log('✅ 开始创建 ChanMACD 系列');
// 创建ChanMACD线系列
@@ -4338,7 +4349,6 @@
});
}
}
// 主周期未完成中枢
if ($('#showMainZs').is(':checked') && currentData.uncompleted_zs_list && currentData.uncompleted_zs_list.length > 0) {
console.log(`绘制主周期未完成中枢数据,共${currentData.uncompleted_zs_list.length}条`);
@@ -5305,7 +5315,6 @@
// 收集所有主周期分型标记
const allMainFxMarkers = [];
const mainFxMarkers = []; // 用于tooltip支持
// 处理主周期KLC分型
if ($('#showKlcFxType').is(':checked') && currentData.klc_fx_info && currentData.klc_fx_info.length > 0) {
console.log(`绘制主周期K线合并分型标签,共${currentData.klc_fx_info.length}条`);
@@ -5671,6 +5680,7 @@
else if (klineType === 'line') targetSeries = tvWidget.series.lineSeries;
else if (klineType === 'area') targetSeries = tvWidget.series.areaSeries;
else if (klineType === 'baseline') targetSeries = tvWidget.series.baselineSeries;
else if (klineType === 'klc') targetSeries = tvWidget.series.klcSeries;
if (targetSeries) {
targetSeries.setMarkers(combinedMarkers);
} else {
@@ -5765,6 +5775,7 @@
else if (klineType2 === 'line') targetSeries2 = tvWidget.series.lineSeries;
else if (klineType2 === 'area') targetSeries2 = tvWidget.series.areaSeries;
else if (klineType2 === 'baseline') targetSeries2 = tvWidget.series.baselineSeries;
else if (klineType2 === 'klc') targetSeries2 = tvWidget.series.klcSeries;
if (targetSeries2) {
targetSeries2.setMarkers(onlyMainAndU);
} else {
@@ -5782,6 +5793,7 @@
else if (klineType3 === 'line') targetSeries3 = tvWidget.series.lineSeries;
else if (klineType3 === 'area') targetSeries3 = tvWidget.series.areaSeries;
else if (klineType3 === 'baseline') targetSeries3 = tvWidget.series.baselineSeries;
else if (klineType3 === 'klc') targetSeries3 = tvWidget.series.klcSeries;
if (targetSeries3) {
targetSeries3.setMarkers([]);
}
@@ -6024,6 +6036,9 @@
} else if (klineType === 'baseline' && tvWidget.series.baselineSeries) {
const baseData = candles.map(c => ({ time: c.time, value: c.close }));
tvWidget.series.baselineSeries.setData(baseData);
} else if (klineType === 'klc' && tvWidget.series.klcSeries) {
const klcCandles = buildKLCFromAnalysis(currentData);
tvWidget.series.klcSeries.setData(klcCandles);
}
// 更新均线数据
@@ -7010,7 +7025,6 @@
// 更新数据源信息
setupDataSourceInfo(data);
}
// 设置数据源信息显示
function setupDataSourceInfo(data) {
// 获取用户当前的周期选择
@@ -7103,7 +7117,6 @@
$('#end_time').val(formatDatetimeLocal(now));
$('#start_time').val(formatDatetimeLocal(oneDayAgo));
}
// 格式化日期为datetime-local输入框格式
function formatDatetimeLocal(date) {
const year = date.getFullYear();
@@ -7753,7 +7766,6 @@
console.error('加载A股股票列表失败');
});
}
// 检测交易对类型并返回相应的配置
function getSymbolConfig(symbol) {
const isAStock = symbol && symbol.length === 6 && /^\d+$/.test(symbol);
@@ -7811,7 +7823,6 @@
return chartOptions;
}
// 过滤非交易时间的数据(仅用于显示优化)
function filterTradingHours(data, symbolConfig) {
if (symbolConfig.type !== 'a_stock') {
@@ -8402,7 +8413,6 @@
function updateMAPanel() {
updateIndicatorPanel();
}
// 切换均线可见性
function toggleMAVisibility(maId) {
console.log('👁️ 切换均线可见性,ID:', maId, 'Type:', typeof maId);
@@ -8605,7 +8615,6 @@
return candles;
}
// 从蜡烛数据生成 Heikin-Ashi(平均K
function buildHeikinFromCandles(candles) {
if (!Array.isArray(candles) || candles.length === 0) return [];
@@ -8627,7 +8636,45 @@
}
return result;
}
// 从分析数据生成KLC蜡烛数据
function buildKLCFromAnalysis(data) {
if (!data) return [];
// 检查是否使用小周期数据
const useElementPeriod = $('#elementPeriodKline').is(':checked') &&
data.element_klc_list &&
Array.isArray(data.element_klc_list);
const klcList = useElementPeriod ? data.element_klc_list : data.klc_list;
if (!klcList) return [];
const klcCandles = [];
// 遍历KLC列表,转换为蜡烛数据格式
klcList.forEach(klc => {
if (!klc || !klc.date) return;
// 使用KLC的date字段,转换为时间戳格式
const date = new Date(klc.date);
const timestamp = date.getTime() / 1000;
// 创建KLC蜡烛数据
const candle = {
time: timestamp,
open: klc.open || 0,
high: klc.high || 0,
low: klc.low || 0,
close: klc.close || 0
};
klcCandles.push(candle);
});
return klcCandles;
}
// 计算默认砖大小(优先使用ATR的最新非零值,否则按收盘价的0.5%)
function computeDefaultBrickSize(candles) {
try {
@@ -8881,10 +8928,10 @@
console.log('🔄 按钮已切换为更新模式');
$('#bbConfigModal').css('display', 'flex');
// 更新预览
setTimeout(updateBBLinePreview, 50);
$('#bbConfigModal').css('display', 'flex');
// 更新预览
setTimeout(updateBBLinePreview, 50);
}
// 更新布林带
function updateBollingerBand(bbId) {
@@ -9008,78 +9055,78 @@
console.warn('addBollingerBandsToChart 未就绪');
}
// 更新均线预览
// 更新均线预览
function updateLinePreview() {
// 检查是否在均线配置窗口
if ($('#maConfigModal').is(':visible')) {
const color = $('#maColor').val();
const width = $('#maLineWidth').val();
const style = $('#maLineStyle').val();
// 检查是否在均线配置窗口
if ($('#maConfigModal').is(':visible')) {
const color = $('#maColor').val();
const width = $('#maLineWidth').val();
const style = $('#maLineStyle').val();
const line = $('#previewLine');
line.attr('stroke', color);
line.attr('stroke-width', width);
// 设置线条样式
switch(parseInt(style)) {
case 0: // 实线
line.attr('stroke-dasharray', 'none');
break;
case 1: // 点线
line.attr('stroke-dasharray', '2,3');
break;
case 2: // 虚线
line.attr('stroke-dasharray', '5,5');
break;
case 3: // 大虚线
line.attr('stroke-dasharray', '10,5');
break;
}
}
}
// 更新布林带预览
function updateBBLinePreview() {
const upperColor = $('#bbUpperColor').val();
const middleColor = $('#bbMiddleColor').val();
const lowerColor = $('#bbLowerColor').val();
const width = $('#bbLineWidth').val();
const style = $('#bbLineStyle').val();
const line = $('#previewLine');
line.attr('stroke', color);
line.attr('stroke-width', width);
const upperLine = $('#bbPreviewUpper');
const middleLine = $('#bbPreviewMiddle');
const lowerLine = $('#bbPreviewLower');
// 设置线条样式
switch(parseInt(style)) {
case 0: // 实线
line.attr('stroke-dasharray', 'none');
break;
case 1: // 点线
line.attr('stroke-dasharray', '2,3');
break;
case 2: // 虚线
line.attr('stroke-dasharray', '5,5');
break;
case 3: // 大虚线
line.attr('stroke-dasharray', '10,5');
break;
}
}
}
// 更新布林带预览
function updateBBLinePreview() {
const upperColor = $('#bbUpperColor').val();
const middleColor = $('#bbMiddleColor').val();
const lowerColor = $('#bbLowerColor').val();
const width = $('#bbLineWidth').val();
const style = $('#bbLineStyle').val();
const upperLine = $('#bbPreviewUpper');
const middleLine = $('#bbPreviewMiddle');
const lowerLine = $('#bbPreviewLower');
// 设置各条线的颜色
upperLine.attr('stroke', upperColor);
middleLine.attr('stroke', middleColor);
lowerLine.attr('stroke', lowerColor);
// 设置线条宽度和样式
[upperLine, middleLine, lowerLine].forEach(line => {
line.attr('stroke-width', width);
// 设置线条样式
switch(parseInt(style)) {
case 0: // 实线
line.attr('stroke-dasharray', 'none');
break;
case 1: // 点线
line.attr('stroke-dasharray', '2,3');
break;
case 2: // 虚线
line.attr('stroke-dasharray', '5,5');
break;
case 3: // 大虚线
line.attr('stroke-dasharray', '10,5');
break;
}
});
}
// 设置各条线的颜色
upperLine.attr('stroke', upperColor);
middleLine.attr('stroke', middleColor);
lowerLine.attr('stroke', lowerColor);
// 设置线条宽度和样式
[upperLine, middleLine, lowerLine].forEach(line => {
line.attr('stroke-width', width);
// 设置线条样式
switch(parseInt(style)) {
case 0: // 线
line.attr('stroke-dasharray', 'none');
break;
case 1: // 点线
line.attr('stroke-dasharray', '2,3');
break;
case 2: // 虚线
line.attr('stroke-dasharray', '5,5');
break;
case 3: // 大虚线
line.attr('stroke-dasharray', '10,5');
break;
}
});
}
// 监听配置变化以更新预览
$(document).on('change', '#maColor, #maLineWidth, #maLineStyle', updateLinePreview);
$(document).on('change', '#bbUpperColor, #bbMiddleColor, #bbLowerColor, #bbLineWidth, #bbLineStyle', updateBBLinePreview);
$(document).on('change', '#bbUpperColor, #bbMiddleColor, #bbLowerColor, #bbLineWidth, #bbLineStyle', updateBBLinePreview);
// 点击弹窗外部关闭
$(document).on('click', '#bbConfigModal', function(e) {
if (e.target === this) {
@@ -9128,8 +9175,6 @@
const now = new Date();
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
// 添加页面滚动事件监听器,清除十字线延长线
$(window).on('scroll', function() {
try {
@@ -9146,18 +9191,10 @@
console.debug('清除滚动中的十字线时出错:', e);
}
});
});
// ====== ChanMACD图表相关函数 ======
// 清除ChanMACD标注
function clearChanMacdMarkers() {
// 清除所有系列的标记
@@ -9219,7 +9256,7 @@
color: seg.seg_dir === 'ABOVE' ? '#e91e63' : '#4caf50',
shape: 'square',
text: `S${index}E`,
size: 0.5
size: 0.5
});
}
});
@@ -9369,7 +9406,6 @@
addStateMarkers(stateMarkers);
}
}
// 添加状态标记
function addStateMarkers(stateMarkers) {
const stateMarkersList = [];