|
@@ -0,0 +1,166 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="app-candlestick">
|
|
|
|
|
+ <HQChart @ready="onReady" />
|
|
|
|
|
+ <Tabs class="app-tabs--indicator" :data-list="tabs" @change="changeIndex" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+import { shallowRef, watch, computed } from 'vue'
|
|
|
|
|
+import { Chart } from 'hqchart'
|
|
|
|
|
+import { ChartCycleType } from '@/constants/chart'
|
|
|
|
|
+import { useFuturesStore } from '@/stores'
|
|
|
|
|
+import { useDataset } from '@/hooks/hqchart/candlestick/dataset'
|
|
|
|
|
+import { NetworkFilterData, NetworkFilterCallback } from '@/hooks/hqchart/candlestick/types'
|
|
|
|
|
+import HQChart from '@/components/base/hqchart/index.vue'
|
|
|
|
|
+import Tabs from '@/components/base/tabs/index.vue'
|
|
|
|
|
+
|
|
|
|
|
+const props = defineProps({
|
|
|
|
|
+ symbol: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+ goodsCode: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ required: true
|
|
|
|
|
+ },
|
|
|
|
|
+ cycleType: {
|
|
|
|
|
+ type: Number,
|
|
|
|
|
+ required: true
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const { cycleType, networkFilter, updateLastData } = useDataset(props.goodsCode, props.cycleType)
|
|
|
|
|
+const { quoteWatch, getGoodsQuote } = useFuturesStore()
|
|
|
|
|
+const goods = getGoodsQuote(props.goodsCode)
|
|
|
|
|
+const chartInstance = shallowRef() // 图表实例
|
|
|
|
|
+
|
|
|
|
|
+const tabs = [
|
|
|
|
|
+ { label: 'VOL', value: 'VOL' },
|
|
|
|
|
+ { label: 'MACD', value: 'MACD' },
|
|
|
|
|
+ { label: 'KDJ', value: 'KDJ' },
|
|
|
|
|
+ { label: 'RSI', value: 'RSI' },
|
|
|
|
|
+ { label: 'WR', value: 'WR' },
|
|
|
|
|
+ { label: 'OBV', value: 'OBV' },
|
|
|
|
|
+ { label: 'CCI', value: 'CCI' },
|
|
|
|
|
+ { label: 'DMI', value: 'DMI' }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+// 自定义品种小数位
|
|
|
|
|
+// https://blog.csdn.net/jones2000/article/details/106592730/
|
|
|
|
|
+Chart.MARKET_SUFFIX_NAME.GetCustomDecimal = () => goods.value?.decimalplace ?? 2
|
|
|
|
|
+
|
|
|
|
|
+// 图表周期
|
|
|
|
|
+const period = computed(() => {
|
|
|
|
|
+ switch (props.cycleType) {
|
|
|
|
|
+ case ChartCycleType.Day:
|
|
|
|
|
+ return 0
|
|
|
|
|
+ case ChartCycleType.Minutes:
|
|
|
|
|
+ return 4
|
|
|
|
|
+ case ChartCycleType.Minutes5:
|
|
|
|
|
+ return 5
|
|
|
|
|
+ case ChartCycleType.Minutes30:
|
|
|
|
|
+ return 7
|
|
|
|
|
+ case ChartCycleType.Minutes60:
|
|
|
|
|
+ return 8
|
|
|
|
|
+ case ChartCycleType.Hours4:
|
|
|
|
|
+ return 12
|
|
|
|
|
+ default:
|
|
|
|
|
+ return -1
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// https://blog.csdn.net/jones2000/article/details/90272733
|
|
|
|
|
+const chartOption = {
|
|
|
|
|
+ Symbol: props.symbol,
|
|
|
|
|
+ Type: '历史K线图',
|
|
|
|
|
+ IsAutoUpdate: false,
|
|
|
|
|
+ IsApiPeriod: true, // 每次切换周期请求接口数据
|
|
|
|
|
+ NetworkFilter: (data: NetworkFilterData, callback: NetworkFilterCallback) => networkFilter(data, callback),
|
|
|
|
|
+ // https://blog.csdn.net/jones2000/article/details/102928907
|
|
|
|
|
+ Windows: [
|
|
|
|
|
+ { Index: 'MA', Change: false },
|
|
|
|
|
+ { Index: 'VOL', Close: false }
|
|
|
|
|
+ ],
|
|
|
|
|
+ IsShowCorssCursorInfo: false,
|
|
|
|
|
+ // https://blog.csdn.net/jones2000/article/details/97682466
|
|
|
|
|
+ CorssCursorInfo: {
|
|
|
|
|
+ Left: 0,
|
|
|
|
|
+ Right: 1,
|
|
|
|
|
+ },
|
|
|
|
|
+ Border: {
|
|
|
|
|
+ Left: 0,
|
|
|
|
|
+ Right: 0,
|
|
|
|
|
+ Top: 30,
|
|
|
|
|
+ Bottom: 25,
|
|
|
|
|
+ AutoLeft: { Blank: 15, MinWidth: 30 },
|
|
|
|
|
+ AutoRight: { Blank: 15, MinWidth: 30 },
|
|
|
|
|
+ },
|
|
|
|
|
+ KLine: {
|
|
|
|
|
+ Right: 1,
|
|
|
|
|
+ // [30001-32000) 秒周期
|
|
|
|
|
+ // 0=日线 1=周线 2=月线 3=年线 9=季线 [40001-50000) 自定义日线
|
|
|
|
|
+ // 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 11=120分钟 12=240分钟 [20001-30000) 自定义分钟
|
|
|
|
|
+ Period: period.value,
|
|
|
|
|
+ PageSize: 50,
|
|
|
|
|
+ // 最后数据和右边框空白间距,空白的宽度=RightSpaceCount*k线宽度
|
|
|
|
|
+ RightSpaceCount: 1,
|
|
|
|
|
+ // https://blog.csdn.net/jones2000/article/details/104443471
|
|
|
|
|
+ IsShowTooltip: true,
|
|
|
|
|
+ },
|
|
|
|
|
+ KLineTitle:
|
|
|
|
|
+ {
|
|
|
|
|
+ IsShowName: false, // 不显示股票名称
|
|
|
|
|
+ IsShowSettingInfo: false // 不显示周期/复权
|
|
|
|
|
+ },
|
|
|
|
|
+ Frame: [
|
|
|
|
|
+ {
|
|
|
|
|
+ Custom: [
|
|
|
|
|
+ {
|
|
|
|
|
+ Type: 0,
|
|
|
|
|
+ Position: 'right',
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ IsShowRightText: false // 是否显示Y轴右侧刻度
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ IsShowRightText: false // 是否显示Y轴右侧刻度
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 切换指标
|
|
|
|
|
+const changeIndex = (index: number) => {
|
|
|
|
|
+ // ChangeIndex(windowIndex, indexName, option)
|
|
|
|
|
+ chartInstance.value.ChangeIndex(1, tabs[index].value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onReady = (chart: unknown) => {
|
|
|
|
|
+ chartInstance.value = chart
|
|
|
|
|
+ chartInstance.value.SetOption(chartOption)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+quoteWatch(props.goodsCode, (q) => {
|
|
|
|
|
+ const data = updateLastData(q)
|
|
|
|
|
+ const { JSChartContainer } = chartInstance.value ?? {}
|
|
|
|
|
+ // 在 SetOption 之后才会出现 JSChartContainer 属性
|
|
|
|
|
+ if (JSChartContainer && data.length) {
|
|
|
|
|
+ JSChartContainer.RecvMinuteRealtimeDataV2({
|
|
|
|
|
+ symbol: props.symbol,
|
|
|
|
|
+ data
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+watch(() => props.cycleType, (val) => {
|
|
|
|
|
+ cycleType.value = val
|
|
|
|
|
+ if (period.value > -1) {
|
|
|
|
|
+ // 切换周期
|
|
|
|
|
+ chartInstance.value.ChangePeriod(period.value)
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="less">
|
|
|
|
|
+@import './index.less';
|
|
|
|
|
+</style>
|