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.
This commit is contained in:
2026-11-11 08:22:16 +03:00
parent fee61537b6
commit 3b55b611b5
60 changed files with 624 additions and 736 deletions
@@ -17,14 +17,13 @@ import {
PerformanceReturnDto, PerformanceSeriesPointDto,
} from './presentation/performance.dto';
const RUB = 'RUB';
const DAY_MS = 86_400_000;
const DAYS_IN_YEAR = 365;
const RATE_SCALE = 6;
const MONEY_SCALE = 2;
const RUB = 'RUB';
const ZERO = 0;
const ONE = 1;
const ZERO_UNITS = 0n;
const ZERO_DECIMAL = decimalFromString('0');
const LAST_FLOW = -1;
const ONE_DECIMAL = decimalFromString('1');
@@ -164,7 +163,7 @@ export class PerformanceService {
const portfolio = account.portfolio;
if (!portfolio) return fail(PerformanceUnavailableReason.PortfolioValuationIncomplete);
const snapshot = portfolio.payload as { positions: { currentValue: unknown }[];
totalsByCurrency: { currency: string; value: string }[]; };
totals: { value: string }; };
if (!isCompleteValuation(snapshot)) return fail(PerformanceUnavailableReason.PortfolioValuationIncomplete);
if (!hasPerformanceCoverage(account.accountId, account.historyFrom, portfolio.fetchedAt, account.coverage)) {
return fail(PerformanceUnavailableReason.OperationsHistoryIncomplete);
@@ -172,16 +171,10 @@ export class PerformanceService {
const operations = account.operations.filter((op) => op.occurredAt <= portfolio.fetchedAt)
.map((op) => ({ ...op, accountId: account.accountId }));
const parsed = readPerformanceOperations(operations);
if (parsed.nonRubAccounts.size || snapshot.totalsByCurrency.some((row) =>
row.currency !== RUB && decimalFromString(row.value).value !== ZERO_UNITS)) {
return fail(PerformanceUnavailableReason.NonRubActivity);
}
if (parsed.missingAmountAccounts.size) return fail(PerformanceUnavailableReason.OperationsHistoryIncomplete);
if (parsed.unvaluedAccounts.size) return fail(PerformanceUnavailableReason.UnvaluedSecuritiesTransfer);
const value = snapshot.totalsByCurrency.find((row) => row.currency === RUB);
if (!value) return fail(PerformanceUnavailableReason.PortfolioValuationIncomplete);
return {
included: { account, flows: parsed.flows, value: decimalFromString(value.value),
included: { account, flows: parsed.flows, value: decimalFromString(snapshot.totals.value),
to: moscowDate(portfolio.fetchedAt) },
reason: null,
};