修改缠论顶低分型判断和笔生成顶低分型判断
This commit is contained in:
+314
-114
@@ -811,8 +811,17 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="form-label me-3 mb-0">基础显示:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="showOriginalKline" checked>
|
||||
<label class="form-check-label" for="showOriginalKline">原始K线</label>
|
||||
<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>
|
||||
</select>
|
||||
<!-- 为兼容现有逻辑,保留隐藏的原始K线开关,与下拉同步 -->
|
||||
<input class="form-check-input" type="checkbox" id="showOriginalKline" checked style="display:none;">
|
||||
</div>
|
||||
|
||||
<!-- 添加K线周期切换 -->
|
||||
@@ -1093,46 +1102,10 @@
|
||||
// ======= 趋势筛选(币对) =======
|
||||
let trendTable = null;
|
||||
|
||||
// 限长截断,减少前端长期持有历史数组导致的内存增长
|
||||
// 不在前端截断数据,保留完整历史,避免K线数量限制
|
||||
function trimDataInPlace(payload, maxLen = 2000) {
|
||||
const keepTail = (arr) => (Array.isArray(arr) && arr.length > maxLen) ? arr.slice(-maxLen) : arr;
|
||||
if (!payload || typeof payload !== 'object') return;
|
||||
|
||||
// 主/次周期K线
|
||||
payload.kline_data = keepTail(payload.kline_data);
|
||||
payload.element_kline_data = keepTail(payload.element_kline_data);
|
||||
|
||||
// MACD
|
||||
if (payload.macd) {
|
||||
payload.macd.macd = keepTail(payload.macd.macd);
|
||||
payload.macd.signal = keepTail(payload.macd.signal);
|
||||
payload.macd.histogram = keepTail(payload.macd.histogram);
|
||||
}
|
||||
if (payload.element_macd) {
|
||||
payload.element_macd.macd = keepTail(payload.element_macd.macd);
|
||||
payload.element_macd.signal = keepTail(payload.element_macd.signal);
|
||||
payload.element_macd.histogram = keepTail(payload.element_macd.histogram);
|
||||
}
|
||||
|
||||
// ATR
|
||||
payload.atr = keepTail(payload.atr);
|
||||
payload.element_atr = keepTail(payload.element_atr);
|
||||
|
||||
// 分形/线段/中枢/买卖点
|
||||
payload.bi_list = keepTail(payload.bi_list);
|
||||
payload.seg_list = keepTail(payload.seg_list);
|
||||
payload.zs_list = keepTail(payload.zs_list);
|
||||
payload.uncompleted_zs_list = keepTail(payload.uncompleted_zs_list);
|
||||
|
||||
payload.element_bi_list = keepTail(payload.element_bi_list);
|
||||
payload.element_seg_list = keepTail(payload.element_seg_list);
|
||||
payload.element_zs_list = keepTail(payload.element_zs_list);
|
||||
payload.element_uncompleted_zs_list = keepTail(payload.element_uncompleted_zs_list);
|
||||
|
||||
payload.trade_points = keepTail(payload.trade_points);
|
||||
payload.element_trade_points = keepTail(payload.element_trade_points);
|
||||
|
||||
// 移除可能保存的旧快照,避免重复持有
|
||||
// 仅清理可能保留的旧快照引用
|
||||
delete payload.original_kline_data;
|
||||
delete payload.original_macd;
|
||||
}
|
||||
@@ -1501,7 +1474,11 @@
|
||||
chanMacdChart: null, // 新增ChanMACD图表
|
||||
series: {
|
||||
candleSeries: null,
|
||||
barSeries: null,
|
||||
lineSeries: null,
|
||||
areaSeries: null,
|
||||
baselineSeries: null,
|
||||
renkoSeries: null,
|
||||
volumeSeries: null,
|
||||
atrLineSeries: null,
|
||||
macdLineSeries: null,
|
||||
@@ -1714,6 +1691,13 @@
|
||||
updateChartDisplay();
|
||||
});
|
||||
|
||||
// 添加K线形态下拉变更事件(同步隐藏的原始K线开关并重绘)
|
||||
$('#klineType').change(function() {
|
||||
const type = $(this).val();
|
||||
$('#showOriginalKline').prop('checked', type === 'candlestick');
|
||||
updateChartDisplay();
|
||||
});
|
||||
|
||||
// 添加笔复选框变更事件
|
||||
$('#showMainBi').change(function() {
|
||||
updateChartDisplay();
|
||||
@@ -1845,7 +1829,6 @@
|
||||
setSmallerOrEqualTimeframe(mainTimeframe);
|
||||
}
|
||||
});
|
||||
|
||||
function updateChartDisplay() {
|
||||
if (currentData) {
|
||||
// 保存当前的可见逻辑范围
|
||||
@@ -2164,7 +2147,6 @@
|
||||
candles = filterTradingHours(candles, symbolConfig);
|
||||
console.log(`A股数据过滤: ${originalLength} -> ${candles.length} 条记录`);
|
||||
}
|
||||
|
||||
// 清除图表容器(释放旧 DOM 与 Canvas)
|
||||
const chartRoot = document.getElementById('tradingview_chart');
|
||||
if (chartRoot) chartRoot.innerHTML = '';
|
||||
@@ -2472,36 +2454,94 @@
|
||||
chanMacdChart = LightweightCharts.createChart(chanMacdChartContainer, createChartOptions(false, 'chanmacd'));
|
||||
}
|
||||
|
||||
// 创建蜡烛图系列并设置数据
|
||||
if (showOriginalKline) {
|
||||
const candleSeries = mainChart.addCandlestickSeries({
|
||||
upColor: '#dc3545',
|
||||
downColor: '#28a745',
|
||||
borderVisible: false,
|
||||
wickUpColor: '#dc3545',
|
||||
wickDownColor: '#28a745',
|
||||
});
|
||||
candleSeries.setData(candles);
|
||||
tvWidget.series.candleSeries = candleSeries;
|
||||
} else {
|
||||
// 如果不显示原始K线,只显示线图
|
||||
const lineSeries = mainChart.addLineSeries({
|
||||
color: '#2962FF',
|
||||
lineWidth: 2,
|
||||
crosshairMarkerVisible: true,
|
||||
lastValueVisible: true,
|
||||
priceLineVisible: true,
|
||||
});
|
||||
|
||||
// 提取收盘价数据
|
||||
const lineData = candles.map(candle => ({
|
||||
time: candle.time,
|
||||
value: candle.close
|
||||
}));
|
||||
|
||||
lineSeries.setData(lineData);
|
||||
tvWidget.series.lineSeries = lineSeries;
|
||||
}
|
||||
// 创建主价格系列并设置数据(支持多种图表类型)
|
||||
(function(){
|
||||
const klineType = ($('#klineType').val() || 'candlestick');
|
||||
// 先清空旧的主系列引用
|
||||
tvWidget.series.candleSeries = null;
|
||||
tvWidget.series.lineSeries = null;
|
||||
tvWidget.series.barSeries = null;
|
||||
tvWidget.series.areaSeries = null;
|
||||
tvWidget.series.baselineSeries = null;
|
||||
tvWidget.series.renkoSeries = null;
|
||||
tvWidget.series.heikinSeries = null;
|
||||
|
||||
if (klineType === 'candlestick') {
|
||||
const series = mainChart.addCandlestickSeries({
|
||||
upColor: '#dc3545',
|
||||
downColor: '#28a745',
|
||||
borderVisible: false,
|
||||
wickUpColor: '#dc3545',
|
||||
wickDownColor: '#28a745',
|
||||
});
|
||||
series.setData(candles);
|
||||
tvWidget.series.candleSeries = series;
|
||||
} else if (klineType === 'renko') {
|
||||
const series = mainChart.addCandlestickSeries({
|
||||
upColor: '#dc3545',
|
||||
downColor: '#28a745',
|
||||
borderVisible: false,
|
||||
wickUpColor: '#dc3545',
|
||||
wickDownColor: '#28a745',
|
||||
});
|
||||
const bricks = buildRenkoFromCandles(candles);
|
||||
series.setData(bricks);
|
||||
tvWidget.series.renkoSeries = series;
|
||||
} else if (klineType === 'heikin') {
|
||||
const series = mainChart.addCandlestickSeries({
|
||||
upColor: '#dc3545',
|
||||
downColor: '#28a745',
|
||||
borderVisible: false,
|
||||
wickUpColor: '#dc3545',
|
||||
wickDownColor: '#28a745',
|
||||
});
|
||||
const hk = buildHeikinFromCandles(candles);
|
||||
series.setData(hk);
|
||||
tvWidget.series.heikinSeries = series;
|
||||
} else if (klineType === 'bar') {
|
||||
const series = mainChart.addBarSeries({
|
||||
upColor: '#dc3545',
|
||||
downColor: '#28a745',
|
||||
thinBars: false
|
||||
});
|
||||
series.setData(candles);
|
||||
tvWidget.series.barSeries = series;
|
||||
} else if (klineType === 'line') {
|
||||
const series = mainChart.addLineSeries({
|
||||
color: '#2962FF',
|
||||
lineWidth: 2,
|
||||
crosshairMarkerVisible: true,
|
||||
lastValueVisible: true,
|
||||
priceLineVisible: true,
|
||||
});
|
||||
const lineData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
series.setData(lineData);
|
||||
tvWidget.series.lineSeries = series;
|
||||
} else if (klineType === 'area') {
|
||||
const series = mainChart.addAreaSeries({
|
||||
topColor: 'rgba(41, 98, 255, 0.4)',
|
||||
bottomColor: 'rgba(41, 98, 255, 0.0)',
|
||||
lineColor: '#2962FF',
|
||||
lineWidth: 2,
|
||||
});
|
||||
const areaData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
series.setData(areaData);
|
||||
tvWidget.series.areaSeries = series;
|
||||
} else if (klineType === 'baseline') {
|
||||
const series = mainChart.addBaselineSeries({
|
||||
baseValue: { type: 'price', price: candles.length ? candles[candles.length - 1].close : 0 },
|
||||
topLineColor: '#26a69a',
|
||||
bottomLineColor: '#ef5350',
|
||||
topFillColor1: 'rgba(38, 166, 154, 0.28)',
|
||||
topFillColor2: 'rgba(38, 166, 154, 0.05)',
|
||||
bottomFillColor1: 'rgba(239, 83, 80, 0.28)',
|
||||
bottomFillColor2: 'rgba(239, 83, 80, 0.05)'
|
||||
});
|
||||
const baseData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
series.setData(baseData);
|
||||
tvWidget.series.baselineSeries = series;
|
||||
}
|
||||
})();
|
||||
|
||||
// 转换成交量数据 - 始终使用主K线周期数据
|
||||
let volumes = [];
|
||||
@@ -2797,7 +2837,7 @@
|
||||
console.log('✅ ChanMACD图表系列已保存到tvWidget');
|
||||
|
||||
// 添加ChanMACD分析标注
|
||||
// 根据主/次周期开关与各自的“显示U”独立控制
|
||||
// 根据主/次周期开关与各自的"显示U"独立控制
|
||||
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
||||
// 默认不显示,必须用户勾选对应复选框
|
||||
const allowU = useElementPeriod ? !!window.showUOnElement : !!window.showUOnMain;
|
||||
@@ -2918,7 +2958,6 @@
|
||||
} else {
|
||||
console.log('⚠️ ChanMACD图表创建条件不满足');
|
||||
}
|
||||
|
||||
// 独立于当前显示周期:计算主/次周期 SD/CD 标记(用于主图合并显示)
|
||||
try {
|
||||
const mainCmAll = currentData.chan_macd || {};
|
||||
@@ -4753,15 +4792,38 @@
|
||||
// 获取当前价格 - 通过param.seriesPrices获取
|
||||
let priceInfo = '';
|
||||
if (param.seriesPrices && param.seriesPrices.size > 0) {
|
||||
// 尝试从蜡烛图系列获取价格
|
||||
// 依次从当前可能的主系列中获取价格
|
||||
if (tvWidget.series.candleSeries && param.seriesPrices.get(tvWidget.series.candleSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.candleSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
// 如果没有蜡烛图系列价格,尝试从线图系列获取
|
||||
else if (tvWidget.series.lineSeries && param.seriesPrices.get(tvWidget.series.lineSeries)) {
|
||||
} else if (tvWidget.series.renkoSeries && param.seriesPrices.get(tvWidget.series.renkoSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.renkoSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.heikinSeries && param.seriesPrices.get(tvWidget.series.heikinSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.heikinSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.barSeries && param.seriesPrices.get(tvWidget.series.barSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.barSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.lineSeries && param.seriesPrices.get(tvWidget.series.lineSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.lineSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.areaSeries && param.seriesPrices.get(tvWidget.series.areaSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.areaSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.baselineSeries && param.seriesPrices.get(tvWidget.series.baselineSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.baselineSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
// 如果没有蜡烛图系列价格,尝试从区域图系列获取
|
||||
else if (tvWidget.series.areaSeries && param.seriesPrices.get(tvWidget.series.areaSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.areaSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
// 如果没有蜡烛图系列价格,尝试从基线图系列获取
|
||||
else if (tvWidget.series.baselineSeries && param.seriesPrices.get(tvWidget.series.baselineSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.baselineSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5342,13 +5404,18 @@
|
||||
if (combinedMarkers.length > 0) {
|
||||
console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)');
|
||||
|
||||
// 尝试在K线系列上设置合并后的标记
|
||||
if (showOriginalKline && tvWidget.series.candleSeries) {
|
||||
tvWidget.series.candleSeries.setMarkers(combinedMarkers);
|
||||
console.log('合并标记已设置到蜡烛图系列');
|
||||
} else if (!showOriginalKline && tvWidget.series.lineSeries) {
|
||||
tvWidget.series.lineSeries.setMarkers(combinedMarkers);
|
||||
console.log('合并标记已设置到线图系列');
|
||||
// 根据当前主系列类型设置标记
|
||||
const klineType = ($('#klineType').val() || (showOriginalKline ? 'candlestick' : 'line'));
|
||||
let targetSeries = null;
|
||||
if (klineType === 'candlestick') targetSeries = tvWidget.series.candleSeries;
|
||||
else if (klineType === 'renko') targetSeries = tvWidget.series.renkoSeries;
|
||||
else if (klineType === 'heikin') targetSeries = tvWidget.series.heikinSeries;
|
||||
else if (klineType === 'bar') targetSeries = tvWidget.series.barSeries;
|
||||
else if (klineType === 'line') targetSeries = tvWidget.series.lineSeries;
|
||||
else if (klineType === 'area') targetSeries = tvWidget.series.areaSeries;
|
||||
else if (klineType === 'baseline') targetSeries = tvWidget.series.baselineSeries;
|
||||
if (targetSeries) {
|
||||
targetSeries.setMarkers(combinedMarkers);
|
||||
} else {
|
||||
console.log('未找到主数据系列,无法设置标记');
|
||||
}
|
||||
@@ -5431,23 +5498,35 @@
|
||||
if (onlyMainAndU.length > 0) {
|
||||
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')');
|
||||
|
||||
// 尝试在K线系列上设置标记
|
||||
if (showOriginalKline && tvWidget.series.candleSeries) {
|
||||
tvWidget.series.candleSeries.setMarkers(onlyMainAndU);
|
||||
console.log('主周期标记已设置到蜡烛图系列');
|
||||
} else if (!showOriginalKline && tvWidget.series.lineSeries) {
|
||||
tvWidget.series.lineSeries.setMarkers(onlyMainAndU);
|
||||
console.log('主周期标记已设置到线图系列');
|
||||
// 根据当前主系列类型设置标记
|
||||
const klineType2 = ($('#klineType').val() || (showOriginalKline ? 'candlestick' : 'line'));
|
||||
let targetSeries2 = null;
|
||||
if (klineType2 === 'candlestick') targetSeries2 = tvWidget.series.candleSeries;
|
||||
else if (klineType2 === 'renko') targetSeries2 = tvWidget.series.renkoSeries;
|
||||
else if (klineType2 === 'heikin') targetSeries2 = tvWidget.series.heikinSeries;
|
||||
else if (klineType2 === 'bar') targetSeries2 = tvWidget.series.barSeries;
|
||||
else if (klineType2 === 'line') targetSeries2 = tvWidget.series.lineSeries;
|
||||
else if (klineType2 === 'area') targetSeries2 = tvWidget.series.areaSeries;
|
||||
else if (klineType2 === 'baseline') targetSeries2 = tvWidget.series.baselineSeries;
|
||||
if (targetSeries2) {
|
||||
targetSeries2.setMarkers(onlyMainAndU);
|
||||
} else {
|
||||
console.log('未找到主数据系列,无法设置标记');
|
||||
}
|
||||
} else {
|
||||
console.log('没有分型标记需要显示,清空图表标记');
|
||||
// 清空图表上的所有标记
|
||||
if (showOriginalKline && tvWidget.series.candleSeries) {
|
||||
tvWidget.series.candleSeries.setMarkers([]);
|
||||
} else if (!showOriginalKline && tvWidget.series.lineSeries) {
|
||||
tvWidget.series.lineSeries.setMarkers([]);
|
||||
// 清空图表上的所有主系列标记
|
||||
const klineType3 = ($('#klineType').val() || (showOriginalKline ? 'candlestick' : 'line'));
|
||||
let targetSeries3 = null;
|
||||
if (klineType3 === 'candlestick') targetSeries3 = tvWidget.series.candleSeries;
|
||||
else if (klineType3 === 'renko') targetSeries3 = tvWidget.series.renkoSeries;
|
||||
else if (klineType3 === 'heikin') targetSeries3 = tvWidget.series.heikinSeries;
|
||||
else if (klineType3 === 'bar') targetSeries3 = tvWidget.series.barSeries;
|
||||
else if (klineType3 === 'line') targetSeries3 = tvWidget.series.lineSeries;
|
||||
else if (klineType3 === 'area') targetSeries3 = tvWidget.series.areaSeries;
|
||||
else if (klineType3 === 'baseline') targetSeries3 = tvWidget.series.baselineSeries;
|
||||
if (targetSeries3) {
|
||||
targetSeries3.setMarkers([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5590,7 +5669,6 @@
|
||||
console.log('🔧 最终时间轴对齐完成');
|
||||
}
|
||||
}, 150);
|
||||
|
||||
// 只有在时间输入框都为空时才设置图表默认时间范围
|
||||
if (!$('#start_time').val() && !$('#end_time').val()) {
|
||||
setDefaultTimeRange();
|
||||
@@ -5673,17 +5751,27 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 更新K线数据
|
||||
if (showOriginalKline && tvWidget.series.candleSeries) {
|
||||
// 更新主系列数据(根据klineType)
|
||||
const klineType = ($('#klineType').val() || (showOriginalKline ? 'candlestick' : 'line'));
|
||||
if (klineType === 'candlestick' && tvWidget.series.candleSeries) {
|
||||
tvWidget.series.candleSeries.setData(candles);
|
||||
} else if (!showOriginalKline && tvWidget.series.lineSeries) {
|
||||
// 提取收盘价数据
|
||||
const lineData = candles.map(candle => ({
|
||||
time: candle.time,
|
||||
value: candle.close
|
||||
}));
|
||||
|
||||
} else if (klineType === 'renko' && tvWidget.series.renkoSeries) {
|
||||
const bricks = buildRenkoFromCandles(candles);
|
||||
tvWidget.series.renkoSeries.setData(bricks);
|
||||
} else if (klineType === 'heikin' && tvWidget.series.heikinSeries) {
|
||||
const hk = buildHeikinFromCandles(candles);
|
||||
tvWidget.series.heikinSeries.setData(hk);
|
||||
} else if (klineType === 'bar' && tvWidget.series.barSeries) {
|
||||
tvWidget.series.barSeries.setData(candles);
|
||||
} else if (klineType === 'line' && tvWidget.series.lineSeries) {
|
||||
const lineData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
tvWidget.series.lineSeries.setData(lineData);
|
||||
} else if (klineType === 'area' && tvWidget.series.areaSeries) {
|
||||
const areaData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
tvWidget.series.areaSeries.setData(areaData);
|
||||
} else if (klineType === 'baseline' && tvWidget.series.baselineSeries) {
|
||||
const baseData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
tvWidget.series.baselineSeries.setData(baseData);
|
||||
}
|
||||
|
||||
// 更新均线数据
|
||||
@@ -6310,15 +6398,38 @@
|
||||
// 获取当前价格 - 通过param.seriesPrices获取
|
||||
let priceInfo = '';
|
||||
if (param.seriesPrices && param.seriesPrices.size > 0) {
|
||||
// 尝试从蜡烛图系列获取价格
|
||||
// 依次从当前可能的主系列中获取价格
|
||||
if (tvWidget.series.candleSeries && param.seriesPrices.get(tvWidget.series.candleSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.candleSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
// 如果没有蜡烛图系列价格,尝试从线图系列获取
|
||||
else if (tvWidget.series.lineSeries && param.seriesPrices.get(tvWidget.series.lineSeries)) {
|
||||
} else if (tvWidget.series.renkoSeries && param.seriesPrices.get(tvWidget.series.renkoSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.renkoSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.heikinSeries && param.seriesPrices.get(tvWidget.series.heikinSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.heikinSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.barSeries && param.seriesPrices.get(tvWidget.series.barSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.barSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.lineSeries && param.seriesPrices.get(tvWidget.series.lineSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.lineSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.areaSeries && param.seriesPrices.get(tvWidget.series.areaSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.areaSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
} else if (tvWidget.series.baselineSeries && param.seriesPrices.get(tvWidget.series.baselineSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.baselineSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
// 如果没有蜡烛图系列价格,尝试从区域图系列获取
|
||||
else if (tvWidget.series.areaSeries && param.seriesPrices.get(tvWidget.series.areaSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.areaSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
// 如果没有蜡烛图系列价格,尝试从基线图系列获取
|
||||
else if (tvWidget.series.baselineSeries && param.seriesPrices.get(tvWidget.series.baselineSeries)) {
|
||||
const price = param.seriesPrices.get(tvWidget.series.baselineSeries);
|
||||
priceInfo = `价格: ${price.toFixed(2)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7068,6 +7179,10 @@
|
||||
tvWidget.series = {
|
||||
candleSeries: null,
|
||||
lineSeries: null,
|
||||
barSeries: null,
|
||||
areaSeries: null,
|
||||
baselineSeries: null,
|
||||
renkoSeries: null,
|
||||
volumeSeries: null,
|
||||
atrLineSeries: null,
|
||||
macdLineSeries: null,
|
||||
@@ -7155,7 +7270,6 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 开始自动刷新
|
||||
function startAutoRefresh() {
|
||||
// 停止已有的刷新定时器
|
||||
@@ -7954,7 +8068,6 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取随机颜色
|
||||
function getRandomColor() {
|
||||
const colors = ['#2962FF', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
|
||||
@@ -8476,6 +8589,93 @@
|
||||
return candles;
|
||||
}
|
||||
|
||||
// 从蜡烛数据生成 Heikin-Ashi(平均K)
|
||||
function buildHeikinFromCandles(candles) {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return [];
|
||||
const result = [];
|
||||
let prevHaClose = (candles[0].open + candles[0].high + candles[0].low + candles[0].close) / 4;
|
||||
let prevHaOpen = (candles[0].open + candles[0].close) / 2;
|
||||
const firstHaHigh = Math.max(candles[0].high, prevHaOpen, prevHaClose);
|
||||
const firstHaLow = Math.min(candles[0].low, prevHaOpen, prevHaClose);
|
||||
result.push({ time: candles[0].time, open: prevHaOpen, high: firstHaHigh, low: firstHaLow, close: prevHaClose });
|
||||
for (let i = 1; i < candles.length; i++) {
|
||||
const c = candles[i];
|
||||
const haClose = (c.open + c.high + c.low + c.close) / 4;
|
||||
const haOpen = (prevHaOpen + prevHaClose) / 2;
|
||||
const haHigh = Math.max(c.high, haOpen, haClose);
|
||||
const haLow = Math.min(c.low, haOpen, haClose);
|
||||
result.push({ time: c.time, open: haOpen, high: haHigh, low: haLow, close: haClose });
|
||||
prevHaOpen = haOpen;
|
||||
prevHaClose = haClose;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算默认砖大小(优先使用ATR的最新非零值,否则按收盘价的0.5%)
|
||||
function computeDefaultBrickSize(candles) {
|
||||
try {
|
||||
const useElementPeriod = $('#elementPeriodKline').is(':checked') && currentData.element_kline_data && Array.isArray(currentData.element_kline_data);
|
||||
const atrArr = useElementPeriod ? (currentData.element_atr || currentData.atr) : currentData.atr;
|
||||
if (Array.isArray(atrArr) && atrArr.length > 0) {
|
||||
for (let i = atrArr.length - 1; i >= 0; i--) {
|
||||
const v = parseFloat(atrArr[i]);
|
||||
if (!isNaN(v) && v > 0) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
const last = candles && candles.length ? candles[candles.length - 1].close : 0;
|
||||
const size = last * 0.005; // 默认0.5%
|
||||
return size > 0 ? size : 1;
|
||||
}
|
||||
|
||||
// 从蜡烛数据生成Renko砖(返回伪K线数据用于CandlestickSeries显示)
|
||||
function buildRenkoFromCandles(candles) {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return [];
|
||||
|
||||
const tryBuild = (size) => {
|
||||
const bricks = [];
|
||||
let lastClose = candles[0].close;
|
||||
for (let i = 1; i < candles.length; i++) {
|
||||
// 确保同一根K内多砖的时间唯一且递增
|
||||
let nextTime = candles[i].time;
|
||||
const price = candles[i].close;
|
||||
let diff = price - lastClose;
|
||||
// 向上生成砖
|
||||
while (diff >= size) {
|
||||
const open = lastClose;
|
||||
const close = lastClose + size;
|
||||
bricks.push({ time: nextTime, open: open, high: close, low: open, close: close });
|
||||
lastClose = close;
|
||||
diff = price - lastClose;
|
||||
nextTime += 1; // 递增1秒,保证唯一
|
||||
}
|
||||
// 向下生成砖
|
||||
while (diff <= -size) {
|
||||
const open = lastClose;
|
||||
const close = lastClose - size;
|
||||
bricks.push({ time: nextTime, open: open, high: open, low: close, close: close });
|
||||
lastClose = close;
|
||||
diff = price - lastClose;
|
||||
nextTime += 1; // 递增1秒,保证唯一
|
||||
}
|
||||
}
|
||||
return bricks;
|
||||
};
|
||||
|
||||
// 计算初始砖大小,若无砖则逐步缩小后重试,避免不显示
|
||||
let brickSize = computeDefaultBrickSize(candles);
|
||||
let result = tryBuild(brickSize);
|
||||
let attempts = 0;
|
||||
while (result.length === 0 && attempts < 4) {
|
||||
brickSize *= 0.5; // 缩小一半后重试
|
||||
result = tryBuild(brickSize);
|
||||
attempts += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 指标统一刷新(若图表未就绪则自动重试)
|
||||
function refreshIndicatorsNowOrLater(retries) {
|
||||
try {
|
||||
@@ -9062,7 +9262,7 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
// 识别“U 结束与新 U 开始同一根K”的边界,并显示合成标记(即使前一个U是 invalid 也显示边界)
|
||||
// 识别"U 结束与新 U 开始同一根K"的边界,并显示合成标记(即使前一个U是 invalid 也显示边界)
|
||||
for (let i = 0; i + 1 < unittfList.length; i++) {
|
||||
const cur = unittfList[i];
|
||||
const nxt = unittfList[i + 1];
|
||||
|
||||
Reference in New Issue
Block a user