Browse Source

Add ability to search for indices and fix gf-symbol-autocomplete validation (#2094)

* Bugfix/Fix gf-symbol-autocomplete validation

* Feature/Add ability to search for indices

* Update changelog
pull/2104/head^2
Arghya Ghosh 2 years ago
committed by GitHub
parent
commit
6a802a62a0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      CHANGELOG.md
  2. 4
      apps/api/src/app/symbol/symbol.controller.ts
  3. 3
      apps/api/src/app/symbol/symbol.service.ts
  4. 10
      apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts
  5. 15
      apps/api/src/services/data-provider/coingecko/coingecko.service.ts
  6. 9
      apps/api/src/services/data-provider/data-provider.service.ts
  7. 12
      apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts
  8. 10
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  9. 12
      apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts
  10. 8
      apps/api/src/services/data-provider/interfaces/data-provider.interface.ts
  11. 12
      apps/api/src/services/data-provider/manual/manual.service.ts
  12. 8
      apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts
  13. 18
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
  14. 5
      apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html
  15. 16
      apps/client/src/app/services/data.service.ts
  16. 10
      libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts

10
CHANGELOG.md

@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Added the ability to add an index for benchmarks as an asset profile in the admin control panel
### Fixed
- Fixed an issue with the clone functionality of a transaction caused by the symbol search component
## 1.283.5 - 2023-06-25
### Added

4
apps/api/src/app/symbol/symbol.controller.ts

@ -36,10 +36,12 @@ export class SymbolController {
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async lookupSymbol(
@Query() { query = '' }
@Query('includeIndices') includeIndices: boolean = false,
@Query('query') query = ''
): Promise<{ items: LookupItem[] }> {
try {
return this.symbolService.lookup({
includeIndices,
query: query.toLowerCase(),
user: this.request.user
});

3
apps/api/src/app/symbol/symbol.service.ts

@ -81,9 +81,11 @@ export class SymbolService {
}
public async lookup({
includeIndices = false,
query,
user
}: {
includeIndices?: boolean;
query: string;
user: UserWithSettings;
}): Promise<{ items: LookupItem[] }> {
@ -95,6 +97,7 @@ export class SymbolService {
try {
const { items } = await this.dataProviderService.search({
includeIndices,
query,
user
});

10
apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts

@ -114,8 +114,14 @@ export class AlphaVantageService implements DataProviderInterface {
return undefined;
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
const result = await this.alphaVantage.data.search(aQuery);
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
const result = await this.alphaVantage.data.search(query);
return {
items: result?.bestMatches?.map((bestMatch) => {

15
apps/api/src/services/data-provider/coingecko/coingecko.service.ts

@ -164,16 +164,17 @@ export class CoinGeckoService implements DataProviderInterface {
return 'bitcoin';
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
let items: LookupItem[] = [];
try {
const get = bent(
`${this.URL}/search?query=${aQuery}`,
'GET',
'json',
200
);
const get = bent(`${this.URL}/search?query=${query}`, 'GET', 'json', 200);
const { coins } = await get();
items = coins.map(({ id: symbol, name }) => {

9
apps/api/src/services/data-provider/data-provider.service.ts

@ -367,9 +367,11 @@ export class DataProviderService {
}
public async search({
includeIndices = false,
query,
user
}: {
includeIndices?: boolean;
query: string;
user: UserWithSettings;
}): Promise<{ items: LookupItem[] }> {
@ -392,7 +394,12 @@ export class DataProviderService {
}
for (const dataSource of dataSources) {
promises.push(this.getDataProvider(DataSource[dataSource]).search(query));
promises.push(
this.getDataProvider(DataSource[dataSource]).search({
includeIndices,
query
})
);
}
const searchResults = await Promise.all(promises);

12
apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts

@ -156,7 +156,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
return !symbol.endsWith('.FOREX');
})
.map((symbol) => {
return this.search(symbol);
return this.search({ query: symbol });
})
);
@ -219,8 +219,14 @@ export class EodHistoricalDataService implements DataProviderInterface {
return 'AAPL.US';
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
const searchResult = await this.getSearchResult(aQuery);
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
const searchResult = await this.getSearchResult(query);
return {
items: searchResult

10
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -143,12 +143,18 @@ export class FinancialModelingPrepService implements DataProviderInterface {
return 'AAPL';
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
let items: LookupItem[] = [];
try {
const get = bent(
`${this.URL}/search?query=${aQuery}&apikey=${this.apiKey}`,
`${this.URL}/search?query=${query}&apikey=${this.apiKey}`,
'GET',
'json',
200

12
apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts

@ -153,7 +153,13 @@ export class GoogleSheetsService implements DataProviderInterface {
return 'INDEXSP:.INX';
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
const items = await this.prismaService.symbolProfile.findMany({
select: {
assetClass: true,
@ -169,14 +175,14 @@ export class GoogleSheetsService implements DataProviderInterface {
dataSource: this.getName(),
name: {
mode: 'insensitive',
startsWith: aQuery
startsWith: query
}
},
{
dataSource: this.getName(),
symbol: {
mode: 'insensitive',
startsWith: aQuery
startsWith: query
}
}
]

8
apps/api/src/services/data-provider/interfaces/data-provider.interface.ts

@ -42,5 +42,11 @@ export interface DataProviderInterface {
getTestSymbol(): string;
search(aQuery: string): Promise<{ items: LookupItem[] }>;
search({
includeIndices,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }>;
}

12
apps/api/src/services/data-provider/manual/manual.service.ts

@ -171,7 +171,13 @@ export class ManualService implements DataProviderInterface {
return undefined;
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
let items = await this.prismaService.symbolProfile.findMany({
select: {
assetClass: true,
@ -187,14 +193,14 @@ export class ManualService implements DataProviderInterface {
dataSource: this.getName(),
name: {
mode: 'insensitive',
startsWith: aQuery
startsWith: query
}
},
{
dataSource: this.getName(),
symbol: {
mode: 'insensitive',
startsWith: aQuery
startsWith: query
}
}
]

8
apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts

@ -117,7 +117,13 @@ export class RapidApiService implements DataProviderInterface {
return undefined;
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
return { items: [] };
}

18
apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts

@ -275,11 +275,23 @@ export class YahooFinanceService implements DataProviderInterface {
return 'AAPL';
}
public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
public async search({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}): Promise<{ items: LookupItem[] }> {
const items: LookupItem[] = [];
try {
const searchResult = await yahooFinance.search(aQuery);
const quoteTypes = ['EQUITY', 'ETF', 'FUTURE', 'MUTUALFUND'];
if (includeIndices) {
quoteTypes.push('INDEX');
}
const searchResult = await yahooFinance.search(query);
const quotes = searchResult.quotes
.filter((quote) => {
@ -295,7 +307,7 @@ export class YahooFinanceService implements DataProviderInterface {
this.baseCurrency
)
)) ||
['EQUITY', 'ETF', 'FUTURE', 'MUTUALFUND'].includes(quoteType)
quoteTypes.includes(quoteType)
);
})
.filter(({ quoteType, symbol }) => {

5
apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html

@ -8,7 +8,10 @@
<div class="flex-grow-1 py-3" mat-dialog-content>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Name, symbol or ISIN</mat-label>
<gf-symbol-autocomplete formControlName="searchSymbol" />
<gf-symbol-autocomplete
formControlName="searchSymbol"
[includeIndices]="true"
/>
</mat-form-field>
</div>
<div class="d-flex justify-content-end" mat-dialog-actions>

16
apps/client/src/app/services/data.service.ts

@ -261,9 +261,21 @@ export class DataService {
});
}
public fetchSymbols(aQuery: string) {
public fetchSymbols({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}) {
let params = new HttpParams().set('query', query);
if (includeIndices) {
params = params.append('includeIndices', includeIndices);
}
return this.http
.get<{ items: LookupItem[] }>(`/api/v1/symbol/lookup?query=${aQuery}`)
.get<{ items: LookupItem[] }>('/api/v1/symbol/lookup', { params })
.pipe(
map((respose) => {
return respose.items;

10
libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts

@ -50,6 +50,7 @@ export class SymbolAutocompleteComponent
extends AbstractMatFormField<LookupItem>
implements OnInit, OnDestroy
{
@Input() private includeIndices = false;
@Input() public isLoading = false;
@ViewChild(MatInput, { static: false }) private input: MatInput;
@ -94,7 +95,10 @@ export class SymbolAutocompleteComponent
this.changeDetectorRef.markForCheck();
}),
switchMap((query: string) => {
return this.dataService.fetchSymbols(query);
return this.dataService.fetchSymbols({
query,
includeIndices: this.includeIndices
});
})
)
.subscribe((filteredLookupItems) => {
@ -132,7 +136,11 @@ export class SymbolAutocompleteComponent
public ngDoCheck() {
if (this.ngControl) {
this.validateRequired();
if (this.control.touched) {
this.validateSelection();
}
this.errorState = this.ngControl.invalid && this.ngControl.touched;
this.stateChanges.next();
}

Loading…
Cancel
Save