add slippage to trade form
This commit is contained in:
parent
75f2fd720b
commit
f9f86b529d
|
@ -8,10 +8,10 @@ import useMangoAccount from 'hooks/useMangoAccount'
|
|||
|
||||
const HealthImpact = ({
|
||||
maintProjectedHealth,
|
||||
responsive,
|
||||
small,
|
||||
}: {
|
||||
maintProjectedHealth: number
|
||||
responsive?: boolean
|
||||
small?: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const group = mangoStore.getState().group
|
||||
|
@ -27,16 +27,14 @@ const HealthImpact = ({
|
|||
<Tooltip content={t('health-tooltip')}>
|
||||
<p
|
||||
className={`tooltip-underline mr-4 mb-1 ${
|
||||
responsive ? 'text-xs lg:text-sm' : ''
|
||||
small ? 'text-xs' : 'text-sm'
|
||||
}`}
|
||||
>
|
||||
{t('health-impact')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<div className="flex items-center space-x-1.5 font-mono">
|
||||
<p
|
||||
className={`text-th-fgd-1 ${responsive ? 'text-xs lg:text-sm' : ''}`}
|
||||
>
|
||||
<p className={`text-th-fgd-1 ${small ? 'text-xs' : 'text-sm'}`}>
|
||||
{currentMaintHealth}%
|
||||
</p>
|
||||
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />
|
||||
|
@ -47,7 +45,7 @@ const HealthImpact = ({
|
|||
: maintProjectedHealth <= 15
|
||||
? 'text-th-red'
|
||||
: 'text-th-green'
|
||||
} ${responsive ? 'text-xs lg:text-sm' : ''}`}
|
||||
} ${small ? 'text-xs' : 'text-sm'}`}
|
||||
>
|
||||
{maintProjectedHealth}%
|
||||
</p>
|
||||
|
|
|
@ -22,7 +22,7 @@ import NumberFormat, {
|
|||
} from 'react-number-format'
|
||||
import { notify } from 'utils/notifications'
|
||||
import SpotSlider from './SpotSlider'
|
||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
||||
import { calculateMarketPrice, calculateSlippage } from 'utils/tradeForm'
|
||||
import Image from 'next/legacy/image'
|
||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import Loading from '@components/shared/Loading'
|
||||
|
@ -37,6 +37,7 @@ import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
|||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
import useMarkPrice from 'hooks/useMarkPrice'
|
||||
|
||||
const TABS: [string, number][] = [
|
||||
['Limit', 0],
|
||||
|
@ -52,6 +53,7 @@ const AdvancedTradeForm = () => {
|
|||
const [useMargin, setUseMargin] = useState(true)
|
||||
const [placingOrder, setPlacingOrder] = useState(false)
|
||||
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'Slider')
|
||||
const markPrice = useMarkPrice()
|
||||
|
||||
const baseSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split(/-|\//)[0]
|
||||
|
@ -336,9 +338,22 @@ const AdvancedTradeForm = () => {
|
|||
: Math.trunc(simulatedHealthRatio!)
|
||||
}, [selectedMarket, tradeForm])
|
||||
|
||||
const slippage = useMemo(() => {
|
||||
if (tradeForm.tradeType === 'Market' && markPrice && selectedMarket) {
|
||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||
return calculateSlippage(
|
||||
orderbook,
|
||||
Number(tradeForm.baseSize),
|
||||
tradeForm.side,
|
||||
markPrice
|
||||
)
|
||||
}
|
||||
return 0
|
||||
}, [tradeForm, markPrice, selectedMarket])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
<div className="border-b border-th-bkg-3 md:border-t lg:border-t-0">
|
||||
<TabButtons
|
||||
activeValue={tradeForm.tradeType}
|
||||
onChange={(tab: 'Limit' | 'Market') => setTradeType(tab)}
|
||||
|
@ -547,8 +562,28 @@ const AdvancedTradeForm = () => {
|
|||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-4 px-4 lg:mt-6">
|
||||
<HealthImpact maintProjectedHealth={maintProjectedHealth} responsive />
|
||||
<div className="mt-4 space-y-1 px-4 lg:mt-6">
|
||||
<HealthImpact maintProjectedHealth={maintProjectedHealth} small />
|
||||
{slippage ? (
|
||||
<div className="flex justify-between">
|
||||
<Tooltip content={t('trade:tooltip-slippage')}>
|
||||
<p className="tooltip-underline mr-4 mb-1 text-xs">
|
||||
{t('trade:est-slippage')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p
|
||||
className={`text-xs ${
|
||||
slippage <= 1
|
||||
? 'text-th-green'
|
||||
: slippage <= 3
|
||||
? 'text-th-orange'
|
||||
: 'text-th-red'
|
||||
}`}
|
||||
>
|
||||
{slippage.toFixed(2)}%
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -117,23 +117,23 @@ const TradeAdvancedPage = () => {
|
|||
},
|
||||
],
|
||||
lg: [
|
||||
{ i: 'market-header', x: 0, y: 0, w: 14, h: marketHeaderHeight },
|
||||
{ i: 'tv-chart', x: 0, y: 1, w: 14, h: 488 },
|
||||
{ i: 'market-header', x: 0, y: 0, w: 12, h: marketHeaderHeight },
|
||||
{ i: 'tv-chart', x: 0, y: 1, w: 12, h: 488 },
|
||||
{
|
||||
i: 'balances',
|
||||
x: 0,
|
||||
y: 2,
|
||||
w: 14,
|
||||
w: 12,
|
||||
h: getHeight(innerHeight, 0, 488 + marketHeaderHeight),
|
||||
},
|
||||
{
|
||||
i: 'orderbook',
|
||||
x: 14,
|
||||
x: 12,
|
||||
y: 0,
|
||||
w: 5,
|
||||
w: 6,
|
||||
h: getHeight(innerHeight, 0, 0),
|
||||
},
|
||||
{ i: 'trade-form', x: 19, y: 0, w: 5, h: getHeight(innerHeight, 0, 0) },
|
||||
{ i: 'trade-form', x: 18, y: 0, w: 6, h: getHeight(innerHeight, 0, 0) },
|
||||
],
|
||||
md: [
|
||||
{ i: 'market-header', x: 0, y: 0, w: 18, h: marketHeaderHeight },
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export default function useMarkPrice() {
|
||||
const set = mangoStore((s) => s.set)
|
||||
const markPrice = mangoStore((s) => s.selectedMarket.markPrice)
|
||||
const orderbook = mangoStore((s) => s.selectedMarket.orderbook)
|
||||
const fills = mangoStore((s) => s.selectedMarket.fills)
|
||||
|
||||
const trades = fills
|
||||
.filter((trade: any) => trade?.eventFlags?.maker || trade?.maker)
|
||||
.map((trade: any) => ({
|
||||
...trade,
|
||||
side: trade.side === 'buy' ? 'sell' : 'buy',
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
const bb = orderbook?.bids?.length > 0 && Number(orderbook.bids[0][0])
|
||||
const ba = orderbook?.asks?.length > 0 && Number(orderbook.asks[0][0])
|
||||
const last = trades && trades.length > 0 && trades[0].price
|
||||
|
||||
const priceElements = [bb, ba, last].filter((e) => e).sort((a, b) => a - b)
|
||||
const newMarkPrice = priceElements.length
|
||||
? priceElements[Math.floor(priceElements.length / 2)]
|
||||
: null
|
||||
if (newMarkPrice !== markPrice) {
|
||||
set((state) => {
|
||||
state.selectedMarket.markPrice = newMarkPrice
|
||||
})
|
||||
}
|
||||
}, [orderbook, trades])
|
||||
|
||||
return markPrice
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"connect-positions": "Connect to view your positions",
|
||||
"connect-unsettled": "Connect to view your unsettled funds",
|
||||
"entry-price": "Entry Price",
|
||||
"est-slippage": "Est. Slippage",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-slippage": "An estimate of the differnece between the current price and the price your trade will be executed at",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"connect-positions": "Connect to view your positions",
|
||||
"connect-unsettled": "Connect to view your unsettled funds",
|
||||
"entry-price": "Entry Price",
|
||||
"est-slippage": "Est. Slippage",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-slippage": "An estimate of the differnece between the current price and the price your trade will be executed at",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"connect-positions": "Connect to view your positions",
|
||||
"connect-unsettled": "Connect to view your unsettled funds",
|
||||
"entry-price": "Entry Price",
|
||||
"est-slippage": "Est. Slippage",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-slippage": "An estimate of the differnece between the current price and the price your trade will be executed at",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"connect-positions": "Connect to view your positions",
|
||||
"connect-unsettled": "Connect to view your unsettled funds",
|
||||
"entry-price": "Entry Price",
|
||||
"est-slippage": "Est. Slippage",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-slippage": "An estimate of the differnece between the current price and the price your trade will be executed at",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"connect-positions": "Connect to view your positions",
|
||||
"connect-unsettled": "Connect to view your unsettled funds",
|
||||
"entry-price": "Entry Price",
|
||||
"est-slippage": "Est. Slippage",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate-Or-Cancel (IOC) orders are guaranteed to be the taker and must be executed immediately. Any portion of the order that can't be filled immediately will be cancelled",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker or they will be canceled",
|
||||
"tooltip-slippage": "An estimate of the differnece between the current price and the price your trade will be executed at",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -195,6 +195,7 @@ export type MangoStore = {
|
|||
current: Serum3Market | PerpMarket | undefined
|
||||
fills: any
|
||||
orderbook: Orderbook
|
||||
markPrice: number
|
||||
}
|
||||
serumMarkets: Serum3Market[]
|
||||
serumOrders: Order[] | undefined
|
||||
|
@ -299,6 +300,7 @@ const mangoStore = create<MangoStore>()(
|
|||
bids: [],
|
||||
asks: [],
|
||||
},
|
||||
markPrice: 0,
|
||||
},
|
||||
serumMarkets: [],
|
||||
serumOrders: undefined,
|
||||
|
|
|
@ -27,3 +27,21 @@ export const calculateMarketPrice = (
|
|||
return selectedOrder[0] * 0.95
|
||||
}
|
||||
}
|
||||
|
||||
export const calculateSlippage = (
|
||||
orderBook: Orderbook,
|
||||
size: number,
|
||||
side: 'buy' | 'sell',
|
||||
markPrice: number
|
||||
): number => {
|
||||
const bb = orderBook?.bids?.length > 0 && Number(orderBook.bids[0][0])
|
||||
const ba = orderBook?.asks?.length > 0 && Number(orderBook.asks[0][0])
|
||||
const referencePrice = bb && ba ? (bb + ba) / 2 : markPrice
|
||||
|
||||
const estimatedPrice = calculateMarketPrice(orderBook, Number(size), side)
|
||||
|
||||
const slippageAbs =
|
||||
Number(size) > 0 ? Math.abs(estimatedPrice - referencePrice) : 0
|
||||
const slippageRel = (slippageAbs / referencePrice) * 100
|
||||
return slippageRel
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue