Files
backend/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts
T

111 lines
3.1 KiB
TypeScript

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<StockSearchResourceDto> {
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<StockResourceDto> {
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<StockResourceDto> {
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),
};
}