perp chart fix

This commit is contained in:
Adrian Brzeziński 2024-11-18 17:59:51 +01:00
parent f334538fac
commit b3f9ab4730
3 changed files with 167 additions and 24 deletions

View File

@ -4,8 +4,7 @@ import { DAILY_SECONDS } from 'utils/constants'
/* eslint-disable @typescript-eslint/no-explicit-any */
export const NEXT_PUBLIC_BIRDEYE_API_KEY =
process.env.NEXT_PUBLIC_BIRDEYE_API_KEY ||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzM0NTE4MDF9.KTEqB1hrmZTMzk19rZNx9aesh2bIHj98Cb8sg5Ikz-Y'
process.env.NEXT_PUBLIC_BIRDEYE_API_KEY || '5afdc994b457493ea9a8882fbf695f46'
export const API_URL = 'https://public-api.birdeye.so/'

View File

@ -11,6 +11,23 @@ import {
ResolutionString,
SearchSymbolResultItem,
} from '@public/charting_library'
import {
makeApiRequest as makePerpApiRequest,
parseResolution as parsePerpResolution,
} from './mngo/helpers'
import mangoStore from '@store/mangoStore'
import {
Group,
PerpMarket,
Serum3Market,
} from '@blockworks-foundation/mango-v4'
import { PublicKey } from '@solana/web3.js'
import {
closeSocket as closePerpSocket,
// isOpen as isPerpOpen,
subscribeOnStream as subscribeOnPerpStream,
unsubscribeFromStream as unsubscribeFromPerpStream,
} from './mngo/streaming'
export const SUPPORTED_RESOLUTIONS = ['1', '5', '15', '60', '240'] as const
@ -29,6 +46,7 @@ export type SymbolInfo = LibrarySymbolInfo & {
}
const lastBarsCache = new Map()
const subscriptionIds = new Map()
const configurationData = {
supported_resolutions: SUPPORTED_RESOLUTIONS,
@ -36,6 +54,84 @@ const configurationData = {
exchanges: [],
}
const getMktFromMktAddress = (
group: Group,
symbolAddress: string,
): Serum3Market | PerpMarket | null => {
try {
const serumMkt = group.getSerum3MarketByExternalMarket(
new PublicKey(symbolAddress),
)
if (serumMkt) {
return serumMkt
}
} catch {
console.log('Address is not a serum market')
}
const perpMarkets = Array.from(group.perpMarketsMapByMarketIndex.values())
const perpMkt = perpMarkets.find(
(perpMarket: any) => perpMarket.publicKey.toString() === symbolAddress,
)
if (perpMkt) {
return perpMkt
}
return null
}
let marketType: 'spot' | 'perp'
export const queryPerpBars = async (
tokenAddress: string,
resolution: (typeof SUPPORTED_RESOLUTIONS)[number],
periodParams: {
firstDataRequest: boolean
from: number
to: number
},
): Promise<Bar[]> => {
const { from, to } = periodParams
if (tokenAddress === 'Loading') return []
const urlParameters = {
'perp-market': tokenAddress,
resolution: parsePerpResolution(resolution),
start_datetime: new Date((from - 3_000_000) * 1000).toISOString(),
end_datetime: new Date(to * 1000).toISOString(),
}
const query = Object.keys(urlParameters)
.map((name: string) => `${name}=${(urlParameters as any)[name]}`)
.join('&')
const data = await makePerpApiRequest(`/stats/candles-perp?${query}`)
if (!data || !data.length) {
return []
}
let bars: Bar[] = []
let previousBar: Bar | undefined = undefined
for (const bar of data) {
const timestamp = new Date(bar.candle_start).getTime()
bars = [
...bars,
{
time: timestamp,
low: bar.low,
high: bar.high,
open: previousBar ? previousBar.close : bar.open,
close: bar.close,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
volume: bar.volume,
timestamp,
},
]
previousBar = bar
}
return bars
}
export const queryBars = async (
tokenAddress: string,
resolution: (typeof SUPPORTED_RESOLUTIONS)[number],
@ -139,14 +235,32 @@ const datafeed = (
}
}
const ticker = `${base_token_name}/${quote_token_name}`
const quote_token = quote_token_mint
const base_token = base_token_mint
let ticker = `${base_token_name}/${quote_token_name}`
let quote_token = quote_token_mint
let base_token = base_token_mint
const mangoStoreState = mangoStore.getState()
const group = mangoStoreState.group
if (group && symbolAddress) {
const market = getMktFromMktAddress(group, symbolAddress)
if (market) {
ticker = market.name
if (market instanceof Serum3Market) {
base_token = group
.getFirstBankByTokenIndex(market.baseTokenIndex)
.mint.toString()
quote_token = group
.getFirstBankByTokenIndex(market.quoteTokenIndex)
.mint.toString()
}
}
}
const symbolInfo: SymbolInfo = {
quote_token,
base_token,
address: base_token,
address: symbolItem.address,
ticker: symbolItem.address,
name: ticker || symbolItem.address,
description: ticker || symbolItem.address,
@ -188,12 +302,24 @@ const datafeed = (
) => {
try {
const { firstDataRequest } = periodParams
const bars = await queryBars(
symbolInfo.base_token,
resolution as any,
periodParams,
symbolInfo.quote_token,
)
let bars
if (symbolInfo.description?.includes('PERP') && symbolInfo.address) {
marketType = 'perp'
bars = await queryPerpBars(
symbolInfo.address,
resolution as any,
periodParams,
)
} else if (symbolInfo.address) {
marketType = 'spot'
bars = await queryBars(
symbolInfo.base_token,
resolution as any,
periodParams,
symbolInfo.quote_token,
)
}
if (!bars || bars.length === 0) {
// "noData" should be set if there is no data in the requested period.
onHistoryCallback([], {
@ -223,22 +349,41 @@ const datafeed = (
subscriberUID: string,
onResetCacheNeededCallback: () => void,
) => {
subscribeOnSpotStream(
symbolInfo,
resolution,
onRealtimeCallback,
onResetCacheNeededCallback,
lastBarsCache.get(symbolInfo.address),
)
subscriptionIds.set(subscriberUID, symbolInfo.address)
if (symbolInfo.description?.includes('PERP')) {
subscribeOnPerpStream(
symbolInfo,
resolution,
onRealtimeCallback,
subscriberUID,
onResetCacheNeededCallback,
lastBarsCache.get(symbolInfo.address),
)
} else {
subscribeOnSpotStream(
symbolInfo,
resolution,
onRealtimeCallback,
onResetCacheNeededCallback,
lastBarsCache.get(symbolInfo.address),
)
}
},
unsubscribeBars: (subscriberUID: string) => {
closeSocket()
// unsubscribeFromStream(subscriberUID)
if (marketType === 'perp') {
unsubscribeFromPerpStream(subscriberUID)
} else {
// unsubscribeFromStream()
}
},
closeSocket: () => {
closeSocket()
if (marketType === 'spot') {
closeSocket()
} else {
closePerpSocket()
}
},
name: 'traffic',

View File

@ -3,8 +3,7 @@ import { DAILY_SECONDS, QUOTE_TOKEN_MINTS } from 'utils/constants'
/* eslint-disable @typescript-eslint/no-explicit-any */
export const NEXT_PUBLIC_BIRDEYE_API_KEY =
process.env.NEXT_PUBLIC_BIRDEYE_API_KEY ||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzM0NTE4MDF9.KTEqB1hrmZTMzk19rZNx9aesh2bIHj98Cb8sg5Ikz-Y'
process.env.NEXT_PUBLIC_BIRDEYE_API_KEY || '5afdc994b457493ea9a8882fbf695f46'
export const API_URL = 'https://public-api.birdeye.so/'