Bugfix/orderbook data cummulative vol (#1833)

* chore: move market depth queries to .graphql file

* chore: fix orderbook data updates

* chore: allow to overlap subscription update with stored data in market depth
This commit is contained in:
Bartłomiej Głownia 2022-10-24 10:26:44 +02:00 committed by GitHub
parent d15af74ba5
commit 6dded8c2d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 81 deletions

View File

@ -1,6 +1,8 @@
import { makeDataProvider } from '@vegaprotocol/react-helpers';
import { updateLevels } from './orderbook-data';
import type { Update } from '@vegaprotocol/react-helpers';
import { captureException } from '@sentry/react';
import {
MarketDepthDocument,
MarketDepthUpdateDocument,
@ -23,18 +25,24 @@ const update: Update<
if (delta.marketId !== data.id) {
continue;
}
/*
const sequenceNumber = Number(delta.sequenceNumber);
if (sequenceNumber <= sequenceNumbers[delta.marketId]) {
const currentSequenceNumber = Number(delta.sequenceNumber);
if (currentSequenceNumber <= sequenceNumbers[delta.marketId]) {
return data;
}
if (sequenceNumber - 1 !== sequenceNumbers[delta.marketId]) {
sequenceNumbers[delta.marketId] = 0;
if (
delta.previousSequenceNumber !==
sequenceNumbers[delta.marketId].toString()
) {
captureException(
new Error(
`Sequence number gap in marketsDepthUpdate for {data.id}, {sequenceNumbers[delta.marketId]} - {delta.previousSequenceNumber}`
)
);
delete sequenceNumbers[delta.marketId];
reload();
return;
}
sequenceNumbers[delta.marketId] = sequenceNumber;
*/
sequenceNumbers[delta.marketId] = Number(currentSequenceNumber);
const updatedData = {
...data,
depth: { ...data.depth },

View File

@ -1,5 +1,7 @@
import groupBy from 'lodash/groupBy';
import uniqBy from 'lodash/uniqBy';
import reverse from 'lodash/reverse';
import cloneDeep from 'lodash/cloneDeep';
import { VolumeType } from '@vegaprotocol/react-helpers';
import { MarketTradingMode } from '@vegaprotocol/types';
import type { MarketData } from '@vegaprotocol/market-list';
@ -56,7 +58,7 @@ const toPercentValue = (value?: number) => Math.ceil((value ?? 0) * 100);
const updateRelativeData = (data: OrderbookRowData[]) => {
const { bid, ask, cumulativeVol } = getMaxVolumes(data);
const maxBidAsk = Math.max(bid, ask);
data.forEach((data) => {
data.forEach((data, i) => {
data.relativeAsk = toPercentValue(data.ask / maxBidAsk);
data.relativeBid = toPercentValue(data.bid / maxBidAsk);
data.cumulativeVol.relativeAsk = toPercentValue(
@ -68,6 +70,20 @@ const updateRelativeData = (data: OrderbookRowData[]) => {
});
};
const updateCumulativeVolume = (data: OrderbookRowData[]) => {
if (data.length > 1) {
const maxIndex = data.length - 1;
for (let i = 0; i <= maxIndex; i++) {
data[i].cumulativeVol.bid =
data[i].bid + (i !== 0 ? data[i - 1].cumulativeVol.bid : 0);
}
for (let i = maxIndex; i >= 0; i--) {
data[i].cumulativeVol.ask =
data[i].ask + (i !== maxIndex ? data[i + 1].cumulativeVol.ask : 0);
}
}
};
export const createPartialRow = (
price: string,
volume = 0,
@ -157,6 +173,7 @@ export const compactRows = (
(i !== maxIndex ? orderbookData[i + 1].cumulativeVol.ask : 0);
}
}
updateCumulativeVolume(orderbookData);
// count relative volumes
updateRelativeData(orderbookData);
return orderbookData;
@ -175,45 +192,26 @@ const partiallyUpdateCompactedRows = (
dataType: VolumeType,
data: OrderbookRowData[],
delta: PriceLevelFieldsFragment,
resolution: number,
modifiedIndex: number
): [number, OrderbookRowData[]] => {
resolution: number
) => {
const { price } = delta;
const volume = Number(delta.volume);
const priceLevel = getPriceLevel(price, resolution);
const isAskDataType = dataType === VolumeType.ask;
const volKey = isAskDataType ? 'ask' : 'bid';
const oppositeVolKey = isAskDataType ? 'bid' : 'ask';
const volByLevelKey = isAskDataType ? 'askByLevel' : 'bidByLevel';
const resolveModifiedIndex = isAskDataType ? Math.max : Math.min;
let index = data.findIndex((row) => row.price === priceLevel);
if (index !== -1) {
modifiedIndex = resolveModifiedIndex(modifiedIndex, index);
data[index] = {
...data[index],
[volKey]:
data[index][volKey] - (data[index][volByLevelKey][price] || 0) + volume,
[volByLevelKey]: {
...data[index][volByLevelKey],
[price]: volume,
},
};
return [modifiedIndex, [...data]];
data[index][volKey] =
data[index][volKey] - (data[index][volByLevelKey][price] || 0) + volume;
data[index][volByLevelKey][price] = volume;
} else {
const newData: OrderbookRowData = createRow(priceLevel, volume, dataType);
index = data.findIndex((row) => BigInt(row.price) < BigInt(priceLevel));
if (index !== -1) {
newData.cumulativeVol[oppositeVolKey] =
data[index + (isAskDataType ? 0 : 1)]?.cumulativeVol[oppositeVolKey] ??
0;
modifiedIndex = resolveModifiedIndex(modifiedIndex, index);
return [
modifiedIndex,
[...data.slice(0, index), newData, ...data.slice(index)],
];
data.splice(index, 0, newData);
} else {
modifiedIndex = data.length - 1;
return [modifiedIndex, [...data, newData]];
data.push(newData);
}
}
};
@ -228,57 +226,19 @@ const partiallyUpdateCompactedRows = (
* @returns void
*/
export const updateCompactedRows = (
rows: OrderbookRowData[],
sell: PriceLevelFieldsFragment[] | null,
buy: PriceLevelFieldsFragment[] | null,
rows: Readonly<OrderbookRowData[]>,
sell: Readonly<PriceLevelFieldsFragment[]> | null,
buy: Readonly<PriceLevelFieldsFragment[]> | null,
resolution: number
) => {
let sellModifiedIndex = -1;
let data = [...rows];
uniqBy(sell?.reverse(), 'price')?.forEach((delta) => {
[sellModifiedIndex, data] = partiallyUpdateCompactedRows(
VolumeType.ask,
data,
delta,
resolution,
sellModifiedIndex
);
const data = cloneDeep(rows as OrderbookRowData[]);
uniqBy(reverse(sell || []), 'price')?.forEach((delta) => {
partiallyUpdateCompactedRows(VolumeType.ask, data, delta, resolution);
});
let buyModifiedIndex = data.length;
uniqBy(buy?.reverse(), 'price')?.forEach((delta) => {
[buyModifiedIndex, data] = partiallyUpdateCompactedRows(
VolumeType.bid,
data,
delta,
resolution,
buyModifiedIndex
);
uniqBy(reverse(buy || []), 'price')?.forEach((delta) => {
partiallyUpdateCompactedRows(VolumeType.bid, data, delta, resolution);
});
// update cumulative ask only below highest modified price level
if (sellModifiedIndex !== -1) {
for (let i = Math.min(sellModifiedIndex, data.length - 2); i >= 0; i--) {
data[i] = {
...data[i],
cumulativeVol: {
...data[i].cumulativeVol,
ask: data[i + 1].cumulativeVol.ask + data[i].ask,
},
};
}
}
// update cumulative bid only above lowest modified price level
if (buyModifiedIndex !== data.length) {
for (let i = Math.max(buyModifiedIndex, 1), l = data.length; i < l; i++) {
data[i] = {
...data[i],
cumulativeVol: {
...data[i].cumulativeVol,
bid: data[i - 1].cumulativeVol.bid + data[i].bid,
},
};
}
}
updateCumulativeVolume(data);
let index = 0;
// remove levels that do not have any volume
while (index < data.length) {