refactor: centralize domain values and enforce numeric lint
This commit is contained in:
@@ -19,6 +19,8 @@ import { BondDto, BondSearchResultDto } from '../presentation/bond.dto';
|
||||
type IdentifiedBond = TbankBond & { uid: string };
|
||||
|
||||
import { money } from '../infrastructure/bond-money.mapper';
|
||||
|
||||
const SEARCH_RESULTS_LIMIT = 30;
|
||||
@Injectable()
|
||||
export class BondCatalogService {
|
||||
private readonly cache = new LRUCache<string, Promise<object>>({
|
||||
@@ -33,7 +35,7 @@ export class BondCatalogService {
|
||||
return this.cached(userId, `search:${query}`, async (connection) => {
|
||||
const { token } = connection;
|
||||
const found = await this.tbank.findBonds(token, query);
|
||||
const bonds = selectBonds(found, query).slice(0, 30);
|
||||
const bonds = selectBonds(found, query).slice(0, SEARCH_RESULTS_LIMIT);
|
||||
if (!bonds.length) return [];
|
||||
// Batch prices and share metadata with detail sections. Resolve readable
|
||||
// aliases only when an issue is opened, not for every search result.
|
||||
|
||||
@@ -27,6 +27,10 @@ import {
|
||||
import { BondMarketService } from './bond-market.service';
|
||||
import { money } from '../infrastructure/bond-money.mapper';
|
||||
import { BondCatalogService } from './bond-catalog.service';
|
||||
|
||||
const ISO_YEAR_LENGTH = 4;
|
||||
const INCOME_HISTORY_YEARS = 5;
|
||||
const COUPON_YIELD_DECIMAL_PLACES = 4;
|
||||
@Injectable()
|
||||
export class BondIncomeService {
|
||||
constructor(
|
||||
@@ -42,8 +46,8 @@ export class BondIncomeService {
|
||||
const instrument = await this.catalog.bond(userId, connection, uid);
|
||||
const { ticker, isin, classCode } = instrument;
|
||||
const today = moscowDay(new Date().toISOString());
|
||||
const from = `${Number(today.slice(0, 4)) - 5}${today.slice(4)}`;
|
||||
const to = `${Number(today.slice(0, 4)) + 1}${today.slice(4)}`;
|
||||
const from = `${Number(today.slice(0, ISO_YEAR_LENGTH)) - INCOME_HISTORY_YEARS}${today.slice(ISO_YEAR_LENGTH)}`;
|
||||
const to = `${Number(today.slice(0, ISO_YEAR_LENGTH)) + 1}${today.slice(ISO_YEAR_LENGTH)}`;
|
||||
const unavailable: string[] = [];
|
||||
const [events, quote, effectiveYield, creditRating] = await Promise.all([
|
||||
optional(
|
||||
@@ -94,7 +98,7 @@ export class BondIncomeService {
|
||||
decimalFromString(String(couponPeriodDays)),
|
||||
decimalFromString(clean.value),
|
||||
),
|
||||
4,
|
||||
COUPON_YIELD_DECIMAL_PLACES,
|
||||
),
|
||||
)
|
||||
: null;
|
||||
@@ -137,8 +141,8 @@ export class BondIncomeService {
|
||||
return this.catalog.cached(userId, `coupons:${uid}`, async (connection) => {
|
||||
await this.catalog.bond(userId, connection, uid);
|
||||
const today = moscowDay(new Date().toISOString());
|
||||
const from = `${Number(today.slice(0, 4)) - 5}${today.slice(4)}`;
|
||||
const to = `${Number(today.slice(0, 4)) + 1}${today.slice(4)}`;
|
||||
const from = `${Number(today.slice(0, ISO_YEAR_LENGTH)) - INCOME_HISTORY_YEARS}${today.slice(ISO_YEAR_LENGTH)}`;
|
||||
const to = `${Number(today.slice(0, ISO_YEAR_LENGTH)) + 1}${today.slice(ISO_YEAR_LENGTH)}`;
|
||||
const unavailable: string[] = [];
|
||||
const [events, position] = await Promise.all([
|
||||
this.couponEvents(userId, connection, uid, from, to),
|
||||
|
||||
@@ -24,6 +24,12 @@ const dayMs = 86_400_000;
|
||||
|
||||
import { money } from '../infrastructure/bond-money.mapper';
|
||||
import { BondCatalogService } from './bond-catalog.service';
|
||||
|
||||
const PREVIOUS_CLOSE_LOOKBACK_DAYS = 20;
|
||||
const HALF_YEAR_MONTHS = 6;
|
||||
const YEAR_MONTHS = 12;
|
||||
const CANDLE_WINDOW_DAYS = 365;
|
||||
const PRICE_DECIMAL_PLACES = 9;
|
||||
@Injectable()
|
||||
export class BondMarketService {
|
||||
constructor(
|
||||
@@ -66,7 +72,7 @@ export class BondMarketService {
|
||||
decimalFromString(nominal.value),
|
||||
),
|
||||
decimalFromString('100'),
|
||||
9,
|
||||
PRICE_DECIMAL_PLACES,
|
||||
),
|
||||
),
|
||||
}
|
||||
@@ -90,7 +96,9 @@ export class BondMarketService {
|
||||
await this.tbank.getCandles(
|
||||
token,
|
||||
uid,
|
||||
new Date(Date.parse(priceAt) - 20 * dayMs),
|
||||
new Date(
|
||||
Date.parse(priceAt) - PREVIOUS_CLOSE_LOOKBACK_DAYS * dayMs,
|
||||
),
|
||||
new Date(priceAt),
|
||||
),
|
||||
priceAt,
|
||||
@@ -134,7 +142,11 @@ export class BondMarketService {
|
||||
const from = new Date(to);
|
||||
from.setUTCMonth(
|
||||
from.getUTCMonth() -
|
||||
(period === 'month' ? 1 : period === 'halfYear' ? 6 : 12),
|
||||
(period === 'month'
|
||||
? 1
|
||||
: period === 'halfYear'
|
||||
? HALF_YEAR_MONTHS
|
||||
: YEAR_MONTHS),
|
||||
);
|
||||
if (period === 'all') {
|
||||
if (!historyFrom)
|
||||
@@ -147,7 +159,10 @@ export class BondMarketService {
|
||||
const collected = new Map<string, BondCandlesDto['candles'][number]>();
|
||||
// Conservative one-year windows also work with older deployed RPC limits.
|
||||
for (let start = from.valueOf(); start < to.valueOf();) {
|
||||
const end = Math.min(start + 365 * dayMs, to.valueOf());
|
||||
const end = Math.min(
|
||||
start + CANDLE_WINDOW_DAYS * dayMs,
|
||||
to.valueOf(),
|
||||
);
|
||||
for (const candle of await this.tbank.getCandles(
|
||||
token,
|
||||
uid,
|
||||
|
||||
Reference in New Issue
Block a user