修改缠论顶低分型判断和笔生成顶低分型判断

This commit is contained in:
jackyu66git
2025-10-18 02:36:29 +08:00
parent a1f8c6c605
commit cb621c3d87
4 changed files with 571 additions and 127 deletions
+314 -114
View File
@@ -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];