Compare commits

..
9 changed files with 25853 additions and 176 deletions
@@ -7,7 +7,7 @@ import {
NetworkParams,
} from '@vegaprotocol/network-parameters';
import { useMarketList } from '@vegaprotocol/markets';
import { formatNumber } from '@vegaprotocol/utils';
import { formatNumber, formatNumberRounded } from '@vegaprotocol/utils';
import { useDiscountProgramsQuery, useFeesQuery } from './__generated__/Fees';
import { FeeCard } from './fees-card';
import { MarketFees } from './market-fees';
@@ -284,7 +284,7 @@ export const CurrentVolume = ({
return (
<div>
<Stat
value={formatNumber(windowLengthVolume)}
value={formatNumberRounded(new BigNumber(windowLengthVolume))}
text={t('Past %s epochs', epochs.toString())}
/>
{requiredForNextTier > 0 && (
@@ -0,0 +1,74 @@
import BigNumber from 'bignumber.js';
import { getAdjustedFee } from './utils';
describe('getAdjustedFee', () => {
it('simple', () => {
const volumeDiscount = 0.5;
const referralDiscount = 0.5;
const infraFee = 0.1;
const makerFee = 0.1;
const liqFee = 0.1;
const fees = [
new BigNumber(infraFee),
new BigNumber(makerFee),
new BigNumber(liqFee),
];
const discounts = [
new BigNumber(volumeDiscount),
new BigNumber(referralDiscount),
];
// 1 - 0.5 - 0.5
const v = new BigNumber(1).minus(new BigNumber(volumeDiscount));
// 1 - 0.5 = 0.5
const r = new BigNumber(1).minus(new BigNumber(referralDiscount));
// 0.5 * 0.5 = 0.25
// 1 - 0.25 = 0.75
const factor = new BigNumber(1).minus(v.times(r));
// 0.1 + 0.1 + 0.1 = 0.3
const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0));
// 0.3 * 0.75 = 0.225
const expected = new BigNumber(totalFees).times(factor).toNumber();
expect(getAdjustedFee(fees, discounts)).toBe(expected);
});
it('combines discount factors multiplicativly', () => {
const volumeDiscount = 0.4;
const referralDiscount = 0.1;
const infraFee = 0.0005;
const makerFee = 0.0002;
const liqFee = 0.01;
const fees = [
new BigNumber(infraFee),
new BigNumber(makerFee),
new BigNumber(liqFee),
];
const discounts = [
new BigNumber(volumeDiscount),
new BigNumber(referralDiscount),
];
// formula for calculating adjusted fees
const v = new BigNumber(1).minus(new BigNumber(volumeDiscount));
const r = new BigNumber(1).minus(new BigNumber(referralDiscount));
const factor = new BigNumber(1).minus(v.times(r));
// summed fees
const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0));
const expected = new BigNumber(totalFees).times(factor).toNumber();
expect(getAdjustedFee(fees, discounts)).toBe(expected);
});
});
@@ -88,14 +88,18 @@ export const getReferralBenefitTier = (
/**
* Given a set of fees and a set of discounts return
* the adjusted fee factor
*
* Formula for calculating the adjusted fees
* total_discount_factor = 1 - (1 - volumeDiscount) * (1 - referralDiscount)
*/
export const getAdjustedFee = (fees: BigNumber[], discounts: BigNumber[]) => {
const totalFee = fees.reduce((sum, f) => sum.plus(f), new BigNumber(0));
const totalDiscount = discounts.reduce(
(sum, d) => sum.plus(d),
new BigNumber(0)
);
return totalFee
.times(BigNumber.max(0, new BigNumber(1).minus(totalDiscount)))
.toNumber();
const combinedFactors = discounts.reduce((acc, d) => {
return acc.times(new BigNumber(1).minus(d));
}, new BigNumber(1));
const totalFactor = new BigNumber(1).minus(combinedFactors);
return totalFee.times(BigNumber.max(0, totalFactor)).toNumber();
};
+2 -3
View File
@@ -32,7 +32,6 @@ export const CandlesChartContainer = ({
overlays,
studies,
studySizes,
showOrders,
setStudies,
setStudySizes,
setOverlays,
@@ -48,8 +47,8 @@ export const CandlesChartContainer = ({
);
const dataSource = useMemo(() => {
return new VegaDataSource(client, marketId, pubKey, showOrders);
}, [client, marketId, pubKey, showOrders]);
return new VegaDataSource(client, marketId, pubKey);
}, [client, marketId, pubKey]);
return (
<AutoSizer>
+9 -10
View File
@@ -38,26 +38,21 @@ export const CandlesMenu = () => {
chartType,
studies,
overlays,
showOrders,
setInterval,
setType,
setStudies,
setOverlays,
setShowOrders,
} = useCandlesChartSettings();
const triggerClasses = 'text-xs';
const contentAlign = 'end';
const buttonProps = { size: 'extra-small' } as const;
const triggerButtonProps = { size: 'extra-small' } as const;
return (
<>
<TradingButton {...buttonProps} onClick={() => setShowOrders()}>
{showOrders ? t('Hide orders') : t('Show orders')}
</TradingButton>
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...buttonProps}>
<TradingButton {...triggerButtonProps}>
{t(`Interval: ${intervalLabels[interval]}`)}
</TradingButton>
</TradingDropdownTrigger>
@@ -86,7 +81,7 @@ export const CandlesMenu = () => {
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...buttonProps}>
<TradingButton {...triggerButtonProps}>
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</TradingButton>
</TradingDropdownTrigger>
@@ -111,7 +106,9 @@ export const CandlesMenu = () => {
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...buttonProps}>{t('Overlays')}</TradingButton>
<TradingButton {...triggerButtonProps}>
{t('Overlays')}
</TradingButton>
</TradingDropdownTrigger>
}
>
@@ -140,7 +137,9 @@ export const CandlesMenu = () => {
<TradingDropdown
trigger={
<TradingDropdownTrigger className={triggerClasses}>
<TradingButton {...buttonProps}>{t('Studies')}</TradingButton>
<TradingButton {...triggerButtonProps}>
{t('Studies')}
</TradingButton>
</TradingDropdownTrigger>
}
>
+3 -144
View File
@@ -6,19 +6,10 @@ import {
differenceInHours,
differenceInMinutes,
} from 'date-fns';
import type {
Candle,
DataSource,
LabelAnnotation,
PriceMonitoringBounds,
} from 'pennant';
import type { Candle, DataSource, PriceMonitoringBounds } from 'pennant';
import { Interval as PennantInterval } from 'pennant';
import {
addDecimal,
addDecimalsFormatNumber,
getDateTimeFormat,
} from '@vegaprotocol/utils';
import { addDecimal } from '@vegaprotocol/utils';
import { ChartDocument } from './__generated__/Chart';
import type { ChartQuery, ChartQueryVariables } from './__generated__/Chart';
import {
@@ -34,15 +25,6 @@ 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,
@@ -65,12 +47,6 @@ 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.
*/
@@ -80,11 +56,8 @@ 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
@@ -111,13 +84,11 @@ export class VegaDataSource implements DataSource {
constructor(
client: ApolloClient<object>,
marketId: string,
partyId: null | string = null,
showOrders = false
partyId: null | string = null
) {
this.client = client;
this.marketId = marketId;
this.partyId = partyId;
this.showOrders = showOrders;
}
/**
@@ -195,7 +166,6 @@ export class VegaDataSource implements DataSource {
},
fetchPolicy: 'no-cache',
});
if (data?.market?.candlesConnection?.edges) {
const decimalPlaces = data.market.decimalPlaces;
const positionDecimalPlaces = data.market.positionDecimalPlaces;
@@ -253,65 +223,6 @@ 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 = (
@@ -407,55 +318,3 @@ 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,7 +13,6 @@ interface StoredSettings {
overlays: Overlay[];
studies: Study[];
studySizes: StudySizes;
showOrders: boolean;
}
export const STUDY_SIZE = 90;
@@ -31,7 +30,6 @@ const DEFAULT_CHART_SETTINGS = {
overlays: [Overlay.MOVING_AVERAGE],
studies: [Study.MACD, Study.VOLUME],
studySizes: {},
showOrders: true,
};
export const useCandlesChartSettingsStore = create<
@@ -41,7 +39,6 @@ export const useCandlesChartSettingsStore = create<
setOverlays: (overlays?: Overlay[]) => void;
setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void;
setShowOrders: (show?: boolean) => void;
}
>()(
persist(
@@ -84,11 +81,6 @@ export const useCandlesChartSettingsStore = create<
});
});
},
setShowOrders: (show) => {
set((state) => {
state.showOrders = show === undefined ? !state.showOrders : show;
});
},
})),
{
name: 'vega_candles_chart_store',
@@ -135,12 +127,10 @@ 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,
};
};
+30
View File
@@ -179,3 +179,33 @@ export const toNumberParts = (
export const isNumeric = (
value?: string | number | BigNumber | bigint | null
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
/**
* Format a number greater than 1 million with m for million, b for billion
* and t for trillion
*/
export const formatNumberRounded = (num: BigNumber) => {
let value = '';
const format = (divisor: string) => {
const result = num.dividedBy(divisor);
return result.isInteger() ? result.toString() : result.toFixed(1);
};
if (num.isGreaterThan(new BigNumber('1e14'))) {
value = '>100t';
} else if (num.isGreaterThanOrEqualTo(new BigNumber('1e12'))) {
// Trillion
value = `${format('1e12')}t`;
} else if (num.isGreaterThanOrEqualTo(new BigNumber('1e9'))) {
// Billion
value = `${format('1e9')}b`;
} else if (num.isGreaterThanOrEqualTo(new BigNumber('1e6'))) {
// Million
value = `${format('1e6')}m`;
} else {
value = formatNumber(num);
}
return value;
};
+25722
View File
File diff suppressed because it is too large Load Diff