mirror of
https://git.hmsn.ink/coin/chart.git
synced 2026-03-20 00:02:17 +09:00
432 lines
8.9 KiB
Vue
432 lines
8.9 KiB
Vue
<script setup>
|
|
import {
|
|
ref,
|
|
onMounted,
|
|
onUnmounted,
|
|
watch,
|
|
defineExpose,
|
|
defineProps,
|
|
} from 'vue';
|
|
import {
|
|
createChart,
|
|
LineSeries,
|
|
AreaSeries,
|
|
BarSeries,
|
|
CandlestickSeries,
|
|
HistogramSeries,
|
|
BaselineSeries,
|
|
} from 'lightweight-charts';
|
|
|
|
const props = defineProps({
|
|
type: {
|
|
type: String,
|
|
default: 'line',
|
|
},
|
|
data: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
ema: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
macd: {
|
|
type: Object,
|
|
},
|
|
bb: {
|
|
type: Object,
|
|
},
|
|
rsi: {
|
|
type: Object,
|
|
},
|
|
autosize: {
|
|
default: true,
|
|
type: Boolean,
|
|
},
|
|
chartOptions: {
|
|
type: Object,
|
|
},
|
|
emaOptions: {
|
|
type: Object,
|
|
},
|
|
candleOptions: {
|
|
type: Object,
|
|
},
|
|
macdLineOptions: {
|
|
type: Object,
|
|
},
|
|
macdSignalOptions: {
|
|
type: Object,
|
|
},
|
|
macdHistogramOptions: {
|
|
type: Object,
|
|
},
|
|
rsiLineOptions: {
|
|
type: Object,
|
|
},
|
|
upperBbOptions: {
|
|
type: Object,
|
|
},
|
|
middleBbOptions: {
|
|
type: Object,
|
|
},
|
|
lowerBbOptions: {
|
|
type: Object,
|
|
},
|
|
timeScaleOptions: {
|
|
type: Object,
|
|
},
|
|
priceScaleOptions: {
|
|
type: Object,
|
|
},
|
|
lazyLock: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
const emits = defineEmits(['lazyLoad', 'mouseMove'])
|
|
|
|
function getChartSeriesDefinition(type) {
|
|
switch (type.toLowerCase()) {
|
|
case 'line':
|
|
return LineSeries;
|
|
case 'area':
|
|
return AreaSeries;
|
|
case 'bar':
|
|
return BarSeries;
|
|
case 'candlestick':
|
|
return CandlestickSeries;
|
|
case 'histogram':
|
|
return HistogramSeries;
|
|
case 'baseline':
|
|
return BaselineSeries;
|
|
}
|
|
return LineSeries;
|
|
}
|
|
|
|
// Lightweight Charts™ instances are stored as normal JS variables
|
|
// If you need to use a ref then it is recommended that you use `shallowRef` instead
|
|
let emaLine;
|
|
let candlestick;
|
|
let chart;
|
|
|
|
let macdLine;
|
|
let macdSignalLine;
|
|
let macdHistogram;
|
|
let macdPane;
|
|
let rsiPane;
|
|
|
|
let upperBb;
|
|
let middleBb;
|
|
let lowerBb;
|
|
|
|
let rsiLine;
|
|
|
|
const chartContainer = ref();
|
|
|
|
const fitContent = () => {
|
|
if (!chart) return;
|
|
chart.timeScale().fitContent();
|
|
};
|
|
|
|
const getChart = () => {
|
|
return chart;
|
|
};
|
|
|
|
defineExpose({fitContent, getChart});
|
|
|
|
// Auto resizes the chart when the browser window is resized.
|
|
const resizeHandler = () => {
|
|
if (!chart || !chartContainer.value) return;
|
|
const dimensions = chartContainer.value.getBoundingClientRect();
|
|
chart.resize(dimensions.width, dimensions.height);
|
|
};
|
|
|
|
// Creates the chart series and sets the data.
|
|
const addSeriesAndData = props => {
|
|
const candleStickDefinition = getChartSeriesDefinition(props.type);
|
|
candlestick = chart.addSeries(candleStickDefinition, props.candleOptions);
|
|
let oldT = 0;
|
|
let list = []
|
|
props.data.forEach(item => {
|
|
if (item.t !== oldT) {
|
|
list.push({
|
|
time: Number(item.t),
|
|
open: Number(item.o),
|
|
high: Number(item.h),
|
|
low: Number(item.l),
|
|
close: Number(item.c),
|
|
volume: Number(item.v),
|
|
})
|
|
}
|
|
oldT = item.t
|
|
})
|
|
candlestick.setData(list);
|
|
|
|
const seriesDefinition = getChartSeriesDefinition('line')
|
|
emaLine = chart.addSeries(seriesDefinition, props.emaOptions)
|
|
emaLine.setData(props.ema)
|
|
|
|
macdLine = macdPane.addSeries(seriesDefinition, props.macdLineOptions)
|
|
macdSignalLine = macdPane.addSeries(seriesDefinition, props.macdSignalOptions)
|
|
macdHistogram = macdPane.addSeries(getChartSeriesDefinition('histogram'), props.macdHistogramOptions)
|
|
if(Object.keys(props.macd).length) {
|
|
macdLine.setData(props.macd.m)
|
|
macdHistogram.setData(props.macd.h)
|
|
macdSignalLine.setData(props.macd.s)
|
|
}
|
|
|
|
upperBb = chart.addSeries(seriesDefinition, props.upperBbOptions);
|
|
middleBb = chart.addSeries(seriesDefinition, props.middleBbOptions);
|
|
lowerBb = chart.addSeries(seriesDefinition, props.lowerBbOptions);
|
|
if(Object.keys(props.macd).length) {
|
|
upperBb.setData(props.bb.u);
|
|
middleBb.setData(props.bb.m);
|
|
lowerBb.setData(props.bb.l);
|
|
}
|
|
|
|
rsiLine = rsiPane.addSeries(seriesDefinition, props.rsiLineOptions)
|
|
if(Object.keys(props.macd).length) {
|
|
rsiLine.setData(props.rsi)
|
|
}
|
|
// console.log(3)
|
|
};
|
|
|
|
onMounted(() => {
|
|
// Create the Lightweight Charts Instance using the container ref.
|
|
chart = createChart(chartContainer.value, props.chartOptions);
|
|
macdPane = chart.addPane();
|
|
macdPane.setStretchFactor(0.5)
|
|
rsiPane = chart.addPane();
|
|
rsiPane.setStretchFactor(0.4)
|
|
|
|
addSeriesAndData(props);
|
|
|
|
if (props.priceScaleOptions) {
|
|
chart.priceScale().applyOptions(props.priceScaleOptions);
|
|
}
|
|
|
|
if (props.timeScaleOptions) {
|
|
chart.timeScale().applyOptions(props.timeScaleOptions);
|
|
}
|
|
|
|
chart.timeScale().fitContent();
|
|
|
|
if (props.autosize) {
|
|
window.addEventListener('resize', resizeHandler);
|
|
}
|
|
chart.timeScale().subscribeVisibleLogicalRangeChange(onVisibleLogicalRangeChanged);
|
|
|
|
chart.subscribeCrosshairMove(onCrosshairMove)
|
|
|
|
chart.subscribeClick(onClick);
|
|
|
|
});
|
|
|
|
function onClick(dd) {
|
|
console.log(dd)
|
|
}
|
|
|
|
function onVisibleLogicalRangeChanged(newVisibleLogicalRange) {
|
|
const barsInfo = candlestick.barsInLogicalRange(newVisibleLogicalRange);
|
|
// if there less than 50 bars to the left of the visible area
|
|
if (barsInfo !== null && barsInfo.barsBefore < 500 && !props.lazyLock) {
|
|
|
|
// try to load additional historical data and prepend it to the series data
|
|
emits('lazyLoad', props.data[0].t - 5000);
|
|
}
|
|
}
|
|
|
|
function onCrosshairMove(param) {
|
|
|
|
if (param.point === null || param.time === undefined) {
|
|
return;
|
|
}
|
|
emits('mouseMove', param.seriesData.get(candlestick))
|
|
//
|
|
// // 현재 포인터 아래의 캔들 데이터 가져오기
|
|
// const data = param.seriesData.get(candleSeries);
|
|
// console.log(data)
|
|
}
|
|
|
|
onUnmounted(() => {
|
|
if (chart) {
|
|
chart.remove();
|
|
chart = null;
|
|
}
|
|
if (candlestick) {
|
|
candlestick = null;
|
|
}
|
|
window.removeEventListener('resize', resizeHandler);
|
|
});
|
|
|
|
/*
|
|
* Watch for changes to any of the component properties.
|
|
|
|
* If an options property is changed then we will apply those options
|
|
* on top of any existing options previously set (since we are using the
|
|
* `applyOptions` method).
|
|
*
|
|
* If there is a change to the chart type, then the existing series is removed
|
|
* and the new series is created, and assigned the data.
|
|
*
|
|
*/
|
|
watch(
|
|
() => props.autosize,
|
|
enabled => {
|
|
if (!enabled) {
|
|
window.removeEventListener('resize', resizeHandler);
|
|
return;
|
|
}
|
|
window.addEventListener('resize', resizeHandler);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.type,
|
|
newType => {
|
|
if (candlestick && chart) {
|
|
chart.removeSeries(candlestick);
|
|
}
|
|
addSeriesAndData(props);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.data,
|
|
newData => {
|
|
if (!candlestick) return;
|
|
let oldT = 0;
|
|
let list = []
|
|
newData.forEach(item => {
|
|
if (item.t !== oldT) {
|
|
|
|
list.push({
|
|
time: item.t,
|
|
open: Number(item.o),
|
|
high: Number(item.h),
|
|
low: Number(item.l),
|
|
close: Number(item.c),
|
|
volume: Number(item.v),
|
|
})
|
|
}
|
|
oldT = item.t
|
|
})
|
|
candlestick.setData(list);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.ema,
|
|
newData => {
|
|
if (!emaLine) return;
|
|
emaLine.setData(newData);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.macd,
|
|
newData => {
|
|
if (!macdLine || !macdHistogram || !macdSignalLine) return;
|
|
macdLine.setData(newData.m);
|
|
macdHistogram.setData(newData.h);
|
|
macdSignalLine.setData(newData.s);
|
|
}
|
|
);
|
|
|
|
|
|
watch(
|
|
() => props.bb,
|
|
newData => {
|
|
if (!upperBb || !middleBb || !lowerBb) return;
|
|
upperBb.setData(newData.u);
|
|
middleBb.setData(newData.m);
|
|
lowerBb.setData(newData.l);
|
|
}
|
|
);
|
|
|
|
|
|
watch(
|
|
() => props.rsi,
|
|
newData => {
|
|
if (!rsiLine) return;
|
|
rsiLine.setData(props.rsi);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.chartOptions,
|
|
newOptions => {
|
|
if (!chart) return;
|
|
chart.applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.candleOptions,
|
|
newOptions => {
|
|
if (!candlestick) return;
|
|
candlestick.applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.emaOptions,
|
|
newOptions => {
|
|
if (!emaLine) return;
|
|
emaLine.applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.macdLineOptions,
|
|
newOptions => {
|
|
if (!macdLine) return;
|
|
macdLine.applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.macdSignalOptions,
|
|
newOptions => {
|
|
if (!macdSignalLine) return;
|
|
macdSignalLine.applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.macdHistogramOptions,
|
|
newOptions => {
|
|
if (!macdHistogram) return;
|
|
macdHistogram.applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.priceScaleOptions,
|
|
newOptions => {
|
|
if (!chart) return;
|
|
chart.priceScale().applyOptions(newOptions);
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.timeScaleOptions,
|
|
newOptions => {
|
|
if (!chart) return;
|
|
chart.timeScale().applyOptions(newOptions);
|
|
}
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="lw-chart" ref="chartContainer"></div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.lw-chart {
|
|
height: 100%;
|
|
}
|
|
</style>
|