diff --git a/src/App.vue b/src/App.vue
index 0cd676a..a32c418 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -24,15 +24,20 @@ const { width, height } = useWindowSize()
let socket;
const lazyLock = ref(false)
-const positionData = ref('')
+const positionHtml = ref('')
+const rsiHtml = ref('')
+const macdHtml = ref('')
+const bbHtml = ref('')
+const emaHtml = ref('')
const scrollContainer1 = ref()
const scrollContainer2 = ref()
const signal1 = ref([])
const signal2 = ref([])
-const contract = ref('ETH_USDT')
+const contract = ref(window.sessionStorage.getItem('contract') || 'ETH_USDT')
+
/* Interval : "10s", "1m", "5m", "15m", "30m", "1h", "4h", "8h", "1d", "7d"*/
-const time = ref('5m')
+const time = ref(window.sessionStorage.getItem('time')|| '10s')
const chartOptions = ref({
layout: {
textColor: 'black',
@@ -89,7 +94,6 @@ const line_color = [
const emaLineOptions = ref({
lineWidth: 2,
color: '#AD81FF',
- title: 'EMA',
});
const candleOptions = ref({
@@ -103,46 +107,39 @@ const candleOptions = ref({
const macdLineOptions = ref({
lineWidth: 2,
color: '#FFBAB5',
- title: 'MACD',
});
const macdSignalOptions = ref({
lineWidth: 2,
color: '#AD81FF',
- title: 'SIGNAL',
});
const macdHistogramOptions = ref({
lineWidth: 2,
color: '#27FFE6',
- title: 'HISTOGRAM',
});
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 rsiLineOptions = ref({
lineWidth: 2,
color: '#AD81FF',
- title: 'RSI',
});
const chartType = ref('candlestick');
@@ -154,7 +151,7 @@ onMounted(() => {
})
const init = (cont, ti) => {
- socket = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/usdt");
+ // socket = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/usdt");
const dt = new Date()
getCandleList(cont, ti, Math.round(dt.getTime()/1000)).then(data => {
ema.value = calculateEMA(data, 20)
@@ -165,7 +162,7 @@ const init = (cont, ti) => {
// console.table(entrySignals(data))
candleTick.value = data
})
- candleSocket(cont, ti)
+ socket = candleSocket(cont, ti)
}
const distroy = () => {
@@ -187,73 +184,98 @@ const lazyLoad = async (ti) => {
}
let loopIdx = 0
+
+
+
const candleSocket = (cont, ti) => {
- socket.addEventListener('open', (event) => {
- // socket.send(JSON.stringify({"type":"subscribe", "channel": cont, "time": ti}))
- socket.send(JSON.stringify({"time" : curToUnix(), "channel" : "futures.candlesticks","event": "subscribe", "payload" : [ti, cont]}))
- socket.send(JSON.stringify({"time" : curToUnix(), "channel" : "futures.trades","event": "subscribe", "payload" : [cont]}))
- })
+ return useWebSocket('wss://fx-ws.gateio.ws/v4/ws/usdt', {
+ onConnected: async ws => {
+ ws.send(JSON.stringify({"time" : curToUnix(), "channel" : "futures.candlesticks","event": "subscribe", "payload" : [ti, cont]}))
+ ws.send(JSON.stringify({"time" : curToUnix(), "channel" : "futures.tickers","event": "subscribe", "payload" : [cont]}))
+ },
+ onDisconnected: (ws, event) => {
+ },
+ onMessage: (ws, message) => {
+ const msg = JSON.parse(message.data)
+ const ticks = candleTick.value
+ if(msg.channel === 'futures.candlesticks') {
+ if(msg.event === 'update') {
+ const item = msg.result[0]
+ // console.log(ticks[ticks.length - 1].t, item.t, new Date().getTime() /1000)
- socket.addEventListener('message', (message) => {
- const msg = JSON.parse(message.data)
- if(msg.channel === 'futures.candlesticks') {
- if(msg.event === 'update') {
- const item = msg.result[0]
-
- const ticks = candleTick.value
- if(item.t === ticks[ticks.length - 1].t) {
- ticks[ticks.length - 1] = item
- candleTick.value = [...ticks]
- } else {
- ticks.push(item)
- candleTick.value = [...ticks]
- }
-
- }
- } else {
- if(msg.event === 'update') {
- const item = msg.result[0]
- // console.log(item)
- const ticks = candleTick.value
- try {
- let diff = 0
- if(time.value === '10s') diff = 10
- else if(time.value === '1m') diff = 60
- else if(time.value === '5m') diff = (60 * 5)
- else if(time.value === '15m') diff = (60 * 15)
- else if(time.value === '30m') diff = (60 * 30)
- else if(time.value === '1h') diff = (60 * 60)
- else if(time.value === '4h') diff = (60 * 60 * 4)
- else if(time.value === '8h') diff = (60 * 60 * 8)
- else if(time.value === '1d') diff = (60 * 60 * 24)
- else if(time.value === '7d') diff = (60 * 60 * 24 * 7)
- if(item.create_time >= ticks[ticks.length - 1].t && item.create_time <= (ticks[ticks.length - 1].t + diff)) {
- ticks[ticks.length - 1].c = item.price
- if(ticks[ticks.length - 1].h < item.price) ticks[ticks.length - 1].h = item.price
- if(ticks[ticks.length - 1].l > item.price) ticks[ticks.length - 1].l = item.price
+ if(item.t === ticks[ticks.length - 1].t) {
+ ticks[ticks.length - 1] = item
candleTick.value = [...ticks]
- ema.value = calculateEMA(ticks, 20)
- macd.value = calculateMACD(ticks)
- bb.value = calculateBollingerBands(ticks)
- rsi.value = calculateRSI(ticks)
- // console.table(entrySignals(ticks))
- const dd = entrySignals(ticks)
- if(dd.length) signal2.value = [...signal2.value, entrySignals(ticks)]
- const tt = swingSignal(ticks)
- console.log(tt)
- if(tt[0] !== 'HOLD') signal1.value = [...signal1.value, swingSignal(ticks)]
}
- } catch(error) {}
-
- loopIdx++;
- if(loopIdx > 1000) {
- console.clear();
- loopIdx = 0;
+ // else {
+ // ticks.push(item)
+ // candleTick.value = [...ticks]
+ // }
}
- // console.log('trade', socketLock)
}
- }
- lazyLock.value = false
+ if(msg.channel === 'futures.tickers') {
+ if(msg.event === 'update') {
+ const item = msg.result[0]
+ try {
+ let diff = 0
+ if(time.value === '10s') diff = 10
+ else if(time.value === '1m') diff = 60
+ else if(time.value === '5m') diff = (60 * 5)
+ else if(time.value === '15m') diff = (60 * 15)
+ else if(time.value === '30m') diff = (60 * 30)
+ else if(time.value === '1h') diff = (60 * 60)
+ else if(time.value === '4h') diff = (60 * 60 * 4)
+ else if(time.value === '8h') diff = (60 * 60 * 8)
+ else if(time.value === '1d') diff = (60 * 60 * 24)
+ else if(time.value === '7d') diff = (60 * 60 * 24 * 7)
+ if(msg.time > ticks[ticks.length - 1].t && msg.time < (ticks[ticks.length - 1].t + diff)) {
+ ticks[ticks.length - 1].c = item.last
+ if(ticks[ticks.length - 1].h < item.last) ticks[ticks.length - 1].h = item.last
+ if(ticks[ticks.length - 1].l > item.last) ticks[ticks.length - 1].l = item.last
+ candleTick.value = [...ticks]
+ ema.value = calculateEMA(ticks, 20)
+ emaHtml.value = `EMA${Math.round(ema.value[ema.value.length - 1].value * 100)/ 100}`
+ macd.value = calculateMACD(ticks)
+ macdHtml.value = `RSI
+ H${Math.round(macd.value.h[macd.value.h.length - 1].value * 100)/ 100}
+ M${Math.round(macd.value.m[macd.value.m.length - 1].value * 100)/ 100}
+ S${Math.round(macd.value.s[macd.value.s.length - 1].value * 100)/ 100}`
+ bb.value = calculateBollingerBands(ticks)
+ bbHtml.value = `BB
+ U${Math.round(bb.value.u[bb.value.u.length - 1].value * 100)/ 100}
+ M${Math.round(bb.value.m[bb.value.m.length - 1].value * 100)/ 100}
+ L${Math.round(bb.value.l[bb.value.l.length - 1].value * 100)/ 100}`
+ rsi.value = calculateRSI(ticks)
+ rsiHtml.value = `BB${Math.round(rsi.value[rsi.value.length - 1].value * 100)/ 100}`
+ // console.table(entrySignals(ticks))
+ const dd = entrySignals(ticks)
+ if(dd.length) signal2.value = [...signal2.value, entrySignals(ticks)]
+ const tt = swingSignal(ticks)
+ // console.log(tt)
+ if(tt[0] !== 'HOLD') signal1.value = [...signal1.value, swingSignal(ticks)]
+ } else if(msg.time >= (ticks[ticks.length - 1].t + diff)) {
+ const newTick = JSON.parse(JSON.stringify(ticks[ticks.length - 1]))
+ newTick.t = newTick.t + diff
+ newTick.c = item.last
+ newTick.o = item.last
+ newTick.l = item.last
+ newTick.h = item.last
+ ticks.push(newTick)
+ candleTick.value = [...ticks]
+ }
+ } catch(error) {}
+
+ // loopIdx++;
+ // if(loopIdx > 1000) {
+ // console.clear();
+ // loopIdx = 0;
+ // }
+ // console.log('trade', socketLock)
+ }
+ }
+ lazyLock.value = false
+ },
+ onError: (ws, msg) => {},
})
}
@@ -304,7 +326,7 @@ const colorsTypeMap = {
};
const mouseMove = (data) => {
- positionData.value = `
+ positionHtml.value = `
O${data == null ? 0 : data.open}
C${data == null ? 0 : data.close}
L${data == null ? 0 : data.low}
@@ -315,11 +337,13 @@ const mouseMove = (data) => {
watch(contract, newVal => {
distroy()
+ window.sessionStorage.setItem('contract', newVal)
init(newVal, time.value)
})
watch(time, newVal => {
distroy()
+ window.sessionStorage.setItem('time', newVal)
init(contract.value, newVal)
})
@@ -341,7 +365,7 @@ watch(signal2, newVal => {
-
+
@@ -403,19 +427,23 @@ watch(signal2, newVal => {
@mouseMove="mouseMove"
ref="lwChart"
/>
-
+
+
+
+
+
-
-
-
- {{i}} {{new Date()}}
-
-
-
-
- {{i}} {{new Date()}}
-
-
+
+
+
+ {{i}} {{new Date()}}
+
+
+
+
+ {{i}} {{new Date()}}
+
+
@@ -432,7 +460,56 @@ watch(signal2, newVal => {
left: 10px;
font-size: 12px;
color: #333;
- background: rgba(255, 255, 255, 0.8);
+ background: transparent;
+ padding: 5px;
+ border-radius: 4px;
+ z-index: 1000;
+}
+
+.bb-info {
+ position: absolute;
+ top: 60px;
+ left: 10px;
+ font-size: 12px;
+ color: #333;
+ background: transparent;
+ padding: 5px;
+ border-radius: 4px;
+ z-index: 1000;
+}
+
+
+.ema-info {
+ position: absolute;
+ top: 75px;
+ left: 10px;
+ font-size: 12px;
+ color: #333;
+ background: transparent;
+ padding: 5px;
+ border-radius: 4px;
+ z-index: 1000;
+}
+
+.macd-info {
+ position: absolute;
+ top: 68%;
+ left: 10px;
+ font-size: 12px;
+ color: #333;
+ background: transparent;
+ padding: 5px;
+ border-radius: 4px;
+ z-index: 1000;
+}
+
+.rsi-info {
+ position: absolute;
+ top: 84%;
+ left: 10px;
+ font-size: 12px;
+ color: #333;
+ background: transparent;
padding: 5px;
border-radius: 4px;
z-index: 1000;
diff --git a/src/utils/sig.ts b/src/utils/sig.ts
index 2026b69..7265974 100644
--- a/src/utils/sig.ts
+++ b/src/utils/sig.ts
@@ -101,6 +101,9 @@ const macd = (data: any) => {
const swingSignal = (data: any) => {
// 1) 기존 지표
+ const macdLine = macd(data).macdLine;
+ const macdLinePrev = macdLine[macdLine.length - 2];
+ const macdLineNext = macdLine[macdLine.length - 1];
const macdHist = macd(data).hist;
const rsi14 = rsi(data, 14);
const bbands = bb(data, 20, 2);
@@ -113,7 +116,7 @@ const swingSignal = (data: any) => {
const currentATR63 = atr63[atr63.length - 1] || 1;
// 변동성 급등 조건
- const volatilityBreak = currentATR14 > currentATR63 * 1.2;
+ const volatilityBreak = currentATR14 > currentATR63 * 1.1;
// 3) 기존 offset 계산
const offsetMacd = data.length - macdHist.length;
@@ -128,6 +131,14 @@ const swingSignal = (data: any) => {
const bbl = bbands.lower[latest - offsetBb];
const bbu = bbands.upper[latest - offsetBb];
+
+ const prevLatest = data.length - 2;
+
+ const prevRsiVal = rsi14[prevLatest - offsetRsi];
+ const prevBbl = bbands.lower[prevLatest - offsetBb];
+ const prevBbu = bbands.upper[prevLatest - offsetBb];
+ const prevClose = Number(data[prevLatest].c);
+
// 시나리오1
// if (volatilityBreak &&
// prev < 0 && hist >= 0 &&
@@ -151,15 +162,30 @@ const swingSignal = (data: any) => {
// return ['SHORT',volatilityBreak, prev, hist, rsiVal, close, bbl]
// 시나리오3
- if (prev < hist &&
- rsiVal <= 35 && close <= bbl)
- return ['LONG', prev, hist, rsiVal, close, bbl]
+ // if (prev < hist &&
+ // rsiVal <= 35 && close <= bbl)
+ // return ['LONG', prev, hist, rsiVal, close, bbl]
+ //
+ // if (prev > hist &&
+ // rsiVal >= 65 && close >= bbu)
+ // return ['SHORT', prev, hist, rsiVal, close, bbl]
- if (prev > hist &&
- rsiVal >= 65 && close >= bbu)
- return ['SHORT', prev, hist, rsiVal, close, bbl]
+ // 시나리오4
+ console.log(`LONG : rsi ${prevRsiVal <= 35} macd : ${macdLineNext}, bb : ${close <= prevBbl} hist: ${prev < hist}\n
+ rsi ${prevRsiVal} macd : ${macdLinePrev} ${macdLineNext}, bb : ${close} ${prevBbl}
+ `)
+ if (prevRsiVal <= 35 && macdLineNext < -3 && close <= prevBbl && prev < hist) {
+ return ['LONG', prevRsiVal, macdLinePrev, macdLineNext, close, prevBbl];
+ }
- return ['HOLD', prev, hist, rsiVal, close, bbl];
+ console.log(`SHORT : rsi ${prevRsiVal>=65} macd : ${macdLineNext}, bb : ${close >= prevBbu} hist: ${prev > hist}\n
+ rsi ${prevRsiVal} macd : ${macdLinePrev} ${macdLineNext}, bb : ${close} ${prevBbu}
+
+ `)
+ if (prevRsiVal >= 65 && macdLinePrev > 3 && close >= prevBbu && prev > hist)
+ return ['SHORT', prevRsiVal, macdLinePrev, macdLineNext, close, prevBbu];
+
+ return ['HOLD', currentATR14 , currentATR63 * 1.2,volatilityBreak, macdLinePrev, macdLineNext, close, bbl];
};
export {swingSignal}
\ No newline at end of file
diff --git a/types/imports.d.ts b/types/imports.d.ts
index 16f9057..2e84490 100644
--- a/types/imports.d.ts
+++ b/types/imports.d.ts
@@ -282,6 +282,7 @@ declare global {
const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
+ const useWebChat: typeof import('../src/utils/ws')['useWebChat']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']