From 5aa9bd395a8e49e423764eb7c4a88afe56332ad4 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Mon, 17 Aug 2026 13:34:38 +0300 Subject: [PATCH] feat: expose instrument isin in portfolio positions --- src/tbank/portfolio.dto.ts | 3 +++ src/tbank/portfolio.service.spec.ts | 4 +++- src/tbank/portfolio.service.ts | 5 +++-- src/tbank/tbank.client.ts | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tbank/portfolio.dto.ts b/src/tbank/portfolio.dto.ts index 5e2b295..c1063fa 100644 --- a/src/tbank/portfolio.dto.ts +++ b/src/tbank/portfolio.dto.ts @@ -41,6 +41,9 @@ export class PortfolioPositionDto { @ApiProperty({ type: MoneyDto, nullable: true }) absoluteReturn: MoneyDto | null; + + @ApiProperty({ type: String, nullable: true }) + isin: string | null; } export class PortfolioDto { diff --git a/src/tbank/portfolio.service.spec.ts b/src/tbank/portfolio.service.spec.ts index 478e7a3..568e587 100644 --- a/src/tbank/portfolio.service.spec.ts +++ b/src/tbank/portfolio.service.spec.ts @@ -88,7 +88,7 @@ describe('PortfolioService', () => { ], }); tbank.getInstrumentByFigi - .mockResolvedValueOnce({ ticker: 'ONE', name: 'Первая позиция' }) + .mockResolvedValueOnce({ ticker: 'ONE', name: 'Первая позиция', isin: 'RU000A0JX0J2' }) .mockRejectedValueOnce(new Error('unavailable')); await expect(service.get('user', 'account')).resolves.toMatchObject({ @@ -100,6 +100,7 @@ describe('PortfolioService', () => { quantity: '2', currentValue: { currency: 'RUB', value: '6' }, absoluteReturn: { currency: 'RUB', value: '2' }, + isin: 'RU000A0JX0J2', }, { figi: 'TWO', @@ -107,6 +108,7 @@ describe('PortfolioService', () => { type: 'OTHER', quantity: '-0.5', currentValue: { currency: 'USD', value: '-2' }, + isin: null, }, ], cashBalances: [{ currency: 'RUB', value: '1.5' }], diff --git a/src/tbank/portfolio.service.ts b/src/tbank/portfolio.service.ts index 6e62083..3e54aa2 100644 --- a/src/tbank/portfolio.service.ts +++ b/src/tbank/portfolio.service.ts @@ -44,7 +44,7 @@ const types: Record = { type CachedSnapshot = { snapshot: PortfolioDto; storedAt: number }; type CachedMetadata = { - value: { ticker?: string; name?: string } | null; + value: { ticker?: string; name?: string; isin?: string } | null; storedAt: number; }; @@ -209,6 +209,7 @@ export class PortfolioService { currentValue, averagePurchasePrice, absoluteReturn, + isin: metadata?.isin?.trim() || null, }; } @@ -236,7 +237,7 @@ export class PortfolioService { ) { const lookup = async ( key: string, - fetch: () => Promise<{ ticker?: string; name?: string } | null>, + fetch: () => Promise<{ ticker?: string; name?: string; isin?: string } | null>, ) => { const cached = this.metadata.get(key); if (cached && Date.now() - cached.storedAt <= metadataForMs) diff --git a/src/tbank/tbank.client.ts b/src/tbank/tbank.client.ts index a8b2e73..0074f07 100644 --- a/src/tbank/tbank.client.ts +++ b/src/tbank/tbank.client.ts @@ -44,7 +44,7 @@ export type TbankPortfolio = { }[]; }; export type TbankPositions = { money?: TbankMoney[] }; -export type TbankInstrument = { ticker?: string; name?: string }; +export type TbankInstrument = { ticker?: string; name?: string; isin?: string }; export type TbankTimestamp = { seconds?: string | number; nanos?: number }; export type TbankOperationItem = { cursor?: string;