add slippage to trade form

This commit is contained in:
saml33 2022-11-25 21:10:23 +11:00
parent 75f2fd720b
commit f9f86b529d
11 changed files with 114 additions and 17 deletions

View File

@ -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>

View File

@ -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>
)

View File

@ -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 },

34
hooks/useMarkPrice.tsx Normal file
View File

@ -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
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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,

View File

@ -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
}