From 6a802a62a0f478d01f1940b425422fa0313628d5 Mon Sep 17 00:00:00 2001 From: Arghya Ghosh Date: Mon, 26 Jun 2023 22:08:24 +0530 Subject: [PATCH] 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 --- CHANGELOG.md | 10 ++++++++++ apps/api/src/app/symbol/symbol.controller.ts | 4 +++- apps/api/src/app/symbol/symbol.service.ts | 3 +++ .../alpha-vantage/alpha-vantage.service.ts | 10 ++++++++-- .../coingecko/coingecko.service.ts | 15 ++++++++------- .../data-provider/data-provider.service.ts | 9 ++++++++- .../eod-historical-data.service.ts | 12 +++++++++--- .../financial-modeling-prep.service.ts | 10 ++++++++-- .../google-sheets/google-sheets.service.ts | 12 +++++++++--- .../interfaces/data-provider.interface.ts | 8 +++++++- .../data-provider/manual/manual.service.ts | 12 +++++++++--- .../rapid-api/rapid-api.service.ts | 8 +++++++- .../yahoo-finance/yahoo-finance.service.ts | 18 +++++++++++++++--- .../create-asset-profile-dialog.html | 5 ++++- apps/client/src/app/services/data.service.ts | 16 ++++++++++++++-- .../symbol-autocomplete.component.ts | 12 ++++++++++-- 16 files changed, 132 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77bef7847..8dc8f193c 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/apps/api/src/app/symbol/symbol.controller.ts b/apps/api/src/app/symbol/symbol.controller.ts index 810093611..da73382a6 100644 --- a/apps/api/src/app/symbol/symbol.controller.ts +++ b/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 }); diff --git a/apps/api/src/app/symbol/symbol.service.ts b/apps/api/src/app/symbol/symbol.service.ts index 9a08d7ed5..bc626a97f 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/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 }); diff --git a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 2ac2390b3..4713a4c5f 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts +++ b/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) => { diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index 94ec926a6..35083e810 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/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 }) => { diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 6209b89aa..b4f4e10b5 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/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); diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index f1b56f659..961f960da 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/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 diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index bcee3c5e7..ed65c3f65 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/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 diff --git a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts index fb182167f..f4b592371 100644 --- a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts +++ b/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 } } ] diff --git a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts index 502e14982..2a16cc24c 100644 --- a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts +++ b/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[] }>; } diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index 65035d13f..14df4b7f4 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/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 } } ] diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index 82b9cbae9..053391a8a 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/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: [] }; } diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index e47713680..a525b4685 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/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 }) => { diff --git a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html index 16e67bd51..f4c65afed 100644 --- a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -8,7 +8,10 @@
Name, symbol or ISIN - +
diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 32e7ba7ae..4aa590493 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/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; diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts index e9c1dd8a1..ea99ab0e5 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -50,6 +50,7 @@ export class SymbolAutocompleteComponent extends AbstractMatFormField 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(); - this.validateSelection(); + + if (this.control.touched) { + this.validateSelection(); + } + this.errorState = this.ngControl.invalid && this.ngControl.touched; this.stateChanges.next(); }