Compare commits

...
57 changed files with 486 additions and 478 deletions
+28
View File
@@ -227,3 +227,31 @@ jobs:
name: worker-logs
path: ./logs/
retention-days: 15
#----------------------------------------------
# ----- upload market-sim logs -----
#----------------------------------------------
- name: Find Directory
id: find-dir
run: |
DIR=$(find /tmp -type d -name "vega-sim-*" -print -quit)
if [[ -d "$DIR" ]]; then
echo "Found directory: $DIR"
echo "DIR=$DIR" >> $GITHUB_ENV
else
echo "Directory not found."
exit 1
fi
- name: Compress Files
if: env.DIR
run: |
tar -czvf ${{ github.workspace }}/market-sim-logs.tar.gz -C "$DIR" .
echo "Compressed files at ${{ github.workspace }}/market-sim-logs.tar.gz"
- name: Upload Compressed market-sim-logs
uses: actions/upload-artifact@v3
if: env.DIR
with:
name: market-sim-logs
path: ${{ github.workspace }}/market-sim-logs.tar.gz
retention-days: 15
@@ -44,7 +44,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
cy.getByTestId('icon-cross').click();
});
it.skip('Proposal page displayed on mobile', function () {
it('Proposal page displayed on mobile', function () {
const proposalTitle = 'Add Lorem Ipsum market';
cy.common_switch_to_mobile_and_click_toggle();
@@ -55,7 +55,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
});
});
it('Able to view new asset proposal', function () {
it.skip('Able to view new asset proposal', function () {
const proposalTitle = 'Test new asset proposal';
const newAssetProposalBody = getNewAssetTxBody();
cy.VegaWalletSubmitProposal(newAssetProposalBody);
@@ -196,6 +196,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
timeWindow: '3600',
scalingFactor: 10,
},
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: '0.7',
auctionExtension: '1',
},
@@ -334,6 +335,7 @@ export function createSuccessorMarketProposalTxBody(
timeWindow: '3600',
scalingFactor: 10,
},
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: '0.7',
auctionExtension: '1',
},
@@ -205,7 +205,6 @@ query Proposal(
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
@@ -366,7 +365,6 @@ query Proposal(
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
File diff suppressed because one or more lines are too long
+3
View File
@@ -28,3 +28,6 @@ NX_REFERRALS=true
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
@@ -314,7 +314,7 @@ describe('Closed', () => {
});
it('display market actions', async () => {
// Use market with a succcessor Id as the actions dropdown will optionally
// Use market with a successor Id as the actions dropdown will optionally
// show a link to the successor market
const marketsWithSuccessorAndParent = [
{
@@ -137,6 +137,8 @@ const ClosedMarketsDataGrid = ({
headerName: t('Market'),
field: 'code',
cellRenderer: 'MarketCodeCell',
width: 150,
resizable: true,
},
{
headerName: t('Status'),
@@ -280,6 +282,7 @@ const ClosedMarketsDataGrid = ({
return (
<AgGrid
rowData={rowData}
defaultColDef={COL_DEFS.default}
columnDefs={colDefs}
getRowId={({ data }) => data.id}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
@@ -17,6 +17,7 @@ const defaultColDef = {
filter: true,
resizable: true,
filterParams: { buttons: ['reset'] },
minWidth: 120,
};
const components = {
@@ -53,6 +53,7 @@ export const MarketsPage = () => {
size="extra-small"
data-testid="propose-new-market"
href={externalLink}
target="_blank"
>
{t('Propose a new market')}
</TradingAnchorButton>
@@ -29,6 +29,7 @@ export const useColumnDefs = () => {
{
headerName: t('Market'),
field: 'tradableInstrument.instrument.code',
pinned: true,
cellRenderer: ({
value,
data,
@@ -42,13 +42,9 @@ export const LiquidityHeader = () => {
const assetDecimalPlaces = asset?.decimals || 0;
const symbol = asset?.symbol;
const triggeringRatio =
market?.liquidityMonitoringParameters.triggeringRatio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
const feesObject = feesPaidRes?.paidLiquidityFees?.edges?.find(
@@ -47,9 +47,6 @@ export const MarketLiquiditySupplied = ({
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
const triggeringRatio = Number(
params.market_liquidity_targetstake_triggering_ratio
);
const variables = useMemo(
() => ({
@@ -94,7 +91,6 @@ export const MarketLiquiditySupplied = ({
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: market?.suppliedStake || 0,
targetStake: market?.targetStake || 0,
triggeringRatio,
});
const showMessage =
@@ -131,7 +131,7 @@ def test_terminated_market_no_settlement_date(page: Page, vega: VegaServiceNull)
row_selector = page.locator(
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row'
).first
expect(row_selector.locator('[col-id="state"]')).to_have_text("Trading Terminated")
expect(row_selector.locator('[col-id="state"]')).to_have_text("No trading")
expect(row_selector.locator('[col-id="settlementDate"]')).to_have_text("Unknown")
# TODO Create test for terminated market with settlement date in future
@@ -174,9 +174,9 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
vega.wait_fn(1)
vega.wait_for_total_catchup()
# market state should be changed to "Trading Terminated" because of the invalid oracle
# market state should be changed to "No trading" because of the invalid oracle
expect(trading_mode).to_have_text("No trading")
expect(market_state).to_have_text("Trading Terminated")
expect(market_state).to_have_text("No trading")
# settle market
vega.submit_termination_and_settlement_data(
+5 -2
View File
@@ -67,6 +67,7 @@ const defaultColDef = {
sortable: true,
tooltipComponent: TooltipCellComponent,
comparator: accountValuesComparator,
minWidth: 150,
};
export interface GetRowsParams extends Omit<IGetRowsParams, 'successCallback'> {
successCallback(rowsThisBlock: AccountFields[], lastRow?: number): void;
@@ -139,6 +140,8 @@ export const AccountTable = ({
{
headerName: t('Asset'),
field: 'asset.symbol',
pinned: true,
minWidth: 75,
headerTooltip: t(
'Asset is the collateral that is deposited into the Vega protocol.'
),
@@ -253,8 +256,8 @@ export const AccountTable = ({
colId: 'accounts-actions',
field: 'asset.id',
...COL_DEFS.actions,
minWidth: showDepositButton ? 105 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 105 : COL_DEFS.actions.maxWidth,
minWidth: showDepositButton ? 110 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 110 : COL_DEFS.actions.maxWidth,
cellRenderer: ({
value: assetId,
node,
+6 -3
View File
@@ -23,7 +23,9 @@ import { AccountType } from '@vegaprotocol/types';
const defaultColDef = {
resizable: true,
sortable: true,
minWidth: 100,
};
interface BreakdownTableProps extends AgGridReactProps {
data: AccountFields[] | null;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
@@ -32,12 +34,13 @@ interface BreakdownTableProps extends AgGridReactProps {
const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
({ data }, ref) => {
const t = useT();
const coldefs = useMemo(() => {
const colDefs = useMemo(() => {
const defs: ColDef[] = [
{
headerName: t('Market'),
field: 'market.tradableInstrument.instrument.code',
minWidth: 200,
width: 90,
pinned: true,
sort: 'desc',
cellRenderer: ({
value,
@@ -141,7 +144,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
components={{ PriceCell, ProgressBarCell }}
tooltipShowDelay={500}
defaultColDef={defaultColDef}
columnDefs={coldefs}
columnDefs={colDefs}
domLayout="autoHeight"
/>
);
@@ -15,7 +15,6 @@ export async function proposeMarket(publicKey: string) {
log('sending proposal tx');
const proposalTx = createNewMarketProposal();
const result = await sendVegaTx(publicKey, proposalTx);
return result.result;
}
@@ -119,6 +118,7 @@ function createNewMarketProposal(): ProposalSubmissionBody {
timeWindow: '3600',
scalingFactor: 10,
},
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: '0.7',
auctionExtension: '1',
},
@@ -9,4 +9,7 @@ export const COL_DEFS = {
type: 'rightAligned',
pinned: 'right' as const,
},
default: {
minWidth: 100,
},
};
@@ -329,8 +329,9 @@ export const DealTicket = ({
const marketTradingModeError = validateMarketTradingMode(
marketTradingMode,
t('Trading terminated')
t('No trading')
);
if (marketTradingModeError !== true) {
return {
message: marketTradingModeError,
-4
View File
@@ -11,9 +11,6 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
positionDecimalPlaces: 1,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
state: Schema.MarketState.STATE_ACTIVE,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '',
@@ -75,7 +72,6 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
__typename: 'Instrument',
},
},
fees: {
factors: {
makerFee: '0.001',
+9 -3
View File
@@ -6,7 +6,7 @@ import {
isNumeric,
} from '@vegaprotocol/utils';
import { type ColDef } from 'ag-grid-community';
import { AgGrid } from '@vegaprotocol/datagrid';
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import {
type VegaICellRendererParams,
type VegaValueFormatterParams,
@@ -21,7 +21,7 @@ export const DepositsTable = (
) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{ headerName: 'Asset', field: 'asset.symbol' },
{ headerName: 'Asset', field: 'asset.symbol', pinned: true },
{
headerName: 'Amount',
field: 'amount',
@@ -74,5 +74,11 @@ export const DepositsTable = (
],
[]
);
return <AgGrid columnDefs={columnDefs} {...props} />;
return (
<AgGrid
columnDefs={columnDefs}
defaultColDef={COL_DEFS.default}
{...props}
/>
);
};
+2
View File
@@ -48,6 +48,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
pinned: true,
},
{
headerName: t('Size'),
@@ -143,6 +144,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
<AgGrid
ref={ref}
columnDefs={columnDefs}
defaultColDef={COL_DEFS.default}
overlayNoRowsTemplate={t('No fills')}
getRowId={({ data }) => data?.id}
tooltipShowDelay={0}
-3
View File
@@ -54,9 +54,6 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
decimalPlaces: 5,
state: MarketState.STATE_ACTIVE,
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
fees: {
__typename: 'Fees',
factors: {
@@ -22,9 +22,7 @@ export const generateFundingPayment = (
decimalPlaces: 5,
state: MarketState.STATE_ACTIVE,
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
fees: {
__typename: 'Fees',
factors: {
+1 -1
View File
@@ -116,7 +116,7 @@
"Total fees": "Total fees",
"Total margin available": "Total margin available",
"TOTAL_MARGIN_AVAILABLE": "Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).",
"Trading terminated": "Trading terminated",
"No trading": "No trading",
"Trailing percent offset cannot be higher than 99.9": "Trailing percent offset cannot be higher than 99.9",
"Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}": "Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}",
"Trailing percentage offset": "Trailing percentage offset",
@@ -124,27 +124,11 @@ describe('getChange', () => {
});
describe('useCheckLiquidityStatus', () => {
it('should return amber if liquidity is enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '60',
targetStake: '100',
triggeringRatio: '0.5',
})
);
expect(result.current).toEqual({
status: Intent.Warning,
percentage: new BigNumber('60'),
});
});
it('should return red if liquidity is not enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '60',
targetStake: '100',
triggeringRatio: '1',
})
);
@@ -159,7 +143,6 @@ describe('useCheckLiquidityStatus', () => {
useCheckLiquidityStatus({
suppliedStake: '101',
targetStake: '100',
triggeringRatio: '1',
})
);
@@ -121,11 +121,9 @@ export const getTargetStake = (
export const useCheckLiquidityStatus = ({
suppliedStake,
targetStake,
triggeringRatio,
}: {
suppliedStake: string | number;
targetStake: string | number;
triggeringRatio: string | number;
}): {
status: Intent;
percentage: BigNumber;
@@ -142,23 +140,12 @@ export const useCheckLiquidityStatus = ({
percentage,
};
}
if (new BigNumber(suppliedStake).gte(new BigNumber(targetStake))) {
if (new BigNumber(suppliedStake).gte(targetStake)) {
// show a green status, e.g. "🟢 $13,666,999 liquidity supplied"
return {
status: Intent.Success,
percentage,
};
// ELSE IF supplied_stake > NETPARAM[market.liquidity.targetstake.triggering.ratio] * target_stake THEN
} else if (
new BigNumber(suppliedStake).gte(
new BigNumber(targetStake).multipliedBy(triggeringRatio)
)
) {
// show an amber status, e.g. "🟠 $3,456,123 liquidity supplied"
return {
status: Intent.Warning,
percentage,
};
// ELSE show a red status, e.g. "🔴 $600,002 liquidity supplied"
} else {
return {
File diff suppressed because one or more lines are too long
@@ -188,7 +188,6 @@ query MarketInfo($marketId: ID!) {
long
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
File diff suppressed because one or more lines are too long
@@ -745,7 +745,6 @@ export const LiquidityMonitoringParametersInfoPanel = ({
parentMarket,
}: MarketInfoProps) => {
const marketData = {
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
timeWindow:
market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,
scalingFactor:
@@ -754,8 +753,6 @@ export const LiquidityMonitoringParametersInfoPanel = ({
const parentMarketData = parentMarket
? {
triggeringRatio:
parentMarket.liquidityMonitoringParameters.triggeringRatio,
timeWindow:
parentMarket.liquidityMonitoringParameters.targetStakeParameters
.timeWindow,
@@ -95,7 +95,6 @@ export const marketInfoQuery = (
long: '0.008508132993273576',
},
liquidityMonitoringParameters: {
triggeringRatio: '0.7',
targetStakeParameters: {
timeWindow: 3600,
scalingFactor: 10,
@@ -101,8 +101,6 @@ export const useTooltipMapping: () => Record<string, ReactNode> = () => {
auctionExtensionSecs: t(
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
),
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
timeWindow: t('The length of time over which open interest is measured.'),
scalingFactor: t(
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
-3
View File
@@ -35,9 +35,6 @@ fragment MarketFields on Market {
open
close
}
liquidityMonitoringParameters {
triggeringRatio
}
}
query Markets {
-3
View File
@@ -53,9 +53,6 @@ export const createMarketFragment = (
liquidityFee: '',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
tradableInstrument: {
instrument: {
id: '',
@@ -20,9 +20,6 @@ export const generateOrder = (partialOrder?: PartialDeep<Order>) => {
makerFee: '0.1',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '',
@@ -21,9 +21,6 @@ export const generateStopOrder = (
__typename: 'Market',
id: 'market-id',
decimalPlaces: 1,
liquidityMonitoringParameters: {
triggeringRatio: '0.7',
},
fees: {
__typename: 'Fees',
factors: {
@@ -39,6 +39,7 @@ const defaultColDef = {
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
minWidth: 100,
};
export type OrderListTableProps = TypedDataAgGrid<Order> & {
@@ -82,6 +83,9 @@ export const OrderListTable = memo<
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
pinned: true,
width: 130,
resizable: true,
},
{
headerName: t('Filled'),
@@ -31,9 +31,6 @@ describe('OrderViewDialog', () => {
liquidityFee: '0.001',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
@@ -40,6 +40,7 @@ const defaultColDef = {
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
minWidth: 100,
};
export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
@@ -61,6 +62,7 @@ export const StopOrdersTable = memo(
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
pinned: true,
},
{
headerName: t('Trigger'),
+326 -343
View File
@@ -71,6 +71,7 @@ const defaultColDef = {
filterParams: { buttons: ['reset'] },
tooltipComponent: TooltipCellComponent,
resizable: true,
minWidth: 110,
};
export const PositionsTable = ({
@@ -83,6 +84,330 @@ export const PositionsTable = ({
...props
}: Props) => {
const t = useT();
const colDefs = useMemo<ColDef[]>(() => {
const columnDefs: (ColDef | null)[] = [
multipleKeys
? {
headerName: t('Vega key'),
field: 'partyId',
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
(data?.partyId &&
pubKeys &&
pubKeys.find((key) => key.publicKey === data.partyId)?.name) ||
data?.partyId,
}
: null,
{
headerName: t('Market'),
field: 'marketCode',
resizable: true,
onCellClicked: ({ data }) => {
if (!onMarketClick) return;
onMarketClick(data.marketId);
},
pinned: true,
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Position, 'marketCode'>) => {
if (!data || !value) return '-';
return (
<StackedCell
primary={value}
secondary={
<>
{data?.assetSymbol}
<MarketProductPill productType={data.productType} />
</>
}
/>
);
},
},
{
headerName: t('Size / Notional'),
field: 'openVolume',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellClassRules: signedNumberCssClassRules,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: { data: Position }) => {
return data?.openVolume === undefined
? undefined
: toBigNum(data?.openVolume, data.positionDecimalPlaces).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
if (!data?.openVolume) return '-';
const vol = volumePrefix(
addDecimalsFormatNumber(data.openVolume, data.positionDecimalPlaces)
);
return vol;
},
cellRenderer: OpenVolumeCell,
},
{
headerName: t('Entry / Mark'),
field: 'markPrice',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellRenderer: ({
data,
}: VegaICellRendererParams<Position, 'markPrice'>) => {
if (
!data?.averageEntryPrice ||
!data?.markPrice ||
!data?.marketDecimalPlaces
) {
return <>-</>;
}
if (
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return <>-</>;
}
const entry = addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
const mark = addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
return (
<StackedCell
primary={entry}
secondary={
<PriceFlashCell
value={Number(data.markPrice)}
valueFormatted={mark}
/>
}
/>
);
},
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
},
},
{
headerName: t('Margin / Leverage'),
colId: 'margin',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(
data.marginAccountBalance,
data.assetDecimals
).toNumber();
},
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (
!data ||
!data.marginAccountBalance ||
!data.marketDecimalPlaces
) {
return null;
}
const margin = addDecimalsFormatNumberQuantum(
data.marginAccountBalance,
data.assetDecimals,
data.quantum
);
const lev = data?.currentLeverage ? data.currentLeverage : 1;
const leverage = formatNumber(Math.max(1, lev), 1);
return <StackedCell primary={margin} secondary={leverage + 'x'} />;
},
},
{
colId: 'liquidationPrice',
headerName: 'Liquidation',
headerTooltip: t('Worst case liquidation price'),
cellClass: 'font-mono text-right',
type: 'rightAligned',
// Cannot be sortable as data is fetched within the cell
sortable: false,
filter: false,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) {
return '-';
}
return (
<div className="flex h-[45px] items-center">
<LiquidationPrice
className="block text-right grow"
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.marketDecimalPlaces}
/>
</div>
);
},
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: realisedPNLValueGetter,
cellRenderer: (
args: VegaICellRendererParams<Position, 'realisedPNL'>
) => {
const LOSS_SOCIALIZATION_LINK = DocsLinks?.LOSS_SOCIALIZATION ?? '';
if (!args.data || args.value === undefined) {
return null;
}
const losses = parseInt(args.data?.lossSocializationAmount ?? '0');
if (losses <= 0) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return (
<Tooltip description={args.valueFormatted} align="end">
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
}
const lossesFormatted = addDecimalsFormatNumber(
args.data.lossSocializationAmount,
args.data.assetDecimals
);
return (
<Tooltip
align="end"
description={
<>
<p className="mb-2">
{t('Realised PNL: {{value}}', {
nsSeparator: '*',
replace: { value: args.value },
})}
</p>
<p className="mb-2">
{t('Lifetime loss socialisation deductions: {{losses}}', {
nsSeparator: '*',
replace: {
losses: lossesFormatted,
},
})}
</p>
<p className="mb-2">
{t(
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
{ assetSymbol: args.data.assetSymbol }
)}
</p>
{LOSS_SOCIALIZATION_LINK && (
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
{t('Read more about loss socialisation')}
</ExternalLink>
)}
</>
}
>
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumberQuantum(
data.realisedPNL,
data.assetDecimals,
data.quantum
);
},
headerTooltip: t(
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
),
},
{
headerName: t('Unrealised PNL'),
field: 'unrealisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: unrealisedPNLValueGetter,
// @ts-ignore no type overlap but function can be identical
tooltipValueGetter: unrealisedPNLValueGetter,
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumberQuantum(
data.unrealisedPNL,
data.assetDecimals,
data.quantum
),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
},
onClose && !isReadOnly
? {
...COL_DEFS.actions,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
return (
<div className="flex items-center justify-end gap-2">
{data?.openVolume &&
data?.openVolume !== '0' &&
data.partyId === pubKey ? (
<ButtonLink
data-testid="close-position"
onClick={() => data && onClose(data)}
title={t('Close position')}
>
<VegaIcon name={VegaIconNames.CROSS} size={16} />
</ButtonLink>
) : null}
{data?.assetId && (
<PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 55,
maxWidth: 55,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [isReadOnly, multipleKeys, onClose, onMarketClick, pubKey, pubKeys, t]);
return (
<AgGrid
overlayNoRowsTemplate={t('No positions')}
@@ -95,349 +420,7 @@ export const PositionsTable = ({
MarketNameCell,
}}
rowHeight={45}
columnDefs={useMemo<ColDef[]>(() => {
const columnDefs: (ColDef | null)[] = [
multipleKeys
? {
headerName: t('Vega key'),
field: 'partyId',
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
(data?.partyId &&
pubKeys &&
pubKeys.find((key) => key.publicKey === data.partyId)
?.name) ||
data?.partyId,
}
: null,
{
headerName: t('Market'),
field: 'marketCode',
onCellClicked: ({ data }) => {
if (!onMarketClick) return;
onMarketClick(data.marketId);
},
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Position, 'marketCode'>) => {
if (!data || !value) return '-';
return (
<StackedCell
primary={value}
secondary={
<>
{data?.assetSymbol}
<MarketProductPill productType={data.productType} />
</>
}
/>
);
},
},
{
headerName: t('Size / Notional'),
field: 'openVolume',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellClassRules: signedNumberCssClassRules,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: { data: Position }) => {
return data?.openVolume === undefined
? undefined
: toBigNum(
data?.openVolume,
data.positionDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
if (!data?.openVolume) return '-';
const vol = volumePrefix(
addDecimalsFormatNumber(
data.openVolume,
data.positionDecimalPlaces
)
);
return vol;
},
cellRenderer: OpenVolumeCell,
},
{
headerName: t('Entry / Mark'),
field: 'markPrice',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellRenderer: ({
data,
}: VegaICellRendererParams<Position, 'markPrice'>) => {
if (
!data?.averageEntryPrice ||
!data?.markPrice ||
!data?.marketDecimalPlaces
) {
return <>-</>;
}
if (
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return <>-</>;
}
const entry = addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
const mark = addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
return (
<StackedCell
primary={entry}
secondary={
<PriceFlashCell
value={Number(data.markPrice)}
valueFormatted={mark}
/>
}
/>
);
},
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
},
},
{
headerName: t('Margin / Leverage'),
colId: 'margin',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(
data.marginAccountBalance,
data.assetDecimals
).toNumber();
},
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (
!data ||
!data.marginAccountBalance ||
!data.marketDecimalPlaces
) {
return null;
}
const margin = addDecimalsFormatNumberQuantum(
data.marginAccountBalance,
data.assetDecimals,
data.quantum
);
const lev = data?.currentLeverage ? data.currentLeverage : 1;
const leverage = formatNumber(Math.max(1, lev), 1);
return (
<StackedCell primary={margin} secondary={leverage + 'x'} />
);
},
},
{
colId: 'liquidationPrice',
headerName: 'Liquidation',
headerTooltip: t('Worst case liquidation price'),
cellClass: 'font-mono text-right',
type: 'rightAligned',
// Cannot be sortable as data is fetched within the cell
sortable: false,
filter: false,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) {
return '-';
}
return (
<div className="flex h-[45px] items-center">
<LiquidationPrice
className="block text-right grow"
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.marketDecimalPlaces}
/>
</div>
);
},
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: realisedPNLValueGetter,
cellRenderer: (
args: VegaICellRendererParams<Position, 'realisedPNL'>
) => {
const LOSS_SOCIALIZATION_LINK =
DocsLinks?.LOSS_SOCIALIZATION ?? '';
if (!args.data || args.value === undefined) {
return null;
}
const losses = parseInt(
args.data?.lossSocializationAmount ?? '0'
);
if (losses <= 0) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return (
<Tooltip description={args.valueFormatted} align="end">
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
}
const lossesFormatted = addDecimalsFormatNumber(
args.data.lossSocializationAmount,
args.data.assetDecimals
);
return (
<Tooltip
align="end"
description={
<>
<p className="mb-2">
{t('Realised PNL: {{value}}', {
nsSeparator: '*',
replace: { value: args.value },
})}
</p>
<p className="mb-2">
{t(
'Lifetime loss socialisation deductions: {{losses}}',
{
nsSeparator: '*',
replace: {
losses: lossesFormatted,
},
}
)}
</p>
<p className="mb-2">
{t(
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
{ assetSymbol: args.data.assetSymbol }
)}
</p>
{LOSS_SOCIALIZATION_LINK && (
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
{t('Read more about loss socialisation')}
</ExternalLink>
)}
</>
}
>
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumberQuantum(
data.realisedPNL,
data.assetDecimals,
data.quantum
);
},
headerTooltip: t(
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
),
},
{
headerName: t('Unrealised PNL'),
field: 'unrealisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: unrealisedPNLValueGetter,
// @ts-ignore no type overlap but function can be identical
tooltipValueGetter: unrealisedPNLValueGetter,
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumberQuantum(
data.unrealisedPNL,
data.assetDecimals,
data.quantum
),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
},
onClose && !isReadOnly
? {
...COL_DEFS.actions,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
return (
<div className="flex items-center justify-end gap-2">
{data?.openVolume &&
data?.openVolume !== '0' &&
data.partyId === pubKey ? (
<ButtonLink
data-testid="close-position"
onClick={() => data && onClose(data)}
title={t('Close position')}
>
<VegaIcon name={VegaIconNames.CROSS} size={16} />
</ButtonLink>
) : null}
{data?.assetId && (
<PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 55,
maxWidth: 55,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [
isReadOnly,
multipleKeys,
onClose,
onMarketClick,
pubKey,
pubKeys,
t,
])}
columnDefs={colDefs}
{...props}
/>
);
@@ -29,6 +29,7 @@ export const useColumnDefs = () => {
colId: 'market',
headerName: t('Market'),
field: 'terms.change.instrument.code',
pinned: true,
cellStyle: { lineHeight: '14px' },
cellRenderer: ({
value,
@@ -134,7 +134,6 @@ fragment NewMarketFields on NewMarket {
# timeWindow
# scalingFactor
# }
# triggeringRatio
# auctionExtensionSecs
# }
# linearSlippageFactor
@@ -299,7 +298,6 @@ fragment UpdateMarketFields on UpdateMarket {
timeWindow
scalingFactor
}
triggeringRatio
# auctionExtensionSecs
}
riskParameters {
File diff suppressed because one or more lines are too long
@@ -92,7 +92,6 @@ export const marketUpdateProposal: ProposalListFieldsFragment = {
triggers: [],
},
liquidityMonitoringParameters: {
triggeringRatio: '0',
targetStakeParameters: {
scalingFactor: 0,
timeWindow: 0,
@@ -162,7 +162,6 @@ const generateUpdateMarketProposal = (
__typename: liquidityMonitoring
? 'LiquidityMonitoringParameters'
: undefined,
triggeringRatio: '0',
targetStakeParameters: {
__typename: undefined,
scalingFactor: 0,
+2
View File
@@ -5585,6 +5585,8 @@ export enum StopOrderRejectionReason {
REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED = 'REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED',
/** Stop orders submission must be reduce only */
REJECTION_REASON_MUST_BE_REDUCE_ONLY = 'REJECTION_REASON_MUST_BE_REDUCE_ONLY',
/** Stop orders are not allowed during the opening auction */
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION = 'REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION',
/** Stop orders are not allowed without a position */
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION = 'REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION',
/** This stop order does not close the position */
@@ -85,19 +85,21 @@ export function Dialog({
<VegaIcon name={VegaIconNames.CROSS} size={24} />
</DialogPrimitives.Close>
)}
<div className="flex gap-4 max-w-full">
{icon && <div className="fill-current">{icon}</div>}
<div data-testid="dialog-content" className="flex-1 max-w-full">
{title && (
<h1
className="text-xl uppercase mb-4 pr-2"
data-testid="dialog-title"
>
{title}
</h1>
)}
<div>{children}</div>
</div>
<div data-testid="dialog-content" className="flex-1 max-w-full">
{title && (
<span
className="text-xl uppercase flex gap-4"
data-testid="dialog-title"
>
{icon && (
<span className="fill-current flex items-center">
{icon}
</span>
)}
{title}
</span>
)}
<div>{children}</div>
</div>
</div>
</div>
@@ -5,15 +5,16 @@ import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
import { useCallback } from 'react';
import { Intent } from '../../utils/intent';
import { useT } from '../../use-t';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
export const ToastPositionSetter = () => {
const t = useT();
const setPostion = useToastsConfiguration((store) => store.setPosition);
const setPosition = useToastsConfiguration((store) => store.setPosition);
const position = useToastsConfiguration((store) => store.position);
const setToast = useToasts((store) => store.setToast);
const handleChange = useCallback(
(position: ToastPosition) => {
setPostion(position);
setPosition(position);
setToast({
id: 'test-toast',
intent: Intent.Primary,
@@ -21,11 +22,49 @@ export const ToastPositionSetter = () => {
onClose: () => useToasts.getState().remove('test-toast'),
});
},
[setToast, setPostion, t]
[setToast, setPosition, t]
);
const buttonCssClasses =
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';
const activeIcon = 'fill-vega-clight-900 dark:fill-vega-cdark-900';
const { screenSize } = useScreenDimensions();
const isMobileScreen = screenSize === 'xs';
if (isMobileScreen) {
return (
<div className="flex justify-between">
<div className={classNames('grid grid-cols-1 grid-rows-2 gap-1')}>
<button
className={buttonCssClasses}
onClick={() => handleChange(ToastPosition.TopCenter)}
>
<Icon
className={classNames(
position === ToastPosition.TopCenter && activeIcon
)}
size={3}
name={IconNames.ARROW_UP}
/>
</button>
<button
className={buttonCssClasses}
onClick={() => handleChange(ToastPosition.BottomCenter)}
>
<Icon
className={classNames(
position === ToastPosition.BottomCenter && activeIcon
)}
size={3}
name={IconNames.ARROW_DOWN}
/>
</button>
</div>
</div>
);
}
return (
<div className="flex justify-between">
<div className={classNames('grid grid-cols-3 grid-rows-2 gap-1')}>
@@ -92,7 +92,7 @@ export const ToastsContainer = ({
className={classNames(
'absolute right-0 top-[-38px] z-20 w-full',
'transition-opacity',
'opacity-0 hover:!opacity-100 group-hover:opacity-50',
'sm:opacity-0 sm:hover:!opacity-100 sm:group-hover:opacity-50',
{
hidden: validToasts.length === 0,
}
@@ -149,6 +149,7 @@ interface ProposalNewMarketTerms {
timeWindow: string;
scalingFactor: number;
};
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: string;
auctionExtension: string;
};
@@ -34,7 +34,7 @@ export class ViewConnector implements VegaConnector {
});
return Promise.resolve([
{
name: 'View only pubkey',
name: 'View only',
publicKey: this.pubkey,
},
]);
@@ -149,9 +149,6 @@ describe('WithdrawFormContainer', () => {
liquidityFee: '0.001',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '0.7',
},
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
+3 -1
View File
@@ -15,6 +15,7 @@ import {
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import {
COL_DEFS,
type TypedDataAgGrid,
type VegaICellRendererParams,
type VegaValueFormatterParams,
@@ -46,7 +47,7 @@ export const WithdrawalsTable = ({
const columnDefs = useMemo<ColDef[]>(
() => [
{ headerName: t('Asset'), field: 'asset.symbol' },
{ headerName: t('Asset'), field: 'asset.symbol', pinned: true },
{
headerName: t('Amount'),
field: 'amount',
@@ -135,6 +136,7 @@ export const WithdrawalsTable = ({
<AgGrid
overlayNoRowsTemplate={t('No withdrawals')}
columnDefs={columnDefs}
defaultColDef={COL_DEFS.default}
components={{
RecipientCell,
StatusCell,
+1 -1
View File
@@ -38,7 +38,7 @@
"@radix-ui/react-slider": "^1.1.0",
"@radix-ui/react-switch": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@sentry/nextjs": "^6.19.3",
"@sentry/react": "^6.19.2",
"@sentry/tracing": "^6.19.2",
+1 -1
View File
@@ -4864,7 +4864,7 @@
"@radix-ui/react-separator" "1.0.3"
"@radix-ui/react-toggle-group" "1.0.4"
"@radix-ui/react-tooltip@^1.0.3":
"@radix-ui/react-tooltip@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==