import { ArrowSmDownIcon } from '@heroicons/react/solid' import BN from 'bn.js' import Link from 'next/link' import { useRouter } from 'next/router' import SideBadge from './SideBadge' import Button, { LinkButton } from './Button' import { useSortableData } from '../hooks/useSortableData' import { useViewport } from '../hooks/useViewport' import { breakpoints } from './TradePageGrid' import { Table, TableDateDisplay, Td, Th, TrBody, TrHead, } from './TableElements' import { ExpandableRow } from './TableElements' import { formatUsdValue } from '../utils' import { useTranslation } from 'next-i18next' import Pagination from './Pagination' import usePagination from '../hooks/usePagination' import { useEffect, useMemo, useState } from 'react' import { useFilteredData } from '../hooks/useFilteredData' import TradeHistoryFilterModal from './TradeHistoryFilterModal' import { FilterIcon, InformationCircleIcon, RefreshIcon, SaveIcon, } from '@heroicons/react/outline' import { fetchHourlyPerformanceStats } from './account_page/AccountOverview' import useMangoStore from '../stores/useMangoStore' import Loading from './Loading' import { exportDataToCSV } from '../utils/export' import Tooltip from './Tooltip' import { useWallet } from '@solana/wallet-adapter-react' const formatTradeDateTime = (timestamp: BN | string) => { // don't compare to BN because of npm maddness // prototypes can be different due to multiple versions being imported if (typeof timestamp === 'string') { return timestamp } else { return timestamp.toNumber() * 1000 } } const TradeHistoryTable = ({ numTrades, showExportPnl, }: { numTrades?: number showExportPnl?: boolean }) => { const { t } = useTranslation('common') const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const { asPath } = useRouter() const { width } = useViewport() const tradeHistoryAndLiquidations = useMangoStore( (state) => state.tradeHistory.parsed ) const tradeHistory = tradeHistoryAndLiquidations.filter( (t) => !('liqor' in t) ) const isMobile = width ? width < breakpoints.md : false const [filters, setFilters] = useState({}) const [showFiltersModal, setShowFiltersModal] = useState(false) const [loadExportData, setLoadExportData] = useState(false) const filteredData = useFilteredData(tradeHistory, filters) const initialLoad = useMangoStore((s) => s.tradeHistory.initialLoad) const { publicKey } = useWallet() const { paginatedData, totalPages, nextPage, previousPage, page, firstPage, lastPage, setData, data, } = usePagination(filteredData, { perPage: 100 }) const { items, requestSort, sortConfig } = useSortableData(paginatedData) useEffect(() => { if (data?.length !== filteredData?.length) { setData(filteredData) } }, [filteredData]) const renderMarketName = (trade: any) => { if ( trade.marketName.includes('PERP') || trade.marketName.includes('USDC') ) { const location = `/?name=${trade.marketName}` if (asPath.includes(location)) { return {trade.marketName} } else { return ( {trade.marketName} ) } } else { return {trade.marketName} } } const exportPerformanceDataToCSV = async () => { setLoadExportData(true) const exportData = await fetchHourlyPerformanceStats( mangoAccount.publicKey.toString(), 10000 ) const dataToExport = exportData.map((row) => { const timestamp = new Date(row.time) return { timestamp: `${timestamp.toLocaleDateString()} ${timestamp.toLocaleTimeString()}`, account_equity: row.account_equity, pnl: row.pnl, } }) const title = `${ mangoAccount.name || mangoAccount.publicKey }-Performance-${new Date().toLocaleDateString()}` const headers = ['Timestamp', 'Account Equity', 'PNL'] exportDataToCSV(dataToExport, title, headers, t) setLoadExportData(false) } const hasActiveFilter = useMemo(() => { return tradeHistory.length !== filteredData.length }, [data, filteredData]) const mangoAccountPk = useMemo(() => { console.log('new mango account') return mangoAccount.publicKey.toString() }, [mangoAccount]) const canWithdraw = mangoAccount && publicKey ? mangoAccount.owner.equals(publicKey) : false return ( <>

{!initialLoad ? : data.length}{' '} {data.length === 1 ? 'Trade' : 'Trades'}

{t('delay-displaying-recent')} {t('use-explorer-one')} {t('use-explorer-two')} {t('use-explorer-three')}
} >
{hasActiveFilter ? ( setFilters({})} > {t('reset-filters')} ) : null} {tradeHistory.length >= 15 && tradeHistory.length <= 10000 && initialLoad ? ( ) : null} {canWithdraw && showExportPnl ? ( ) : null} {canWithdraw ? (
e.toString() !== '11111111111111111111111111111111' ) .join(',')}`} download target="_blank" rel="noopener noreferrer" > {t('export-trades-csv')}
) : null}
{tradeHistory && paginatedData.length > 0 ? ( !isMobile ? ( <> {items.map((trade: any) => { return ( ) })}
requestSort('marketName')} > {t('market')} requestSort('side')} > {t('side')} requestSort('size')} > {t('size')} requestSort('price')} > {t('price')} requestSort('value')} > {t('value')} requestSort('liquidity')} > {t('liquidity')} requestSort('feeCost')} > {t('fee')} requestSort('loadTimestamp')} > {t('approximate-time')}
{renderMarketName(trade)}
{trade.size} $ {new Intl.NumberFormat('en-US').format( trade.price )} {formatUsdValue(trade.value)} {t(trade.liquidity.toLowerCase())} {formatUsdValue(trade.feeCost)} {trade.loadTimestamp || trade.timestamp ? ( ) : ( t('recent') )} {trade.marketName.includes('PERP') ? ( {t('view-counterparty')} ) : null}
{numTrades && items.length > numTrades ? (
{t('view-all-trades')}
) : (
)} ) : ( paginatedData.map((trade: any, index) => (
{trade.loadTimestamp || trade.timestamp ? ( ) : ( t('recent') )}
{trade.marketName}
{trade.side.toUpperCase()} {trade.size}
} key={`${index}`} panelTemplate={
{t('price')}
{formatUsdValue(trade.price)}
{t('value')}
{formatUsdValue(trade.value)}
{t('liquidity')}
{trade.liquidity}
{t('fee')}
{formatUsdValue(trade.feeCost)}
} /> )) ) ) : hasActiveFilter ? (
{t('no-trades-found')}
) : (
{t('no-history')} {asPath === '/account' ? ( {t('make-trade')} ) : null}
)}
{showFiltersModal ? ( setShowFiltersModal(false)} /> ) : null} ) } export default TradeHistoryTable