Compare commits

...
5 changed files with 167 additions and 25736 deletions
+3 -2
View File
@@ -32,6 +32,7 @@ export const CandlesChartContainer = ({
overlays,
studies,
studySizes,
showOrders,
setStudies,
setStudySizes,
setOverlays,
@@ -47,8 +48,8 @@ export const CandlesChartContainer = ({
);
const dataSource = useMemo(() => {
return new VegaDataSource(client, marketId, pubKey);
}, [client, marketId, pubKey]);
return new VegaDataSource(client, marketId, pubKey, showOrders);
}, [client, marketId, pubKey, showOrders]);
return (
<AutoSizer>
+10 -9
View File
@@ -38,21 +38,26 @@ export const CandlesMenu = () => {
chartType,
studies,
overlays,
showOrders,
setInterval,
setType,
setStudies,
setOverlays,
setShowOrders,
} = useCandlesChartSettings();
const triggerClasses = 'text-xs';
const contentAlign = 'end';
const triggerButtonProps = { size: 'extra-small' } as const;
const buttonProps = { size: 'extra-small' } as const;
return (
<>
<TradingButton {...buttonProps} onClick={() => setShowOrders()}>
{showOrders ? t('Hide orders') : t('Show orders')}
</TradingButton>
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...triggerButtonProps}>
<TradingButton {...buttonProps}>
{t(`Interval: ${intervalLabels[interval]}`)}
</TradingButton>
</TradingDropdownTrigger>
@@ -81,7 +86,7 @@ export const CandlesMenu = () => {
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...triggerButtonProps}>
<TradingButton {...buttonProps}>
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</TradingButton>
</TradingDropdownTrigger>
@@ -106,9 +111,7 @@ export const CandlesMenu = () => {
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...triggerButtonProps}>
{t('Overlays')}
</TradingButton>
<TradingButton {...buttonProps}>{t('Overlays')}</TradingButton>
</TradingDropdownTrigger>
}
>
@@ -137,9 +140,7 @@ export const CandlesMenu = () => {
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...triggerButtonProps}>
{t('Studies')}
</TradingButton>
<TradingButton {...buttonProps}>{t('Studies')}</TradingButton>
</TradingDropdownTrigger>
}
>
+144 -3
View File
@@ -6,10 +6,19 @@ import {
differenceInHours,
differenceInMinutes,
} from 'date-fns';
import type { Candle, DataSource, PriceMonitoringBounds } from 'pennant';
import type {
Candle,
DataSource,
LabelAnnotation,
PriceMonitoringBounds,
} from 'pennant';
import { Interval as PennantInterval } from 'pennant';
import { addDecimal } from '@vegaprotocol/utils';
import {
addDecimal,
addDecimalsFormatNumber,
getDateTimeFormat,
} from '@vegaprotocol/utils';
import { ChartDocument } from './__generated__/Chart';
import type { ChartQuery, ChartQueryVariables } from './__generated__/Chart';
import {
@@ -25,6 +34,15 @@ import type {
} from './__generated__/Candles';
import type { Subscription } from 'zen-observable-ts';
import * as Schema from '@vegaprotocol/types';
import { OrdersUpdateDocument } from '@vegaprotocol/orders';
import type {
OrderFieldsFragment,
OrderUpdateFieldsFragment,
OrdersUpdateSubscription,
OrdersUpdateSubscriptionVariables,
} from '@vegaprotocol/orders';
import { filterOrderUpdates } from '@vegaprotocol/orders';
const INTERVAL_TO_PENNANT_MAP = {
[PennantInterval.I1M]: Schema.Interval.INTERVAL_I1M,
@@ -47,6 +65,12 @@ const defaultConfig = {
],
};
const ACTIVE_ORDER_STATUSES = [
Schema.OrderStatus.STATUS_ACTIVE,
Schema.OrderStatus.STATUS_PARKED,
];
const ORDER_MAX = 5;
/**
* A data access object that provides access to the Vega GraphQL API.
*/
@@ -56,8 +80,11 @@ export class VegaDataSource implements DataSource {
partyId: null | string;
_decimalPlaces = 0;
_positionDecimalPlaces = 0;
orders: OrderUpdateFieldsFragment[] = [];
candlesSub: Subscription | null = null;
ordersSub: Subscription | null = null;
showOrders = false;
/**
* Indicates the number of decimal places that an integer must be shifted by in order to get a correct
@@ -84,11 +111,13 @@ export class VegaDataSource implements DataSource {
constructor(
client: ApolloClient<object>,
marketId: string,
partyId: null | string = null
partyId: null | string = null,
showOrders = false
) {
this.client = client;
this.marketId = marketId;
this.partyId = partyId;
this.showOrders = showOrders;
}
/**
@@ -166,6 +195,7 @@ export class VegaDataSource implements DataSource {
},
fetchPolicy: 'no-cache',
});
if (data?.market?.candlesConnection?.edges) {
const decimalPlaces = data.market.decimalPlaces;
const positionDecimalPlaces = data.market.positionDecimalPlaces;
@@ -223,6 +253,65 @@ export class VegaDataSource implements DataSource {
unsubscribeData() {
this.candlesSub && this.candlesSub.unsubscribe();
}
async subscribeAnnotations(
onSubscriptionAnnotations: (annotations: LabelAnnotation[]) => void
) {
if (this.partyId === null) {
return;
}
if (!this.showOrders) {
return;
}
const sub = this.client.subscribe<
OrdersUpdateSubscription,
OrdersUpdateSubscriptionVariables
>({
query: OrdersUpdateDocument,
variables: {
marketIds: [this.marketId],
partyId: this.partyId,
},
});
// subscription will return a snap shot of curernt orders immediately
// and then subsequent messages will be sent for updates
this.ordersSub = sub.subscribe(({ data }) => {
if (!data?.orders?.length) return;
const orders = filterOrderUpdates(data.orders);
for (const order of orders) {
const index = this.orders.findIndex((o) => o.id === order.id) ?? -1;
const isActive = ACTIVE_ORDER_STATUSES.includes(order.status);
if (index !== -1) {
if (isActive) {
this.orders[index] = order;
} else {
this.orders.splice(index, 1);
}
} else {
this.orders.unshift(order);
if (this.orders.length > ORDER_MAX) {
this.orders.pop();
}
}
}
onSubscriptionAnnotations(
this.orders.map((o) =>
createOrderLabel(o, this.decimalPlaces, this.positionDecimalPlaces)
)
);
});
}
unsubscribeAnnotations(): void {
this.ordersSub && this.ordersSub.unsubscribe();
}
}
const getDuration = (
@@ -318,3 +407,55 @@ function parseCandle(
volume: Number(addDecimal(candle.volume, positionDecimalPlaces)),
};
}
function createOrderLabel(
order: OrderFieldsFragment | OrderUpdateFieldsFragment,
decimalPlaces: number,
positionDecimalPlaces: number
): LabelAnnotation {
const type = order.type ? Schema.OrderTypeMapping[order.type] : '-';
const price = addDecimalsFormatNumber(order.price, decimalPlaces);
const prefix = order ? (order.side === Schema.Side.SIDE_BUY ? '+' : '-') : '';
const size =
prefix + addDecimalsFormatNumber(order.size, positionDecimalPlaces);
let timeInForce = '-';
if (
order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT &&
order?.expiresAt
) {
const expiry = getDateTimeFormat().format(new Date(order.expiresAt));
timeInForce = `${
Schema.OrderTimeInForceCode[order.timeInForce]
}: ${expiry}`;
} else {
timeInForce = order.timeInForce
? Schema.OrderTimeInForceCode[order.timeInForce]
: '';
}
return {
type: 'label',
id: order.id,
cells: [
{ label: type },
{
label: timeInForce,
},
{
label: price,
numeric: true,
},
{
label: size,
numeric: true,
stroke: true,
},
],
intent: order.side === Schema.Side.SIDE_BUY ? 'success' : 'danger',
y: Number(addDecimal(order.price, decimalPlaces)),
};
}
@@ -13,6 +13,7 @@ interface StoredSettings {
overlays: Overlay[];
studies: Study[];
studySizes: StudySizes;
showOrders: boolean;
}
export const STUDY_SIZE = 90;
@@ -30,6 +31,7 @@ const DEFAULT_CHART_SETTINGS = {
overlays: [Overlay.MOVING_AVERAGE],
studies: [Study.MACD, Study.VOLUME],
studySizes: {},
showOrders: true,
};
export const useCandlesChartSettingsStore = create<
@@ -39,6 +41,7 @@ export const useCandlesChartSettingsStore = create<
setOverlays: (overlays?: Overlay[]) => void;
setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void;
setShowOrders: (show?: boolean) => void;
}
>()(
persist(
@@ -81,6 +84,11 @@ export const useCandlesChartSettingsStore = create<
});
});
},
setShowOrders: (show) => {
set((state) => {
state.showOrders = show === undefined ? !state.showOrders : show;
});
},
})),
{
name: 'vega_candles_chart_store',
@@ -127,10 +135,12 @@ export const useCandlesChartSettings = () => {
overlays,
studies,
studySizes,
showOrders: settings.showOrders,
setInterval: settings.setInterval,
setType: settings.setType,
setStudies: settings.setStudies,
setOverlays: settings.setOverlays,
setStudySizes: settings.setStudySizes,
setShowOrders: settings.setShowOrders,
};
};
-25722
View File
File diff suppressed because it is too large Load Diff