subscribe to orderbook on account page for market close
This commit is contained in:
parent
b312dca2e2
commit
a2f05a8fa7
|
@ -1,4 +1,4 @@
|
|||
import { FunctionComponent, useCallback, useState } from 'react'
|
||||
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import {
|
||||
|
@ -16,6 +16,7 @@ import { SOUND_SETTINGS_KEY } from 'utils/constants'
|
|||
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
|
||||
import { Howl } from 'howler'
|
||||
import { isMangoError } from 'types'
|
||||
import { decodeBook, decodeBookL2 } from './Orderbook'
|
||||
|
||||
interface MarketCloseModalProps {
|
||||
onClose: () => void
|
||||
|
@ -30,6 +31,8 @@ const successSound = new Howl({
|
|||
volume: 0.5,
|
||||
})
|
||||
|
||||
type BidsAndAsks = number[][] | null
|
||||
|
||||
const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
||||
onClose,
|
||||
isOpen,
|
||||
|
@ -37,81 +40,155 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const group = mangoStore.getState().group
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const group = mangoStore((s) => s.group)
|
||||
const perpMarket = group?.getPerpMarketByMarketIndex(position.marketIndex)
|
||||
const [soundSettings] = useLocalStorageState(
|
||||
SOUND_SETTINGS_KEY,
|
||||
INITIAL_SOUND_SETTINGS
|
||||
)
|
||||
const [asks, setAsks] = useState<BidsAndAsks>(null)
|
||||
const [bids, setBids] = useState<BidsAndAsks>(null)
|
||||
|
||||
const handleMarketClose = useCallback(async () => {
|
||||
// subscribe to the bids and asks orderbook accounts
|
||||
useEffect(() => {
|
||||
console.log('setting up orderbook websockets')
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const actions = mangoStore.getState().actions
|
||||
if (!group || !perpMarket) return
|
||||
|
||||
if (!group || !mangoAccount || !perpMarket) return
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const baseSize = position.getBasePositionUi(perpMarket)
|
||||
const sideToClose = baseSize > 0 ? 'sell' : 'buy'
|
||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||
const price = calculateEstPriceForBaseSize(
|
||||
orderbook,
|
||||
baseSize,
|
||||
sideToClose
|
||||
let bidSubscriptionId: number | undefined = undefined
|
||||
let askSubscriptionId: number | undefined = undefined
|
||||
let lastSeenBidsSlot: number
|
||||
let lastSeenAsksSlot: number
|
||||
const bidsPk = perpMarket.bids
|
||||
if (bidsPk) {
|
||||
connection
|
||||
.getAccountInfoAndContext(bidsPk)
|
||||
.then(({ context, value: info }) => {
|
||||
if (!info) return
|
||||
const decodedBook = decodeBook(client, perpMarket, info, 'bids')
|
||||
setBids(decodeBookL2(decodedBook))
|
||||
lastSeenBidsSlot = context.slot
|
||||
})
|
||||
bidSubscriptionId = connection.onAccountChange(
|
||||
bidsPk,
|
||||
(info, context) => {
|
||||
if (context.slot > lastSeenBidsSlot) {
|
||||
const decodedBook = decodeBook(client, perpMarket, info, 'bids')
|
||||
setBids(decodeBookL2(decodedBook))
|
||||
}
|
||||
},
|
||||
'processed'
|
||||
)
|
||||
}
|
||||
|
||||
const maxSlippage = 0.025
|
||||
// const perpOrderType =
|
||||
// tradeForm.tradeType === 'Market'
|
||||
// ? PerpOrderType.market
|
||||
// : tradeForm.ioc
|
||||
// ? PerpOrderType.immediateOrCancel
|
||||
// : tradeForm.postOnly
|
||||
// ? PerpOrderType.postOnly
|
||||
// : PerpOrderType.limit
|
||||
const tx = await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
perpMarket.perpMarketIndex,
|
||||
sideToClose === 'buy' ? PerpOrderSide.bid : PerpOrderSide.ask,
|
||||
price * (sideToClose === 'buy' ? 1 + maxSlippage : 1 - maxSlippage),
|
||||
Math.abs(baseSize) * 2, // send a larger size to ensure full order is closed
|
||||
undefined, // maxQuoteQuantity
|
||||
Date.now(),
|
||||
PerpOrderType.immediateOrCancel,
|
||||
true, // reduce only
|
||||
undefined,
|
||||
undefined
|
||||
const asksPk = perpMarket.asks
|
||||
if (asksPk) {
|
||||
connection
|
||||
.getAccountInfoAndContext(asksPk)
|
||||
.then(({ context, value: info }) => {
|
||||
if (!info) return
|
||||
const decodedBook = decodeBook(client, perpMarket, info, 'asks')
|
||||
setAsks(decodeBookL2(decodedBook))
|
||||
lastSeenAsksSlot = context.slot
|
||||
})
|
||||
askSubscriptionId = connection.onAccountChange(
|
||||
asksPk,
|
||||
(info, context) => {
|
||||
if (context.slot > lastSeenAsksSlot) {
|
||||
const decodedBook = decodeBook(client, perpMarket, info, 'asks')
|
||||
setAsks(decodeBookL2(decodedBook))
|
||||
}
|
||||
},
|
||||
'processed'
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
set((s) => {
|
||||
s.successAnimation.trade = true
|
||||
})
|
||||
if (soundSettings['swap-success']) {
|
||||
successSound.play()
|
||||
}
|
||||
return () => {
|
||||
if (typeof bidSubscriptionId !== 'undefined') {
|
||||
connection.removeAccountChangeListener(bidSubscriptionId)
|
||||
}
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
} catch (e) {
|
||||
if (isMangoError(e)) {
|
||||
if (typeof askSubscriptionId !== 'undefined') {
|
||||
connection.removeAccountChangeListener(askSubscriptionId)
|
||||
}
|
||||
}
|
||||
}, [connection, perpMarket])
|
||||
|
||||
const handleMarketClose = useCallback(
|
||||
async (bids: BidsAndAsks, asks: BidsAndAsks) => {
|
||||
const client = mangoStore.getState().client
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (!group || !mangoAccount || !perpMarket || !bids || !asks) {
|
||||
notify({
|
||||
title: 'There was an issue.',
|
||||
description: e.message,
|
||||
txid: e?.txid,
|
||||
title: 'Something went wrong. Try again later',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
console.error('Place trade error:', e)
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
onClose()
|
||||
}
|
||||
}, [])
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const baseSize = position.getBasePositionUi(perpMarket)
|
||||
const sideToClose = baseSize > 0 ? 'sell' : 'buy'
|
||||
const orderbook = { bids, asks }
|
||||
const price = calculateEstPriceForBaseSize(
|
||||
orderbook,
|
||||
baseSize,
|
||||
sideToClose
|
||||
)
|
||||
|
||||
const maxSlippage = 0.025
|
||||
// const perpOrderType =
|
||||
// tradeForm.tradeType === 'Market'
|
||||
// ? PerpOrderType.market
|
||||
// : tradeForm.ioc
|
||||
// ? PerpOrderType.immediateOrCancel
|
||||
// : tradeForm.postOnly
|
||||
// ? PerpOrderType.postOnly
|
||||
// : PerpOrderType.limit
|
||||
const tx = await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
perpMarket.perpMarketIndex,
|
||||
sideToClose === 'buy' ? PerpOrderSide.bid : PerpOrderSide.ask,
|
||||
price * (sideToClose === 'buy' ? 1 + maxSlippage : 1 - maxSlippage),
|
||||
Math.abs(baseSize) * 2, // send a larger size to ensure full order is closed
|
||||
undefined, // maxQuoteQuantity
|
||||
Date.now(),
|
||||
PerpOrderType.immediateOrCancel,
|
||||
true, // reduce only
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
set((s) => {
|
||||
s.successAnimation.trade = true
|
||||
})
|
||||
if (soundSettings['swap-success']) {
|
||||
successSound.play()
|
||||
}
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
} catch (e) {
|
||||
if (isMangoError(e)) {
|
||||
notify({
|
||||
title: 'There was an issue.',
|
||||
description: e.message,
|
||||
txid: e?.txid,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
console.error('Place trade error:', e)
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
onClose()
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose} isOpen={isOpen}>
|
||||
|
@ -121,7 +198,7 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
|||
<div className="pb-6 text-th-fgd-3">{t('trade:price-expect')}</div>
|
||||
<Button
|
||||
className="mb-4 flex w-full items-center justify-center"
|
||||
onClick={handleMarketClose}
|
||||
onClick={() => handleMarketClose(bids, asks)}
|
||||
size="large"
|
||||
>
|
||||
{submitting ? <Loading /> : <span>{t('trade:close-position')}</span>}
|
||||
|
|
|
@ -49,7 +49,7 @@ export const decodeBookL2 = (book: SpotOrderBook | BookSide): number[][] => {
|
|||
return []
|
||||
}
|
||||
|
||||
function decodeBook(
|
||||
export function decodeBook(
|
||||
client: MangoClient,
|
||||
market: Market | PerpMarket,
|
||||
accInfo: AccountInfo<Buffer>,
|
||||
|
|
Loading…
Reference in New Issue