Files
riwa-ionic/src/tradingview/datafeed.ts

251 lines
6.8 KiB
TypeScript

import type {
Bar,
DatafeedConfiguration,
HistoryCallback,
IDatafeedChartApi,
IExternalDatafeed,
LibrarySymbolInfo,
OnReadyCallback,
PeriodParams,
ResolutionString,
ResolveCallback,
SearchSymbolsCallback,
SubscribeBarsCallback,
} from "#/datafeed-api";
import type { MarketDataStreaming } from "@/api/types";
import { tradeWebSocket } from "./websocket";
/**
* 自定义 TradingView Datafeed 实现
* 用于从后端 API 获取 K 线数据
*/
export class RWADatafeed extends Datafeeds.UDFCompatibleDatafeed {
private subscribers: Map<string, { callback: SubscribeBarsCallback; resolution: ResolutionString; symbol: string }> = new Map();
constructor(apiUrl: string) {
super(apiUrl);
}
// /**
// * 初始化配置
// */
// onReady(callback: OnReadyCallback): void {
// console.log("[RWADatafeed]: onReady called");
// const config: DatafeedConfiguration = {
// // 支持的交易所
// exchanges: [
// { value: "", name: "All Exchanges", desc: "" },
// ],
// // 支持的品种类型
// symbols_types: [
// { name: "All types", value: "" },
// { name: "Stock", value: "stock" },
// { name: "Crypto", value: "crypto" },
// ],
// // 支持的时间周期
// supported_resolutions: [
// "1" as ResolutionString,
// "5" as ResolutionString,
// "15" as ResolutionString,
// "30" as ResolutionString,
// "60" as ResolutionString,
// "240" as ResolutionString,
// "1D" as ResolutionString,
// "1W" as ResolutionString,
// "1M" as ResolutionString,
// ],
// supports_marks: false,
// supports_timescale_marks: false,
// supports_time: true,
// };
// setTimeout(() => callback(config), 0);
// }
// /**
// * 搜索品种
// */
// searchSymbols(
// userInput: string,
// exchange: string,
// symbolType: string,
// onResult: SearchSymbolsCallback,
// ): void {
// console.log("[RWADatafeed]: searchSymbols", userInput, exchange, symbolType);
// // TODO: 调用后端 API 搜索品种
// // const results = await client.api.market.search.get({ query: { keyword: userInput, exchange, type: symbolType } })
// // 模拟搜索结果
// const results = [
// {
// symbol: "AAPL",
// full_name: "NASDAQ:AAPL",
// description: "Apple Inc.",
// exchange: "NASDAQ",
// ticker: "AAPL",
// type: "stock",
// },
// ];
// onResult(results);
// }
// /**
// * 解析品种信息
// */
// resolveSymbol(
// symbolName: string,
// onResolve: ResolveCallback,
// onError: (reason: string) => void,
// ): void {
// console.log("[RWADatafeed]: resolveSymbol", symbolName);
// // TODO: 调用后端 API 获取品种信息
// // const symbolInfo = await client.api.market.symbol[symbolName].get()
// // 模拟品种信息
// const symbolInfo: LibrarySymbolInfo = {
// name: symbolName,
// ticker: symbolName,
// description: symbolName,
// type: "crypto",
// session: "24x7",
// exchange: "Binance",
// listed_exchange: "Binance",
// timezone: "Etc/UTC",
// format: "price",
// pricescale: 100,
// minmov: 1,
// has_intraday: true,
// has_daily: true,
// has_weekly_and_monthly: true,
// supported_resolutions: [
// "1" as ResolutionString,
// "5" as ResolutionString,
// "15" as ResolutionString,
// "30" as ResolutionString,
// "60" as ResolutionString,
// "240" as ResolutionString,
// "1D" as ResolutionString,
// "1W" as ResolutionString,
// "1M" as ResolutionString,
// ],
// intraday_multipliers: ["1", "5", "15", "30", "60", "240"],
// volume_precision: 2,
// data_status: "streaming",
// };
// setTimeout(() => onResolve(symbolInfo), 0);
// }
// /**
// * 获取历史 K 线数据
// */
// getBars(
// symbolInfo: LibrarySymbolInfo,
// resolution: ResolutionString,
// periodParams: PeriodParams,
// onResult: HistoryCallback,
// onError: (reason: string) => void,
// ): void {
// const { from, to, firstDataRequest } = periodParams;
// console.log("[RWADatafeed]: getBars", {
// symbol: symbolInfo.name,
// resolution,
// from: new Date(from * 1000).toISOString(),
// to: new Date(to * 1000).toISOString(),
// firstDataRequest,
// });
// // TODO: 调用后端 API 获取 K 线数据
// // client.api.market.kline.get({
// // query: {
// // symbol: symbolInfo.name,
// // resolution,
// // from,
// // to,
// // }
// // }).then(response => {
// // const bars = response.data.map(item => ({
// // time: item.time * 1000,
// // open: item.open,
// // high: item.high,
// // low: item.low,
// // close: item.close,
// // volume: item.volume,
// // }))
// // onResult(bars, { noData: bars.length === 0 })
// // }).catch(error => {
// // onError(error.message)
// // })
// // 模拟 K 线数据
// const bars: Bar[] = this.generateMockBars(from, to, resolution);
// if (bars.length === 0) {
// onResult([], { noData: true });
// }
// else {
// onResult(bars, { noData: false });
// }
// }
/**
* 订阅实时数据
*/
subscribeBars(
symbolInfo: LibrarySymbolInfo,
resolution: ResolutionString,
onTick: SubscribeBarsCallback,
subscriberUID: string,
onResetCacheNeededCallback: () => void,
): void {
console.log("[RWADatafeed]: subscribeBars", { symbol: symbolInfo.name, resolution, subscriberUID });
this.subscribers.set(subscriberUID, { callback: onTick, resolution, symbol: symbolInfo.name });
const wsConnection = tradeWebSocket.getSocket();
wsConnection.send({
action: "subscribe",
channels: [{
name: "bar",
symbol: symbolInfo.name,
resolution,
}],
});
wsConnection.subscribe((message) => {
const data = message.data as any;
if (data.type !== "bar")
return;
const bar: Bar = {
time: data.bar.time,
open: data.bar.open,
high: data.bar.high,
low: data.bar.low,
close: data.bar.close,
volume: data.bar.volume,
};
onTick(bar);
});
}
/**
* 取消订阅实时数据
*/
unsubscribeBars(subscriberUID: string): void {
console.log("[RWADatafeed]: unsubscribeBars", subscriberUID);
const subscriber = this.subscribers.get(subscriberUID);
const wsConnection = tradeWebSocket.getSocket();
wsConnection.send({
action: "unsubscribe",
channels: [{
name: "bar",
symbol: subscriber?.symbol || "",
}],
});
}
}