feat(trading): store study sizes, reduce candles gap (#4708)

This commit is contained in:
Matthew Russell 2023-09-12 09:56:43 -07:00 committed by GitHub
parent 2453d7841a
commit a77765b1e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 23 deletions

View File

@ -82,6 +82,7 @@ html [data-theme='light'] {
--pennant-color-volume-sell: theme(colors.market.red.DEFAULT); --pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
/* reduce space between candles */
--pennant-candlestick-inner-padding: 0.1; --pennant-candlestick-inner-padding: 0.1;
} }

View File

@ -3,6 +3,7 @@ import { CandlestickChart } from 'pennant';
import { VegaDataSource } from './data-source'; import { VegaDataSource } from './data-source';
import { useApolloClient } from '@apollo/client'; import { useApolloClient } from '@apollo/client';
import { useMemo } from 'react'; import { useMemo } from 'react';
import debounce from 'lodash/debounce';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers'; import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
@ -22,8 +23,25 @@ export const CandlesChartContainer = ({
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { theme } = useThemeSwitcher(); const { theme } = useThemeSwitcher();
const { interval, chartType, overlays, studies, merge } = const {
useCandlesChartSettings(); interval,
chartType,
overlays,
studies,
studySizes,
setStudies,
setStudySizes,
setOverlays,
} = useCandlesChartSettings();
const handlePaneChange = useMemo(
() =>
debounce((sizes: number[]) => {
// first number is main pain, which is greedy so we don't store it
setStudySizes(sizes.filter((_, i) => i !== 0));
}, 300),
[setStudySizes]
);
const dataSource = useMemo(() => { const dataSource = useMemo(() => {
return new VegaDataSource(client, marketId, pubKey); return new VegaDataSource(client, marketId, pubKey);
@ -45,15 +63,16 @@ export const CandlesChartContainer = ({
initialNumCandlesToDisplay: Math.floor( initialNumCandlesToDisplay: Math.floor(
width * CANDLES_TO_WIDTH_FACTOR width * CANDLES_TO_WIDTH_FACTOR
), ),
studySize: 150, // default size
studySizes,
}} }}
interval={interval} interval={interval}
theme={theme} theme={theme}
onOptionsChanged={(options) => { onOptionsChanged={(options) => {
merge({ setStudies(options.studies);
overlays: options.overlays, setOverlays(options.overlays);
studies: options.studies,
});
}} }}
onPaneChanged={handlePaneChange}
/> />
</div> </div>
)} )}

View File

@ -5,36 +5,45 @@ import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer'; import { immer } from 'zustand/middleware/immer';
type StudySizes = { [S in Study]?: number };
interface StoredSettings { interface StoredSettings {
interval: Interval; interval: Interval;
type: ChartType; type: ChartType;
overlays: Overlay[]; overlays: Overlay[];
studies: Study[]; studies: Study[];
studySizes: StudySizes;
} }
export const STUDY_SIZE = 100;
const STUDY_ORDER: Study[] = [
Study.FORCE_INDEX,
Study.RELATIVE_STRENGTH_INDEX,
Study.ELDAR_RAY,
Study.MACD,
Study.VOLUME,
];
const DEFAULT_CHART_SETTINGS = { const DEFAULT_CHART_SETTINGS = {
interval: Interval.I15M, interval: Interval.I15M,
type: ChartType.CANDLE, type: ChartType.CANDLE,
overlays: [Overlay.MOVING_AVERAGE], overlays: [Overlay.MOVING_AVERAGE],
studies: [Study.MACD, Study.VOLUME], studies: [Study.MACD, Study.VOLUME],
studySizes: {},
}; };
export const useCandlesChartSettingsStore = create< export const useCandlesChartSettingsStore = create<
StoredSettings & { StoredSettings & {
merge: (settings: Partial<StoredSettings>) => void;
setType: (type: ChartType) => void; setType: (type: ChartType) => void;
setInterval: (interval: Interval) => void; setInterval: (interval: Interval) => void;
setOverlays: (overlays: Overlay[]) => void; setOverlays: (overlays?: Overlay[]) => void;
setStudies: (studies: Study[]) => void; setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void;
} }
>()( >()(
persist( persist(
immer((set) => ({ immer((set) => ({
...DEFAULT_CHART_SETTINGS, ...DEFAULT_CHART_SETTINGS,
merge: (settings: Partial<StoredSettings>) =>
set((state) => {
Object.assign(state, settings);
}),
setType: (type) => setType: (type) =>
set((state) => { set((state) => {
state.type = type; state.type = type;
@ -43,14 +52,35 @@ export const useCandlesChartSettingsStore = create<
set((state) => { set((state) => {
state.interval = interval; state.interval = interval;
}), }),
setOverlays: (overlays) => setOverlays: (overlays) => {
if (!overlays) return;
set((state) => { set((state) => {
state.overlays = overlays; state.overlays = overlays;
}), });
setStudies: (studies) => },
setStudies: (studies) => {
if (!studies) return;
// Make sure studies are always returned in the same order
studies.sort((a, b) => {
return STUDY_ORDER.indexOf(a) - STUDY_ORDER.indexOf(b);
});
set((state) => { set((state) => {
state.studies = studies; state.studies = studies;
}), });
},
setStudySizes: (sizes) => {
set((state) => {
// for every study find the corresonding size and update
// the size record for that study
state.studies.forEach((s, i) => {
const size = sizes[i];
state.studySizes[s] = size;
});
});
},
})), })),
{ {
name: 'vega_candles_chart_store', name: 'vega_candles_chart_store',
@ -85,15 +115,22 @@ export const useCandlesChartSettings = () => {
[Study.VOLUME] [Study.VOLUME]
); );
// find the study size
const studySizes = studies.map((s) => {
const size = settings.studySizes[s] || STUDY_SIZE;
return size;
});
return { return {
interval, interval,
chartType, chartType,
overlays, overlays,
studies, studies,
studySizes,
setInterval: settings.setInterval, setInterval: settings.setInterval,
setType: settings.setType, setType: settings.setType,
setStudies: settings.setStudies, setStudies: settings.setStudies,
setOverlays: settings.setOverlays, setOverlays: settings.setOverlays,
merge: settings.merge, setStudySizes: settings.setStudySizes,
}; };
}; };

View File

@ -70,7 +70,7 @@
"jsondiffpatch": "^0.4.1", "jsondiffpatch": "^0.4.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"next": "13.3.0", "next": "13.3.0",
"pennant": "1.11.1", "pennant": "1.12.0",
"react": "18.2.0", "react": "18.2.0",
"react-copy-to-clipboard": "^5.0.4", "react-copy-to-clipboard": "^5.0.4",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View File

@ -20253,10 +20253,10 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
pennant@1.11.1: pennant@1.12.0:
version "1.11.1" version "1.12.0"
resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.11.1.tgz#f47bceaade01db215eeba666cd755840d16fe9ed" resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.12.0.tgz#e12707d5f1aac554d81bad060637e608335e0b50"
integrity sha512-U26OxjxETWLJAvCFj20oH0y5gzyBfJ5oD4O3dYg1aWnH4giUYDPWuUDFnzlMJ+yXolyYECccXm6S1BkoVcVzDg== integrity sha512-xosg5erRf+Ke9iORdqyv+SOGcD3uJX1dgf990q1DvHuzz36w2txCZsfnvcXhRO++HYVKS9sU/YLPRLJgolFGtA==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@d3fc/d3fc-technical-indicator" "^8.0.1" "@d3fc/d3fc-technical-indicator" "^8.0.1"