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

118 lines
3.0 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import {
decimalFromString,
divide,
formatDecimal,
multiply,
subtract,
} from '../../../../../common/decimal';
import { ISO_DATE_LENGTH, ISO_DATE_START } from '../../../../../common/time';
import { decimal } from '../../../../../integrations/tbank/decimal';
import { CandleService } from '../../application/candle.service';
import {
CatalogEnrichmentService,
type CatalogPayload,
} from '../../application/catalog-enrichment.service';
import { timestamp } from '../../application/instrument-market';
import {
StockCandlesQueryDto,
StockQuoteDto,
StockQuoteResourceDto,
} from '../presentation/stock.dto';
const ZERO_AMOUNT = 0n;
const CHANGE_PERCENT_DECIMAL_PLACES = 6;
@Injectable()
export class StockMarketService {
constructor(
private readonly history: CandleService,
private readonly sources: CatalogEnrichmentService,
) {}
async quote(
userId: string,
uid: string,
observe = false,
): Promise<StockQuoteResourceDto> {
const result = await this.sources.section(
userId,
'share',
uid,
'quote',
observe,
);
return {
data: result.values.instrument
? stockQuote(result.values, result.unavailable, result.observedAt)
: null,
meta: result.meta,
};
}
candles(
userId: string,
uid: string,
period: StockCandlesQueryDto['period'] = 'month',
observe = false,
) {
return this.history.read(
userId,
{ uid, instrumentType: 'share', period },
observe,
);
}
}
export function stockQuote(
values: CatalogPayload,
unavailable: string[],
observedAt: string,
): StockQuoteDto {
const { price: last, candle: previous, tradingStatus = null } = values;
const share = values.instrument;
const priceAt = timestamp(last?.time);
const currency = share?.currency?.toUpperCase();
const price =
last?.price && currency
? { currency, value: formatDecimal(decimal(last.price)) }
: null;
const previousPrice =
previous?.close && currency
? { currency, value: formatDecimal(decimal(previous.close)) }
: null;
const difference =
price && previousPrice
? subtract(
decimalFromString(price.value),
decimalFromString(previousPrice.value),
)
: null;
return {
price,
priceAt,
tradingStatus,
previousCloseDate: timestamp(previous?.time)?.slice(ISO_DATE_START, ISO_DATE_LENGTH) ?? null,
previousClose: previousPrice,
change:
difference && currency
? { currency, value: formatDecimal(difference) }
: null,
changePercent:
difference &&
previousPrice &&
decimalFromString(previousPrice.value).value > ZERO_AMOUNT
? formatDecimal(
divide(
multiply(difference, decimalFromString('100')),
decimalFromString(previousPrice.value),
CHANGE_PERCENT_DECIMAL_PLACES,
),
)
: null,
unavailable,
observedAt,
};
}