Files
Chan/web/static/js/app/ui.js
T
jackyu66gitandCursor c22cd48f36 Web 加上资金面/情绪叠图,并收紧默认图面。
默认主/次/次次改为 45m/15m/5m、开始时间一周;SD/CD 按 hist 摆位置和箭头;去掉笔线段背驰与第四类勾选。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-12 02:25:44 +08:00

872 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ui.js */
function loadSymbols() {
$.get('/api/symbols', function(data) {
if (Array.isArray(data)) {
const $select = $('#symbol');
const currentSymbol = $select.val(); // 保存当前选中的值
$select.empty();
data.forEach(function(symbol) {
$select.append($('<option>', {
value: symbol,
text: symbol
}));
});
// 如果有保存的选中值,恢复它
if (currentSymbol && data.includes(currentSymbol)) {
$select.val(currentSymbol);
} else {
// 设置默认值为BTC/USDT:USDT
$select.val('BTC/USDT:USDT');
}
}
});
}
// 设置默认时间范围:现在倒退 1 周
function setDefaultTimeRange() {
const now = new Date();
const daysBack = 7;
const start = new Date(now.getTime() - (daysBack * 24 * 60 * 60 * 1000));
// 格式化为datetime-local输入框所需的格式 YYYY-MM-DDThh:mm
$('#end_time').val(formatDatetimeLocal(now));
$('#start_time').val(formatDatetimeLocal(start));
}
// 格式化日期为datetime-local输入框格式
function formatDatetimeLocal(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
// 页面加载时初始化
$(document).ready(function() {
// 从本地存储中恢复时区设置
const savedTimezone = localStorage.getItem('selectedTimezone');
if (savedTimezone) {
$('#timezone').val(savedTimezone);
console.log('从本地存储恢复时区设置:', savedTimezone);
}
// 初始化数据源切换:先按数据源重新拉取周期元信息,再切换 UI
$('#dataSource').on('change', function() {
const dataSource = $(this).val();
const apiSrc = dataSource === 'a_stock' ? 'a_stock' : 'crypto';
$.getJSON('/api/chart_metadata', { source: apiSrc })
.done(function(meta) {
applyChartMetadata(meta);
})
.always(function() {
if (dataSource === 'crypto') {
$('#cryptoSymbolContainer').show();
$('#astockSymbolContainer').hide();
if (window.astockStatusInterval) {
clearInterval(window.astockStatusInterval);
window.astockStatusInterval = null;
}
loadSymbols();
if (window.ChanDeriv) ChanDeriv.setVisible(true);
} else if (dataSource === 'a_stock') {
$('#cryptoSymbolContainer').hide();
$('#astockSymbolContainer').show();
loadAStockSymbols();
startAStockStatusUpdater();
if (window.ChanDeriv) ChanDeriv.setVisible(false);
}
});
});
// 检查初始数据源设置
const initialDataSource = $('#dataSource').val();
if (window.ChanDeriv) ChanDeriv.setVisible(initialDataSource !== 'a_stock');
if (initialDataSource === 'a_stock') {
$.getJSON('/api/chart_metadata', { source: 'a_stock' })
.done(function(meta) {
applyChartMetadata(meta);
})
.always(function() {
loadAStockSymbols();
startAStockStatusUpdater();
// A 股:metadata 完成后再拉数(下方不再重复 updateChart
setTimeout(function() {
analyzeChart({ reason: 'astock-init' });
}, 300);
});
}
// 加密货币:统一在文末单次 updateChart,避免重复请求
// 初始化交易对下拉菜单
$('#symbol').val('BTC/USDT:USDT');
$('#astockSymbol').val('000001');
if (initialDataSource !== 'a_stock') {
const mainDefault = window.DEFAULT_MAIN_TIMEFRAME || $('#timeframe option:first').val();
const elementDefault = window.DEFAULT_ELEMENT_TIMEFRAME || $('#elementTimeframe option:first').val();
if (mainDefault) {
$('#timeframe').val(mainDefault);
}
if (elementDefault) {
$('#elementTimeframe').val(elementDefault);
}
}
// 测试打印时区偏移量
console.log('当前时区偏移量 (UTC+8):', getTimezoneOffset('Asia/Shanghai'));
console.log('当前时区偏移量 (UTC):', getTimezoneOffset('UTC'));
const now = new Date();
console.log('当前时间UTC:', now.toUTCString());
console.log('当前时间本地:', now.toString());
console.log('当前时间戳(秒):', now.getTime()/1000);
console.log('UTC时间戳:', Math.floor(now.getTime()/1000));
// 设置默认时间范围
setDefaultTimeRange();
// 默认禁用买卖点显示
$('#showTradePoints').prop('checked', false);
// 尝试加载更多交易对
loadSymbols();
// 初始化图表:默认加密货币延迟拉取;若首屏为 A 股则在 chart_metadata 完成后再 analyze
if (initialDataSource !== 'a_stock') {
setTimeout(function() {
analyzeChart({ reason: 'crypto-init' });
}, 500);
}
// 初始化自动刷新功能
initAutoRefresh();
// 确保在文档加载完成后初始化时区设置
// 默认设置为Shanghai时区
if (!$('#timezone').val()) {
$('#timezone').val('Asia/Shanghai');
}
// 记录当前时区设置
console.log('页面加载完成,当前时区设置:', $('#timezone').val());
// 添加自定义事件处理 - 让时区选择变更立即生效
$('#timezone').on('change', function() {
const newTimezone = $(this).val();
console.log('时区已更改为:', newTimezone);
// 保存到本地存储,下次访问时自动使用
localStorage.setItem('selectedTimezone', newTimezone);
// 如果已有数据,重新渲染图表和表格
if (currentData) {
// 先销毁现有图表实例
if (tvWidget.mainChart) {
try {
// 清理EMA52系列
clearEMA52Series();
// 销毁主图表及其关联的线系列
tvWidget.mainChart = null;
tvWidget.volumeChart = null;
tvWidget.atrChart = null;
tvWidget.macdChart = null;
// 重置系列数据
tvWidget.series = {
candleSeries: null,
lineSeries: null,
barSeries: null,
areaSeries: null,
baselineSeries: null,
renkoSeries: null,
volumeSeries: null,
atrLineSeries: null,
macdLineSeries: null,
signalLineSeries: null,
histogramSeries: null,
mainBiSeries: [],
mainSegSeries: [],
mainZsSeries: [],
mainUncompletedZsSeries: [],
elementBiSeries: [],
elementSegSeries: [],
elementZsSeries: [],
elementUncompletedZsSeries: [],
tradePointSeries: [],
mainBollingerSeries: [],
elementBollingerSeries: [],
maSeries: [], // 添加均线系列
bbSeries: [], // 添加布林带系列
ema52Series: [] // 添加EMA52系列数组
};
} catch (e) {
console.error('销毁图表错误:', e);
}
}
// 使用新的时区重新初始化图表
initTradingView($('#symbol').val(), $('#timeframe').val());
// 重新渲染图表数据
renderChart();
// 更新表格
updateTables(currentData);
}
});
// 页面加载完成后初始化
$(document).ready(function() {
// 设置默认的筛选时间(最近7天)
const now = new Date();
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
// 添加页面滚动事件监听器,清除十字线延长线
$(window).on('scroll', function() {
try {
// 清除所有十字线延长线,防止它们跟着页面滚动
const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line');
existingVolumeLines.forEach(line => line.remove());
const existingAtrLines = document.querySelectorAll('.atr-crosshair-line');
existingAtrLines.forEach(line => line.remove());
const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
existingMacdLines.forEach(line => line.remove());
const existingChanMacdLines = document.querySelectorAll('.chanmacd-crosshair-line');
existingChanMacdLines.forEach(line => line.remove());
} catch (e) {
console.debug('清除滚动中的十字线时出错:', e);
}
});
});
});
// 自动刷新相关变量
let autoRefreshTimer = null;
let nextRefreshTime = null;
let autoRefreshTick = 0;
/** 自动刷新时,缠论全量重算间隔(毫秒);时间戳见 window._lastFullAnalyzeAt */
const AUTO_FULL_ANALYZE_MS = 60 * 1000;
// 初始化自动刷新功能
function initAutoRefresh() {
// 监听自动刷新勾选框变化
$('#autoRefresh').change(function() {
if ($(this).is(':checked')) {
startAutoRefresh();
} else {
stopAutoRefresh();
}
});
// 监听刷新频率变化
$('#refreshInterval').change(function() {
if ($('#autoRefresh').is(':checked')) {
// 如果自动刷新已开启,重启定时器
stopAutoRefresh();
startAutoRefresh();
}
});
}
// 开始自动刷新
function startAutoRefresh() {
// 停止已有的刷新定时器
stopAutoRefresh();
// 获取刷新频率(分钟)
const interval = parseFloat($('#refreshInterval').val()) || (5 / 60);
const intervalMs = interval * 60 * 1000;
console.log(`开始自动刷新,频率: ${interval}分钟 (${intervalMs}毫秒);缠论全量每 ${AUTO_FULL_ANALYZE_MS / 1000}s`);
// 计算下次刷新时间
nextRefreshTime = new Date(Date.now() + intervalMs);
updateNextRefreshTimeDisplay();
// 启动定时器
autoRefreshTick = 0;
// 进入实时模式:结束时间=现在,立刻走自动刷新全量(视窗由 autoRefreshChart 内 freeze 冻结)
updateEndTimeToNow();
autoRefreshChart({
mode: 'full',
incremental: false,
forceFullRebuild: true,
reason: 'auto-refresh-start'
});
autoRefreshTimer = setInterval(function() {
// 自动刷新专用:结束时间推进到现在
updateEndTimeToNow();
autoRefreshTick += 1;
const now = Date.now();
const lastFull = window._lastFullAnalyzeAt || 0;
const tf = $('#timeframe').val() || '4h';
const needFull = !lastFull || (now - lastFull >= AUTO_FULL_ANALYZE_MS) ||
(typeof isLiveBaselineStale === 'function' && isLiveBaselineStale(tf));
if (needFull) {
console.log('自动刷新 tick → live 全量');
autoRefreshChart({ mode: 'full', incremental: false, forceFullRebuild: true });
} else {
console.log('自动刷新 tick → recent 尾部');
autoRefreshChart({ mode: 'recent' });
}
nextRefreshTime = new Date(Date.now() + intervalMs);
updateNextRefreshTimeDisplay();
}, intervalMs);
// 启动倒计时显示
startCountdownDisplay();
// 显示下次刷新时间
$('#nextRefreshTime').show();
}
// 更新结束时间为当前时间
function updateEndTimeToNow() {
const now = new Date();
$('#end_time').val(formatDatetimeLocal(now));
console.log('已更新结束时间为当前时间:', formatDatetimeLocal(now));
}
// 停止自动刷新
function stopAutoRefresh() {
if (autoRefreshTimer) {
clearInterval(autoRefreshTimer);
autoRefreshTimer = null;
}
// 停止倒计时显示
clearInterval(countdownTimer);
countdownTimer = null;
// 隐藏下次刷新时间
$('#nextRefreshTime').hide();
}
// 更新下次刷新时间显示
function updateNextRefreshTimeDisplay() {
if (!nextRefreshTime) return;
const timeStr = nextRefreshTime.toLocaleTimeString();
$('#nextRefreshTime').text(`下次刷新: ${timeStr}`);
}
// 倒计时定时器
let countdownTimer = null;
// 启动倒计时显示
function startCountdownDisplay() {
// 清除已有的倒计时
if (countdownTimer) {
clearInterval(countdownTimer);
}
// 启动新的倒计时,每秒更新一次
countdownTimer = setInterval(function() {
if (!nextRefreshTime) return;
const now = new Date();
const diffMs = nextRefreshTime - now;
if (diffMs <= 0) {
// 已经到达或超过刷新时间,等待刷新发生
$('#nextRefreshTime').text('正在刷新...');
} else {
// 计算剩余时间
const diffSec = Math.floor(diffMs / 1000);
// 如果时间超过1分钟,显示分和秒
if (diffSec >= 60) {
const minutes = Math.floor(diffSec / 60);
const seconds = diffSec % 60;
// 格式化显示
const timeStr = `${minutes}${seconds.toString().padStart(2, '0')}秒后刷新`;
$('#nextRefreshTime').text(timeStr);
} else {
// 少于1分钟只显示秒数
const timeStr = `${diffSec}秒后刷新`;
$('#nextRefreshTime').text(timeStr);
}
}
}, 1000);
}
// 将时间周期映射到数值(保留此函数以供后端API调用)
function mapTimeframeToInterval(timeframe) {
const mapping = {
'1m': '1',
'3m': '3',
'5m': '5',
'15m': '15',
'30m': '30',
'45m': '45',
'1h': '60',
'2h': '120',
'4h': '240',
'6h': '360',
'8h': '480',
'12h': '720',
'1d': 'D',
'3d': '3D',
'1w': 'W',
'1M': 'M'
};
return mapping[timeframe] || '5';
}
// 只重绘分形元素(笔、线段、中枢),保留现有的K线、MACD和成交量
function redrawFractalElements() {
if (!tvWidget || !tvWidget.mainChart) return;
// 确保使用主周期的K线和MACD数据
if (currentData.original_kline_data) {
currentData.kline_data = currentData.original_kline_data;
}
if (currentData.original_macd) {
currentData.macd = currentData.original_macd;
}
delete currentData.original_kline_data;
delete currentData.original_macd;
if (typeof reinitTradingViewPreservingViewport === 'function') {
reinitTradingViewPreservingViewport();
} else {
initTradingView($('#symbol').val(), $('#timeframe').val());
}
}
// 只更新分形元素(笔、线段、中枢)的表格数据
function updateFractalTables() {
if (!currentData) return;
const data = currentData;
// 笔数据表更新
if (tables.bi) {
tables.bi.clear().destroy();
}
// 使用小周期笔数据(如果存在)
const biData = data.element_bi_list || data.bi_list;
const biSource = data.element_bi_list ? '元素周期' : '主周期';
console.log(`表格显示${biSource}笔数据,共${biData ? biData.length : 0}条`);
tables.bi = $('#biTable').DataTable({
data: biData || [],
order: [[0, 'desc']],
pageLength: 25,
columns: [
{ data: 'start_time', render: formatTime },
{ data: 'end_time', render: formatTime },
{ data: 'sure_time', render: formatConfirmTime },
{ data: 'start_price', render: formatPrice },
{ data: 'end_price', render: formatPrice },
{ data: 'direction', render: formatDirection },
{ data: 'macd_div', render: formatMacdValue }
]
});
// 线段数据表更新
if (tables.seg) {
tables.seg.clear().destroy();
}
// 使用小周期线段数据(如果存在)
const segData = data.element_seg_list || data.seg_list;
const segSource = data.element_seg_list ? '元素周期' : '主周期';
console.log(`表格显示${segSource}线段数据,共${segData ? segData.length : 0}条`);
tables.seg = $('#segTable').DataTable({
data: segData || [],
order: [[0, 'desc']],
pageLength: 25,
columns: [
{ data: 'start_time', render: formatTime },
{ data: 'end_time', render: formatTime },
{ data: 'sure_time', render: formatConfirmTime },
{ data: 'start_price', render: formatPrice },
{ data: 'end_price', render: formatPrice },
{ data: 'direction', render: formatDirection }
]
});
// 中枢数据表更新
if (tables.zs) {
tables.zs.clear().destroy();
}
// 使用小周期中枢数据(如果存在)
const zsData = data.element_zs_list || data.zs_list;
const zsSource = data.element_zs_list ? '元素周期' : '主周期';
console.log(`表格显示${zsSource}中枢数据,共${zsData ? zsData.length : 0}条`);
tables.zs = $('#zsTable').DataTable({
data: zsData || [],
order: [[0, 'desc']],
pageLength: 25,
columns: [
{ data: 'start_time', render: formatTime },
{ data: 'end_time', render: formatTime },
{ data: 'zg', render: formatPrice },
{ data: 'zd', render: formatPrice }
]
});
// 更新数据源信息
setupDataSourceInfo(data);
}
// 刷新图表并更新表格
function refreshChart(data, options) {
// 检查是否接收到数据
if (!data) {
console.error('未收到数据,无法刷新图表');
return;
}
if (data.element_timeframe) {
$('#elementTimeframe').val(data.element_timeframe);
}
options = options || {};
const preferIncremental = !!options.incremental;
const chartsReady = tvWidget && tvWidget.state && tvWidget.state.isInitialized && tvWidget.mainChart;
// 自动刷新:增量更新,避免每次销毁/重建 Lightweight Charts
if (preferIncremental && chartsReady) {
try {
// 视窗已在请求发出时 freezeChartViewportBeforeRequest 冻结,勿在此重拍(会弄错 barCount)
updateTradingViewData({ tailOnly: !!options.skipTables });
// recent-tail 刷新结构未变,跳过表格重绘以提速
if (!options.skipTables) {
updateTables(data);
}
if (currentData && currentData.ema52_dict) {
updateEMA52Display(currentData);
}
if (window.ChanDeriv) ChanDeriv.sync({ overlay: false });
return;
} catch (e) {
console.warn('增量刷新失败,回退全量重建:', e);
}
}
// 全量重建:优先用请求前冻结的视窗(分析按钮在请求发出时已 capture)
if (typeof ensurePendingChartViewportBeforeInit === 'function') {
ensurePendingChartViewportBeforeInit();
if (window._preserveViewOnRefresh) {
console.log('📌 全量重建:使用请求前冻结视窗');
} else if (window._pendingRestoreView) {
console.log('📌 使用已保存图表视图');
}
} else if (window._preserveViewOnRefresh) {
window._pendingRestoreView = window._preserveViewOnRefresh;
console.log('📌 全量重建:使用请求前冻结视窗');
} else if (!window._pendingRestoreView && tvWidget && tvWidget.mainChart) {
try {
window._pendingRestoreView = captureChartViewState(tvWidget.mainChart);
console.log('📌 保存图表视图:', JSON.stringify(window._pendingRestoreView));
} catch (e) {
console.warn('保存图表视图失败:', e);
window._pendingRestoreView = null;
}
} else if (window._pendingRestoreView) {
console.log('📌 使用已保存图表视图:', JSON.stringify(window._pendingRestoreView));
}
initTradingView($('#symbol').val(), $('#timeframe').val());
// 更新表格数据
updateTables(data);
if (currentData && currentData.ema52_dict) {
updateEMA52Display(currentData);
}
if (window.ChanDeriv) ChanDeriv.sync({ overlay: true });
}
function refreshChartOnly() {
// 仅使用当前数据刷新图表显示,不从服务器加载新数据
if (currentData) {
console.log('仅刷新图表显示,不重新获取数据');
refreshChart(currentData);
} else {
console.log('没有当前数据,无法刷新显示');
}
}
// 绑定主/次/次次周期笔面积、线段面积显示开关
$('#showMainBiArea, #showMainSegArea, #showElementBiArea, #showElementSegArea, #showSubSubBiArea, #showSubSubSegArea').change(function() {
updateChartDisplay();
});
// 绑定分型类型显示开关(与笔一致:全量重建,避免增量路径标记未对齐)
$('#showKlcFxType').change(function() {
updateChartDisplay();
});
// 绑定小周期分型显示开关
$('#showElementKlcFxType').change(function() {
updateChartDisplay();
});
// 绑定布林带显示变更事件
$('#showMainBollinger').change(function() {
updateChartDisplay();
});
$('#showElementBollinger').change(function() {
updateChartDisplay();
});
// K线周期切换由 macd_ui.js 统一走 updateChartDisplay(勿再绑 refreshChart,会重复且易漏对齐)
// 绑定主图U显示开关
$('#toggleUOnMain').change(function() {
window.showUOnMain = $('#toggleUOnMain').is(':checked');
refreshChartOnly();
});
// 次周期 U 显示开关
$('#toggleUOnElement').change(function() {
window.showUOnElement = $('#toggleUOnElement').is(':checked');
refreshChartOnly();
});
// 买卖点显示开关
$('#showOi').change(function() {
if (window.ChanDeriv) ChanDeriv.loadOverlays();
});
$('#showDeriv').change(function() {
updateChartDisplay();
});
$('#showMainBsp').change(function() {
updateChartDisplay();
});
$('#showElementBsp').change(function() {
updateChartDisplay();
});
// 在控制台输出当前显示状态
console.log('当前显示状态:', {
'showOriginalKline': $('#showOriginalKline').is(':checked'),
'showMainBi': $('#showMainBi').is(':checked'),
'showMainSeg': $('#showMainSeg').is(':checked'),
'showMainZs': $('#showMainZs').is(':checked'),
'showVolume': false,
'showMacd': $('#showMacd').is(':checked'),
'showKlcFxType': $('#showKlcFxType').is(':checked'),
'showKluFxType': $('#showKluFxType').is(':checked'),
'showElementKlcFxType': $('#showElementKlcFxType').is(':checked'),
'showElementKluFxType': $('#showElementKluFxType').is(':checked'),
'showTradePoints': $('#showTradePoints').is(':checked'),
'showMainBollinger': $('#showMainBollinger').is(':checked'),
'showElementBollinger': $('#showElementBollinger').is(':checked'),
'timeframe': $('#timeframe').val(),
'elementTimeframe': $('#elementTimeframe').val(),
'timezone': $('#timezone').val(),
'start_time': $('#start_time').val(),
'end_time': $('#end_time').val()
});
// 初始化提示工具
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
// 获取A股股票列表(全市场,来自 /api/a_stocks
function loadAStockSymbols() {
const $select = $('#astockSymbol');
const currentSymbol = $select.val();
$select.prop('disabled', true);
$.get('/api/a_stocks', function(data) {
$select.prop('disabled', false);
if (!Array.isArray(data)) {
console.error('加载A股股票列表失败: 返回非数组', data);
return;
}
$select.empty();
data.forEach(function(stock) {
$select.append($('<option>', {
value: stock.symbol,
text: stock.symbol + ' - ' + (stock.name || '')
}));
});
if (currentSymbol && data.some(stock => stock.symbol === currentSymbol)) {
$select.val(currentSymbol);
} else {
$select.val('000001');
}
}).fail(function(xhr) {
$select.prop('disabled', false);
console.error('加载A股股票列表失败', xhr && xhr.status);
});
}
// 检测交易对类型并返回相应的配置
function getSymbolConfig(symbol) {
const isAStock = symbol && symbol.length === 6 && /^\d+$/.test(symbol);
if (isAStock) {
return {
type: 'a_stock',
displayName: symbol,
tradingSessions: [
// A股交易时间配置
{ start: '09:30', end: '11:30' }, // 上午
{ start: '13:00', end: '15:00' } // 下午
],
timezone: 'Asia/Shanghai',
// A股的交易日配置(周一到周五,除节假日)
tradingDays: [1, 2, 3, 4, 5] // 1=周一, 7=周日
};
} else {
return {
type: 'crypto',
displayName: symbol,
tradingSessions: [
{ start: '00:00', end: '23:59' } // 24小时交易
],
timezone: 'UTC',
tradingDays: [1, 2, 3, 4, 5, 6, 7] // 7天交易
};
}
}
// 根据交易对类型调整图表配置
function adjustChartForSymbolType(chartOptions, symbolConfig) {
if (symbolConfig.type === 'a_stock') {
// A股特殊配置
chartOptions.timeScale = {
...chartOptions.timeScale,
// 禁用非交易时间的显示
borderVisible: true,
borderColor: '#ddd',
// 自定义时间格式化,只显示交易时间
timeVisible: true,
// 添加A股特定的时间范围限制
rightOffset: 12,
barSpacing: 6,
minBarSpacing: 3,
};
// 添加A股交易时间提示
chartOptions.layout = {
...chartOptions.layout,
fontSize: 12,
fontFamily: 'Arial, sans-serif'
};
}
return chartOptions;
}
// 过滤非交易时间的数据(仅用于显示优化)
function filterTradingHours(data, symbolConfig) {
if (symbolConfig.type !== 'a_stock') {
return data; // 非A股数据不需要过滤
}
return data.filter(item => {
const date = new Date(item.time * 1000);
const hour = date.getHours();
const minute = date.getMinutes();
const timeStr = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
// 检查是否在交易时间内
return symbolConfig.tradingSessions.some(session => {
return timeStr >= session.start && timeStr <= session.end;
});
});
}
// 更新A股交易时间状态
function updateAStockTradingStatus() {
const now = new Date();
const chinaTime = new Date(now.toLocaleString("en-US", {timeZone: "Asia/Shanghai"}));
const hour = chinaTime.getHours();
const minute = chinaTime.getMinutes();
const dayOfWeek = chinaTime.getDay(); // 0=周日, 1=周一, ..., 6=周六
const statusElement = document.getElementById('tradingTimeStatus');
if (!statusElement) return;
// 检查是否为交易日(周一到周五)
const isTradingDay = dayOfWeek >= 1 && dayOfWeek <= 5;
if (!isTradingDay) {
statusElement.className = 'badge bg-secondary';
statusElement.textContent = '非交易日';
return;
}
// 检查是否在交易时间内
const currentTime = hour * 60 + minute; // 转换为分钟
const morningStart = 9 * 60 + 30; // 09:30
const morningEnd = 11 * 60 + 30; // 11:30
const afternoonStart = 13 * 60; // 13:00
const afternoonEnd = 15 * 60; // 15:00
let status = '';
let className = '';
if (currentTime >= morningStart && currentTime <= morningEnd) {
status = '上午交易中';
className = 'badge bg-success';
} else if (currentTime >= afternoonStart && currentTime <= afternoonEnd) {
status = '下午交易中';
className = 'badge bg-success';
} else if (currentTime > morningEnd && currentTime < afternoonStart) {
status = '午间休市';
className = 'badge bg-warning';
} else if (currentTime < morningStart) {
status = '开盘前';
className = 'badge bg-info';
} else if (currentTime > afternoonEnd) {
status = '收盘后';
className = 'badge bg-dark';
} else {
status = '非交易时间';
className = 'badge bg-secondary';
}
statusElement.className = className;
statusElement.textContent = status;
}
// 启动A股交易时间状态更新
function startAStockStatusUpdater() {
// 如果已经有定时器在运行,先清除
if (window.astockStatusInterval) {
clearInterval(window.astockStatusInterval);
}
// 立即更新一次
updateAStockTradingStatus();
// 每30秒更新一次
window.astockStatusInterval = setInterval(updateAStockTradingStatus, 30000);
console.log('A股交易时间状态更新器已启动');
}
// 均线系统全局变量
var movingAverages = []; // 存储所有均线配置
var maIdCounter = 0; // 均线ID计数器
// 布林带系统全局变量
var bollingerBands = []; // 存储所有布林带配置
var bbIdCounter = 0; // 布林带ID计数器
// 清理EMA52系列