import { add, decimalFromString, divide, formatDecimal, multiply, } from '../../../../common/decimal'; import { MoneyDto } from '../../../../common/money.dto'; import { PortfolioDto, PortfolioPositionDto, } from '../../portfolio/portfolio.api'; import { StockPositionDto } from '../stocks/presentation/stock.dto'; const PRICE_DECIMAL_PLACES = 9; const RETURN_PERCENT_DECIMAL_PLACES = 6; export function instrumentPosition( portfolio: PortfolioDto, uid: string, type: 'STOCK' | 'BOND', ): StockPositionDto { const positions = portfolio.positions.filter( (p) => p.type === type && p.instrumentUid === uid, ); const zero = decimalFromString('0'); const quantity = positions .map((p) => decimalFromString(p.quantity)) .reduce(add, zero); const costs = positions.map(cost); const totalCost = sumComplete(costs); const currentValue = sumComplete(positions.map((p) => p.currentValue)); const absoluteReturn = sumComplete(positions.map((p) => p.absoluteReturn)); const positive = positions.length > 0 && positions.every((p) => decimalFromString(p.quantity).value > 0n); return { quantity: formatDecimal(quantity), currentValue, absoluteReturn, averagePurchasePrice: positive && totalCost ? { currency: totalCost.currency, value: formatDecimal( divide( decimalFromString(totalCost.value), quantity, PRICE_DECIMAL_PLACES, ), ), } : null, returnPercent: positive ? percent(absoluteReturn, totalCost) : null, accounts: positions.map((p) => ({ ...p, returnPercent: decimalFromString(p.quantity).value > 0n ? percent(p.absoluteReturn, cost(p)) : null, })), isStale: portfolio.isStale, observedAt: portfolio.lastSuccessfulAt, }; } function cost(position: PortfolioPositionDto): MoneyDto | null { const price = position.averagePurchasePrice; return price ? { currency: price.currency, value: formatDecimal( multiply( decimalFromString(price.value), decimalFromString(position.quantity), ), ), } : null; } function sumComplete(values: (MoneyDto | null)[]): MoneyDto | null { const currency = values[0]?.currency; if ( !currency || !values.every((v): v is MoneyDto => v !== null && v.currency === currency) ) return null; return { currency, value: formatDecimal( values.reduce( (sum, value) => add(sum, decimalFromString(value.value)), decimalFromString('0'), ), ), }; } function percent( result: MoneyDto | null, base: MoneyDto | null, ): string | null { if ( !base || !result || base.currency !== result.currency || decimalFromString(base.value).value <= 0n ) return null; return formatDecimal( divide( multiply(decimalFromString(result.value), decimalFromString('100')), decimalFromString(base.value), RETURN_PERCENT_DECIMAL_PLACES, ), ); }