// localStorage.removeItem("tradingview.chartproperties");
|
// 在初始化 Highcharts 图表之前清除 TradingView 配置
|
localStorage.removeItem("tradingview.chartproperties");
|
|
// 然后继续初始化 Highcharts 或处理 Datafeed 类
|
// const datafeed = new Datafeed(vm);
|
// const chart = Highcharts.stockChart('container', {
|
// // Highcharts 配置
|
// });
|
$(function() {
|
// Light Theme for Highcharts
|
let light = {
|
chart: {
|
backgroundColor: "#ffffff",
|
borderColor: "#e6e6e6",
|
borderWidth: 1
|
},
|
title: {
|
style: {
|
color: '#333',
|
fontSize: '16px'
|
}
|
},
|
xAxis: {
|
gridLineColor: '#dcdee0',
|
labels: {
|
style: {
|
color: '#333',
|
fontSize: '9px'
|
}
|
},
|
lineColor: '#dcdee0'
|
},
|
yAxis: {
|
gridLineColor: '#dcdee0',
|
labels: {
|
style: {
|
color: '#333',
|
fontSize: '9px'
|
}
|
},
|
lineColor: '#dcdee0'
|
},
|
legend: {
|
itemStyle: {
|
color: '#333'
|
}
|
},
|
plotOptions: {
|
candlestick: {
|
upColor: '#2EBD85',
|
downColor: '#F4465D',
|
borderColor: '#2EBD85',
|
borderWidth: 1
|
}
|
}
|
};
|
|
// Dark Theme for Highcharts
|
let dark = {
|
chart: {
|
backgroundColor: "#2b2b37",
|
borderColor: "#49495F",
|
borderWidth: 1
|
},
|
title: {
|
style: {
|
color: '#fff',
|
fontSize: '16px'
|
}
|
},
|
xAxis: {
|
gridLineColor: '#49495F',
|
labels: {
|
style: {
|
color: '#fff',
|
fontSize: '9px'
|
}
|
},
|
lineColor: '#49495F'
|
},
|
yAxis: {
|
gridLineColor: '#49495F',
|
labels: {
|
style: {
|
color: '#fff',
|
fontSize: '9px'
|
}
|
},
|
lineColor: '#49495F'
|
},
|
legend: {
|
itemStyle: {
|
color: '#fff'
|
}
|
},
|
plotOptions: {
|
candlestick: {
|
upColor: '#2EBD85',
|
downColor: '#F4465D',
|
borderColor: '#2EBD85',
|
borderWidth: 1
|
}
|
}
|
};
|
|
// Final TV Style (light and dark theme options)
|
let tvStyle = {
|
light,
|
dark
|
};
|
class Datafeed {
|
constructor(vm) {
|
this.self = vm; // 传入的数据和配置
|
}
|
|
// onReady 方法:传递图表支持的配置
|
onReady(callback) {
|
// 返回 Highcharts 所需的配置选项
|
callback({
|
supported_resolutions: ['1', '5', '15', '30', '60', '240', '1D'],
|
supports_search: false,
|
supports_group_request: false
|
});
|
}
|
|
// resolveSymbol 方法:解析符号,返回图表所需的元数据
|
resolveSymbol(symbolName, onSymbolResolvedCallback, onResolveErrorCallback) {
|
// 解析符号(这里只是模拟数据,实际上可能需要连接到外部API来获取详细信息)
|
const symbolInfo = {
|
name: symbolName,
|
description: 'Some stock symbol',
|
type: 'stock',
|
timezone: 'America/New_York',
|
session: '24x7',
|
minmov: 1, // 最小变动单位
|
pricescale: 100, // 价格缩放
|
has_intraday: true, // 是否支持日内数据
|
has_daily: true, // 是否支持日线数据
|
};
|
// 解析完成后回调
|
onSymbolResolvedCallback(symbolInfo);
|
}
|
// getBars 方法:获取数据,转换为 Highcharts K线图所需的格式
|
getBars(symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback) {
|
// 模拟获取K线数据的逻辑(从数据源或服务器获取数据)
|
const bars = []; // 假设这是从你的数据源返回的数据
|
|
// 将数据转换为 Highcharts 所需的格式
|
bars.forEach(bar => {
|
const chartData = {
|
x: bar.time, // 时间戳(单位:毫秒)
|
open: bar.open,
|
high: bar.high,
|
low: bar.low,
|
close: bar.close,
|
volume: bar.volume
|
};
|
|
onHistoryCallback([chartData], { noData: false });
|
});
|
|
// 错误回调(如果有的话)
|
onErrorCallback('Some error occurred');
|
}
|
|
subscribeBars(symbolInfo, resolution, onRealtimeCallback) {
|
const updateInterval = 1000; // 更新频率,例如每秒更新一次
|
|
// 伪代码,假设这里每秒钟获取一次数据
|
setInterval(() => {
|
// 获取实时的价格数据(实际操作中需要从数据源获取)
|
const latestBar = getLatestBarFromDataSource(); // 假设这是从服务器获取的数据
|
// 转换为 Highcharts 格式
|
const chartData = {
|
x: latestBar.time,
|
open: latestBar.open,
|
high: latestBar.high,
|
low: latestBar.low,
|
close: latestBar.close,
|
volume: latestBar.volume
|
};
|
// 调用回调函数,推送数据到 Highcharts
|
onRealtimeCallback(chartData);
|
}, updateInterval);
|
}
|
|
// subscribeBars 方法:订阅数据更新
|
subscribeBars(symbolInfo, resolution, onRealtimeCallback, listenerGUID) {
|
this.self.subscribeBars(symbolInfo, resolution, (bar) => {
|
// 转换并推送实时更新的 K线数据
|
const updatedBar = {
|
x: bar.time * 1000, // 时间戳转换为毫秒
|
open: bar.open,
|
high: bar.high,
|
low: bar.low,
|
close: bar.close,
|
volume: bar.volume || 0
|
};
|
onRealtimeCallback(updatedBar);
|
});
|
}
|
|
// unsubscribeBars 方法:取消订阅数据更新
|
unsubscribeBars(listenerGUID) {
|
this.self.unsubscribeBars(listenerGUID);
|
}
|
|
// 默认的符号信息
|
defaultSymbol() {
|
return {
|
'timezone': 'Asia/Shanghai',
|
'minmov': 1,
|
'minmov2': 0,
|
'fractional': true,
|
'session': '24x7',
|
'has_intraday': true,
|
'has_no_volume': false,
|
'has_daily': true,
|
'has_weekly_and_monthly': true,
|
'pricescale': 10000
|
};
|
}
|
}
|
|
|
class Page {
|
constructor() {
|
this.datafeed = undefined; // 数据源
|
this.page = 1;
|
this.onRealtimeCallback = undefined;
|
this.TView = undefined;
|
this.interval = this.getQuery('interval') || '60'; // 默认 60 分钟
|
this.symbolName = this.getQuery('symbol'); // 从 URL 获取 symbol
|
this.theme = this.getQuery('theme') || 'dark'; // 默认 dark 主题
|
this.lang = this.getQuery('lang') || 'en'; // 默认语言
|
this.resolutions = this.getQuery('resolutions') || ["5", "15", "30", "60", "240", "1D", "1W",
|
"1M"
|
];
|
this.isLoad = false;
|
this.url = this.getQuery('getLinkUrl'); // 获取链接
|
this.TVID = "tradingview_10798345"; // TV ID
|
this.Ws = undefined;
|
this.msg = '';
|
this.contract = this.getQuery('contract');
|
this.init(); // 初始化
|
this.studies = []; // 配置项
|
this.tvBars = []; // 数据
|
|
}
|
|
// 获取路径上的参数
|
getQuery(name) {
|
const urlParams = new URLSearchParams(window.location.search);
|
return urlParams.get(name); // 返回 URL 查询参数的值
|
}
|
|
// 初始化
|
init() {
|
this.linkSocket();
|
// 数据模型
|
this.datafeed = new Datafeed(this);
|
|
// 配置图表
|
this.chart = Highcharts.chart('tradingview_10798345', {
|
chart: {
|
type: 'candlestick', // 设置图表类型为 K线图
|
height: 500,
|
zoomType: 'x', // 允许水平缩放
|
events: {
|
// load: () => {
|
// this.chart.update({
|
// tooltip: {
|
// positioner: function (labelWidth, labelHeight, point) {
|
// return { x: point.plotX, y: point.plotY };
|
// }
|
// }
|
// });
|
// }
|
}
|
},
|
title: {
|
text: null // 不显示标题
|
},
|
rangeSelector: {
|
selected: 1 // 选择默认时间范围
|
},
|
time: {
|
timezone: 'Asia/Shanghai' // 设置时区
|
},
|
tooltip: {
|
split: true,
|
valueDecimals: 2, // 小数点后2位
|
valuePrefix: '$' // 显示货币符号
|
},
|
navigator: {
|
enabled: false // 禁用导航器
|
},
|
scrollbar: {
|
enabled: false // 禁用滚动条
|
},
|
xAxis: {
|
type: 'datetime', // X轴显示时间
|
labels: {
|
formatter: function () {
|
return Highcharts.dateFormat('%Y-%m-%d %H:%M', this.value);
|
}
|
},
|
title: {
|
text: '时间'
|
}
|
},
|
yAxis: {
|
title: {
|
text: '价格'
|
}
|
},
|
series: [{
|
name: 'K线',
|
data: this.tvBars, // 假设这是通过 Datafeed 获取的 K线数据
|
type: 'candlestick', // 设置为K线图
|
tooltip: {
|
valueDecimals: 2 // 设置价格的精度
|
}
|
}],
|
exporting: {
|
enabled: false // 禁用导出功能
|
},
|
credits: {
|
enabled: false // 禁用版权信息
|
},
|
legend: {
|
enabled: false // 禁用图例
|
}
|
});
|
|
// 监听图表上的事件
|
this.chart.series[0].points.forEach((point, i) => {
|
point.on('mouseOver', function () {
|
const time = point.x;
|
const price = point.open; // 取开盘价或其他价格
|
console.log('Time:', time, 'Price:', price);
|
this.showKlineQuoter(time);
|
});
|
});
|
}
|
|
// 创建Highcharts图表
|
createChart() {
|
console.log('222', this.tvBars);
|
const chartOptions = {
|
chart: {
|
renderTo: 'tradingview_10798345', // 图表容器ID
|
type: 'candlestick', // 设置图表类型为蜡烛图
|
backgroundColor: this.theme === 'light' ? '#fff' : '#2b2b37', // 根据主题选择背景颜色
|
height: 500, // 设置图表高度
|
events: {
|
// load: function() {
|
// const chart = this;
|
// // 使用 chart 来绑定 mousemove 事件
|
// chart.container.addEventListener('mousemove', function(event) {
|
// // 获取鼠标位置的时间戳
|
// const time = chart.xAxis[0].toValue(event.offsetX);
|
// const resolutionTime = this
|
// .getResolutionTime(); // 获取分辨率的时间周期
|
// const fTime = Math.floor(time / resolutionTime) *
|
// resolutionTime; // 根据分辨率计算时间
|
// console.log('fTime', fTime);
|
// chart.showKlineQuoter(fTime); // 显示对应的K线数据
|
// });
|
// }
|
}
|
},
|
title: {
|
// text: `${this.symbolName} Kline Chart`, // 标题显示symbol名称
|
style: {
|
color: this.theme === 'light' ? '#000' : '#fff' // 依据主题设置标题颜色
|
}
|
},
|
subtitle: {
|
// text: `Interval: ${this.interval}`, // 显示时间间隔
|
style: {
|
color: this.theme === 'light' ? '#000' : '#fff' // 依据主题设置副标题颜色
|
}
|
},
|
xAxis: {
|
type: 'datetime', // 设置x轴为时间格式
|
labels: {
|
style: {
|
color: this.theme === 'light' ? '#000' : '#fff' // 依据主题设置x轴标签颜色
|
}
|
}
|
},
|
yAxis: {
|
title: {
|
text: 'Price', // y轴标题
|
style: {
|
color: this.theme === 'light' ? '#000' : '#fff' // 依据主题设置y轴标题颜色
|
}
|
},
|
labels: {
|
style: {
|
color: this.theme === 'light' ? '#000' : '#fff' // 依据主题设置y轴标签颜色
|
}
|
},
|
},
|
series: [{
|
data: this.tvBars, // K线数据,假设数据格式已适配
|
// data: [
|
// [Date.UTC(2025, 2, 1), 100],
|
// [Date.UTC(2025, 2, 2), 105],
|
// [Date.UTC(2025, 2, 3), 110]
|
// ],
|
type: 'candlestick', // 设置图表类型为蜡烛图
|
upColor: '#4fff33', // 设置上涨时的颜色
|
color: '#f00707', // 设置下跌时的颜色
|
lineColor: '#f00707', // 设置线的颜色
|
dataGrouping: {
|
enabled: true, // 启用数据分组
|
groupPixelWidth: 10 // 每个分组的宽度
|
}
|
}],
|
plotOptions: {
|
candlestick: {
|
color: '#f00707', // 下跌时的颜色
|
upColor: '#4fff33' // 上涨时的颜色
|
}
|
},
|
navigation: {
|
buttonOptions: {
|
enabled: false // 禁用导航按钮
|
}
|
},
|
tooltip: {
|
pointFormatter: function() {
|
// 格式化鼠标悬停时显示的tooltip内容
|
return `<b>Time:</b> ${new Date(this.x).toLocaleString()}<br>
|
<b>Open:</b> ${this.open}<br>
|
<b>High:</b> ${this.high}<br>
|
<b>Low:</b> ${this.low}<br>
|
<b>Close:</b> ${this.close}<br>`;
|
}
|
}
|
};
|
|
// 渲染Highcharts图表
|
console.log(chartOptions);
|
Highcharts.stockChart(chartOptions);
|
}
|
upsertTvBars(isFirstCall, bars) {
|
if (isFirstCall) {
|
this.tvBars = []; // 初始化 tvBars 数组
|
}
|
|
bars.forEach((bar) => {
|
// 确保不会重复添加时间戳相同的条目
|
if (this.tvBars.length === 0 || !this.tvBars.find(o => o.time === bar.time)) {
|
// 将数据转换为 Highcharts K 线图需要的格式:[时间戳, 开盘价, 最高价, 最低价, 收盘价]
|
const highchartsBar = [
|
bar.time, // 时间戳
|
bar.open, // 开盘价
|
bar.high, // 最高价
|
bar.low, // 最低价
|
bar.close // 收盘价
|
];
|
this.tvBars.push(highchartsBar);
|
}
|
});
|
|
// 按照时间戳排序
|
this.tvBars.sort((a, b) => {
|
if (a[0] < b[0]) return -1;
|
if (a[0] > b[0]) return 1;
|
return 0;
|
});
|
|
// 更新 Highcharts 图表的数据
|
this.updateChart();
|
}
|
showKlineQuoter(time) {
|
if (this.tvBars.length === 0) return;
|
const {
|
from,
|
to
|
} = this.chart.xAxis[0].getExtremes(); // 获取 visible range
|
const bar = this.tvBars.find(o => o.time === time * 1000); // 查找对应的 bar
|
if (!bar) return;
|
|
const barTime = bar.time / 1000 + 60 * 60 * 8; // 时区调整
|
const fromSize = barTime - from;
|
const toSize = to - barTime;
|
|
// 更新 TV Quoter 面板
|
const tvQuoter = $('#tv-quoter');
|
tvQuoter.css({
|
visibility: 'visible',
|
});
|
|
// 调整 Quoter 面板的位置
|
if (fromSize > toSize) {
|
tvQuoter.css({
|
left: '10px',
|
right: 'auto'
|
});
|
} else {
|
tvQuoter.css({
|
right: '10px',
|
left: 'auto'
|
});
|
}
|
|
// 计算涨跌幅
|
let zhangdiefu = (bar.close - bar.open) / bar.open;
|
let zhangdiee = bar.close - bar.open;
|
|
// 更新 quoter 显示的数据
|
tvQuoter.find('[data-name="date"]').text(this.timestampToTime(bar.time));
|
tvQuoter.find('[data-name="date_lang"]').text(this.lang == 'zh-CN' ? '时间' : 'Date');
|
tvQuoter.find('[data-name="open"]').text(bar.open);
|
tvQuoter.find('[data-name="open_lang"]').text(this.lang == 'zh-CN' ? '开' : 'Open');
|
tvQuoter.find('[data-name="high"]').text(bar.high);
|
tvQuoter.find('[data-name="high_lang"]').text(this.lang == 'zh-CN' ? '高' : 'H');
|
tvQuoter.find('[data-name="low"]').text(bar.low);
|
tvQuoter.find('[data-name="low_lang"]').text(this.lang == 'zh-CN' ? '低' : 'L');
|
tvQuoter.find('[data-name="close"]').text(bar.close);
|
tvQuoter.find('[data-name="close_lang"]').text(this.lang == 'zh-CN' ? '收' : 'Close');
|
tvQuoter.find('[data-name="volume"]').text((+bar.volume).toFixed());
|
tvQuoter.find('[data-name="volume_lang"]').text(this.lang == 'zh-CN' ? '成交量' : 'Executed');
|
tvQuoter.find('[data-name="zhangdiefu"]').text((zhangdiefu * 100).toFixed(2) + '%');
|
tvQuoter.find('[data-name="zhangdiefu_lang"]').text(this.lang == 'zh-CN' ? '涨跌幅' : 'Change%');
|
tvQuoter.find('[data-name="zhangdiefu"]').css('color', zhangdiee > 0 ? '#53b987' : '#eb4d5c');
|
tvQuoter.find('[data-name="zhangdiee"]').text(zhangdiee.toFixed(4));
|
tvQuoter.find('[data-name="zhangdiee_lang"]').text(this.lang == 'zh-CN' ? '涨跌额' : 'Change');
|
tvQuoter.find('[data-name="zhangdiee"]').css('color', zhangdiee > 0 ? '#53b987' : '#eb4d5c');
|
|
// Highcharts 更新数据:例如,更新显示的特定点的 hover 信息
|
let chart = this.chart; // 获取 Highcharts 图表对象
|
|
// 查找与给定时间相匹配的点
|
const point = chart.series[0].data.find(p => p.x === bar.time);
|
if (point) {
|
// 更新图表的 tooltip 或者标记
|
chart.tooltip.refresh(point); // 刷新 tooltip 显示当前点的数据
|
}
|
}
|
timestampToTime(timestamp) {
|
const date = new Date(timestamp);
|
const yyyy = `${date.getFullYear()}`;
|
const yy = `${date.getFullYear()}`.substr(2);
|
const MM = `0${date.getMonth() + 1}`.slice(-2);
|
const dd = `0${date.getDate()}`.slice(-2);
|
const HH = `0${date.getHours()}`.slice(-2);
|
const mm = `0${date.getMinutes()}`.slice(-2);
|
|
// 获取Highcharts的分辨率(假设你已经设置了chart的分辨率)
|
const resolution = this.TView.chart().resolution();
|
let dateStr = '';
|
|
if (resolution === 'D' || resolution === 'W' || resolution === 'M') {
|
// 日期格式为 yyyy-MM-dd
|
dateStr = `${yyyy}-${MM}-${dd}`;
|
} else {
|
// 日期格式为 yy-MM-dd HH:mm
|
dateStr = `${yy}-${MM}-${dd} ${HH}:${mm}`;
|
}
|
|
return dateStr;
|
}
|
|
getResolutionTime() {
|
const chart = this.TView.chart();
|
const resolution = chart.resolution(); // 获取图表的分辨率(例如:'1', '5', 'D', 'W'等)
|
|
switch (resolution) {
|
case '1':
|
return 60; // 1分钟
|
case '5':
|
return 5 * 60; // 5分钟
|
case '10':
|
return 10 * 60; // 10分钟
|
case '15':
|
return 15 * 60; // 15分钟
|
case '30':
|
return 30 * 60; // 30分钟
|
case '60':
|
return 60 * 60; // 1小时
|
case '120':
|
return 120 * 60; // 2小时
|
case '240':
|
return 4 * 60 * 60; // 4小时
|
case 'D':
|
return 24 * 60 * 60; // 1天
|
case 'W':
|
return 7 * 24 * 60 * 60; // 1周
|
case 'M':
|
return 4 * 7 * 24 * 60 * 60; // 1月(假设每月4周)
|
default:
|
return 1; // 如果分辨率没有匹配项,默认返回1秒
|
}
|
}
|
|
createStudy() {
|
let chart = this.TView.chart(); // 获取图表实例
|
let theme = this.theme; // 获取主题
|
|
// 假设你的数据是存储在 `data` 中,这里会为每条移动平均线创建一个新的 `series`
|
const data = this.data; // 图表的数据
|
|
// 计算不同周期的移动平均线
|
const movingAverage = (data, period) => {
|
let maData = [];
|
for (let i = period - 1; i < data.length; i++) {
|
let sum = 0;
|
for (let j = i - period + 1; j <= i; j++) {
|
sum += data[j][1]; // 假设 `data[j][1]` 是价格
|
}
|
maData.push([data[i][0], sum / period]); // 将时间戳和均值添加到结果
|
}
|
return maData;
|
};
|
|
// 定义三个不同周期的移动平均线
|
let ma5 = movingAverage(data, 5);
|
let ma10 = movingAverage(data, 10);
|
let ma20 = movingAverage(data, 20);
|
|
// 创建图表
|
chart.update({
|
series: [{
|
name: 'MA 5',
|
data: ma5,
|
type: 'line',
|
color: theme === "light" ? "#efc149" : 'rgb(238, 218, 154)',
|
lineWidth: 3
|
},
|
{
|
name: 'MA 10',
|
data: ma10,
|
type: 'line',
|
color: theme === "light" ? "#7fcec0" : 'rgb(123, 201, 187)',
|
lineWidth: 3
|
},
|
{
|
name: 'MA 20',
|
data: ma20,
|
type: 'line',
|
color: "rgb(194, 148, 247)",
|
lineWidth: 3
|
}
|
]
|
});
|
}
|
// 连接socket并更新Highcharts图表数据
|
linkSocket() {
|
// 连接socket
|
this.Ws = new Ws(this.getQuery('ws'))
|
|
this.Ws.on('message', (evt) => {
|
console.log('111111112');
|
console.log('Received message:', evt); // 查看 evt 的结构
|
// 检测ping消息并回应pong
|
if (evt.cmd === 'ping' || evt.type === 'ping') {
|
this.Ws.send({
|
cmd: 'pong'
|
});
|
}
|
console.log('Received message:', evt); // 打印事件内容
|
// 处理特定的消息
|
if (evt.sub == this.msg) {
|
// 假设evt.data是图表数据的更新,按需求处理
|
const updatedData = this.getMap(evt.data);
|
console.log('updatedData', updatedData);
|
// 更新Highcharts图表数据
|
// this.updateChartData(updatedData);
|
console.log('123123',updatedData);
|
this.onRealtimeCallback(this.getMap(updatedData))
|
}
|
});
|
}
|
// 更新Highcharts图表数据
|
updateChartData(newData) {
|
// 假设newData是数组形式的数据,[时间戳, 值]
|
const series = this.chart.series[0]; // 获取第一个数据系列
|
|
// 向图表中添加新的数据点
|
series.addPoint(newData, true, true); // 参数: 新数据, 是否重新计算坐标轴, 是否移除旧数据点
|
|
// 或者更新整个数据系列
|
// series.setData(newData);
|
}
|
// 图表语言映射
|
chartLang() {
|
switch (this.lang) {
|
case "zh-CN":
|
return 'zh';
|
case "zh-TW":
|
return 'zh_TW';
|
case "tr":
|
return 'tr';
|
case "jp":
|
return 'ja';
|
case "kor":
|
return 'ko';
|
case "de":
|
return 'de_DE';
|
case "fra":
|
return 'fr';
|
case "it":
|
return 'it';
|
case "pt":
|
return 'pt';
|
case "spa":
|
return 'es';
|
default:
|
return 'en';
|
}
|
}
|
getMap(data) {
|
return [
|
data.id * 1000,
|
data.close * 1,
|
data.open * 1,
|
data.high * 1,
|
data.low * 1,
|
data.vol * 1,
|
];
|
}
|
|
// 获取数据
|
getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onDataCallback, onErrorCallback,
|
isFirstCall) {
|
console.log('这里获取数据');
|
let page = this.page > 3 ? 3 : this.page;
|
let data = {
|
symbol: symbolInfo.name,
|
period: this.resolution(resolution),
|
form: rangeStartDate,
|
to: rangeEndDate,
|
size: page * 200,
|
zip: 2
|
};
|
|
this.page++;
|
this.isLoad = true;
|
this.unSub(); // 取消订阅
|
|
// 使用 AJAX 请求获取数据
|
$.get(this.url, data).then(res => {
|
// 解压并转换数据为 Highcharts 可用格式
|
let arr = this.unzip(res.data.data).map(item => {
|
return this.getMap(item); // 假设 getMap(item) 返回 Highcharts 可识别的格式
|
});
|
|
// 更新数据到 Highcharts 图表
|
this.upsertTvBars(isFirstCall, arr); // 这里假设你已经在 Highcharts 中设置了图表
|
onDataCallback(arr); // 调用回调,传递数据
|
|
// 更新消息和订阅机制
|
this.msg = this.createMsg();
|
this.sub(); // 重新订阅
|
}).catch(err => {
|
// 错误回调,返回空数据
|
onDataCallback([]);
|
});
|
}
|
// 解压并适配 Highcharts 数据格式
|
unzip(b64Data) {
|
// 解码 Base64 字符串
|
let u8 = atob(b64Data);
|
|
// 使用 pako 解压
|
let jiya = pako.inflate(u8);
|
|
// 将 Uint8Array 转换为字符串
|
let str = this.Uint8ArrayToString(jiya);
|
|
// 解析为 JSON 格式
|
let jsonData = JSON.parse(str);
|
|
// 假设解压后的数据是一个数组,并且每个数据项包含时间戳和价格值
|
// 比如 jsonData = [{time: 1625479000000, value: 123}, ...]
|
|
// 将数据转换为 Highcharts 可识别的格式 [[timestamp, value], ...]
|
let highchartsData = jsonData.map(item => {
|
return [item.time, item.value]; // 假设数据项有 'time' 和 'value' 字段
|
});
|
|
return highchartsData;
|
}
|
|
// 将 Uint8Array 转换为字符串的函数
|
Uint8ArrayToString(fileData) {
|
var dataString = "";
|
for (var i = 0; i < fileData.length; i++) {
|
dataString += String.fromCharCode(fileData[i]);
|
}
|
return dataString;
|
}
|
|
// 将解压后的 JSON 数据适配为 Highcharts 数据格式
|
unzipAndAdaptToHighcharts(b64Data) {
|
// 1. 解码 Base64 字符串
|
let u8 = atob(b64Data);
|
|
// 2. 使用 pako 解压 Base64 字符串
|
let jiya = pako.inflate(u8);
|
|
// 3. 将解压后的 Uint8Array 转换为字符串
|
let str = this.Uint8ArrayToString(jiya);
|
|
// 4. 解析 JSON 数据
|
let jsonData = JSON.parse(str);
|
|
// 5. 假设解压后的数据格式为 [{time: timestamp, value: dataValue}, ...]
|
// 6. 转换为 Highcharts 支持的数据格式 [[timestamp, value], ...]
|
let highchartsData = jsonData.map(item => {
|
return [item.time, item.value]; // 假设每个数据项有 'time' 和 'value' 字段
|
});
|
|
return highchartsData;
|
}
|
|
|
// 获取传给后台的精度
|
resolution(resolution) {
|
let T = "";
|
if (isNaN(resolution * 1)) {
|
T = resolution
|
.replace("D", "day")
|
.replace("W", "week")
|
.replace("M", "mon");
|
} else {
|
if (resolution > 60) {
|
T = Math.floor(resolution / 60) + "hour";
|
} else {
|
T = resolution + "min";
|
}
|
}
|
return T;
|
}
|
|
// 获取推送回调,适配为 Highcharts 数据更新机制
|
subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) {
|
this.onRealtimeCallback = onRealtimeCallback;
|
|
if (!this.symbolName) {
|
setTimeout(() => {
|
// 在Highcharts中实现数据更新机制,这里模拟重新加载数据
|
onResetCacheNeededCallback();
|
}, 100);
|
}
|
|
// 假设我们通过某种方式获取到新的数据,比如通过API或WebSocket
|
// 这个地方模拟新的数据推送
|
const newData = [
|
// 这里的数据是模拟的,格式为 [timestamp, value]
|
[Date.now(), Math.random() * 100], // 时间戳和随机值
|
];
|
|
// 更新 Highcharts 图表的数据
|
if (this.chart) {
|
// 假设 chart 是 Highcharts 的实例
|
this.chart.series[0].addPoint(newData[0], true, true); // 添加新数据点
|
}
|
|
// 使用 onRealtimeCallback 更新图表数据
|
if (onRealtimeCallback) {
|
onRealtimeCallback(newData);
|
}
|
}
|
|
// 生成符号的名称,处理为小写
|
getSymbol(name) {
|
return name.split("/").join("").toLowerCase();
|
}
|
|
// 生成订阅消息的内容
|
createMsg() {
|
if (this.contract) {
|
return `swapKline_${this.symbolName}_${this.resolution(this.interval)}`;
|
} else {
|
return `Kline_${this.getSymbol(this.symbolName)}_${this.resolution(this.interval)}`;
|
}
|
}
|
|
// 解析时间粒度
|
resolution(resolution) {
|
let T = "";
|
if (isNaN(resolution * 1)) {
|
T = resolution.replace("D", "day").replace("W", "week").replace("M", "mon");
|
} else {
|
if (resolution > 60) {
|
T = Math.floor(resolution / 60) + "hour";
|
} else {
|
T = resolution + "min";
|
}
|
}
|
return T;
|
}
|
|
// 订阅消息,推送给 WebSocket
|
sub() {
|
console.log('订阅消息:', this.msg);
|
this.Ws.send({
|
cmd: "sub",
|
msg: this.msg
|
});
|
}
|
|
// 取消订阅
|
unSub() {
|
if (!this.msg) return;
|
this.Ws.send({
|
cmd: "unsub",
|
msg: this.msg
|
});
|
}
|
|
}
|
new Page()
|
})
|