diff --git a/package.json b/package.json index eb88b03..bdc6541 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@cssninja/bulma-css-vars": "0.9.2", "@fontsource/noto-sans-kr": "^5.2.5", "@intlify/unplugin-vue-i18n": "^6.0.8", + "@mdi/font": "^7.4.47", "@unhead/addons": "^2.0.14", "@unhead/vue": "^2.0.14", "@vueuse/core": "10.9.0", @@ -24,11 +25,14 @@ "notyf": "3.10.0", "pinia": "2.1.7", "std-env": "^3.9.0", + "technicalindicators": "^3.1.0", "unplugin-vue-router": "^0.15.0", "vite-plugin-pwa": "^1.0.3", + "vite-plugin-vuetify": "^2.1.2", "vue": "^3.5.18", "vue-i18n": "9.13.1", "vue-router": "4.3.2", + "vuetify": "^3.9.5", "yarn": "^1.22.22" }, "devDependencies": { diff --git a/src/App.vue b/src/App.vue index 91d860b..d20f3c6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -10,104 +10,178 @@ import { ref } from 'vue'; */ import LWChart from './components/LWChart.vue'; import {getCandleList} from '/@src/utils/api' +import {calculateMACD, calculateEMA, calculateBollingerBands} from "./utils/meter.js"; /** * Generates sample data for the lightweight chart * @param {Boolean} ohlc Whether generated dat should include open, high, low, and close values * @returns {Array} sample data */ -const socket = new WebSocket("ws://127.0.0.1:8765"); -const sk = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/usdt") -let oldLow = 9999999999; -let oldHigh = 0; -let lock = false +let socket; +const lazyLock = ref(false) const contract = ref('BTC_USDT') +const time = ref('15m') const chartOptions = ref({ layout: { textColor: 'black', background: { type: 'solid', color: 'white' }, + panes: { + separatorColor: '#FF0000', + }, }, - height: 800, + height: 1300, timeScale: { timeVisible: true, // 분/초까지 표시 secondsVisible: false // 초 숨기고 싶으면 false }, + }); -const candleTick = ref([]); -const seriesOptions = ref({ + +const candleTick = ref([]) +const ema = ref([]) +const macd = ref({}) +const bb = ref({}) + +const selectCoins = [ + { title: 'BTC_USDT'}, + { title: 'ETH_USDT'}, + { title: 'SOL_USDT'}, + { title: 'XRP_USDT'}, + { title: 'LINK_USDT'}, +] +const selectTimes = [ + { title: '5m'}, + { title: '15m'}, + { title: '30m'}, + { title: '1h'}, + { title: '4h'}, + { title: '8h'}, + { title: '1d'}, + { title: '7d'}, +] + +const line_color = [ + '#AD81FF', + '#FFBAB5', + '#6BFF74', + '#FFFC7B', + '#27FFE6', + '#225EFF' +] + + +const emaLineOptions = ref({ + lineWidth: 2, + color: '#AD81FF', + title: 'EMA', +}); + +const candleOptions = ref({ upColor: '#26a69a', downColor: '#ef5350', borderVisible: false, wickUpColor: '#26a69a', wickDownColor: '#ef5350', }); + +const macdLineOptions = ref({ + lineWidth: 2, + color: '#FFBAB5' +}); + +const macdSignalOptions = ref({ + lineWidth: 2, + color: '#AD81FF' +}); + +const macdHistogramOptions = ref({ + lineWidth: 2, + color: '#27FFE6' +}); + +const upperBbOptions = ref({ + color: '#006AFF', // 주황색 계열 + lineStyle: 0, // 점선 스타일 (선택) + lineWidth: 2, + title: 'UpperBB', +}) + +const middleBbOptions = ref({ + color: '#006AFF', // 주황색 계열 + lineStyle: 1, // 점선 스타일 (선택) + lineWidth: 0, + title: 'MiddleBB(SMA)', +}) + +const lowerBbOptions = ref({ + color: '#006AFF', // 주황색 계열 + lineStyle: 0, // 점선 스타일 (선택) + lineWidth: 2, + title: 'LowerBB', +}) + const chartType = ref('candlestick'); const lwChart = ref(); onMounted(() => { - getCandleList('BTC_USDT', '1m').then(data => { - candleTick.value = data - }) - - - tradeSocket() - candleSocket('1m') - + init(contract.value, time.value) }) -const tradeSocket = () => { - sk.addEventListener('open', (event) => { - sk.send(JSON.stringify({"time" : curToUnix(), "channel" : "futures.trades", - "event": "subscribe", "payload" : [contract.value]})) +const init = (cont, ti) => { + socket = new WebSocket("ws://127.0.0.1:8765"); + const dt = new Date() + getCandleList(cont, ti, Math.round(dt.getTime()/1000)).then(data => { + ema.value = calculateEMA(data, 20) + macd.value = calculateMACD(data) + bb.value = calculateBollingerBands(data) + candleTick.value = data + console.log(bb.value) }) - sk.addEventListener('message', (message) => { - const getDa = JSON.parse(message.data) - if (getDa.event === 'update') { - // console.log(getDa.result[0].price) + candleSocket(cont, ti) +} - if(candleTick.value.length === 0) return +const distroy = (cont) => { + candleTick.value = [] +} - const lastCandle = candleTick.value[candleTick.value.length - 1] +const lazyLoad = async (ti) => { + lazyLock.value = true + getCandleList(contract.value, time.value, ti).then(data => { + candleTick.value = [...data, ...candleTick.value] + ema.value = calculateEMA(data, 20) + macd.value = calculateMACD(data) + bb.value = calculateBollingerBands(data) + setTimeout(() => {lazyLock.value = false}, 3000) - const price = parseFloat(getDa.result[0].price); - lastCandle.close = price - if(oldLow > candleTick.value[candleTick.value.length - 1].close) { - lastCandle.low = price - oldLow = price; - } - if(oldHigh < candleTick.value[candleTick.value.length - 1].open) { - lastCandle.high = price - oldHigh = price; - } - - candleTick.value[candleTick.value.length - 1] = lastCandle - candleTick.value = [...candleTick.value] - } }) } -const candleSocket = (time) => { +const candleSocket = (cont, ti) => { socket.addEventListener('open', (event) => { - socket.send(JSON.stringify({"type":"subscribe", "channel": contract.value, "time": time})) + socket.send(JSON.stringify({"type":"subscribe", "channel": cont, "time": ti})) }) socket.addEventListener('message', (message) => { - lock = true - const getDa = JSON.parse(message.data) - const msg = JSON.parse(getDa.msg) - const tick = msg.data[0] + const msg = JSON.parse(message.data) + const item = JSON.parse(msg.msg) + const ticks = candleTick.value - if(tick.time !== ticks[ticks.length - 1].time) { - // ticks[ticks.length - 1] = tick - // candleTick.value = [...ticks] - // } else { - const ticks = candleTick.value - ticks.push(tick) + if(item.t === ticks[ticks.length - 1].t) { + ticks[ticks.length - 1] = item + candleTick.value = [...ticks] + } else { + ticks.push(item) candleTick.value = [...ticks] } + + ema.value = calculateEMA(ticks, 20) + macd.value = calculateMACD(ticks) + bb.value = calculateBollingerBands(ticks) + lazyLock.value = false + }) } @@ -120,14 +194,6 @@ const curToUnix = (() => { }) -function randomShade() { - return Math.round(Math.random() * 255); -} - -const randomColor = (alpha = 1) => { - return `rgba(${randomShade()}, ${randomShade()}, ${randomShade()}, ${alpha})`; -}; - const colorsTypeMap = { area: [ ['topColor', 0.4], @@ -158,64 +224,82 @@ const colorsTypeMap = { line: [['color', 1]], }; -// Set a random colour for the series as an example of how -// to apply new options to series. A similar appraoch will work on the -// option properties. -const changeColors = () => { - const options = {}; - const colorsToSet = colorsTypeMap[chartType.value]; - colorsToSet.forEach((c) => { - options[c[0]] = randomColor(c[1]); - }); - seriesOptions.value = options; -}; +watch(contract, newVal => { + distroy() + console.log(newVal); + init(newVal, time.value) +}) -const changeData = () => { - const candlestickTypeData = ['candlestick', 'bar'].includes(chartType.value); - const newData = generateSampleData(candlestickTypeData); - data.value = newData; - if (chartType.value === 'baseline') { - const average = - newData.reduce((s, c) => { - return s + c.value; - }, 0) / newData.length; - seriesOptions.value = { baseValue: { type: 'price', price: average, format: 'yyyy-MM-dd HH:mm' } }; - } -}; - -const changeType = () => { - const types = [ - 'line', - 'area', - 'baseline', - 'histogram', - 'candlestick', - 'bar', - ].filter((t) => t !== chartType.value); - const randIndex = Math.round(Math.random() * (types.length - 1)); - chartType.value = types[randIndex]; - changeData(); - - // call a method on the component. - lwChart.value.fitContent(); -}; +watch(time, newVal => { + distroy() + console.log(newVal); + init(contract.value, newVal) +})