import { Injectable } from '@nestjs/common'; import type { TbankShare } from '../../../../../integrations/tbank/tbank.api'; import { formatDecimal } from '../../../../../common/decimal'; import { decimal } from '../../../../../integrations/tbank/decimal'; import { CatalogDiscoveryService } from '../../application/catalog-discovery.service'; import { timestamp } from '../../application/instrument-market'; import { StockDto, StockResourceDto, StockSearchResourceDto, } from '../presentation/stock.dto'; const MIN_LOT_EXCLUSIVE = 0; type IdentifiedShare = TbankShare & { uid: string }; @Injectable() export class StockCatalogService { constructor(private readonly discovery: CatalogDiscoveryService) {} async search( userId: string, query: string, observe = false, ): Promise { const result = await this.discovery.search( userId, { instrumentType: 'share', query }, observe, ); return { ...result, data: result.data?.map((item) => { const short = stock(item); const detail = item.detail ? stock({ ...item.detail, uid: item.uid }) : short; const last = item.price; return { ...detail, classCode: detail.classCode ?? short.classCode, isin: detail.isin ?? short.isin, apiTradeAvailable: short.apiTradeAvailable, price: last?.price && detail.currency ? { currency: detail.currency, value: formatDecimal(decimal(last.price)), } : null, priceAt: timestamp(last?.time), }; }) ?? null, }; } async get( userId: string, uid: string, observe = false, ): Promise { const result = await this.discovery.detail(userId, 'share', uid, observe); return { ...result, data: result.data ? { ...stock(result.data), slug: result.data.slug } : null, }; } async getByTicker( userId: string, ticker: string, observe = false, ): Promise { const result = await this.discovery.byTicker( userId, 'share', ticker, observe, ); return { ...result, data: result.data ? { ...stock(result.data), slug: result.data.slug } : null, }; } } function stock(share: IdentifiedShare): StockDto { return { instrumentUid: share.uid, slug: share.uid, name: share.name?.trim() || share.ticker || share.uid, ticker: share.ticker?.trim() || null, isin: share.isin?.trim() || null, classCode: share.classCode?.trim() || null, realExchange: share.realExchange || null, apiTradeAvailable: share.apiTradeAvailableFlag ?? null, logoName: share.brand?.logoName?.trim() || null, currency: share.currency?.toUpperCase() || null, shareType: share.shareType || null, sector: share.sector || null, lot: share.lot && share.lot > MIN_LOT_EXCLUSIVE ? share.lot : null, historyFrom: timestamp(share.first_1dayCandleDate), }; }