111 lines
3.1 KiB
TypeScript
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),
|
|
};
|
|
}
|