Skip to content

Commit

Permalink
Merge pull request #873 from alexstotsky/summary-active-positions
Browse files Browse the repository at this point in the history
(feature) App summary: active positions section
  • Loading branch information
ezewer authored Oct 8, 2024
2 parents ee88f0e + 895e4b6 commit 58cbea5
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 2 deletions.
7 changes: 7 additions & 0 deletions public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,13 @@
"min_balance_switch": "Minimum Balance",
"min_balance_input": "Set a minimum balance for assets to show up in the table."
}
},
"positions": {
"title": "Active Positions",
"derivative": "Derivative",
"margin": "Margin",
"position": "Position",
"amount": "Amount"
}
},
"symbols": {
Expand Down
125 changes: 124 additions & 1 deletion src/components/AppSummary/AppSummary.columns.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react'
import { Cell } from '@blueprintjs/table'

import { fixedFloat } from 'ui/utils'
import { formatAmount, fixedFloat } from 'ui/utils'
import LoadingPlaceholder from 'ui/LoadingPlaceholder'
import {
getCell,
getCellLoader,
getCellNoData,
getColumnWidth,
Expand All @@ -12,10 +13,12 @@ import {

import {
getIsTotal,
getPairLabel,
formatUsdValue,
getFeePercentCell,
formatPercentValue,
formatUsdValueChange,
formatSecondaryPercentValue,
} from './AppSummary.helpers'

export const getFeesColumns = ({
Expand Down Expand Up @@ -302,3 +305,123 @@ export const getAssetColumns = ({
copyText: rowIndex => fixedFloat(preparedData[rowIndex]?.marginFundingPayment, 2),
},
]

export const getPositionsColumns = ({
t,
isNoData,
isLoading,
entries,
columnsWidth,
}) => [
{
id: 'pair',
name: 'column.pair',
className: 'align-left',
width: getColumnWidth('pair', columnsWidth),
renderer: (rowIndex) => {
if (isLoading) return getCellLoader(22, 80)
if (isNoData) return getCellNoData(t('column.noResults'))
const { pair, leverage } = entries[rowIndex]
const pairLabel = getPairLabel(t, pair, leverage)
return (
<Cell tooltip={getTooltipContent(pair, t)}>
<>
<span className='cell-value'>
{pair}
</span>
<br />
<span className='cell-value secondary-value-left'>
{pairLabel}
</span>
</>
</Cell>
)
},
copyText: rowIndex => entries[rowIndex].pair,
},
{
id: 'amount',
name: 'column.amount',
width: getColumnWidth('amount', columnsWidth),
renderer: (rowIndex) => {
if (isLoading) return getCellLoader(22, 80)
if (isNoData) return getCellNoData()
const { amount, basePrice } = entries[rowIndex]
return (
<Cell tooltip={getTooltipContent(fixedFloat(amount), t)}>
<>
<span className='cell-value'>
{fixedFloat(amount)}
</span>
<br />
<span className='cell-value secondary-value'>
@
{fixedFloat(basePrice)}
</span>
</>
</Cell>
)
},
copyText: rowIndex => fixedFloat(entries[rowIndex].amount),
},
{
id: 'pl',
name: 'column.pl',
width: getColumnWidth('pl', columnsWidth),
renderer: (rowIndex) => {
if (isLoading) return getCellLoader(22, 80)
if (isNoData) return getCellNoData()
const { pl, plPerc } = entries[rowIndex]
return (
<Cell tooltip={getTooltipContent(fixedFloat(pl), t)}>
<>
<span className='cell-value'>
{formatAmount(pl)}
</span>
<br />
<span className='cell-value secondary-value'>
{formatSecondaryPercentValue(plPerc)}
</span>
</>
</Cell>
)
},
copyText: rowIndex => fixedFloat(entries[rowIndex].pl),
},
{
id: 'liquidationPrice',
name: 'column.liq-price',
width: getColumnWidth('liquidationPrice', columnsWidth),
renderer: (rowIndex) => {
if (isLoading) return getCellLoader(22, 80)
if (isNoData) return getCellNoData()
const { liquidationPrice } = entries[rowIndex]
return getCell(formatAmount(liquidationPrice, { color: 'red' }), t, fixedFloat(liquidationPrice))
},
copyText: rowIndex => fixedFloat(entries[rowIndex].liquidationPrice),
},
{
id: 'marginFunding',
name: 'column.fundingCost',
width: getColumnWidth('marginFunding', columnsWidth),
renderer: (rowIndex) => {
if (isLoading) return getCellLoader(22, 80)
if (isNoData) return getCellNoData()
const { marginFunding } = entries[rowIndex]
return getCell(fixedFloat(marginFunding), t)
},
copyText: rowIndex => fixedFloat(entries[rowIndex].marginFunding),
},
{
id: 'collateral',
name: 'column.collateral',
width: getColumnWidth('collateral', columnsWidth),
renderer: (rowIndex) => {
if (isLoading) return getCellLoader(22, 80)
if (isNoData) return getCellNoData()
const { collateral } = entries[rowIndex]
return getCell(`$${fixedFloat(collateral)}`, t)
},
copyText: rowIndex => fixedFloat(entries[rowIndex].collateral),
},
]
2 changes: 2 additions & 0 deletions src/components/AppSummary/AppSummary.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from 'state/accountBalance/actions'
import { refresh as refreshSummaryByAsset } from 'state/summaryByAsset/actions'
import { refresh as refreshProfits } from 'state/profits/actions'
import { refresh as refreshPositions } from 'state/positionsActive/actions'
import {
getData,
getPageLoading,
Expand Down Expand Up @@ -44,6 +45,7 @@ const mapDispatchToProps = {
setParams,
refreshBalance,
refreshProfits,
refreshPositions,
refreshSummaryByAsset,
}

Expand Down
15 changes: 15 additions & 0 deletions src/components/AppSummary/AppSummary.helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react'
import { Cell } from '@blueprintjs/table'
import _isNil from 'lodash/isNil'
import _endsWith from 'lodash/endsWith'

import LoadingPlaceholder from 'ui/LoadingPlaceholder'
import { fixedFloat, formatFee, formatThousands } from 'ui/utils'
Expand Down Expand Up @@ -60,3 +62,16 @@ export const getFeePercentCell = (isLoading, value) => (
)}
</Cell>
)

export const formatSecondaryPercentValue = (value) => {
const val = prepareNumericValue(value)
if (val > 1) return <span className='percent-pos-value'>{`${val}%`}</span>
if (val < 1) return <span className='percent-neg-value'>{`${val}%`}</span>
return <span>{`${val}%`}</span>
}

export const getPairLabel = (t, pair, leverage) => {
if (_endsWith(pair, 'PERP')) return t('summary.positions.derivative')
if (_isNil(leverage)) return t('summary.positions.position')
return t('summary.positions.margin')
}
7 changes: 6 additions & 1 deletion src/components/AppSummary/AppSummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Fees from './AppSummary.fees'
import Value from './AppSummary.value'
import Profits from './AppSummary.profits'
import ByAsset from './AppSummary.byAsset'
import Positions from './AppSummary.positions'

const AppSummary = ({
t,
Expand All @@ -33,6 +34,7 @@ const AppSummary = ({
isSyncRequired,
refreshBalance,
refreshProfits,
refreshPositions,
currentTimeFrame,
refreshSummaryByAsset,
isUnrealizedProfitExcluded,
Expand All @@ -55,8 +57,9 @@ const AppSummary = ({
refresh()
refreshBalance()
refreshProfits()
refreshPositions()
refreshSummaryByAsset()
}, [refresh, refreshBalance, refreshSummaryByAsset, refreshProfits])
}, [refresh, refreshBalance, refreshSummaryByAsset, refreshProfits, refreshPositions])

return (
<Card
Expand Down Expand Up @@ -111,6 +114,7 @@ const AppSummary = ({
<Profits />
</div>
<ByAsset />
<Positions />
<div className='app-summary-data-row'>
<Fees
t={t}
Expand Down Expand Up @@ -151,6 +155,7 @@ AppSummary.propTypes = {
isUnrealizedProfitExcluded: PropTypes.bool.isRequired,
refreshSummaryByAsset: PropTypes.func.isRequired,
refreshProfits: PropTypes.func.isRequired,
refreshPositions: PropTypes.func.isRequired,
}

AppSummary.defaultProps = {
Expand Down
93 changes: 93 additions & 0 deletions src/components/AppSummary/AppSummary.positions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useEffect, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import { isEmpty } from '@bitfinex/lib-js-util-base'

import config from 'config'
import DataTable from 'ui/DataTable'
import { fetchAPositions } from 'state/positionsActive/actions'
import {
getEntries,
getPageLoading,
getDataReceived,
} from 'state/positionsActive/selectors'
import queryConstants from 'state/query/constants'
import { getColumnsWidth } from 'state/columns/selectors'
import { getIsSyncRequired, getIsFirstSyncing } from 'state/sync/selectors'

import { getPositionsColumns } from './AppSummary.columns'

const { showFrameworkMode } = config
const TYPE = queryConstants.SUMMARY_POSITIONS

const SummaryActivePositions = () => {
const { t } = useTranslation()
const dispatch = useDispatch()
const entries = useSelector(getEntries)
const pageLoading = useSelector(getPageLoading)
const dataReceived = useSelector(getDataReceived)
const isFirstSync = useSelector(getIsFirstSyncing)
const isSyncRequired = useSelector(getIsSyncRequired)
const columnsWidth = useSelector((state) => getColumnsWidth(state, TYPE))
const isLoading = isFirstSync || (!dataReceived && pageLoading)
const isNoData = dataReceived && isEmpty(entries)
const tableClasses = classNames('summary-positions-table', {
'empty-table': isNoData,
})

useEffect(() => {
if (!dataReceived && !pageLoading && !isSyncRequired) {
dispatch(fetchAPositions())
}
}, [dataReceived, pageLoading, isSyncRequired])


const columns = useMemo(
() => getPositionsColumns({
entries, t, isLoading, isNoData, columnsWidth,
}),
[entries, t, isLoading, isNoData, columnsWidth],
)

let showContent
if (isNoData) {
showContent = (
<DataTable
isNoData={isNoData}
isLoading={isLoading}
defaultRowHeight={73}
tableColumns={columns}
className={tableClasses}
numRows={isLoading ? 3 : 1}
enableColumnResizing={showFrameworkMode}
/>
)
} else {
showContent = (
<DataTable
section={TYPE}
defaultRowHeight={73}
tableColumns={columns}
className={tableClasses}
numRows={isLoading ? 3 : entries.length}
enableColumnResizing={showFrameworkMode}
/>
)
}

return (
<div className='app-summary-item full-width-item'>
<div className='app-summary-item-title--row'>
<div>
<div className='app-summary-item-title'>
{t('summary.positions.title')}
</div>
</div>
</div>
{showContent}
</div>
)
}

export default SummaryActivePositions
Loading

0 comments on commit 58cbea5

Please sign in to comment.