-
-
Save yuryprokashev/a5f1e7c385bda342541d8c2a3245e5f8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { | |
DomainTuple, | |
VictoryAxis, | |
VictoryCandlestick, | |
VictoryChart, | |
VictoryPrimitiveShapeProps, | |
VictoryZoomContainer | |
} from 'victory'; | |
import { PageProps } from './types'; | |
import { VictoryClosedOrder } from "../component/VictoryClosedOrder"; | |
import dayjs from "dayjs"; | |
import { useLocation } from "react-router-dom"; | |
import { useAppSelector } from "../redux/hooks"; | |
import { selectSymbolParameters } from "../redux/slice/symbolParameterSlice"; | |
import { useTheme } from "@mui/material"; | |
type VictoryCandle = { | |
x: Date, | |
open: number, | |
close: number, | |
high: number, | |
low: number | |
} | |
type ChartState = { | |
zoomDomain: { | |
x: DomainTuple, | |
y: DomainTuple | |
} | |
} | |
const defaultChart = { | |
msPerRangePoint: 16539428, | |
candleWidth: 4 | |
} | |
const OrderPage: React.FC<PageProps> = ({ thunks }) => { | |
const location = useLocation(); | |
const queryParams = new URLSearchParams(location.search); | |
const symbolKey = queryParams.get('symbol_key'); | |
const symbolParameterState = useAppSelector(selectSymbolParameters); | |
const currentSymbolParameterState = symbolParameterState?.map?.[symbolKey ?? ''] ?? null; | |
const victoryCandles = currentSymbolParameterState ? currentSymbolParameterState?.data?.['timestamp'].map((timestamp: string | number, index: number) => { | |
return { | |
x: dayjs(timestamp).toDate(), | |
open: currentSymbolParameterState?.data?.['open'][index] as number, | |
close: currentSymbolParameterState?.data?.['close'][index] as number, | |
high: currentSymbolParameterState?.data?.['high'][index] as number, | |
low: currentSymbolParameterState?.data?.['low'][index] as number, | |
} as VictoryCandle; | |
}) : []; | |
const entryPrice = parseFloat(queryParams.get('entry_price') ?? '0'); | |
const takeProfitPrice = parseFloat(queryParams.get('tp_price') ?? '0'); | |
const stopLossPrice = parseFloat(queryParams.get('sl_price') ?? '0'); | |
const orderEntry = dayjs(queryParams.get('entry_time')).toDate(); | |
const orderClose = dayjs(queryParams.get('close_time')).toDate(); | |
const orderDuration = dayjs(orderClose).diff(dayjs(orderEntry), 'ms'); | |
const getEntireDomain = (props: VictoryPrimitiveShapeProps) => { | |
const { data } = props; | |
if (!data) return { x: [0, 0] as DomainTuple, y: [0, 0] as DomainTuple }; | |
return { | |
y: [Math.min(...data.map((d: { y: number }) => d.y)), Math.max(...data.map((d: { y: number }) => d.y))] as DomainTuple, | |
x: [Math.min(...data.map((d: { x: Date }) => d.x)), Math.max(...data.map((d: { x: Date }) => d.x))] as DomainTuple | |
}; | |
}; | |
const getZoomedCandles = (props: VictoryPrimitiveShapeProps): VictoryCandle[] => { | |
const { data } = props; | |
const { x: xDomain } = chartState.zoomDomain; | |
const [minX, maxX] = xDomain; | |
return data?.filter((d: VictoryCandle) => { | |
return d.x >= minX && d.x <= maxX; | |
}); | |
} | |
const orderFitYDomain = [ | |
Math.min(entryPrice, takeProfitPrice, stopLossPrice) * 0.95, | |
Math.max(entryPrice, takeProfitPrice, stopLossPrice) * 1.05 | |
] as DomainTuple; | |
const orderFitXDomain = [ | |
dayjs(orderEntry).subtract(1, 'month').toDate(), | |
dayjs(orderClose).add(1, 'month').toDate() | |
] as DomainTuple; | |
const [chartState, setChartState] = React.useState<ChartState>({ | |
zoomDomain: { | |
x: orderFitXDomain, | |
y: orderFitYDomain | |
} | |
}); | |
const handleZoomDomainChange = (domain: { x: DomainTuple }, props: any) => { | |
console.log('props', props) | |
setChartState({ | |
zoomDomain: { | |
x: [ | |
dayjs(domain.x[0]).toDate(), | |
dayjs(domain.x[1]).toDate() | |
], | |
y: orderFitYDomain | |
} | |
}); | |
}; | |
const theme = useTheme(); | |
const candleColors = { | |
positive: theme.palette.success.main, | |
negative: theme.palette.error.main | |
}; | |
const candleWidth = (props: any) => { | |
const { scale } = props; | |
const { x } = scale; | |
const range = x.range(); | |
const domain = x.domain(); | |
const rangeSize = range[1] - range[0]; | |
const domainSize = domain[1] - domain[0]; | |
const currentMsPerRangePoint = domainSize / rangeSize; | |
const candleWidth = defaultChart.candleWidth * defaultChart.msPerRangePoint / currentMsPerRangePoint; | |
return Math.max( | |
Math.floor(candleWidth), | |
1 | |
); | |
}; | |
const entireDomain = getEntireDomain({ data: victoryCandles }); | |
// console.log('chartState.zoomDomain', chartState.zoomDomain) | |
// @ts-ignore | |
console.log('chartState.zoomDomain.width', chartState.zoomDomain.x[1] - chartState.zoomDomain.x[0]) | |
const zoomedCandles = getZoomedCandles({ data: victoryCandles }); | |
// console.log('zoomedCandles', zoomedCandles) | |
return ( | |
<div> | |
<h1>Order Page</h1> | |
<VictoryChart | |
scale={{ x: "time" }} | |
domain={entireDomain} | |
containerComponent={ | |
// @ts-ignore | |
<VictoryZoomContainer | |
onZoomDomainChange={handleZoomDomainChange} | |
zoomDimension="x" | |
zoomDomain={chartState.zoomDomain} | |
minimumZoom={{x: orderDuration}} | |
/> | |
} | |
> | |
<VictoryAxis /> | |
<VictoryAxis dependentAxis /> | |
<VictoryCandlestick | |
data={zoomedCandles} | |
candleColors={candleColors} | |
candleWidth={candleWidth} | |
wickStrokeWidth={1} | |
style={{ | |
data: { | |
strokeWidth: 0 | |
} | |
}} | |
/> | |
<VictoryClosedOrder | |
orderEntry={orderEntry} | |
orderClose={orderClose} | |
entryPrice={entryPrice} | |
takeProfitPrice={takeProfitPrice} | |
stopLossPrice={stopLossPrice} | |
/> | |
</VictoryChart> | |
</div> | |
); | |
}; | |
export default OrderPage; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment