添加布林带显示

This commit is contained in:
jackyu66git
2025-07-23 01:53:19 +08:00
parent ba5e452af7
commit c6d02d7802
2 changed files with 576 additions and 11 deletions
+1 -1
View File
@@ -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
View File
@@ -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>