import type { TbankShare } from '#src/integrations/tbank/tbank.api'; export type StockScreenerCatalog = { asOf: Date; instruments: TbankShare[]; }; export type StockScreenerConditions = { countryOfRisk?: string; maxLot?: number; query?: string; realExchange?: string; sector?: string; shareType?: string; }; type IdentifiedShare = TbankShare & { uid: string }; const sortBefore = 0; export function selectStockScreenerInstruments( catalog: StockScreenerCatalog, conditions: StockScreenerConditions, ): StockScreenerCatalog { const representatives = new Map(); for (const instrument of catalog.instruments) { if (!matches(instrument, conditions)) continue; const { uid } = instrument; const isin = instrument.isin?.trim().toUpperCase(); const issueKey = isin ? `isin:${isin}` : `uid:${uid}`; const existing = representatives.get(issueKey); if (!existing || uid.localeCompare(existing.uid) < sortBefore) { representatives.set(issueKey, instrument); } } return { asOf: catalog.asOf, instruments: [...representatives.values()].sort( (left, right) => (left.ticker?.trim() ?? '').localeCompare(right.ticker?.trim() ?? '') || left.uid.localeCompare(right.uid), ), }; } function matches( instrument: TbankShare, conditions: StockScreenerConditions, ): instrument is IdentifiedShare { if ( !instrument.uid?.trim() || instrument.buyAvailableFlag !== true || instrument.apiTradeAvailableFlag !== true ) { return false; } if ( conditions.query !== undefined && !`${instrument.name ?? ''}\n${instrument.ticker ?? ''}` .toLowerCase() .includes(conditions.query.toLowerCase()) ) { return false; } if (conditions.sector !== undefined && instrument.sector !== conditions.sector) { return false; } if ( conditions.shareType !== undefined && instrument.shareType !== conditions.shareType ) { return false; } if (instrument.currency?.toUpperCase() !== 'RUB') return false; if ( conditions.realExchange !== undefined && instrument.realExchange !== conditions.realExchange ) { return false; } if ( conditions.countryOfRisk !== undefined && instrument.countryOfRisk !== conditions.countryOfRisk ) { return false; } const minimumPositiveLot = 0; return ( conditions.maxLot === undefined || (Number.isInteger(instrument.lot) && instrument.lot !== undefined && instrument.lot > minimumPositiveLot && instrument.lot <= conditions.maxLot) ); }