Browse Source

Merge branch 'feature/fix-issue-with-recent-transactions' of https://github.com/gizmodus/ghostfolio into feature/fix-issue-with-recent-transactions

pull/750/head
Reto Kaul 3 years ago
parent
commit
903be68f33
  1. 27
      CHANGELOG.md
  2. 43
      apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts
  3. 3
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
  4. 26
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  5. 12
      libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts
  6. 2
      package.json
  7. 2
      prisma/migrations/20220313200604_added_fixed_income_to_asset_class/migration.sql
  8. 2
      prisma/migrations/20220313200721_added_bond_to_asset_sub_class/migration.sql
  9. 2
      prisma/schema.prisma

27
CHANGELOG.md

@ -5,7 +5,32 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased ## 1.127.0 - 16.03.2022
### Changed
- Improved the error handling in the scraper configuration
### Fixed
- Fixed the support for multiple symbols of the data source `GHOSTFOLIO`
## 1.126.0 - 14.03.2022
### Added
- Added support for bonds
### Changed
- Restructured the portfolio summary tab on the home page
- Improved the tooltips in the portfolio proportion chart component by introducing multilines
### Todo
- Apply data migration (`yarn database:migrate`)
## 1.125.0 - 12.03.2022
### Added ### Added

43
apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts

@ -50,16 +50,18 @@ export class GhostfolioScraperApiService implements DataProviderInterface {
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles(
[symbol] [symbol]
); );
const scraperConfiguration = symbolProfile?.scraperConfiguration; const { selector, url } = symbolProfile.scraperConfiguration;
const get = bent(scraperConfiguration?.url, 'GET', 'string', 200, {}); if (selector === undefined || url === undefined) {
return {};
}
const get = bent(url, 'GET', 'string', 200, {});
const html = await get(); const html = await get();
const $ = cheerio.load(html); const $ = cheerio.load(html);
const value = this.extractNumberFromString( const value = this.extractNumberFromString($(selector).text());
$(scraperConfiguration?.selector).text()
);
return { return {
[symbol]: { [symbol]: {
@ -82,33 +84,42 @@ export class GhostfolioScraperApiService implements DataProviderInterface {
public async getQuotes( public async getQuotes(
aSymbols: string[] aSymbols: string[]
): Promise<{ [symbol: string]: IDataProviderResponse }> { ): Promise<{ [symbol: string]: IDataProviderResponse }> {
const response: { [symbol: string]: IDataProviderResponse } = {};
if (aSymbols.length <= 0) { if (aSymbols.length <= 0) {
return {}; return response;
} }
try { try {
const [symbol] = aSymbols; const symbolProfiles = await this.symbolProfileService.getSymbolProfiles(
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( aSymbols
[symbol]
); );
const { marketPrice } = await this.prismaService.marketData.findFirst({ const marketData = await this.prismaService.marketData.findMany({
distinct: ['symbol'],
orderBy: { orderBy: {
date: 'desc' date: 'desc'
}, },
take: aSymbols.length,
where: { where: {
symbol symbol: {
in: aSymbols
}
} }
}); });
return { for (const symbolProfile of symbolProfiles) {
[symbol]: { response[symbolProfile.symbol] = {
marketPrice, currency: symbolProfile.currency,
currency: symbolProfile?.currency,
dataSource: this.getName(), dataSource: this.getName(),
marketPrice: marketData.find((marketDataItem) => {
return marketDataItem.symbol === symbolProfile.symbol;
}).marketPrice,
marketState: MarketState.delayed marketState: MarketState.delayed
}
}; };
}
return response;
} catch (error) { } catch (error) {
Logger.error(error, 'GhostfolioScraperApiService'); Logger.error(error, 'GhostfolioScraperApiService');
} }

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

@ -21,6 +21,7 @@ import Big from 'big.js';
import { countries } from 'countries-list'; import { countries } from 'countries-list';
import { addDays, format, isSameDay } from 'date-fns'; import { addDays, format, isSameDay } from 'date-fns';
import yahooFinance from 'yahoo-finance2'; import yahooFinance from 'yahoo-finance2';
import type { Price } from 'yahoo-finance2/dist/esm/src/modules/quoteSummary-iface';
@Injectable() @Injectable()
export class YahooFinanceService implements DataProviderInterface { export class YahooFinanceService implements DataProviderInterface {
@ -303,7 +304,7 @@ export class YahooFinanceService implements DataProviderInterface {
return { items }; return { items };
} }
private parseAssetClass(aPrice: any): { private parseAssetClass(aPrice: Price): {
assetClass: AssetClass; assetClass: AssetClass;
assetSubClass: AssetSubClass; assetSubClass: AssetSubClass;
} { } {

26
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -119,7 +119,7 @@
<div class="col"><hr /></div> <div class="col"><hr /></div>
</div> </div>
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Value</div> <div class="d-flex flex-grow-1" i18n>Total</div>
<div class="d-flex flex-column flex-wrap justify-content-end"> <div class="d-flex flex-column flex-wrap justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -130,6 +130,17 @@
></gf-value> ></gf-value>
</div> </div>
</div> </div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Valuables</div>
<div class="d-flex justify-content-end">
<gf-value
class="justify-content-end"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : summary?.items"
></gf-value>
</div>
</div>
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Emergency Fund</div> <div class="d-flex flex-grow-1" i18n>Emergency Fund</div>
<div <div
@ -151,7 +162,7 @@
</div> </div>
</div> </div>
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Cash (Buying Power)</div> <div class="d-flex flex-grow-1" i18n>Buying Power</div>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -161,17 +172,6 @@
></gf-value> ></gf-value>
</div> </div>
</div> </div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Items</div>
<div class="d-flex justify-content-end">
<gf-value
class="justify-content-end"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : summary?.items"
></gf-value>
</div>
</div>
<div class="row"> <div class="row">
<div class="col"><hr /></div> <div class="col"><hr /></div>
</div> </div>

12
libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts

@ -324,16 +324,16 @@ export class PortfolioProportionChartComponent
const percentage = (context.parsed * 100) / sum; const percentage = (context.parsed * 100) / sum;
if (this.isInPercent) { if (this.isInPercent) {
return `${name ?? symbol} (${percentage.toFixed(2)}%)`; return [`${name ?? symbol}`, `${percentage.toFixed(2)}%`];
} else { } else {
const value = <number>context.raw; const value = <number>context.raw;
return `${name ?? symbol}: ${value.toLocaleString( return [
this.locale, `${name ?? symbol}`,
{ `${value.toLocaleString(this.locale, {
maximumFractionDigits: 2, maximumFractionDigits: 2,
minimumFractionDigits: 2 minimumFractionDigits: 2
} })} ${this.baseCurrency} (${percentage.toFixed(2)}%)`
)} ${this.baseCurrency} (${percentage.toFixed(2)}%)`; ];
} }
} }
} }

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "1.124.0", "version": "1.127.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {

2
prisma/migrations/20220313200604_added_fixed_income_to_asset_class/migration.sql

@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "AssetClass" ADD VALUE 'FIXED_INCOME';

2
prisma/migrations/20220313200721_added_bond_to_asset_sub_class/migration.sql

@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "AssetSubClass" ADD VALUE 'BOND';

2
prisma/schema.prisma

@ -171,9 +171,11 @@ enum AssetClass {
CASH CASH
COMMODITY COMMODITY
EQUITY EQUITY
FIXED_INCOME
} }
enum AssetSubClass { enum AssetSubClass {
BOND
CRYPTOCURRENCY CRYPTOCURRENCY
ETF ETF
MUTUALFUND MUTUALFUND

Loading…
Cancel
Save