diff --git a/src/tradingview/datafeed.ts b/src/tradingview/datafeed.ts index 1fd026d..0a030f4 100644 --- a/src/tradingview/datafeed.ts +++ b/src/tradingview/datafeed.ts @@ -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 = 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 连接 diff --git a/src/tradingview/index.tsx b/src/tradingview/index.tsx index 04ebe16..1d778ea 100644 --- a/src/tradingview/index.tsx +++ b/src/tradingview/index.tsx @@ -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>, @@ -46,20 +75,25 @@ export const TradingViewChart = defineComponent({ }, setup(props) { const el = ref(null); + const { isDark } = useTheme(); + const widget = ref(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 () =>
; + return () =>
; }, }); diff --git a/src/views/trade/index.vue b/src/views/trade/index.vue index f9b645d..7ecb695 100644 --- a/src/views/trade/index.vue +++ b/src/views/trade/index.vue @@ -33,12 +33,7 @@ const mode = ref<"buy" | "sell">("buy"); - -
+
@@ -52,7 +47,8 @@ const mode = ref<"buy" | "sell">("buy");
-
+ +