This commit is contained in:
jackyu66git
2025-06-18 00:56:41 +08:00
parent 3dc7dd5322
commit de756e38eb
3 changed files with 338 additions and 53 deletions
+310 -46
View File
@@ -322,9 +322,21 @@
<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 %}
<option value="1m">1分钟</option>
<option value="3m">3分钟</option>
<option value="5m" selected>5分钟</option>
<option value="15m">15分钟</option>
<option value="30m">30分钟</option>
<option value="1h">1小时</option>
<option value="2h">2小时</option>
<option value="4h">4小时</option>
<option value="6h">6小时</option>
<option value="8h">8小时</option>
<option value="12h">12小时</option>
<option value="1d">1日</option>
<option value="3d">3日</option>
<option value="1w">1周</option>
<option value="1M">1月</option>
</select>
</div>
<div class="col-md-1">
@@ -440,9 +452,21 @@
<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 %}
<option value="1m" selected>1分钟</option>
<option value="3m">3分钟</option>
<option value="5m">5分钟</option>
<option value="15m">15分钟</option>
<option value="30m">30分钟</option>
<option value="1h">1小时</option>
<option value="2h">2小时</option>
<option value="4h">4小时</option>
<option value="6h">6小时</option>
<option value="8h">8小时</option>
<option value="12h">12小时</option>
<option value="1d">1日</option>
<option value="3d">3日</option>
<option value="1w">1周</option>
<option value="1M">1月</option>
</select>
</div>
@@ -663,12 +687,19 @@
<div class="col-md-3">
<label for="filterTimeframe" class="form-label">时间周期:</label>
<select id="filterTimeframe" class="form-select">
<option value="1m">1分钟</option>
<option value="3m">3分钟</option>
<option value="5m">5分钟</option>
<option value="15m">15分钟</option>
<option value="30m">30分钟</option>
<option value="1h">1小时</option>
<option value="2h">2小时</option>
<option value="4h">4小时</option>
<option value="6h">6小时</option>
<option value="8h">8小时</option>
<option value="12h">12小时</option>
<option value="1d" selected>1日</option>
<option value="3d">3日</option>
<option value="1w">1周</option>
<option value="1M">1月</option>
</select>
@@ -736,6 +767,7 @@
macdLineSeries: null,
signalLineSeries: null,
histogramSeries: null,
atrLineSeries: null,
mainBiSeries: [],
mainSegSeries: [],
mainZsSeries: [],
@@ -1396,22 +1428,34 @@
volumeChartContainer.style.right = '0';
volumeChartContainer.style.borderTop = '1px solid #e0e0e0';
// 如果需要显示MACD,创建MACD容器
// 如果需要显示MACD,创建MACD和ATR容器
let macdChartContainer = null;
let atrChartContainer = null;
if (showMacd) {
// 设置各图表高度 - 为个图表分配合理比例,主图表适度增加高度
mainChartContainer.style.height = '55%'; // 主图占55%(约385px
volumeChartContainer.style.top = '55%';
volumeChartContainer.style.height = '22.5%'; // 成交量图占22.5%(约157.5px
// 设置各图表高度 - 为个图表分配合理比例
mainChartContainer.style.height = '45%'; // 主图占45%(约315px
volumeChartContainer.style.top = '45%';
volumeChartContainer.style.height = '18.5%'; // 成交量图占18.5%(约129.5px
// 创建MACD容器
macdChartContainer = document.createElement('div');
macdChartContainer.style.width = '100%';
macdChartContainer.style.height = '22.5%'; // MACD图占22.5%(约157.5px
macdChartContainer.style.height = '18.5%'; // MACD图占18.5%(约129.5px
macdChartContainer.style.position = 'absolute';
macdChartContainer.style.top = '77.5%'; // 从77.5%位置开始
macdChartContainer.style.top = '63.5%'; // 从63.5%位置开始
macdChartContainer.style.left = '0';
macdChartContainer.style.right = '0';
macdChartContainer.style.borderTop = '1px solid #e0e0e0';
// 创建ATR容器
atrChartContainer = document.createElement('div');
atrChartContainer.style.width = '100%';
atrChartContainer.style.height = '18%'; // ATR图占18%(约126px
atrChartContainer.style.position = 'absolute';
atrChartContainer.style.top = '82%'; // 从82%位置开始
atrChartContainer.style.left = '0';
atrChartContainer.style.right = '0';
atrChartContainer.style.borderTop = '1px solid #e0e0e0';
} else {
// 不显示MACD时的高度 - 主图和成交量图分配
mainChartContainer.style.height = '72%'; // 主图占72%(约504px
@@ -1421,7 +1465,10 @@
container.appendChild(mainChartContainer);
container.appendChild(volumeChartContainer);
if (showMacd) container.appendChild(macdChartContainer);
if (showMacd) {
container.appendChild(macdChartContainer);
container.appendChild(atrChartContainer);
}
// 防止同步过程中的无限循环
let syncInProgress = false;
@@ -1436,6 +1483,8 @@
chartHeight = volumeChartContainer.clientHeight;
} else if (chartType === 'macd') {
chartHeight = macdChartContainer ? macdChartContainer.clientHeight : 0;
} else if (chartType === 'atr') {
chartHeight = atrChartContainer ? atrChartContainer.clientHeight : 0;
} else {
chartHeight = mainChartContainer.clientHeight;
}
@@ -1585,8 +1634,29 @@
// 创建MACD图表(如果需要)
let macdChart = null;
let atrChart = null;
if (showMacd) {
macdChart = LightweightCharts.createChart(macdChartContainer, createChartOptions(false, 'macd'));
macdChart = LightweightCharts.createChart(macdChartContainer, createChartOptions(false, 'macd'));
atrChart = LightweightCharts.createChart(atrChartContainer, createChartOptions(false, 'atr'));
// 强制所有图表使用相同的时间刻度配置,确保时间对齐
const timeScaleOptions = {
borderVisible: false,
timeVisible: true,
secondsVisible: false,
tickMarkMaxCharacterLength: 8,
};
// 应用统一的时间刻度到所有图表
mainChart.timeScale().applyOptions(timeScaleOptions);
volumeChart.timeScale().applyOptions(timeScaleOptions);
macdChart.timeScale().applyOptions(timeScaleOptions);
atrChart.timeScale().applyOptions(timeScaleOptions);
mainChart.timeScale().applyOptions(timeScaleOptions);
volumeChart.timeScale().applyOptions(timeScaleOptions);
macdChart.timeScale().applyOptions(timeScaleOptions);
atrChart.timeScale().applyOptions(timeScaleOptions);
}
// 创建蜡烛图系列并设置数据
@@ -1623,9 +1693,10 @@
// 转换成交量数据 - 始终使用主K线周期数据
let volumes = [];
// 使用与K线和MACD相同的数据源选择逻辑
const volumeDataSource = useElementPeriod ? currentData.element_kline_data : currentData.kline_data;
// 强制使用主周期数据源,确保与MACD、ATR图表时间完全一致
const volumeDataSource = currentData.kline_data; // 总是使用主周期数据
console.log('成交量数据源选择:', useElementPeriod ? '次周期' : '主周期');
console.log('成交量数据源: 主周期(强制与MACD、ATR一致)');
console.log('成交量数据长度:', volumeDataSource.length);
if (volumeDataSource && Array.isArray(volumeDataSource)) {
@@ -1688,13 +1759,11 @@
const signalData = [];
const histogramData = [];
// 使用与K线数据相同的数据源确保时间对齐
const klineDataSource = useElementPeriod ? currentData.element_kline_data : currentData.kline_data;
const macdDataSource = useElementPeriod ?
(currentData.element_macd || currentData.macd) : // 如果有次周期MACD数据则使用,否则使用主周期
currentData.macd; // 主周期使用主周期MACD数据
// 强制使用主周期数据源确保与ATR图表时间完全一致
const klineDataSource = currentData.kline_data; // 总是使用主周期数据
const macdDataSource = currentData.macd; // 总是使用主周期MACD数据
console.log('MACD数据源选择:', useElementPeriod ? '次周期' : '主周期');
console.log('MACD数据源: 主周期(强制与ATR一致)');
console.log('K线数据长度:', klineDataSource.length);
console.log('MACD数据:', macdDataSource);
@@ -1735,6 +1804,71 @@
tvWidget.series.histogramSeries = histogramSeries;
}
// 添加ATR图表 - 独立显示ATR数据
if (showMacd && currentData.kline_data && Array.isArray(currentData.kline_data)) {
// 创建ATR线
const atrLineSeries = atrChart.addLineSeries({
color: '#FF9800',
lineWidth: 2,
title: 'ATR',
lastValueVisible: false,
priceLineVisible: false,
baseLineVisible: false,
crosshairMarkerVisible: false,
pointMarkersVisible: false,
});
// 提取ATR数据
const atrData = [];
// 强制使用主周期数据源,确保与MACD图表时间完全一致
const klineDataSource = currentData.kline_data; // 总是使用主周期数据
console.log('ATR数据源: 主周期(强制与MACD一致)');
console.log('ATR K线数据长度:', klineDataSource.length);
// 创建一个隐藏的基础数据系列来保持时间轴对齐
const baseLineSeries = atrChart.addLineSeries({
color: 'transparent',
lineWidth: 0,
lastValueVisible: false,
priceLineVisible: false,
baseLineVisible: false,
crosshairMarkerVisible: false,
pointMarkersVisible: false,
visible: false
});
// 添加完整的时间数据(透明)来保持时间轴连续
const baseData = [];
for (let i = 0; i < klineDataSource.length; i++) {
const kline = klineDataSource[i];
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
baseData.push({
time: timestamp,
value: 0.1 // 使用很小的值,不会显示
});
// 只添加ATR>0的有效数据到可见系列
if (kline.atr && kline.atr > 0) {
atrData.push({
time: timestamp,
value: kline.atr
});
}
}
// 先设置基础数据保持时间轴
baseLineSeries.setData(baseData);
// 再设置ATR可见数据
atrLineSeries.setData(atrData);
console.log('ATR基础数据点数:', baseData.length);
console.log('ATR可见数据点数:', atrData.length);
tvWidget.series.atrLineSeries = atrLineSeries;
}
// 实现三图联动滚动
// 同步图表的时间范围
@@ -1748,7 +1882,8 @@
syncInProgress = true;
console.log('🚀 开始同步图表,来源:',
sourceChart === mainChart ? '主图' :
sourceChart === volumeChart ? '成交量图' : 'MACD图');
sourceChart === volumeChart ? '成交量图' :
sourceChart === macdChart ? 'MACD图' : 'ATR图');
try {
if (sourceChart && sourceChart.timeScale) {
@@ -1787,6 +1922,16 @@
}
}
// 同步ATR图
if (showMacd && atrChart && sourceChart !== atrChart && atrChart.timeScale) {
try {
atrChart.timeScale().setVisibleLogicalRange(logicalRange);
console.log('✅ ATR图同步完成');
} catch (e) {
console.error('❌ ATR图同步失败:', e);
}
}
// 保存当前的可见范围到全局状态
if (tvWidget && tvWidget.state) {
tvWidget.state.logicalRange = logicalRange;
@@ -1812,7 +1957,8 @@
let localDragStates = {
main: false,
volume: false,
macd: false
macd: false,
atr: false
};
// 全局鼠标抬起事件(只添加一次)
@@ -1828,10 +1974,15 @@
// 为每个图表添加事件监听
const addChartSyncEvents = (chartContainer, chart) => {
console.log('为图表添加同步事件监听:', chart === mainChart ? '主图' : chart === volumeChart ? '成交量图' : 'MACD图');
console.log('为图表添加同步事件监听:',
chart === mainChart ? '主图' :
chart === volumeChart ? '成交量图' :
chart === macdChart ? 'MACD图' : 'ATR图');
// 确定当前图表类型
const chartType = chart === mainChart ? 'main' : chart === volumeChart ? 'volume' : 'macd';
const chartType = chart === mainChart ? 'main' :
chart === volumeChart ? 'volume' :
chart === macdChart ? 'macd' : 'atr';
// 使用LightweightCharts内置的时间范围变化事件(这是最可靠的方法)
chart.timeScale().subscribeVisibleTimeRangeChange(() => {
@@ -1890,6 +2041,9 @@
if (showMacd && macdChart) {
addChartSyncEvents(macdChartContainer, macdChart);
}
if (showMacd && atrChart) {
addChartSyncEvents(atrChartContainer, atrChart);
}
// 窗口大小变化时重绘图表
window.addEventListener('resize', () => {
@@ -1913,6 +2067,14 @@
});
}
// 调整ATR图大小
if (showMacd && atrChart && atrChartContainer) {
atrChart.applyOptions({
width: atrChartContainer.clientWidth,
height: atrChartContainer.clientHeight
});
}
// 重新同步 - 使用主图作为同步源
setTimeout(() => {
if (mainChart) {
@@ -3035,15 +3197,50 @@
if (showMacd && macdChart) {
macdChart.timeScale().fitContent();
}
if (showMacd && atrChart) {
atrChart.timeScale().fitContent();
}
// 保存图表对象
tvWidget.mainChart = mainChart;
tvWidget.volumeChart = volumeChart;
tvWidget.macdChart = macdChart;
tvWidget.atrChart = atrChart;
tvWidget.state.isInitialized = true;
// 绑定同步事件
bindSyncEvents(mainChartContainer, volumeChartContainer, macdChartContainer, mainChart, volumeChart, macdChart, showMacd);
bindSyncEvents(mainChartContainer, volumeChartContainer, macdChartContainer, atrChartContainer, mainChart, volumeChart, macdChart, atrChart, showMacd);
// 强制同步所有图表时间范围(确保时间对齐)
setTimeout(() => {
if (mainChart && mainChart.timeScale) {
// 先让所有图表自适应内容
mainChart.timeScale().fitContent();
if (volumeChart) volumeChart.timeScale().fitContent();
if (showMacd && macdChart) macdChart.timeScale().fitContent();
if (showMacd && atrChart) atrChart.timeScale().fitContent();
// 等待适应完成后强制同步时间范围
setTimeout(() => {
const mainRange = mainChart.timeScale().getVisibleLogicalRange();
console.log('主图时间范围:', mainRange);
if (mainRange && volumeChart && volumeChart.timeScale) {
volumeChart.timeScale().setVisibleLogicalRange(mainRange);
console.log('成交量图时间范围已同步');
}
if (mainRange && showMacd && macdChart && macdChart.timeScale) {
macdChart.timeScale().setVisibleLogicalRange(mainRange);
console.log('MACD图时间范围已同步');
}
if (mainRange && showMacd && atrChart && atrChart.timeScale) {
atrChart.timeScale().setVisibleLogicalRange(mainRange);
console.log('ATR图时间范围已同步');
}
console.log('所有图表时间范围已强制同步完成');
}, 50);
}
}, 200);
// 只有在时间输入框都为空时才设置图表默认时间范围
if (!$('#start_time').val() && !$('#end_time').val()) {
@@ -3051,7 +3248,7 @@
}
// 添加买卖点提示
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, macdChartContainer, volumeChart, macdChart, showMacd);
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, macdChartContainer, atrChartContainer, volumeChart, macdChart, atrChart, showMacd);
console.log('图表初始化完成');
} catch (e) {
@@ -3128,18 +3325,9 @@
tvWidget.series.lineSeries.setData(lineData);
}
// 更新成交量数据
// 更新成交量数据 - 强制使用主周期数据源确保时间对齐
let volumes = [];
if (useElementPeriod && currentData.element_kline_data && Array.isArray(currentData.element_kline_data)) {
volumes = currentData.element_kline_data.map(kline => {
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
return {
time: timestamp,
value: parseFloat(kline.volume),
color: parseFloat(kline.close) >= parseFloat(kline.open) ? 'rgba(220, 53, 69, 0.5)' : 'rgba(40, 167, 69, 0.5)',
};
});
} else if (currentData.kline_data && Array.isArray(currentData.kline_data)) {
if (currentData.kline_data && Array.isArray(currentData.kline_data)) {
volumes = currentData.kline_data.map(kline => {
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
return {
@@ -3191,6 +3379,26 @@
tvWidget.series.histogramSeries.setData(histogramData);
}
// 更新ATR数据 - 保持时间轴对齐
if (currentData.kline_data && Array.isArray(currentData.kline_data) && tvWidget.series.atrLineSeries) {
const atrData = [];
for (let i = 0; i < currentData.kline_data.length; i++) {
const kline = currentData.kline_data[i];
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
// 只添加ATR>0的有效数据,不显示ATR=0的点
if (kline.atr && kline.atr > 0) {
atrData.push({
time: timestamp,
value: kline.atr
});
}
}
tvWidget.series.atrLineSeries.setData(atrData);
}
// 先恢复可视范围,避免先显示默认范围再跳转的跳跃效果
if (tvWidget.mainChart && (tvWidget.state.logicalRange || tvWidget.state.visibleRange)) {
console.log('回放模式:优先恢复视图范围以避免跳跃');
@@ -3206,10 +3414,12 @@
tvWidget.mainChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange);
if (tvWidget.volumeChart) tvWidget.volumeChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange);
if (tvWidget.macdChart) tvWidget.macdChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange);
if (tvWidget.atrChart) tvWidget.atrChart.timeScale().setVisibleLogicalRange(tvWidget.state.logicalRange);
} else if (tvWidget.state.visibleRange) {
tvWidget.mainChart.timeScale().setVisibleRange(tvWidget.state.visibleRange);
if (tvWidget.volumeChart) tvWidget.volumeChart.timeScale().setVisibleRange(tvWidget.state.visibleRange);
if (tvWidget.macdChart) tvWidget.macdChart.timeScale().setVisibleRange(tvWidget.state.visibleRange);
if (tvWidget.atrChart) tvWidget.atrChart.timeScale().setVisibleRange(tvWidget.state.visibleRange);
}
}, 20);
}
@@ -3231,7 +3441,7 @@
}
}
function bindSyncEvents(mainChartContainer, volumeChartContainer, macdChartContainer, mainChart, volumeChart, macdChart, showMacd) {
function bindSyncEvents(mainChartContainer, volumeChartContainer, macdChartContainer, atrChartContainer, mainChart, volumeChart, macdChart, atrChart, showMacd) {
// 防止同步过程中的无限循环
let syncInProgress = false;
@@ -3239,7 +3449,8 @@
let localDragStates = {
main: false,
volume: false,
macd: false
macd: false,
atr: false
};
// 同步图表的时间范围
@@ -3253,7 +3464,8 @@
syncInProgress = true;
console.log('🚀 开始同步图表,来源:',
sourceChart === mainChart ? '主图' :
sourceChart === volumeChart ? '成交量图' : 'MACD图');
sourceChart === volumeChart ? '成交量图' :
sourceChart === macdChart ? 'MACD图' : 'ATR图');
try {
if (sourceChart && sourceChart.timeScale) {
@@ -3292,6 +3504,16 @@
}
}
// 同步ATR图
if (showMacd && atrChart && sourceChart !== atrChart && atrChart.timeScale) {
try {
atrChart.timeScale().setVisibleLogicalRange(logicalRange);
console.log('✅ ATR图同步完成');
} catch (e) {
console.error('❌ ATR图同步失败:', e);
}
}
// 保存当前的可见范围到全局状态
if (tvWidget && tvWidget.state) {
tvWidget.state.logicalRange = logicalRange;
@@ -3315,10 +3537,15 @@
// 为每个图表添加事件监听
const addChartSyncEvents = (chartContainer, chart) => {
console.log('为图表添加同步事件监听:', chart === mainChart ? '主图' : chart === volumeChart ? '成交量图' : 'MACD图');
console.log('为图表添加同步事件监听:',
chart === mainChart ? '主图' :
chart === volumeChart ? '成交量图' :
chart === macdChart ? 'MACD图' : 'ATR图');
// 确定当前图表类型
const chartType = chart === mainChart ? 'main' : chart === volumeChart ? 'volume' : 'macd';
const chartType = chart === mainChart ? 'main' :
chart === volumeChart ? 'volume' :
chart === macdChart ? 'macd' : 'atr';
// 使用LightweightCharts内置的时间范围变化事件(这是最可靠的方法)
chart.timeScale().subscribeVisibleTimeRangeChange(() => {
@@ -3381,6 +3608,9 @@
if (showMacd && macdChartContainer && macdChart) {
addChartSyncEvents(macdChartContainer, macdChart);
}
if (showMacd && atrChartContainer && atrChart) {
addChartSyncEvents(atrChartContainer, atrChart);
}
// 窗口大小变化时重绘图表
window.addEventListener('resize', () => {
@@ -3408,6 +3638,14 @@
});
}
// 调整ATR图大小
if (showMacd && atrChart && atrChartContainer) {
atrChart.applyOptions({
width: atrChartContainer.clientWidth,
height: atrChartContainer.clientHeight
});
}
// 重新同步 - 使用主图作为同步源
setTimeout(() => {
if (mainChart) {
@@ -3417,7 +3655,7 @@
});
}
function setupTooltip(mainChart, buyMarkers = [], sellMarkers = [], mainChartContainer, volumeChartContainer, macdChartContainer, volumeChart, macdChart, showMacd) {
function setupTooltip(mainChart, buyMarkers = [], sellMarkers = [], mainChartContainer, volumeChartContainer, macdChartContainer, atrChartContainer, volumeChart, macdChart, atrChart, showMacd) {
// 调试变量
window.debugMode = true;
@@ -3451,6 +3689,8 @@
existingVolumeLines.forEach(line => line.remove());
const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
existingMacdLines.forEach(line => line.remove());
const existingAtrLines = document.querySelectorAll('.atr-crosshair-line');
existingAtrLines.forEach(line => line.remove());
// 获取时间对应的坐标位置
const mainTimeCoordinate = mainChart.timeScale().timeToCoordinate(param.time);
@@ -3495,6 +3735,26 @@
document.body.appendChild(macdLine);
}
}
// 如果有ATR图,也在ATR图上绘制垂直线
if (showMacd && atrChart && atrChartContainer) {
const atrTimeCoordinate = atrChart.timeScale().timeToCoordinate(param.time);
if (atrTimeCoordinate !== null) {
const atrChartRect = atrChartContainer.getBoundingClientRect();
const atrLine = document.createElement('div');
atrLine.className = 'atr-crosshair-line';
atrLine.style.position = 'fixed'; // 改为fixed定位
atrLine.style.left = (atrChartRect.left + atrTimeCoordinate) + 'px';
atrLine.style.top = atrChartRect.top + 'px';
atrLine.style.width = '1px';
atrLine.style.height = atrChartRect.height + 'px';
atrLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
atrLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)';
atrLine.style.pointerEvents = 'none';
atrLine.style.zIndex = '1000';
document.body.appendChild(atrLine);
}
}
}
} catch (e) {
console.debug('十字线同步出错:', e);
@@ -3506,6 +3766,8 @@
existingVolumeLines.forEach(line => line.remove());
const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
existingMacdLines.forEach(line => line.remove());
const existingAtrLines = document.querySelectorAll('.atr-crosshair-line');
existingAtrLines.forEach(line => line.remove());
} catch (e) {
console.debug('清除十字线时出错:', e);
}
@@ -4037,6 +4299,7 @@
tvWidget.mainChart = null;
tvWidget.volumeChart = null;
tvWidget.macdChart = null;
tvWidget.atrChart = null;
// 重置系列数据
tvWidget.series = {
candleSeries: null,
@@ -4045,6 +4308,7 @@
macdLineSeries: null,
signalLineSeries: null,
histogramSeries: null,
atrLineSeries: null,
mainBiSeries: [],
mainSegSeries: [],
mainZsSeries: [],