Skip to content

Commit

Permalink
Minor changes to PnL details
Browse files Browse the repository at this point in the history
  • Loading branch information
rylorin committed Apr 21, 2024
1 parent 8f9e06f commit 5ed05b5
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/app/components/Portfolio/Report/FeesDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const FeesDetails = ({ theReports, ..._rest }: Props): React.ReactNode => {
result = {
id: statement.id,
date: new Date(statement.date),
amount: statement.fees * statement.fxRateToBase,
amount: statement.amount * statement.fxRateToBase,
description: statement.description,
};
break;
Expand Down
18 changes: 15 additions & 3 deletions src/app/components/Portfolio/Report/PnLsDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { IconButton, Link, Tooltip } from "@chakra-ui/react";
import { createColumnHelper } from "@tanstack/react-table";
import { default as React } from "react";
import { Link as RouterLink, useParams } from "react-router-dom";
import { StatementTypes } from "../../../../models/types";
import { ContractType, StatementTypes } from "../../../../models/types";
import {
BondStatementEntry,
EquityStatementEntry,
OptionStatementEntry,
ReportEntry,
StatementUnderlyingEntry,
StatementUnderlyingOption,
} from "../../../../routers/types";
import Number from "../../Number/Number";
import { StatementLink } from "../Statement/links";
Expand All @@ -36,6 +37,15 @@ const PnLsDetails = ({ theReports, ..._rest }: Props): React.ReactNode => {

let totalPnl = 0;

const getSymbol = (contract: StatementUnderlyingEntry | StatementUnderlyingOption): string => {
switch (contract.secType) {
case ContractType.Option:
return contract.name;
default:
return contract.symbol;
}
};

const data: PnLDetails[] = theReports
.reduce(
(p, report) => p.concat(report.tradesDetails),
Expand All @@ -53,15 +63,17 @@ const PnLsDetails = ({ theReports, ..._rest }: Props): React.ReactNode => {
description: statement.description,
};
break;

case StatementTypes.OptionStatement:
result = {
id: statement.id,
date: new Date(statement.date),
pnl: statement.pnl * statement.fxRateToBase,
underlying: statement.underlying,
underlying: statement.option,
description: statement.description,
};
break;

case StatementTypes.BondStatement:
result = {
id: statement.id,
Expand Down Expand Up @@ -95,7 +107,7 @@ const PnLsDetails = ({ theReports, ..._rest }: Props): React.ReactNode => {
footer: "Total",
}),
columnHelper.accessor("underlying", {
cell: (info) => info.getValue().symbol,
cell: (info) => getSymbol(info.getValue()),
sortingFn: (a, b) =>
(a.getValue("underlying") as StatementUnderlyingEntry).symbol.localeCompare(
(b.getValue("underlying") as StatementUnderlyingEntry).symbol,
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/Portfolio/Report/ReportIndex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const ReportsIndex: FunctionComponent<Props> = ({ ..._rest }): React.ReactNode =
/>
<TableContainer>
<Table variant="simple" size="sm" className="table-tiny">
<TableCaption>Available reports</TableCaption>
<TableCaption>Available reports ({months.length})</TableCaption>
<Thead>
<Tr>
<Td>Year</Td>
Expand Down
2 changes: 1 addition & 1 deletion src/bots/importer.bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ export class ImporterBot extends ITradingBot {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
protected processCashTransaction(element: any): Promise<Statement | null> {
logger.log(LogLevel.Trace, MODULE + ".processCashTransaction", element.securityID as string, element);
if (element.assetCategory == "BOND") console.log("processCashTransaction", element);
// if (element.assetCategory == "BOND") console.log("processCashTransaction", element);
let statementType: StatementTypes;
switch (element.type) {
case "Withholding Tax":
Expand Down
39 changes: 24 additions & 15 deletions src/routers/statements.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type StatementUnderlyingEntry = {
price: number | null;
};

export type StatementOptionEntry = StatementUnderlyingEntry & {
export type StatementUnderlyingOption = StatementUnderlyingEntry & {
lastTradeDate: string;
strike: number;
callOrPut: OptionType;
Expand All @@ -27,45 +27,54 @@ export type BaseStatement = {
currency: string;
fxRateToBase: number;
amount: number;
underlying: StatementUnderlyingEntry | undefined;
// underlying: StatementUnderlyingEntry | undefined;
trade_id: number | null;
description: string;

fees: number | undefined;
};

export type EquityStatementEntry = BaseStatement & {
statementType: "Trade";
quantity: number | undefined;
underlying: StatementUnderlyingEntry;
quantity: number;
pnl: number;
fees: number;
};

export type OptionStatementEntry = BaseStatement & {
statementType: "TradeOption";
option: StatementOptionEntry;
quantity: number | undefined;
option: StatementUnderlyingOption;
quantity: number;
pnl: number;
fees: number;
};

export type BondStatementEntry = BaseStatement & {
statementType: "Bond";
country: string;
underlying: StatementUnderlyingEntry | undefined;
accruedInterests: number;
quantity: number;
pnl: number;
fees: number;
};

export type DividendStatementEntry = BaseStatement & {
statementType: "Dividend";
country: string;
underlying: StatementUnderlyingEntry | undefined;
};
export type TaxStatementEntry = BaseStatement & { statementType: "Tax"; country: string };

export type InterestStatementEntry = BaseStatement & { statementType: "Interest"; country: string | null };
export type InterestStatementEntry = BaseStatement & {
statementType: "Interest";
country: string | null;
underlying: StatementUnderlyingEntry | undefined;
};
export type WithHoldingStatementEntry = BaseStatement & { statementType: "WithHolding" };

export type FeeStatementEntry = BaseStatement & { statementType: "OtherFee" };
export type CorporateStatementEntry = BaseStatement & { statementType: "CorporateStatement" };
export type CashStatementEntry = BaseStatement & { statementType: "Cash" };
export type BondStatementEntry = BaseStatement & {
statementType: "Bond";
country: string;
accruedInterests: number;
quantity: number | undefined;
pnl: number;
};
export type StatementEntry =
| EquityStatementEntry
| OptionStatementEntry
Expand Down
33 changes: 16 additions & 17 deletions src/routers/statements.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "../models";
import { ContractType, StatementTypes } from "../models/types";
import { DididendSummary, InterestsSummary, ReportEntry } from "./reports.types";
import { BaseStatement, StatementEntry, StatementOptionEntry } from "./statements.types";
import { BaseStatement, StatementEntry, StatementUnderlyingOption } from "./statements.types";

export const statementModelToStatementEntry = (item: Statement): Promise<StatementEntry> => {
const baseStatement: BaseStatement = {
Expand All @@ -21,22 +21,20 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
date: item.date.getTime(),
currency: item.currency,
amount: item.netCash,
fees: undefined,
fxRateToBase: item.fxRateToBase,
description: item.description,
trade_id: item.trade_unit_id,
underlying: item.stock,
};
switch (item.statementType) {
case StatementTypes.EquityStatement:
return EquityStatement.findByPk(item.id).then((thisStatement) => {
// baseStatement.quantity = thisStatement?.quantity;
baseStatement.fees = thisStatement?.fees;
return {
statementType: StatementTypes.EquityStatement,
...baseStatement,
quantity: thisStatement?.quantity,
pnl: thisStatement!.realizedPnL || 0,
underlying: item.stock,
quantity: thisStatement!.quantity,
pnl: thisStatement!.realizedPnL,
fees: thisStatement!.fees,
};
});

Expand All @@ -47,9 +45,7 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
{ model: OptionContract, as: "option" },
],
}).then((thisStatement) => {
// baseStatement.quantity = thisStatement!.quantity;
baseStatement.fees = thisStatement!.fees;
const option: StatementOptionEntry = {
const option: StatementUnderlyingOption = {
id: thisStatement!.contract_id,
secType: ContractType.Option,
symbol: thisStatement!.contract.symbol,
Expand All @@ -64,9 +60,11 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
return {
statementType: StatementTypes.OptionStatement,
...baseStatement,
underlying: item.stock,
option,
quantity: thisStatement?.quantity,
quantity: thisStatement!.quantity,
pnl: thisStatement!.realizedPnL,
fees: thisStatement!.fees,
};
});

Expand All @@ -76,6 +74,7 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
statementType: StatementTypes.DividendStatement,
...baseStatement,
country: thisStatement!.country || "XY",
underlying: item.stock,
};
});

Expand All @@ -94,6 +93,7 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
statementType: StatementTypes.InterestStatement,
...baseStatement,
country: thisStatement!.country || "",
underlying: item.stock,
};
});

Expand All @@ -104,7 +104,7 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
});

case StatementTypes.FeeStatement:
baseStatement.fees = item.netCash;
// baseStatement.fees = item.netCash;
return Promise.resolve({
statementType: StatementTypes.FeeStatement,
...baseStatement,
Expand All @@ -127,17 +127,17 @@ export const statementModelToStatementEntry = (item: Statement): Promise<Stateme
if (!thisStatement) throw Error(`BondStatement ${item.id} not found!`);
let country: string;
if (thisStatement.country) country = thisStatement.country;
else if (item.stock.isin) country = item.stock.isin.substring(0, 2);
else if (item.stock?.isin) country = item.stock.isin.substring(0, 2);
else country = "ZZ";
baseStatement.fees = thisStatement.fees;
// console.log("baseStatement", baseStatement);
return {
statementType: StatementTypes.BondStatement,
...baseStatement,
country,
underlying: item.stock,
accruedInterests: thisStatement.accruedInterests,
quantity: thisStatement.quantity,
pnl: thisStatement.realizedPnL || 0,
pnl: thisStatement.realizedPnL,
fees: thisStatement.fees,
};
});

Expand Down Expand Up @@ -176,7 +176,6 @@ export const prepareReport = (portfolio: Portfolio): Promise<ReportEntry[]> => {
let interestEntry: InterestsSummary | undefined;
let report = result.find((item) => item.year == year && item.month == month);
if (!report) {
// console.log(date, "creating report for ", year, month);
report = {
year,
month,
Expand Down
51 changes: 26 additions & 25 deletions src/routers/trades.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import {
OptionPositionEntry,
PositionEntry,
StatementEntry,
StatementOptionEntry,
StatementUnderlyingEntry,
StatementUnderlyingOption,
} from "./types";

const MODULE = "TradesRouter";
Expand All @@ -33,7 +33,7 @@ type parentParams = { portfolioId: number };

const initVirtualFromStatement = (item: StatementEntry): VirtualPositionEntry => {
let id: number;
let contract: StatementUnderlyingEntry | StatementOptionEntry;
let contract: StatementUnderlyingEntry | StatementUnderlyingOption;
switch (item.statementType) {
case StatementTypes.EquityStatement:
id = item.underlying!.id;
Expand Down Expand Up @@ -123,9 +123,10 @@ const updateTradeExpiry = (thisTrade: Trade, virtuals: Record<number, VirtualPos
thisTrade.expectedExpiry = null;
Object.values(virtuals).forEach((item) => {
if (item.contract.secType == ContractType.Option && item.quantity) {
if (!thisTrade.expectedExpiry) thisTrade.expectedExpiry = (item.contract as StatementOptionEntry).lastTradeDate;
else if (thisTrade.expectedExpiry < (item.contract as StatementOptionEntry).lastTradeDate)
thisTrade.expectedExpiry = (item.contract as StatementOptionEntry).lastTradeDate;
if (!thisTrade.expectedExpiry)
thisTrade.expectedExpiry = (item.contract as StatementUnderlyingOption).lastTradeDate;
else if (thisTrade.expectedExpiry < (item.contract as StatementUnderlyingOption).lastTradeDate)
thisTrade.expectedExpiry = (item.contract as StatementUnderlyingOption).lastTradeDate;
}
});
};
Expand Down Expand Up @@ -349,35 +350,35 @@ const computeComboRisk = (thisTrade: Trade, virtuals: Record<number, VirtualPosi
stocks[item.contract.id] = { contract: item.contract, cost: item.cost, quantity: item.quantity };
break;
case SecType.OPT:
if (item.quantity && !((item.contract as StatementOptionEntry).strike in limits))
limits.push((item.contract as StatementOptionEntry).strike);
switch ((item.contract as StatementOptionEntry).callOrPut) {
if (item.quantity && !((item.contract as StatementUnderlyingOption).strike in limits))
limits.push((item.contract as StatementUnderlyingOption).strike);
switch ((item.contract as StatementUnderlyingOption).callOrPut) {
case OptionType.Call:
if (calls[(item.contract as StatementOptionEntry).strike])
calls[(item.contract as StatementOptionEntry).strike] = {
cost: calls[(item.contract as StatementOptionEntry).strike].cost + item.cost,
if (calls[(item.contract as StatementUnderlyingOption).strike])
calls[(item.contract as StatementUnderlyingOption).strike] = {
cost: calls[(item.contract as StatementUnderlyingOption).strike].cost + item.cost,
quantity:
calls[(item.contract as StatementOptionEntry).strike].quantity +
item.quantity * (item.contract as StatementOptionEntry).multiplier,
calls[(item.contract as StatementUnderlyingOption).strike].quantity +
item.quantity * (item.contract as StatementUnderlyingOption).multiplier,
};
else
calls[(item.contract as StatementOptionEntry).strike] = {
calls[(item.contract as StatementUnderlyingOption).strike] = {
cost: item.cost,
quantity: item.quantity * (item.contract as StatementOptionEntry).multiplier,
quantity: item.quantity * (item.contract as StatementUnderlyingOption).multiplier,
};
break;
case OptionType.Put:
if (puts[(item.contract as StatementOptionEntry).strike])
puts[(item.contract as StatementOptionEntry).strike] = {
cost: puts[(item.contract as StatementOptionEntry).strike].cost + item.cost,
if (puts[(item.contract as StatementUnderlyingOption).strike])
puts[(item.contract as StatementUnderlyingOption).strike] = {
cost: puts[(item.contract as StatementUnderlyingOption).strike].cost + item.cost,
quantity:
puts[(item.contract as StatementOptionEntry).strike].quantity +
item.quantity * (item.contract as StatementOptionEntry).multiplier,
puts[(item.contract as StatementUnderlyingOption).strike].quantity +
item.quantity * (item.contract as StatementUnderlyingOption).multiplier,
};
else
puts[(item.contract as StatementOptionEntry).strike] = {
puts[(item.contract as StatementUnderlyingOption).strike] = {
cost: item.cost,
quantity: item.quantity * (item.contract as StatementOptionEntry).multiplier,
quantity: item.quantity * (item.contract as StatementUnderlyingOption).multiplier,
};
break;
}
Expand Down Expand Up @@ -451,7 +452,7 @@ const countContratTypes = (virtuals: Record<number, VirtualPositionEntry>): numb
else if (item.quantity < 0) short_stock += 1;
break;
case SecType.OPT:
switch ((item.contract as StatementOptionEntry).callOrPut) {
switch ((item.contract as StatementUnderlyingOption).callOrPut) {
case OptionType.Call:
if (item.quantity > 0) long_call += 1;
else if (item.quantity < 0) short_call += 1;
Expand Down Expand Up @@ -495,9 +496,9 @@ const computeTradeStrategy = (thisTrade: Trade, virtuals: Record<number, Virtual
.filter(
(item) =>
item.contract.secType == SecType.OPT &&
(item.contract as StatementOptionEntry).callOrPut == OptionType.Call,
(item.contract as StatementUnderlyingOption).callOrPut == OptionType.Call,
)
.reduce((p, v) => Math.min(p, (v.contract as StatementOptionEntry).strike), Infinity);
.reduce((p, v) => Math.min(p, (v.contract as StatementUnderlyingOption).strike), Infinity);
if (strike < stocks_cost / stocks_quantity) strategy = TradeStrategy["buy write"];
else strategy = TradeStrategy["covered short call"];
} else strategy = TradeStrategy["long stock"];
Expand Down
Loading

0 comments on commit 5ed05b5

Please sign in to comment.