feat(#994): market info - price monitoring bounds, calc 24h volume, oracle price & termination IDs etc. (#1050)
* feat: #994 add price monitoring bounds and candles update interface * fix: move best bid price to diff section * fix: add insurance pool and calc volume 24h * feat: display some oracle min info, asset id, insurance pool, move open interest and 24hVol * fix: add market-info.cy.ts case * fix: remove # from numbered price monitoring settings * fix: add insurance pool test * fix: format 24hvol * fix: remove indicativeVolume and oracleSpecBinding from market info * fix: add oracleSpecBinding back * Update libs/deal-ticket/src/components/info-market.tsx Co-authored-by: botond <105208209+notbot00@users.noreply.github.com> * fix: memo yesterday's timestamp * fix: memo timestamp on market info Co-authored-by: botond <105208209+notbot00@users.noreply.github.com>
This commit is contained in:
parent
d9e098f942
commit
f597048c43
@ -25,19 +25,13 @@ describe('market info is displayed', () => {
|
||||
|
||||
it('market volume displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Market volume').click();
|
||||
|
||||
validateMarketDataRow(0, 'Indicative Volume', '0');
|
||||
validateMarketDataRow(1, 'Best Bid Volume', '5');
|
||||
validateMarketDataRow(2, 'Best Offer Volume', '1');
|
||||
validateMarketDataRow(3, 'Best Static Bid Volume', '5');
|
||||
validateMarketDataRow(4, 'Best Static Offer Volume', '1');
|
||||
validateMarketDataRow(0, '24 Hour Volume', '-');
|
||||
validateMarketDataRow(1, 'Open Interest', '0');
|
||||
});
|
||||
|
||||
it('market price and interest displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Market price and interest').click();
|
||||
|
||||
it('market price', () => {
|
||||
cy.getByTestId(marketTitle).contains('Market price').click();
|
||||
validateMarketDataRow(0, 'Mark Price', '57.49');
|
||||
validateMarketDataRow(1, 'Open Interest', '0.00');
|
||||
});
|
||||
|
||||
it('key details displayed', () => {
|
||||
@ -67,7 +61,7 @@ describe('market info is displayed', () => {
|
||||
validateMarketDataRow(1, 'Symbol', 'tBTC');
|
||||
validateMarketDataRow(
|
||||
2,
|
||||
'ID',
|
||||
'Asset ID',
|
||||
'5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c'
|
||||
);
|
||||
});
|
||||
|
@ -3,7 +3,7 @@
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { MarketState, MarketTradingMode, AccountType } from "@vegaprotocol/types";
|
||||
import { Interval, MarketState, AccountType, MarketTradingMode, AuctionTrigger } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: MarketInfoQuery
|
||||
@ -115,6 +115,44 @@ export interface MarketInfoQuery_market_data_market {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface MarketInfoQuery_market_data_priceMonitoringBounds_trigger {
|
||||
__typename: "PriceMonitoringTrigger";
|
||||
/**
|
||||
* Price monitoring projection horizon τ in seconds (> 0).
|
||||
*/
|
||||
horizonSecs: number;
|
||||
/**
|
||||
* Price monitoring probability level p. (>0 and < 1)
|
||||
*/
|
||||
probability: number;
|
||||
/**
|
||||
* Price monitoring auction extension duration in seconds should the price
|
||||
* breach its theoretical level over the specified horizon at the specified
|
||||
* probability level (> 0)
|
||||
*/
|
||||
auctionExtensionSecs: number;
|
||||
}
|
||||
|
||||
export interface MarketInfoQuery_market_data_priceMonitoringBounds {
|
||||
__typename: "PriceMonitoringBounds";
|
||||
/**
|
||||
* Minimum price that isn't currently breaching the specified price monitoring trigger
|
||||
*/
|
||||
minValidPrice: string;
|
||||
/**
|
||||
* Maximum price that isn't currently breaching the specified price monitoring trigger
|
||||
*/
|
||||
maxValidPrice: string;
|
||||
/**
|
||||
* Price monitoring trigger associated with the bounds
|
||||
*/
|
||||
trigger: MarketInfoQuery_market_data_priceMonitoringBounds_trigger;
|
||||
/**
|
||||
* Reference price used to calculate the valid price range
|
||||
*/
|
||||
referencePrice: string;
|
||||
}
|
||||
|
||||
export interface MarketInfoQuery_market_data {
|
||||
__typename: "MarketData";
|
||||
/**
|
||||
@ -149,6 +187,22 @@ export interface MarketInfoQuery_market_data {
|
||||
* the sum of the size of all positions greater than 0.
|
||||
*/
|
||||
openInterest: string;
|
||||
/**
|
||||
* the highest price level on an order book for buy orders.
|
||||
*/
|
||||
bestBidPrice: string;
|
||||
/**
|
||||
* the lowest price level on an order book for offer orders.
|
||||
*/
|
||||
bestOfferPrice: string;
|
||||
/**
|
||||
* what triggered an auction (if an auction was started)
|
||||
*/
|
||||
trigger: AuctionTrigger;
|
||||
/**
|
||||
* a list of valid price ranges per associated trigger
|
||||
*/
|
||||
priceMonitoringBounds: MarketInfoQuery_market_data_priceMonitoringBounds[] | null;
|
||||
}
|
||||
|
||||
export interface MarketInfoQuery_market_liquidityMonitoringParameters_targetStakeParameters {
|
||||
@ -175,6 +229,14 @@ export interface MarketInfoQuery_market_liquidityMonitoringParameters {
|
||||
targetStakeParameters: MarketInfoQuery_market_liquidityMonitoringParameters_targetStakeParameters;
|
||||
}
|
||||
|
||||
export interface MarketInfoQuery_market_candles {
|
||||
__typename: "Candle";
|
||||
/**
|
||||
* Volume price (uint64)
|
||||
*/
|
||||
volume: string;
|
||||
}
|
||||
|
||||
export interface MarketInfoQuery_market_tradableInstrument_instrument_metadata {
|
||||
__typename: "InstrumentMetadata";
|
||||
/**
|
||||
@ -389,14 +451,14 @@ export interface MarketInfoQuery_market {
|
||||
* Current state of the market
|
||||
*/
|
||||
state: MarketState;
|
||||
/**
|
||||
* Current mode of execution of the market
|
||||
*/
|
||||
tradingMode: MarketTradingMode;
|
||||
/**
|
||||
* Get account for a party or market
|
||||
*/
|
||||
accounts: MarketInfoQuery_market_accounts[] | null;
|
||||
/**
|
||||
* Current mode of execution of the market
|
||||
*/
|
||||
tradingMode: MarketTradingMode;
|
||||
/**
|
||||
* Fees related data
|
||||
*/
|
||||
@ -417,6 +479,10 @@ export interface MarketInfoQuery_market {
|
||||
* Liquidity monitoring parameters for the market
|
||||
*/
|
||||
liquidityMonitoringParameters: MarketInfoQuery_market_liquidityMonitoringParameters;
|
||||
/**
|
||||
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by parameters
|
||||
*/
|
||||
candles: (MarketInfoQuery_market_candles | null)[] | null;
|
||||
/**
|
||||
* An instance of, or reference to, a tradable instrument.
|
||||
*/
|
||||
@ -436,4 +502,6 @@ export interface MarketInfoQuery {
|
||||
|
||||
export interface MarketInfoQueryVariables {
|
||||
marketId: string;
|
||||
interval: Interval;
|
||||
since: string;
|
||||
}
|
||||
|
143
libs/deal-ticket/src/components/info-market-query.ts
Normal file
143
libs/deal-ticket/src/components/info-market-query.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const MARKET_INFO_QUERY = gql`
|
||||
query MarketInfoQuery($marketId: ID!, $interval: Interval!, $since: String!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
name
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
state
|
||||
accounts {
|
||||
type
|
||||
asset {
|
||||
id
|
||||
}
|
||||
balance
|
||||
}
|
||||
tradingMode
|
||||
accounts {
|
||||
type
|
||||
asset {
|
||||
id
|
||||
}
|
||||
balance
|
||||
}
|
||||
fees {
|
||||
factors {
|
||||
makerFee
|
||||
infrastructureFee
|
||||
liquidityFee
|
||||
}
|
||||
}
|
||||
priceMonitoringSettings {
|
||||
parameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
}
|
||||
riskFactors {
|
||||
market
|
||||
short
|
||||
long
|
||||
}
|
||||
accounts {
|
||||
type
|
||||
asset {
|
||||
id
|
||||
}
|
||||
balance
|
||||
}
|
||||
data {
|
||||
market {
|
||||
id
|
||||
}
|
||||
markPrice
|
||||
indicativeVolume
|
||||
bestBidVolume
|
||||
bestOfferVolume
|
||||
bestStaticBidVolume
|
||||
bestStaticOfferVolume
|
||||
openInterest
|
||||
bestBidPrice
|
||||
bestOfferPrice
|
||||
trigger
|
||||
priceMonitoringBounds {
|
||||
minValidPrice
|
||||
maxValidPrice
|
||||
trigger {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
referencePrice
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
candles(interval: $interval, since: $since) {
|
||||
volume
|
||||
}
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
id
|
||||
name
|
||||
code
|
||||
metadata {
|
||||
tags
|
||||
}
|
||||
product {
|
||||
... on Future {
|
||||
quoteName
|
||||
settlementAsset {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
}
|
||||
oracleSpecForSettlementPrice {
|
||||
id
|
||||
}
|
||||
oracleSpecForTradingTermination {
|
||||
id
|
||||
}
|
||||
oracleSpecBinding {
|
||||
settlementPriceProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
riskModel {
|
||||
... on LogNormalRiskModel {
|
||||
tau
|
||||
riskAversionParameter
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
depth {
|
||||
lastTrade {
|
||||
price
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatLabel,
|
||||
@ -21,139 +22,36 @@ import pick from 'lodash/pick';
|
||||
import omit from 'lodash/omit';
|
||||
import type { MarketInfoQuery, MarketInfoQuery_market } from './__generated__';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { totalFees } from '@vegaprotocol/market-list';
|
||||
|
||||
const MARKET_INFO_QUERY = gql`
|
||||
query MarketInfoQuery($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
name
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
state
|
||||
tradingMode
|
||||
accounts {
|
||||
type
|
||||
asset {
|
||||
id
|
||||
}
|
||||
balance
|
||||
}
|
||||
fees {
|
||||
factors {
|
||||
makerFee
|
||||
infrastructureFee
|
||||
liquidityFee
|
||||
}
|
||||
}
|
||||
priceMonitoringSettings {
|
||||
parameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
}
|
||||
riskFactors {
|
||||
market
|
||||
short
|
||||
long
|
||||
}
|
||||
accounts {
|
||||
type
|
||||
asset {
|
||||
id
|
||||
}
|
||||
balance
|
||||
}
|
||||
data {
|
||||
market {
|
||||
id
|
||||
}
|
||||
markPrice
|
||||
indicativeVolume
|
||||
bestBidVolume
|
||||
bestOfferVolume
|
||||
bestStaticBidVolume
|
||||
bestStaticOfferVolume
|
||||
indicativeVolume
|
||||
openInterest
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
id
|
||||
name
|
||||
code
|
||||
metadata {
|
||||
tags
|
||||
}
|
||||
product {
|
||||
... on Future {
|
||||
quoteName
|
||||
settlementAsset {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
}
|
||||
oracleSpecForSettlementPrice {
|
||||
id
|
||||
}
|
||||
oracleSpecForTradingTermination {
|
||||
id
|
||||
}
|
||||
oracleSpecBinding {
|
||||
settlementPriceProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
riskModel {
|
||||
... on LogNormalRiskModel {
|
||||
tau
|
||||
riskAversionParameter
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
depth {
|
||||
lastTrade {
|
||||
price
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
import { AccountType, Interval } from '@vegaprotocol/types';
|
||||
import { MARKET_INFO_QUERY } from './info-market-query';
|
||||
|
||||
export interface InfoProps {
|
||||
market: MarketInfoQuery_market;
|
||||
}
|
||||
|
||||
export const calcCandleVolume = (
|
||||
m: MarketInfoQuery_market
|
||||
): string | undefined => {
|
||||
return m?.candles
|
||||
?.reduce((acc, c) => {
|
||||
return acc.plus(new BigNumber(c?.volume ?? 0));
|
||||
}, new BigNumber(0))
|
||||
.toString();
|
||||
};
|
||||
|
||||
export interface MarketInfoContainerProps {
|
||||
marketId: string;
|
||||
}
|
||||
export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
||||
const yTimestamp = useMemo(() => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
}, []);
|
||||
|
||||
const { data, loading, error } = useQuery(MARKET_INFO_QUERY, {
|
||||
variables: { marketId },
|
||||
variables: { marketId, interval: Interval.INTERVAL_I1H, since: yTimestamp },
|
||||
});
|
||||
|
||||
return (
|
||||
@ -174,6 +72,7 @@ export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
||||
export const Info = ({ market }: InfoProps) => {
|
||||
const headerClassName =
|
||||
'text-h5 font-medium uppercase text-black dark:text-white';
|
||||
const dayVolume = calcCandleVolume(market);
|
||||
const marketDataPanels = [
|
||||
{
|
||||
title: t('Current fees'),
|
||||
@ -195,10 +94,16 @@ export const Info = ({ market }: InfoProps) => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Market price and interest'),
|
||||
title: t('Market price'),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={pick(market.data, 'name', 'markPrice', 'openInterest')}
|
||||
data={pick(
|
||||
market.data,
|
||||
'name',
|
||||
'markPrice',
|
||||
'bestBidPrice',
|
||||
'bestOfferPrice'
|
||||
)}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
/>
|
||||
),
|
||||
@ -207,19 +112,36 @@ export const Info = ({ market }: InfoProps) => {
|
||||
title: t('Market volume'),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={pick(
|
||||
market.data,
|
||||
'name',
|
||||
'indicativeVolume',
|
||||
'bestBidVolume',
|
||||
'bestOfferVolume',
|
||||
'bestStaticBidVolume',
|
||||
'bestStaticOfferVolume'
|
||||
)}
|
||||
data={{
|
||||
'24hourVolume':
|
||||
dayVolume && dayVolume !== '0' ? formatNumber(dayVolume) : '-',
|
||||
...pick(
|
||||
market.data,
|
||||
'openInterest',
|
||||
'name',
|
||||
'bestBidVolume',
|
||||
'bestOfferVolume',
|
||||
'bestStaticBidVolume',
|
||||
'bestStaticOfferVolume'
|
||||
),
|
||||
}}
|
||||
decimalPlaces={market.positionDecimalPlaces}
|
||||
/>
|
||||
),
|
||||
},
|
||||
...(market.accounts || [])
|
||||
.filter((a) => a.type === AccountType.ACCOUNT_TYPE_INSURANCE)
|
||||
.map((a, i) => ({
|
||||
title: t(`Insurance pool`),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
balance: `${a.balance}
|
||||
${market.tradableInstrument.instrument.product?.settlementAsset.symbol}`,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
})),
|
||||
];
|
||||
|
||||
const keyDetails = pick(
|
||||
@ -270,8 +192,8 @@ export const Info = ({ market }: InfoProps) => {
|
||||
symbol:
|
||||
market.tradableInstrument.instrument.product?.settlementAsset
|
||||
.symbol,
|
||||
ID: market.tradableInstrument.instrument.product?.settlementAsset
|
||||
.id,
|
||||
assetID:
|
||||
market.tradableInstrument.instrument.product?.settlementAsset.id,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
@ -317,6 +239,12 @@ export const Info = ({ market }: InfoProps) => {
|
||||
content: <MarketInfoTable data={trigger} />,
|
||||
})
|
||||
),
|
||||
...(market.data?.priceMonitoringBounds || []).map((trigger, i) => ({
|
||||
title: t(`Price monitoring bound ${i + 1}`),
|
||||
content: (
|
||||
<MarketInfoTable data={trigger} decimalPlaces={market.decimalPlaces} />
|
||||
),
|
||||
})),
|
||||
{
|
||||
title: t('Liquidity monitoring parameters'),
|
||||
content: (
|
||||
@ -329,6 +257,22 @@ export const Info = ({ market }: InfoProps) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
...market.tradableInstrument.instrument.product.oracleSpecBinding,
|
||||
priceOracle:
|
||||
market.tradableInstrument.instrument.product
|
||||
.oracleSpecForSettlementPrice.id,
|
||||
terminationOracle:
|
||||
market.tradableInstrument.instrument.product
|
||||
.oracleSpecForTradingTermination.id,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
|
@ -17,8 +17,10 @@ export const MarketsContainer = () => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const dataRef = useRef<MarketList_markets[] | null>(null);
|
||||
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
const yTimestamp = new Date(yesterday * 1000).toISOString();
|
||||
const yTimestamp = useMemo(() => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
}, []);
|
||||
const variables = useMemo(
|
||||
() => ({ interval: Interval.INTERVAL_I1H, since: yTimestamp }),
|
||||
[yTimestamp]
|
||||
|
@ -120,8 +120,10 @@ export const SelectMarketPopover = ({
|
||||
|
||||
const { keypair } = useVegaWallet();
|
||||
const [open, setOpen] = useState(false);
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
const yTimestamp = new Date(yesterday * 1000).toISOString();
|
||||
const yTimestamp = useMemo(() => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
}, []);
|
||||
|
||||
const variables = useMemo(() => ({ partyId: keypair?.pub }), [keypair?.pub]);
|
||||
const { data } = useQuery<MarketList>(MARKET_LIST_QUERY, {
|
||||
@ -223,8 +225,10 @@ export const SelectMarketDialog = ({
|
||||
detailed?: boolean;
|
||||
onSelect: (id: string) => void;
|
||||
}) => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
const yTimestamp = new Date(yesterday * 1000).toISOString();
|
||||
const yTimestamp = useMemo(() => {
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
return new Date(yesterday * 1000).toISOString();
|
||||
}, []);
|
||||
|
||||
const onSelectMarket = (id: string) => {
|
||||
onSelect(id);
|
||||
|
@ -2,8 +2,7 @@ module.exports = {
|
||||
client: {
|
||||
service: {
|
||||
name: 'vega',
|
||||
url:
|
||||
process.env.NX_VEGA_URL || 'https://api.n11.testnet.vega.xyz/graphql',
|
||||
url: process.env.NX_VEGA_URL,
|
||||
},
|
||||
includes: ['../../{apps,libs}/**/*.{ts,tsx,js,jsx,graphql}'],
|
||||
excludes: ['**/generic-data-provider.ts'],
|
||||
|
Loading…
Reference in New Issue
Block a user