添加均线系统
This commit is contained in:
+742
-2
@@ -286,6 +286,186 @@
|
||||
}
|
||||
|
||||
/* 主次周期元素样式区分 */
|
||||
|
||||
/* 均线系统样式 */
|
||||
.ma-panel {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 1000;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
max-width: 300px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.ma-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.ma-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.ma-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ma-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ma-value {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.ma-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.ma-action-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.ma-action-btn:hover {
|
||||
background-color: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.ma-color-indicator {
|
||||
width: 12px;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.add-ma-btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 120px;
|
||||
z-index: 100;
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.add-ma-btn:hover {
|
||||
background-color: #0a58ca;
|
||||
}
|
||||
|
||||
/* 配置弹窗样式 */
|
||||
.ma-config-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 10000;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ma-config-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
width: 400px;
|
||||
max-width: 90vw;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.ma-config-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.ma-config-form {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.ma-config-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.ma-config-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.ma-config-input, .ma-config-select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ma-config-input:focus, .ma-config-select:focus {
|
||||
outline: none;
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.ma-config-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.ma-config-btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ma-config-btn-primary {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ma-config-btn-primary:hover {
|
||||
background-color: #0a58ca;
|
||||
}
|
||||
|
||||
.ma-config-btn-secondary {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ma-config-btn-secondary:hover {
|
||||
background-color: #5c636a;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -513,6 +693,12 @@
|
||||
|
||||
<div class="chart-container">
|
||||
<div id="tradingview_chart"></div>
|
||||
<!-- 添加均线按钮 -->
|
||||
<button class="add-ma-btn" onclick="showMAConfig()">
|
||||
<i class="bi bi-plus-lg"></i> 添加均线
|
||||
</button>
|
||||
<!-- 均线信息面板 -->
|
||||
<div id="maPanel" class="ma-panel" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<div class="data-container">
|
||||
@@ -1382,7 +1568,8 @@
|
||||
elementUncompletedZsSeries: [],
|
||||
tradePointSeries: [],
|
||||
mainBollingerSeries: [],
|
||||
elementBollingerSeries: []
|
||||
elementBollingerSeries: [],
|
||||
maSeries: [] // 添加均线系列数组
|
||||
},
|
||||
state: {
|
||||
isInitialized: false,
|
||||
@@ -3387,7 +3574,7 @@
|
||||
if (fx.fx_strength < 1.0) { // 降低阈值,让更多分型显示
|
||||
displayText = fx.fx_strength >= 0.8 ? '' : '' // 0.8以上显示点,0.8以下不显示文本
|
||||
}
|
||||
displayText = fx.fx_type.replace("TOP", "").replace("BOTTOM", "").replace("1", "").replace("2", "").replace("30", "");
|
||||
displayText = fx.fx_type.replace("TOP", "").replace("BOTTOM", "").replace("11", "").replace("21", "").replace("3", "");
|
||||
// 添加标记配置
|
||||
const markerConfig = {
|
||||
time: timestamp,
|
||||
@@ -3735,6 +3922,9 @@
|
||||
tvWidget.macdChart = macdChart;
|
||||
tvWidget.state.isInitialized = true;
|
||||
|
||||
// 添加均线到图表
|
||||
addMovingAveragesToChart(candles);
|
||||
|
||||
// 绑定同步事件
|
||||
bindSyncEvents(mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, mainChart, volumeChart, atrChart, macdChart, showMacd);
|
||||
|
||||
@@ -3843,6 +4033,9 @@
|
||||
tvWidget.series.lineSeries.setData(lineData);
|
||||
}
|
||||
|
||||
// 更新均线数据
|
||||
addMovingAveragesToChart(candles);
|
||||
|
||||
// 更新成交量数据
|
||||
let volumes = [];
|
||||
if (useElementPeriod && currentData.element_kline_data && Array.isArray(currentData.element_kline_data)) {
|
||||
@@ -6621,6 +6814,499 @@
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
// 均线系统全局变量
|
||||
let movingAverages = []; // 存储所有均线配置
|
||||
let maIdCounter = 0; // 均线ID计数器
|
||||
|
||||
// 显示均线配置弹窗
|
||||
function showMAConfig() {
|
||||
$('#maConfigModal').css('display', 'flex');
|
||||
// 重置表单
|
||||
$('#maType').val('SMA');
|
||||
$('#maLength').val(20);
|
||||
$('#maSource').val('close');
|
||||
$('#maSmoothType').val('none');
|
||||
$('#maSmoothLength').val(3).prop('disabled', true);
|
||||
$('#maColor').val(getRandomColor());
|
||||
}
|
||||
|
||||
// 隐藏均线配置弹窗
|
||||
function hideMAConfig() {
|
||||
$('#maConfigModal').css('display', 'none');
|
||||
}
|
||||
|
||||
// 获取随机颜色
|
||||
function getRandomColor() {
|
||||
const colors = ['#2962FF', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
|
||||
'#FECA57', '#48CAE4', '#F38BA8', '#A8DADC', '#F1C0E8'];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
|
||||
// 添加均线
|
||||
function addMovingAverage() {
|
||||
const config = {
|
||||
id: ++maIdCounter,
|
||||
type: $('#maType').val(),
|
||||
length: parseInt($('#maLength').val()),
|
||||
source: $('#maSource').val(),
|
||||
smoothType: $('#maSmoothType').val(),
|
||||
smoothLength: parseInt($('#maSmoothLength').val()),
|
||||
color: $('#maColor').val(),
|
||||
visible: true
|
||||
};
|
||||
|
||||
// 验证输入
|
||||
if (config.length < 1 || config.length > 200) {
|
||||
alert('均线长度必须在1-200之间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.smoothType !== 'none' && (config.smoothLength < 1 || config.smoothLength > 50)) {
|
||||
alert('平滑长度必须在1-50之间');
|
||||
return;
|
||||
}
|
||||
|
||||
movingAverages.push(config);
|
||||
hideMAConfig();
|
||||
|
||||
// 重新绘制图表以包含新均线
|
||||
if (typeof updateChart === 'function') {
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
|
||||
// 计算均线数据
|
||||
function calculateMA(data, type, length, source) {
|
||||
if (!data || data.length < length) return [];
|
||||
|
||||
const sourceData = data.map(candle => {
|
||||
switch(source) {
|
||||
case 'open': return candle.open;
|
||||
case 'high': return candle.high;
|
||||
case 'low': return candle.low;
|
||||
case 'close': return candle.close;
|
||||
case 'hl2': return (candle.high + candle.low) / 2;
|
||||
case 'hlc3': return (candle.high + candle.low + candle.close) / 3;
|
||||
case 'ohlc4': return (candle.open + candle.high + candle.low + candle.close) / 4;
|
||||
default: return candle.close;
|
||||
}
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (let i = length - 1; i < sourceData.length; i++) {
|
||||
let value;
|
||||
|
||||
switch(type) {
|
||||
case 'SMA':
|
||||
value = sourceData.slice(i - length + 1, i + 1)
|
||||
.reduce((sum, val) => sum + val, 0) / length;
|
||||
break;
|
||||
case 'EMA':
|
||||
if (i === length - 1) {
|
||||
// 第一个EMA值使用SMA
|
||||
value = sourceData.slice(0, length)
|
||||
.reduce((sum, val) => sum + val, 0) / length;
|
||||
} else {
|
||||
const multiplier = 2 / (length + 1);
|
||||
value = sourceData[i] * multiplier + result[result.length - 1].value * (1 - multiplier);
|
||||
}
|
||||
break;
|
||||
case 'WMA':
|
||||
let weightSum = 0;
|
||||
let valueSum = 0;
|
||||
for (let j = 0; j < length; j++) {
|
||||
const weight = j + 1;
|
||||
weightSum += weight;
|
||||
valueSum += sourceData[i - length + 1 + j] * weight;
|
||||
}
|
||||
value = valueSum / weightSum;
|
||||
break;
|
||||
default:
|
||||
value = sourceData[i];
|
||||
}
|
||||
|
||||
result.push({
|
||||
time: data[i].time,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 应用平滑处理
|
||||
function applySmoothToMA(maData, smoothType, smoothLength) {
|
||||
if (smoothType === 'none' || maData.length < smoothLength) {
|
||||
return maData;
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
for (let i = smoothLength - 1; i < maData.length; i++) {
|
||||
let value;
|
||||
|
||||
switch(smoothType) {
|
||||
case 'SMA':
|
||||
value = maData.slice(i - smoothLength + 1, i + 1)
|
||||
.reduce((sum, item) => sum + item.value, 0) / smoothLength;
|
||||
break;
|
||||
case 'EMA':
|
||||
if (i === smoothLength - 1) {
|
||||
value = maData.slice(0, smoothLength)
|
||||
.reduce((sum, item) => sum + item.value, 0) / smoothLength;
|
||||
} else {
|
||||
const multiplier = 2 / (smoothLength + 1);
|
||||
value = maData[i].value * multiplier + result[result.length - 1].value * (1 - multiplier);
|
||||
}
|
||||
break;
|
||||
case 'WMA':
|
||||
let weightSum = 0;
|
||||
let valueSum = 0;
|
||||
for (let j = 0; j < smoothLength; j++) {
|
||||
const weight = j + 1;
|
||||
weightSum += weight;
|
||||
valueSum += maData[i - smoothLength + 1 + j].value * weight;
|
||||
}
|
||||
value = valueSum / weightSum;
|
||||
break;
|
||||
default:
|
||||
value = maData[i].value;
|
||||
}
|
||||
|
||||
result.push({
|
||||
time: maData[i].time,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 更新均线面板
|
||||
function updateMAPanel() {
|
||||
const panel = $('#maPanel');
|
||||
|
||||
if (movingAverages.length === 0) {
|
||||
panel.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
let hasVisibleMA = false;
|
||||
|
||||
movingAverages.forEach(ma => {
|
||||
// 获取最新价格
|
||||
const latestValue = ma.data && ma.data.length > 0 ?
|
||||
ma.data[ma.data.length - 1].value.toFixed(2) : '--';
|
||||
|
||||
const smoothText = ma.smoothType !== 'none' ?
|
||||
`, ${ma.smoothType}(${ma.smoothLength})` : '';
|
||||
|
||||
// 显示所有均线(包括隐藏的),但用不同样式区分
|
||||
const itemStyle = ma.visible ? '' : 'opacity: 0.5;';
|
||||
const eyeIcon = ma.visible ? 'bi-eye' : 'bi-eye-slash';
|
||||
|
||||
html += `
|
||||
<div class="ma-item" data-ma-id="${ma.id}" style="${itemStyle}">
|
||||
<div class="ma-info">
|
||||
<div class="ma-color-indicator" style="background-color: ${ma.color}"></div>
|
||||
<span class="ma-label">${ma.type}(${ma.length})${smoothText}</span>
|
||||
<span class="ma-value">${ma.visible ? latestValue : '--'}</span>
|
||||
</div>
|
||||
<div class="ma-actions">
|
||||
<button class="ma-action-btn" onclick="toggleMAVisibility(${ma.id})" title="${ma.visible ? '隐藏' : '显示'}">
|
||||
<i class="bi ${eyeIcon}"></i>
|
||||
</button>
|
||||
<button class="ma-action-btn" onclick="configMA(${ma.id})" title="配置">
|
||||
<i class="bi bi-gear"></i>
|
||||
</button>
|
||||
<button class="ma-action-btn" onclick="deleteMA(${ma.id})" title="删除">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (ma.visible) {
|
||||
hasVisibleMA = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (html.trim() === '') {
|
||||
panel.hide();
|
||||
} else {
|
||||
panel.html(html);
|
||||
panel.show();
|
||||
}
|
||||
}
|
||||
|
||||
// 切换均线可见性
|
||||
function toggleMAVisibility(maId) {
|
||||
const ma = movingAverages.find(m => m.id === maId);
|
||||
if (ma) {
|
||||
ma.visible = !ma.visible;
|
||||
|
||||
// 如果图表已初始化,直接更新均线显示
|
||||
if (tvWidget.mainChart && currentData && currentData.kline_data) {
|
||||
// 获取K线数据
|
||||
const useElementPeriod = $('#elementPeriodKline').is(':checked') &&
|
||||
currentData.element_kline_data &&
|
||||
Array.isArray(currentData.element_kline_data);
|
||||
|
||||
let candles = [];
|
||||
if (useElementPeriod) {
|
||||
candles = currentData.element_kline_data.map((kline) => {
|
||||
const date = new Date(kline.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
return {
|
||||
time: timestamp,
|
||||
open: parseFloat(kline.open),
|
||||
high: parseFloat(kline.high),
|
||||
low: parseFloat(kline.low),
|
||||
close: parseFloat(kline.close),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
candles = currentData.kline_data.map((kline) => {
|
||||
const date = new Date(kline.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
return {
|
||||
time: timestamp,
|
||||
open: parseFloat(kline.open),
|
||||
high: parseFloat(kline.high),
|
||||
low: parseFloat(kline.low),
|
||||
close: parseFloat(kline.close),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 重新添加均线到图表
|
||||
addMovingAveragesToChart(candles);
|
||||
} else {
|
||||
// 如果图表未初始化,重新加载整个图表
|
||||
if (typeof updateChart === 'function') {
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 配置均线
|
||||
function configMA(maId) {
|
||||
const ma = movingAverages.find(m => m.id === maId);
|
||||
if (ma) {
|
||||
// 填充表单
|
||||
$('#maType').val(ma.type);
|
||||
$('#maLength').val(ma.length);
|
||||
$('#maSource').val(ma.source);
|
||||
$('#maSmoothType').val(ma.smoothType);
|
||||
$('#maSmoothLength').val(ma.smoothLength);
|
||||
$('#maColor').val(ma.color);
|
||||
|
||||
// 启用/禁用平滑长度输入
|
||||
$('#maSmoothLength').prop('disabled', ma.smoothType === 'none');
|
||||
|
||||
// 修改按钮为更新模式
|
||||
$('.ma-config-btn-primary').text('更新').off('click').on('click', function() {
|
||||
updateMovingAverage(maId);
|
||||
});
|
||||
|
||||
$('#maConfigModal').css('display', 'flex');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新均线
|
||||
function updateMovingAverage(maId) {
|
||||
const ma = movingAverages.find(m => m.id === maId);
|
||||
if (ma) {
|
||||
// 验证输入
|
||||
const newLength = parseInt($('#maLength').val());
|
||||
const newSmoothLength = parseInt($('#maSmoothLength').val());
|
||||
const newSmoothType = $('#maSmoothType').val();
|
||||
|
||||
if (newLength < 1 || newLength > 200) {
|
||||
alert('均线长度必须在1-200之间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newSmoothType !== 'none' && (newSmoothLength < 1 || newSmoothLength > 50)) {
|
||||
alert('平滑长度必须在1-50之间');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新配置
|
||||
ma.type = $('#maType').val();
|
||||
ma.length = newLength;
|
||||
ma.source = $('#maSource').val();
|
||||
ma.smoothType = newSmoothType;
|
||||
ma.smoothLength = newSmoothLength;
|
||||
ma.color = $('#maColor').val();
|
||||
|
||||
hideMAConfig();
|
||||
|
||||
// 恢复按钮为添加模式
|
||||
$('.ma-config-btn-primary').text('添加').off('click').on('click', addMovingAverage);
|
||||
|
||||
// 如果图表已初始化,直接更新均线显示
|
||||
if (tvWidget.mainChart && currentData && currentData.kline_data) {
|
||||
const useElementPeriod = $('#elementPeriodKline').is(':checked') &&
|
||||
currentData.element_kline_data &&
|
||||
Array.isArray(currentData.element_kline_data);
|
||||
|
||||
let candles = [];
|
||||
if (useElementPeriod) {
|
||||
candles = currentData.element_kline_data.map((kline) => {
|
||||
const date = new Date(kline.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
return {
|
||||
time: timestamp,
|
||||
open: parseFloat(kline.open),
|
||||
high: parseFloat(kline.high),
|
||||
low: parseFloat(kline.low),
|
||||
close: parseFloat(kline.close),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
candles = currentData.kline_data.map((kline) => {
|
||||
const date = new Date(kline.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
return {
|
||||
time: timestamp,
|
||||
open: parseFloat(kline.open),
|
||||
high: parseFloat(kline.high),
|
||||
low: parseFloat(kline.low),
|
||||
close: parseFloat(kline.close),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
addMovingAveragesToChart(candles);
|
||||
} else {
|
||||
if (typeof updateChart === 'function') {
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除均线
|
||||
function deleteMA(maId) {
|
||||
movingAverages = movingAverages.filter(m => m.id !== maId);
|
||||
|
||||
// 如果图表已初始化,直接更新均线显示
|
||||
if (tvWidget.mainChart && currentData && currentData.kline_data) {
|
||||
const useElementPeriod = $('#elementPeriodKline').is(':checked') &&
|
||||
currentData.element_kline_data &&
|
||||
Array.isArray(currentData.element_kline_data);
|
||||
|
||||
let candles = [];
|
||||
if (useElementPeriod) {
|
||||
candles = currentData.element_kline_data.map((kline) => {
|
||||
const date = new Date(kline.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
return {
|
||||
time: timestamp,
|
||||
open: parseFloat(kline.open),
|
||||
high: parseFloat(kline.high),
|
||||
low: parseFloat(kline.low),
|
||||
close: parseFloat(kline.close),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
candles = currentData.kline_data.map((kline) => {
|
||||
const date = new Date(kline.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
return {
|
||||
time: timestamp,
|
||||
open: parseFloat(kline.open),
|
||||
high: parseFloat(kline.high),
|
||||
low: parseFloat(kline.low),
|
||||
close: parseFloat(kline.close),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
addMovingAveragesToChart(candles);
|
||||
} else {
|
||||
if (typeof updateChart === 'function') {
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加均线到图表
|
||||
function addMovingAveragesToChart(candleData) {
|
||||
if (!tvWidget.mainChart || !candleData || candleData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除现有的均线系列
|
||||
if (tvWidget.series.maSeries) {
|
||||
tvWidget.series.maSeries.forEach(series => {
|
||||
try {
|
||||
tvWidget.mainChart.removeSeries(series);
|
||||
} catch (e) {
|
||||
console.debug('移除均线系列时出错:', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
tvWidget.series.maSeries = [];
|
||||
|
||||
// 为每个均线配置创建系列
|
||||
movingAverages.forEach(maConfig => {
|
||||
if (!maConfig.visible) return;
|
||||
|
||||
try {
|
||||
// 计算均线数据
|
||||
const maData = calculateMA(candleData, maConfig.type, maConfig.length, maConfig.source);
|
||||
|
||||
// 应用平滑处理
|
||||
const smoothedData = maConfig.smoothType !== 'none' ?
|
||||
applySmoothToMA(maData, maConfig.smoothType, maConfig.smoothLength) :
|
||||
maData;
|
||||
|
||||
// 创建线系列
|
||||
const maSeries = tvWidget.mainChart.addLineSeries({
|
||||
color: maConfig.color,
|
||||
lineWidth: 2,
|
||||
title: `${maConfig.type}(${maConfig.length})`,
|
||||
lastValueVisible: false,
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: true,
|
||||
});
|
||||
|
||||
// 设置数据
|
||||
maSeries.setData(smoothedData);
|
||||
|
||||
// 保存数据到配置中以便显示最新值
|
||||
maConfig.data = smoothedData;
|
||||
|
||||
// 保存系列引用
|
||||
tvWidget.series.maSeries.push(maSeries);
|
||||
|
||||
} catch (e) {
|
||||
console.error('添加均线时出错:', e, maConfig);
|
||||
}
|
||||
});
|
||||
|
||||
// 更新均线面板
|
||||
updateMAPanel();
|
||||
}
|
||||
|
||||
// 监听平滑类型变化
|
||||
$(document).on('change', '#maSmoothType', function() {
|
||||
const isNone = $(this).val() === 'none';
|
||||
$('#maSmoothLength').prop('disabled', isNone);
|
||||
});
|
||||
|
||||
// 点击弹窗外部关闭
|
||||
$(document).on('click', '#maConfigModal', function(e) {
|
||||
if (e.target === this) {
|
||||
hideMAConfig();
|
||||
}
|
||||
});
|
||||
|
||||
// 页面加载完成后初始化
|
||||
$(document).ready(function() {
|
||||
// 设置默认的筛选时间(最近7天)
|
||||
@@ -6680,5 +7366,59 @@
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 均线配置弹窗 -->
|
||||
<div id="maConfigModal" class="ma-config-modal">
|
||||
<div class="ma-config-content">
|
||||
<div class="ma-config-title">添加均线</div>
|
||||
<div class="ma-config-form">
|
||||
<div class="ma-config-group">
|
||||
<label class="ma-config-label">类型</label>
|
||||
<select id="maType" class="ma-config-select">
|
||||
<option value="SMA">SMA (简单移动平均)</option>
|
||||
<option value="EMA">EMA (指数移动平均)</option>
|
||||
<option value="WMA">WMA (加权移动平均)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ma-config-group">
|
||||
<label class="ma-config-label">长度</label>
|
||||
<input type="number" id="maLength" class="ma-config-input" value="20" min="1" max="200">
|
||||
</div>
|
||||
<div class="ma-config-group">
|
||||
<label class="ma-config-label">数据来源</label>
|
||||
<select id="maSource" class="ma-config-select">
|
||||
<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 class="ma-config-group">
|
||||
<label class="ma-config-label">平滑线类型</label>
|
||||
<select id="maSmoothType" class="ma-config-select">
|
||||
<option value="none">无平滑</option>
|
||||
<option value="SMA">SMA</option>
|
||||
<option value="EMA">EMA</option>
|
||||
<option value="WMA">WMA</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ma-config-group">
|
||||
<label class="ma-config-label">平滑长度</label>
|
||||
<input type="number" id="maSmoothLength" class="ma-config-input" value="3" min="1" max="50" disabled>
|
||||
</div>
|
||||
<div class="ma-config-group">
|
||||
<label class="ma-config-label">颜色</label>
|
||||
<input type="color" id="maColor" class="ma-config-input" value="#2962FF">
|
||||
</div>
|
||||
</div>
|
||||
<div class="ma-config-actions">
|
||||
<button class="ma-config-btn ma-config-btn-secondary" onclick="hideMAConfig()">取消</button>
|
||||
<button class="ma-config-btn ma-config-btn-primary" onclick="addMovingAverage()">添加</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user