feat: generate queries add period params
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"ignorePatterns": ["!**/*", "__generated__"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
fragment Bar on Candle {
|
||||
periodStart
|
||||
lastUpdateInPeriod
|
||||
high
|
||||
low
|
||||
open
|
||||
close
|
||||
volume
|
||||
}
|
||||
|
||||
query GetBars(
|
||||
$marketId: ID!
|
||||
$interval: Interval!
|
||||
$since: String!
|
||||
$to: String
|
||||
) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
candlesConnection(
|
||||
interval: $interval
|
||||
since: $since
|
||||
to: $to
|
||||
pagination: { last: 5000 }
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...Bar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription LastBar($marketId: ID!, $interval: Interval!) {
|
||||
candles(marketId: $marketId, interval: $interval) {
|
||||
...Bar
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
query Symbol($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
name
|
||||
product {
|
||||
... on Future {
|
||||
__typename
|
||||
}
|
||||
... on Perpetual {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type BarFragment = { __typename?: 'Candle', periodStart: any, lastUpdateInPeriod: any, high: string, low: string, open: string, close: string, volume: string };
|
||||
|
||||
export type GetBarsQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
interval: Types.Interval;
|
||||
since: Types.Scalars['String'];
|
||||
to?: Types.InputMaybe<Types.Scalars['String']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type GetBarsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', periodStart: any, lastUpdateInPeriod: any, high: string, low: string, open: string, close: string, volume: string } } | null> | null } | null } | null };
|
||||
|
||||
export type LastBarSubscriptionVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
interval: Types.Interval;
|
||||
}>;
|
||||
|
||||
|
||||
export type LastBarSubscription = { __typename?: 'Subscription', candles: { __typename?: 'Candle', periodStart: any, lastUpdateInPeriod: any, high: string, low: string, open: string, close: string, volume: string } };
|
||||
|
||||
export const BarFragmentDoc = gql`
|
||||
fragment Bar on Candle {
|
||||
periodStart
|
||||
lastUpdateInPeriod
|
||||
high
|
||||
low
|
||||
open
|
||||
close
|
||||
volume
|
||||
}
|
||||
`;
|
||||
export const GetBarsDocument = gql`
|
||||
query GetBars($marketId: ID!, $interval: Interval!, $since: String!, $to: String) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
candlesConnection(
|
||||
interval: $interval
|
||||
since: $since
|
||||
to: $to
|
||||
pagination: {last: 5000}
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...Bar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${BarFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetBarsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetBarsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetBarsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetBarsQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* interval: // value for 'interval'
|
||||
* since: // value for 'since'
|
||||
* to: // value for 'to'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetBarsQuery(baseOptions: Apollo.QueryHookOptions<GetBarsQuery, GetBarsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetBarsQuery, GetBarsQueryVariables>(GetBarsDocument, options);
|
||||
}
|
||||
export function useGetBarsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetBarsQuery, GetBarsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetBarsQuery, GetBarsQueryVariables>(GetBarsDocument, options);
|
||||
}
|
||||
export type GetBarsQueryHookResult = ReturnType<typeof useGetBarsQuery>;
|
||||
export type GetBarsLazyQueryHookResult = ReturnType<typeof useGetBarsLazyQuery>;
|
||||
export type GetBarsQueryResult = Apollo.QueryResult<GetBarsQuery, GetBarsQueryVariables>;
|
||||
export const LastBarDocument = gql`
|
||||
subscription LastBar($marketId: ID!, $interval: Interval!) {
|
||||
candles(marketId: $marketId, interval: $interval) {
|
||||
...Bar
|
||||
}
|
||||
}
|
||||
${BarFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useLastBarSubscription__
|
||||
*
|
||||
* To run a query within a React component, call `useLastBarSubscription` and pass it any options that fit your needs.
|
||||
* When your component renders, `useLastBarSubscription` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useLastBarSubscription({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* interval: // value for 'interval'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useLastBarSubscription(baseOptions: Apollo.SubscriptionHookOptions<LastBarSubscription, LastBarSubscriptionVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSubscription<LastBarSubscription, LastBarSubscriptionVariables>(LastBarDocument, options);
|
||||
}
|
||||
export type LastBarSubscriptionHookResult = ReturnType<typeof useLastBarSubscription>;
|
||||
export type LastBarSubscriptionResult = Apollo.SubscriptionResult<LastBarSubscription>;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type SymbolQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type SymbolQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string, name: string, product: { __typename: 'Future' } | { __typename: 'Perpetual' } | { __typename?: 'Spot' } } } } | null };
|
||||
|
||||
|
||||
export const SymbolDocument = gql`
|
||||
query Symbol($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
name
|
||||
product {
|
||||
... on Future {
|
||||
__typename
|
||||
}
|
||||
... on Perpetual {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useSymbolQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useSymbolQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useSymbolQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useSymbolQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useSymbolQuery(baseOptions: Apollo.QueryHookOptions<SymbolQuery, SymbolQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<SymbolQuery, SymbolQueryVariables>(SymbolDocument, options);
|
||||
}
|
||||
export function useSymbolLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<SymbolQuery, SymbolQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<SymbolQuery, SymbolQueryVariables>(SymbolDocument, options);
|
||||
}
|
||||
export type SymbolQueryHookResult = ReturnType<typeof useSymbolQuery>;
|
||||
export type SymbolLazyQueryHookResult = ReturnType<typeof useSymbolLazyQuery>;
|
||||
export type SymbolQueryResult = Apollo.QueryResult<SymbolQuery, SymbolQueryVariables>;
|
||||
@@ -1,214 +0,0 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
type LibrarySymbolInfo,
|
||||
type IBasicDataFeed,
|
||||
type ResolutionString,
|
||||
type SeriesFormat,
|
||||
} from '../charting_library/charting_library';
|
||||
|
||||
type Candle = {
|
||||
start: string;
|
||||
lastUpdate: string;
|
||||
high: string;
|
||||
low: string;
|
||||
open: string;
|
||||
close: string;
|
||||
volume: string;
|
||||
notional: string;
|
||||
};
|
||||
|
||||
/* eslint-disable no-console */
|
||||
const url = 'https://api.n07.testnet.vega.xyz/api/v2';
|
||||
const websocketUrl = 'wss://api.n07.testnet.vega.xyz/api/v2/stream/candle/data';
|
||||
let socket: WebSocket;
|
||||
|
||||
const configurationData = {
|
||||
// only showing Vega ofc
|
||||
exchanges: undefined,
|
||||
|
||||
// Represents the resolutions for bars supported by your datafeed
|
||||
supported_resolutions: ['1'] as ResolutionString[],
|
||||
} as const;
|
||||
|
||||
const resolutionMap: Record<ResolutionString, (marketId: string) => string> = {
|
||||
// @ts-ignore something something
|
||||
'1': (marketId: string) => `trades_candle_1_minute_${marketId}`,
|
||||
};
|
||||
|
||||
export const useDataFeed = (marketId: string) => {
|
||||
const datafeed = useMemo(() => {
|
||||
const feed: IBasicDataFeed = {
|
||||
onReady: (callback) => {
|
||||
console.log('[onReady]: Method call');
|
||||
setTimeout(() => callback(configurationData));
|
||||
},
|
||||
searchSymbols: () => {
|
||||
console.log('[searchSymbols]: Method call');
|
||||
},
|
||||
resolveSymbol: async (
|
||||
marketId,
|
||||
onSymbolResolvedCallback,
|
||||
onResolveErrorCallback,
|
||||
) => {
|
||||
console.log('[resolveSymbol]: Method call', marketId);
|
||||
try {
|
||||
const data = await request('market/' + marketId);
|
||||
|
||||
const symbolInfo: LibrarySymbolInfo = {
|
||||
ticker: data.market.id,
|
||||
name: data.market.tradableInstrument.instrument.code,
|
||||
full_name: data.market.tradableInstrument.instrument.code,
|
||||
listed_exchange: 'vega',
|
||||
format: 'price' as SeriesFormat,
|
||||
description: data.market.tradableInstrument.instrument.name,
|
||||
type: 'futures',
|
||||
session: '24x7',
|
||||
timezone: 'Etc/UTC',
|
||||
exchange: '',
|
||||
minmov: 1,
|
||||
pricescale: 100,
|
||||
visible_plots_set: 'ohlc',
|
||||
supported_resolutions: ['1'] as ResolutionString[],
|
||||
volume_precision: data.market.positionDecimalPlaces,
|
||||
data_status: 'pulsed',
|
||||
delay: 1000,
|
||||
has_intraday: true, // required for less than 1 day interval
|
||||
};
|
||||
|
||||
onSymbolResolvedCallback(symbolInfo);
|
||||
} catch (err) {
|
||||
onResolveErrorCallback('Cannot resolve symbol');
|
||||
}
|
||||
},
|
||||
getBars: async (
|
||||
symbolInfo,
|
||||
resolution,
|
||||
periodParams,
|
||||
onHistoryCallback,
|
||||
onErrorCallback,
|
||||
) => {
|
||||
console.log('HERE', resolution, symbolInfo);
|
||||
|
||||
if (!symbolInfo.ticker) {
|
||||
onErrorCallback('No symbol.ticker');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const candleId = resolutionMap[resolution](symbolInfo.ticker);
|
||||
console.log(candleId);
|
||||
const data = await request(`candle?candleId=${candleId}`);
|
||||
|
||||
if (!data.candles.edges.length) {
|
||||
onHistoryCallback([], { noData: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const bars = data.candles.edges
|
||||
.map((e: { node: Candle }) => {
|
||||
return prepareBar(e.node);
|
||||
})
|
||||
.reverse();
|
||||
|
||||
console.log(bars);
|
||||
|
||||
onHistoryCallback(bars, { noData: false });
|
||||
} catch (err) {
|
||||
console.log('Cannot getBars', err);
|
||||
onErrorCallback(err as Error);
|
||||
}
|
||||
},
|
||||
|
||||
subscribeBars: (
|
||||
symbolInfo,
|
||||
resolution,
|
||||
onTick,
|
||||
subscriberUID,
|
||||
onResetCacheNeededCallback,
|
||||
) => {
|
||||
console.log(
|
||||
'[subscribeBars]: Method call with subscriberUID:',
|
||||
subscriberUID,
|
||||
);
|
||||
|
||||
if (!symbolInfo.ticker) {
|
||||
console.error('No symbolInfo.ticker');
|
||||
return;
|
||||
}
|
||||
|
||||
const candleId = resolutionMap[resolution](symbolInfo.ticker);
|
||||
|
||||
socket = new WebSocket(`${websocketUrl}?candleId=${candleId}`);
|
||||
|
||||
socket.onopen = (event) => {
|
||||
console.log('open', event);
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
console.log('message', event);
|
||||
try {
|
||||
// TODO: update or append to candle set
|
||||
const data = JSON.parse(event.data);
|
||||
const bar = prepareBar(data.result.candle);
|
||||
onTick(bar);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = (event) => {
|
||||
console.log('error', event);
|
||||
};
|
||||
|
||||
socket.onclose = (event) => {
|
||||
console.log('close', event);
|
||||
};
|
||||
},
|
||||
|
||||
unsubscribeBars: (subscriberUID) => {
|
||||
console.log(
|
||||
'[unsubscribeBars]: Method call with subscriberUID:',
|
||||
subscriberUID,
|
||||
);
|
||||
|
||||
if (socket) {
|
||||
socket.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return feed;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (socket) {
|
||||
socket.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return datafeed;
|
||||
};
|
||||
|
||||
const prepareBar = (candle: Candle) => {
|
||||
return {
|
||||
time: Number(candle.start.slice(0, -6)), // trim to milliseconds
|
||||
low: Number(candle.low),
|
||||
high: Number(candle.high),
|
||||
open: Number(candle.open),
|
||||
close: Number(candle.close),
|
||||
volume: Number(candle.volume),
|
||||
};
|
||||
};
|
||||
|
||||
const request = async (path: string) => {
|
||||
try {
|
||||
const res = await fetch(`${url}/${path}`);
|
||||
const json = await res.json();
|
||||
return json;
|
||||
console.log(json);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
@@ -6,14 +6,14 @@ import {
|
||||
type ResolutionString,
|
||||
widget,
|
||||
} from '../charting_library';
|
||||
import { useDataFeed } from './datafeed';
|
||||
import { useDatafeed } from './use-datafeed';
|
||||
|
||||
export const TradingView = ({ marketId }: { marketId: string }) => {
|
||||
const { theme } = useThemeSwitcher();
|
||||
const chartContainerRef =
|
||||
useRef<HTMLDivElement>() as React.MutableRefObject<HTMLInputElement>;
|
||||
|
||||
const datafeed = useDataFeed(marketId);
|
||||
const datafeed = useDatafeed();
|
||||
|
||||
useEffect(() => {
|
||||
const widgetOptions: ChartingLibraryWidgetOptions = {
|
||||
@@ -43,26 +43,6 @@ export const TradingView = ({ marketId }: { marketId: string }) => {
|
||||
|
||||
const tvWidget = new widget(widgetOptions);
|
||||
|
||||
// Add a custom button
|
||||
tvWidget.onChartReady(() => {
|
||||
tvWidget.headerReady().then(() => {
|
||||
const button = tvWidget.createButton();
|
||||
button.setAttribute('title', 'Click to show a notification popup');
|
||||
button.classList.add('apply-common-tooltip');
|
||||
button.addEventListener('click', () =>
|
||||
tvWidget.showNoticeDialog({
|
||||
title: 'Notification',
|
||||
body: 'TradingView Charting Library API works correctly',
|
||||
callback: () => {
|
||||
console.log('Noticed!');
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
button.innerHTML = 'Check API';
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
tvWidget.remove();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import compact from 'lodash/compact';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { type Subscription } from 'zen-observable-ts';
|
||||
import {
|
||||
type LibrarySymbolInfo,
|
||||
type IBasicDataFeed,
|
||||
type ResolutionString,
|
||||
type SeriesFormat,
|
||||
} from '../charting_library/charting_library';
|
||||
import {
|
||||
GetBarsDocument,
|
||||
LastBarDocument,
|
||||
type BarFragment,
|
||||
type GetBarsQuery,
|
||||
type GetBarsQueryVariables,
|
||||
type LastBarSubscription,
|
||||
type LastBarSubscriptionVariables,
|
||||
} from './__generated__/Bars';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import {
|
||||
SymbolDocument,
|
||||
type SymbolQuery,
|
||||
type SymbolQueryVariables,
|
||||
} from './__generated__/Symbol';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
|
||||
const resolutionMap: Record<string, Interval> = {
|
||||
'1T': Interval.INTERVAL_BLOCK,
|
||||
'1': Interval.INTERVAL_I1M,
|
||||
'5': Interval.INTERVAL_I5M,
|
||||
'15': Interval.INTERVAL_I15M,
|
||||
'60': Interval.INTERVAL_I1H,
|
||||
'360': Interval.INTERVAL_I6H,
|
||||
'1D': Interval.INTERVAL_I1D,
|
||||
} as const;
|
||||
|
||||
const supportedResolutions = Object.keys(resolutionMap);
|
||||
|
||||
const configurationData = {
|
||||
// only showing Vega ofc
|
||||
exchanges: undefined,
|
||||
|
||||
// Represents the resolutions for bars supported by your datafeed
|
||||
supported_resolutions: supportedResolutions as ResolutionString[],
|
||||
} as const;
|
||||
|
||||
export const useDatafeed = () => {
|
||||
const subRef = useRef<Subscription>();
|
||||
const client = useApolloClient();
|
||||
|
||||
const datafeed = useMemo(() => {
|
||||
const feed: IBasicDataFeed = {
|
||||
onReady: (callback) => {
|
||||
setTimeout(() => callback(configurationData));
|
||||
},
|
||||
searchSymbols: () => {
|
||||
/* no op, we handle finding markets in app */
|
||||
},
|
||||
resolveSymbol: async (
|
||||
marketId,
|
||||
onSymbolResolvedCallback,
|
||||
onResolveErrorCallback
|
||||
) => {
|
||||
try {
|
||||
const result = await client.query<SymbolQuery, SymbolQueryVariables>({
|
||||
query: SymbolDocument,
|
||||
variables: {
|
||||
marketId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.data.market) {
|
||||
onResolveErrorCallback('Cannot resolve symbol: market not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const market = result.data.market;
|
||||
const instrument = market.tradableInstrument.instrument;
|
||||
const productType = instrument.product.__typename;
|
||||
|
||||
if (!productType) {
|
||||
onResolveErrorCallback(
|
||||
'Cannot resolve symbol: invalid product type'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const symbolInfo: LibrarySymbolInfo = {
|
||||
ticker: market.id,
|
||||
name: instrument.code,
|
||||
full_name: instrument.code,
|
||||
listed_exchange: 'vega',
|
||||
format: 'price' as SeriesFormat,
|
||||
description: instrument.name,
|
||||
type: 'futures', // TODO no hard code
|
||||
session: '24x7',
|
||||
timezone: 'Etc/UTC',
|
||||
exchange: '',
|
||||
minmov: 1,
|
||||
pricescale: Number('1' + '0'.repeat(market.decimalPlaces)), // for number of decimal places
|
||||
visible_plots_set: 'ohlc',
|
||||
volume_precision: market.positionDecimalPlaces,
|
||||
data_status: 'pulsed',
|
||||
delay: 1000,
|
||||
has_intraday: true, // required for less than 1 day interval
|
||||
|
||||
// @ts-ignore required for data conversion
|
||||
vegaDecimalPlaces: market.decimalPlaces,
|
||||
// @ts-ignore required for data conversion
|
||||
vegaPositionDecimalPlaces: market.positionDecimalPlaces,
|
||||
};
|
||||
|
||||
onSymbolResolvedCallback(symbolInfo);
|
||||
} catch (err) {
|
||||
onResolveErrorCallback('Cannot resolve symbol');
|
||||
}
|
||||
},
|
||||
getBars: async (
|
||||
symbolInfo,
|
||||
resolution,
|
||||
periodParams,
|
||||
onHistoryCallback,
|
||||
onErrorCallback
|
||||
) => {
|
||||
if (!symbolInfo.ticker) {
|
||||
onErrorCallback('No symbol.ticker');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await client.query<
|
||||
GetBarsQuery,
|
||||
GetBarsQueryVariables
|
||||
>({
|
||||
query: GetBarsDocument,
|
||||
variables: {
|
||||
marketId: symbolInfo.ticker,
|
||||
since: unixTimestampToDate(periodParams.from).toISOString(),
|
||||
to: unixTimestampToDate(periodParams.to).toISOString(),
|
||||
interval: resolutionMap[resolution],
|
||||
},
|
||||
});
|
||||
|
||||
const candleEdges = compact(
|
||||
result.data.market?.candlesConnection?.edges
|
||||
);
|
||||
|
||||
if (!candleEdges.length) {
|
||||
onHistoryCallback([], { noData: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const bars = candleEdges.map((edge) => {
|
||||
return prepareBar(
|
||||
edge.node,
|
||||
// @ts-ignore added in resolveSymbol
|
||||
symbolInfo.vegaDecimalPlaces,
|
||||
// @ts-ignore added in resolveSymbol
|
||||
symbolInfo.vegaPositionDecimalPlaces
|
||||
);
|
||||
});
|
||||
|
||||
onHistoryCallback(bars, { noData: false });
|
||||
} catch (err) {
|
||||
onErrorCallback(
|
||||
err instanceof Error ? err.message : 'Failed to get bars'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
subscribeBars: (
|
||||
symbolInfo,
|
||||
resolution,
|
||||
onTick
|
||||
|
||||
// subscriberUID, // chart will subscribe and unsbuscribe when the parent market of the page changes so we don't need to use subscriberUID as of now
|
||||
|
||||
// TODO: figure out how/when we should use onResetCacheNeededCallback
|
||||
// onResetCacheNeededCallback,
|
||||
) => {
|
||||
if (!symbolInfo.ticker) {
|
||||
throw new Error('No symbolInfo.ticker');
|
||||
}
|
||||
|
||||
subRef.current = client
|
||||
.subscribe<LastBarSubscription, LastBarSubscriptionVariables>({
|
||||
query: LastBarDocument,
|
||||
variables: {
|
||||
marketId: symbolInfo.ticker,
|
||||
interval: resolutionMap[resolution],
|
||||
},
|
||||
})
|
||||
.subscribe(({ data }) => {
|
||||
if (data) {
|
||||
const bar = prepareBar(
|
||||
data.candles,
|
||||
// @ts-ignore added in resolveSymbol
|
||||
symbolInfo.vegaDecimalPlaces,
|
||||
// @ts-ignore added in resolveSymbol
|
||||
symbolInfo.vegaPositionDecimalPlaces
|
||||
);
|
||||
|
||||
onTick(bar);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* We only have one active subscription no need to use the uid provided by unsubscribeBars
|
||||
*/
|
||||
unsubscribeBars: () => {
|
||||
if (subRef.current) {
|
||||
subRef.current.unsubscribe();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return feed;
|
||||
}, [client]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (subRef.current) {
|
||||
subRef.current.unsubscribe();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return datafeed;
|
||||
};
|
||||
|
||||
const prepareBar = (
|
||||
bar: BarFragment,
|
||||
decimalPlaces: number,
|
||||
positionDecimalPlaces: number
|
||||
) => {
|
||||
return {
|
||||
time: new Date(bar.periodStart).getTime(),
|
||||
low: toBigNum(bar.low, decimalPlaces).toNumber(),
|
||||
high: toBigNum(bar.high, decimalPlaces).toNumber(),
|
||||
open: toBigNum(bar.open, decimalPlaces).toNumber(),
|
||||
close: toBigNum(bar.close, decimalPlaces).toNumber(),
|
||||
volume: toBigNum(bar.volume, positionDecimalPlaces).toNumber(),
|
||||
};
|
||||
};
|
||||
|
||||
const unixTimestampToDate = (timestamp: number) => {
|
||||
return new Date(timestamp * 1000);
|
||||
};
|
||||
Reference in New Issue
Block a user