添加布林带显示
This commit is contained in:
+1
-1
@@ -370,7 +370,7 @@ def analyze_chan(df, timeframe='1d'):
|
|||||||
"divergence_rate": float("inf"),
|
"divergence_rate": float("inf"),
|
||||||
"bsp2_follow_1": False,
|
"bsp2_follow_1": False,
|
||||||
"bsp3_follow_1": False,
|
"bsp3_follow_1": False,
|
||||||
"min_zs_cnt": 1,
|
"min_zs_cnt": 2,
|
||||||
"bs1_peak": False,
|
"bs1_peak": False,
|
||||||
"macd_algo": "peak",
|
"macd_algo": "peak",
|
||||||
"bs_type": '1,2,3a,1p,2s,3b',
|
"bs_type": '1,2,3a,1p,2s,3b',
|
||||||
|
|||||||
+575
-10
@@ -371,6 +371,18 @@
|
|||||||
.ma-indicator-value {
|
.ma-indicator-value {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 布林带指标样式 */
|
||||||
|
.bb-indicator-colors {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb-values {
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -496,14 +508,13 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="showTradePoints" checked>
|
<input class="form-check-input" type="checkbox" id="showTradePoints" checked>
|
||||||
<label class="form-check-label" for="showTradePoints">买卖点</label>
|
<label class="form-check-label" for="showTradePoints">买卖点</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="d-inline-block">
|
||||||
<input class="form-check-input" type="checkbox" id="showMainBollinger">
|
|
||||||
<label class="form-check-label" for="showMainBollinger">布林带</label>
|
|
||||||
</div>
|
|
||||||
<div class="d-inline-block ms-3">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary" id="addMovingAverageBtn" title="添加均线">
|
<button type="button" class="btn btn-sm btn-outline-primary" id="addMovingAverageBtn" title="添加均线">
|
||||||
<i class="bi bi-graph-up"></i> 均线
|
<i class="bi bi-graph-up"></i> 均线
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-info ms-2" id="addBollingerBandBtn" title="添加布林带">
|
||||||
|
<i class="bi bi-activity"></i> 布林带
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" onclick="window.testMovingAverageSystem()" title="测试均线系统">
|
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" onclick="window.testMovingAverageSystem()" title="测试均线系统">
|
||||||
<i class="bi bi-bug"></i>
|
<i class="bi bi-bug"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -891,6 +902,10 @@
|
|||||||
let movingAverages = [];
|
let movingAverages = [];
|
||||||
let maCounter = 0;
|
let maCounter = 0;
|
||||||
|
|
||||||
|
// 布林带系统相关变量
|
||||||
|
let bollingerBands = [];
|
||||||
|
let bbCounter = 0;
|
||||||
|
|
||||||
// 均线计算函数
|
// 均线计算函数
|
||||||
function calculateMovingAverage(data, period, type = 'SMA') {
|
function calculateMovingAverage(data, period, type = 'SMA') {
|
||||||
if (!data || data.length < period) return [];
|
if (!data || data.length < period) return [];
|
||||||
@@ -1007,6 +1022,39 @@
|
|||||||
return calculateMovingAverage(data, smoothingPeriod, smoothingType);
|
return calculateMovingAverage(data, smoothingPeriod, smoothingType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 布林带计算函数
|
||||||
|
function calculateBollingerBands(data, period, multiplier = 2) {
|
||||||
|
if (!data || data.length < period) return { middle: [], upper: [], lower: [] };
|
||||||
|
|
||||||
|
const middle = [];
|
||||||
|
const upper = [];
|
||||||
|
const lower = [];
|
||||||
|
|
||||||
|
for (let i = period - 1; i < data.length; i++) {
|
||||||
|
// 计算移动平均线(中线)
|
||||||
|
const sum = data.slice(i - period + 1, i + 1).reduce((acc, val) => acc + val.value, 0);
|
||||||
|
const ma = sum / period;
|
||||||
|
|
||||||
|
// 计算标准差
|
||||||
|
const variance = data.slice(i - period + 1, i + 1).reduce((acc, val) => {
|
||||||
|
return acc + Math.pow(val.value - ma, 2);
|
||||||
|
}, 0) / period;
|
||||||
|
const stdDev = Math.sqrt(variance);
|
||||||
|
|
||||||
|
// 计算上下轨
|
||||||
|
const upperValue = ma + (multiplier * stdDev);
|
||||||
|
const lowerValue = ma - (multiplier * stdDev);
|
||||||
|
|
||||||
|
const timestamp = data[i].time;
|
||||||
|
|
||||||
|
middle.push({ time: timestamp, value: ma });
|
||||||
|
upper.push({ time: timestamp, value: upperValue });
|
||||||
|
lower.push({ time: timestamp, value: lowerValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { middle, upper, lower };
|
||||||
|
}
|
||||||
|
|
||||||
// 添加均线到图表
|
// 添加均线到图表
|
||||||
function addMovingAverageToChart(config) {
|
function addMovingAverageToChart(config) {
|
||||||
const ma = {
|
const ma = {
|
||||||
@@ -1108,19 +1156,20 @@
|
|||||||
|
|
||||||
// 更新均线指标显示
|
// 更新均线指标显示
|
||||||
function updateMovingAverageIndicators() {
|
function updateMovingAverageIndicators() {
|
||||||
console.log('更新均线指标显示,当前均线数量:', movingAverages.length);
|
console.log('更新指标显示,当前均线数量:', movingAverages.length, '布林带数量:', bollingerBands.length);
|
||||||
try {
|
try {
|
||||||
const container = $('#movingAverageIndicators');
|
const container = $('#movingAverageIndicators');
|
||||||
const list = $('#maIndicatorsList');
|
const list = $('#maIndicatorsList');
|
||||||
|
|
||||||
if (movingAverages.length === 0) {
|
if (movingAverages.length === 0 && bollingerBands.length === 0) {
|
||||||
container.hide();
|
container.hide();
|
||||||
console.log('没有均线,隐藏指标容器');
|
console.log('没有指标,隐藏指标容器');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.empty();
|
list.empty();
|
||||||
|
|
||||||
|
// 显示均线
|
||||||
movingAverages.forEach((ma, index) => {
|
movingAverages.forEach((ma, index) => {
|
||||||
console.log(`创建均线指标 ${index + 1}:`, ma.id, ma.type, ma.period);
|
console.log(`创建均线指标 ${index + 1}:`, ma.id, ma.type, ma.period);
|
||||||
const latestValue = ma.data && ma.data.length > 0 ? ma.data[ma.data.length - 1].value : 0;
|
const latestValue = ma.data && ma.data.length > 0 ? ma.data[ma.data.length - 1].value : 0;
|
||||||
@@ -1148,10 +1197,47 @@
|
|||||||
list.append(indicator);
|
list.append(indicator);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 显示布林带
|
||||||
|
bollingerBands.forEach((bb, index) => {
|
||||||
|
console.log(`创建布林带指标 ${index + 1}:`, bb.id, bb.period, bb.multiplier);
|
||||||
|
const upperValue = bb.data && bb.data.upper && bb.data.upper.length > 0 ? bb.data.upper[bb.data.upper.length - 1].value : 0;
|
||||||
|
const middleValue = bb.data && bb.data.middle && bb.data.middle.length > 0 ? bb.data.middle[bb.data.middle.length - 1].value : 0;
|
||||||
|
const lowerValue = bb.data && bb.data.lower && bb.data.lower.length > 0 ? bb.data.lower[bb.data.lower.length - 1].value : 0;
|
||||||
|
|
||||||
|
const indicator = $(`
|
||||||
|
<div class="ma-indicator-item" data-bb-id="${bb.id}" style="opacity: ${bb.visible ? 1 : 0.5}">
|
||||||
|
<div class="bb-indicator-colors">
|
||||||
|
<div class="ma-indicator-color" style="background-color: ${bb.upperColor}; width: 8px; height: 8px; margin-right: 2px;"></div>
|
||||||
|
<div class="ma-indicator-color" style="background-color: ${bb.middleColor}; width: 8px; height: 8px; margin-right: 2px;"></div>
|
||||||
|
<div class="ma-indicator-color" style="background-color: ${bb.lowerColor}; width: 8px; height: 8px;"></div>
|
||||||
|
</div>
|
||||||
|
<span>BOLL(${bb.period},${bb.multiplier})</span>
|
||||||
|
<div class="bb-values" style="display: flex; flex-direction: column; font-size: 11px;">
|
||||||
|
<span style="color: ${bb.upperColor}">上: ${upperValue.toFixed(2)}</span>
|
||||||
|
<span style="color: ${bb.middleColor}">中: ${middleValue.toFixed(2)}</span>
|
||||||
|
<span style="color: ${bb.lowerColor}">下: ${lowerValue.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="ma-indicator-controls">
|
||||||
|
<button class="ma-control-btn" onclick="window.toggleBollingerBand('${bb.id}')" title="${bb.visible ? '隐藏' : '显示'}">
|
||||||
|
<i class="bi ${bb.visible ? 'bi-eye-slash' : 'bi-eye'}"></i>
|
||||||
|
</button>
|
||||||
|
<button class="ma-control-btn" onclick="window.editBollingerBand('${bb.id}')" title="配置">
|
||||||
|
<i class="bi bi-gear"></i>
|
||||||
|
</button>
|
||||||
|
<button class="ma-control-btn" onclick="window.removeBollingerBand('${bb.id}')" title="删除">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
list.append(indicator);
|
||||||
|
});
|
||||||
|
|
||||||
container.show();
|
container.show();
|
||||||
console.log('均线指标显示更新完成');
|
console.log('指标显示更新完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新均线指标显示时出错:', error);
|
console.error('更新指标显示时出错:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1258,10 +1344,260 @@
|
|||||||
updateMovingAverageIndicators();
|
updateMovingAverageIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加布林带到图表
|
||||||
|
function addBollingerBandToChart(config) {
|
||||||
|
const bb = {
|
||||||
|
id: `bb_${++bbCounter}`,
|
||||||
|
...config,
|
||||||
|
series: {
|
||||||
|
upper: null,
|
||||||
|
middle: null,
|
||||||
|
lower: null
|
||||||
|
},
|
||||||
|
visible: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!currentData) {
|
||||||
|
console.error('没有数据,无法添加布林带');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取价格数据
|
||||||
|
const priceData = getPriceData(null, config.source);
|
||||||
|
|
||||||
|
// 计算布林带
|
||||||
|
const bbData = calculateBollingerBands(priceData, config.period, config.multiplier);
|
||||||
|
|
||||||
|
// 添加到图表
|
||||||
|
const upperSeries = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: config.upperColor,
|
||||||
|
lineWidth: config.lineWidth,
|
||||||
|
lineStyle: config.style === 'dashed' ? 1 : config.style === 'dotted' ? 3 : 0,
|
||||||
|
title: `BOLL上轨(${config.period},${config.multiplier})`,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const middleSeries = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: config.middleColor,
|
||||||
|
lineWidth: config.lineWidth,
|
||||||
|
lineStyle: config.style === 'dashed' ? 1 : config.style === 'dotted' ? 3 : 0,
|
||||||
|
title: `BOLL中轨(${config.period})`,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const lowerSeries = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: config.lowerColor,
|
||||||
|
lineWidth: config.lineWidth,
|
||||||
|
lineStyle: config.style === 'dashed' ? 1 : config.style === 'dotted' ? 3 : 0,
|
||||||
|
title: `BOLL下轨(${config.period},${config.multiplier})`,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
upperSeries.setData(bbData.upper);
|
||||||
|
middleSeries.setData(bbData.middle);
|
||||||
|
lowerSeries.setData(bbData.lower);
|
||||||
|
|
||||||
|
bb.series.upper = upperSeries;
|
||||||
|
bb.series.middle = middleSeries;
|
||||||
|
bb.series.lower = lowerSeries;
|
||||||
|
bb.data = bbData;
|
||||||
|
|
||||||
|
bollingerBands.push(bb);
|
||||||
|
tvWidget.series.movingAverageSeries.push(upperSeries, middleSeries, lowerSeries);
|
||||||
|
|
||||||
|
// 更新显示
|
||||||
|
updateMovingAverageIndicators();
|
||||||
|
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除布林带
|
||||||
|
function removeBollingerBand(bbId) {
|
||||||
|
console.log('删除布林带:', bbId);
|
||||||
|
try {
|
||||||
|
const index = bollingerBands.findIndex(bb => bb.id === bbId);
|
||||||
|
if (index !== -1) {
|
||||||
|
const bb = bollingerBands[index];
|
||||||
|
console.log('找到要删除的布林带:', bb);
|
||||||
|
|
||||||
|
if (bb.series && tvWidget.mainChart) {
|
||||||
|
// 删除三条线
|
||||||
|
tvWidget.mainChart.removeSeries(bb.series.upper);
|
||||||
|
tvWidget.mainChart.removeSeries(bb.series.middle);
|
||||||
|
tvWidget.mainChart.removeSeries(bb.series.lower);
|
||||||
|
|
||||||
|
// 从数组中移除
|
||||||
|
const removeFromArray = (arr, series) => {
|
||||||
|
const seriesIndex = arr.indexOf(series);
|
||||||
|
if (seriesIndex !== -1) {
|
||||||
|
arr.splice(seriesIndex, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
removeFromArray(tvWidget.series.movingAverageSeries, bb.series.upper);
|
||||||
|
removeFromArray(tvWidget.series.movingAverageSeries, bb.series.middle);
|
||||||
|
removeFromArray(tvWidget.series.movingAverageSeries, bb.series.lower);
|
||||||
|
}
|
||||||
|
bollingerBands.splice(index, 1);
|
||||||
|
updateMovingAverageIndicators();
|
||||||
|
console.log('布林带删除成功');
|
||||||
|
} else {
|
||||||
|
console.warn('未找到要删除的布林带:', bbId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除布林带时出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换布林带显示/隐藏
|
||||||
|
function toggleBollingerBand(bbId) {
|
||||||
|
console.log('切换布林带显示状态:', bbId);
|
||||||
|
try {
|
||||||
|
const bb = bollingerBands.find(bb => bb.id === bbId);
|
||||||
|
if (bb && bb.series) {
|
||||||
|
bb.visible = !bb.visible;
|
||||||
|
console.log('布林带新状态:', bb.visible ? '显示' : '隐藏');
|
||||||
|
bb.series.upper.applyOptions({ visible: bb.visible });
|
||||||
|
bb.series.middle.applyOptions({ visible: bb.visible });
|
||||||
|
bb.series.lower.applyOptions({ visible: bb.visible });
|
||||||
|
updateMovingAverageIndicators();
|
||||||
|
console.log('布林带状态切换成功');
|
||||||
|
} else {
|
||||||
|
console.warn('未找到布林带或series不存在:', bbId, bb);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('切换布林带显示状态时出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑布林带配置
|
||||||
|
function editBollingerBand(bbId) {
|
||||||
|
console.log('编辑布林带配置:', bbId);
|
||||||
|
try {
|
||||||
|
const bb = bollingerBands.find(bb => bb.id === bbId);
|
||||||
|
if (!bb) {
|
||||||
|
console.warn('未找到要编辑的布林带:', bbId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('找到布林带配置:', bb);
|
||||||
|
|
||||||
|
// 填充表单
|
||||||
|
$('#bbPeriod').val(bb.period);
|
||||||
|
$('#bbMultiplier').val(bb.multiplier);
|
||||||
|
$('#bbSource').val(bb.source);
|
||||||
|
$('#bbUpperColor').val(bb.upperColor);
|
||||||
|
$('#bbMiddleColor').val(bb.middleColor);
|
||||||
|
$('#bbLowerColor').val(bb.lowerColor);
|
||||||
|
$('#bbLineWidth').val(bb.lineWidth);
|
||||||
|
$('#bbLineWidthDisplay').text(bb.lineWidth);
|
||||||
|
$('#bbStyle').val(bb.style);
|
||||||
|
|
||||||
|
// 更新确认按钮
|
||||||
|
$('#addBollingerBandConfirm').text('更新布林带').data('editId', bbId);
|
||||||
|
|
||||||
|
// 显示对话框
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('bollingerBandModal'));
|
||||||
|
modal.show();
|
||||||
|
console.log('编辑对话框已显示');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('编辑布林带配置时出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新所有布林带数据
|
||||||
|
function updateAllBollingerBands() {
|
||||||
|
if (!currentData) return;
|
||||||
|
|
||||||
|
console.log('更新所有布林带数据,布林带数量:', bollingerBands.length);
|
||||||
|
|
||||||
|
bollingerBands.forEach(bb => {
|
||||||
|
const priceData = getPriceData(null, bb.source);
|
||||||
|
|
||||||
|
if (priceData.length === 0) {
|
||||||
|
console.warn('布林带数据为空,跳过:', bb.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bbData = calculateBollingerBands(priceData, bb.period, bb.multiplier);
|
||||||
|
bb.data = bbData;
|
||||||
|
|
||||||
|
// 如果序列不存在或无效,重新创建
|
||||||
|
if ((!bb.series.upper || !bb.series.middle || !bb.series.lower) && tvWidget.mainChart) {
|
||||||
|
console.log('重新创建布林带序列:', bb.period, bb.multiplier);
|
||||||
|
try {
|
||||||
|
bb.series.upper = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: bb.upperColor,
|
||||||
|
lineWidth: bb.lineWidth,
|
||||||
|
lineStyle: bb.style === 'solid' ? 0 : bb.style === 'dotted' ? 1 : bb.style === 'dashed' ? 2 : 0,
|
||||||
|
title: `BOLL上轨(${bb.period},${bb.multiplier})`,
|
||||||
|
visible: bb.visible !== false,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
bb.series.middle = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: bb.middleColor,
|
||||||
|
lineWidth: bb.lineWidth,
|
||||||
|
lineStyle: bb.style === 'solid' ? 0 : bb.style === 'dotted' ? 1 : bb.style === 'dashed' ? 2 : 0,
|
||||||
|
title: `BOLL中轨(${bb.period})`,
|
||||||
|
visible: bb.visible !== false,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
bb.series.lower = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: bb.lowerColor,
|
||||||
|
lineWidth: bb.lineWidth,
|
||||||
|
lineStyle: bb.style === 'solid' ? 0 : bb.style === 'dotted' ? 1 : bb.style === 'dashed' ? 2 : 0,
|
||||||
|
title: `BOLL下轨(${bb.period},${bb.multiplier})`,
|
||||||
|
visible: bb.visible !== false,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!tvWidget.series.movingAverageSeries) {
|
||||||
|
tvWidget.series.movingAverageSeries = [];
|
||||||
|
}
|
||||||
|
tvWidget.series.movingAverageSeries.push(bb.series.upper, bb.series.middle, bb.series.lower);
|
||||||
|
console.log('布林带序列创建成功:', bb.id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建布林带序列失败:', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (!tvWidget.mainChart) {
|
||||||
|
console.warn('主图表不存在,跳过布林带更新:', bb.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置数据
|
||||||
|
if (bb.series.upper && bb.series.middle && bb.series.lower) {
|
||||||
|
bb.series.upper.setData(bbData.upper);
|
||||||
|
bb.series.middle.setData(bbData.middle);
|
||||||
|
bb.series.lower.setData(bbData.lower);
|
||||||
|
console.log(`布林带 BOLL(${bb.period},${bb.multiplier}) 数据已更新,数据点数:`, bbData.upper.length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateMovingAverageIndicators();
|
||||||
|
}
|
||||||
|
|
||||||
// 确保函数在全局作用域中可用
|
// 确保函数在全局作用域中可用
|
||||||
window.removeMovingAverage = removeMovingAverage;
|
window.removeMovingAverage = removeMovingAverage;
|
||||||
window.toggleMovingAverage = toggleMovingAverage;
|
window.toggleMovingAverage = toggleMovingAverage;
|
||||||
window.editMovingAverage = editMovingAverage;
|
window.editMovingAverage = editMovingAverage;
|
||||||
|
window.removeBollingerBand = removeBollingerBand;
|
||||||
|
window.toggleBollingerBand = toggleBollingerBand;
|
||||||
|
window.editBollingerBand = editBollingerBand;
|
||||||
|
|
||||||
// 测试函数 - 用于验证均线系统是否工作
|
// 测试函数 - 用于验证均线系统是否工作
|
||||||
window.testMovingAverageSystem = function() {
|
window.testMovingAverageSystem = function() {
|
||||||
@@ -1358,6 +1694,28 @@
|
|||||||
modal.show();
|
modal.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 布林带按钮点击事件
|
||||||
|
$('#addBollingerBandBtn').click(function() {
|
||||||
|
// 重置表单
|
||||||
|
$('#bollingerBandForm')[0].reset();
|
||||||
|
$('#bbPeriod').val(20);
|
||||||
|
$('#bbMultiplier').val(2);
|
||||||
|
$('#bbSource').val('close');
|
||||||
|
$('#bbUpperColor').val('#FF6B6B');
|
||||||
|
$('#bbMiddleColor').val('#4ECDC4');
|
||||||
|
$('#bbLowerColor').val('#45B7D1');
|
||||||
|
$('#bbLineWidth').val(1);
|
||||||
|
$('#bbLineWidthDisplay').text('1');
|
||||||
|
$('#bbStyle').val('solid');
|
||||||
|
|
||||||
|
// 重置确认按钮
|
||||||
|
$('#addBollingerBandConfirm').text('添加布林带').removeData('editId');
|
||||||
|
|
||||||
|
// 显示对话框
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('bollingerBandModal'));
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
|
||||||
// 平滑算法变更事件
|
// 平滑算法变更事件
|
||||||
$('#maSmoothing').change(function() {
|
$('#maSmoothing').change(function() {
|
||||||
const smoothing = $(this).val();
|
const smoothing = $(this).val();
|
||||||
@@ -1369,6 +1727,11 @@
|
|||||||
$('#lineWidthDisplay').text($(this).val());
|
$('#lineWidthDisplay').text($(this).val());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 布林带线宽滑块变更事件
|
||||||
|
$('#bbLineWidth').on('input', function() {
|
||||||
|
$('#bbLineWidthDisplay').text($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
// 确认添加/更新均线
|
// 确认添加/更新均线
|
||||||
$('#addMovingAverageConfirm').click(function() {
|
$('#addMovingAverageConfirm').click(function() {
|
||||||
const form = $('#movingAverageForm')[0];
|
const form = $('#movingAverageForm')[0];
|
||||||
@@ -1440,6 +1803,101 @@
|
|||||||
const modal = bootstrap.Modal.getInstance(document.getElementById('movingAverageModal'));
|
const modal = bootstrap.Modal.getInstance(document.getElementById('movingAverageModal'));
|
||||||
modal.hide();
|
modal.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 确认添加/更新布林带
|
||||||
|
$('#addBollingerBandConfirm').click(function() {
|
||||||
|
const editId = $(this).data('editId');
|
||||||
|
const config = {
|
||||||
|
period: parseInt($('#bbPeriod').val()),
|
||||||
|
multiplier: parseFloat($('#bbMultiplier').val()),
|
||||||
|
source: $('#bbSource').val(),
|
||||||
|
upperColor: $('#bbUpperColor').val(),
|
||||||
|
middleColor: $('#bbMiddleColor').val(),
|
||||||
|
lowerColor: $('#bbLowerColor').val(),
|
||||||
|
lineWidth: parseInt($('#bbLineWidth').val()),
|
||||||
|
style: $('#bbStyle').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (editId) {
|
||||||
|
// 更新现有布林带
|
||||||
|
const bb = bollingerBands.find(bb => bb.id === editId);
|
||||||
|
if (bb) {
|
||||||
|
// 删除旧系列
|
||||||
|
if (bb.series.upper && bb.series.middle && bb.series.lower) {
|
||||||
|
tvWidget.mainChart.removeSeries(bb.series.upper);
|
||||||
|
tvWidget.mainChart.removeSeries(bb.series.middle);
|
||||||
|
tvWidget.mainChart.removeSeries(bb.series.lower);
|
||||||
|
|
||||||
|
const removeFromArray = (arr, series) => {
|
||||||
|
const seriesIndex = arr.indexOf(series);
|
||||||
|
if (seriesIndex !== -1) {
|
||||||
|
arr.splice(seriesIndex, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
removeFromArray(tvWidget.series.movingAverageSeries, bb.series.upper);
|
||||||
|
removeFromArray(tvWidget.series.movingAverageSeries, bb.series.middle);
|
||||||
|
removeFromArray(tvWidget.series.movingAverageSeries, bb.series.lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新配置
|
||||||
|
Object.assign(bb, config);
|
||||||
|
|
||||||
|
// 重新计算和添加
|
||||||
|
const priceData = getPriceData(null, config.source);
|
||||||
|
const bbData = calculateBollingerBands(priceData, config.period, config.multiplier);
|
||||||
|
|
||||||
|
const upperSeries = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: config.upperColor,
|
||||||
|
lineWidth: config.lineWidth,
|
||||||
|
lineStyle: config.style === 'dashed' ? 1 : config.style === 'dotted' ? 3 : 0,
|
||||||
|
title: `BOLL上轨(${config.period},${config.multiplier})`,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const middleSeries = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: config.middleColor,
|
||||||
|
lineWidth: config.lineWidth,
|
||||||
|
lineStyle: config.style === 'dashed' ? 1 : config.style === 'dotted' ? 3 : 0,
|
||||||
|
title: `BOLL中轨(${config.period})`,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const lowerSeries = tvWidget.mainChart.addLineSeries({
|
||||||
|
color: config.lowerColor,
|
||||||
|
lineWidth: config.lineWidth,
|
||||||
|
lineStyle: config.style === 'dashed' ? 1 : config.style === 'dotted' ? 3 : 0,
|
||||||
|
title: `BOLL下轨(${config.period},${config.multiplier})`,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
upperSeries.setData(bbData.upper);
|
||||||
|
middleSeries.setData(bbData.middle);
|
||||||
|
lowerSeries.setData(bbData.lower);
|
||||||
|
|
||||||
|
bb.series.upper = upperSeries;
|
||||||
|
bb.series.middle = middleSeries;
|
||||||
|
bb.series.lower = lowerSeries;
|
||||||
|
bb.data = bbData;
|
||||||
|
|
||||||
|
tvWidget.series.movingAverageSeries.push(upperSeries, middleSeries, lowerSeries);
|
||||||
|
updateMovingAverageIndicators();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 添加新布林带
|
||||||
|
addBollingerBandToChart(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭对话框
|
||||||
|
const modal = bootstrap.Modal.getInstance(document.getElementById('bollingerBandModal'));
|
||||||
|
modal.hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 买卖点样式定义 - 根据desc字段直接显示
|
// 买卖点样式定义 - 根据desc字段直接显示
|
||||||
@@ -2063,6 +2521,16 @@
|
|||||||
});
|
});
|
||||||
console.log('已清空均线序列引用,配置保留:', movingAverages.length);
|
console.log('已清空均线序列引用,配置保留:', movingAverages.length);
|
||||||
|
|
||||||
|
// 清空所有布林带的序列引用
|
||||||
|
bollingerBands.forEach(bb => {
|
||||||
|
bb.series = {
|
||||||
|
upper: null,
|
||||||
|
middle: null,
|
||||||
|
lower: null
|
||||||
|
};
|
||||||
|
});
|
||||||
|
console.log('已清空布林带序列引用,配置保留:', bollingerBands.length);
|
||||||
|
|
||||||
tvWidget = {
|
tvWidget = {
|
||||||
mainChart: null,
|
mainChart: null,
|
||||||
volumeChart: null,
|
volumeChart: null,
|
||||||
@@ -4385,6 +4853,14 @@
|
|||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有布林带配置,用新数据重新计算和显示布林带
|
||||||
|
if (bollingerBands.length > 0) {
|
||||||
|
console.log('图表重新初始化后,更新布林带数据,布林带数量:', bollingerBands.length);
|
||||||
|
setTimeout(() => {
|
||||||
|
updateAllBollingerBands();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
// 窗口大小变化时重绘图表
|
// 窗口大小变化时重绘图表
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
// 调整主图大小
|
// 调整主图大小
|
||||||
@@ -6870,5 +7346,94 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 布林带配置对话框 -->
|
||||||
|
<div class="modal fade" id="bollingerBandModal" tabindex="-1" aria-labelledby="bollingerBandModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="bollingerBandModalLabel">配置布林带</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="bollingerBandForm">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbPeriod" class="form-label">周期长度</label>
|
||||||
|
<input type="number" class="form-control" id="bbPeriod" min="1" value="20" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbMultiplier" class="form-label">上下轨倍数</label>
|
||||||
|
<input type="number" class="form-control" id="bbMultiplier" min="0.1" step="0.1" value="2" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbSource" class="form-label">价格来源</label>
|
||||||
|
<select class="form-select" id="bbSource" required>
|
||||||
|
<option value="close">收盘价</option>
|
||||||
|
<option value="open">开盘价</option>
|
||||||
|
<option value="high">最高价</option>
|
||||||
|
<option value="low">最低价</option>
|
||||||
|
<option value="hl2">高低平均价 (HL2)</option>
|
||||||
|
<option value="hlc3">高低收平均价 (HLC3)</option>
|
||||||
|
<option value="ohlc4">开高低收平均价 (OHLC4)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbUpperColor" class="form-label">上轨颜色</label>
|
||||||
|
<input type="color" class="form-control form-control-color" id="bbUpperColor" value="#FF6B6B" title="选择上轨颜色">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbMiddleColor" class="form-label">中轨颜色</label>
|
||||||
|
<input type="color" class="form-control form-control-color" id="bbMiddleColor" value="#4ECDC4" title="选择中轨颜色">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbLowerColor" class="form-label">下轨颜色</label>
|
||||||
|
<input type="color" class="form-control form-control-color" id="bbLowerColor" value="#45B7D1" title="选择下轨颜色">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbLineWidth" class="form-label">线宽</label>
|
||||||
|
<input type="range" class="form-range" id="bbLineWidth" min="1" max="5" value="1">
|
||||||
|
<span id="bbLineWidthDisplay">1</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bbStyle" class="form-label">线型</label>
|
||||||
|
<select class="form-select" id="bbStyle">
|
||||||
|
<option value="solid">实线</option>
|
||||||
|
<option value="dashed">虚线</option>
|
||||||
|
<option value="dotted">点线</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||||
|
<button type="button" class="btn btn-info" id="addBollingerBandConfirm">添加布林带</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user