You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

359 lines
12 KiB

import Datafeeds from "./Datafees.js";
import Socket from './Socket.js'
import servlet from './servlet.js';
// import { resolveConfig } from "prettier";
// import { resolveConfig } from "prettier";
// import
class Model {
/**
* 初始化模型
* @param {mixed} ws socket请求的url或者一个已经存在的socket对象
* @param {Object} sub_params socket订阅的参数
* @param {Object} unsub_params 取消订阅的参数
*/
constructor(link, view) {
this.socket = new Socket(link);
this.view = view;
// 用于订阅数据
this.sub_msg; // 订阅标签 用于鉴定返回message 固定格式 'label_id_interval'
this.marketId;
// 用于生成图表
this.symbol; // 请求商品
this.interval; // 请求周期
this.ticker; // 商品唯一标识
this.priceDecimals; // 价格精度
// 缓存用于K线的历史记录数据
this.cacheData = []; //
this.lastTime = null; // 最后一条数据的创建日期
this.getBarTimer = null;
this.isLoading = true;
this.datafeeds = new Datafeeds(this); // 数据模型的接口
this.widget; // td实例对象
this.getHistoryCallback; // ajax取记录的回调函数
this.onRealtimeCallback;
}
/**
* 生成图表库 支持初始化订阅 也可以手动在外部订阅,但订阅必须先于图标库
* @param {Object} optionals 图表初始化配置
* @param {Object|Array} sub_params 初始化订阅参数 可选值
*/
init(optionals, extenals = null) {
if (extenals) {
if (extenals.constructor == Function) { // 获取历史记录
this.getHistoryCallback = extenals;
} else if (extenals.constructor == Object) { // 初始化订阅信息
this.subscribe(extenals);
}
}
// 写入全局
this.symbol = optionals.symbol;
// 初始化设置interval 后期从订阅参数中取
this.interval = optionals.interval;
this.ticker = this.symbol.concat('_', this.interval);
this.priceDecimals = optionals.priceDecimals;
// 写入动态配置 覆盖默认配置
const settings = {
datafeed: this.datafeeds // 传入的api对象
}
Object.keys(optionals).forEach(key => {
if (optionals[key]) settings[key] = optionals[key];
})
// 接收数据值
this.socket.on('message', this.handleMessage.bind(this));
// 创建tv组件
this.widget = new TradingView.widget(servlet(settings));
}
//重连WS后的操作
conglian(link){
this.socket = new Socket(link);
this.socket.on('message', this.handleMessage.bind(this));
}
/**
* 发起订阅新的数据
* @param {Array|Object} sub_params 所有订阅参数
*/
subscribe(sub_params) {
// 将订阅参数 写入全局,别计算分时
if (sub_params instanceof Array) {
this.sub_msg = sub_params.find(item => item.cmd == "sub").msg
} else {
this.sub_msg = sub_params.msg;
}
this.sendMessage(sub_params);
}
sendMessage(data) {
// debugger
// console.log(">>>>>>>>>>" + JSON.stringify(data));
// 已经open状态
if (this.socket.checkOpen()) this.socket.send(data);
else{
// 先open 再send
this.socket.on('open', (evt) => {
// console.info(this.socket)
this.socket.send(data);
})
}
}
unSubscribe() {
this.sendMessage(this.sub_params);
}
// 处理历史数据和动态更新的数据
handleMessage(response) {
// if (response.sub != "history" && response.sub != "dynamic") return;
let { data = null, sub = null, type = null } = response;
// 答复连接
if (type == "ping") {
this.sendMessage({
cmd: "pong"
});
return;
}
// 严格匹配返回的订阅内容
if (sub === this.sub_msg) {
if (type == "history") {
// console.log("<<<<<<<<<History");
let list = [];
for (let item of data) {
list.push({
time: item.id * 1000,
open: item.open,
high: item.high,
low: item.low,
close: item.close,
volume: item.amount
})
}
// 缓存数据
this.cacheData[this.ticker] = list;
// 记录最新时间 1595423640 1595423580
this.lastTime = list[list.length - 1].time;
} else if (type == "dynamic") {
// 最新的数据值
// console.log('<<<<<<<<<New');
// 检测是否已经加载历史数据
if (!this.lastTime) return;
else {
// debugger
// console.log(data.id * 1000, data.id * 1000 - this.lastTime, data.close);
}
// this.datafeeds.barsUpdater.updateData();
const barsData = {
time: data.id * 1000,
open: data.open,
high: data.high,
low: data.low,
close: data.close,
volume: data.amount
};
// if (barsData.time >= this.lastTime && this.cacheData[this.ticker] && this.cacheData[this.ticker].length) {
// // 更新最后一条记录
// // this.cacheData[this.ticker][this.cacheData[this.ticker].length - 1] = barsData
// this.onRealtimeCallback(barsData);
// }
if (barsData.time >= this.lastTime) {
// 更新最后一条记录
this.lastTime = barsData.time;
this.onRealtimeCallback(barsData);
}
}
}
}
getConfig() {
// console.log("Model.getConfig");
return {
// supports_search: true,
supports_group_request: true,
// 支持的周期数组,周期可以是数字或字符串。
// 如果周期是一个数字,它被视为分钟数。
supported_resolutions: ['1', '5', '15', '30', '60', 'D', 'W', 'M'],
supports_marks: true, // 是否在K线上显示标记
supports_timescale_marks: true,
};
}
// 切换交易对方法
setSymbol(symbolName) {
this.symbol = symbolName;
this.ticker = this.symbol.concat('_', this.interval);
this.widget.setSymbol(symbolName, this.interval);
}
// 切换分时的方法
setResolution(resolution, callback) {
this.ticker = this.symbol.concat('_', resolution);
this.widget.chart().setResolution(resolution, callback);
}
getSymbol(symbolName) {// 小写的交易对名称
// 1、查找已有精度值
let priceDecimals = this.priceDecimals || this.view.priceDecimals;
let pms,
base = {
'name': symbolName, // 用于请求数据
// 这个商品的交易所时区
// 'timezone': 'Asia/Shanghai',
timezone: "America/Toronto",
// 最小波动
'minmov': 1,
'minmov2': 0,
'pointvalue': 1,
'fractional': false,
// 设置周期
'session': '24x7',
'has_intraday': true,
'has_no_volume': false,
// 设置是否支持周月线
"has_daily": true,
// 设置是否支持周月线
"has_weekly_and_monthly": true,
"has_empty_bars": true,
// 唯一标识符,如果您指定此属性,则其值将用于所有数据请求
'ticker': this.ticker,
//'supported_resolutions': ['1', '5', '15', '30', '60', '240', '1D', '5D', '1W', '1M']
};
// 2、还没获取到价格精度
if (!priceDecimals && this.view.getSymbol) {
pms = this.view.getSymbol().then(data => {
return (Object.assign({
// 商品说明 将被打印在图表的标题栏中
'description': data.pair_name,
// 设置精度 100表示保留两位小数 1000三位 10000四位
'pricescale': Math.pow(10, data.price_decimals),
}, base));
});
} else { // 已经获取到价格精度
pms = Promise.resolve(Object.assign({
// 商品说明 将被打印在图表的标题栏中
'description': symbolName.toUpperCase(),
// 设置精度 100表示保留两位小数 1000三位 10000四位
'pricescale': Math.pow(10, priceDecimals),
}, base))
}
return pms;
}
getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) {
// console.log("Model.getHistory");
if (this.getHistoryCallback instanceof Function) { // ajax加载
// 通过图表动作 切换分时 需要同步数据和分时值
if (this.interval !== resolution) {
// 需要转化resolute为实际的订阅interval
this.interval = resolution;
}
this.getHistoryCallback((data, lastTime) => {
this.lastTime = lastTime;
// 过滤筛选数据
const ret = [];
data.forEach(item => {
// 取得该时间内的记录
if (item.time >= rangeStartDate * 1000 && item.time <= rangeEndDate * 1000) {
ret.push(item);
}
});
onLoadedCallback(ret);
})
} else { // websocket加载
// 通过图表动作 切换分时 需要同步数据和分时值
if (this.interval !== resolution) {
// 需要转化resolute为实际的订阅interval
this.interval = resolution;
let period = this.translateInterval2Period(this.interval);
// 重新订阅所有数据 在cache缓存中控制过滤
this.subscribe([{
cmd: "unsub",
msg: `Kline_${symbolInfo.name}_${period}`
}, {
cmd: "req",
msg: `Kline_${symbolInfo.name}_${period}`
}, {
cmd: "sub",
msg: `Kline_${symbolInfo.name}_${period}`
}]);
}
if (this.cacheData[this.ticker] && this.cacheData[this.ticker].length) {
// 已取得数据
this.isLoading = false;
const ret = [];
this.cacheData[this.ticker].forEach(item => {
// 取得该时间内的记录
if (item.time >= rangeStartDate * 1000 && item.time <= rangeEndDate * 1000) {
ret.push(item);
}
});
onLoadedCallback(ret);
} else { // 重新加载数据
this.getBarTimer = setTimeout(() => {
this.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)
}, 1000)
}
}
}
// 订阅新的数据
subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) {
this.onRealtimeCallback = onRealtimeCallback;
}
}
export default Model;