Files
backend/src/modules/investments/performance/domain/performance-availability.ts
T
ksv741 3b55b611b5 refactor!: support RUB only (WealthLedger/main#55)
Filter non-RUB data at ingest, collapse per-currency collections into single RUB Money, drop currency from TbankPortfolioValuePoint, remove NON_RUB_ACTIVITY and screener currency filter.

BREAKING CHANGE: per-currency API fields replaced with single RUB Money; stock screener currency param removed.
2026-11-11 08:22:16 +03:00

35 lines
1.4 KiB
TypeScript

import { uncoveredOperationIntervals } from '../../operations/domain/operations-sync-plan';
const ZERO_MILLIS = 0;
const NEXT_MILLISECOND = 1;
export enum PerformanceUnavailableReason {
AccountValuationDatesDiffer = 'ACCOUNT_VALUATION_DATES_DIFFER',
AmbiguousReturn = 'AMBIGUOUS_RETURN',
HistoryStartUnknown = 'HISTORY_START_UNKNOWN',
IndexHistoryUnavailable = 'INDEX_HISTORY_UNAVAILABLE',
NoSolution = 'NO_SOLUTION',
OperationsHistoryIncomplete = 'OPERATIONS_HISTORY_INCOMPLETE',
PortfolioValuationIncomplete = 'PORTFOLIO_VALUATION_INCOMPLETE',
UnconfirmedTransfer = 'UNCONFIRMED_TRANSFER',
UnvaluedSecuritiesTransfer = 'UNVALUED_SECURITIES_TRANSFER',
}
export const performanceUnavailableReasons = Object.values(PerformanceUnavailableReason);
type Coverage = { asOf: Date; from: Date; to: Date };
/** Coverage is half-open and must include the instant of the last valuation. */
export function hasPerformanceCoverage(
accountId: string,
from: Date,
valuationAt: Date,
coverage: Coverage[],
): boolean {
if (from.valueOf() >= valuationAt.valueOf()) return false;
const end = new Date(valuationAt.valueOf() + NEXT_MILLISECOND);
if (!coverage.some((item) => item.asOf >= valuationAt && item.to > valuationAt)) return false;
const confirmed = coverage.map((item) => ({ accountId, from: item.from, to: item.to }));
return uncoveredOperationIntervals([{ accountId, from, to: end }], confirmed).length === ZERO_MILLIS;
}