feat: 重构 TradingView 数据源实现,更新数据源类名为 RWADatafeed;优化 TradingViewChart 组件的使用方式
This commit is contained in:
@@ -13,16 +13,19 @@ import type {
|
||||
SubscribeBarsCallback,
|
||||
} from "#/datafeed-api";
|
||||
|
||||
const { VITE_TRADINGVIEW_DATA_API_URL } = useEnv();
|
||||
|
||||
/**
|
||||
* 自定义 TradingView Datafeed 实现
|
||||
* 用于从后端 API 获取 K 线数据
|
||||
*/
|
||||
export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
private apiUrl: string;
|
||||
export class RWADatafeed extends Datafeeds.UDFCompatibleDatafeed {
|
||||
private apiUrl: string = VITE_TRADINGVIEW_DATA_API_URL;
|
||||
private updateInterval: number;
|
||||
private subscribers: Map<string, { callback: SubscribeBarsCallback; resolution: ResolutionString }> = new Map();
|
||||
|
||||
constructor(apiUrl: string, updateInterval = 1000) {
|
||||
super(apiUrl);
|
||||
this.apiUrl = apiUrl;
|
||||
this.updateInterval = updateInterval;
|
||||
}
|
||||
@@ -31,7 +34,7 @@ export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
* 初始化配置
|
||||
*/
|
||||
onReady(callback: OnReadyCallback): void {
|
||||
console.log("[CustomDatafeed]: onReady called");
|
||||
console.log("[RWADatafeed]: onReady called");
|
||||
|
||||
const config: DatafeedConfiguration = {
|
||||
// 支持的交易所
|
||||
@@ -73,7 +76,7 @@ export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
symbolType: string,
|
||||
onResult: SearchSymbolsCallback,
|
||||
): void {
|
||||
console.log("[CustomDatafeed]: searchSymbols", userInput, exchange, symbolType);
|
||||
console.log("[RWADatafeed]: searchSymbols", userInput, exchange, symbolType);
|
||||
|
||||
// TODO: 调用后端 API 搜索品种
|
||||
// const results = await client.api.market.search.get({ query: { keyword: userInput, exchange, type: symbolType } })
|
||||
@@ -101,7 +104,7 @@ export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
onResolve: ResolveCallback,
|
||||
onError: (reason: string) => void,
|
||||
): void {
|
||||
console.log("[CustomDatafeed]: resolveSymbol", symbolName);
|
||||
console.log("[RWADatafeed]: resolveSymbol", symbolName);
|
||||
|
||||
// TODO: 调用后端 API 获取品种信息
|
||||
// const symbolInfo = await client.api.market.symbol[symbolName].get()
|
||||
@@ -152,7 +155,7 @@ export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
onError: (reason: string) => void,
|
||||
): void {
|
||||
const { from, to, firstDataRequest } = periodParams;
|
||||
console.log("[CustomDatafeed]: getBars", {
|
||||
console.log("[RWADatafeed]: getBars", {
|
||||
symbol: symbolInfo.name,
|
||||
resolution,
|
||||
from: new Date(from * 1000).toISOString(),
|
||||
@@ -203,7 +206,7 @@ export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
listenerGuid: string,
|
||||
onResetCacheNeededCallback: () => void,
|
||||
): void {
|
||||
console.log("[CustomDatafeed]: subscribeBars", {
|
||||
console.log("[RWADatafeed]: subscribeBars", {
|
||||
symbol: symbolInfo.name,
|
||||
resolution,
|
||||
listenerGuid,
|
||||
@@ -231,7 +234,7 @@ export class CustomDatafeed implements IDatafeedChartApi, IExternalDatafeed {
|
||||
* 取消订阅实时数据
|
||||
*/
|
||||
unsubscribeBars(listenerGuid: string): void {
|
||||
console.log("[CustomDatafeed]: unsubscribeBars", listenerGuid);
|
||||
console.log("[RWADatafeed]: unsubscribeBars", listenerGuid);
|
||||
this.subscribers.delete(listenerGuid);
|
||||
|
||||
// TODO: 关闭 WebSocket 连接
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import type { ChartingLibraryWidgetOptions } from "#/charting_library";
|
||||
import type { ChartingLibraryWidgetOptions, IChartingLibraryWidget } from "#/charting_library";
|
||||
import type { ResolutionString } from "#/datafeed-api";
|
||||
import { CustomDatafeed } from "./datafeed";
|
||||
import { RWADatafeed } from "./datafeed";
|
||||
|
||||
const { VITE_TRADINGVIEW_LIBRARY_URL, VITE_TRADINGVIEW_DATA_API_URL } = useEnv();
|
||||
|
||||
const datafeed = new RWADatafeed(VITE_TRADINGVIEW_DATA_API_URL, 60000);
|
||||
|
||||
const defaultOptions = {
|
||||
container: "tradingview_chart_container",
|
||||
locale: "zh",
|
||||
library_path: `${VITE_TRADINGVIEW_LIBRARY_URL}/charting_library/`,
|
||||
datafeed: new CustomDatafeed(VITE_TRADINGVIEW_DATA_API_URL, 60000),
|
||||
symbol: "AAPL",
|
||||
datafeed,
|
||||
interval: "1D" as ResolutionString,
|
||||
symbol: "AAPL",
|
||||
debug: true,
|
||||
autosize: true,
|
||||
// 禁用移动端不友好的功能
|
||||
@@ -18,18 +20,46 @@ const defaultOptions = {
|
||||
"left_toolbar", // 隐藏左侧绘图工具栏
|
||||
"timeframes_toolbar", // 隐藏底部时间框架工具栏
|
||||
"volume_force_overlay", // 禁用成交量覆盖在价格图表上
|
||||
// 禁用顶部工具栏
|
||||
"header_widget",
|
||||
"header_compare",
|
||||
"header_symbol_search",
|
||||
"header_fullscreen_button",
|
||||
"header_settings",
|
||||
// "header_widget", // 禁用顶部工具栏
|
||||
"header_compare", // 禁用比较功能
|
||||
"header_symbol_search", // 禁用符号搜索
|
||||
// "header_fullscreen_button", // 禁用全屏按钮
|
||||
// "header_settings", // 禁用设置按钮
|
||||
"header_screenshot", // 禁用截图功能
|
||||
"header_undo_redo", // 禁用撤销/重做按钮
|
||||
"header_saveload", // 禁用保存/加载按钮
|
||||
"control_bar", // 禁用底部控制栏
|
||||
"edit_buttons_in_legend", // 禁用图例中的编辑按钮
|
||||
"border_around_the_chart", // 移除图表边框
|
||||
"main_series_scale_menu", // 禁用主序列比例菜单
|
||||
"display_market_status", // 禁用市场状态显示
|
||||
// "create_volume_indicator_by_default", // 默认不创建成交量指标
|
||||
// "go_to_date", // 禁用跳转到日期功能
|
||||
"pane_context_menu", // 禁用窗格右键菜单
|
||||
// "legend_context_menu", // 禁用图例右键菜单
|
||||
// "legend_inplace_edit", // 禁用图例内联编辑
|
||||
// "legend_widget", // 禁用图例小部件
|
||||
],
|
||||
// 启用移动端友好的功能
|
||||
enabled_features: [
|
||||
"show_zoom_and_move_buttons_on_touch", // 在触摸设备上显示缩放和移动按钮
|
||||
"adaptive_logo", // 在小屏幕设备上隐藏 TradingView logo
|
||||
"hide_left_toolbar_by_default", // 默认隐藏左侧工具栏
|
||||
"items_favoriting", // 启用收藏功能
|
||||
],
|
||||
// 图表样式配置
|
||||
overrides: {
|
||||
"mainSeriesProperties.candleStyle.upColor": "#0ecb81", // 涨(绿色)
|
||||
"mainSeriesProperties.candleStyle.downColor": "#f6465d", // 跌(红色)
|
||||
"mainSeriesProperties.candleStyle.borderUpColor": "#0ecb81",
|
||||
"mainSeriesProperties.candleStyle.borderDownColor": "#f6465d",
|
||||
"mainSeriesProperties.candleStyle.wickUpColor": "#0ecb81",
|
||||
"mainSeriesProperties.candleStyle.wickDownColor": "#f6465d",
|
||||
},
|
||||
// 时间刻度配置
|
||||
time_scale: {
|
||||
min_bar_spacing: 6,
|
||||
},
|
||||
} satisfies ChartingLibraryWidgetOptions;
|
||||
|
||||
export const TradingViewChart = defineComponent({
|
||||
@@ -37,7 +67,6 @@ export const TradingViewChart = defineComponent({
|
||||
props: {
|
||||
symbol: {
|
||||
type: String,
|
||||
default: "BTCUSDT",
|
||||
},
|
||||
options: {
|
||||
type: Object as PropType<Partial<ChartingLibraryWidgetOptions>>,
|
||||
@@ -46,20 +75,25 @@ export const TradingViewChart = defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const el = ref<HTMLDivElement | null>(null);
|
||||
const { isDark } = useTheme();
|
||||
const widget = ref<IChartingLibraryWidget | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
// eslint-disable-next-line new-cap
|
||||
const widget = new TradingView.widget({
|
||||
widget.value = new TradingView.widget({
|
||||
...defaultOptions,
|
||||
...props.options,
|
||||
theme: isDark.value ? "dark" : "light",
|
||||
container: el.value!,
|
||||
});
|
||||
|
||||
widget.onChartReady(() => {
|
||||
widget.value.onChartReady(() => {
|
||||
console.log("[TradingView]: Chart is ready");
|
||||
});
|
||||
|
||||
console.log("widget", widget.value.activeChart);
|
||||
});
|
||||
|
||||
return () => <div ref={el} class="w-full h-70" />;
|
||||
return () => <div ref={el} class="w-full h-100" />;
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user