Feature/kline perp stats (#88)
* kline perp wip * fix * make data more common with trading view * datafeeds * fixes * fixes * fix * fix error * better logging
This commit is contained in:
parent
2c8da6f1a7
commit
d9fc7f11ac
|
@ -1,5 +1,10 @@
|
||||||
import { makeApiRequest, parseResolution } from './helpers'
|
import { makeApiRequest, parseResolution } from './helpers'
|
||||||
import { subscribeOnStream, unsubscribeFromStream } from './streaming'
|
import {
|
||||||
|
closeSocket,
|
||||||
|
isOpen,
|
||||||
|
subscribeOnStream,
|
||||||
|
unsubscribeFromStream,
|
||||||
|
} from './streaming'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import {
|
import {
|
||||||
DatafeedConfiguration,
|
DatafeedConfiguration,
|
||||||
|
@ -194,6 +199,7 @@ export default {
|
||||||
resolution as any,
|
resolution as any,
|
||||||
periodParams
|
periodParams
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!bars || bars.length === 0) {
|
if (!bars || bars.length === 0) {
|
||||||
// "noData" should be set if there is no data in the requested period.
|
// "noData" should be set if there is no data in the requested period.
|
||||||
onHistoryCallback([], {
|
onHistoryCallback([], {
|
||||||
|
@ -210,6 +216,7 @@ export default {
|
||||||
onHistoryCallback(bars, {
|
onHistoryCallback(bars, {
|
||||||
noData: false,
|
noData: false,
|
||||||
})
|
})
|
||||||
|
return bars
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[getBars]: Get error', error)
|
console.warn('[getBars]: Get error', error)
|
||||||
onErrorCallback(error)
|
onErrorCallback(error)
|
||||||
|
@ -234,7 +241,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
unsubscribeBars: () => {
|
unsubscribeBars: () => {
|
||||||
console.warn('[unsubscribeBars]')
|
|
||||||
unsubscribeFromStream()
|
unsubscribeFromStream()
|
||||||
},
|
},
|
||||||
|
closeSocket: () => {
|
||||||
|
closeSocket()
|
||||||
|
},
|
||||||
|
name: 'birdeye',
|
||||||
|
isSocketOpen: isOpen,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ const socket = new WebSocket(socketUrl, 'echo-protocol')
|
||||||
|
|
||||||
// Connection opened
|
// Connection opened
|
||||||
socket.addEventListener('open', (_event) => {
|
socket.addEventListener('open', (_event) => {
|
||||||
console.log('[socket] Connected')
|
console.log('[socket] Connected birdeye')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
|
@ -21,7 +21,6 @@ socket.addEventListener('message', (msg) => {
|
||||||
const nextBarTime = getNextBarTime(lastBar, resolution)
|
const nextBarTime = getNextBarTime(lastBar, resolution)
|
||||||
|
|
||||||
let bar
|
let bar
|
||||||
|
|
||||||
if (currTime >= nextBarTime) {
|
if (currTime >= nextBarTime) {
|
||||||
bar = {
|
bar = {
|
||||||
time: nextBarTime,
|
time: nextBarTime,
|
||||||
|
@ -67,6 +66,11 @@ export function subscribeOnStream(
|
||||||
currency: symbolInfo.type || 'usd',
|
currency: symbolInfo.type || 'usd',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[subscribeBars birdeye]')
|
||||||
socket.send(JSON.stringify(msg))
|
socket.send(JSON.stringify(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,5 +79,24 @@ export function unsubscribeFromStream() {
|
||||||
type: 'UNSUBSCRIBE_PRICE',
|
type: 'UNSUBSCRIBE_PRICE',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[unsubscribeBars birdeye]')
|
||||||
socket.send(JSON.stringify(msg))
|
socket.send(JSON.stringify(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function closeSocket() {
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed birdeye')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[closeSocket birdeye]')
|
||||||
|
socket.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOpen(ws?: WebSocket) {
|
||||||
|
const sock = ws || socket
|
||||||
|
return sock.readyState === sock.OPEN
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import { makeApiRequest, parseResolution } from './helpers'
|
import { makeApiRequest, parseResolution } from './helpers'
|
||||||
import { subscribeOnStream, unsubscribeFromStream } from './streaming'
|
import {
|
||||||
|
subscribeOnStream,
|
||||||
|
unsubscribeFromStream,
|
||||||
|
closeSocket,
|
||||||
|
isOpen,
|
||||||
|
} from './streaming'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import {
|
import {
|
||||||
DatafeedConfiguration,
|
DatafeedConfiguration,
|
||||||
|
@ -216,6 +221,7 @@ export default {
|
||||||
onHistoryCallback(bars, {
|
onHistoryCallback(bars, {
|
||||||
noData: false,
|
noData: false,
|
||||||
})
|
})
|
||||||
|
return bars
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[getBars]: Get error', error)
|
console.warn('[getBars]: Get error', error)
|
||||||
onErrorCallback(error)
|
onErrorCallback(error)
|
||||||
|
@ -240,7 +246,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
unsubscribeBars: () => {
|
unsubscribeBars: () => {
|
||||||
console.warn('[unsubscribeBars]')
|
|
||||||
unsubscribeFromStream()
|
unsubscribeFromStream()
|
||||||
},
|
},
|
||||||
|
closeSocket: () => {
|
||||||
|
closeSocket()
|
||||||
|
},
|
||||||
|
name: 'mngo',
|
||||||
|
isSocketOpen: isOpen,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { parseResolution, getNextBarTime } from './helpers'
|
import { getNextBarTime } from './helpers'
|
||||||
|
|
||||||
let subscriptionItem: any = {}
|
let subscriptionItem: any = {}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ const socket = new WebSocket(`wss://api.mngo.cloud/fills/v1/`)
|
||||||
|
|
||||||
// Connection opened
|
// Connection opened
|
||||||
socket.addEventListener('open', (_event) => {
|
socket.addEventListener('open', (_event) => {
|
||||||
console.log('[socket] Connected')
|
console.log('[socket] Connected mngo')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
|
@ -16,7 +16,6 @@ socket.addEventListener('message', (msg) => {
|
||||||
|
|
||||||
if (!data.event) return console.warn(data)
|
if (!data.event) return console.warn(data)
|
||||||
if (data.event.maker) return
|
if (data.event.maker) return
|
||||||
|
|
||||||
const currTime = new Date(data.event.timestamp).getTime()
|
const currTime = new Date(data.event.timestamp).getTime()
|
||||||
const lastBar = subscriptionItem.lastBar
|
const lastBar = subscriptionItem.lastBar
|
||||||
const resolution = subscriptionItem.resolution
|
const resolution = subscriptionItem.resolution
|
||||||
|
@ -66,7 +65,11 @@ export function subscribeOnStream(
|
||||||
command: 'subscribe',
|
command: 'subscribe',
|
||||||
marketId: 'HwhVGkfsSQ9JSQeQYu2CbkRCLvsh3qRZxG6m4oMVwZpN',
|
marketId: 'HwhVGkfsSQ9JSQeQYu2CbkRCLvsh3qRZxG6m4oMVwZpN',
|
||||||
}
|
}
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[subscribeBars mngo]')
|
||||||
socket.send(JSON.stringify(msg))
|
socket.send(JSON.stringify(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +78,24 @@ export function unsubscribeFromStream() {
|
||||||
command: 'unsubscribe',
|
command: 'unsubscribe',
|
||||||
marketId: 'HwhVGkfsSQ9JSQeQYu2CbkRCLvsh3qRZxG6m4oMVwZpN',
|
marketId: 'HwhVGkfsSQ9JSQeQYu2CbkRCLvsh3qRZxG6m4oMVwZpN',
|
||||||
}
|
}
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[unsubscribeBars mngo]')
|
||||||
socket.send(JSON.stringify(msg))
|
socket.send(JSON.stringify(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function closeSocket() {
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed mngo')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[closeSocket mngo]')
|
||||||
|
socket.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOpen(ws?: WebSocket) {
|
||||||
|
const sock = ws || socket
|
||||||
|
return sock.readyState === sock.OPEN
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
|
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import klinecharts, { init, dispose, KLineData } from 'klinecharts'
|
import klinecharts, { init, dispose } from 'klinecharts'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
import usePrevious from '@components/shared/usePrevious'
|
import usePrevious from '@components/shared/usePrevious'
|
||||||
import Modal from '@components/shared/Modal'
|
import Modal from '@components/shared/Modal'
|
||||||
|
@ -18,16 +18,12 @@ import {
|
||||||
} from 'utils/kLineChart'
|
} from 'utils/kLineChart'
|
||||||
import Loading from '@components/shared/Loading'
|
import Loading from '@components/shared/Loading'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { useTheme } from 'next-themes'
|
|
||||||
import { COLORS } from 'styles/colors'
|
|
||||||
import { IconButton } from '@components/shared/Button'
|
import { IconButton } from '@components/shared/Button'
|
||||||
import { ArrowsPointingOutIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
import { ArrowsPointingOutIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
||||||
import { queryBars } from 'apis/birdeye/datafeed'
|
import spotDataFeed from 'apis/birdeye/datafeed'
|
||||||
import {
|
import perpDataFeed from 'apis/mngo/datafeed'
|
||||||
getNextBarTime,
|
import { sleep } from 'utils'
|
||||||
parseResolution,
|
import { useKlineChart } from 'hooks/useKlineChart'
|
||||||
socketUrl,
|
|
||||||
} from 'apis/birdeye/helpers'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
setIsFullView?: Dispatch<SetStateAction<boolean>>
|
setIsFullView?: Dispatch<SetStateAction<boolean>>
|
||||||
|
@ -35,233 +31,12 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
const { theme } = useTheme()
|
const { styles } = useKlineChart()
|
||||||
const styles = {
|
|
||||||
grid: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
candle: {
|
|
||||||
bar: {
|
|
||||||
upColor: COLORS.UP[theme],
|
|
||||||
downColor: COLORS.DOWN[theme],
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
labels: ['', 'O:', 'C:', 'H:', 'L:', 'V:'],
|
|
||||||
text: {
|
|
||||||
size: 12,
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
marginLeft: 8,
|
|
||||||
marginTop: 6,
|
|
||||||
marginRight: 8,
|
|
||||||
marginBottom: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
priceMark: {
|
|
||||||
show: true,
|
|
||||||
high: {
|
|
||||||
show: true,
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
textMargin: 5,
|
|
||||||
textSize: 10,
|
|
||||||
textFamily: 'TT Mono',
|
|
||||||
textWeight: 'normal',
|
|
||||||
},
|
|
||||||
low: {
|
|
||||||
show: true,
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
textMargin: 5,
|
|
||||||
textSize: 10,
|
|
||||||
textFamily: 'TT Mono',
|
|
||||||
textWeight: 'normal',
|
|
||||||
},
|
|
||||||
last: {
|
|
||||||
show: true,
|
|
||||||
upColor: COLORS.BKG4[theme],
|
|
||||||
downColor: COLORS.BKG4[theme],
|
|
||||||
noChangeColor: COLORS.BKG4[theme],
|
|
||||||
line: {
|
|
||||||
show: true,
|
|
||||||
// 'solid'|'dash'
|
|
||||||
style: 'dash',
|
|
||||||
dashValue: [4, 4],
|
|
||||||
size: 1,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
show: true,
|
|
||||||
size: 10,
|
|
||||||
paddingLeft: 2,
|
|
||||||
paddingTop: 2,
|
|
||||||
paddingRight: 2,
|
|
||||||
paddingBottom: 2,
|
|
||||||
color: '#FFFFFF',
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
borderRadius: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
color: COLORS.BKG4[theme],
|
|
||||||
size: 1,
|
|
||||||
},
|
|
||||||
tickLine: {
|
|
||||||
show: true,
|
|
||||||
size: 1,
|
|
||||||
length: 3,
|
|
||||||
color: COLORS.BKG4[theme],
|
|
||||||
},
|
|
||||||
tickText: {
|
|
||||||
show: true,
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
size: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
color: COLORS.BKG4[theme],
|
|
||||||
size: 1,
|
|
||||||
},
|
|
||||||
tickLine: {
|
|
||||||
show: true,
|
|
||||||
size: 1,
|
|
||||||
length: 3,
|
|
||||||
color: COLORS.BKG4[theme],
|
|
||||||
},
|
|
||||||
tickText: {
|
|
||||||
show: true,
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
size: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
crosshair: {
|
|
||||||
show: true,
|
|
||||||
horizontal: {
|
|
||||||
show: true,
|
|
||||||
line: {
|
|
||||||
show: true,
|
|
||||||
style: 'dash',
|
|
||||||
dashValue: [4, 2],
|
|
||||||
size: 1,
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
show: true,
|
|
||||||
color: '#FFFFFF',
|
|
||||||
size: 10,
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
paddingLeft: 2,
|
|
||||||
paddingRight: 2,
|
|
||||||
paddingTop: 2,
|
|
||||||
paddingBottom: 2,
|
|
||||||
borderSize: 1,
|
|
||||||
borderColor: COLORS.FGD4[theme],
|
|
||||||
borderRadius: 2,
|
|
||||||
backgroundColor: COLORS.FGD4[theme],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vertical: {
|
|
||||||
show: true,
|
|
||||||
line: {
|
|
||||||
show: true,
|
|
||||||
style: 'dash',
|
|
||||||
dashValue: [4, 2],
|
|
||||||
size: 1,
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
show: true,
|
|
||||||
color: '#FFFFFF',
|
|
||||||
size: 10,
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
paddingLeft: 2,
|
|
||||||
paddingRight: 2,
|
|
||||||
paddingTop: 2,
|
|
||||||
paddingBottom: 2,
|
|
||||||
borderSize: 1,
|
|
||||||
borderColor: COLORS.FGD4[theme],
|
|
||||||
borderRadius: 2,
|
|
||||||
backgroundColor: COLORS.FGD4[theme],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
technicalIndicator: {
|
|
||||||
margin: {
|
|
||||||
top: 0.2,
|
|
||||||
bottom: 0.1,
|
|
||||||
},
|
|
||||||
bar: {
|
|
||||||
upColor: COLORS.UP[theme],
|
|
||||||
downColor: COLORS.DOWN[theme],
|
|
||||||
noChangeColor: '#888888',
|
|
||||||
},
|
|
||||||
line: {
|
|
||||||
size: 1,
|
|
||||||
colors: ['#FF9600', '#9D65C9', '#2196F3', '#E11D74', '#01C5C4'],
|
|
||||||
},
|
|
||||||
circle: {
|
|
||||||
upColor: '#26A69A',
|
|
||||||
downColor: '#EF5350',
|
|
||||||
noChangeColor: '#888888',
|
|
||||||
},
|
|
||||||
lastValueMark: {
|
|
||||||
show: false,
|
|
||||||
text: {
|
|
||||||
show: false,
|
|
||||||
color: '#ffffff',
|
|
||||||
size: 12,
|
|
||||||
family: 'Helvetica Neue',
|
|
||||||
weight: 'normal',
|
|
||||||
paddingLeft: 3,
|
|
||||||
paddingTop: 2,
|
|
||||||
paddingRight: 3,
|
|
||||||
paddingBottom: 2,
|
|
||||||
borderRadius: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
// 'always' | 'follow_cross' | 'none'
|
|
||||||
showRule: 'always',
|
|
||||||
// 'standard' | 'rect'
|
|
||||||
showType: 'standard',
|
|
||||||
showName: true,
|
|
||||||
showParams: true,
|
|
||||||
defaultValue: 'n/a',
|
|
||||||
text: {
|
|
||||||
size: 12,
|
|
||||||
family: 'TT Mono',
|
|
||||||
weight: 'normal',
|
|
||||||
color: COLORS.FGD4[theme],
|
|
||||||
marginTop: 6,
|
|
||||||
marginRight: 8,
|
|
||||||
marginBottom: 0,
|
|
||||||
marginLeft: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
separator: {
|
|
||||||
size: 2,
|
|
||||||
color: COLORS.BKG4[theme],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const socket = new WebSocket(socketUrl, 'echo-protocol')
|
|
||||||
const unsub_msg = {
|
|
||||||
type: 'UNSUBSCRIBE_PRICE',
|
|
||||||
}
|
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const prevWidth = usePrevious(width)
|
|
||||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||||
|
const prevWidth = usePrevious(width)
|
||||||
|
const [currentDataFeed, setCurrentDataFeed] = useState(spotDataFeed)
|
||||||
|
const previousDataFeed = usePrevious(currentDataFeed)
|
||||||
const [socketConnected, setSocketConnected] = useState(false)
|
const [socketConnected, setSocketConnected] = useState(false)
|
||||||
const selectedMarketName = selectedMarket?.name
|
const selectedMarketName = selectedMarket?.name
|
||||||
const [isTechnicalModalOpen, setIsTechnicalModalOpen] = useState(false)
|
const [isTechnicalModalOpen, setIsTechnicalModalOpen] = useState(false)
|
||||||
|
@ -277,10 +52,12 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
const [chart, setChart] = useState<klinecharts.Chart | null>(null)
|
const [chart, setChart] = useState<klinecharts.Chart | null>(null)
|
||||||
const previousChart = usePrevious(chart)
|
const previousChart = usePrevious(chart)
|
||||||
const [baseChartQuery, setQuery] = useState<BASE_CHART_QUERY | null>(null)
|
const [baseChartQuery, setQuery] = useState<BASE_CHART_QUERY | null>(null)
|
||||||
|
|
||||||
const fetchData = async (
|
const fetchData = async (
|
||||||
baseQuery: BASE_CHART_QUERY,
|
baseQuery: BASE_CHART_QUERY,
|
||||||
from: number,
|
from: number,
|
||||||
to?: number
|
to?: number,
|
||||||
|
firstDataRequest?: boolean
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
@ -289,15 +66,31 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
time_from: from,
|
time_from: from,
|
||||||
time_to: to ? to : baseQuery.time_to,
|
time_to: to ? to : baseQuery.time_to,
|
||||||
}
|
}
|
||||||
const response = await queryBars(query.address, query.type, {
|
let symbolInfo: any
|
||||||
firstDataRequest: false,
|
currentDataFeed.resolveSymbol(baseQuery.address, (sInfo) => {
|
||||||
|
symbolInfo = sInfo
|
||||||
|
})
|
||||||
|
const response = await currentDataFeed.getBars(
|
||||||
|
symbolInfo,
|
||||||
|
query.type as any,
|
||||||
|
{
|
||||||
|
firstDataRequest: !!firstDataRequest,
|
||||||
from: query.time_from,
|
from: query.time_from,
|
||||||
to: query.time_to,
|
to: query.time_to,
|
||||||
})
|
countBack: 0,
|
||||||
const dataSize = response.length
|
},
|
||||||
|
() => {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
console.log(e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const dataSize = response?.length || 0
|
||||||
const dataList = []
|
const dataList = []
|
||||||
for (let i = 0; i < dataSize; i++) {
|
for (let i = 0; i < dataSize; i++) {
|
||||||
const row = response[i]
|
const row = response![i]
|
||||||
const kLineModel = {
|
const kLineModel = {
|
||||||
...row,
|
...row,
|
||||||
}
|
}
|
||||||
|
@ -312,74 +105,35 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//update data every 10 secs
|
async function setupSocket(
|
||||||
function setupSocket(
|
|
||||||
kLineChart: klinecharts.Chart,
|
kLineChart: klinecharts.Chart,
|
||||||
baseQuery: BASE_CHART_QUERY
|
baseQuery: BASE_CHART_QUERY
|
||||||
) {
|
) {
|
||||||
// Connection opened
|
await sleep(1500)
|
||||||
socket.addEventListener('open', (_event) => {
|
|
||||||
console.log('[socket] Kline Connected')
|
|
||||||
})
|
|
||||||
socket.addEventListener('message', (msg) => {
|
|
||||||
const data = JSON.parse(msg.data)
|
|
||||||
if (data.type === 'WELLCOME') {
|
|
||||||
setSocketConnected(true)
|
setSocketConnected(true)
|
||||||
socket.send(JSON.stringify(unsub_msg))
|
let symbolInfo: any = undefined
|
||||||
const msg = {
|
currentDataFeed.resolveSymbol(baseQuery.address, (symbolInf) => {
|
||||||
type: 'SUBSCRIBE_PRICE',
|
symbolInfo = symbolInf
|
||||||
data: {
|
|
||||||
chartType: parseResolution(baseQuery.type),
|
|
||||||
address: baseQuery.address,
|
|
||||||
currency: 'pair',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
socket.send(JSON.stringify(msg))
|
|
||||||
}
|
|
||||||
if (data.type === 'PRICE_DATA') {
|
|
||||||
const dataList = kLineChart.getDataList()
|
|
||||||
const lastItem = dataList[dataList.length - 1]
|
|
||||||
const currTime = data.data.unixTime * 1000
|
|
||||||
if (!dataList.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const lastBar: KLineData & { time: number } = {
|
|
||||||
...lastItem,
|
|
||||||
time: lastItem.timestamp,
|
|
||||||
}
|
|
||||||
const resolution = parseResolution(baseQuery.type)
|
|
||||||
const nextBarTime = getNextBarTime(lastBar, resolution)
|
|
||||||
let bar: KLineData
|
|
||||||
|
|
||||||
if (currTime >= nextBarTime) {
|
|
||||||
bar = {
|
|
||||||
timestamp: nextBarTime,
|
|
||||||
open: data.data.o,
|
|
||||||
high: data.data.h,
|
|
||||||
low: data.data.l,
|
|
||||||
close: data.data.c,
|
|
||||||
volume: data.data.v,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bar = {
|
|
||||||
...lastBar,
|
|
||||||
high: Math.max(lastBar.high, data.data.h),
|
|
||||||
low: Math.min(lastBar.low, data.data.l),
|
|
||||||
close: data.data.c,
|
|
||||||
volume: data.data.v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kLineChart.updateData(bar)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
previousDataFeed.unsubscribeBars()
|
||||||
|
currentDataFeed.subscribeBars(
|
||||||
|
symbolInfo,
|
||||||
|
baseQuery.type,
|
||||||
|
(bar) => {
|
||||||
|
kLineChart.updateData(bar)
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
() => {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const fetchFreshData = async (daysToSubtractFromToday: number) => {
|
const fetchFreshData = async (daysToSubtractFromToday: number) => {
|
||||||
const from =
|
const from =
|
||||||
Math.floor(Date.now() / 1000) - ONE_DAY_SECONDS * daysToSubtractFromToday
|
Math.floor(Date.now() / 1000) - ONE_DAY_SECONDS * daysToSubtractFromToday
|
||||||
const data = await fetchData(baseChartQuery!, from)
|
const data = await fetchData(baseChartQuery!, from, undefined, true)
|
||||||
if (chart) {
|
if (chart) {
|
||||||
chart.applyNewData(data)
|
chart.applyNewData(data)
|
||||||
//after we fetch fresh data start to update data every x seconds
|
|
||||||
setupSocket(chart, baseChartQuery!)
|
setupSocket(chart, baseChartQuery!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,7 +152,7 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
//when base query change we refetch with fresh data
|
//when base query change we refetch with fresh data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chart && baseChartQuery) {
|
if (chart && baseChartQuery) {
|
||||||
//becuase bird eye send onlu 1k records at one time
|
//because bird eye send only 1k records at one time
|
||||||
//we query for lower amounts of days at the start
|
//we query for lower amounts of days at the start
|
||||||
const halfDayThreshold = ['1', '3']
|
const halfDayThreshold = ['1', '3']
|
||||||
const twoDaysThreshold = ['5', '15', '30']
|
const twoDaysThreshold = ['5', '15', '30']
|
||||||
|
@ -414,23 +168,40 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
const unixTime = timestamp / 1000
|
const unixTime = timestamp / 1000
|
||||||
const from = unixTime - ONE_DAY_SECONDS * daysToSub
|
const from = unixTime - ONE_DAY_SECONDS * daysToSub
|
||||||
const data = await fetchData(baseChartQuery!, from, unixTime)
|
const data = await fetchData(baseChartQuery!, from, unixTime)
|
||||||
|
if (!data.length) {
|
||||||
|
chart.loadMore(() => null)
|
||||||
|
}
|
||||||
chart.applyMoreData(data)
|
chart.applyMoreData(data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error fetching new data')
|
console.error('Error fetching new data')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [baseChartQuery])
|
}, [baseChartQuery, currentDataFeed.name])
|
||||||
|
|
||||||
//change query based on market and resolution
|
//change query based on market and resolution
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedMarketName && resolution) {
|
let dataFeed = spotDataFeed
|
||||||
|
const group = mangoStore.getState().group
|
||||||
|
let address = '8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6'
|
||||||
|
|
||||||
|
if (!selectedMarketName?.toLowerCase().includes('perp') && group) {
|
||||||
|
address = group!
|
||||||
|
.getSerum3MarketByName(selectedMarketName!)
|
||||||
|
.serumMarketExternal.toString()
|
||||||
|
} else if (group) {
|
||||||
|
dataFeed = perpDataFeed
|
||||||
|
|
||||||
|
address = group!
|
||||||
|
.getPerpMarketByName(selectedMarketName!)
|
||||||
|
.publicKey.toString()
|
||||||
|
}
|
||||||
|
setCurrentDataFeed(dataFeed)
|
||||||
setQuery({
|
setQuery({
|
||||||
type: resolution.val,
|
type: resolution.val,
|
||||||
address: '8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6',
|
address: address,
|
||||||
time_to: Math.floor(Date.now() / 1000),
|
time_to: Math.floor(Date.now() / 1000),
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}, [selectedMarketName, resolution])
|
}, [selectedMarketName, resolution])
|
||||||
|
|
||||||
// init default technical indicators after init of chart
|
// init default technical indicators after init of chart
|
||||||
|
@ -466,9 +237,8 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
||||||
return () => {
|
return () => {
|
||||||
dispose('update-k-line')
|
dispose('update-k-line')
|
||||||
if (socketConnected) {
|
if (socketConnected) {
|
||||||
console.log('[socket] kline disconnected')
|
currentDataFeed.unsubscribeBars()
|
||||||
socket.send(JSON.stringify(unsub_msg))
|
currentDataFeed.closeSocket()
|
||||||
socket.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
import { useTheme } from 'next-themes'
|
||||||
|
import { COLORS } from 'styles/colors'
|
||||||
|
|
||||||
|
export function useKlineChart() {
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const styles = {
|
||||||
|
grid: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
candle: {
|
||||||
|
bar: {
|
||||||
|
upColor: COLORS.UP[theme],
|
||||||
|
downColor: COLORS.DOWN[theme],
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
labels: ['', 'O:', 'C:', 'H:', 'L:', 'V:'],
|
||||||
|
text: {
|
||||||
|
size: 12,
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
marginLeft: 8,
|
||||||
|
marginTop: 6,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
priceMark: {
|
||||||
|
show: true,
|
||||||
|
high: {
|
||||||
|
show: true,
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
textMargin: 5,
|
||||||
|
textSize: 10,
|
||||||
|
textFamily: 'TT Mono',
|
||||||
|
textWeight: 'normal',
|
||||||
|
},
|
||||||
|
low: {
|
||||||
|
show: true,
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
textMargin: 5,
|
||||||
|
textSize: 10,
|
||||||
|
textFamily: 'TT Mono',
|
||||||
|
textWeight: 'normal',
|
||||||
|
},
|
||||||
|
last: {
|
||||||
|
show: true,
|
||||||
|
upColor: COLORS.BKG4[theme],
|
||||||
|
downColor: COLORS.BKG4[theme],
|
||||||
|
noChangeColor: COLORS.BKG4[theme],
|
||||||
|
line: {
|
||||||
|
show: true,
|
||||||
|
// 'solid'|'dash'
|
||||||
|
style: 'dash',
|
||||||
|
dashValue: [4, 4],
|
||||||
|
size: 1,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
show: true,
|
||||||
|
size: 10,
|
||||||
|
paddingLeft: 2,
|
||||||
|
paddingTop: 2,
|
||||||
|
paddingRight: 2,
|
||||||
|
paddingBottom: 2,
|
||||||
|
color: '#FFFFFF',
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
borderRadius: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
color: COLORS.BKG4[theme],
|
||||||
|
size: 1,
|
||||||
|
},
|
||||||
|
tickLine: {
|
||||||
|
show: true,
|
||||||
|
size: 1,
|
||||||
|
length: 3,
|
||||||
|
color: COLORS.BKG4[theme],
|
||||||
|
},
|
||||||
|
tickText: {
|
||||||
|
show: true,
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
color: COLORS.BKG4[theme],
|
||||||
|
size: 1,
|
||||||
|
},
|
||||||
|
tickLine: {
|
||||||
|
show: true,
|
||||||
|
size: 1,
|
||||||
|
length: 3,
|
||||||
|
color: COLORS.BKG4[theme],
|
||||||
|
},
|
||||||
|
tickText: {
|
||||||
|
show: true,
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
crosshair: {
|
||||||
|
show: true,
|
||||||
|
horizontal: {
|
||||||
|
show: true,
|
||||||
|
line: {
|
||||||
|
show: true,
|
||||||
|
style: 'dash',
|
||||||
|
dashValue: [4, 2],
|
||||||
|
size: 1,
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
show: true,
|
||||||
|
color: '#FFFFFF',
|
||||||
|
size: 10,
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
paddingLeft: 2,
|
||||||
|
paddingRight: 2,
|
||||||
|
paddingTop: 2,
|
||||||
|
paddingBottom: 2,
|
||||||
|
borderSize: 1,
|
||||||
|
borderColor: COLORS.FGD4[theme],
|
||||||
|
borderRadius: 2,
|
||||||
|
backgroundColor: COLORS.FGD4[theme],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vertical: {
|
||||||
|
show: true,
|
||||||
|
line: {
|
||||||
|
show: true,
|
||||||
|
style: 'dash',
|
||||||
|
dashValue: [4, 2],
|
||||||
|
size: 1,
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
show: true,
|
||||||
|
color: '#FFFFFF',
|
||||||
|
size: 10,
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
paddingLeft: 2,
|
||||||
|
paddingRight: 2,
|
||||||
|
paddingTop: 2,
|
||||||
|
paddingBottom: 2,
|
||||||
|
borderSize: 1,
|
||||||
|
borderColor: COLORS.FGD4[theme],
|
||||||
|
borderRadius: 2,
|
||||||
|
backgroundColor: COLORS.FGD4[theme],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
technicalIndicator: {
|
||||||
|
margin: {
|
||||||
|
top: 0.2,
|
||||||
|
bottom: 0.1,
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
upColor: COLORS.UP[theme],
|
||||||
|
downColor: COLORS.DOWN[theme],
|
||||||
|
noChangeColor: '#888888',
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
size: 1,
|
||||||
|
colors: ['#FF9600', '#9D65C9', '#2196F3', '#E11D74', '#01C5C4'],
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
upColor: '#26A69A',
|
||||||
|
downColor: '#EF5350',
|
||||||
|
noChangeColor: '#888888',
|
||||||
|
},
|
||||||
|
lastValueMark: {
|
||||||
|
show: false,
|
||||||
|
text: {
|
||||||
|
show: false,
|
||||||
|
color: '#ffffff',
|
||||||
|
size: 12,
|
||||||
|
family: 'Helvetica Neue',
|
||||||
|
weight: 'normal',
|
||||||
|
paddingLeft: 3,
|
||||||
|
paddingTop: 2,
|
||||||
|
paddingRight: 3,
|
||||||
|
paddingBottom: 2,
|
||||||
|
borderRadius: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
// 'always' | 'follow_cross' | 'none'
|
||||||
|
showRule: 'always',
|
||||||
|
// 'standard' | 'rect'
|
||||||
|
showType: 'standard',
|
||||||
|
showName: true,
|
||||||
|
showParams: true,
|
||||||
|
defaultValue: 'n/a',
|
||||||
|
text: {
|
||||||
|
size: 12,
|
||||||
|
family: 'TT Mono',
|
||||||
|
weight: 'normal',
|
||||||
|
color: COLORS.FGD4[theme],
|
||||||
|
marginTop: 6,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 0,
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
size: 2,
|
||||||
|
color: COLORS.BKG4[theme],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
styles,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { SUPPORTED_RESOLUTIONS } from 'apis/birdeye/datafeed'
|
import { SUPPORTED_RESOLUTIONS as SUPPORTED_SPOT_RESOLUTIONS } from 'apis/birdeye/datafeed'
|
||||||
|
|
||||||
export const ONE_HOUR_MINS = 60
|
export const ONE_HOUR_MINS = 60
|
||||||
export const ONE_MINUTE_SECONDS = 60
|
export const ONE_MINUTE_SECONDS = 60
|
||||||
|
@ -6,7 +6,7 @@ export const ONE_HOUR_SECONDS = ONE_HOUR_MINS * ONE_MINUTE_SECONDS
|
||||||
export const ONE_DAY_SECONDS = ONE_HOUR_SECONDS * 24
|
export const ONE_DAY_SECONDS = ONE_HOUR_SECONDS * 24
|
||||||
export type BASE_CHART_QUERY = {
|
export type BASE_CHART_QUERY = {
|
||||||
address: string
|
address: string
|
||||||
type: typeof SUPPORTED_RESOLUTIONS[number]
|
type: typeof SUPPORTED_SPOT_RESOLUTIONS[number]
|
||||||
time_to: number
|
time_to: number
|
||||||
}
|
}
|
||||||
export type CHART_QUERY = BASE_CHART_QUERY & {
|
export type CHART_QUERY = BASE_CHART_QUERY & {
|
||||||
|
@ -16,7 +16,7 @@ export type CHART_QUERY = BASE_CHART_QUERY & {
|
||||||
//Translate values that api accepts to chart seconds
|
//Translate values that api accepts to chart seconds
|
||||||
export const RES_NAME_TO_RES_VAL: {
|
export const RES_NAME_TO_RES_VAL: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
val: typeof SUPPORTED_RESOLUTIONS[number]
|
val: typeof SUPPORTED_SPOT_RESOLUTIONS[number]
|
||||||
seconds: number
|
seconds: number
|
||||||
}
|
}
|
||||||
} = {
|
} = {
|
||||||
|
|
Loading…
Reference in New Issue