remove orderbook subscription hook and other fixes
This commit is contained in:
parent
c2aa4676b0
commit
a1fbe18bf1
|
@ -1,8 +1,5 @@
|
||||||
import Slider from '@components/forms/Slider'
|
import Slider from '@components/forms/Slider'
|
||||||
import useMarkPrice from 'hooks/useMarkPrice'
|
import useMarkPrice from 'hooks/useMarkPrice'
|
||||||
import useOrderbookSubscription, {
|
|
||||||
cumOrderbookSide,
|
|
||||||
} from 'hooks/useOrderbookSubscription'
|
|
||||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
import { useTheme } from 'next-themes'
|
import { useTheme } from 'next-themes'
|
||||||
|
@ -22,6 +19,8 @@ import { COLORS } from 'styles/colors'
|
||||||
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
||||||
import { gridBreakpoints } from './TradeAdvancedPage'
|
import { gridBreakpoints } from './TradeAdvancedPage'
|
||||||
import { CartesianViewBox } from 'recharts/types/util/types'
|
import { CartesianViewBox } from 'recharts/types/util/types'
|
||||||
|
import { cumOrderbookSide } from 'types'
|
||||||
|
import mangoStore from '@store/mangoStore'
|
||||||
|
|
||||||
type LabelPosition =
|
type LabelPosition =
|
||||||
| 'left'
|
| 'left'
|
||||||
|
@ -40,14 +39,6 @@ type LabelPosition =
|
||||||
|
|
||||||
const Y_TICK_COUNT = 10
|
const Y_TICK_COUNT = 10
|
||||||
|
|
||||||
// content: (props)=> {…}
|
|
||||||
// fill: "var(--fgd-2)"
|
|
||||||
// fontSize: 9
|
|
||||||
// offset: 7
|
|
||||||
// position: "left"
|
|
||||||
// value: "0.0"
|
|
||||||
// viewBox: {x: 52, y: 452, width: 82, height: 0}
|
|
||||||
|
|
||||||
interface CustomLabel extends LabelProps {
|
interface CustomLabel extends LabelProps {
|
||||||
viewBox?: CartesianViewBox
|
viewBox?: CartesianViewBox
|
||||||
}
|
}
|
||||||
|
@ -69,20 +60,52 @@ const MarkPriceLabel = ({ value, viewBox }: CustomLabel) => {
|
||||||
} else return null
|
} else return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const DepthChart = ({ grouping }: { grouping: number }) => {
|
type RawOrderbook = number[][]
|
||||||
|
type DepthOrderbookSide = {
|
||||||
|
price: number
|
||||||
|
size: number
|
||||||
|
cumulativeSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const DepthChart = () => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { serumOrPerpMarket } = useSelectedMarket()
|
const { serumOrPerpMarket } = useSelectedMarket()
|
||||||
const [priceRangePercent, setPriceRangePercentPercent] = useState('5')
|
|
||||||
const [mouseData, setMouseData] = useState<cumOrderbookSide | null>(null)
|
const [mouseData, setMouseData] = useState<cumOrderbookSide | null>(null)
|
||||||
const markPrice = useMarkPrice()
|
const markPrice = useMarkPrice()
|
||||||
const orderbook = useOrderbookSubscription(100, grouping)
|
const orderbook = mangoStore((s) => s.selectedMarket.orderbook)
|
||||||
|
const [priceRangePercent, setPriceRangePercentPercent] = useState('10')
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const increaseHeight = width ? width > gridBreakpoints.xxxl : false
|
const increaseHeight = width ? width > gridBreakpoints.xxxl : false
|
||||||
|
|
||||||
|
const formatOrderbookData = (orderbook: RawOrderbook, markPrice: number) => {
|
||||||
|
const maxPrice = markPrice * 4
|
||||||
|
const minPrice = markPrice / 4
|
||||||
|
const formattedBook = []
|
||||||
|
|
||||||
|
let cumulativeSize = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < orderbook.length; i++) {
|
||||||
|
const [price, size] = orderbook[i]
|
||||||
|
|
||||||
|
cumulativeSize += size
|
||||||
|
|
||||||
|
const object = {
|
||||||
|
price: price,
|
||||||
|
size: size,
|
||||||
|
cumulativeSize: cumulativeSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (price >= minPrice && price <= maxPrice) {
|
||||||
|
formattedBook.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formattedBook
|
||||||
|
}
|
||||||
|
|
||||||
// format chart data for the bids and asks series
|
// format chart data for the bids and asks series
|
||||||
const mergeCumulativeData = (
|
const mergeCumulativeData = (
|
||||||
bids: cumOrderbookSide[],
|
bids: DepthOrderbookSide[],
|
||||||
asks: cumOrderbookSide[]
|
asks: DepthOrderbookSide[]
|
||||||
) => {
|
) => {
|
||||||
const bidsWithSide = bids.map((b) => ({ ...b, bids: b.cumulativeSize }))
|
const bidsWithSide = bids.map((b) => ({ ...b, bids: b.cumulativeSize }))
|
||||||
const asksWithSide = asks.map((a) => ({ ...a, asks: a.cumulativeSize }))
|
const asksWithSide = asks.map((a) => ({ ...a, asks: a.cumulativeSize }))
|
||||||
|
@ -90,13 +113,15 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
if (!orderbook) return []
|
if (!orderbook || !serumOrPerpMarket || !markPrice) return []
|
||||||
return mergeCumulativeData(orderbook.bids, orderbook.asks)
|
const formattedBids = formatOrderbookData(orderbook.bids, markPrice)
|
||||||
}, [orderbook])
|
const formattedAsks = formatOrderbookData(orderbook.asks, markPrice)
|
||||||
|
return mergeCumulativeData(formattedBids, formattedAsks)
|
||||||
|
}, [markPrice, orderbook, serumOrPerpMarket])
|
||||||
|
|
||||||
// find the max value for the x-axis
|
// find the max value for the x-axis
|
||||||
const findXDomainMax = (
|
const findXDomainMax = (
|
||||||
data: cumOrderbookSide[],
|
data: DepthOrderbookSide[],
|
||||||
yMin: number,
|
yMin: number,
|
||||||
yMax: number
|
yMax: number
|
||||||
) => {
|
) => {
|
||||||
|
@ -177,7 +202,7 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
|
|
||||||
const priceFormatter = useCallback(
|
const priceFormatter = useCallback(
|
||||||
(price: number) => {
|
(price: number) => {
|
||||||
if (!serumOrPerpMarket) return price.toFixed(1)
|
if (!serumOrPerpMarket) return price.toFixed()
|
||||||
const tickDecimals = getDecimalCount(serumOrPerpMarket.tickSize)
|
const tickDecimals = getDecimalCount(serumOrPerpMarket.tickSize)
|
||||||
if (tickDecimals >= 7) {
|
if (tickDecimals >= 7) {
|
||||||
return price.toExponential(3)
|
return price.toExponential(3)
|
||||||
|
@ -186,6 +211,15 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
[serumOrPerpMarket]
|
[serumOrPerpMarket]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const xTickFormatter = useCallback(
|
||||||
|
(size: number) => {
|
||||||
|
if (!serumOrPerpMarket) return size.toFixed()
|
||||||
|
const minOrderDecimals = getDecimalCount(serumOrPerpMarket.minOrderSize)
|
||||||
|
return size.toFixed(minOrderDecimals)
|
||||||
|
},
|
||||||
|
[serumOrPerpMarket]
|
||||||
|
)
|
||||||
|
|
||||||
const isWithinRangeOfTick = useCallback(
|
const isWithinRangeOfTick = useCallback(
|
||||||
(value: number, baseValue: number) => {
|
(value: number, baseValue: number) => {
|
||||||
const difference = Math.abs(value - baseValue)
|
const difference = Math.abs(value - baseValue)
|
||||||
|
@ -265,7 +299,7 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
setMouseData(null)
|
setMouseData(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return chartData.length ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-10 items-center border-b border-th-bkg-3 py-1 px-2">
|
<div className="flex h-10 items-center border-b border-th-bkg-3 py-1 px-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
@ -274,7 +308,7 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
</span>
|
</span>
|
||||||
<Slider
|
<Slider
|
||||||
amount={parseFloat(priceRangePercent)}
|
amount={parseFloat(priceRangePercent)}
|
||||||
max="50"
|
max="100"
|
||||||
min="0.5"
|
min="0.5"
|
||||||
onChange={(p) => setPriceRangePercentPercent(p)}
|
onChange={(p) => setPriceRangePercentPercent(p)}
|
||||||
step={0.5}
|
step={0.5}
|
||||||
|
@ -300,6 +334,7 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
type="number"
|
type="number"
|
||||||
tick={false}
|
tick={false}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
|
tickFormatter={(tick) => xTickFormatter(tick)}
|
||||||
/>
|
/>
|
||||||
<YAxis
|
<YAxis
|
||||||
dataKey="price"
|
dataKey="price"
|
||||||
|
@ -315,16 +350,20 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
tickFormatter={(tick) => yTickFormatter(tick)}
|
tickFormatter={(tick) => yTickFormatter(tick)}
|
||||||
/>
|
/>
|
||||||
<Area
|
<Area
|
||||||
type="step"
|
type="stepBefore"
|
||||||
dataKey="bids"
|
dataKey="bids"
|
||||||
stroke={COLORS.UP[theme]}
|
stroke={COLORS.UP[theme]}
|
||||||
fill="url(#bidsGradient)"
|
fill="url(#bidsGradient)"
|
||||||
|
isAnimationActive={false}
|
||||||
|
strokeWidth={1}
|
||||||
/>
|
/>
|
||||||
<Area
|
<Area
|
||||||
type="step"
|
type="stepBefore"
|
||||||
dataKey="asks"
|
dataKey="asks"
|
||||||
stroke={COLORS.DOWN[theme]}
|
stroke={COLORS.DOWN[theme]}
|
||||||
fill="url(#asksGradient)"
|
fill="url(#asksGradient)"
|
||||||
|
isAnimationActive={false}
|
||||||
|
strokeWidth={1}
|
||||||
/>
|
/>
|
||||||
<ReferenceLine
|
<ReferenceLine
|
||||||
y={mouseData?.price}
|
y={mouseData?.price}
|
||||||
|
@ -471,7 +510,7 @@ const DepthChart = ({ grouping }: { grouping: number }) => {
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DepthChart
|
export default DepthChart
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { SOUND_SETTINGS_KEY } from 'utils/constants'
|
||||||
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
|
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
|
||||||
import { Howl } from 'howler'
|
import { Howl } from 'howler'
|
||||||
import { isMangoError } from 'types'
|
import { isMangoError } from 'types'
|
||||||
import { decodeBook, decodeBookL2 } from './Orderbook'
|
import { decodeBook, decodeBookL2 } from 'utils/orderbook'
|
||||||
|
|
||||||
interface MarketCloseModalProps {
|
interface MarketCloseModalProps {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
|
|
@ -11,7 +11,6 @@ import FavoriteMarketsBar from './FavoriteMarketsBar'
|
||||||
|
|
||||||
const MobileTradeAdvancedPage = () => {
|
const MobileTradeAdvancedPage = () => {
|
||||||
const [activeTab, setActiveTab] = useState('trade:book')
|
const [activeTab, setActiveTab] = useState('trade:book')
|
||||||
const [grouping, setGrouping] = useState(0.01)
|
|
||||||
const [showChart, setShowChart] = useState(false)
|
const [showChart, setShowChart] = useState(false)
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-3">
|
<div className="grid grid-cols-2 sm:grid-cols-3">
|
||||||
|
@ -42,7 +41,7 @@ const MobileTradeAdvancedPage = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={activeTab === 'trade:book' ? 'visible' : 'hidden'}>
|
<div className={activeTab === 'trade:book' ? 'visible' : 'hidden'}>
|
||||||
<Orderbook grouping={grouping} setGrouping={setGrouping} />
|
<Orderbook />
|
||||||
</div>
|
</div>
|
||||||
<div className={activeTab === 'trade:trades' ? 'visible' : 'hidden'}>
|
<div className={activeTab === 'trade:trades' ? 'visible' : 'hidden'}>
|
||||||
<RecentTrades />
|
<RecentTrades />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AccountInfo, PublicKey } from '@solana/web3.js'
|
import { PublicKey } from '@solana/web3.js'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Market, Orderbook as SpotOrderBook } from '@project-serum/serum'
|
import { Market } from '@project-serum/serum'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import {
|
import {
|
||||||
floorToDecimal,
|
floorToDecimal,
|
||||||
|
@ -18,20 +18,23 @@ import Decimal from 'decimal.js'
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
import Tooltip from '@components/shared/Tooltip'
|
||||||
import GroupSize from './GroupSize'
|
import GroupSize from './GroupSize'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
import {
|
import { BookSide, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||||
BookSide,
|
|
||||||
BookSideType,
|
|
||||||
MangoClient,
|
|
||||||
PerpMarket,
|
|
||||||
Serum3Market,
|
|
||||||
} from '@blockworks-foundation/mango-v4'
|
|
||||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||||
import { OrderbookFeed } from '@blockworks-foundation/mango-feeds'
|
import { OrderbookFeed } from '@blockworks-foundation/mango-feeds'
|
||||||
import useOrderbookSubscription from 'hooks/useOrderbookSubscription'
|
|
||||||
import Switch from '@components/forms/Switch'
|
import Switch from '@components/forms/Switch'
|
||||||
import { gridBreakpoints } from './TradeAdvancedPage'
|
import { gridBreakpoints } from './TradeAdvancedPage'
|
||||||
import { breakpoints } from 'utils/theme'
|
import { breakpoints } from 'utils/theme'
|
||||||
|
import {
|
||||||
|
decodeBook,
|
||||||
|
decodeBookL2,
|
||||||
|
getCumulativeOrderbookSide,
|
||||||
|
getMarket,
|
||||||
|
groupBy,
|
||||||
|
updatePerpMarketOnGroup,
|
||||||
|
} from 'utils/orderbook'
|
||||||
|
import { OrderbookData, OrderbookL2 } from 'types'
|
||||||
|
import { isEqual } from 'lodash'
|
||||||
|
|
||||||
const sizeCompacter = Intl.NumberFormat('en', {
|
const sizeCompacter = Intl.NumberFormat('en', {
|
||||||
maximumFractionDigits: 6,
|
maximumFractionDigits: 6,
|
||||||
|
@ -40,72 +43,13 @@ const sizeCompacter = Intl.NumberFormat('en', {
|
||||||
|
|
||||||
const SHOW_EXPONENTIAL_THRESHOLD = 0.00001
|
const SHOW_EXPONENTIAL_THRESHOLD = 0.00001
|
||||||
|
|
||||||
const getMarket = () => {
|
const Orderbook = () => {
|
||||||
const group = mangoStore.getState().group
|
|
||||||
const selectedMarket = mangoStore.getState().selectedMarket.current
|
|
||||||
if (!group || !selectedMarket) return
|
|
||||||
return selectedMarket instanceof PerpMarket
|
|
||||||
? selectedMarket
|
|
||||||
: group?.getSerum3ExternalMarket(selectedMarket.serumMarketExternal)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const decodeBookL2 = (book: SpotOrderBook | BookSide): number[][] => {
|
|
||||||
const depth = 300
|
|
||||||
if (book instanceof SpotOrderBook) {
|
|
||||||
return book.getL2(depth).map(([price, size]) => [price, size])
|
|
||||||
} else if (book instanceof BookSide) {
|
|
||||||
return book.getL2Ui(depth)
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decodeBook(
|
|
||||||
client: MangoClient,
|
|
||||||
market: Market | PerpMarket,
|
|
||||||
accInfo: AccountInfo<Buffer>,
|
|
||||||
side: 'bids' | 'asks'
|
|
||||||
): SpotOrderBook | BookSide {
|
|
||||||
if (market instanceof Market) {
|
|
||||||
const book = SpotOrderBook.decode(market, accInfo.data)
|
|
||||||
return book
|
|
||||||
} else {
|
|
||||||
const decodedAcc = client.program.coder.accounts.decode(
|
|
||||||
'bookSide',
|
|
||||||
accInfo.data
|
|
||||||
)
|
|
||||||
const book = BookSide.from(
|
|
||||||
client,
|
|
||||||
market,
|
|
||||||
side === 'bids' ? BookSideType.bids : BookSideType.asks,
|
|
||||||
decodedAcc
|
|
||||||
)
|
|
||||||
return book
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePerpMarketOnGroup = (book: BookSide, side: 'bids' | 'asks') => {
|
|
||||||
const group = mangoStore.getState().group
|
|
||||||
const perpMarket = group?.getPerpMarketByMarketIndex(
|
|
||||||
book.perpMarket.perpMarketIndex
|
|
||||||
)
|
|
||||||
if (perpMarket) {
|
|
||||||
perpMarket[`_${side}`] = book
|
|
||||||
// mangoStore.getState().actions.fetchOpenOrders()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Orderbook = ({
|
|
||||||
grouping,
|
|
||||||
setGrouping,
|
|
||||||
}: {
|
|
||||||
grouping: number
|
|
||||||
setGrouping: (g: number) => void
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation(['common', 'trade'])
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
const { serumOrPerpMarket: market } = useSelectedMarket()
|
const { serumOrPerpMarket: market } = useSelectedMarket()
|
||||||
const connection = mangoStore((s) => s.connection)
|
const connection = mangoStore((s) => s.connection)
|
||||||
|
|
||||||
const [tickSize, setTickSize] = useState(0)
|
const [tickSize, setTickSize] = useState(0)
|
||||||
|
const [grouping, setGrouping] = useState(0.01)
|
||||||
const [useOrderbookFeed, setUseOrderbookFeed] = useState(
|
const [useOrderbookFeed, setUseOrderbookFeed] = useState(
|
||||||
localStorage.getItem(USE_ORDERBOOK_FEED_KEY) !== null
|
localStorage.getItem(USE_ORDERBOOK_FEED_KEY) !== null
|
||||||
? localStorage.getItem(USE_ORDERBOOK_FEED_KEY) === 'true'
|
? localStorage.getItem(USE_ORDERBOOK_FEED_KEY) === 'true'
|
||||||
|
@ -117,13 +61,13 @@ const Orderbook = ({
|
||||||
)
|
)
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const isMobile = width ? width < breakpoints.lg : false
|
const isMobile = width ? width < breakpoints.lg : false
|
||||||
|
const [orderbookData, setOrderbookData] = useState<OrderbookData | null>(null)
|
||||||
|
const currentOrderbookData = useRef<OrderbookL2>()
|
||||||
|
|
||||||
const depth = useMemo(() => {
|
const depth = useMemo(() => {
|
||||||
return width > gridBreakpoints.xxxl ? 12 : 10
|
return width > gridBreakpoints.xxxl ? 12 : 10
|
||||||
}, [width])
|
}, [width])
|
||||||
|
|
||||||
const orderbookData = useOrderbookSubscription(depth, grouping)
|
|
||||||
|
|
||||||
const depthArray: number[] = useMemo(() => {
|
const depthArray: number[] = useMemo(() => {
|
||||||
return Array(depth).fill(0)
|
return Array(depth).fill(0)
|
||||||
}, [depth])
|
}, [depth])
|
||||||
|
@ -151,6 +95,109 @@ const Orderbook = ({
|
||||||
return asksPk.toString()
|
return asksPk.toString()
|
||||||
}, [market])
|
}, [market])
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() =>
|
||||||
|
mangoStore.subscribe(
|
||||||
|
(state) => state.selectedMarket.orderbook,
|
||||||
|
(newOrderbook) => {
|
||||||
|
if (
|
||||||
|
newOrderbook &&
|
||||||
|
market &&
|
||||||
|
!isEqual(currentOrderbookData.current, newOrderbook)
|
||||||
|
) {
|
||||||
|
// check if user has open orders so we can highlight them on orderbook
|
||||||
|
const openOrders = mangoStore.getState().mangoAccount.openOrders
|
||||||
|
const marketPk = market.publicKey.toString()
|
||||||
|
const bids2 = mangoStore.getState().selectedMarket.bidsAccount
|
||||||
|
const asks2 = mangoStore.getState().selectedMarket.asksAccount
|
||||||
|
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||||
|
let usersOpenOrderPrices: number[] = []
|
||||||
|
if (
|
||||||
|
mangoAccount &&
|
||||||
|
bids2 &&
|
||||||
|
asks2 &&
|
||||||
|
bids2 instanceof BookSide &&
|
||||||
|
asks2 instanceof BookSide
|
||||||
|
) {
|
||||||
|
usersOpenOrderPrices = [...bids2.items(), ...asks2.items()]
|
||||||
|
.filter((order) => order.owner.equals(mangoAccount.publicKey))
|
||||||
|
.map((order) => order.price)
|
||||||
|
} else {
|
||||||
|
usersOpenOrderPrices =
|
||||||
|
marketPk && openOrders[marketPk]?.length
|
||||||
|
? openOrders[marketPk]?.map((order) => order.price)
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// updated orderbook data
|
||||||
|
const bids =
|
||||||
|
groupBy(newOrderbook?.bids, market, grouping, true) || []
|
||||||
|
const asks =
|
||||||
|
groupBy(newOrderbook?.asks, market, grouping, false) || []
|
||||||
|
|
||||||
|
const sum = (total: number, [, size]: number[], index: number) =>
|
||||||
|
index < depth ? total + size : total
|
||||||
|
const totalSize = bids.reduce(sum, 0) + asks.reduce(sum, 0)
|
||||||
|
|
||||||
|
const maxSize =
|
||||||
|
Math.max(
|
||||||
|
...bids.map((b: number[]) => {
|
||||||
|
return b[1]
|
||||||
|
})
|
||||||
|
) +
|
||||||
|
Math.max(
|
||||||
|
...asks.map((a: number[]) => {
|
||||||
|
return a[1]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const isGrouped = grouping !== market.tickSize
|
||||||
|
const bidsToDisplay = getCumulativeOrderbookSide(
|
||||||
|
bids,
|
||||||
|
totalSize,
|
||||||
|
maxSize,
|
||||||
|
depth,
|
||||||
|
usersOpenOrderPrices,
|
||||||
|
grouping,
|
||||||
|
isGrouped
|
||||||
|
)
|
||||||
|
const asksToDisplay = getCumulativeOrderbookSide(
|
||||||
|
asks,
|
||||||
|
totalSize,
|
||||||
|
maxSize,
|
||||||
|
depth,
|
||||||
|
usersOpenOrderPrices,
|
||||||
|
grouping,
|
||||||
|
isGrouped
|
||||||
|
)
|
||||||
|
|
||||||
|
currentOrderbookData.current = newOrderbook
|
||||||
|
if (bidsToDisplay[0] || asksToDisplay[0]) {
|
||||||
|
const bid = bidsToDisplay[0]?.price
|
||||||
|
const ask = asksToDisplay[0]?.price
|
||||||
|
let spread = 0,
|
||||||
|
spreadPercentage = 0
|
||||||
|
if (bid && ask) {
|
||||||
|
spread = parseFloat(
|
||||||
|
(ask - bid).toFixed(getDecimalCount(market.tickSize))
|
||||||
|
)
|
||||||
|
spreadPercentage = (spread / ask) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
setOrderbookData({
|
||||||
|
bids: bidsToDisplay,
|
||||||
|
asks: asksToDisplay.reverse(),
|
||||||
|
spread,
|
||||||
|
spreadPercentage,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setOrderbookData(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
[depth, grouping, market]
|
||||||
|
)
|
||||||
|
|
||||||
// subscribe to the bids and asks orderbook accounts
|
// subscribe to the bids and asks orderbook accounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const set = mangoStore.getState().set
|
const set = mangoStore.getState().set
|
||||||
|
|
|
@ -11,13 +11,7 @@ export const TABS: [string, number][] = [
|
||||||
['trade:trades', 0],
|
['trade:trades', 0],
|
||||||
]
|
]
|
||||||
|
|
||||||
const OrderbookAndTrades = ({
|
const OrderbookAndTrades = () => {
|
||||||
grouping,
|
|
||||||
setGrouping,
|
|
||||||
}: {
|
|
||||||
grouping: number
|
|
||||||
setGrouping: (g: number) => void
|
|
||||||
}) => {
|
|
||||||
const [activeTab, setActiveTab] = useState('trade:book')
|
const [activeTab, setActiveTab] = useState('trade:book')
|
||||||
const [showDepthChart] = useLocalStorageState<boolean>(DEPTH_CHART_KEY, false)
|
const [showDepthChart] = useLocalStorageState<boolean>(DEPTH_CHART_KEY, false)
|
||||||
return (
|
return (
|
||||||
|
@ -34,12 +28,12 @@ const OrderbookAndTrades = ({
|
||||||
className={`flex ${activeTab === 'trade:book' ? 'visible' : 'hidden'}`}
|
className={`flex ${activeTab === 'trade:book' ? 'visible' : 'hidden'}`}
|
||||||
>
|
>
|
||||||
{showDepthChart ? (
|
{showDepthChart ? (
|
||||||
<div className="z-20 w-1/2 border-r border-th-bkg-3">
|
<div className="hidden w-1/2 border-r border-th-bkg-3 lg:block">
|
||||||
<DepthChart grouping={grouping} />
|
<DepthChart />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={showDepthChart ? 'w-1/2' : 'w-full'}>
|
<div className={showDepthChart ? 'w-full lg:w-1/2' : 'w-full'}>
|
||||||
<Orderbook grouping={grouping} setGrouping={setGrouping} />
|
<Orderbook />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo } from 'react'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import ReactGridLayout, { Responsive, WidthProvider } from 'react-grid-layout'
|
import ReactGridLayout, { Responsive, WidthProvider } from 'react-grid-layout'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
|
@ -51,7 +51,6 @@ const TradeAdvancedPage = () => {
|
||||||
const { height, width } = useViewport()
|
const { height, width } = useViewport()
|
||||||
const { uiLocked } = mangoStore((s) => s.settings)
|
const { uiLocked } = mangoStore((s) => s.settings)
|
||||||
const showMobileView = width <= breakpoints.md
|
const showMobileView = width <= breakpoints.md
|
||||||
const [grouping, setGrouping] = useState(0.01)
|
|
||||||
// const tourSettings = mangoStore((s) => s.settings.tours)
|
// const tourSettings = mangoStore((s) => s.settings.tours)
|
||||||
// const { connected } = useWallet()
|
// const { connected } = useWallet()
|
||||||
// const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
// const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||||
|
@ -308,7 +307,7 @@ const TradeAdvancedPage = () => {
|
||||||
: ''
|
: ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<OrderbookAndTrades grouping={grouping} setGrouping={setGrouping} />
|
<OrderbookAndTrades />
|
||||||
</div>
|
</div>
|
||||||
</ResponsiveGridLayout>
|
</ResponsiveGridLayout>
|
||||||
{/* {!tourSettings?.trade_tour_seen && isOnboarded && connected ? (
|
{/* {!tourSettings?.trade_tour_seen && isOnboarded && connected ? (
|
||||||
|
|
|
@ -1,237 +0,0 @@
|
||||||
import { BookSide, PerpMarket } from '@blockworks-foundation/mango-v4'
|
|
||||||
import { Market } from '@project-serum/serum'
|
|
||||||
import mangoStore from '@store/mangoStore'
|
|
||||||
import Big from 'big.js'
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { getDecimalCount } from 'utils/numbers'
|
|
||||||
import useSelectedMarket from './useSelectedMarket'
|
|
||||||
import { OrderbookL2 } from 'types'
|
|
||||||
import isEqual from 'lodash/isEqual'
|
|
||||||
|
|
||||||
export type cumOrderbookSide = {
|
|
||||||
price: number
|
|
||||||
size: number
|
|
||||||
cumulativeSize: number
|
|
||||||
sizePercent: number
|
|
||||||
maxSizePercent: number
|
|
||||||
cumulativeSizePercent: number
|
|
||||||
isUsersOrder: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type OrderbookData = {
|
|
||||||
bids: cumOrderbookSide[]
|
|
||||||
asks: cumOrderbookSide[]
|
|
||||||
spread: number
|
|
||||||
spreadPercentage: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCumulativeOrderbookSide = (
|
|
||||||
orders: number[][],
|
|
||||||
totalSize: number,
|
|
||||||
maxSize: number,
|
|
||||||
depth: number,
|
|
||||||
usersOpenOrderPrices: number[],
|
|
||||||
grouping: number,
|
|
||||||
isGrouped: boolean
|
|
||||||
): cumOrderbookSide[] => {
|
|
||||||
let cumulativeSize = 0
|
|
||||||
return orders.slice(0, depth).map(([price, size]) => {
|
|
||||||
cumulativeSize += size
|
|
||||||
return {
|
|
||||||
price: Number(price),
|
|
||||||
size,
|
|
||||||
cumulativeSize,
|
|
||||||
sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100),
|
|
||||||
cumulativeSizePercent: Math.round((size / (cumulativeSize || 1)) * 100),
|
|
||||||
maxSizePercent: Math.round((size / (maxSize || 1)) * 100),
|
|
||||||
isUsersOrder: hasOpenOrderForPriceGroup(
|
|
||||||
usersOpenOrderPrices,
|
|
||||||
price,
|
|
||||||
grouping,
|
|
||||||
isGrouped
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupBy = (
|
|
||||||
ordersArray: number[][],
|
|
||||||
market: PerpMarket | Market,
|
|
||||||
grouping: number,
|
|
||||||
isBids: boolean
|
|
||||||
) => {
|
|
||||||
if (!ordersArray || !market || !grouping || grouping == market?.tickSize) {
|
|
||||||
return ordersArray || []
|
|
||||||
}
|
|
||||||
const groupFloors: Record<number, number> = {}
|
|
||||||
for (let i = 0; i < ordersArray.length; i++) {
|
|
||||||
if (typeof ordersArray[i] == 'undefined') {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
const bigGrouping = Big(grouping)
|
|
||||||
const bigOrder = Big(ordersArray[i][0])
|
|
||||||
|
|
||||||
const floor = isBids
|
|
||||||
? bigOrder
|
|
||||||
.div(bigGrouping)
|
|
||||||
.round(0, Big.roundDown)
|
|
||||||
.times(bigGrouping)
|
|
||||||
.toNumber()
|
|
||||||
: bigOrder
|
|
||||||
.div(bigGrouping)
|
|
||||||
.round(0, Big.roundUp)
|
|
||||||
.times(bigGrouping)
|
|
||||||
.toNumber()
|
|
||||||
if (typeof groupFloors[floor] == 'undefined') {
|
|
||||||
groupFloors[floor] = ordersArray[i][1]
|
|
||||||
} else {
|
|
||||||
groupFloors[floor] = ordersArray[i][1] + groupFloors[floor]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const sortedGroups = Object.entries(groupFloors)
|
|
||||||
.map((entry) => {
|
|
||||||
return [
|
|
||||||
+parseFloat(entry[0]).toFixed(getDecimalCount(grouping)),
|
|
||||||
entry[1],
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.sort((a: number[], b: number[]) => {
|
|
||||||
if (!a || !b) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return isBids ? b[0] - a[0] : a[0] - b[0]
|
|
||||||
})
|
|
||||||
return sortedGroups
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasOpenOrderForPriceGroup = (
|
|
||||||
openOrderPrices: number[],
|
|
||||||
price: number,
|
|
||||||
grouping: number,
|
|
||||||
isGrouped: boolean
|
|
||||||
) => {
|
|
||||||
if (!isGrouped) {
|
|
||||||
return !!openOrderPrices.find((ooPrice) => {
|
|
||||||
return ooPrice === price
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return !!openOrderPrices.find((ooPrice) => {
|
|
||||||
return ooPrice >= price - grouping && ooPrice <= price + grouping
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const useOrderbookSubscription = (depth: number, grouping: number) => {
|
|
||||||
const { serumOrPerpMarket } = useSelectedMarket()
|
|
||||||
const [orderbookData, setOrderbookData] = useState<OrderbookData | null>(null)
|
|
||||||
const currentOrderbookData = useRef<OrderbookL2>()
|
|
||||||
|
|
||||||
useEffect(
|
|
||||||
() =>
|
|
||||||
mangoStore.subscribe(
|
|
||||||
(state) => state.selectedMarket.orderbook,
|
|
||||||
(newOrderbook) => {
|
|
||||||
if (
|
|
||||||
newOrderbook &&
|
|
||||||
serumOrPerpMarket &&
|
|
||||||
!isEqual(currentOrderbookData.current, newOrderbook)
|
|
||||||
) {
|
|
||||||
// check if user has open orders so we can highlight them on orderbook
|
|
||||||
const openOrders = mangoStore.getState().mangoAccount.openOrders
|
|
||||||
const marketPk = serumOrPerpMarket.publicKey.toString()
|
|
||||||
const bids2 = mangoStore.getState().selectedMarket.bidsAccount
|
|
||||||
const asks2 = mangoStore.getState().selectedMarket.asksAccount
|
|
||||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
|
||||||
let usersOpenOrderPrices: number[] = []
|
|
||||||
if (
|
|
||||||
mangoAccount &&
|
|
||||||
bids2 &&
|
|
||||||
asks2 &&
|
|
||||||
bids2 instanceof BookSide &&
|
|
||||||
asks2 instanceof BookSide
|
|
||||||
) {
|
|
||||||
usersOpenOrderPrices = [...bids2.items(), ...asks2.items()]
|
|
||||||
.filter((order) => order.owner.equals(mangoAccount.publicKey))
|
|
||||||
.map((order) => order.price)
|
|
||||||
} else {
|
|
||||||
usersOpenOrderPrices =
|
|
||||||
marketPk && openOrders[marketPk]?.length
|
|
||||||
? openOrders[marketPk]?.map((order) => order.price)
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
|
|
||||||
// updated orderbook data
|
|
||||||
const bids =
|
|
||||||
groupBy(newOrderbook?.bids, serumOrPerpMarket, grouping, true) ||
|
|
||||||
[]
|
|
||||||
const asks =
|
|
||||||
groupBy(newOrderbook?.asks, serumOrPerpMarket, grouping, false) ||
|
|
||||||
[]
|
|
||||||
|
|
||||||
const sum = (total: number, [, size]: number[], index: number) =>
|
|
||||||
index < depth ? total + size : total
|
|
||||||
const totalSize = bids.reduce(sum, 0) + asks.reduce(sum, 0)
|
|
||||||
|
|
||||||
const maxSize =
|
|
||||||
Math.max(
|
|
||||||
...bids.map((b: number[]) => {
|
|
||||||
return b[1]
|
|
||||||
})
|
|
||||||
) +
|
|
||||||
Math.max(
|
|
||||||
...asks.map((a: number[]) => {
|
|
||||||
return a[1]
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const isGrouped = grouping !== serumOrPerpMarket.tickSize
|
|
||||||
const bidsToDisplay = getCumulativeOrderbookSide(
|
|
||||||
bids,
|
|
||||||
totalSize,
|
|
||||||
maxSize,
|
|
||||||
depth,
|
|
||||||
usersOpenOrderPrices,
|
|
||||||
grouping,
|
|
||||||
isGrouped
|
|
||||||
)
|
|
||||||
const asksToDisplay = getCumulativeOrderbookSide(
|
|
||||||
asks,
|
|
||||||
totalSize,
|
|
||||||
maxSize,
|
|
||||||
depth,
|
|
||||||
usersOpenOrderPrices,
|
|
||||||
grouping,
|
|
||||||
isGrouped
|
|
||||||
)
|
|
||||||
|
|
||||||
currentOrderbookData.current = newOrderbook
|
|
||||||
if (bidsToDisplay[0] || asksToDisplay[0]) {
|
|
||||||
const bid = bidsToDisplay[0]?.price
|
|
||||||
const ask = asksToDisplay[0]?.price
|
|
||||||
let spread = 0,
|
|
||||||
spreadPercentage = 0
|
|
||||||
if (bid && ask) {
|
|
||||||
spread = parseFloat(
|
|
||||||
(ask - bid).toFixed(
|
|
||||||
getDecimalCount(serumOrPerpMarket.tickSize)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
spreadPercentage = (spread / ask) * 100
|
|
||||||
}
|
|
||||||
|
|
||||||
setOrderbookData({
|
|
||||||
bids: bidsToDisplay,
|
|
||||||
asks: asksToDisplay.reverse(),
|
|
||||||
spread,
|
|
||||||
spreadPercentage,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
setOrderbookData(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
[grouping, serumOrPerpMarket]
|
|
||||||
)
|
|
||||||
return orderbookData
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useOrderbookSubscription
|
|
|
@ -3,17 +3,12 @@
|
||||||
"assets-liabilities": "Assets & Liabilities",
|
"assets-liabilities": "Assets & Liabilities",
|
||||||
"daily-volume": "24h Volume",
|
"daily-volume": "24h Volume",
|
||||||
"export": "Export {{dataType}}",
|
"export": "Export {{dataType}}",
|
||||||
<<<<<<< Updated upstream
|
|
||||||
"liabilities": "Liabilities",
|
|
||||||
"lifetime-volume": "Lifetime Trade Volume",
|
|
||||||
=======
|
|
||||||
"funding-chart": "Funding Chart",
|
"funding-chart": "Funding Chart",
|
||||||
"init-health": "Init Health",
|
"init-health": "Init Health",
|
||||||
"liabilities": "Liabilities",
|
"liabilities": "Liabilities",
|
||||||
"lifetime-volume": "Lifetime Trade Volume",
|
"lifetime-volume": "Lifetime Trade Volume",
|
||||||
"maint-health": "Maint Health",
|
"maint-health": "Maint Health",
|
||||||
"no-data": "No data to display",
|
"no-data": "No data to display",
|
||||||
>>>>>>> Stashed changes
|
|
||||||
"no-pnl-history": "No PnL History",
|
"no-pnl-history": "No PnL History",
|
||||||
"pnl-chart": "PnL Chart",
|
"pnl-chart": "PnL Chart",
|
||||||
"pnl-history": "PnL History",
|
"pnl-history": "PnL History",
|
||||||
|
|
|
@ -413,3 +413,20 @@ export type TickerData = {
|
||||||
target_volume: string
|
target_volume: string
|
||||||
ticker_id: string
|
ticker_id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type cumOrderbookSide = {
|
||||||
|
price: number
|
||||||
|
size: number
|
||||||
|
cumulativeSize: number
|
||||||
|
sizePercent: number
|
||||||
|
maxSizePercent: number
|
||||||
|
cumulativeSizePercent: number
|
||||||
|
isUsersOrder: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OrderbookData = {
|
||||||
|
bids: cumOrderbookSide[]
|
||||||
|
asks: cumOrderbookSide[]
|
||||||
|
spread: number
|
||||||
|
spreadPercentage: number
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
import {
|
||||||
|
BookSide,
|
||||||
|
BookSideType,
|
||||||
|
MangoClient,
|
||||||
|
PerpMarket,
|
||||||
|
} from '@blockworks-foundation/mango-v4'
|
||||||
|
import { Market, Orderbook as SpotOrderBook } from '@project-serum/serum'
|
||||||
|
import { AccountInfo } from '@solana/web3.js'
|
||||||
|
import mangoStore from '@store/mangoStore'
|
||||||
|
import Big from 'big.js'
|
||||||
|
import { cumOrderbookSide } from 'types'
|
||||||
|
import { getDecimalCount } from './numbers'
|
||||||
|
|
||||||
|
export const getMarket = () => {
|
||||||
|
const group = mangoStore.getState().group
|
||||||
|
const selectedMarket = mangoStore.getState().selectedMarket.current
|
||||||
|
if (!group || !selectedMarket) return
|
||||||
|
return selectedMarket instanceof PerpMarket
|
||||||
|
? selectedMarket
|
||||||
|
: group?.getSerum3ExternalMarket(selectedMarket.serumMarketExternal)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeBookL2 = (book: SpotOrderBook | BookSide): number[][] => {
|
||||||
|
const depth = 300
|
||||||
|
if (book instanceof SpotOrderBook) {
|
||||||
|
return book.getL2(depth).map(([price, size]) => [price, size])
|
||||||
|
} else if (book instanceof BookSide) {
|
||||||
|
return book.getL2Ui(depth)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeBook(
|
||||||
|
client: MangoClient,
|
||||||
|
market: Market | PerpMarket,
|
||||||
|
accInfo: AccountInfo<Buffer>,
|
||||||
|
side: 'bids' | 'asks'
|
||||||
|
): SpotOrderBook | BookSide {
|
||||||
|
if (market instanceof Market) {
|
||||||
|
const book = SpotOrderBook.decode(market, accInfo.data)
|
||||||
|
return book
|
||||||
|
} else {
|
||||||
|
const decodedAcc = client.program.coder.accounts.decode(
|
||||||
|
'bookSide',
|
||||||
|
accInfo.data
|
||||||
|
)
|
||||||
|
const book = BookSide.from(
|
||||||
|
client,
|
||||||
|
market,
|
||||||
|
side === 'bids' ? BookSideType.bids : BookSideType.asks,
|
||||||
|
decodedAcc
|
||||||
|
)
|
||||||
|
return book
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updatePerpMarketOnGroup = (
|
||||||
|
book: BookSide,
|
||||||
|
side: 'bids' | 'asks'
|
||||||
|
) => {
|
||||||
|
const group = mangoStore.getState().group
|
||||||
|
const perpMarket = group?.getPerpMarketByMarketIndex(
|
||||||
|
book.perpMarket.perpMarketIndex
|
||||||
|
)
|
||||||
|
if (perpMarket) {
|
||||||
|
perpMarket[`_${side}`] = book
|
||||||
|
// mangoStore.getState().actions.fetchOpenOrders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hasOpenOrderForPriceGroup = (
|
||||||
|
openOrderPrices: number[],
|
||||||
|
price: number,
|
||||||
|
grouping: number,
|
||||||
|
isGrouped: boolean
|
||||||
|
) => {
|
||||||
|
if (!isGrouped) {
|
||||||
|
return !!openOrderPrices.find((ooPrice) => {
|
||||||
|
return ooPrice === price
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return !!openOrderPrices.find((ooPrice) => {
|
||||||
|
return ooPrice >= price - grouping && ooPrice <= price + grouping
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCumulativeOrderbookSide = (
|
||||||
|
orders: number[][],
|
||||||
|
totalSize: number,
|
||||||
|
maxSize: number,
|
||||||
|
depth: number,
|
||||||
|
usersOpenOrderPrices: number[],
|
||||||
|
grouping: number,
|
||||||
|
isGrouped: boolean
|
||||||
|
): cumOrderbookSide[] => {
|
||||||
|
let cumulativeSize = 0
|
||||||
|
return orders.slice(0, depth).map(([price, size]) => {
|
||||||
|
cumulativeSize += size
|
||||||
|
return {
|
||||||
|
price: Number(price),
|
||||||
|
size,
|
||||||
|
cumulativeSize,
|
||||||
|
sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100),
|
||||||
|
cumulativeSizePercent: Math.round((size / (cumulativeSize || 1)) * 100),
|
||||||
|
maxSizePercent: Math.round((size / (maxSize || 1)) * 100),
|
||||||
|
isUsersOrder: hasOpenOrderForPriceGroup(
|
||||||
|
usersOpenOrderPrices,
|
||||||
|
price,
|
||||||
|
grouping,
|
||||||
|
isGrouped
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const groupBy = (
|
||||||
|
ordersArray: number[][],
|
||||||
|
market: PerpMarket | Market,
|
||||||
|
grouping: number,
|
||||||
|
isBids: boolean
|
||||||
|
) => {
|
||||||
|
if (!ordersArray || !market || !grouping || grouping == market?.tickSize) {
|
||||||
|
return ordersArray || []
|
||||||
|
}
|
||||||
|
const groupFloors: Record<number, number> = {}
|
||||||
|
for (let i = 0; i < ordersArray.length; i++) {
|
||||||
|
if (typeof ordersArray[i] == 'undefined') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
const bigGrouping = Big(grouping)
|
||||||
|
const bigOrder = Big(ordersArray[i][0])
|
||||||
|
|
||||||
|
const floor = isBids
|
||||||
|
? bigOrder
|
||||||
|
.div(bigGrouping)
|
||||||
|
.round(0, Big.roundDown)
|
||||||
|
.times(bigGrouping)
|
||||||
|
.toNumber()
|
||||||
|
: bigOrder
|
||||||
|
.div(bigGrouping)
|
||||||
|
.round(0, Big.roundUp)
|
||||||
|
.times(bigGrouping)
|
||||||
|
.toNumber()
|
||||||
|
if (typeof groupFloors[floor] == 'undefined') {
|
||||||
|
groupFloors[floor] = ordersArray[i][1]
|
||||||
|
} else {
|
||||||
|
groupFloors[floor] = ordersArray[i][1] + groupFloors[floor]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sortedGroups = Object.entries(groupFloors)
|
||||||
|
.map((entry) => {
|
||||||
|
return [
|
||||||
|
+parseFloat(entry[0]).toFixed(getDecimalCount(grouping)),
|
||||||
|
entry[1],
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.sort((a: number[], b: number[]) => {
|
||||||
|
if (!a || !b) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return isBids ? b[0] - a[0] : a[0] - b[0]
|
||||||
|
})
|
||||||
|
return sortedGroups
|
||||||
|
}
|
Loading…
Reference in New Issue