chore: strengthen backend eslint profile
This commit is contained in:
+20
-17
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import type { TbankFundamentals } from '../../../../../integrations/tbank/tbank.api';
|
||||
|
||||
import { CatalogEnrichmentService } from '../../application/catalog-enrichment.service';
|
||||
import {
|
||||
StockCompanyResourceDto,
|
||||
@@ -21,8 +23,8 @@ export class GetStockCompanyUseCase {
|
||||
observe,
|
||||
);
|
||||
return {
|
||||
data: result.values.instrument
|
||||
? {
|
||||
data: result.values.instrument ?
|
||||
{
|
||||
description: result.values.description ?? null,
|
||||
metrics: metrics(
|
||||
result.values.fundamentals ?? null,
|
||||
@@ -30,14 +32,14 @@ export class GetStockCompanyUseCase {
|
||||
),
|
||||
unavailable: result.unavailable,
|
||||
observedAt: result.observedAt,
|
||||
}
|
||||
: null,
|
||||
} :
|
||||
null,
|
||||
meta: result.meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
export function metrics(
|
||||
values: TbankFundamentals | null,
|
||||
values: null | TbankFundamentals,
|
||||
sector?: string,
|
||||
): StockMetricDto[] {
|
||||
if (!values) return [];
|
||||
@@ -49,30 +51,31 @@ export function metrics(
|
||||
][] = [
|
||||
['marketCapitalization', 'money', null],
|
||||
['peRatioTtm', 'ratio', 'TTM'],
|
||||
...(bank
|
||||
? ([
|
||||
...(bank ?
|
||||
([
|
||||
['priceToBookTtm', 'ratio', 'TTM'],
|
||||
['roe', 'percent', null],
|
||||
] as typeof selection)
|
||||
: ([
|
||||
] as typeof selection) :
|
||||
([
|
||||
['revenueTtm', 'money', 'TTM'],
|
||||
['ebitdaTtm', 'money', 'TTM'],
|
||||
] as typeof selection)),
|
||||
['netIncomeTtm', 'money', 'TTM'],
|
||||
...(!bank
|
||||
? ([
|
||||
...(!bank ?
|
||||
([
|
||||
['totalDebtMrq', 'money', 'MRQ'],
|
||||
['netDebtToEbitda', 'ratio', null],
|
||||
] as typeof selection)
|
||||
: []),
|
||||
] as typeof selection) :
|
||||
[]),
|
||||
];
|
||||
return selection.flatMap(([key, unit, period]) => {
|
||||
// eslint-disable-next-line security/detect-object-injection -- Keys come from the fixed metric selection declared above.
|
||||
const value = values[key];
|
||||
const currency = values.currency?.toUpperCase() || null;
|
||||
return typeof value === 'number' &&
|
||||
Number.isFinite(value) &&
|
||||
(unit !== 'money' || currency)
|
||||
? [
|
||||
(unit !== 'money' || currency) ?
|
||||
[
|
||||
{
|
||||
key,
|
||||
value,
|
||||
@@ -80,7 +83,7 @@ export function metrics(
|
||||
period,
|
||||
currency: unit === 'money' ? currency : null,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
] :
|
||||
[];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { CatalogDiscoveryService } from '../../application/catalog-discovery.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { formatDecimal } from '../../../../../common/decimal';
|
||||
|
||||
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,
|
||||
@@ -30,9 +32,9 @@ export class StockCatalogService {
|
||||
data:
|
||||
result.data?.map((item) => {
|
||||
const short = stock(item);
|
||||
const detail = item.detail
|
||||
? stock({ ...item.detail, uid: item.uid })
|
||||
: short;
|
||||
const detail = item.detail ?
|
||||
stock({ ...item.detail, uid: item.uid }) :
|
||||
short;
|
||||
const last = item.price;
|
||||
return {
|
||||
...detail,
|
||||
@@ -40,17 +42,18 @@ export class StockCatalogService {
|
||||
isin: detail.isin ?? short.isin,
|
||||
apiTradeAvailable: short.apiTradeAvailable,
|
||||
price:
|
||||
last?.price && detail.currency
|
||||
? {
|
||||
last?.price && detail.currency ?
|
||||
{
|
||||
currency: detail.currency,
|
||||
value: formatDecimal(decimal(last.price)),
|
||||
}
|
||||
: null,
|
||||
} :
|
||||
null,
|
||||
priceAt: timestamp(last?.time),
|
||||
};
|
||||
}) ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async get(
|
||||
userId: string,
|
||||
uid: string,
|
||||
@@ -59,11 +62,12 @@ export class StockCatalogService {
|
||||
const result = await this.discovery.detail(userId, 'share', uid, observe);
|
||||
return {
|
||||
...result,
|
||||
data: result.data
|
||||
? { ...stock(result.data), slug: result.data.slug }
|
||||
: null,
|
||||
data: result.data ?
|
||||
{ ...stock(result.data), slug: result.data.slug } :
|
||||
null,
|
||||
};
|
||||
}
|
||||
|
||||
async getByTicker(
|
||||
userId: string,
|
||||
ticker: string,
|
||||
@@ -77,9 +81,9 @@ export class StockCatalogService {
|
||||
);
|
||||
return {
|
||||
...result,
|
||||
data: result.data
|
||||
? { ...stock(result.data), slug: result.data.slug }
|
||||
: null,
|
||||
data: result.data ?
|
||||
{ ...stock(result.data), slug: result.data.slug } :
|
||||
null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
decimalFromString,
|
||||
formatDecimal,
|
||||
multiply,
|
||||
} from '../../../../../common/decimal';
|
||||
import { decimal } from '../../../../../integrations/tbank/decimal';
|
||||
import {
|
||||
TbankAccessService,
|
||||
type TbankDividend,
|
||||
} from '../../../../../integrations/tbank/tbank.api';
|
||||
import {
|
||||
PortfolioService,
|
||||
ForecastService,
|
||||
PortfolioService,
|
||||
} from '../../../portfolio/portfolio.api';
|
||||
import { CatalogEnrichmentService } from '../../application/catalog-enrichment.service';
|
||||
import { moscowDay, timestamp } from '../../application/instrument-market';
|
||||
import { instrumentPosition } from '../../application/instrument-position';
|
||||
import {
|
||||
StockDividendDto,
|
||||
StockDividendsDto,
|
||||
StockDividendsResourceDto,
|
||||
StockPositionDto,
|
||||
StockDividendDto,
|
||||
} from '../presentation/stock.dto';
|
||||
import { decimal } from '../../../../../integrations/tbank/decimal';
|
||||
const ISO_YEAR_LENGTH = 4;
|
||||
const INCOME_HISTORY_YEARS = 5;
|
||||
@Injectable()
|
||||
@@ -40,6 +41,7 @@ export class StockIncomeService {
|
||||
meta: { ...saved.meta, resourceKey: `stock/position/${uid}` },
|
||||
};
|
||||
}
|
||||
|
||||
async dividends(
|
||||
userId: string,
|
||||
uid: string,
|
||||
@@ -58,21 +60,22 @@ export class StockIncomeService {
|
||||
['detail', 'dividends'],
|
||||
{ from, to },
|
||||
);
|
||||
if (sources[0].meta.availability === 'complete' && !sources[0].instrument)
|
||||
if (sources[0].meta.availability === 'complete' && !sources[0].instrument) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
const events = sources[1].payload?.dividends;
|
||||
const position = portfolio.data
|
||||
? instrumentPosition(portfolio.data, uid, 'STOCK')
|
||||
: null;
|
||||
const projected = events
|
||||
? stockDividends(
|
||||
const position = portfolio.data ?
|
||||
instrumentPosition(portfolio.data, uid, 'STOCK') :
|
||||
null;
|
||||
const projected = events ?
|
||||
stockDividends(
|
||||
events,
|
||||
position,
|
||||
{ from, to, today },
|
||||
sources[1].meta.lastSuccessfulAt ?? new Date().toISOString(),
|
||||
!portfolio.data,
|
||||
)
|
||||
: undefined;
|
||||
) :
|
||||
undefined;
|
||||
const result = await this.forecasts.result({
|
||||
userId,
|
||||
revision: connection.revision,
|
||||
@@ -83,9 +86,9 @@ export class StockIncomeService {
|
||||
candidate:
|
||||
portfolio.data && sources[0].instrument ? projected : undefined,
|
||||
});
|
||||
return result.data || !projected
|
||||
? result
|
||||
: {
|
||||
return result.data || !projected ?
|
||||
result :
|
||||
{
|
||||
data: stockDividends(
|
||||
events ?? [],
|
||||
null,
|
||||
@@ -100,7 +103,7 @@ export class StockIncomeService {
|
||||
|
||||
export function stockDividends(
|
||||
events: TbankDividend[],
|
||||
position: StockPositionDto | null,
|
||||
position: null | StockPositionDto,
|
||||
window: { from: string; to: string; today: string },
|
||||
observedAt: string,
|
||||
positionUnavailable: boolean,
|
||||
@@ -109,9 +112,9 @@ export function stockDividends(
|
||||
const quantity =
|
||||
position &&
|
||||
position.accounts.length &&
|
||||
position.accounts.every((p) => decimalFromString(p.quantity).value > 0n)
|
||||
? decimalFromString(position.quantity)
|
||||
: null;
|
||||
position.accounts.every((p) => decimalFromString(p.quantity).value > 0n) ?
|
||||
decimalFromString(position.quantity) :
|
||||
null;
|
||||
const dividends: StockDividendDto[] = events
|
||||
.filter((event) => event.dividendType?.toLowerCase() !== 'cancelled')
|
||||
.map((event) => {
|
||||
@@ -120,29 +123,29 @@ export function stockDividends(
|
||||
const lastBuyDate = timestamp(event.lastBuyDate)?.slice(0, 10) ?? null;
|
||||
const currency = event.dividendNet?.currency?.toUpperCase();
|
||||
const perShare =
|
||||
currency && event.dividendNet
|
||||
? { currency, value: formatDecimal(decimal(event.dividendNet)) }
|
||||
: null;
|
||||
currency && event.dividendNet ?
|
||||
{ currency, value: formatDecimal(decimal(event.dividendNet)) } :
|
||||
null;
|
||||
// A future payment can have an already-fixed register: keep the event, but do not estimate entitlement from today's holding.
|
||||
const eventDate = paymentDate ?? recordDate;
|
||||
const timing: StockDividendDto['timing'] = eventDate
|
||||
? eventDate >= today
|
||||
? 'upcoming'
|
||||
: 'past'
|
||||
: 'undated';
|
||||
const timing: StockDividendDto['timing'] = eventDate ?
|
||||
eventDate >= today ?
|
||||
'upcoming' :
|
||||
'past' :
|
||||
'undated';
|
||||
const estimatedAmount =
|
||||
quantity &&
|
||||
perShare &&
|
||||
recordDate &&
|
||||
recordDate >= today &&
|
||||
timing === 'upcoming'
|
||||
? {
|
||||
timing === 'upcoming' ?
|
||||
{
|
||||
currency: perShare.currency,
|
||||
value: formatDecimal(
|
||||
multiply(quantity, decimalFromString(perShare.value)),
|
||||
),
|
||||
}
|
||||
: null;
|
||||
} :
|
||||
null;
|
||||
return {
|
||||
perShare,
|
||||
recordDate,
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { CandleService } from '../../application/candle.service';
|
||||
import {
|
||||
CatalogEnrichmentService,
|
||||
type CatalogPayload,
|
||||
} from '../../application/catalog-enrichment.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
decimalFromString,
|
||||
divide,
|
||||
@@ -12,6 +8,11 @@ import {
|
||||
subtract,
|
||||
} from '../../../../../common/decimal';
|
||||
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,
|
||||
@@ -26,6 +27,7 @@ export class StockMarketService {
|
||||
private readonly history: CandleService,
|
||||
private readonly sources: CatalogEnrichmentService,
|
||||
) {}
|
||||
|
||||
async quote(
|
||||
userId: string,
|
||||
uid: string,
|
||||
@@ -39,12 +41,13 @@ export class StockMarketService {
|
||||
observe,
|
||||
);
|
||||
return {
|
||||
data: result.values.instrument
|
||||
? stockQuote(result.values, result.unavailable, result.observedAt)
|
||||
: null,
|
||||
data: result.values.instrument ?
|
||||
stockQuote(result.values, result.unavailable, result.observedAt) :
|
||||
null,
|
||||
meta: result.meta,
|
||||
};
|
||||
}
|
||||
|
||||
candles(
|
||||
userId: string,
|
||||
uid: string,
|
||||
@@ -69,20 +72,20 @@ export function stockQuote(
|
||||
const priceAt = timestamp(last?.time);
|
||||
const currency = share?.currency?.toUpperCase();
|
||||
const price =
|
||||
last?.price && currency
|
||||
? { currency, value: formatDecimal(decimal(last.price)) }
|
||||
: null;
|
||||
last?.price && currency ?
|
||||
{ currency, value: formatDecimal(decimal(last.price)) } :
|
||||
null;
|
||||
const previousPrice =
|
||||
previous?.close && currency
|
||||
? { currency, value: formatDecimal(decimal(previous.close)) }
|
||||
: null;
|
||||
previous?.close && currency ?
|
||||
{ currency, value: formatDecimal(decimal(previous.close)) } :
|
||||
null;
|
||||
const difference =
|
||||
price && previousPrice
|
||||
? subtract(
|
||||
price && previousPrice ?
|
||||
subtract(
|
||||
decimalFromString(price.value),
|
||||
decimalFromString(previousPrice.value),
|
||||
)
|
||||
: null;
|
||||
) :
|
||||
null;
|
||||
return {
|
||||
price,
|
||||
priceAt,
|
||||
@@ -90,21 +93,21 @@ export function stockQuote(
|
||||
previousCloseDate: timestamp(previous?.time)?.slice(0, 10) ?? null,
|
||||
previousClose: previousPrice,
|
||||
change:
|
||||
difference && currency
|
||||
? { currency, value: formatDecimal(difference) }
|
||||
: null,
|
||||
difference && currency ?
|
||||
{ currency, value: formatDecimal(difference) } :
|
||||
null,
|
||||
changePercent:
|
||||
difference &&
|
||||
previousPrice &&
|
||||
decimalFromString(previousPrice.value).value > 0n
|
||||
? formatDecimal(
|
||||
decimalFromString(previousPrice.value).value > 0n ?
|
||||
formatDecimal(
|
||||
divide(
|
||||
multiply(difference, decimalFromString('100')),
|
||||
decimalFromString(previousPrice.value),
|
||||
CHANGE_PERCENT_DECIMAL_PLACES,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
) :
|
||||
null,
|
||||
unavailable,
|
||||
observedAt,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { previousClose } from '../../application/instrument-market';
|
||||
import { metrics } from './get-stock-company.use-case';
|
||||
import { stockDividends } from './stock-income.service';
|
||||
import { stockQuote } from './stock-market.service';
|
||||
import { previousClose } from '../../application/instrument-market';
|
||||
jest.mock('../../../../../prisma/prisma.service', () => ({
|
||||
PrismaService: class {},
|
||||
}));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { InvestmentReadQueryDto } from '../../../investment-resource.dto';
|
||||
import type { Request } from 'express';
|
||||
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
@@ -9,25 +10,26 @@ import {
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
|
||||
import { ApiProblemResponses } from '../../../../../common/decorators/api-safe-error-response.decorator';
|
||||
import { AccessPayload, JwtAuthGuard } from '../../../../auth/auth.api';
|
||||
import { InvestmentReadQueryDto } from '../../../investment-resource.dto';
|
||||
import { GetStockCompanyUseCase } from '../application/get-stock-company.use-case';
|
||||
import { StockCatalogService } from '../application/stock-catalog.service';
|
||||
import { StockIncomeService } from '../application/stock-income.service';
|
||||
import { StockMarketService } from '../application/stock-market.service';
|
||||
import {
|
||||
StockCandlesResourceDto,
|
||||
StockCandlesQueryDto,
|
||||
StockCandlesResourceDto,
|
||||
StockCompanyResourceDto,
|
||||
StockDividendsResourceDto,
|
||||
StockResourceDto,
|
||||
StockPositionResourceDto,
|
||||
StockQuoteResourceDto,
|
||||
StockResourceDto,
|
||||
StockSearchQueryDto,
|
||||
StockSearchResourceDto,
|
||||
StockTickerParamsDto,
|
||||
} from './stock.dto';
|
||||
import { StockCatalogService } from '../application/stock-catalog.service';
|
||||
|
||||
@ApiTags('stocks')
|
||||
@ApiProblemResponses()
|
||||
@@ -41,6 +43,7 @@ export class StockController {
|
||||
private readonly income: StockIncomeService,
|
||||
private readonly companyInfo: GetStockCompanyUseCase,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ApiOkResponse({ type: StockSearchResourceDto })
|
||||
search(
|
||||
@@ -53,6 +56,7 @@ export class StockController {
|
||||
query.sync === 'observe',
|
||||
);
|
||||
}
|
||||
|
||||
@Get('by-ticker/:ticker')
|
||||
@ApiOkResponse({ type: StockResourceDto })
|
||||
getByTicker(
|
||||
@@ -66,6 +70,7 @@ export class StockController {
|
||||
query.sync === 'observe',
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':uid')
|
||||
@ApiOkResponse({ type: StockResourceDto })
|
||||
get(
|
||||
@@ -75,6 +80,7 @@ export class StockController {
|
||||
) {
|
||||
return this.stocks.get(request.user.sub, uid, query.sync === 'observe');
|
||||
}
|
||||
|
||||
@Get(':uid/quote')
|
||||
@ApiOkResponse({ type: StockQuoteResourceDto })
|
||||
quote(
|
||||
@@ -84,6 +90,7 @@ export class StockController {
|
||||
) {
|
||||
return this.market.quote(request.user.sub, uid, query.sync === 'observe');
|
||||
}
|
||||
|
||||
@Get(':uid/candles')
|
||||
@ApiOkResponse({ type: StockCandlesResourceDto })
|
||||
candles(
|
||||
@@ -98,6 +105,7 @@ export class StockController {
|
||||
query.sync === 'observe',
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':uid/position')
|
||||
@ApiOkResponse({ type: StockPositionResourceDto })
|
||||
position(
|
||||
@@ -111,6 +119,7 @@ export class StockController {
|
||||
query.sync === 'observe',
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':uid/company')
|
||||
@ApiOkResponse({ type: StockCompanyResourceDto })
|
||||
company(
|
||||
@@ -124,6 +133,7 @@ export class StockController {
|
||||
query.sync === 'observe',
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':uid/dividends')
|
||||
@ApiOkResponse({ type: StockDividendsResourceDto })
|
||||
dividends(
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import {
|
||||
InvestmentReadQueryDto,
|
||||
InvestmentResourceMetaDto,
|
||||
} from '../../../investment-resource.dto';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
@@ -12,7 +8,12 @@ import {
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
|
||||
import { MoneyDto } from '../../../../../common/money.dto';
|
||||
import {
|
||||
InvestmentReadQueryDto,
|
||||
InvestmentResourceMetaDto,
|
||||
} from '../../../investment-resource.dto';
|
||||
import { PortfolioPositionDto } from '../../../portfolio/portfolio.api';
|
||||
|
||||
const MAX_INSTRUMENT_QUERY_LENGTH = 80;
|
||||
@@ -36,7 +37,7 @@ export class StockTickerParamsDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(MAX_INSTRUMENT_QUERY_LENGTH)
|
||||
@Matches(/^[A-Za-z0-9][A-Za-z0-9._@+-]*$/)
|
||||
@Matches(/^[A-Z0-9][\w.@+-]*$/i)
|
||||
declare ticker: string;
|
||||
}
|
||||
export class StockCandlesQueryDto extends InvestmentReadQueryDto {
|
||||
@@ -46,7 +47,7 @@ export class StockCandlesQueryDto extends InvestmentReadQueryDto {
|
||||
})
|
||||
@IsOptional()
|
||||
@IsIn(['month', 'halfYear', 'year', 'all'])
|
||||
period?: 'month' | 'halfYear' | 'year' | 'all';
|
||||
period?: 'all' | 'halfYear' | 'month' | 'year';
|
||||
}
|
||||
export class StockDto {
|
||||
@ApiProperty({ format: 'uuid' }) declare instrumentUid: string;
|
||||
@@ -54,48 +55,63 @@ export class StockDto {
|
||||
description: 'Unambiguous ticker URL segment, or the exact instrument UID.',
|
||||
})
|
||||
declare slug: string;
|
||||
|
||||
@ApiProperty() declare name: string;
|
||||
@ApiProperty({ type: String, nullable: true }) declare isin: string | null;
|
||||
@ApiProperty({ type: String, nullable: true }) declare isin: null | string;
|
||||
@ApiProperty({ type: String, nullable: true }) declare classCode:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare realExchange:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: Boolean, nullable: true }) declare apiTradeAvailable:
|
||||
boolean | null;
|
||||
@ApiProperty({ type: String, nullable: true }) declare ticker: string | null;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare ticker: null | string;
|
||||
@ApiProperty({ type: String, nullable: true }) declare logoName:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare currency:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare shareType:
|
||||
string | null;
|
||||
@ApiProperty({ type: String, nullable: true }) declare sector: string | null;
|
||||
@ApiProperty({ type: Number, nullable: true }) declare lot: number | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare sector: null | string;
|
||||
@ApiProperty({ type: Number, nullable: true }) declare lot: null | number;
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
declare historyFrom: string | null;
|
||||
declare historyFrom: null | string;
|
||||
}
|
||||
export class StockSearchResultDto extends StockDto {
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare price: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
declare priceAt: string | null;
|
||||
declare priceAt: null | string;
|
||||
}
|
||||
export class StockQuoteDto {
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare price: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
declare priceAt: string | null;
|
||||
declare priceAt: null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare tradingStatus:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare change: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare changePercent:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date' })
|
||||
declare previousCloseDate: string | null;
|
||||
declare previousCloseDate: null | string;
|
||||
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare previousClose: MoneyDto | null;
|
||||
@ApiProperty({ type: [String] }) declare unavailable: string[];
|
||||
|
||||
@ApiProperty({ type: String, isArray: true }) declare unavailable: string[];
|
||||
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
|
||||
}
|
||||
export class StockCandleDto {
|
||||
@@ -104,29 +120,36 @@ export class StockCandleDto {
|
||||
@ApiProperty() declare isComplete: boolean;
|
||||
}
|
||||
export class StockCandlesDto {
|
||||
@ApiProperty({ type: [StockCandleDto] }) declare candles: StockCandleDto[];
|
||||
@ApiProperty({ type: StockCandleDto, isArray: true }) declare candles: StockCandleDto[];
|
||||
@ApiProperty({ type: String, nullable: true }) declare currency:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
declare historyFrom: string | null;
|
||||
declare historyFrom: null | string;
|
||||
|
||||
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
|
||||
}
|
||||
export class StockPositionRowDto extends PortfolioPositionDto {
|
||||
@ApiProperty({ type: String, nullable: true }) declare returnPercent:
|
||||
string | null;
|
||||
null | string;
|
||||
}
|
||||
export class StockPositionDto {
|
||||
@ApiProperty() declare quantity: string;
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare currentValue: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare averagePurchasePrice: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare absoluteReturn: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare returnPercent:
|
||||
string | null;
|
||||
@ApiProperty({ type: [StockPositionRowDto] })
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: StockPositionRowDto, isArray: true })
|
||||
declare accounts: StockPositionRowDto[];
|
||||
|
||||
@ApiProperty() declare isStale: boolean;
|
||||
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
|
||||
}
|
||||
@@ -134,90 +157,108 @@ export class StockMetricDto {
|
||||
@ApiProperty() declare key: string;
|
||||
@ApiProperty() declare value: number;
|
||||
@ApiProperty({ enum: ['money', 'ratio', 'percent'] }) declare unit:
|
||||
'money' | 'ratio' | 'percent';
|
||||
'money' | 'percent' | 'ratio';
|
||||
|
||||
@ApiProperty({ type: String, nullable: true }) declare currency:
|
||||
string | null;
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, enum: ['TTM', 'MRQ'] })
|
||||
declare period: 'TTM' | 'MRQ' | null;
|
||||
declare period: 'MRQ' | 'TTM' | null;
|
||||
}
|
||||
export class StockCompanyDto {
|
||||
@ApiProperty({ type: String, nullable: true }) declare description:
|
||||
string | null;
|
||||
@ApiProperty({ type: [StockMetricDto] }) declare metrics: StockMetricDto[];
|
||||
@ApiProperty({ type: [String] }) declare unavailable: string[];
|
||||
null | string;
|
||||
|
||||
@ApiProperty({ type: StockMetricDto, isArray: true }) declare metrics: StockMetricDto[];
|
||||
@ApiProperty({ type: String, isArray: true }) declare unavailable: string[];
|
||||
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
|
||||
}
|
||||
export class StockDividendDto {
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare perShare: MoneyDto | null;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date' })
|
||||
declare recordDate: string | null;
|
||||
declare recordDate: null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date' })
|
||||
declare lastBuyDate: string | null;
|
||||
declare lastBuyDate: null | string;
|
||||
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date' })
|
||||
declare paymentDate: string | null;
|
||||
declare paymentDate: null | string;
|
||||
|
||||
@ApiProperty({ enum: ['upcoming', 'past', 'undated'] }) declare timing:
|
||||
'upcoming' | 'past' | 'undated';
|
||||
'past' | 'undated' | 'upcoming';
|
||||
|
||||
@ApiProperty({ type: MoneyDto, nullable: true })
|
||||
declare estimatedAmount: MoneyDto | null;
|
||||
}
|
||||
export class StockDividendsDto {
|
||||
@ApiProperty({ type: [StockDividendDto] })
|
||||
@ApiProperty({ type: StockDividendDto, isArray: true })
|
||||
declare dividends: StockDividendDto[];
|
||||
|
||||
@ApiProperty({ format: 'date' }) declare from: string;
|
||||
@ApiProperty({ format: 'date' }) declare to: string;
|
||||
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
declare positionAt: string | null;
|
||||
declare positionAt: null | string;
|
||||
|
||||
@ApiProperty() declare estimateUnavailable: boolean;
|
||||
@ApiProperty() declare positionIsStale: boolean;
|
||||
}
|
||||
|
||||
export class StockPositionResourceDto {
|
||||
@ApiProperty({ type: StockPositionDto, nullable: true })
|
||||
declare data: StockPositionDto | null;
|
||||
declare data: null | StockPositionDto;
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
export class StockSearchResourceDto {
|
||||
// eslint-disable-next-line @darraghor/nestjs-typed/api-property-returning-array-should-set-array -- Nullable array union requires isArray in the explicit wire schema.
|
||||
@ApiProperty({ type: StockSearchResultDto, isArray: true, nullable: true })
|
||||
declare data: StockSearchResultDto[] | null;
|
||||
declare data: null | StockSearchResultDto[];
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
export class StockResourceDto {
|
||||
@ApiProperty({ type: StockDto, nullable: true })
|
||||
declare data: StockDto | null;
|
||||
declare data: null | StockDto;
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
export class StockQuoteResourceDto {
|
||||
@ApiProperty({ type: StockQuoteDto, nullable: true })
|
||||
declare data: StockQuoteDto | null;
|
||||
declare data: null | StockQuoteDto;
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
export class StockCompanyResourceDto {
|
||||
@ApiProperty({ type: StockCompanyDto, nullable: true })
|
||||
declare data: StockCompanyDto | null;
|
||||
declare data: null | StockCompanyDto;
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
export class StockCandlesResourceDto {
|
||||
@ApiProperty({ type: StockCandlesDto, nullable: true })
|
||||
declare data: StockCandlesDto | null;
|
||||
declare data: null | StockCandlesDto;
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
export class StockDividendsResourceDto {
|
||||
@ApiProperty({ type: StockDividendsDto, nullable: true })
|
||||
declare data: StockDividendsDto | null;
|
||||
declare data: null | StockDividendsDto;
|
||||
|
||||
@ApiProperty({ type: InvestmentResourceMetaDto })
|
||||
declare meta: InvestmentResourceMetaDto;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user