Skip to content

Commit

Permalink
Display opening dates details row for trades summary
Browse files Browse the repository at this point in the history
  • Loading branch information
rylorin committed Oct 31, 2024
1 parent fdbb907 commit f8630a0
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 145 deletions.
12 changes: 4 additions & 8 deletions src/app/components/Number/Number.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Text, TextProps } from "@chakra-ui/react";
import React, { FunctionComponent } from "react";
import { formatNumber } from "../../utils";

type NumberProps = {
/** Value to render */
Expand All @@ -15,19 +16,14 @@ type NumberProps = {
const Number: FunctionComponent<NumberProps> = ({
value,
decimals = 0,
isPercent = false,
color,
isPercent,
...rest
}): React.ReactNode => {
const rounded = value
? (isPercent ? value * 100 : value).toLocaleString(undefined, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
}) + (isPercent ? "%" : "")
: "-";
const rounded = formatNumber(value, decimals, isPercent);
let style: { color: string };
if (color) {
style = { color };
if (color !== "-") style = { color };
} else if (value > 0) {
style = { color: "green.500" };
} else if (value < 0) {
Expand Down
208 changes: 105 additions & 103 deletions src/app/components/Portfolio/Trade/TradesMonthlyTable.tsx
Original file line number Diff line number Diff line change
@@ -1,115 +1,117 @@
import { Link, Table, TableCaption, TableContainer, Tbody, Td, Tfoot, Thead, Tr } from "@chakra-ui/react";
import { HStack, Link, Text, VStack } from "@chakra-ui/react";
import React, { FunctionComponent } from "react";
import { Link as RouterLink } from "react-router-dom";
import { TradeMonthlySynthesys } from "../../../../routers/trades.types";
import { formatNumber } from "../../../utils";
import { TradeMonthlyRow, TradeMonthlySynthesys, TradeMonthlySynthesysEntry } from "../../../../routers/trades.types";
import Number from "../../Number/Number";

type TradesMonthlyTableProps = {
title?: string;
content: TradeMonthlySynthesys;
};

const TradesMonthlyTable: FunctionComponent<TradesMonthlyTableProps> = ({
title,
content,
..._rest
}): React.ReactNode => {
type minorRowProps = {
major: string;
index: string;
content: TradeMonthlySynthesysEntry;
};

const MinorRow: FunctionComponent<minorRowProps> = ({ major, index, content, ..._rest }): React.ReactNode => {
console.log("MinorRow", index, content);
return (
<>
<HStack>
<Text width="100px"></Text>
<Text width="100px">
<Link to={`../../month/${major.substring(0, 4)}/${major.substring(5)}`} as={RouterLink}>
{index}
</Link>
</Text>
<Number value={content.count} color="-" width="100px" />
<Number value={content.success / content.count} isPercent color="-" width="100px" />
<Number value={content.duration / content.count} color="-" width="100px" />
<Number value={content.min} width="100px" />
<Number value={content.total / content.count} width="100px" />
<Number value={content.max} width="100px" />
<Number value={content.total} width="100px" />
</HStack>
</>
);
};

type majorRowProps = {
index: string;
content: TradeMonthlyRow;
};

const MajorRow: FunctionComponent<majorRowProps> = ({ index, content, ..._rest }): React.ReactNode => {
console.log("MajorRow", index, content);
return (
<>
<HStack>
<Text width="100px">
<Link to={`../../month/${index.substring(0, 4)}/${index.substring(5)}`} as={RouterLink}>
{index}
</Link>
</Text>
<Text width="100px"></Text>
<Number value={content["-"].count} color="-" width="100px" />
<Number value={content["-"].success / content["-"].count} isPercent color="-" width="100px" />
<Number value={content["-"].duration / content["-"].count} color="-" width="100px" />
<Number value={content["-"].min} width="100px" />
<Number value={content["-"].total / content["-"].count} width="100px" />
<Number value={content["-"].max} width="100px" />
<Number value={content["-"].total} width="100px" />
</HStack>
{Object.keys(content)
.sort((a: string, b: string) => b.localeCompare(a))
.filter((index) => index !== "-")
.map((subIndex) => (
<MinorRow key={subIndex} major={index} index={subIndex} content={content[subIndex]} />
))}
</>
);
};

const TradesMonthlyTable: FunctionComponent<TradesMonthlyTableProps> = ({ content, ..._rest }): React.ReactNode => {
console.log("TradesMonthlyTable", content);
return (
<TableContainer>
<Table variant="simple" size="sm">
<TableCaption>
{title} ({Object.keys(content).length})
</TableCaption>
<Thead>
<Tr>
<Td>Month</Td>
<Td>#</Td>
<Td>Success</Td>
<Td>Duration</Td>
<Td>Min</Td>
<Td>Average</Td>
<Td>Max</Td>
<Td>PnL</Td>
</Tr>
</Thead>
<Tbody>
{Object.keys(content)
.sort((a: string, b: string) => b.localeCompare(a))
.map((key) => (
<Tr key={key}>
<Td>
<Link to={`../../month/${key.substring(0, 4)}/${key.substring(5)}`} as={RouterLink}>
{key}
</Link>
</Td>
<Td isNumeric>{content[key].count}</Td>
<Td isNumeric>{formatNumber((content[key].success / content[key].count) * 100)}%</Td>
<Td isNumeric>{formatNumber(content[key].duration / content[key].count)}</Td>
<Td>
<Number value={content[key].min} />
</Td>
<Td>
<Number value={content[key].total / content[key].count} />
</Td>
<Td>
<Number value={content[key].max} />
</Td>
<Td>
<Number value={content[key].total} />
</Td>
</Tr>
))}
</Tbody>
<Tfoot>
<Tr>
<Td fontWeight="bold">Total</Td>
<Td isNumeric fontWeight="bold">
{Object.values(content).reduce((p: number, v) => (p += v.count || 0), 0)}
</Td>
<Td isNumeric fontWeight="bold">
{formatNumber(
(Object.values(content).reduce((p: number, v) => (p += v.success || 0), 0) /
Object.values(content).reduce((p: number, v) => (p += v.count || 0), 0)) *
100,
)}
%
</Td>
<Td isNumeric fontWeight="bold">
{formatNumber(
Object.values(content).reduce((p: number, v) => (p += v.duration || 0), 0) /
Object.values(content).reduce((p: number, v) => (p += v.count || 0), 0),
)}
</Td>
<Td>
<Number
value={Object.values(content).reduce((p: number, v) => (p ? Math.min(p, v.min) : v.min), undefined)}
/>
</Td>
<Td>
<Number
value={
Object.values(content).reduce((p: number, v) => (p += v.total || 0), 0) /
Object.values(content).reduce((p: number, v) => (p += v.count || 0), 0)
}
fontWeight="bold"
/>
</Td>
<Td>
<Number
value={Object.values(content).reduce((p: number, v) => (p ? Math.max(p, v.max) : v.max), undefined)}
/>
</Td>
<Td>
<Number
value={Object.values(content).reduce((p: number, v) => (p += v.total || 0), 0)}
fontWeight="bold"
/>
</Td>
</Tr>
</Tfoot>
</Table>
</TableContainer>
<>
<VStack>
<HStack>
<Text width="100px" align="center">
Close
</Text>
<Text width="100px" align="center">
Open
</Text>
<Text width="100px" align="center">
#
</Text>
<Text width="100px" align="center">
Success
</Text>
<Text width="100px" align="center">
Duration
</Text>
<Text width="100px" align="center">
Min
</Text>
<Text width="100px" align="center">
Average
</Text>
<Text width="100px" align="center">
Max
</Text>
<Text width="100px" align="center">
PnL
</Text>
</HStack>
{Object.keys(content)
.sort((a: string, b: string) => b.localeCompare(a))
.map((index) => (
<MajorRow key={index} index={index} content={content[index]} />
))}
</VStack>
</>
);
};

Expand Down
4 changes: 2 additions & 2 deletions src/app/components/Portfolio/Trade/TradesSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const TradeSummary: FunctionComponent<TradeSummaryProps> = ({ ..._rest }): React
{
label: "PnL",
data: labels.reduce((p, v) => {
p.push(theSynthesys.byMonth[v].total); // eslint-disable-line @typescript-eslint/no-unsafe-argument
p.push(theSynthesys.byMonth[v]["-"].total); // eslint-disable-line @typescript-eslint/no-unsafe-argument
return p;
}, [] as number[]),
},
Expand Down Expand Up @@ -44,7 +44,7 @@ const TradeSummary: FunctionComponent<TradeSummaryProps> = ({ ..._rest }): React
</Box>
<Text>Closed trades by month</Text>
<BarChart title="Realized Performance" labels={labels} datasets={datasets} />
<TradesMonthlyTable content={theSynthesys.byMonth} title="Closed trades" />
<TradesMonthlyTable content={theSynthesys.byMonth} />
</>
);
};
Expand Down
6 changes: 3 additions & 3 deletions src/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ export const obfuscate = (text: string): string => {
return len < 4 ? "****" : text.slice(0, 2) + "*".repeat(len - 4) + text.slice(len - 2, len);
};

export const formatNumber = (value: number | undefined, decimals = 0): string => {
export const formatNumber = (value: number | undefined, decimals = 0, isPercent = false): string => {
return value
? value.toLocaleString(undefined, {
? (isPercent ? value * 100 : value).toLocaleString(undefined, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
})
}) + (isPercent ? "%" : "")
: "-";
};
10 changes: 9 additions & 1 deletion src/models/trade.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { CreationOptional, ForeignKey, InferAttributes, InferCreationAttributes, NonAttribute } from "sequelize";
import {
CreationOptional,
ForeignKey,
HasManyGetAssociationsMixin,
InferAttributes,
InferCreationAttributes,
NonAttribute,
} from "sequelize";
import { BelongsTo, Column, DataType, HasMany, Model, Table } from "sequelize-typescript";
import { Contract } from "./contract.model";
import { expirationToDate } from "./date_utils";
Expand Down Expand Up @@ -69,6 +76,7 @@ export class Trade extends Model<

@HasMany(() => Statement, { foreignKey: "trade_unit_id" })
declare statements?: Statement[];
declare getStatements: HasManyGetAssociationsMixin<Statement>;

/** Total duration if trade is closed or current duration is trade is open */
get duration(): NonAttribute<number> {
Expand Down
4 changes: 2 additions & 2 deletions src/routers/trades.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export type TradeMonthlySynthesysEntry = {
max: number;
total: number;
};
export type TradeMonthlySynthesys = Record<"string", TradeMonthlySynthesysEntry>;

export type TradeMonthlyRow = Record<"string", TradeMonthlySynthesysEntry>;
export type TradeMonthlySynthesys = Record<"string", TradeMonthlyRow>;
export type TradeSynthesys = { open: TradeEntry[]; byMonth: TradeMonthlySynthesys };
export type OpenTradesWithPositions = { trades: TradeEntry[]; positions: (PositionEntry | OptionPositionEntry)[] };
Loading

0 comments on commit f8630a0

Please sign in to comment.