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; }