chore(explorer): market info shared panels (#3192)

This commit is contained in:
Art 2023-03-15 09:21:12 +01:00 committed by GitHub
parent 93f8450ecb
commit 66a85cce2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 500 additions and 449 deletions

View File

@ -1,143 +1,52 @@
import {
addDecimalsFormatNumber,
formatNumberPercentage,
getMarketExpiryDateFormatted,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/market-info';
import { MarketInfoTable } from '@vegaprotocol/market-info';
import { LiquidityInfoPanel } from '@vegaprotocol/market-info';
import { LiquidityMonitoringParametersInfoPanel } from '@vegaprotocol/market-info';
import {
MarketStateMapping,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { Splash } from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
InstrumentInfoPanel,
KeyDetailsInfoPanel,
LiquidityPriceRangeInfoPanel,
MetadataInfoPanel,
OracleInfoPanel,
RiskFactorsInfoPanel,
RiskModelInfoPanel,
RiskParametersInfoPanel,
SettlementAssetInfoPanel,
} from '@vegaprotocol/market-info';
import { MarketInfoTable } from '@vegaprotocol/market-info';
import { Link } from 'react-router-dom';
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
const quoteUnit = market?.tradableInstrument.instrument.product.quoteName;
const assetId = useMemo(
() => market?.tradableInstrument.instrument.product?.settlementAsset.id,
[market]
);
const { data: asset } = useAssetDataProvider(assetId ?? '');
if (!market) return null;
const keyDetails = {
decimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
tradingMode: market.tradingMode,
state: MarketStateMapping[market.state],
};
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
const liquidityPriceRange = formatNumberPercentage(
new BigNumber(market.lpPriceRange).times(100)
);
const panels = [
{
title: t('Key details'),
content: (
<MarketInfoTable
noBorder={false}
data={{
name: market.tradableInstrument.instrument.name,
marketID: market.id,
tradingMode:
keyDetails.tradingMode &&
MarketTradingModeMapping[keyDetails.tradingMode],
marketDecimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
settlementAssetDecimalPlaces: assetDecimals,
}}
/>
),
content: <KeyDetailsInfoPanel noBorder={false} market={market} />,
},
{
title: t('Instrument'),
content: (
<MarketInfoTable
noBorder={false}
data={{
marketName: market.tradableInstrument.instrument.name,
code: market.tradableInstrument.instrument.code,
productType:
market.tradableInstrument.instrument.product.__typename,
...market.tradableInstrument.instrument.product,
}}
/>
),
content: <InstrumentInfoPanel noBorder={false} market={market} />,
},
{
title: t('Settlement asset'),
content: asset ? (
<AssetDetailsTable
asset={asset}
inline={true}
noBorder={false}
dtClassName="text-black dark:text-white text-ui !px-0 !font-normal"
ddClassName="text-black dark:text-white text-ui !px-0 !font-normal max-w-full"
/>
) : (
<Splash>{t('No data')}</Splash>
),
content: <SettlementAssetInfoPanel market={market} noBorder={false} />,
},
{
title: t('Metadata'),
content: (
<MarketInfoTable
noBorder={false}
data={{
expiryDate: getMarketExpiryDateFormatted(
market.tradableInstrument.instrument.metadata.tags
),
...market.tradableInstrument.instrument.metadata.tags
?.map((tag) => {
const [key, value] = tag.split(':');
return { [key]: value };
})
.reduce((acc, curr) => ({ ...acc, ...curr }), {}),
}}
/>
),
content: <MetadataInfoPanel noBorder={false} market={market} />,
},
{
title: t('Risk model'),
content: (
<MarketInfoTable
noBorder={false}
data={market.tradableInstrument.riskModel}
unformatted={true}
omits={[]}
/>
),
content: <RiskModelInfoPanel noBorder={false} market={market} />,
},
{
title: t('Risk parameters'),
content: (
<MarketInfoTable
noBorder={false}
data={market.tradableInstrument.riskModel.params}
unformatted={true}
omits={[]}
/>
),
content: <RiskParametersInfoPanel noBorder={false} market={market} />,
},
{
title: t('Risk factors'),
content: (
<MarketInfoTable
noBorder={false}
data={market.riskFactors}
unformatted={true}
omits={['market', '__typename']}
/>
),
content: <RiskFactorsInfoPanel noBorder={false} market={market} />,
},
...(market.priceMonitoringSettings?.parameters?.triggers || []).map(
(trigger, i) => ({
@ -158,7 +67,10 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
<MarketInfoTable
noBorder={false}
data={{ referencePrice: trigger.referencePrice }}
decimalPlaces={assetDecimals}
decimalPlaces={
market.tradableInstrument.instrument.product.settlementAsset
.decimals
}
/>
</>
),
@ -166,64 +78,26 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
{
title: t('Liquidity monitoring parameters'),
content: (
<MarketInfoTable
<LiquidityMonitoringParametersInfoPanel
noBorder={false}
data={{
triggeringRatio:
market.liquidityMonitoringParameters.triggeringRatio,
...market.liquidityMonitoringParameters.targetStakeParameters,
}}
market={market}
/>
),
},
{
title: t('Liquidity'),
content: <LiquidityInfoPanel market={market} noBorder={false} />,
},
{
title: t('Liquidity price range'),
content: (
<>
<p className="text-xs mb-4">
{`For liquidity orders to count towards a commitment, they must be
within the liquidity monitoring bounds.`}
</p>
<p className="text-xs mb-4">
{`The liquidity price range is a ${liquidityPriceRange} difference from the mid
price.`}
</p>
<MarketInfoTable
noBorder={false}
data={{
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
lowestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.minus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
highestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.plus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
}}
></MarketInfoTable>
</>
<LiquidityPriceRangeInfoPanel market={market} noBorder={false} />
),
},
{
title: t('Oracle'),
content: (
<MarketInfoTable
noBorder={false}
data={
market.tradableInstrument.instrument.product.dataSourceSpecBinding
}
>
<OracleInfoPanel noBorder={false} market={market}>
<Link
className="text-xs hover:underline"
to={`/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id}`}
@ -236,7 +110,7 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
>
{t('View termination oracle specification')}
</Link>
</MarketInfoTable>
</OracleInfoPanel>
),
},
];
@ -244,7 +118,7 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
return (
<>
{panels.map((p) => (
<div className="mb-3">
<div key={p.title} className="mb-3">
<h2 className="font-alpha calt text-xl">{p.title}</h2>
{p.content}
</div>

View File

@ -8,7 +8,7 @@ import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import compact from 'lodash/compact';
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { marketInfoProvider } from '@vegaprotocol/market-info';
import { marketInfoWithDataProvider } from '@vegaprotocol/market-info';
import { PageTitle } from '../../components/page-helpers/page-title';
export const MarketPage = () => {
@ -17,7 +17,7 @@ export const MarketPage = () => {
const { marketId } = useParams<{ marketId: string }>();
const { data, loading, error } = useDataProvider({
dataProvider: marketInfoProvider,
dataProvider: marketInfoWithDataProvider,
skipUpdates: true,
variables: {
marketId: marketId || '',

View File

@ -3,3 +3,4 @@ export * from './info-market';
export * from './tooltip-mapping';
export * from './__generated__/MarketInfo';
export * from './market-info-data-provider';
export * from './market-info-panels';

View File

@ -1,17 +1,5 @@
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { useEnvironment } from '@vegaprotocol/environment';
import {
totalFeesPercentage,
calcCandleVolume,
} from '@vegaprotocol/market-list';
import {
addDecimalsFormatNumber,
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
TokenLinks,
getMarketExpiryDateFormatted,
} from '@vegaprotocol/utils';
import { removePaginationWrapper, TokenLinks } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider, useYesterday } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
@ -22,15 +10,31 @@ import {
Link as UILink,
Splash,
} from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import { generatePath, Link } from 'react-router-dom';
import { MarketInfoTable } from './info-key-value-table';
import { marketInfoWithDataAndCandlesProvider } from './market-info-data-provider';
import type { MarketInfoWithDataAndCandles } from './market-info-data-provider';
import { MarketProposalNotification } from '@vegaprotocol/proposals';
import {
CurrentFeesInfoPanel,
InstrumentInfoPanel,
InsurancePoolInfoPanel,
KeyDetailsInfoPanel,
LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel,
LiquidityPriceRangeInfoPanel,
MarketPriceInfoPanel,
MarketVolumeInfoPanel,
MetadataInfoPanel,
OracleInfoPanel,
PriceMonitoringBoundsInfoPanel,
RiskFactorsInfoPanel,
RiskModelInfoPanel,
RiskParametersInfoPanel,
SettlementAssetInfoPanel,
} from './market-info-panels';
export interface InfoProps {
market: MarketInfoWithDataAndCandles;
@ -80,15 +84,6 @@ export const MarketInfoContainer = ({
export const Info = ({ market, onSelect }: InfoProps) => {
const { VEGA_TOKEN_URL, VEGA_EXPLORER_URL } = useEnvironment();
const headerClassName = 'uppercase text-lg';
const assetSymbol =
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
const quoteUnit =
market?.tradableInstrument.instrument.product?.quoteName || '';
const assetId = useMemo(
() => market?.tradableInstrument.instrument.product?.settlementAsset.id,
[market]
);
const { data: asset } = useAssetDataProvider(assetId ?? '');
if (!market) return null;
@ -96,272 +91,75 @@ export const Info = ({ market, onSelect }: InfoProps) => {
market.accountsConnection?.edges
);
const last24hourVolume = market.candles && calcCandleVolume(market.candles);
const marketDataPanels = [
{
title: t('Current fees'),
content: (
<>
<MarketInfoTable
data={{
...market.fees.factors,
totalFees: totalFeesPercentage(market.fees.factors),
}}
asPercentage={true}
/>
<p className="text-xs">
{t(
'All fees are paid by price takers and are a % of the trade notional value. Fees are not paid during auction uncrossing.'
)}
</p>
</>
),
content: <CurrentFeesInfoPanel market={market} />,
},
{
title: t('Market price'),
content: (
<>
<MarketInfoTable
data={{
markPrice: market.data?.markPrice,
bestBidPrice: market.data?.bestBidPrice,
bestOfferPrice: market.data?.bestOfferPrice,
quoteUnit: market.tradableInstrument.instrument.product.quoteName,
}}
decimalPlaces={market.decimalPlaces}
/>
<p className="text-xs mt-4">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
)}
</p>
</>
),
content: <MarketPriceInfoPanel market={market} />,
},
{
title: t('Market volume'),
content: (
<MarketInfoTable
data={{
'24hourVolume':
last24hourVolume && last24hourVolume !== '0'
? addDecimalsFormatNumber(
last24hourVolume,
market.positionDecimalPlaces
)
: '-',
openInterest: market.data?.openInterest,
bestBidVolume: market.data?.bestBidVolume,
bestOfferVolume: market.data?.bestOfferVolume,
bestStaticBidVolume: market.data?.bestStaticBidVolume,
bestStaticOfferVolume: market.data?.bestStaticOfferVolume,
}}
decimalPlaces={market.positionDecimalPlaces}
/>
),
content: <MarketVolumeInfoPanel market={market} />,
},
...marketAccounts
.filter((a) => a.type === Schema.AccountType.ACCOUNT_TYPE_INSURANCE)
.map((a) => ({
title: t(`Insurance pool`),
content: (
<MarketInfoTable
data={{
balance: a.balance,
}}
assetSymbol={assetSymbol}
decimalPlaces={
market.tradableInstrument.instrument.product.settlementAsset
.decimals
}
/>
),
content: <InsurancePoolInfoPanel market={market} account={a} />,
})),
];
const keyDetails = {
decimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
tradingMode: market.tradingMode,
state: Schema.MarketStateMapping[market.state],
};
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
const liquidityPriceRange = formatNumberPercentage(
new BigNumber(market.lpPriceRange).times(100)
);
const marketSpecPanels = [
{
title: t('Key details'),
content: (
<MarketInfoTable
data={{
name: market.tradableInstrument.instrument.name,
marketID: market.id,
tradingMode:
keyDetails.tradingMode &&
Schema.MarketTradingModeMapping[keyDetails.tradingMode],
marketDecimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
settlementAssetDecimalPlaces: assetDecimals,
}}
/>
),
content: <KeyDetailsInfoPanel market={market} />,
},
{
title: t('Instrument'),
content: (
<MarketInfoTable
data={{
marketName: market.tradableInstrument.instrument.name,
code: market.tradableInstrument.instrument.code,
productType:
market.tradableInstrument.instrument.product.__typename,
...market.tradableInstrument.instrument.product,
}}
/>
),
content: <InstrumentInfoPanel market={market} />,
},
{
title: t('Settlement asset'),
content: asset ? (
<>
<AssetDetailsTable
asset={asset}
inline={true}
noBorder={true}
dtClassName="text-black dark:text-white text-ui !px-0 !font-normal"
ddClassName="text-black dark:text-white text-ui !px-0 !font-normal max-w-full"
/>
<p className="text-xs mt-4">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
)}
</p>
</>
) : (
<Splash>{t('No data')}</Splash>
),
content: <SettlementAssetInfoPanel market={market} />,
},
{
title: t('Metadata'),
content: (
<MarketInfoTable
data={{
expiryDate: getMarketExpiryDateFormatted(
market.tradableInstrument.instrument.metadata.tags
),
...market.tradableInstrument.instrument.metadata.tags
?.map((tag) => {
const [key, value] = tag.split(':');
return { [key]: value };
})
.reduce((acc, curr) => ({ ...acc, ...curr }), {}),
}}
/>
),
content: <MetadataInfoPanel market={market} />,
},
{
title: t('Risk model'),
content: (
<MarketInfoTable
data={market.tradableInstrument.riskModel}
unformatted={true}
omits={[]}
/>
),
content: <RiskModelInfoPanel market={market} />,
},
{
title: t('Risk parameters'),
content: (
<MarketInfoTable
data={market.tradableInstrument.riskModel.params}
unformatted={true}
omits={[]}
/>
),
content: <RiskParametersInfoPanel market={market} />,
},
{
title: t('Risk factors'),
content: (
<MarketInfoTable
data={market.riskFactors}
unformatted={true}
omits={['market', '__typename']}
/>
),
content: <RiskFactorsInfoPanel market={market} />,
},
...(market.priceMonitoringSettings?.parameters?.triggers || []).map(
(trigger, i) => {
const bounds = market.data?.priceMonitoringBounds?.[i];
return {
title: t(`Price monitoring bounds ${i + 1}`),
content: (
<div className="text-xs">
<div className="grid grid-cols-2 text-xs mb-4">
<p className="col-span-1">
{t('%s probability price bounds', [
formatNumberPercentage(
new BigNumber(trigger.probability).times(100)
),
])}
</p>
<p className="col-span-1 text-right">
{t('Within %s seconds', [formatNumber(trigger.horizonSecs)])}
</p>
</div>
<div className="pl-2 pb-0 text-xs border-l-2">
{bounds && (
<MarketInfoTable
data={{
highestPrice: bounds.maxValidPrice,
lowestPrice: bounds.minValidPrice,
}}
decimalPlaces={market.decimalPlaces}
assetSymbol={quoteUnit}
/>
)}
</div>
<p className="mt-4">
{t('Results in %s seconds auction if breached', [
trigger.auctionExtensionSecs.toString(),
])}
</p>
</div>
),
};
}
(_, triggerIndex) => ({
title: t(`Price monitoring bounds ${triggerIndex + 1}`),
content: (
<PriceMonitoringBoundsInfoPanel
market={market}
triggerIndex={triggerIndex}
/>
),
})
),
{
title: t('Liquidity monitoring parameters'),
content: (
<MarketInfoTable
data={{
triggeringRatio:
market.liquidityMonitoringParameters.triggeringRatio,
...market.liquidityMonitoringParameters.targetStakeParameters,
}}
/>
),
content: <LiquidityMonitoringParametersInfoPanel market={market} />,
},
{
title: t('Liquidity'),
content: (
<MarketInfoTable
data={{
targetStake: market.data && market.data.targetStake,
suppliedStake: market.data && market.data?.suppliedStake,
marketValueProxy: market.data && market.data.marketValueProxy,
}}
decimalPlaces={assetDecimals}
assetSymbol={assetSymbol}
>
<LiquidityInfoPanel market={market}>
<Link
to={`/liquidity/${market.id}`}
onClick={() => onSelect(market.id)}
@ -369,57 +167,17 @@ export const Info = ({ market, onSelect }: InfoProps) => {
>
<UILink>{t('View liquidity provision table')}</UILink>
</Link>
</MarketInfoTable>
</LiquidityInfoPanel>
),
},
{
title: t('Liquidity price range'),
content: (
<>
<p className="text-xs mb-4">
{`For liquidity orders to count towards a commitment, they must be
within the liquidity monitoring bounds.`}
</p>
<p className="text-xs mb-4">
{`The liquidity price range is a ${liquidityPriceRange} difference from the mid
price.`}
</p>
<div className="pl-2 pb-0 text-xs border-l-2">
<MarketInfoTable
data={{
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
lowestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.minus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
highestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.plus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
}}
></MarketInfoTable>
</div>
</>
),
content: <LiquidityPriceRangeInfoPanel market={market} />,
},
{
title: t('Oracle'),
content: (
<MarketInfoTable
data={
market.tradableInstrument.instrument.product.dataSourceSpecBinding
}
>
<OracleInfoPanel market={market}>
<ExternalLink
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id}`}
>
@ -430,7 +188,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
>
{t('View termination oracle specification')}
</ExternalLink>
</MarketInfoTable>
</OracleInfoPanel>
),
},
];

View File

@ -0,0 +1,418 @@
import type { ComponentProps } from 'react';
import { useMemo } from 'react';
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import {
calcCandleVolume,
totalFeesPercentage,
} from '@vegaprotocol/market-list';
import { Splash } from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
formatNumber,
formatNumberPercentage,
getMarketExpiryDateFormatted,
} from '@vegaprotocol/utils';
import type { Get } from 'type-fest';
import { MarketInfoTable } from './info-key-value-table';
import type {
MarketInfo,
MarketInfoWithData,
MarketInfoWithDataAndCandles,
} from './market-info-data-provider';
import BigNumber from 'bignumber.js';
import { MarketTradingModeMapping } from '@vegaprotocol/types';
type PanelProps = Pick<
ComponentProps<typeof MarketInfoTable>,
'children' | 'noBorder'
>;
type MarketInfoProps = {
market: MarketInfo;
};
type MarketInfoWithDataProps = {
market: MarketInfoWithData;
};
type MarketInfoWithDataAndCandlesProps = {
market: MarketInfoWithDataAndCandles;
};
export const CurrentFeesInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<>
<MarketInfoTable
data={{
...market.fees.factors,
totalFees: totalFeesPercentage(market.fees.factors),
}}
asPercentage={true}
{...props}
/>
<p className="text-xs">
{t(
'All fees are paid by price takers and are a % of the trade notional value. Fees are not paid during auction uncrossing.'
)}
</p>
</>
);
export const MarketPriceInfoPanel = ({
market,
...props
}: MarketInfoWithDataProps & PanelProps) => {
const assetSymbol =
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
const quoteUnit =
market?.tradableInstrument.instrument.product?.quoteName || '';
return (
<>
<MarketInfoTable
data={{
markPrice: market.data?.markPrice,
bestBidPrice: market.data?.bestBidPrice,
bestOfferPrice: market.data?.bestOfferPrice,
quoteUnit: market.tradableInstrument.instrument.product.quoteName,
}}
decimalPlaces={market.decimalPlaces}
{...props}
/>
<p className="text-xs mt-4">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
)}
</p>
</>
);
};
export const MarketVolumeInfoPanel = ({
market,
...props
}: MarketInfoWithDataAndCandlesProps & PanelProps) => {
const last24hourVolume = market.candles && calcCandleVolume(market.candles);
return (
<MarketInfoTable
data={{
'24hourVolume':
last24hourVolume && last24hourVolume !== '0'
? addDecimalsFormatNumber(
last24hourVolume,
market.positionDecimalPlaces
)
: '-',
openInterest: market.data?.openInterest,
bestBidVolume: market.data?.bestBidVolume,
bestOfferVolume: market.data?.bestOfferVolume,
bestStaticBidVolume: market.data?.bestStaticBidVolume,
bestStaticOfferVolume: market.data?.bestStaticOfferVolume,
}}
decimalPlaces={market.positionDecimalPlaces}
{...props}
/>
);
};
export const InsurancePoolInfoPanel = ({
market,
account,
...props
}: {
account: NonNullable<
Get<MarketInfoWithData, 'accountsConnection.edges[0].node'>
>;
} & MarketInfoProps &
PanelProps) => {
const assetSymbol =
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
return (
<MarketInfoTable
data={{
balance: account.balance,
}}
assetSymbol={assetSymbol}
decimalPlaces={
market.tradableInstrument.instrument.product.settlementAsset.decimals
}
{...props}
/>
);
};
export const KeyDetailsInfoPanel = ({
market,
}: MarketInfoProps & PanelProps) => {
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
return (
<MarketInfoTable
data={{
name: market.tradableInstrument.instrument.name,
marketID: market.id,
tradingMode:
market.tradingMode && MarketTradingModeMapping[market.tradingMode],
marketDecimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
settlementAssetDecimalPlaces: assetDecimals,
}}
/>
);
};
export const InstrumentInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={{
marketName: market.tradableInstrument.instrument.name,
code: market.tradableInstrument.instrument.code,
productType: market.tradableInstrument.instrument.product.__typename,
...market.tradableInstrument.instrument.product,
}}
{...props}
/>
);
export const SettlementAssetInfoPanel = ({
market,
noBorder = true,
}: MarketInfoProps & PanelProps) => {
const assetSymbol =
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
const quoteUnit =
market?.tradableInstrument.instrument.product?.quoteName || '';
const assetId = useMemo(
() => market?.tradableInstrument.instrument.product?.settlementAsset.id,
[market]
);
const { data: asset } = useAssetDataProvider(assetId ?? '');
return asset ? (
<>
<AssetDetailsTable
asset={asset}
inline={true}
noBorder={noBorder}
dtClassName="text-black dark:text-white text-ui !px-0 !font-normal"
ddClassName="text-black dark:text-white text-ui !px-0 !font-normal max-w-full"
/>
<p className="text-xs mt-4">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
)}
</p>
</>
) : (
<Splash>{t('No data')}</Splash>
);
};
export const MetadataInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={{
expiryDate: getMarketExpiryDateFormatted(
market.tradableInstrument.instrument.metadata.tags
),
...market.tradableInstrument.instrument.metadata.tags
?.map((tag) => {
const [key, value] = tag.split(':');
return { [key]: value };
})
.reduce((acc, curr) => ({ ...acc, ...curr }), {}),
}}
{...props}
/>
);
export const RiskModelInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={market.tradableInstrument.riskModel}
unformatted={true}
omits={[]}
{...props}
/>
);
export const RiskParametersInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={market.tradableInstrument.riskModel.params}
unformatted={true}
omits={[]}
{...props}
/>
);
export const RiskFactorsInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={market.riskFactors}
unformatted={true}
omits={['market', '__typename']}
{...props}
/>
);
export const PriceMonitoringBoundsInfoPanel = ({
market,
triggerIndex,
...props
}: {
triggerIndex: number;
} & MarketInfoWithDataProps &
PanelProps) => {
const quoteUnit =
market?.tradableInstrument.instrument.product?.quoteName || '';
const trigger =
market.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
const bounds = market.data?.priceMonitoringBounds?.[triggerIndex];
if (!trigger) {
console.error(
`Could not find data for trigger ${triggerIndex} (market id: ${market.id})`
);
return null;
}
return (
<div className="text-xs">
<div className="grid grid-cols-2 text-xs mb-4">
<p className="col-span-1">
{t('%s probability price bounds', [
formatNumberPercentage(
new BigNumber(trigger.probability).times(100)
),
])}
</p>
<p className="col-span-1 text-right">
{t('Within %s seconds', [formatNumber(trigger.horizonSecs)])}
</p>
</div>
<div className="pl-2 pb-0 text-xs border-l-2">
{bounds && (
<MarketInfoTable
data={{
highestPrice: bounds.maxValidPrice,
lowestPrice: bounds.minValidPrice,
}}
decimalPlaces={market.decimalPlaces}
assetSymbol={quoteUnit}
{...props}
/>
)}
</div>
<p className="mt-4">
{t('Results in %s seconds auction if breached', [
trigger.auctionExtensionSecs.toString(),
])}
</p>
</div>
);
};
export const LiquidityMonitoringParametersInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={{
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
...market.liquidityMonitoringParameters.targetStakeParameters,
}}
{...props}
/>
);
export const LiquidityInfoPanel = ({
market,
...props
}: MarketInfoWithDataProps & PanelProps) => {
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
const assetSymbol =
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
return (
<MarketInfoTable
data={{
targetStake: market.data && market.data.targetStake,
suppliedStake: market.data && market.data?.suppliedStake,
marketValueProxy: market.data && market.data.marketValueProxy,
}}
decimalPlaces={assetDecimals}
assetSymbol={assetSymbol}
{...props}
/>
);
};
export const LiquidityPriceRangeInfoPanel = ({
market,
...props
}: MarketInfoWithDataProps & PanelProps) => {
const quoteUnit =
market?.tradableInstrument.instrument.product?.quoteName || '';
const liquidityPriceRange = formatNumberPercentage(
new BigNumber(market.lpPriceRange).times(100)
);
return (
<>
<p className="text-xs mb-4">
{`For liquidity orders to count towards a commitment, they must be
within the liquidity monitoring bounds.`}
</p>
<p className="text-xs mb-4">
{`The liquidity price range is a ${liquidityPriceRange} difference from the mid
price.`}
</p>
<div className="pl-2 pb-0 text-xs border-l-2">
<MarketInfoTable
data={{
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
lowestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.minus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
highestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.plus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
}}
></MarketInfoTable>
</div>
</>
);
};
export const OracleInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={market.tradableInstrument.instrument.product.dataSourceSpecBinding}
{...props}
/>
);