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:
m.ray 2022-08-23 20:43:53 +01:00 committed by GitHub
parent d9e098f942
commit f597048c43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 313 additions and 159 deletions

View File

@ -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'
);
});

View File

@ -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;
}

View 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
}
}
}
}
`;

View File

@ -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 (

View File

@ -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]

View File

@ -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);

View File

@ -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'],