Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import type { AppRouter } from '@server/routers';
- import type { Backtest } from '@server/routers/backtest';
- import type { Execution } from '@server/routers/execution';
- import type { TRPCClientErrorLike } from '@trpc/client';
- import type { UseTRPCQueryResult } from '@trpc/react-query/shared';
- import { subDays } from 'date-fns';
- import type Decimal from 'decimal.js';
- import { useTheme } from 'next-themes';
- import { useEffect, useMemo, useRef, useState } from 'react';
- import { Line, LineChart, ReferenceLine, ResponsiveContainer, YAxis } from 'recharts';
- import { BacktestStatus, ExecutionStatus, ExecutionType } from '@cybotrade/core';
- import { calculatePerformance } from '@/lib/calculation';
- import { Skeleton } from '@/ui';
- import { trpcVanilla } from '@/utils/trpc';
- type EquityCurveRendererProps = {
- backtests: UseTRPCQueryResult<Backtest[], TRPCClientErrorLike<AppRouter>>;
- executions: UseTRPCQueryResult<Execution[], TRPCClientErrorLike<AppRouter>>;
- };
- export const EquityCurveRenderer = ({ backtests, executions }: EquityCurveRendererProps) => {
- const { theme } = useTheme();
- const isDarkTheme = theme === 'dark';
- const [backtestHistories, setBacktestHistories] = useState<
- {
- id: string;
- equity: { name: number; equity: Decimal }[];
- createdAt: Date;
- }[]
- >([]);
- const [executionHistories, setExecutionHistories] = useState<
- {
- id: string;
- equity: { name: number; equity: Decimal }[];
- createdAt: Date;
- }[]
- >([]);
- // const [chartData, setChartData] = useState<
- // {
- // name: number;
- // equity: Decimal;
- // }[]
- // >([]);
- const [isLoading, setLoading] = useState(true);
- const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
- useEffect(() => {
- async function computeBacktests() {
- if (!backtests.data) return;
- if (backtests.data.length === 0) return;
- const succeed = (
- await Promise.all(
- backtests.data
- .filter(({ status }) => status === BacktestStatus.Completed)
- .map(
- async ({
- id,
- parameters: { comission, initialCapital, endDate },
- closedTrades,
- openedTrades,
- createdAt,
- }) => {
- const equities = (
- await trpcVanilla.statistics.equity.list.query({
- correlationId: id,
- timestamp: {
- start: subDays(new Date(endDate), 7),
- end: new Date(endDate),
- },
- })
- ).Items.map((item) => ({
- value: item.value,
- timestamp: new Date(item.timestamp.div(10 ** 6).toNumber()),
- }));
- return {
- id,
- createdAt,
- ...calculatePerformance({
- history: {
- closedTrades,
- openedTrades,
- equities,
- drawdowns: equities.map((equity) => ({
- ...equity,
- value: equity.value.sub(initialCapital),
- })),
- },
- parameters: { comission, initialCapital, riskFreeRate: 0.02 },
- }),
- };
- },
- ),
- )
- ).map(({ id, equities, createdAt }) => ({
- id,
- createdAt,
- equity: equities.map((equity, i) => ({
- name: i,
- equity: equity.value.div(equities[0].value),
- })),
- }));
- const failed = backtests.data
- .filter(({ status }) => status === BacktestStatus.Failed)
- .map(({ id, createdAt }) => ({
- id,
- equity: [],
- createdAt,
- }));
- const pending = backtests.data
- .filter(({ status }) => [BacktestStatus.Pending, BacktestStatus.Running].includes(status))
- .map(({ id, createdAt }) => ({
- id,
- equity: [],
- createdAt,
- }));
- setBacktestHistories([...succeed, ...failed, ...pending]);
- }
- computeBacktests();
- }, [backtests.data]);
- useEffect(() => {
- async function computeExecutions() {
- if (!executions.data) return;
- if (executions.data.length === 0) return;
- const succeed = (
- await Promise.all(
- executions.data
- .filter(({ type }) => type === ExecutionType.Live)
- .filter(({ status }) => status === ExecutionStatus.Running)
- .map(
- async ({
- id,
- parameters: { comission, initialCapital },
- closedTrades,
- openedTrades,
- createdAt,
- }) => {
- const equities = (
- await trpcVanilla.statistics.equity.list.query({
- correlationId: id,
- timestamp: {
- start: subDays(new Date(), 7),
- end: new Date(),
- },
- })
- ).Items.map((item) => ({
- value: item.value,
- timestamp: new Date(item.timestamp.div(10 ** 6).toNumber()),
- }));
- return {
- id,
- createdAt,
- ...calculatePerformance({
- history: {
- closedTrades,
- openedTrades,
- equities: equities,
- drawdowns: equities.map((equity) => ({
- ...equity,
- value: equity.value.sub(initialCapital),
- })),
- },
- parameters: {
- comission,
- initialCapital:
- equities && equities.length > 0 ? equities[0].value.toNumber() : 0,
- riskFreeRate: 0.02,
- },
- }),
- };
- },
- ),
- )
- ).map(({ id, equities, createdAt }) => ({
- id,
- equity: equities.map((equity, i) => ({
- name: i,
- equity: equity.value.div(equities[0].value),
- })),
- createdAt,
- }));
- const failed = executions.data
- .filter(({ status }) => status === ExecutionStatus.Failed)
- .map(({ id, createdAt }) => ({
- id,
- equity: [],
- createdAt,
- }));
- const pending = executions.data
- .filter(({ status }) => [ExecutionStatus.Pending].includes(status))
- .map(({ id, createdAt }) => ({
- id,
- equity: [],
- createdAt,
- }));
- setExecutionHistories([...succeed, ...failed, ...pending]);
- }
- computeExecutions();
- }, [executions.data]);
- // not working as i expected
- // useEffect(() => {
- // timer.current && clearTimeout(timer.current);
- // const computeChartData = [...backtestHistories, ...executionHistories]
- // .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
- // .flatMap(({ equity }) => equity);
- // timer.current = setTimeout(() => {
- // setChartData(computeChartData);
- // setLoading(false);
- // }, 5000);
- // }, [backtestHistories, executionHistories]);
- const chartData = useMemo(
- () =>
- [...backtestHistories, ...executionHistories]
- .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
- .flatMap(({ equity }) => equity),
- [backtestHistories, executionHistories],
- );
- // loading skeleton
- // if (isLoading) {
- // return (
- // <div className="flex mx-auto max-w-[150px] h-[80px]">
- // <Skeleton className="w-full h-full" />
- // </div>
- // );
- // }
- return (
- <div className="flex mx-auto max-w-[150px] h-[80px]">
- <ResponsiveContainer width="100%" height="100%">
- <LineChart data={chartData}>
- <ReferenceLine y={chartData?.[0]?.equity.toNumber() || 0} stroke="black" />
- <YAxis domain={['dataMin', 'dataMax']} hide={true} />
- <Line
- type="monotone"
- dataKey="equity"
- dot={false}
- stroke={isDarkTheme ? '#00FC65' : '#FC9E26'}
- // stroke="#FC9E26"
- />
- </LineChart>
- </ResponsiveContainer>
- </div>
- );
- };
Advertisement
Add Comment
Please, Sign In to add comment