81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import { Injectable, ServiceUnavailableException } from '@nestjs/common';
|
|
import { isZero } from '../../../common/decimal';
|
|
import {
|
|
TbankAccessService,
|
|
TbankClient,
|
|
} from '../../../integrations/tbank/tbank.api';
|
|
import { decimal } from '../../../integrations/tbank/decimal';
|
|
import { totalsByCurrency } from '../portfolio/portfolio.api';
|
|
import { BrokerageAccountDto } from './brokerage-account.dto';
|
|
|
|
@Injectable()
|
|
export class BrokerageAccountsService {
|
|
constructor(
|
|
private readonly access: TbankAccessService,
|
|
private readonly tbank: TbankClient,
|
|
) {}
|
|
|
|
async list(userId: string): Promise<BrokerageAccountDto[]> {
|
|
return this.access.withSupportedAccounts(
|
|
userId,
|
|
async ({ token, accounts }) =>
|
|
Promise.all(
|
|
accounts.map(async (account) => {
|
|
const [portfolioResult, positionsResult] = await Promise.allSettled(
|
|
[
|
|
this.tbank.getPortfolio(token, account.id),
|
|
this.tbank.getPositions(token, account.id),
|
|
],
|
|
);
|
|
if (
|
|
portfolioResult.status === 'rejected' &&
|
|
!isTbankUnavailable(portfolioResult.reason)
|
|
)
|
|
throw portfolioResult.reason;
|
|
if (
|
|
positionsResult.status === 'rejected' &&
|
|
!isTbankUnavailable(positionsResult.reason)
|
|
)
|
|
throw positionsResult.reason;
|
|
if (
|
|
portfolioResult.status === 'rejected' ||
|
|
positionsResult.status === 'rejected'
|
|
)
|
|
return { ...account, portfolioSummary: null };
|
|
const positions = portfolioResult.value.positions ?? [];
|
|
let stockPositionCount = 0;
|
|
let bondPositionCount = 0;
|
|
for (const position of positions) {
|
|
if (isZero(decimal(position.quantity ?? {}))) continue;
|
|
const type = position.instrumentType?.toLowerCase();
|
|
if (type === 'share') stockPositionCount += 1;
|
|
if (type === 'bond') bondPositionCount += 1;
|
|
}
|
|
return {
|
|
...account,
|
|
portfolioSummary: {
|
|
totalsByCurrency: totalsByCurrency(
|
|
positions,
|
|
positionsResult.value.money ?? [],
|
|
),
|
|
stockPositionCount,
|
|
bondPositionCount,
|
|
},
|
|
};
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
function isTbankUnavailable(error: unknown): boolean {
|
|
if (!(error instanceof ServiceUnavailableException)) return false;
|
|
const response = error.getResponse();
|
|
return (
|
|
typeof response === 'object' &&
|
|
response !== null &&
|
|
'code' in response &&
|
|
response.code === 'TBANK_UNAVAILABLE'
|
|
);
|
|
}
|