subscribe to orderbook on account page for market close

This commit is contained in:
saml33 2023-03-28 21:12:11 +11:00
parent b312dca2e2
commit a2f05a8fa7
2 changed files with 139 additions and 62 deletions

View File

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

View File

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