Compare commits

...

2219 Commits

Author SHA1 Message Date
1877b31f00 Release 2.41.0 (#2888) 2024-01-16 22:33:13 +01:00
00895b7bb1 Feature/increase timeout to load historical data in data provider service (#2887)
* Increase timeout to load historical data

* Update changelog
2024-01-16 22:31:28 +01:00
bff60ddbe0 Feature/improve asset profile validation in activities import for manual data source (#2886)
* Improve asset profile validation for MANUAL data source

* Update changelog
2024-01-16 21:21:51 +01:00
d46de0a15e Fix typo (#2878) 2024-01-16 19:42:56 +01:00
7b45a8b3fc Introduce type (#2885) 2024-01-16 19:42:39 +01:00
693791d113 Feature/add validation of search results in eod historical data service (#2883)
* Validate currency

* Update changelog
2024-01-16 18:03:31 +01:00
1b2d2a9860 Feature/Add holdings tab to account detail dialog (#2853) (#2864)
* Feature/Add holdings tab to account detail dialog (#2853)

* Update changelog
2024-01-15 20:35:45 +01:00
bde8be1385 Release 2.40.0 (#2877) 2024-01-15 19:42:11 +01:00
74ca058364 Extend pricing page section (#2876) 2024-01-15 19:40:27 +01:00
ba3cf82c6e Feature/increase robustness of exchange rates by always getting quotes (#2875)
* Always get quotes (with fallback to historical data)

* Update changelog
2024-01-15 19:02:00 +01:00
217bb6aa5a Release 2.39.0 (#2871) 2024-01-14 17:25:05 +01:00
440dc470fa Bugfix/fix currency inconsistency with conversion of ZAR to ZAc (#2869)
* Fix conversion from ZAR to ZAc

* Update changelog
2024-01-14 17:22:03 +01:00
165ca94f5b Feature/improve alignment in portfolio performance component (#2867)
* Improve alignment

* Update changelog
2024-01-14 17:05:42 +01:00
c418e75139 Bugfix/fix currency in error log in exchange rate data service (#2868)
* Fix currency

* Update changelog
2024-01-14 10:36:32 +01:00
76bf839010 Release 2.38.0 (#2865) 2024-01-13 16:19:57 +01:00
3bdc4c9b4a Feature/upgrade prettier to version 3.2.1 (#2860)
* Upgrade prettier to version 3.2.1

* Update changelog
2024-01-13 16:18:18 +01:00
005890d785 Improve extractNumberFromString() for international number formats (#2843)
* Set up test

* Add support for international formatted numbers

* Expose locale in scraper configuration

* Update changelog
2024-01-13 16:17:38 +01:00
256c020e88 Feature/improve indicator for delayed market data (#2862)
* Improve indicator for delayed market data

* Update changelog
2024-01-13 16:08:37 +01:00
5fa3388609 Feature/break down performance into asset and currency (#2863)
* Break down performance into asset and currency

* Nullify values

* Update changelog
2024-01-13 14:23:00 +01:00
be801b481e Feature/Add exchange rate effects to portfolio calculation (#2834)
* Add exchange rate effects to portfolio calculation

* Update changelog

---------

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
2024-01-13 13:07:33 +01:00
a72e98f73c Add AllInvestView (#2861) 2024-01-13 13:06:18 +01:00
f5df970685 Release 2.37.0 (#2858) 2024-01-11 18:57:59 +01:00
edfdc0c346 Feature/improve chart size in asset profile details dialog (#2849)
* Improve chart size

* Update changelog
2024-01-11 18:55:52 +01:00
fcfe7b1787 Clean up (#2844) 2024-01-11 18:55:34 +01:00
170b8acc65 Feature/Add git pre-commit hook for yarn format (#2840)
* Set up git pre-commit hook for yarn format

* Update changelog
2024-01-10 20:36:10 +01:00
a47829082e Bugfix/fix hidden fifth tab on mobile (#2848)
* Fix hidden fifth tab

* Update changelog
2024-01-09 08:28:01 +01:00
48ab5fcf08 Feature/update docker compose instructions to compose v2 (#2836)
* Update docker compose instructions

* Update changelog
2024-01-08 20:21:47 +01:00
dc8b60eeb1 Rename "Jobs" to "Job Queue" (#2847) 2024-01-08 20:21:13 +01:00
ee67432ffc Release 2.36.0 (#2842) 2024-01-07 17:15:30 +01:00
7755a6b655 Update translations (#2841) 2024-01-07 17:13:19 +01:00
d7f72819de Feature/extend assistant by tag selector (#2838)
* Extend assistant by tag selector

* Update changelog
2024-01-07 16:56:25 +01:00
2a4d7bf14f Feature/improve language localization for de 20240106 (#2837)
* Update translations

* Update changelog
2024-01-07 16:52:02 +01:00
d49287922f Feature/refresh cryptocurrencies list 20240106 (#2835)
* Update cryptocurrencies.json

* Update changelog
2024-01-06 19:29:03 +01:00
ac0f6f40cf Remove closing tags (#2816) 2024-01-06 19:28:35 +01:00
d91f947ab0 Feature/Add Coingecko api keys support (#2827)
* Add CoinGecko API keys support

* Update changelog
2024-01-06 19:06:07 +01:00
af71274ea9 Feature/remove account type enum (#2832)
* Remove AccountType enum

* Update changelog
2024-01-06 14:19:42 +01:00
0feba4b8d9 Release 2.35.0 (#2831) 2024-01-06 10:45:26 +01:00
62f85293e2 #2820 Grant private access (#2822)
* Grant private access

* Update changelog
2024-01-06 10:27:21 +01:00
6a048cee85 Feature/add hint for twr to portfolio summary (#2824)
* Add hint for TWR

* Add TWR to README.md

* Update changelog
2024-01-06 09:31:59 +01:00
0d93612d16 Feature/improve style of assistant (#2828)
* Minor style improvements

* Update changelog
2024-01-06 09:14:48 +01:00
9bf68b0d20 Feature/enable redis authentication in docker compose files (#2805)
* Enable redis authentication in docker-compose files

* Update changelog
2024-01-04 20:14:45 +01:00
371f1dc451 Feature/support rest api in scraper (#2810)
* Support REST APIs in scraper

* Update changelog
2024-01-03 21:59:45 +01:00
5cb2ec6411 Feature/Improve user interface of access table (#2821)
* Improve alignment

* Update changelog
2024-01-03 21:08:35 +01:00
3723a1d8b8 Release 2.34.0 (#2817) 2024-01-02 17:05:12 +01:00
4c30e9459d Feature/extend assistant by date range selector (#2815)
* Extend assistant by date range selector

* Update changelog
2024-01-02 17:02:15 +01:00
23d323073d Fix performance percentage for 1d (#2814)
* Fix performance percentage for 1d

* Improve response of positions endpoint

* Update changelog
2024-01-02 14:10:08 +01:00
0ad734262a Bugfix/improve tabs on ios (#2811)
* Improve tabs on iOS (Add to Home Screen)

* Update changelog
2024-01-02 10:06:13 +01:00
0649f9fd2c Clean up (#2787) 2024-01-02 10:05:31 +01:00
d089662dab Feature/improve the style of the top 3 and bottom 3 performers (#2807)
* Refactor to ordered list

* Update changelog
2024-01-02 09:44:15 +01:00
8c1c336fc6 Feature/upgrade nx to version 17.2.8 (#2809)
* Upgrade Nx to version 17.2.8

* Update changelog
2024-01-01 17:14:53 +01:00
43b4f14ace Feature/add button to test scraper configuration (#2808)
* Add button to test scraper configuration

* Update changelog

---------

Co-authored-by: Manushreshta B L <manushreshta27@gmail.com>
Co-authored-by: Hugo Persson <hugo.e.persson@gmail.com>
2024-01-01 11:53:42 +01:00
3717e38845 Update year (#2803) 2024-01-01 10:11:04 +01:00
265d4d0450 Release 2.33.0 (#2806) 2023-12-31 13:30:27 +01:00
726e727c7d Feature/benchmark currency correction (#2790)
* Convert benchmark performance to base currency

* Introduce getExchangeRates() for multiple dates

* Update changelog

---------

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
2023-12-31 13:28:11 +01:00
cb664774c0 Update translations (#2798)
* Update translations
2023-12-31 10:33:12 +01:00
b89bf1d5e8 Feature/increase timeout to load currencies (#2800)
* Increase timeout

* Update changelog
2023-12-31 10:23:56 +01:00
53ce37a83a Update OSS friends (#2799) 2023-12-31 10:23:13 +01:00
e9ac9057ff Fix debug instructions (#2802) 2023-12-30 21:11:56 +01:00
7020fc2a93 Feature/add hint for community language support (#2793)
* Add hint

* Update changelog
2023-12-30 10:55:11 +01:00
efcd9539dd Feature/upgrade ng extract i18n merge to 2.9.1 (#2797)
* Upgrade ng-extract-i18n-merge to version 2.9.1

* Update changelog
2023-12-30 10:54:04 +01:00
61ecc48d0e Feature/improve language localization for de 20231229 (#2796)
* Improve language localizations

* Update changelog
2023-12-30 10:34:17 +01:00
e465f1b791 Feature/add support to edit currency of asset profile with data source manual (#2789)
* Add support to edit currency

* Update changelog
2023-12-29 19:55:51 +01:00
01b6c14bcc Feature/improve handling of derived currency usx (#2788)
* Improve handling of USX

* Update changelog
2023-12-29 17:31:50 +01:00
34b02210df Feature/expose the environment variable REQUEST_TIMEOUT (#2792)
* Expose the environment variable `REQUEST_TIMEOUT`

* Update changelog
2023-12-29 17:29:33 +01:00
0034776b34 Feature/upgrade nx to version 17.2.7 (#2781)
* Upgrade Nx to version 17.2.7

* Update changelog
2023-12-29 11:26:24 +01:00
b183c45027 Time weighted portfolio performance calculation (#2778)
* Implement time weighted portfolio performance calculation

* Update changelog
2023-12-27 15:55:35 +01:00
7d68905f1b Feature/use has permission annotation in endpoints (#2771)
* Use HasPermission in endpoints

* Update changelog
2023-12-26 19:23:25 +01:00
0953c072fe Release 2.32.0 (#2784) 2023-12-26 10:18:32 +01:00
d152187ee8 Feature/upgrade prisma to version 5.7.1 (#2780)
* Upgrade prisma to version 5.7.1

* Update changelog
2023-12-26 10:16:20 +01:00
3c5affce88 Feature/upgrade prettier to version 3.1.1 (#2768)
* Upgrade prettier to version 3.1.1

* Update changelog
2023-12-24 16:23:17 +01:00
f27e21f9a0 Extend issue template (#2776) 2023-12-23 17:45:37 +01:00
337ca328c3 Feature/drop activity id on import (#2769)
* Drop activity id on import

* Update changelog
2023-12-22 20:16:02 +01:00
beb9e2c43f Feature/modernize nx executors (#2767)
* Modernize Nx executors

* @nx/eslint:lint
* @nx/webpack:webpack

* Update changelog
2023-12-21 11:44:36 +01:00
4d79df90a7 Feature/support search by asset profile id (#2765)
* Add support to search for an asset profile by id

* Update changelog
2023-12-20 19:24:03 +01:00
aa72d9b730 Feature/improve validation of currency management (#2761)
* Improve validation

* Update changelog
2023-12-20 11:53:40 +01:00
80e899a5d3 Bugfix/reset letter spacing in buttons (#2762)
* Reset letter spacing

* Update changelog
2023-12-19 19:54:20 +01:00
7c33120546 Reimplement redactObject() without cloneDeep() (#2760)
* Reimplement redactObject() without cloneDeep()

* Update changelog
2023-12-19 19:53:37 +01:00
7f3c86038f Sort imports (#2739) 2023-12-18 19:52:57 +01:00
c1446f8559 Feature/set select column to sticky in lazy loaded activities table (#2755)
* Set select column to sticky

* Update changelog
2023-12-17 20:09:56 +01:00
88d5dfe435 Release 2.31.0 (#2757) 2023-12-16 20:01:38 +01:00
7dc8f80fdf Feature/upgrade to nx version 17.2.5 (#2756)
* Upgrade Nx to version 17.2.5

* Update changelog
2023-12-16 20:00:04 +01:00
96f90c7259 Feature/add lazy loaded activities table to import activities dialog (#2754)
* Add lazy-loaded activities table

* Update changelog
2023-12-16 19:23:01 +01:00
a10d9cb6ba Feature/add lazy loaded activities table to account detail dialog (#2752)
* Add lazy-loaded activities table

* Update changelog
2023-12-16 19:04:08 +01:00
4547c5da1d Feature/add lazy loaded activities table to position detail dialog (#2753)
* Add lazy-loaded activities table

* Update changelog
2023-12-16 17:10:19 +01:00
28706d7b26 Refactor order service (#2751) 2023-12-16 17:08:59 +01:00
492bc5e17b Feature/improve font weight in value component (#2747)
* Improve font weight

* Update changelog
2023-12-16 14:41:50 +01:00
6c37737051 Bugfix/fix loading state of lazy loaded activities table component (#2744)
* Fix loading state

* Update changelog
2023-12-16 10:24:04 +01:00
8677d20c2c Feature/upgrade inter to version 4 (#2746)
* Upgrade to Inter 4

* Update changelog
2023-12-16 10:23:08 +01:00
4d905065ad Extend turkish translation (#2750) 2023-12-16 09:23:42 +01:00
5599b41b83 Bugfix/fix edit of activity in lazy loaded activities table (#2749)
* Fix edit

* Update changelog
2023-12-16 09:19:09 +01:00
8d5a60d777 Update turkish locales (#2748)
* Update translation

* Update changelog
2023-12-15 08:15:51 +01:00
695acf4f3f Update turkish translation (#2731)
* Update locales

* Update changelog
2023-12-14 20:04:22 +01:00
67dbef3b7a Release 2.30.0 (#2743) 2023-12-12 19:56:46 +01:00
0e94112dc7 Feature/adjust holdings weight threshold in trackinsight data enhancer (#2742)
* Adjust holdings weight threshold

* Update changelog
2023-12-12 19:54:58 +01:00
b22edff16b Feature/add support for column sorting to lazy loaded activities table (#2738)
* Add support for column sorting

* Update changelog
2023-12-12 19:54:40 +01:00
ffb7cbff50 Feature/highlight all time high in benchmarks of the markets overview (#2740)
* Highlight all time high

* Update changelog
2023-12-11 19:45:30 +01:00
25424ad280 Feature/update prisma to version 5.7.0 (#2737)
* Update prisma to version 5.7.0

* Update changelog
2023-12-10 14:50:32 +01:00
a768902b00 Feature/update prisma to version 5.7.0 (#2734)
* Update prisma to version 5.7.0

* Update changelog
2023-12-09 17:24:25 +01:00
2c7ece50fe Release 2.29.0 (#2733) 2023-12-09 17:14:13 +01:00
51a0ede3e4 Feature/introduce lazy loaded activities table (#2729)
* Introduce lazy-loaded activities table

* Add icon column

* Emit paginator event

* Add pagination logic

* Integrate total items

* Update changelog
2023-12-09 17:12:09 +01:00
531964636b Fix type (#2708) 2023-12-08 19:59:06 +01:00
e461fff1d7 Extend turkish localization (#2730) 2023-12-07 14:13:28 +01:00
4f9a5f0340 Feature/set actions columns of tables to stick at end (#2726)
* Set up stickyEnd in actions columns

* Update changelog
2023-12-06 18:35:03 +01:00
8d80e840b8 Feature/upgrade ngx markdown to version 17.1.1 (#2714)
* Upgrade marked and ngx-markdown

* Update changelog
2023-12-05 18:16:03 +01:00
833982a9de Bugfix/fix biometric authentication registration (#2713)
* Remove token on device registration

* Update changelog
2023-12-05 18:13:35 +01:00
c85966e5ed Improve language localization for tr (#2717)
* Improve language localization for tr

* Update changelog
2023-12-04 21:04:20 +01:00
43f67ba832 Feature/upgrade ng extract i18n merge to version 2.9.0 (#2715)
* Upgrade ng-extract-i18n-merge to version 2.9.0

* Update changelog
2023-12-04 19:54:43 +01:00
cbea8ac9d3 Feature/increase tab height on mobile (#2712)
* Increase tab height on mobile

* Update changelog
2023-12-04 19:53:50 +01:00
d4c939e41d Feature/improve language localization for german 20231202 (#2710)
* Update locales

* Update changelog
2023-12-03 09:26:12 +01:00
c1f129501a Release 2.28.0 (#2709) 2023-12-02 17:20:10 +01:00
377ba75e4c Add support to delete a cash balance (#2707) 2023-12-02 17:17:25 +01:00
77b13b88f0 Relax check for duplicates in activities import (#2704)
* Relax check for duplicates in activities import (allow same day)

* Update changelog
2023-12-02 10:37:03 +01:00
813e73a0a3 Introduce HasPermission annotation (#2693)
* Introduce HasPermission annotation

* Update changelog
2023-12-02 10:21:19 +01:00
1d796a9597 Bugfix/change intraday data gathering to operate synchronously (#2705)
* Change intraday data gathering to operate synchronously

* Update changelog
2023-12-02 10:20:05 +01:00
4eedf64a3c Update OSS Friends (#2701) 2023-12-02 10:00:00 +01:00
ed4dd79c72 Add cash balances table to account detail dialog (#2549)
* Add cash balances table to account detail dialog

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-12-01 21:12:41 +01:00
6f4fd0826c Feature/respect with excluded accounts flag in get account balances (#2697)
* Respect withExcludedAccounts in getAccountBalances()

* Update changelog
2023-12-01 17:22:13 +01:00
8e3a144a37 Fix date (#2699) 2023-11-30 07:43:41 +01:00
07b0a2c40a Add guard (#2696) 2023-11-29 20:10:37 +01:00
c5dc3d4272 Release 2.27.1 (#2698) 2023-11-28 07:53:00 +01:00
73e69273b4 Upgrade Nx to version 17.1.3 (#2694) 2023-11-27 18:47:15 +01:00
e0b74ef418 Release 2.27.0 (#2692) 2023-11-26 21:18:37 +01:00
2b491dc732 Extend performance endpoint by net worth per day (#2574)
* Extend performance endpoint by net worth per day

* Update changelog
2023-11-26 21:17:15 +01:00
79fc22b5ae Change tweet to post (#2684) 2023-11-26 08:30:02 +01:00
0a83bcd697 Feature/improve error log for data source request timeout (#2688)
* Improve error log for timeouts

* Update changelog
2023-11-25 18:50:24 +01:00
52540d460b Fix alignment (#2689) 2023-11-25 18:48:54 +01:00
6ff2e0f952 Introduce base product page (#2687) 2023-11-25 18:48:39 +01:00
b3e72383bc Feature/extract locales 20231125 (#2686)
* Update locales

* Update changelog
2023-11-25 12:46:56 +01:00
bdfba4d509 Upgrade to Nx 17.1 (#2635)
* Upgrade to Nx 17

* Run migrations

* Extend instructions

* Update changelog
2023-11-25 12:31:45 +01:00
8a411b707d Release 2.26.0 (#2685) 2023-11-24 19:17:54 +01:00
e21601202e update ci and add permissions block (#2683) 2023-11-24 10:20:26 +01:00
8f66040df1 Feature/upgrade prisma to version 5.6.0 (#2680)
* Upgrade prisma to version 5.6.0

* Update changelog
2023-11-23 15:16:01 +01:00
5ad248a643 Improve algorithm (#2676) 2023-11-23 15:15:40 +01:00
fa36c42af4 Improve yeekatee comparison data (#2682) 2023-11-23 15:14:59 +01:00
d4ddc781e1 Feature/upgrade yahoo finance2 to version 2.9.0 (#2679)
* Upgrade yahoo-finance2 to version 2.9.0

* Upgdate changelog
2023-11-22 08:11:04 +01:00
386dd56590 Feature/extend personal finance tools pages 20231120 (#2678)
* Improve tags

* Add Compound Planning

* Add Whal

* Add De.Fi

* Add Tiller

* Add Empower
2023-11-22 08:10:42 +01:00
f28b13604a Add as const (#2677) 2023-11-21 20:12:59 +01:00
d827858d0b Release 2.25.1 (#2674) 2023-11-19 16:46:32 +01:00
c758ca4bfa Release 2.25.0 (#2673) 2023-11-19 16:30:36 +01:00
37183a07bd Change black friday to black week (#2672)
* Change black friday to black week

* Change image
2023-11-19 16:28:30 +01:00
fb294fc6e2 Improve wording (#2668) 2023-11-18 11:15:03 +01:00
8898d02442 Bugfix/fix cannot read properties of undefined reading items in get position (#2667)
* Fix "Cannot read properties of undefined (reading 'items')"

* Update changelog
2023-11-18 11:05:05 +01:00
232d30234c Update OSS Friends (#2663) 2023-11-18 09:59:27 +01:00
e2234c4966 Feature/add black friday 2023 blog post (#2664)
* Add blog post: Black Friday 2023

* Update changelog
2023-11-17 20:20:49 +01:00
272a34195b Refactor folder (#2665) 2023-11-17 20:09:19 +01:00
8c25294da7 Feature/upgrade http status codes to version 2.3.0 (#2644)
* Upgrade http-status-codes to version 2.3.0

* Update changelog
2023-11-17 20:08:23 +01:00
6f11627006 Release 2.24.0 (#2661) 2023-11-16 20:29:49 +01:00
215098e418 Feature/improve language localization for german 20231116 (#2660)
* Update locales

* Update changelog
2023-11-16 20:28:09 +01:00
781496383b Bugfix/improve get range query in market data service (#2659)
* Attempt to fix "too many bind variables in prepared statement, expected maximum of 32767"

* Update changelog
2023-11-16 20:22:56 +01:00
f0f304c012 Change tweet to post (#2658) 2023-11-16 20:22:18 +01:00
4bf97c104b Update changelog (#2656) 2023-11-15 21:45:38 +01:00
0b35a3c7a7 Release 2.23.0 (#2655) 2023-11-15 21:22:20 +01:00
1586cd3a59 Feature/change twitter to x (#2654)
* Change Twitter to X

* Update changelog
2023-11-15 21:20:51 +01:00
ae763cbb87 Improve style of sub title (#2652) 2023-11-15 21:11:10 +01:00
aa72287d54 Extend benchmarks in the markets overview by 50-Day and 200-Day trends (#2575)
* Extend benchmarks in the markets overview by 50-Day and 200-Day trends

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-11-15 20:25:16 +01:00
d155ab6f28 Feature/improve data source validation in activities import (#2645)
* Improve data source validation

* Update changelog
2023-11-14 19:15:57 +01:00
913ca71aa5 Feature/upgrade prettier to version 3.1.0 (#2649)
* Upgrade prettier to version 3.1.0

* Update changelog
2023-11-13 20:40:25 +01:00
1ffde2a27e Feature/setup polish (#2650)
* Set up polski

* Update changelog
2023-11-13 20:35:15 +01:00
fcf0cea982 Change BTC to BTCUSD (#2646) 2023-11-13 20:22:47 +01:00
ae1968aadf Feature/extract locales 20231112 (#2643)
* Update locales

* Update changelog
2023-11-12 19:03:11 +01:00
3e6333ef95 Feature/upgrade ng extract i18n merge to version 2.8.3 (#2642)
* Upgrade ng-extract-i18n-merge to version 2.8.3

* Update changelog
2023-11-12 18:28:26 +01:00
c69686651e Release 2.22.0 (#2638) 2023-11-11 18:59:43 +01:00
93b6011ddc Feature/refactor get range in market data service (#2631)
* Refactor to unique asset in getRange()

* Update changelog
2023-11-11 18:57:41 +01:00
f567e25f27 Feature/introduce action menus in overview of action control panel (#2637)
* Introduce action menus

* Exchange rates management
* Coupons management

* Update changelog
2023-11-11 18:48:23 +01:00
5dc538bafb Feature/optimize testimonial carousel style on mobile (#2634)
* Optimize style on mobile

* Update changelog
2023-11-11 17:29:59 +01:00
b4de06fcf0 Feature/add platform icons to account selectors (#2633)
* Add platform icons to account selectors

* Update changelog
2023-11-11 17:27:29 +01:00
27da0eb26e Feature/harmonize name column of historical market data table (#2632)
* Harmonize name column

* Update changelog
2023-11-11 09:02:12 +01:00
8ff80c10e5 Feature/extend personal finance tools pages 20231110 (#2630)
* Add Magnifi

* Add Basil Finance
2023-11-11 09:01:58 +01:00
5db5d5e79a Release 2.21.0 (#2629) 2023-11-09 19:25:14 +01:00
12aac101bd Feature/extend system message (#2628)
* Extend system message

* Update changelog
2023-11-09 19:23:36 +01:00
3a66ccdebe Bugfix/fix unit in overview of home page (#2626)
* Fix unit

* Update changelog
2023-11-09 19:22:15 +01:00
6a722d1bb7 Bugfix/fix get quotes in financial modeling prep service (#2627)
* Fix get quotes

* Update changelog
2023-11-09 18:03:09 +01:00
7c9407d5dc Release 2.20.0 (#2623) 2023-11-08 19:39:38 +01:00
8abb517ac6 Feature/remove loading indicator of unit in overview of home page (#2622)
* Remove loading indicator of unit

* Update changelog
2023-11-08 19:37:45 +01:00
dec1d89c5c Feature/increase timeout in data provider and enhancer health check endpoint (#2621)
* Increase timeout in health check endpoint (data enhancer and provider)

* Update changelog
2023-11-08 18:02:38 +01:00
24e9ecc3e2 Update locales (#2620) 2023-11-08 16:51:44 +01:00
4a1e05b8cd Improve historical market data import (#2581)
* Add form group for historical data import

* Update changelog
2023-11-07 18:16:00 +01:00
39d1a85267 Feature/extend personal finance tools pages 20231107 (#2618)
* Add Monarch Money

* Add YNAB

* Add Allvue Systems
2023-11-07 18:14:54 +01:00
7cb86de7af Feature/remove account type from account database model (#2616)
* Remove account type

* Update changelog
2023-11-07 17:56:18 +01:00
aa078588e8 Release 2.19.0 (#2615) 2023-11-06 18:02:15 +01:00
fcef0a72d5 Bugfix/improve handling of derived currencies (#2604)
* Improve handling of derived currencies

* Update changelog
2023-11-06 17:58:39 +01:00
29987d3e2f Add missing activity types (#2601)
* FEE
* INTEREST
* LIABILITY
2023-11-06 13:09:14 +01:00
6284b4dfe8 Feature/improve localization of fear and greed index (#2612)
* Improve localization

* Update changelog
2023-11-06 13:07:53 +01:00
00342ca1f7 Improve wording (#2603) 2023-11-06 12:15:42 +01:00
234c4fd511 Add CoinGecko (#2611) 2023-11-06 08:33:23 +01:00
669f1fb60c Feature/add database migration to reset account type in account table (#2602)
* Set accountType to NULL

* Update changelog
2023-11-05 18:31:16 +01:00
52df0c62ab Release 2.18.0 (#2600) 2023-11-05 11:53:38 +01:00
e8e1bb83bf Fix get quotes in CoinGecko service (#2595)
* Fix get quotes in CoinGecko service

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-11-05 11:52:09 +01:00
45510702d0 Feature/add hacktoberfest 2023 debriefing blog post (#2599)
* Add blog post: Hacktoberfest 2023 Debriefing

* Update changelog
2023-11-05 11:36:52 +01:00
1b7e3a1e47 Feature/support activities import by isin for yahoo finance (#2597)
* Add support to import activities by isin

* Update changelog
2023-11-05 09:58:57 +01:00
35f98b9d2d Bugfix/handle failing database query for account find many (#2598)
* Handle issue with account.findMany() -> where: { id: { in: [ null ] } }

* Update changelog
2023-11-05 09:57:23 +01:00
e980aed9e7 Reorder functions (#2594) 2023-11-05 08:50:43 +01:00
d993067e9a Feature/extend personal finance tools pages 20231104 2 (#2591)
* Add Vyzer

* Add FinWise
2023-11-05 08:50:27 +01:00
3d09bfdb0c Feature/upgrade angular to version 16.2.12 (#2590)
* Add upgrade guides for Angular

* Upgrade Angular dependencies to version 16.2.12

* Update changelog
2023-11-04 11:59:49 +01:00
3fbc4f500f Add empty columns (#2589) 2023-11-04 10:39:36 +01:00
373201a98f Add major version to docker tags (#2586)
* Add major version to docker tags

* Update changelog
2023-11-04 10:38:35 +01:00
681f88f002 Clean up import (#2492) 2023-11-04 10:19:15 +01:00
8a523a981a Bugfix/fix fees on account level (#2588)
* Fix fees on account level

* Update changelog
2023-11-04 10:17:58 +01:00
81ded53363 Center membership card (#2582) 2023-11-04 10:17:35 +01:00
5272407af8 Feature/extend personal finance tools pages 20231104 (#2587)
* Introduce alias

* Add Rocket Money

* Add 8FIGURES
2023-11-04 10:17:17 +01:00
c48f89d117 Add empty columns (#2583) 2023-11-04 10:11:58 +01:00
046fdd3ae7 Release 2.17.0 (#2579) 2023-11-02 19:36:59 +01:00
e69c7a753c Feature/add edit exchange rate button to admin control (#2577)
* Ad edit button

* Update changelog
2023-11-02 19:35:03 +01:00
5191415b5a Add Intuit Mint (#2578) 2023-11-02 19:34:42 +01:00
a704378702 Refactor interface of getQuotes() to object (#2570) 2023-11-01 13:55:48 +01:00
cf7ce64de7 Bugfix/improve alignment of menu item icons (#2566)
* Improve alignment

* Update changelog
2023-10-31 14:01:33 +01:00
8c1b45f35b Bugfix/fix exception in webauthn page (#2564)
* Remove useBrowserAutofill option in startAuthentication()

* Update changelog
2023-10-30 19:23:55 +01:00
6ad1528d01 Feature/improve language localization for german 20231029 (#2565)
* Update locales

* Update changelog
2023-10-29 19:33:42 +01:00
4a6fbe4d30 Release 2.16.0 (#2563) 2023-10-29 08:59:00 +01:00
e31741f0c7 Add Capitally (#2562) 2023-10-29 08:57:33 +01:00
b26aa7f51d Feature/improve duplicate check in activities import (#2561)
* Allow different accounts

* Update changelog
2023-10-29 08:40:42 +01:00
c0fccd186f Feature/upgrade prisma to version 5.5.2 (#2560)
* Upgrade prisma to version 5.5.2

* Update changelog
2023-10-29 08:20:55 +01:00
a7baad10d1 Feature/improve import of historical market data (#2559)
* Improve historical market data import

* Update changelog
2023-10-28 21:08:44 +02:00
16f1b16e41 Feature/change checkboxes to slide toggles in admin control panel (#2551)
* Change checkboxes to slide toggles

* Update changelog
2023-10-28 20:46:44 +02:00
409ddc90ce Feature/improve language localization for german 20231028 (#2556)
* Improve localization

* Update changelog
2023-10-28 20:46:13 +02:00
95bc84956e Feature/localize keywords of meta data (#2555)
* Localize keywords

* Update changelog
2023-10-28 15:39:01 +02:00
20cefaba19 Improve usability and validation in cash balance transfer (#2552)
* Improve usability and validation in cash balance transfer

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-28 15:09:01 +02:00
379c651ce0 Add translation for product slogan (#2554) 2023-10-28 15:07:15 +02:00
7804c6879d Clean up (#2543) 2023-10-28 11:24:50 +02:00
de2255f9ba Release 2.15.0 (#2548) 2023-10-26 19:38:44 +02:00
e4ec5f213e Feature/extend personal finance tools pages 20231026 (#2547)
* Improve wording (vs)

* Improve breadcrumb (vs)

* Add Beanvest

* Add Wealthica

* Update locales
2023-10-26 19:36:34 +02:00
f3c2fb853d Upgrade to Nx 17 (#2545)
* Upgrade to Nx 17

* Update changelog
2023-10-26 19:35:56 +02:00
f5ad1d2d24 Feature/set validation rule to positive number in cash balance transfer (#2544)
* Add validation rule (positive number)

* Update changelog
2023-10-26 19:19:43 +02:00
0af37ca1d7 Extend asset profile dialog form (#2535)
* Extend asset profile dialog form

* Update changelog
2023-10-25 20:28:51 +02:00
2992a0da4c Verify current benchmark before loading it (#2541)
* Verify current benchmark before loading it

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-25 20:27:41 +02:00
2dcc7e161c Improve validation of activities import (#2496)
* Improve validation of activities import: expects positive values for fee, quantity and unitPrice

* Update changelog

---------

Co-authored-by: Rafael Claudio <rafacla@github.com>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-24 20:51:48 +02:00
fa627f686f Bugfix/fix chart for account excluded from analysis (#2534)
* Fix chart for account excluded from analysis

* Update changelog
2023-10-24 20:13:57 +02:00
0567083fc1 Feature/upgrade UUID to version 9.0.1 (#2537)
* Upgrade uuid to version 9.0.1

* Update changelog
2023-10-24 20:12:00 +02:00
3212efef17 Feature/upgrade yahoo finance2 to version 2.8.1 (#2536)
* Upgrade yahoo-finance2 to version 2.8.1

* Update changelog
2023-10-24 19:40:53 +02:00
6077e7c2f9 Feature/improve position detail dialog (#2532)
* Improve style and wording

* Update locales

* Update changelog
2023-10-23 20:50:40 +02:00
96b5dcfaf8 Create reusable currency selector component using mat-autocomplete (#2487)
* Create reusable currency selector component using mat-autocomplete

* Update changelog
2023-10-23 20:30:05 +02:00
c4e8e37884 Release 2.14.0 (#2530) 2023-10-21 20:07:40 +02:00
281d33f825 Feature/update oss friends 20231021 (#2529)
* Update OSS friends

* Improve style of sub title
2023-10-21 20:05:20 +02:00
5822e4d186 Bugfix/fix style of active page in header navigation (#2528)
* Fix style of active page

* Update changelog
2023-10-21 19:34:44 +02:00
cb166dcc78 Redirect to membership page (#2527) 2023-10-21 18:43:33 +02:00
4e7b7375a9 Feature/setup open figi (#2526)
* Setup OpenFIGI

* Update changelog
2023-10-21 18:12:50 +02:00
b8626c2086 Feature/change fees interest and search to general availability (#2525)
* Change features to general availability

* Fees on account level
* Interest on account level
* Search for a holding

* Update changelog

* Add documentation for experimental features
2023-10-21 10:25:05 +02:00
a59f9fa037 Feature/remove version from client (#2522)
* Remove version

* Update changelog
2023-10-21 09:41:07 +02:00
1666486940 Bugfix/trim text in i18n service (#2520)
* Trim text

* Update changelog
2023-10-20 22:36:03 +02:00
ac0ad48a65 Display invalid activity in csv import (#2460)
* Display invalid activity in csv import

* Update changelog
2023-10-20 22:07:57 +02:00
6a19eab425 Feature/improve membership card (#2517)
* Improve style

* Add animated border
2023-10-20 22:05:27 +02:00
750c627613 Feature/allow to edit market data of today (#2515)
* Allow to edit today's market data

* Update changelog
2023-10-20 17:37:55 +02:00
60b2115e3b Release 2.13.0 (#2514) 2023-10-20 08:24:49 +02:00
e7956943ba Make holdings request only once (#2453)
* Make holdings request only once

* Update changelog
2023-10-20 08:21:23 +02:00
f66edf8de0 Add membership card component (#2507)
* Add membership card component

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-20 08:18:34 +02:00
29028a81f5 Add i18n service to query XML files (#2503)
* Add i18n service to query XML files

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-19 17:13:40 +02:00
c9878c9050 Migrated users table in Admin Control to mat-table (#2469)
* Migrated users table in Admin Control to mat-table

* Update changelog
2023-10-19 16:58:01 +02:00
73ac4b4197 Add chart to account detail dialog (#2502)
* Add chart to account detail dialog

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-19 15:51:31 +02:00
016634a77f Feature/setup i18n page (#2508)
* Setup i18n page

* Add meta description
2023-10-18 17:35:07 +02:00
ea65dc5034 Release 2.12.0 (#2505) 2023-10-17 20:52:02 +02:00
84db54babd Change checkboxes to slide toggles on user settings page (#2497)
* Change checkboxes to slide toggles on user settings page

* Update changelog
2023-10-17 20:49:54 +02:00
653c9c62a8 Sort imports (#2490) 2023-10-17 20:42:32 +02:00
74278073b3 Bugfix/fix query to get asset profiles matching data source and symbol (#2504)
* Match dataSource and symbol

* Update changelog
2023-10-17 20:36:01 +02:00
0375b938a2 Add confirmation dialog (#2501) 2023-10-17 20:14:46 +02:00
32df7620d9 Add support for creating asset profiles with MANUAL data source (#2479)
* Add support for creating asset profiles with MANUAL data source

* Refactoring

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-17 18:33:22 +02:00
8492a8fed0 Upgrade simplewebauthn (#2498)
* Upgrade simplewebauthn to new major version

* Update changelog
2023-10-17 09:17:44 +02:00
30e561c06f Feature/extend assistant with search for asset profile (#2499)
* Extend assistant with search for asset profile

* Extend search results by currency, symbol and asset sub class

* Update changelog
2023-10-16 21:54:36 +02:00
7243090c0e Feature/copy client locales to assets of server (#2493)
* Copy client locales to server’s assets

* Update changelog
2023-10-15 18:08:44 +02:00
7ae49eb839 Add endpoint for account balances (#2484)
* Add endpoint for account balances

* Update changelog

---------

Co-authored-by: Pavol Kolcun <pavol.kolcun@student.tuke.sk>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-15 17:09:47 +02:00
bf816c3b89 Bugfix/show transfer balance button based on permission (#2489)
* Show button depending on permission

* Update changelog
2023-10-15 10:02:55 +02:00
20f9225daa Release 2.11.0 (#2488) 2023-10-14 19:12:37 +02:00
b6101c6375 Feature/import historical data (#2448)
* Import historical data for an asset

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-14 19:06:27 +02:00
e1022846b9 Bugfix/fix displayed currency of cash balance in account dialog (#2480)
* Fix currency

* Update changelog
2023-10-14 15:01:29 +02:00
9ba79f6721 Improve style (#2478) 2023-10-13 22:11:05 +02:00
0ac97bd112 Transfer cash balance between accounts (#2455)
* Transfer cash balance between accounts

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-13 21:46:49 +02:00
827270704a Bugfix/fix import for activities of type fee and interest (#2474)
* Fix import for activities of type fee and interest

* Update changelog
2023-10-13 19:51:02 +02:00
8634463597 Feature/upgrade prisma to version 5.4.2 (#2477)
* Upgrade prisma to version 5.4.2

* Update changelog
2023-10-13 19:50:06 +02:00
3905782ad6 Fix fab-container (#2476) 2023-10-13 19:49:30 +02:00
5db984ffef Add date column to benchmark component (#2466)
* Add date column to benchmark component
2023-10-12 10:21:00 +02:00
fb3cd4b689 Remove icon (#2467) 2023-10-11 09:58:18 +02:00
3b5a34f6f3 Use fab-button in access management tab (#2456)
* Use fab-button in access management tab

* Refactor fab container

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-11 09:57:35 +02:00
22b43b5bfc Feature/extract locales 20231010 (#2462)
* Update locales

* Update changelog
2023-10-10 20:17:45 +02:00
6c66033eb4 Add date to markets overview by benchmarks (#2436)
* Add date

* Update changelog
2023-10-10 17:31:53 +02:00
162fc25e23 Release 2.10.0 (#2459) 2023-10-09 20:31:34 +02:00
45f385a483 Feature/improve symbol conversion in eod historical data service (#2457)
* Improve conversion of currency symbols

* Update changelog
2023-10-09 20:29:56 +02:00
e9ef911548 Feature/improve search results display in assistant (#2458)
* Only show search results if search is active

* Update changelog
2023-10-09 20:28:39 +02:00
d8d4d8f001 Change jobs table in admin control to mat-table (#2444)
* Change jobs table in admin control to mat-table

* Update changelog
2023-10-09 19:38:33 +02:00
f47c7313af Support enter key press to submit access dialog form (#2437)
* Support enter key press to submit access dialog form

* Update changelog
2023-10-09 19:11:09 +02:00
31f0056a2d Release 2.9.0 (#2454) 2023-10-08 20:34:39 +02:00
550e646079 Feature/introduce assistant (#2451)
* Introduce assistant

* Update changelog
2023-10-08 20:32:00 +02:00
37ff7acf04 Change platform control from select to autocomplete in account dialog (#2429)
* Change platform control from select to autocomplete in account dialog

* Update changelog
2023-10-08 19:17:55 +02:00
8236091477 Feature/add support for search query in portfolio position endpoint (#2443)
* Introduce search query filter

* Update changelog
2023-10-07 19:30:28 +02:00
2a71cb66de Use port numbers from environment variables in docker-compose.dev.yml (#2406) 2023-10-07 17:18:13 +02:00
e60fe48fdd Add dialog for cash transfer between accounts (#2433)
* Add dialog for cash transfer between accounts

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-07 14:46:13 +02:00
d40bc5070a Feature/remove permission to markets overview on home page (#2441)
* Remove show condition for markets overview

* Update changelog
2023-10-07 09:15:54 +02:00
fda4e0ea7d Use application version from API endpoint in Admin Control panel (#2427)
* Use application version from API endpoint

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-07 09:11:32 +02:00
08d696ce33 Align ok.json with ok.csv (#2439) 2023-10-07 08:44:51 +02:00
46614a7c24 Create carousel component for testimonials (#2394)
* Create carousel component for testimonials

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-06 22:12:09 +02:00
02b433eb1e Prevent empty form submission in account dialog (#2428)
* Prevent empty form submission in account dialog
2023-10-05 21:02:35 +02:00
25112a450b Add support for comment in csv import (#2416)
* Add support for comment in csv import (activities)

* Update changelog
2023-10-05 20:42:35 +02:00
727340748b Clean up imports (#2411) 2023-10-05 20:40:31 +02:00
8ad6492477 Feature/various improvements in client (#2434)
* Various improvements

* Update changelog
2023-10-05 20:31:00 +02:00
4af76f6f6d Fix hasNotDefinedValuesInObject() in object.helper.ts (#2421) 2023-10-05 20:29:34 +02:00
10940214a5 Update OSS Friends (#2431) 2023-10-05 20:27:39 +02:00
d9a6c22e1e Add application version to admin endpoint (#2423)
* Add application version to admin endpoint

* Update changelog
2023-10-04 16:15:08 +02:00
692309988c Add Prisma Studio (#2415) 2023-10-04 08:48:50 +02:00
42a54263f9 Release 2.8.0 (#2420) 2023-10-03 20:10:29 +02:00
4fb88859b2 Improve form in account dialog (#2408)
* Improve form in account dialog

* Update changelog
2023-10-03 19:34:04 +02:00
aa24b5e8c6 Feature/reload platform and tag on change (#2417)
* Reload platforms via info

* Reload tags via info

* Update changelog
2023-10-03 18:58:23 +02:00
90e18338f6 Change UX to set an asset profile as a benchmark (#2409)
* Add checkbox functionality to set / unset benchmark

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-10-02 20:50:12 +02:00
ad5ae938ef Feature/harmonize settings icon of user account page (#2412)
* Harmonize icon

* Update changelog
2023-10-02 11:29:33 +02:00
c9a8dd4958 Support copy-assets Nx target on Windows (#2410)
* Introduce shx plugin
2023-10-02 10:48:36 +02:00
f1ec5e704e Add version to Overview of Admin Control panel (#2414)
* Add version to Overview of Admin Control panel

* Update changelog
2023-10-02 10:05:01 +02:00
f40f0653c2 Add request params (ship, take) for pagination to GET order endpoint (#2382)
* Add request params (ship, take) for pagination to GET order endpoint

* Update changelog
2023-10-02 08:54:21 +02:00
5f7a230fd3 Bugfix/fix sidebar on user account page (#2413)
* Fix sidebar on user account page

* Update changelog
2023-10-01 19:11:48 +02:00
71feb531e8 Release 2.7.0 (#2404) 2023-09-30 08:26:04 +02:00
ec3552d7f6 Feature/add tabs to user account page (#2396)
* Create components for access, membership and settings

* Add tabs

* Update changelog
2023-09-30 08:24:26 +02:00
41875e70d6 Extend personal finance tools (#2403) 2023-09-30 08:24:00 +02:00
5fa0540936 Feature/add emergency fund setup to static portfolio analysis rules (#2400)
* Add new static portfolio analysis rule: Emergency fund setup

* Update changelog
2023-09-30 08:08:20 +02:00
5b69dee246 Feature/setup inter font family (#2402)
* Setup Inter font family

* Update changelog
2023-09-30 08:06:01 +02:00
19b0fe04a6 Feature/upgrade yahoo finance2 to version 2.8.0 (#2401)
* Upgrade yahoo-finance2 to version 2.8.0

* Update changelog
2023-09-30 07:31:02 +02:00
19ea4479ff Bugfix/fix link on features page (#2398)
* Fix link

* Update changelog
2023-09-30 07:11:44 +02:00
0b2f6a312c Sort imports (#2399) 2023-09-30 07:11:10 +02:00
f79d60014b Release 2.6.0 (#2395) 2023-09-26 18:58:22 +02:00
5b7409d08e Feature/add tag management in admin control panel (#2389)
* Add tag management

* Update locales

* Update changelog
2023-09-26 18:56:09 +02:00
6230aa87e2 Feature/add hacktoberfest 2023 blog post (#2359)
* Add blog post: Hacktoberfest 2023

* Update changelog
2023-09-26 16:08:17 +02:00
8b615d2f56 Feature/upgrade prettier to version 3.0.3 (#2393)
* Upgrade prettier to version 3.0.3

* Update changelog
2023-09-26 15:07:20 +02:00
4100446cac Update OSS Friends (#2385) 2023-09-26 15:05:14 +02:00
ad3e6d637c Improve file name (#2391) 2023-09-26 15:04:32 +02:00
aa87262954 Feature/upgrade yahoo finance2 to version 2.7.0 (#2392)
* Upgrade yahoo-finance2 to version 2.7.0

* Update changelog
2023-09-26 07:51:27 +02:00
01b6bb5b99 Clean up (#2390) 2023-09-25 23:37:52 +02:00
884b7f4de7 Clean up (#2342) 2023-09-24 08:25:25 +02:00
3f8a2b47f9 Release 2.5.0 (#2380) 2023-09-23 20:11:46 +02:00
e2e4c9be3c Feature/skip data gathering for manual data source (#2379)
* Skip data gathering

* Update changelog
2023-09-23 20:10:08 +02:00
0f7c6ff0fe Bugfix/fix asset class of cash position for empty account (#2378)
* Fix assetClass and assetSubClass

* Update changelog
2023-09-23 19:52:28 +02:00
703a96f4db Add guard (#2377) 2023-09-23 19:45:15 +02:00
42c0560422 Feature/translate activity type (#2376)
* Introduce ActivityTypeComponent with localized label

* Update changelog
2023-09-23 16:44:03 +02:00
eb63802d01 Feature/extend supported date formats in activities import (#2362)
* Extend supported date formats in activities import

* Update changelog
2023-09-23 16:14:54 +02:00
6d9191a46f Feature/setup turkish (#2300)
* Setup Turkish

* Add Turkish translations

* Update changelog

---------

Co-authored-by: sadmimye <134071831+sadmimye@users.noreply.github.com>
2023-09-22 20:26:45 +02:00
6744245d8b Feature/extend personal finance tools pages 20230922 (#2369)
* Extend pages

* Refactoring
2023-09-22 20:04:40 +02:00
8f64a77a9d Clean up (#2329) 2023-09-21 19:56:31 +02:00
0d5fc7655b Improve wording (#2358) 2023-09-21 19:55:36 +02:00
c511ec7e33 Release 2.4.0 (#2356) 2023-09-19 20:38:50 +02:00
b12349a148 Feature/add support for interest on account level (#2354)
* Add support for interest

* Update changelog
2023-09-19 20:37:04 +02:00
f7e3a4c727 Update OSS Friends (#2352) 2023-09-19 20:27:14 +02:00
5f276469b7 Feature/upgrade prisma to version 5.3.1 (#2355)
* Upgrade prisma to version 5.3.1

* Update changelog
2023-09-19 19:37:33 +02:00
69e1d92ed3 Feature/unlock experimental features setting for all users (#2351)
* Unlock experimental features setting for all users

* Update changelog
2023-09-19 18:41:12 +02:00
ef2849aa6c Remove this (#2341) 2023-09-19 10:28:07 +02:00
c668d7b456 Feature/improve preselected currency in create or update activity dialog (#2349)
* Preselect currency based on account's currency

* Update changelog
2023-09-18 19:45:02 +02:00
e23bf62859 Fix Memory Leak on Data Gathering when server TZ is behind UTC (#2332)
* Fix for timezones behind UTC (the previous code converted the date to one day before (in local time) then added a day, which resulted in the same day after converting back to UTC and thus generating an infinite loop)

* Update changelog

---------

Co-authored-by: Rafael Claudio <rafacla@github.com>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-09-17 22:19:06 +02:00
54c5746d21 Release 2.3.0 (#2348) 2023-09-17 18:23:28 +02:00
7130ac7565 Feature/add support for fees on account level (#1954)
* Add migration

* Add business logic for fees

* Fix export for liabilities

* Update changelog
2023-09-17 18:20:54 +02:00
1851ae137f Release 2.2.0 (#2346) 2023-09-17 07:17:20 +02:00
6f6ff94979 Improve sidebar (#2343)
* Improve sidebar

* Improve style of system message

* Update changelog
2023-09-17 07:08:26 +02:00
7f25066f0f Remove ALPHA_VANTAGE_API_KEY (#2345) 2023-09-17 06:54:38 +02:00
fc795aaa8c Update postgres to version 15 in docker-compose files (#1596)
* Update postgres to version 15 in docker-compose files

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-09-17 06:52:41 +02:00
d0112968e8 Feature/introduce sidebar navigation on desktop (#2340)
* Introduce sidebar navigation on desktop

* Update changelog
2023-09-16 14:40:05 +02:00
522025ffa0 Fix entry (#2339) 2023-09-16 14:37:08 +02:00
27bf662281 Release 2.1.0 (#2338) 2023-09-15 19:48:09 +02:00
93c27277c6 Extend sitemap with Italian pages (#2337) 2023-09-15 19:46:32 +02:00
5e6adfcef5 Feature/improve language localization for german 20230915 (#2336)
* Improve language localization

* Update changelog
2023-09-15 19:38:15 +02:00
ab691bb27a Feature/remove account type from user interface (#2335)
* Remove account type from user interface and set it optional

* Update changelog
2023-09-15 19:11:20 +02:00
8fc5676443 Feature/improve timeout of data source requests (#2330)
* Improve timeout

* Update changelog
2023-09-15 16:25:01 +02:00
1fe1e2fe0c Feature/improve read only mode (#2322)
* Improve read-only mode

* Update changelog
2023-09-15 16:22:39 +02:00
921d38a706 Feature/harmonize style of granted access user interface (#2326)
* Harmonize style

* Update changelog
2023-09-15 16:21:14 +02:00
6161d5e77c Feature/improve logger output of info service (#2331)
* Improve context of logger output

* Update changelog
2023-09-15 08:26:08 +02:00
369386f976 Add drop file functionality on import (#2323)
* Add drop file functionality on import

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-09-14 21:12:48 +02:00
41437636b1 Update messages.it.xlf (#2325)
* Update messages.it.xlf

* Update changelog
2023-09-14 19:48:47 +02:00
b21884eb66 Feature/harmonize logger output (#2321)
* Harmonize logger output

* Update changelog
2023-09-13 08:39:37 +02:00
1c5437e1fd Extend sitemap.xml with dutch pages (#2318) 2023-09-13 08:39:09 +02:00
58278ba5e6 Bugfix/fix dutch localization of portfolio summary (#2315)
* Revert reserved keyword (plural)

* Update changelog
2023-09-11 12:04:09 +02:00
921f3e9807 Update dutch translation (#2314)
* Update dutch translation

* Update changelog
2023-09-11 11:42:36 +02:00
75ca125a70 Add home server systems (#2311) 2023-09-10 08:01:01 +02:00
a1fd4e7a38 Improve wording (#2312) 2023-09-10 08:00:07 +02:00
0d5a8eb33e Add Ghostfolio 2.0 (#2309) 2023-09-09 09:10:36 +02:00
b088df2fa3 Release 2.0.0 (#2310) 2023-09-09 08:29:18 +02:00
f45d8f616a Bugfix/fix blog post ghostfolio 2 (#2307)
* Fix month

* Update sitemap.xml

* Update locales
2023-09-08 21:36:01 +02:00
d8300502ce Feature/upgrade yahoo finance2 to version 2.5.0 (#2306)
* Upgrade yahoo-finance2 to version 2.5.0

* Update changelog
2023-09-08 21:22:57 +02:00
502d51ad29 Feature/add blog post ghostfolio 2 (#2269)
* Add blog post: Ghostfolio 2.0

* Update changelog
2023-09-08 20:45:31 +02:00
bc33e5f147 Feature/remove deprecated environment variable base currency (#2255)
* Remove the deprecated environment variable BASE_CURRENCY

* Update changelog
2023-09-08 20:43:23 +02:00
48ba8f936b Feature/deactivate internet identity for account registration (#2293)
* Deactivate Internet Identity

* Update changelog
2023-09-08 20:23:22 +02:00
05ec4cce05 Add Portuguese landing page (#2301) 2023-09-08 17:06:58 +02:00
d74f283707 Eliminate prisma service (#2286)
* Eliminate prisma service
2023-09-08 17:05:42 +02:00
0f8bc7db32 Bugfix/do not remove countries and sectors in yahoo finance data enhancer (#2297)
* Do not remove countries and sectors

* Update changelog
2023-09-08 15:07:17 +02:00
431500f28a Update docker compose files to version 3.9 (#2299)
* Update docker compose files to version 3.9

* Format yml files

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-09-07 20:44:52 +02:00
9672de174e Feature/improve language localization for german 20230903 (#2294)
* Update locales

* Update changelog
2023-09-07 19:20:30 +02:00
c6aa06b933 Feature/improve import validation (#2305)
* Improve import validation

* Update changelog

Co-authored-by: httpiga <36515569+httpiga@users.noreply.github.com>
2023-09-07 18:28:47 +02:00
1f46a6b6f3 Clean up (#2291) 2023-09-05 19:44:45 +02:00
1bed940bc0 Feature/refresh cryptocurrencies list 20230903 (#2290)
* Refresh cryptocurrencies list

* Add CyberConnect

* Update changelog
2023-09-04 09:07:06 +02:00
f9eb3cc3c5 Release 1.305.0 (#2289) 2023-09-03 08:16:02 +02:00
2519c3ffb0 Bugfix/fix alignment in menu of impersonation mode (#2284)
* Fix alignment

* Update changelog
2023-09-02 17:57:50 +02:00
91013d1d10 Bugfix/fix alignment in header navigation (#2285)
* Fix alignment

* Update changelog
2023-09-02 17:13:13 +02:00
6deefb9c43 Update OSS Friends (#2282) 2023-09-02 11:52:29 +02:00
d0744e07df Feature/upgrade replace in file to version 7.0.1 (#2277)
* Upgrade replace-in-file to version 7.0.1

* Update changelog
2023-09-02 08:58:25 +02:00
93e1ee3ba7 Feature/improve localization of personal finance tools (#2274)
* Improve localization

* Update changelog
2023-09-02 08:58:10 +02:00
dceaa55a6c Feature/add hacker news logo to landing page (#2281)
* Add Hacker News

* Update changelog
2023-09-02 08:39:32 +02:00
8b4d55925d Feature/upgrade yahoo finance2 to version 2.4.4 (#2276)
* Upgrade yahoo-finance2 to version 2.4.4

* Update changelog
2023-09-02 08:36:46 +02:00
754b49e50f Feature/shorten page titles (#2273)
* Shorten page titles

* Update changelog
2023-08-31 18:22:22 +02:00
6ccbda8169 Feature/upgrade prisma to version 5.2.0 (#2217)
* Upgrade prisma to version 5.2.0

* Update changelog
2023-08-29 13:44:53 +02:00
b0fb986208 Release 1.304.0 (#2272) 2023-08-27 11:19:14 +02:00
0b59fc639d Feature/upgrade prettier to version 3 (#2163)
* Upgrade prettier to version 3.0.2

* Prettify code

* Update changelog
2023-08-27 11:13:11 +02:00
7ddd6f27b5 Feature/upgrade nx to version 16.7.4 (#2271)
* Upgrade Nx to version 16.7.4

* Update changelog
2023-08-27 10:44:06 +02:00
c5d56f4b47 Fix border (#2268) 2023-08-27 10:20:36 +02:00
2f2b712999 Fix breadcrumb (#2267) 2023-08-27 10:20:21 +02:00
c2fd31f5e5 Feature/add health check endpoints for data enhancers (#2265)
* Add health check for data enhancers

* Update changelog
2023-08-27 10:19:53 +02:00
f2d70f9070 Sort imports (#2266) 2023-08-26 11:22:19 +02:00
f41dd9cd8e Fix lint script (#2264) 2023-08-25 15:13:04 +02:00
7d238b4935 Release 1.303.0 (#2261) 2023-08-23 18:52:59 +02:00
da6591fca0 Bugfix/fix base url in trackinsight data enhancer (#2258)
* Fix base url

* Update changelog
2023-08-23 18:51:02 +02:00
1f9b9e9998 Feature/blog post ghostfolio joins oss friends (#2260)
* Add blog post: Ghostfolio joins OSS Friends

* Update changelog
2023-08-23 18:49:53 +02:00
49c4ea306d Feature/improve oss friends page (#2257)
* Improve OSS Friends page

* Update changelog
2023-08-22 09:02:39 +02:00
ccb5c664ef Feature/refresh cryptocurrencies list 20230821 (#2256)
* Update cryptocurrencies.json

* Update changelog
2023-08-21 20:20:59 +02:00
97e165ff69 Improve localization (#2254) 2023-08-21 18:05:08 +02:00
45aefb6a45 Reorder charts (#2253) 2023-08-21 18:04:49 +02:00
2435535975 Release 1.302.0 (#2252) 2023-08-20 10:35:13 +02:00
bd3d43bf05 Feature/upgrade nx to version 16.7.2 (#2251)
* Upgrade Nx and Angular dependencies

* Update changelog
2023-08-20 10:32:52 +02:00
02dc7c52b1 Localize routes (#2250)
* Localize about path

* Localize faq path

* Localize features path

* Localize markets path

* Localize pricing path

* Localize register path

* Localize resources path

* Extend sitemap
2023-08-20 10:01:40 +02:00
ff59fd4196 Feature/improve language localization for german 20230819 (#2249)
* Improve language localization

* Update changelog
2023-08-19 19:49:40 +02:00
4955555ddd Release 1.301.1 (#2247) 2023-08-19 08:49:31 +02:00
a98c788a26 Release 1.301.0 (#2246) 2023-08-18 20:46:58 +02:00
9c16af81c7 Feature/setup oss friends page (#2245)
* Setup OSS Friends page

* Update changelog
2023-08-18 20:45:10 +02:00
2df27100f0 Add middleware (#2239)
* Add middleware

* Update changelog
2023-08-18 20:27:19 +02:00
6cf6538719 Feature/add currencies preset to historical market data table (#2243)
* Add currencies preset

* Update locales

* Update changelog
2023-08-18 19:33:00 +02:00
0fd3db3228 Bugfix/fix cash position rows in holdings table (#2237)
* Fix cash position rows

* Update changelog
2023-08-17 20:23:23 +02:00
18835149e2 Add repository (#2189) 2023-08-16 20:32:54 +02:00
6c9779fb0d Bugfix/change date creation from string using parse iso (#2236)
* Change date creation using parseISO

parseISO provides consistent date parsing across different time zones

* Update changelog
2023-08-15 19:24:31 +02:00
3e98f097ef Refactor account page to user account page (#2235)
* Refactor account page to user account page
2023-08-13 09:24:54 +02:00
183ac8fa2b Feature/add data export to user account page (#2234)
* Add data export

* Update changelog
2023-08-12 21:51:35 +02:00
9036f53e7d Reset benchmark in user settings (#2233) 2023-08-12 21:50:01 +02:00
f7c04e469a Release 1.300.0 (#2232) 2023-08-11 20:24:23 +02:00
b5f01c0d15 Feature/migrate requests from bent to got (#2231)
* Migrate requests from bent to got

* Update changelog
2023-08-11 20:20:35 +02:00
5a23cd34ad Replace variables (#2229) 2023-08-11 18:29:39 +02:00
6e87f34c6f Feature/add more durations in coupon system (#2228)
* Add 90 and 180 days

* Update changelog
2023-08-10 20:49:06 +02:00
6618aa2e9b Release 1.299.1 (#2227) 2023-08-10 07:58:34 +02:00
0d25a96f7e Release 1.299.0 (#2225) 2023-08-09 20:59:52 +02:00
4f6d9d3a76 Feature/add timeout to eod historical data requests (#2222)
* Add timeout to requests using got

* Update changelog
2023-08-09 20:58:00 +02:00
928f6f0c45 Bugfix/fix historical data gathering interval for benchmarks with activity (#2221)
* Fix historical data gathering interval for asset profiles used as benchmarks having activities

* Update changelog
2023-08-09 20:43:03 +02:00
09e95ddcee Bugfix/fix editing of emergency fund (#2220)
* Fix editing of emergency fund

* Update changelog
2023-08-09 19:41:42 +02:00
2d003225bc Allow custom currency in activity import (#2215)
* Allow custom currency in activity import

* Extend import test files

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-08-08 20:00:55 +02:00
de93cabd69 Release 1.298.0 (#2214) 2023-08-06 09:13:04 +02:00
51489cca81 Feature/upgrade ng extract i18n merge to version 2.7.0 (#2155)
* Upgrade ng-extract-i18n-merge to version 2.7.0

* Update changelog
2023-08-06 09:10:14 +02:00
f7f4c3afb1 Feature/localize open startup page (#2213)
* Localize Open Startup page

* Update changelog
2023-08-06 09:09:21 +02:00
0821086e41 Bugfix/fix various styles after angular material 16 upgrade (#2212)
* Fix styles

* Update changelog
2023-08-06 08:52:45 +02:00
7a905fde63 Clean up (#2210) 2023-08-06 08:32:06 +02:00
d2882b1119 Clean up (#2206) 2023-08-06 08:31:48 +02:00
3a500598c5 Feature/upgrade nx to version 16.6.0 (#2211)
* Upgrade Nx to version 16.6.0

* Update changelog
2023-08-06 08:30:28 +02:00
42274917e0 Release 1.297.4 (#2209) 2023-08-05 19:57:39 +02:00
8ba50f2729 Release 1.297.3 (#2208) 2023-08-05 16:08:06 +02:00
f22071f061 Release 1.297.2 (#2207) 2023-08-05 14:51:10 +02:00
d2312371a6 Release 1.297.1 (#2205) 2023-08-05 14:35:13 +02:00
ba837c3c30 Release 1.297.0 (#2204) 2023-08-05 13:10:18 +02:00
d85d83a0f5 Feature/improve alignment of region percentages (#2203)
* Improve alignment

* Update changelog
2023-08-05 11:11:06 +02:00
62e8594c57 Feature/improve language localization for german 20230802 (#2200)
* Improve localization

* Update changelog
2023-08-05 11:10:15 +02:00
509f95ea30 Feature/add footer to public page (#2202)
* Add footer to public page

* Update changelog
2023-08-05 11:09:27 +02:00
43d0b55004 Feature/upgrade to angular 16 (#2156)
* Upgrade Angular, NestJS and Nx

* Replace executor to @nx/angular:webpack-browser and @nx/angular:webpack-dev-server

* Add target for copying assets

* Improve redirection of home page

* Update changelog
2023-08-05 11:08:10 +02:00
c0f130a077 Remove sitemap.xml (#2201) 2023-08-04 08:04:21 +02:00
90dc34380e Release 1.296.0 (#2199) 2023-08-01 09:12:26 +02:00
286e41eb21 Feature/optimize import validation by reducing to unique asset profiles (#2198)
* Optimize activities validation

* Optimize data gathering in import

* Update changelog
2023-08-01 09:10:13 +02:00
4973d0261d Release 1.295.0 (#2197) 2023-07-30 19:41:22 +02:00
c4a62dfd68 Bugfix/remove stay signed in setting from local storage on sign in with fingerprint activation (#2196)
* Remove staySignedIn from local storage

* Update changelog
2023-07-30 19:36:06 +02:00
4d6be0a507 Exclude open-source-alternative-to-markets.sh (#2195) 2023-07-30 19:35:49 +02:00
b259ab7b0c Feature/add step by step introduction for new users (#2191)
* Add introduction for new users

* Update changelog
2023-07-30 18:49:38 +02:00
e1ac5245c7 Release 1.294.0 (#2192) 2023-07-29 20:33:31 +02:00
d4fea075af Feature/include unavailable data in allocations by market chart (#2190)
* Include unavailable data in allocations by market chart

* Update changelog
2023-07-28 20:20:08 +02:00
cef7fa79de Fix total account value calculation for liabilities (#2184)
* Fix calculation

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-07-28 19:42:57 +02:00
ca05397dcd Extend Community Projects section (#2188) 2023-07-27 17:39:29 +02:00
2a11977001 Release 1.293.0 (#2186) 2023-07-26 21:32:35 +02:00
fb1a5c93ef Bugfix/fix no such file or directory error caused by missing favicon.ico (#2185)
* Add instructions to copy favicon.ico

* Update changelog
2023-07-26 21:26:05 +02:00
77e9791e03 Feature/set lastmod dates of sitemap.xml dynamically (#2170)
* Setup template with interpolation for sitemap.xml

* Update changelog
2023-07-26 21:08:38 +02:00
efd9e7a5c7 Fix RedisClient import (#2183) 2023-07-26 20:36:34 +02:00
d9ced885e1 Feature/add error handling for redis connections (#2179)
* Add error handling

* Update changelog
2023-07-26 20:30:32 +02:00
5fe07cb85f Bugfix/fix value in holdings table (#2182)
* Fix missing value

* Update changelog
2023-07-26 20:05:26 +02:00
af008aa74f Release 1.292.0 (#2175) 2023-07-24 20:17:46 +02:00
ca7bf27c20 Feature/upgrade yahoo finance2 to version 2.4.3 (#2174)
* Upgrade yahoo-finance2 to version 2.4.3

* Update changelog
2023-07-24 20:16:14 +02:00
0866587cab Increase frequency (#2169) 2023-07-24 20:12:07 +02:00
622bb8b0cf Feature/add allocations by market chart (#2171)
* Add allocations by (advanced) market

* Fix public page

* Update changelog
2023-07-24 20:04:34 +02:00
16b9fbe00e Release 1.291.0 (#2168) 2023-07-23 16:06:45 +02:00
c9353d0a39 Support account balance time series (#2166)
* Initial setup

* Support account balance in export

* Handle account balance update

* Add schema migration

* Update changelog
2023-07-23 15:55:58 +02:00
ea101dd3bd Refactor value to valueInBaseCurrency (#2167)
* Revert value to valueInBaseCurrency refactoring
2023-07-23 14:13:02 +02:00
cd67ce82fa Feature/rename queries to presets in market data table of admin control (#2165)
* Rename queries to presets

* Update changelog
2023-07-21 11:40:49 +02:00
d5b3c52602 Refactor value to valueInBaseCurrency (#2164) 2023-07-20 20:28:56 +02:00
bdf72164b1 Feature/break down emergency fund by cash and assets (#2159)
* Break down emergency fund in cash and assets

* Update changelog
2023-07-19 11:30:48 +02:00
455a2d2e92 Refactor value to valueInBaseCurrency (#2160) 2023-07-18 21:29:08 +02:00
9c0f46b587 Add markets.sh (#2161) 2023-07-18 21:28:44 +02:00
8533606177 Release 1.290.0 (#2158) 2023-07-16 08:01:31 +02:00
6728e04ff7 Improve http response interceptor (#2157)
Do not show snack bar for login endpoint
2023-07-15 22:17:07 +02:00
2bf4f1237a Feature/Improve login dialog (#2124)
* Improve login dialog

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-07-15 22:09:12 +02:00
4857b2e620 Update locales (#2154) 2023-07-15 19:50:11 +02:00
68a9a7f6f9 Feature/add queries to market data table in admin control (#2153)
* Add queries

* ETF_WITHOUT_COUNTRIES
* ETF_WITHOUT_SECTORS

* Update changelog
2023-07-15 17:54:16 +02:00
81ef95e13e Setup permissions (#2151) 2023-07-15 12:32:59 +02:00
b633132757 Feature/upgrade prisma to version 4.16.2 (#2109)
* Upgrade prisma to version 4.16.2

* Update changelog
2023-07-15 12:32:43 +02:00
2b0f961370 Feature/improve faq page (#2152)
* Extend content

* Update changelog
2023-07-15 12:16:19 +02:00
30f1a3514a Feature/add hints to activity types in create or edit activity dialog (#2150)
* Add hints

* Update changelog
2023-07-15 11:31:05 +02:00
ed735e0b29 Feature/disable caching in health check endpoints for data providers (#2147)
* Disable caching in health check endpoint

* Update changelog
2023-07-15 10:54:19 +02:00
b89ccd2dde Release 1.289.0 (#2149) 2023-07-14 10:26:27 +02:00
df6d39377f Upgrade @types/lodash to version 4.14.195 (#2125) 2023-07-14 07:53:39 +02:00
d5d14497d6 Release 1.288.0 (#2146) 2023-07-12 19:56:16 +02:00
09c300661a Feature/improve language localization for german 20230711 (#2144)
* Improve i18n

* Update changelog
2023-07-12 19:53:55 +02:00
92382e0b4d Feature/improve loading state during filtering on allocations page (#2141)
* Improve loading state

* Update changelog
2023-07-11 21:41:12 +02:00
c25f532487 Improve product pages (#2143) 2023-07-11 21:40:45 +02:00
5d26d94586 Sort imports (#2142) 2023-07-11 20:27:54 +02:00
73b6784e9f Feature/beautify ampersand in asset profile names (#2138)
* Beautify ampersand

* Update changelog
2023-07-10 20:16:38 +02:00
6159f48a62 Feature/setup personal finance tools pages 2 (#2140) 2023-07-10 20:16:20 +02:00
7d34fba7c1 Release 1.287.0 (#2136) 2023-07-09 10:44:41 +02:00
c434b730a8 Feature/hide average buy price in position detail chart if no holding (#2133)
* Hide the average buy price if no holding

* Update changelog
2023-07-09 10:42:53 +02:00
2d23c566f1 Feature/setup personal finance tools pages (#2135) 2023-07-09 10:42:10 +02:00
ba220eaee9 Bugfix/fix sorting by currency in activities table (#2122)
* Fix sorting by currency

* Update changelog
2023-07-09 09:38:48 +02:00
09023214ce Feature/French translation update (#2130)
* French translation update

* Update changelog
2023-07-07 21:26:51 +02:00
1ceabb6e6b Feature/refactor blog articles to standalone components (#2117)
* Refactor blog articles to standalone components

* Update changelog
2023-07-04 18:42:40 +02:00
421072c7fa Release 1.286.0 (#2120) 2023-07-03 22:30:33 +02:00
0d421e7181 Bugfix/fix adding 'Item' and 'Liability' activities (#2119)
* Fix adding activities of type item and liability

* Update changelog
2023-07-03 22:29:00 +02:00
f5180ce88f Improve wording (#2118) 2023-07-03 10:10:08 +02:00
aabf27dc96 Remove empty style files (#2116) 2023-07-02 08:17:39 +02:00
421809ae95 Release 1.285.0 (#2114) 2023-07-01 19:14:26 +02:00
d3234f9e77 Feature/add blog post exploring the path to fire (#2113)
* Add blog post: Exploring the Path to FIRE

* Update changelog
2023-07-01 18:33:31 +02:00
a40be2f744 Feature/extract locales 20230701 (#2112)
* Improve i18n

* Update changelog
2023-07-01 18:30:09 +02:00
e62da06c5c Feature/extend scraper configuration support (#2110)
* Extend scraper configuration support

* Update changelog
2023-07-01 11:08:10 +02:00
b7f635bdfc Increase frequency (#2111) 2023-07-01 11:06:34 +02:00
0a465f125d Feature/add pagination to market data table in admin control panel (#2108)
* Add pagination

* Update changelog
2023-07-01 10:49:00 +02:00
c02e390bc1 Rename Slack channel to community (#2091) 2023-06-28 15:59:38 +02:00
f9bec0d793 Release 1.284.0 (#2106) 2023-06-27 18:47:44 +02:00
2f44748f79 Feature/upgrade internet identity dependencies to version 0.15.7 (#2105)
* Disable crossOriginOpenerPolicy

* Upgrade Internet Identity dependencies

* Update changelog
2023-06-27 18:46:03 +02:00
97504756be Feature/add currency to cash balance in create or update account dialog (#2104)
* Add currency as text suffix to cash balance

* Update changelog
2023-06-27 18:33:50 +02:00
6a802a62a0 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
2023-06-26 18:38:24 +02:00
51ca26bb4d Release 1.283.5 (#2103) 2023-06-25 13:39:39 +02:00
2ecc8dbc4e Release 1.283.4 (#2101) 2023-06-24 18:41:37 +02:00
c0e0e2401e Release 1.283.3 (#2100) 2023-06-24 18:25:44 +02:00
1a30c180bc Release 1.283.2 (#2099) 2023-06-24 18:05:12 +02:00
39d4f80f36 Release 1.283.1 (#2098) 2023-06-24 17:49:12 +02:00
3693091ad6 Release 1.283.0 (#2097) 2023-06-24 17:14:04 +02:00
bf52f1137d Feature/setup helmet (#2096)
* Setup helmet

* Update changelog
2023-06-24 17:12:05 +02:00
54ea6c84b4 Feature/add caching for quotes (#2095)
* Add caching for quotes

* Update changelog
2023-06-24 13:06:28 +02:00
689e50ae1a Improve bug report template (#2092)
* Update Slack community link
* Remove checkboxes
2023-06-23 08:52:24 +02:00
677757fdf0 Feature/improve import dividends dialog (#2086)
* Improve dialog

* Add loading indicator
* Improve selected item of holding selector

* Update changelog
2023-06-22 20:29:50 +02:00
58d9816f01 Feature/extend symbol search component by asset sub classes (#2087)
* Add asset sub classes

* Update changelog
2023-06-21 16:09:18 +02:00
5f3d445f1d Release 1.282.0 (#2085) 2023-06-19 20:58:30 +02:00
fce6caebc2 Fix arm64 prisma binary target (#2082)
* Fix arm64 prisma binary target

* Update changelog
2023-06-19 20:56:28 +02:00
d0a4f5c000 Feature/added ability to add asset profile in admin control panel (#2075)
* Added ability to add asset profile in admin control panel

* Update changelog
2023-06-19 20:50:11 +02:00
b5e2a3aa91 Feature/harmonize use of permissions on about and landing page (#2084)
* Harmonize use of permissions

* About page
* Landing page

* About changelog
2023-06-19 20:29:36 +02:00
f47883fb0b Feature/add icon to external links in footer (#2083)
* Add icon for external links

* Update changelog
2023-06-19 20:29:12 +02:00
2932744a68 Feature/improve language localization for german 20230618 (#2081)
* Update translations

* Update changelog
2023-06-19 19:40:10 +02:00
73c0f02e06 Add the final translations for Portuguese (#2079) 2023-06-18 08:59:16 +02:00
382fe24f29 Release 1.281.0 (#2080) 2023-06-17 17:38:30 +02:00
908876ca6e Feature/setup language localization for portuguese (#2076)
* Set up Portuguese

* Update changelog
2023-06-17 17:26:40 +02:00
99cf9f8802 Feature/translation pt 2 (#2074)
* Add more Portuguese translations
2023-06-16 20:47:06 +02:00
7444ff97fc Feature/translation pt (#2073)
* Complete Portuguese translations for home screen and various other Portuguese translations
2023-06-15 08:34:47 +02:00
834a48466e Feature/add liabilities to feature page (#2072)
* Add section for liabilities

* Update changelog
2023-06-14 20:08:04 +02:00
a9526430c2 Improve column headers in holdings table for mobile (#2071)
* Improve column headers in holdings table for mobile

* Update changelog
2023-06-13 21:00:56 +02:00
fce3b2084e Feature/extract symbol search to component (#2003) (#2056)
* Extract symbol search to component (#2003)

* Update changelog
2023-06-13 20:36:16 +02:00
f5a50a95de Feature/upgrade prisma to version 4.15.0 (#2070)
* Upgrade prisma to version 4.15.0

* Update changelog
2023-06-12 15:16:43 +02:00
06dfb91f82 Release 1.280.1 (#2069) 2023-06-10 21:41:39 +02:00
be36050d76 Release 1.280.0 (#2068) 2023-06-10 17:18:28 +02:00
7931e6950d Feature/add support for liabilities (#1789)
* Add support for liabilities

* Update changelog
2023-06-10 16:17:11 +02:00
04eb452e04 Add missing guards (#2067) 2023-06-10 16:16:27 +02:00
6f7e370fca Release 1.279.0 (#2066) 2023-06-10 12:21:11 +02:00
b4a126280f Bugfix/fix public page (#2065)
* Check for user in request because of public page

* Update changelog
2023-06-10 12:19:34 +02:00
2d009aacc4 Bugfix/handle value nullifcation for undefined object (#2064)
* Handle undefined object

* Update changelog
2023-06-10 12:01:26 +02:00
9116443305 Feature/support note in accounts (#2063)
* Add support for a note in accounts

* Update changelog
2023-06-10 12:01:13 +02:00
0adaf12a01 Add new French translations (#2057)
* Add new French translations

* Update changelog

Signed-off-by: Martin Vandenbussche <vandenbusschemartin@gmail.com>
2023-06-10 11:19:33 +02:00
b6562b6e2c Release 1.278.0 (#2062) 2023-06-09 21:14:38 +02:00
b0a4b09ef5 Feature/extract license to dedicated tab (#2061)
* Extract license to tab on about page

* Update changelog
2023-06-09 21:13:05 +02:00
ad8b9ad333 Bugfix/improve spacing in bechmark comparator (#2060)
* Improve spacing

* Update changelog
2023-06-09 20:52:51 +02:00
809956f210 Feature/display markets overview link in footer based on permission (#2059)
* Add check for permission

* Update changelog
2023-06-09 20:01:24 +02:00
6077bfa754 Feature/change direction of ellipsis icon to horizontal in tables (#2055)
* Change direction of ellipsis icon to horizontal

* Update changelog
2023-06-09 19:39:14 +02:00
09498bd804 Feature/refresh cryptocurrencies list 20230606 (#2049)
* Update cryptocurrencies.json

* Update changelog
2023-06-09 18:55:24 +02:00
fd84f4ec14 Change to routerLink (#2058) 2023-06-09 17:37:52 +02:00
c711a11d6e Feature/upgrade node.js from version 16 to 18 (#2053)
* Upgrade to Node.js 18

* Update changelog
2023-06-09 17:28:05 +02:00
8232b05f62 Feature/extend activity cloning by quantity (#2054)
* Allow to clone quantity

* Update changelog
2023-06-09 16:14:40 +02:00
0ea66aebcb Release 1.277.0 (#2052) 2023-06-07 17:35:19 +02:00
64087de3fc Bugfix/fix date format parsing in activities import (#2051)
* Fix date format parsing

* Update changelog
2023-06-07 17:33:35 +02:00
7082ff12f8 Feature/add semantic list structure to header navigation (#2044)
* Add semantic list structure (ul and li elements)

* Update changelog
2023-06-07 17:22:09 +02:00
1c7d92e15e Harmonize Slack community links (#2047) 2023-06-06 09:23:32 +02:00
a53461d257 Feature/migrate currency to unit in value component (#2043)
* Migrate currency to unit

* Update locales
2023-06-04 21:46:49 +02:00
d630fb900d Feature/add investment streaks (#2042)
* Add investment streaks

* Current streak
* Longest streak

* Add unit to value component

* Update changelog
2023-06-04 09:35:58 +02:00
51e8555fa5 Update link (#2040) 2023-06-04 09:30:35 +02:00
9db675b955 Feature/improve get symbol data endpoint (#2041)
* Add default value to query parameter

* Update changelog
2023-06-03 19:32:48 +02:00
45bd8ed029 Release 1.276.0 (#2039) 2023-06-03 13:39:30 +02:00
707fd31550 Feature/add changefreq to sitemap.xml (#2038)
* Add changefreq to /markets and /open

* Update changelog
2023-06-03 13:37:50 +02:00
6e5f0086a1 Bugfix/fix price when creating subscription (#2037)
* Fix price

* Update changelog
2023-06-03 13:26:05 +02:00
97bcd8ff49 Feature/enforce yyyy instead of yy as date format in activities import (#2036)
* Enforce yyyy (instead of yy)

* Update changelog
2023-06-03 10:19:50 +02:00
1809fc8a80 Feature/update url of ghostfolio slack channel (#2035)
* Update Slack url

* Update changelog
2023-06-03 08:28:36 +02:00
beb24f9bd4 Revert "Update Slack url" (#2034)
* Revert "Update Slack url (#2025)"

This reverts commit c48670ccdc.
2023-06-03 08:28:07 +02:00
ae57a188f5 Feature/improve tab navigations (#2031)
* Improve tab navigations

* Update changelog
2023-06-03 07:44:59 +02:00
23db85e940 Feature/add tabs to about page (#2030)
* Add tabs to about page

* Update changelog
2023-06-02 21:23:57 +02:00
bd8bb1a36a Feature/remove ghostfolio in numbers section from about page (#2029)
* Remove Ghostfolio in Numbers section

* Update changelog
2023-06-01 19:38:38 +02:00
c48670ccdc Update Slack url (#2025) 2023-05-31 17:10:59 +02:00
fc019002e2 Release 1.275.0 (#2028) 2023-05-30 21:22:25 +02:00
4282cb66b8 Feature/add localized versions to footer (#2027)
* Add links to localized versions

* Update changelog
2023-05-30 21:20:44 +02:00
1d0ba5fe4b Bugfix/fix indirect calculation in exchange rate service for specific date (#2026)
* Fix indirect calculation

* Update changelog
2023-05-30 21:05:53 +02:00
24cfb26c5b Feature/improve language localization for german 20230529 2 (#2022)
* Improve locales

* Update changelog
2023-05-30 20:48:09 +02:00
26a70aa208 Release 1.274.0 (#2021) 2023-05-29 19:43:52 +02:00
ab7e050066 Feature/extend footer by navigation (#2020)
* Extend footer

* Update changelog
2023-05-29 19:41:21 +02:00
26b1fd6572 Feature/localize meta description (#2019)
* Add localized meta description

* Update changelog
2023-05-29 18:02:18 +02:00
d7e682b65a Feature/extend testimonial section (#2018)
* Add testimonials

* Update changelog
2023-05-29 14:16:17 +02:00
f589ccb775 Feature/improve language localization for german 20230529 (#2017)
* Improve locales

* Update changelog
2023-05-29 13:24:18 +02:00
206b6567fd Feature/improve activities import dialog (#2016)
* Improve activities import dialog

* Update changelog
2023-05-29 07:45:55 +02:00
6857e0314f Feature/add support for localized routes in spanish (#2015)
* Add support for localized routes in Spanish

* Update changelog
2023-05-28 22:48:27 +02:00
c8682a7393 Release 1.273.0 (#2014) 2023-05-28 10:31:37 +02:00
144b6b2211 Bugfix/handle undefined in decode data source (#2013)
* Handle undefined data source

* Update changelog
2023-05-28 10:29:52 +02:00
16a5ace4be Introduced stepper to import activities (#1990)
* Introduced stepper to import activities

* Update changelog
2023-05-28 10:21:07 +02:00
b24ddc30c9 Add localized routes for fr, it and nl (#2012) 2023-05-28 10:18:39 +02:00
19333ab084 Fix warnings (#2011) 2023-05-27 10:48:10 +02:00
7529a7a26c Feature/support localized routes (#2009)
* Support localized routes

* Update changelog
2023-05-27 09:53:48 +02:00
21ebaae6ef Add Cloud vs. Self-hosted (#2010) 2023-05-27 09:47:58 +02:00
3bc8b3c836 Feature/add link to manage benchmarks in benchmark comparator component (#2007)
* Add link to manage benchmarks

* Update changelog
2023-05-27 09:34:02 +02:00
bb9415cc15 Release 1.272.0 (#2005) 2023-05-26 20:43:09 +02:00
b3baeb8a5d Feature/support case insensitive names in portfolio proportion chart (#2004)
* Sum up case insensitive names

* Update changelog
2023-05-26 20:41:33 +02:00
1f393e78f6 Feature/improve error handling in delete user endpoint (#2000)
* Improve error handling

* Update changelog
2023-05-25 17:27:33 +02:00
215f5eafa6 Feature/set asset profile as benchmark (#2002)
* Set asset profile as benchmark

* Update changelog

Co-authored-by: Arghya Ghosh <arghyag5@gmail.com>
2023-05-24 21:22:32 +02:00
1916e5343d Feature/decrease table density (#2001)
* Decrease table density

* Update changelog
2023-05-24 19:34:12 +02:00
fa9863fc54 Feature/upgrade ionicons to version 7.1.0 (#1997)
* Upgrade ionicons to version 7.1.0

* Update changelog
2023-05-23 14:45:21 +02:00
7bf48ef351 Improve style on about page (#1998)
* Center changelog button

* Update changelog
2023-05-22 20:20:16 +02:00
faef3606fd Feature/improve breadcrumb navigation style in blog posts for mobile (#1996)
* Improve style for mobile

* Update changelog
2023-05-21 07:50:45 +02:00
d0ccd4d238 Release 1.271.0 (#1995) 2023-05-20 18:13:41 +02:00
51e3650790 Feature/add blog post unlock your financial potential (#1994)
* Add blog post: Unlock your Financial Potential with Ghostfolio

* Update changelog
2023-05-20 18:12:12 +02:00
db29e2b666 Feature/extend financial modeling prep service (#1989)
* Add getHistorical() and search() logic

* Update changelog
2023-05-20 11:10:07 +02:00
655a68a847 Feature/change uptime to last 90 days (#1993)
* Change uptime to last 90 days

* Update changelog
2023-05-20 11:07:53 +02:00
86296b3591 Feature/improve local number formatting in value component (#1992)
* Improve local number formatting

* Update changelog
2023-05-20 10:53:04 +02:00
73c127f10c Bugfix/fix vertical alignment in gf toggle (#1991)
* Fix alignment

* Update changelog
2023-05-20 10:10:53 +02:00
cf4c981cd9 Release 1.270.1 (#1988) 2023-05-19 15:36:20 +02:00
1b9541b933 Release 1.270.0 (#1987) 2023-05-19 15:11:46 +02:00
5bca8de44e Feature/check for duplicates in dividend import (#1986)
* Check for duplicates in dividend import

* Update changelog
2023-05-19 15:05:29 +02:00
136c4bf50b Bugfix/fix data source transformation in dividends import (#1985)
* Fix data source transformation

* Update changelog
2023-05-19 14:47:20 +02:00
4d700e3b83 Feature/add error message if importing duplicates (#1984)
* Add error messages if import include duplicates

* Update changelog
2023-05-19 13:16:40 +02:00
740fa6fc84 Feature/upgrade prisma to version 4.14.1 (#1983)
* Upgrade prisma to version 4.14.1

* Update changelog
2023-05-19 10:16:08 +02:00
cdb8dc72c7 Feature/improve mobile layout of portfolio summary (#1982)
* Improve layout for mobile

* Update changelog
2023-05-18 22:36:30 +02:00
4b3afb5c97 Feature/extract locales 20230518 (#1980)
* Update locales

* Update changelog
2023-05-18 19:27:58 +02:00
abf208432a Feature/extend account detail dialog by cash balance and equity (#1978)
* Add cash balance and equity

* Update changelog
2023-05-18 19:05:22 +02:00
19e6df4fb2 Fix exception (#1979) 2023-05-18 19:03:27 +02:00
7fc3fff431 Bugfix/fix storybook setup (#1976)
* Fix Storybook setup

* Update changelog
2023-05-18 17:51:38 +02:00
edd690850c Feature/setup open startup page (#1967)
* Setup Open Startup page

* Update changelog
2023-05-18 12:31:36 +02:00
302339e1cd Add Postgres connect_timeout for M1 "Can't reach database" error (#1472)
* Add postgres connect_timeout

* Update changelog
2023-05-18 12:02:07 +02:00
739796bc79 Clean up (#1965) 2023-05-13 18:10:45 +02:00
9c30139b86 FIX #1951: select checkbox state fix for duplicates (#1958)
* FIX #1951: select checkbox state fix for duplicates

* Update changelog
2023-05-13 12:15:11 +02:00
0af528b649 fix(launch.json): set "cwd" to apps/api (#1962) 2023-05-13 11:22:04 +02:00
9636c87a2e Release 1.269.0 (#1961) 2023-05-11 09:54:34 +02:00
ad46fb6d61 Feature/add financial modeling prep as data source type (#1960)
* Add Financial Modeling Prep as new data source type

* Update changelog
2023-05-11 09:52:23 +02:00
8e000baef2 fix update activity, hide update cash balance on edit (#1959)
* Fix update activity, hide update cash balance on edit

* Update changelog
2023-05-11 09:11:44 +02:00
a2e1209196 Feature/introduce admin settings (#1957)
* Introduce admin settings

* Update changelog
2023-05-10 17:56:17 +02:00
ef4a75d1f0 Feature/improve market price of first buy date in chart of position detail dialog (#1956)
* Improve market price on first buy date: market price or average price

* Update changelog
2023-05-09 20:33:15 +02:00
3db20feb54 Release 1.268.0 (#1953) 2023-05-08 22:25:44 +02:00
b9ec381ea2 Feature/upgrade yahoo finance2 to version 2.4.1 (#1952)
* Upgrade yahoo-finance2 to version 2.4.1

* Update changelog
2023-05-08 22:24:12 +02:00
7d6a74a67d Feature/improve check for duplicates in import #1932 (#1940)
* Improve check for duplicates in import

* Update changelog
2023-05-08 21:45:59 +02:00
b923cf7752 Fix docker compose services startup (#1947)
* Fix docker compose services startup

* Update changelog
2023-05-08 20:13:45 +02:00
e32e457ff8 Release 1.267.0 (#1945) 2023-05-07 17:59:04 +02:00
32c1e6b390 Feature/upgrade to nx 16.0 (#1943)
* Upgrade to Nx 16.0

* Update changelog
2023-05-07 17:56:57 +02:00
b42c0c8355 Add comment (#1944) 2023-05-07 17:33:55 +02:00
7140ed8512 Feature/add stripe checkout to pricing page (#1942)
* Add Stripe checkout directly to pricing page

* Update changelog
2023-05-07 16:51:51 +02:00
27d9b075ce Feature/improve interstitial for subscription (#1941)
* Improve style and wording

* Update changelog
2023-05-07 10:38:12 +02:00
5249257dd8 Feature/improve platform managment in admin control (#1939)
* Refactoring, fix tab style, add account count

* Update changelog
2023-05-06 20:24:16 +02:00
606f6159c4 Feature/improve language localization for german 20230506 (#1938)
* Add missing translations

* Update changelog
2023-05-06 17:24:53 +02:00
2e095603b5 Release 1.266.0 (#1937) 2023-05-06 11:34:28 +02:00
3a99b81ade Bugfix/add fallback in yahoo finance service (#1935)
* Add fallback to use quoteSummary(symbol) if quote(symbols) fails

* Update changelog
2023-05-06 11:17:41 +02:00
577a487301 Fix import (#1936) 2023-05-06 11:16:44 +02:00
086d43376c Feature/add dev community logo to landing page (#1934)
* Add DEV Community

* Update changelog
2023-05-06 10:56:21 +02:00
31a4c2ff1f Sort imports (#1933) 2023-05-06 10:06:24 +02:00
6a1fad611c Bugfix/add missing data source in activities import (#1930)
* Add dataSource

* Update changelog
2023-05-06 09:45:18 +02:00
e1892d2870 Feature/platform management (#1922)
* Added platform management to admin control panel

* Update changelog
2023-05-06 09:44:28 +02:00
8ba15f8f72 Optionally update cash balance when adding activity (#1926)
* Optionally update cash balance when adding activity

* Update changelog
2023-05-06 09:01:09 +02:00
876b66f324 Feature/upgrade prisma to version 4.13.0 (#1920)
* Upgrade prisma to version 4.13.0

* Update changelog
2023-05-05 07:39:51 +02:00
2c5bfb19d3 Feature/upgrade class transformer and class validator (#1925)
* Upgrade class-transformer and class-validator

* Update changelog
2023-05-03 16:24:05 +02:00
1bb94a04e3 Release 1.265.0 (#1921) 2023-05-01 19:28:17 +02:00
e3c9316486 Feature/improve tooltip of portfolio proportion chart (#1919)
* Hide title

* Update changelog
2023-05-01 19:26:49 +02:00
c19984c3d0 Bugfix/fix missing platform name in allocations by platform (#1918)
* Fix missing platform name

* Update changelog
2023-05-01 18:55:34 +02:00
9002c20165 Release 1.264.0 (#1916) 2023-05-01 17:46:28 +02:00
15c96a9757 Feature/add allocations by platform chart (#1915)
* Add allocations by platform

* Update changelog
2023-05-01 17:44:35 +02:00
1ca3792a4b Feature/clean up initial values from x ray (#1914)
* Clean up initial (original) values from X-Ray

* Refactor current to valueInBaseCurrency

* Update changelog
2023-05-01 17:16:02 +02:00
90fe467114 Feature/deprecate base currency (#1913)
* Deprecate BASE_CURRENCY

* Update changelog
2023-05-01 15:45:59 +02:00
e61b3b34a7 Eliminate getSymbolProfilesBySymbols() (#1912) 2023-05-01 15:45:33 +02:00
1326418ffc Release 1.263.0 (#1911) 2023-04-30 19:21:35 +02:00
a5f0f48ddb Fix accounts page (#1908)
* Add guards

* Update changelog
2023-04-30 19:20:17 +02:00
e500ccb61b Feature/introduce env variable data source exchange rates and data source import (#1910)
* Introduce env variables DATA_SOURCE_EXCHANGE_RATES and DATA_SOURCE_IMPORT

* Update changelog
2023-04-30 18:26:34 +02:00
4090b03406 Release 1.262.0 (#1906) 2023-04-29 10:41:28 +02:00
431d1d5fec Feature/extract locales 20230429 (#1905)
* Update locales

* Update changelog
2023-04-29 10:39:17 +02:00
d74d79198b Feature/add labels to tabs (#1847)
* Add labels

* Update changelog
2023-04-29 10:15:55 +02:00
623a284ba4 Feature/add and update historical data in bulk (#1904)
* Upsert historical data in bulk

* Update changelog
2023-04-29 10:12:50 +02:00
f79c36edbb Fix import (#1902) 2023-04-29 10:00:04 +02:00
f4c748f67a Feature/extend support for impersonation mode (#1898)
* Support impersonation of all users for local development

* Update changelog
2023-04-28 21:02:24 +02:00
672d8dfab2 Fix/holdings always include cash position (#1897)
* Improved holdings table showing cash position also when the filter contains accounts

* Update changelog
2023-04-28 12:08:45 +02:00
0464adccce Fix script (#1885) 2023-04-26 13:41:05 +02:00
c3df6c3194 Release 1.261.0 (#1896) 2023-04-25 20:24:39 +02:00
29d53c7df4 Feature/add distance to now to the subscription expiration date (#1895)
* Add distance to now

* Update changelog
2023-04-25 20:23:07 +02:00
7b77dc044a Feature/add state to market data database schema (#1893)
* Add state (CLOSE / INTRADAY) to MarketData

* Update changelog
2023-04-25 20:09:12 +02:00
67e758365f feature: allow to delete all activities of a user (#1880)
* Allow to delete all activities of a user

* Update changelog
2023-04-23 19:49:32 +02:00
475231ffd8 Release 1.260.0 (#1892) 2023-04-23 12:04:06 +02:00
513a564e2c Restructure services (#1891) 2023-04-23 12:02:01 +02:00
cddea0401f Feature/add data source as unique constraint to market data schema (#1889)
* Add dataSource as unique constraint to MarketData schema

* Update changelog
2023-04-23 11:13:08 +02:00
3dafbf7fef Add schema synchronization (#1890) 2023-04-23 11:02:12 +02:00
fcd75414be Update date (#1860) 2023-04-23 11:01:53 +02:00
c1b5bfff8c Bugfix/remove sort header in comment column of market data table (#1888)
* Remove sort header

* Update changelog
2023-04-23 10:49:11 +02:00
3c322cca0d Release 1.259.0 (#1887) 2023-04-22 16:05:44 +02:00
e965d12e31 Feature/add health check endpoints (#1886)
* Add health check endpoints

* Update changelog
2023-04-22 16:03:45 +02:00
3daf55a0dd Bugfix/remove sort header in comment column of activities table (#1883)
* Remove sort header

* Update changelog
2023-04-22 14:44:45 +02:00
aafedd5f75 Feature/increase robustness if live data is missing (#1884)
* Continuously persist today's market data

* Add fallback to historical market data if data provider does not provide live data

* Update changelog
2023-04-22 14:43:57 +02:00
32956ae04c Fix: performance column header alignment (#1881)
* Fix: performance column header alignment

* Update changelog
2023-04-21 18:05:51 +02:00
bfd0241b2d update target in proxy to work with api in locahost (#1875)
Co-authored-by: francisco <francisco@innonova.ch>
Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
2023-04-20 18:51:35 +02:00
5eff8402db Release/1.258.0 (#1878)
* Release 1.258.0
  * Introduce data source mapping

* Update changelog
2023-04-20 09:07:22 +02:00
ffa020ee2a Release 1.257.0 (#1872) 2023-04-18 20:36:22 +02:00
80a3668aa9 Bugfix/fix world map chart component (#1871)
* Clone countries before manipulation

* Update changelog
2023-04-18 20:32:18 +02:00
7378900050 Bugfix/fix currency inconsistency with GBX and GBp in eod historical data service (#1869)
* Fix currency inconsistency (GBX vs. GBp)

* Update changelog
2023-04-18 20:31:33 +02:00
9be457943c Feature/introduce allocations by etf provider (#1870)
* Introduce allocations by etf provider

* Update changelog
2023-04-18 20:13:03 +02:00
93454c6c15 Release 1.256.0 (#1868) 2023-04-17 14:11:59 +02:00
fccbd76993 Bugfix/fix style of apply current market price button (#1866)
* Fix styles

* Update changelog
2023-04-17 14:09:55 +02:00
922876a893 Feature/add yahoo finance data enhancer (#1865)
* Add Yahoo Finance data enhancer

* Update changelog
2023-04-17 14:09:27 +02:00
654446f068 Feature/improve handling of jobs (#1864)
* Improve handling of jobs
  * Remove jobs on complete
  * Refactor jobs removal

* Update changelog
2023-04-16 19:56:19 +02:00
947460abdd Bugfix/fix ids of gather asset profile process jobs (#1863)
* Fix job ids

* Update changelog
2023-04-16 09:49:27 +02:00
c5635b0050 Release 1.255.0 (#1862) 2023-04-15 16:00:47 +02:00
8a3a6308a3 Feature/make system message expandable (#1861)
* Make system message expandable

* Update changelog
2023-04-15 15:59:04 +02:00
290a07fe79 Feature/upgrade prisma to version 4.12.0 (#1845)
* Update prisma to version 4.12.0

* Update changelog
2023-04-15 15:32:02 +02:00
4c907d56f0 Improve message (#1859) 2023-04-15 15:09:15 +02:00
56b437ca74 Bugfix/improve info message style (#1858)
* Improve info message style

* Update changelog
2023-04-15 15:08:56 +02:00
e23ff33e6f Feature/skip job creation for manual data source without scraper configuration (#1857)
* Skip job creation for MANUAL data source without scraper configuration

* Update changelog
2023-04-15 11:54:47 +02:00
f2d206262e Release 1.254.0 (#1856) 2023-04-14 19:58:49 +02:00
1ed5690b33 Feature/improve queue jobs implementation (#1855)
* Improve queue jobs implementation

* Update changelog
2023-04-14 19:57:23 +02:00
4451514ec5 Release 1.253.0 (#1854) 2023-04-14 07:01:34 +02:00
8f73f85276 Bugfix/fix background color of dialogs in dark mode (#1853)
* Fix background color

* Update changelog
2023-04-13 08:47:19 +02:00
9a5d7b664b Release 1.252.2 (#1852) 2023-04-11 18:06:34 +02:00
7d2d1d971a Feature/deprecate get auth endpoint (#1851)
* Deprecate GET auth endpoint

* Update documentation

* Update changelog
2023-04-11 18:04:18 +02:00
d111493eed Release 1.252.1 (#1849) 2023-04-10 20:52:34 +02:00
e975f92a96 Release 1.252.0 (#1848) 2023-04-10 18:03:02 +02:00
739cb4242d Feature/decrease density of theme (#1846)
* Decrease density

* Update changelog
2023-04-10 17:59:29 +02:00
a37eebc9f1 Feature/upgrade nestjs from version 9.1.4 to 9.4.0 (#1843)
* Upgrade nestjs from version 9.1.4 to 9.4.0

* Update changelog
2023-04-10 13:50:27 +02:00
e92730879e Feature/migrate dialog components to angular material 15 (#1844)
* Migrate MatDialog

* Update changelog
2023-04-10 13:49:53 +02:00
464973f9b0 Feature/migrate form components to angular material 15 (#1842)
* Upgrade @angular/cdk

* Upgrade form components to Angular Material 15

* Update changelog
2023-04-10 10:59:44 +02:00
f6228c099f Migrate MatRadio (#1841) 2023-04-10 09:23:16 +02:00
a57fdfb2bb Feature/migrate slide toggle components to angular material 15 (#1840)
* Upgrade @angular/material

* Change MatSlideToggle to MatCheckbox

* Update changelog
2023-04-09 09:33:36 +02:00
24716f0561 Migrate checkbox and chips components to Angular Material 15 (#1839) 2023-04-09 08:55:09 +02:00
3453100afd Feature/migrate various components to angular material 15 part 2 (#1838)
* Migrate tooltips to Angular Material 15

* Migrate tabs to Angular Material 15
2023-04-09 08:54:32 +02:00
84de2c0c68 Migrate card components to Angular Material 15 (#1837) 2023-04-08 17:08:30 +02:00
1b7b082003 Feature/migrate various components to angular material 15 (#1836)
* Migrate components to Angular Material 15

* Update changelog
2023-04-08 15:33:27 +02:00
1928c2c2cc Release 1.251.0 (#1834) 2023-04-07 19:33:26 +02:00
52e7a7886d Feature/migrate libs components to angular material 15 (#1833)
* Migrate to Angular Material 15

* Update changelog
2023-04-07 17:10:03 +02:00
36298b217e Feature/improve tick abbreviation function (#1828)
* Keep the value if value smaller than 1000

* Update changelog
2023-04-07 17:09:34 +02:00
9bce57894e Feature/increase historical market data gathering to 10 years (#1830)
* Increase historical market data gathering of currency pairs to 10+ years

* Update changelog
2023-04-07 16:35:37 +02:00
9d6bb325cd Improve initialization (#1832) 2023-04-07 16:33:55 +02:00
a5f833c612 Feature/upgrade angular and nx 20230405 (#1826)
* Upgrade angular and Nx

* Update changelog
2023-04-06 19:18:23 +02:00
732b14c6ab Feature/improve activities import for csv files of ibkr (#1824)
* Improve import for csv files by Interactive Brokers

* Update changelog
2023-04-05 20:09:00 +02:00
b74a042da8 Feature/change auth endpoint from get to post (#1823)
* Change auth endpoint from GET to POST
  * Login with security token
  * Login with Internet Identity

* Update changelog
2023-04-05 18:10:29 +02:00
d55c052f57 Feature/improve content of pricing and faq pages (#1822)
* Improve content

* Update changelog
2023-04-03 17:39:32 +02:00
864f585efa Release/1.250.0 (#1821) 2023-04-02 09:47:13 +02:00
6d56146054 Feature/add support for multiple subscription offers (#1818)
* Setup for multiple subscription offers

* Update changelog
2023-04-02 09:44:13 +02:00
2c9f29a3c6 Feature/improve handling of platforms in accounts import (#1820)
* Improve handling of platforms

* Fix issue with pagination

* Update changelog
2023-04-01 10:51:55 +02:00
9bef2e960c Feature/ignore first item in portfolio evolution chart (#1816)
* Ignore first item in portfolio evolution chart

* Update changelog
2023-04-01 10:29:39 +02:00
17b8c41673 Add test file (#1810) 2023-03-30 08:24:44 +02:00
f0afbd7346 Release 1.249.0 (#1815) 2023-03-27 20:38:46 +02:00
5dc7429f6a Feature/add testimonials (#1814)
* Add testimonials

* Update changelog
2023-03-27 20:36:49 +02:00
7b39b32293 Feature/improve allocations page (#1813)
* Improve loading state

* Update changelog
2023-03-26 17:18:48 +02:00
e5b5a9e7e9 Feature/improve language localization for german (#1809)
* Improve language localization

* Update changelog
2023-03-26 17:17:48 +02:00
1f3511368a Feature/always show label in value component (#1812)
* Always show label while loading

* Update changelog
2023-03-26 16:59:52 +02:00
b37df2c84f Bugfix/fix algebraic sign in value component (#1811)
* Fix algebraic sign by resetting member variables

* Update changelog
2023-03-26 16:16:24 +02:00
f92ba54060 Release/1.248.0 (#1808) 2023-03-25 17:45:51 +01:00
a3bbd4030e Improve blog post and add images (#1807) 2023-03-25 17:42:41 +01:00
4b30da2d92 Feature/conditionally hide platform selector (#1805)
* Hide platform selector conditionally

* Update changelog
2023-03-25 16:46:46 +01:00
93d082afbb Feature/add blog post 1000 stars on GitHub (#1804)
* Add blog post: 1000 Stars on GitHub

* Add breadcrumb navigation

* Update changelog
2023-03-25 14:28:06 +01:00
0c85380dbf Feature/refactor portfolio calculator (#1803)
* Refactor chart calculation in portfolio calculator

* Update changelog
2023-03-25 12:20:42 +01:00
fb576376dc Feature/upgrade ng extract i18n merge (#1802)
* Upgrade ng-extract-i18n-merge

* Extract locales

* Update changelog
2023-03-24 17:34:27 +01:00
ff111d4c6c Release 1.247.0 (#1801) 2023-03-23 19:26:01 +01:00
bc6e9a8b68 Bugfix/fix total amount calculation in portfolio evolution chart (#1799)
* Fix total amount calculation

* Update changelog
2023-03-23 19:23:31 +01:00
bd1963ec26 Feature/add asset and asset sub class to search endpoint (#1795)
* Add asset and asset sub class to search endpoint

* Update changelog
2023-03-23 19:11:38 +01:00
a0bec9e97f Feature/remove mail address part 2 (#1800)
* Remove mail address and update Slack url

* Update changelog
2023-03-23 14:14:31 +01:00
c45df20d88 Sort imports (#1797) 2023-03-21 19:34:40 +01:00
fa1d669633 Feature/upgrade prisma to version 4.11.0 (#1798)
* Upgrade prisma to version 4.11.0

* Update changelog
2023-03-20 20:08:08 +01:00
1009b462e9 Feature/add subscription expiration dates to the admin control panel (#1796)
* Add expiration date

* Update changelog
2023-03-19 12:03:47 +01:00
b404858904 Release 1.246.0 (#1794) 2023-03-18 10:36:40 +01:00
7ec033577f Feature/extend trackinsight data enhancer by isin (#1793)
* Extend data enhancer by isin

* Update changelog
2023-03-18 10:34:50 +01:00
c8ca82b803 Feature/extend data source eod historical data by asset class and isin (#1791)
* Extend EodHistoricalDataService

* asset and asset sub class
* isin

* Update changelog
2023-03-18 10:09:11 +01:00
5db2faa17d Bugfix/fix border color in fire calculator (#1792)
* Fix border color

* Update changelog
2023-03-17 19:37:36 +01:00
1605fb8d48 Feature/improve language localization for data gathering (#1790)
* Improve locales

* Update changelog
2023-03-16 20:54:34 +01:00
b6a7804a26 Refactoring (#1784) 2023-03-14 10:46:11 +01:00
de31381fd9 Release 1.245.0 (#1787) 2023-03-12 14:33:00 +01:00
0d92b8d8bb Reduce search requests (#1786) 2023-03-12 14:29:22 +01:00
7c6ff776d9 Feature/add search functionality for eod historical data (#1783)
* Add search functionality for EOD_HISTORICAL_DATA

* Update changelog
2023-03-12 13:13:34 +01:00
e37a34ed6c Feature/improve exchange rate service for specific date (#1785)
* Calculate exchange rate indirectly via base currency

* Update changelog
2023-03-12 12:19:13 +01:00
c4d9c00f92 Feature/upgrade ngx device detector to version 5.0.1 (#1782)
* Upgrade ngx-device-detector to version 5.0.1

* Update changelog
2023-03-12 10:14:35 +01:00
3af8be89e3 Feature/improve usability of fire calculator (#1779)
* Improve usability

* Add debounce
* Persist annualInterestRate
* Partially disable date picker

* Update changelog
2023-03-12 09:55:55 +01:00
0f1db71604 Release 1.244.0 (#1778) 2023-03-09 22:14:28 +01:00
fce9e7fb0c Feature/extend fire calculator by retirement date (#1748)
* Extend fire calculator by retirement date

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-03-09 22:12:01 +01:00
6301c0c21c Clean up (#1767) 2023-03-09 13:33:41 +01:00
30bb484d5a Release 1.243.0 (#1777) 2023-03-08 20:25:27 +01:00
f88ee5e5a0 Feature/improve validation for manual currency (#1769)
* Improve validation

* Update changelog
2023-03-08 20:23:03 +01:00
73b5030972 Remove documentation of BASE_CURRENCY (#1776) 2023-03-08 20:22:39 +01:00
a69a3442ab Feature/add coingecko as default data source (#1775)
* Add CoinGecko as a default data source

* Update changelog
2023-03-08 20:13:53 +01:00
d4dff744b5 Sort imports (#1770) 2023-03-07 22:05:02 +01:00
62c93ad99d Make NODE_ENV optional in production
* Make NODE_ENV optional

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-03-06 21:02:20 +01:00
1e42d6bffa Feature/harmonize axis style of charts (#1768)
* Harmonize axis style

* Update changelog
2023-03-06 19:58:43 +01:00
002ac29f2f Feature/remove environment variable for custom symbols (#1766)
* Remove environment variable

* Update changelog
2023-03-05 09:46:38 +01:00
20ccf389e9 Release 1.242.0 (#1765) 2023-03-04 10:32:51 +01:00
a2f99ed4d2 Feature/upgrade ngx skeleton loader to version 7.0.0 (#1758)
* Upgrade ngx-skeleton-loader to version 7.0.0

* Update changelog
2023-03-04 10:30:15 +01:00
cc6320acfd Feature/simplify database seeding (#1763)
* Simplify database seeding

* Update documentation

* Update changelog
2023-03-04 10:16:16 +01:00
261a0fb0b9 Refactor AuthInterceptor (#1764)
* Refactor AuthInterceptor

* Refactor JwtStrategy
2023-03-04 10:13:04 +01:00
cfc05cce41 Resolve segmentation fault during prisma db migrations (#1761)
* Resolves segmentation fault during prisma db migrations by downgrading Node.js from version 18 to 16

* Update changelog
2023-03-02 19:28:58 +01:00
1f15b70134 Release 1.241.0 (#1760) 2023-03-01 20:53:59 +01:00
a5b49b286d Feature/filter item activities from search results (#1759)
* Filter ITEM activities from search results

* Update changelog
2023-03-01 20:52:12 +01:00
f3333f24da Feature/upgrade stripe dependencies 20230226 (#1755)
* Upgrade Stripe dependencies

* Consider language of user in Stripe checkout

* Update changelog
2023-03-01 20:25:48 +01:00
cad8f0d0e2 Feature/upgrade twitter api v2 to version 1.14.2 (#1754)
* Update twitter-api-v2 to version 1.14.2

* Update changelog
2023-02-27 08:17:13 +01:00
edd3e75730 Release 1.240.0 (#1753) 2023-02-26 17:13:50 +01:00
ab68c2c69a Bugfix/fix feature graphic of umbrel blog post (#1752)
* Fix feature graphic

* Update changelog
2023-02-26 17:12:38 +01:00
cbb95f21a3 Feature/support manual currency for unit price (#1751)
* Support manual currency for unit price

* Update changelog
2023-02-26 17:10:13 +01:00
74d3954335 Release 1.239.0 (#1750) 2023-02-25 20:28:30 +01:00
92449b0369 Feature/remove rimraf (#1739)
* Remove rimraf

* Update changelog
2023-02-25 20:26:56 +01:00
65276483e0 Feature/add umbrel blog post (#1749)
* Add blog post: Ghostfolio meets Umbrel

* Update changelog
2023-02-25 20:14:33 +01:00
dde0d1e465 Add linux/arm/v7 (#1741) 2023-02-25 11:34:22 +01:00
3ad802c6f5 Release 1.238.0 (#1747) 2023-02-25 11:23:05 +01:00
b81377a682 Feature/rename example env file (#1734)
* Rename .env to .env.example

* Ignore .env file

* Update changelog
2023-02-25 11:20:38 +01:00
545180b88f Feature/add reddit and umbrel logos to landing page (#1745)
* Add Reddit and Umbrel logos

* Update changelog
2023-02-25 11:20:04 +01:00
a9819b9e25 Feature/upgrade zone.js to version 0.12.0 (#1740)
* Upgrade zone.js

* Update changelog
2023-02-25 10:33:59 +01:00
897e941e7a Feature/add data provider info to position (#1730)
* Add data provider info

* Update changelog
2023-02-25 10:33:45 +01:00
aef840c2cc Bugfix/fix maximum call stack size exceeded error in value redaction (#1743)
* Bugfix for RangeError: Maximum call stack size exceeded

* Update changelog
2023-02-25 10:15:25 +01:00
80d0638922 Adding Coingecko Data Provider (#1736)
* Adding Coingecko Data Provider

* Update changelog

---------

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-02-25 09:45:01 +01:00
494ba36d44 Feature/reset letter spacing in buttons (#1742)
* Reset letter spacing in buttons

* Update changelog
2023-02-24 20:44:20 +01:00
dab9154092 Add support for armv7 processors (#1738)
* Add support for armv7 processors

* Update changelog
2023-02-23 18:09:35 +01:00
cd4a85abbf Improve wording (#1729) 2023-02-21 14:43:21 +01:00
e7977a9fbb Release 1.237.0 (#1733) 2023-02-19 19:26:16 +01:00
684c1e55b0 Bugfix/do not skip manual data source part 2 (#1732)
* Do not skip MANUAL data source

* Update changelog
2023-02-19 19:24:52 +01:00
1ffa831c5c Feature/improve style of symbol search results (#1728)
* Improve style

* Update changelog
2023-02-19 11:20:56 +01:00
40eed0016c Feature/migrate header module to angular material 15 (#1725)
* Migrate GfHeaderModule to Angular Material 15

* Update changelog
2023-02-19 10:36:52 +01:00
b58631083b Increase file size limit for imports (#1726)
* Increase file size limit for imports

* Update changelog
2023-02-19 10:09:13 +01:00
e0c0425d21 Add development guide (#1722) 2023-02-19 10:06:52 +01:00
bf2de5d572 Feature/add support to pricing page (#1723)
* Add support

* Update changelog
2023-02-19 10:02:46 +01:00
2b4a1dc480 Bugfix/fix issue with exact matches in activities filter (#1724)
* Fix issue with exact match

* Update changelog
2023-02-19 10:01:51 +01:00
ce022c024f Feature/upgrade nx to version 15.7.2 (#1721)
* Upgrade Angular and Nx

* Update changelog
2023-02-18 19:34:06 +01:00
0f4bf529d8 Handle impersonation mode with guard (#1714) 2023-02-18 14:59:38 +01:00
dad6bf7095 Release 1.236.0 (#1720) 2023-02-17 19:23:04 +01:00
86ca9eaae6 Bugfix/fix logout url in development (#1715)
* Add language

* Update changelog
2023-02-17 19:20:58 +01:00
9d9b805b0e Bugfix/do not skip manual data source (#1718)
* Do not skip MANUAL data source

* Update changelog
2023-02-17 19:20:14 +01:00
851401be1e Feature/remove ghostfolio as data source type (#1717)
* Remove GHOSTFOLIO

* Update changelog
2023-02-17 19:19:16 +01:00
85052bc9bc Bugfix/fix buying power calculation with emergency fund tag (#1713)
* Fix buying power calculation

* Update changelog
2023-02-17 17:29:48 +01:00
bff09f529d Feature/beautify etf names in asset profiles (#1709)
* Beautify ETF names

* Update changelog
2023-02-17 11:20:46 +01:00
f438458687 Release 1.235.0 (#1708) 2023-02-16 17:20:22 +01:00
7125b12631 Feature/improve styles on about page (#1707)
* Improve styles

* Update changelog
2023-02-16 17:17:30 +01:00
0cbf275a2e Feature/eliminate ghostfolio scraper api service (#1706)
* Eliminate GhostfolioScraperApiService

* Update changelog
2023-02-16 16:25:23 +01:00
0ec50819f5 Release 1.234.0 (#1703) 2023-02-15 10:52:02 +01:00
c9abe818bc Revert import (#1702) 2023-02-15 10:50:19 +01:00
bfa32537a8 Feature/improve usability of import activities action (#1695)
* Improve usability of import activities action

* Update changelog
2023-02-15 10:07:25 +01:00
cef15afab8 Add styling (#1701) 2023-02-15 10:01:35 +01:00
1b9587c454 Update default coupon duration (#1700) 2023-02-15 10:00:04 +01:00
de76b0d8c3 Feature/add data import and export to pricing page (#1697)
* Add data import and export

* Update changelog
2023-02-15 09:52:09 +01:00
e62989c981 Feature/copy logic of ghostfolio scraper api service to manual service (#1691)
* Copy logic of GhostfolioScraperApiService to ManualService

* Update changelog
2023-02-15 09:50:31 +01:00
d6b71e6314 Bugfix/fix links in subscription interstitial dialog (#1696)
* Fix links

* Update changelog
2023-02-14 18:25:12 +01:00
8c59bfd6d7 Feature/upgrade prisma to version 4.10.1 (#1688)
* Upgrade prisma to version 4.10.1

* Update changelog
2023-02-14 11:35:04 +01:00
f32df73256 Feature/migrate pages to angular material 15 (#1689)
* Migrate to Angular Material 15

* Update changelog
2023-02-14 10:04:22 +01:00
9d03a8002c Feature/improve content of faq and landing page (#1687)
* Conditionally show content

* Update changelog
2023-02-13 09:41:25 +01:00
3c36ca29af Feature/upgrade ionicons to version 6.1.2 (#1676)
* Upgrade ionicons to version 6.1.2

* Update changelog
2023-02-12 10:08:04 +01:00
efed7e3c2b Modify default exposed port (#1681)
* Modify default exposed port

* Update changelog
2023-02-11 10:37:44 +01:00
b09d3cea95 Fix landing page by setting a default value for countriesOfSubscribers
* Set default value for countriesOfSubscribers

* Update changelog
2023-02-11 09:57:27 +01:00
eabd2f3934 Add url (#1683) 2023-02-11 09:55:03 +01:00
cc184c2827 Feature/upgrade prettier to version 2.8.4 (#1675)
* Upgrade prettier to version 2.8.4

* Update changelog
2023-02-10 09:27:26 +01:00
436f791fa4 Feature/upgrade chart.js to version 4.2.0 (#1567)
* Upgrade chart.js to version 4.2.0

* Update changelog
2023-02-09 21:22:55 +01:00
e935a57dec Release 1.233.0 (#1678) 2023-02-09 20:30:53 +01:00
203909d917 Feature/upgrade eslint dependencies (#1674)
* Upgrade eslint dependencies

* Update changelog
2023-02-09 10:22:50 +01:00
eed4f57f30 Clean up (#1669) 2023-02-09 09:59:29 +01:00
7878036bac Feature/remove google play badge from landing page (#1672)
* Remove Google Play badge

* Update changelog
2023-02-08 14:17:49 +01:00
75d140b436 Harmonize file name (#1662) 2023-02-07 08:44:22 +01:00
a79f31b006 Feature/add accounts import export (#1635)
* Add accounts to activities export

* Add logic for importing accounts

* Update changelog
2023-02-06 21:59:59 +01:00
45cfd61dbb Feature/improve styling in admin control panel (#1665)
* Improve styling

* Update changelog
2023-02-06 11:35:56 +01:00
7fcfca952e Release 1.232.0 (#1664) 2023-02-05 19:46:18 +01:00
279f16cc67 Feature/extract locales 20230205 (#1663)
* Extract locales

* Update changelog
2023-02-05 19:44:33 +01:00
e7b1d8a5d3 Feature/upgrade ngx markdown to version 15.1.0 (#1657)
* Upgrade ngx-markdown to version 15.1.0

* Update changelog
2023-02-05 18:57:35 +01:00
1b2f8e5586 Feature/extend analytics by country (#1661)
* Extend analytics by country

* Fix Upgrade Plan button of subscription interstitial

* Update changelog
2023-02-05 18:57:12 +01:00
e4468252c6 Feature/upgrade ng extract i18n merge to version 2.5.0 (#1656)
* Upgrade ng-extract-i18n-merge to version 2.5.0

* Update changelog
2023-02-05 11:44:06 +01:00
ad3ebd42bb Feature/migrate mat suffix to angular material 15 (#1655)
* Migrate matSuffix to @angular/material 15

* Update changelog
2023-02-05 09:49:37 +01:00
55b03733f4 Release 1.231.0 (#1654) 2023-02-04 11:28:48 +01:00
0000317041 Upgrade Nx and Angular (#1646)
* Upgrade Nx and Angular

* Update changelog

* Feature/eliminate angular material css vars (#1648)

* Eliminate angular-material-css-vars

* Update changelog
2023-02-04 11:26:06 +01:00
e5f2a3865d Update types of node and papaparse (#1650) 2023-02-03 13:27:59 +01:00
c61561664f Relax validation for REDIS_HOST (#1652)
* Relax validation for REDIS_HOST

* Update changelog
2023-02-02 13:39:13 +01:00
a7d8a63ab8 Remove year (#1649) 2023-02-01 08:58:17 +01:00
5c51c1e825 Reorder features (#1644) 2023-01-30 20:22:03 +01:00
3a67bf9bb4 Add /de/pricing and /de/blog (#1645) 2023-01-30 20:21:37 +01:00
f7597c213d Feature/add dividend and fees to position detail dialog (#1643)
* Add dividend and fees to position detail dialog

* Update changelog
2023-01-30 20:21:16 +01:00
2e7f46ad78 Feature/allow account for activity of type item (#1641)
* Support linking wealth items to account

* Update changelog
2023-01-30 20:00:07 +01:00
cfffb99f52 Feature/extract locales 20230129 (#1642)
* Improve translations

* Update changelog
2023-01-30 19:45:27 +01:00
69ac3408f1 Release 1.230.0 (#1639) 2023-01-29 10:00:28 +01:00
e1806b4bd8 Feature/add sourceforge logo to landing page (#1638)
* Add SourceForge

* Update changelog
2023-01-29 09:58:53 +01:00
6aae0cc1e4 Bugfix/fix issue with value in value redaction interceptor (#1627)
* Fix issue with value in value redaction interceptor

* Fix format of world map

* Update changelog
2023-01-29 09:58:05 +01:00
5d8a50a80d Feature/add interstitial for subscription (#1637)
* Add interstitial

* Improve pricing page

* Update changelog
2023-01-28 09:42:15 +01:00
662231e830 Feature/upgrade prisma to version 4.9.0 (#1630)
* Upgrade prisma to version 4.9.0

* Update changelog
2023-01-28 09:41:33 +01:00
4d84459b5b Clean up imports (#1632) 2023-01-27 08:49:31 +01:00
efba7429c1 Bugfix/fix click of unknown accounts (#1629)
* Check for unknown key

* Update changelog
2023-01-25 12:00:52 +01:00
9cae5a3e79 Feature/improve sackgeld.com blog post (#1628)
* Improve blog post and add quote

* Update changelog
2023-01-24 08:11:21 +01:00
c2ed0a436f Downgrade to Node.js 16 (for development) (#1633) 2023-01-23 21:52:46 +01:00
8486c02575 Update Node to version 18.x (#1595)
* Update Node to version 18.x

* Add .nvmrc

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-01-23 08:28:19 +01:00
5122ef3456 Release 1.229.0 (#1626) 2023-01-21 16:10:04 +01:00
579b86665e Feature/add sackgeld.com blog post (#1625)
* Add blog post: Ghostfolio auf Sackgeld.com vorgestellt

* Update changelog
2023-01-21 16:07:32 +01:00
52b3ad6dc3 Feature/refactor value redaction interceptor (#1624)
* Reuse redactAttributes()

* Update changelog
2023-01-21 11:46:56 +01:00
bf9b60aa74 Feature/add sackgeld.com to as seen in section (#1622)
* Add Sackgeld.com

* Update changelog
2023-01-21 11:31:52 +01:00
6cd51fb044 Feature/hide irrelevant errors in client (#1623)
* Hide irrelevant errors in client

* Update changelog
2023-01-21 11:30:53 +01:00
271001f523 Feature/remove toggle on allocations page (#1620)
* Rename allocationCurrent, remove allocationInvestment

* Update changelog
2023-01-21 09:52:58 +01:00
a7e513a6d1 Bugfix/fix filtered value for emergency fund (#1619)
* Fix filtered value

* Update changelog
2023-01-20 20:51:08 +01:00
b5f256be95 Remove mail address (#1621) 2023-01-20 20:50:32 +01:00
a834ef6b4c Release 1.228.1 (#1617) 2023-01-18 22:01:59 +01:00
e5bd0d1bfa Release 1.228.0 (#1616) 2023-01-18 21:39:11 +01:00
7fa6eda45d Feature/remove emergency fund as asset class (#1615)
* Remove emergency fund as asset class

* Update changelog
2023-01-18 21:37:22 +01:00
f47e4d3b04 Clean up imports (#1613) 2023-01-17 20:10:08 +01:00
0300c6f3b7 Feature/extend hints on account page (#1609)
* Extend hints

* Update changelog
2023-01-17 20:09:50 +01:00
4865c45fd4 Feature/reduce data gathering interval to every four hours (#1611)
* Reduce execution interval

* Update changelog
2023-01-17 10:04:03 +01:00
2beceb36cf Overriding tooltip title for graphs where grouping is defined (#1605)
* Overriding tooltip title for graphs where grouping is defined

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2023-01-16 10:46:48 +01:00
cd64601482 Release 1.227.1 (#1608) 2023-01-14 19:12:18 +01:00
efac39eb51 Feature/extract locales 20230114 (#1607)
* Extract locales

* Update changelog
2023-01-14 19:11:14 +01:00
4da8a547ca Bugfix/fix create activity dialog caused by missing tags (#1606)
* Add guard

* Update changelog
2023-01-14 19:10:28 +01:00
9e8a9e4670 Release 1.227.0 (#1603) 2023-01-14 13:37:06 +01:00
bb99141e9c Fix group by month/year not working for YTD (#1598)
* Set time to 00:00:00 when getting current timestamp

* Update changelog
2023-01-14 13:30:51 +01:00
d147c2313f Feature/support assets in emergency fund (#1601)
* Support assets in emergency fund

* Update changelog
2023-01-14 13:13:26 +01:00
0878941c4f Feature/support translated tags (#1600)
* Support translated tags

* Update changelog
2023-01-14 12:36:30 +01:00
69a9e77820 Feature/improve logo alignment (#1599)
* Improve logo alignment

* Update changelog
2023-01-14 12:22:52 +01:00
104cca069f New Translations (#1597) 2023-01-13 16:19:48 +01:00
7ad58b1a62 Add i18n (#1594) 2023-01-13 08:58:13 +01:00
e88dbb0181 Fix wording (#1593) 2023-01-12 20:14:00 +01:00
152fd4fdf8 Release 1.226.0 (#1592) 2023-01-11 20:12:45 +01:00
6b022b8de8 Extract locales (#1591) 2023-01-11 20:10:57 +01:00
7ab699e5fe Feature/uncover french translation feature (#1590)
* Uncover Français

* Update changelog
2023-01-11 19:59:41 +01:00
a7e5a316be Bugfix/fix big.js exception in report endpoint (#1586)
* Fix exception with missing marketPrice

* Update changelog
2023-01-11 19:59:13 +01:00
3f2d3a2da9 Minor improvements (#1589) 2023-01-11 19:45:44 +01:00
0208bd0923 Feature/French translation (#1583)
* Finished the French translation
2023-01-11 19:38:46 +01:00
aeba6e1f03 Feature/configure thousand separator in world map chart (#1588)
* Set thousandSeparator

* Update changelog
2023-01-11 07:47:44 +01:00
1b899da9ff Minor tweaks to README.md (#1585)
* Minor tweaks to README.md

- Reduced reliance on HTML in the README.md file.
- Added alt text on images for improved accessibility
- Very minor formatting tweaks

Signed-off-by: Martin Vandenbussche <vandenbusschemartin@gmail.com>
2023-01-10 20:59:32 +01:00
90a7a84ac5 Feature/add global heat map to landing page (#1584)
* Add global heat map

* Update changelog
2023-01-10 20:52:05 +01:00
fc8e23a9c8 Feature/improve form of import dividends dialog (#1582)
* Disable while loading

* Update changelog
2023-01-10 20:43:48 +01:00
f3c8ec27cb Feature/improve deprecated sass imports (#1581)
* Improve deprecated Sass imports

* Update changelog
2023-01-10 20:06:42 +01:00
38474f54b0 Release 1.225.0 (#1580) 2023-01-07 18:26:38 +01:00
18d25fb6c2 Feature/extend faq page (#1577)
* Extend FAQ page

* Update changelog
2023-01-07 18:21:09 +01:00
a850e8ca22 Import dividend (#1560)
* Import dividend

* Update changelog
2023-01-07 18:20:02 +01:00
b5f565c054 Simplify data source transformation (#1578) 2023-01-07 17:06:42 +01:00
aa6d0a4533 Update dates (#1575) 2023-01-07 08:48:06 +01:00
25e9028a41 Release 1.224.0 (#1573) 2023-01-04 20:15:29 +01:00
925d38703e Feature/add group by year option on analysis page (#1568)
* Add group by year option
2023-01-04 20:13:13 +01:00
158bb00b8a Add missing dutch translations (#1572)
* Add missing dutch translations

* Update changelog
2023-01-03 21:12:01 +01:00
b17111e6f1 Feature/setup fr (#1571)
* Setup fr

* Update changelog
2023-01-02 21:19:19 +01:00
c4765e31cd Feature/setup pt (#1439)
* Setup pt

* Update changelog
2023-01-02 20:52:48 +01:00
d321d56dee Release 1.223.0 (#1566) 2023-01-01 18:53:10 +01:00
07dd22f7fe Feature/extend asset profile details dialog by currency and symbol (#1565)
* Extend asset profile details dialog

* Update changelog
2023-01-01 12:05:28 +01:00
eb4d088a80 Feature/optimize page title for mobile (#1564)
* Optimize page title for mobile

* Update changelog
2023-01-01 09:57:27 +01:00
0509f0101f Feature/add student discount (#1563)
* Add student discount

* Update changelog
2023-01-01 09:22:38 +01:00
8818e09be8 Feature/add prefix to coupon codes (#1562)
* Add prefix

* Update changelog
2022-12-31 17:06:15 +01:00
d97fe4da9c Release 1.222.0 (#1561) 2022-12-29 18:11:26 +01:00
b20fa55b79 Feature/add filters to analytics page (#1559)
* Add filters to analysis page

* Update changelog
2022-12-29 10:31:21 +01:00
dd7a6f1562 Bugfix/fix i18n for account type (#1554)
* Translate account type

* Update changelog
2022-12-29 10:20:23 +01:00
15357bd5b5 Feature/add asset profile details to activities import (#1552)
* Add asset profile details

* Update changelog
2022-12-29 10:19:30 +01:00
52c7adc266 Feature/upgrade internet identity dependencies to version 0.15.1 (#1549)
* Upgrade Internet Identity dependencies to version 0.15.1

* Update changelog
2022-12-28 20:08:30 +01:00
1ae8970045 Feature/add price to subscription (#1551)
* Add price

* Update changelog
2022-12-28 13:57:15 +01:00
7c4c047140 Remove .gitkeep (#1553) 2022-12-28 13:45:30 +01:00
527f7e4faf Feature/upgrade observable store to version 2.2.15 (#1550)
* Upgrade observable-store to version 2.2.15

* Update changelog
2022-12-28 13:43:28 +01:00
50160eb9dc Feature/upgrade countup.js to version 2.3.2 (#1548)
* Upgrade countup.js to version 2.3.2

* Update changelog
2022-12-28 13:22:19 +01:00
58dff8a1e0 Feature/upgrade prisma to version 4.8.0 (#1547)
* Upgrade prisma to version 4.8.0

* Update changelog
2022-12-28 10:40:34 +01:00
2cd41615b2 Feature/change execution time of asset profile data gathering (#1544)
* Change execution time

* Update changelog
2022-12-28 09:31:46 +01:00
66d5793528 Refactoring (#1545) 2022-12-27 10:05:26 +01:00
e8d65e1c85 Feature/upgrade bull to version 4.10.2 (#1542)
* Upgrade bull to version 4.10.2

* Update changelog
2022-12-27 09:12:44 +01:00
da827a08f5 Release 1.221.0 (#1541) 2022-12-26 17:09:51 +01:00
d545e4877c Feature/improve activities import by preview step (#1540)
* Improve activities import

* Update changelog
2022-12-26 17:07:51 +01:00
1918dee9c5 Format code 2022-12-26 16:50:50 +01:00
a08610b603 Feature/improve activities import (#1531) 2022-12-26 16:26:51 +01:00
c22733db56 Feature/resolve blog post titles (#1539)
* Resolve blog post titles

* Update changelog
2022-12-26 16:23:21 +01:00
ee4866eb7d Feature/add blog post the importance of tracking your personal finances (#1537)
* Add blog post: The importance of tracking your personal finances

* Update changelog
2022-12-26 12:23:43 +01:00
327b1fa0d7 Feature/refresh cryptocurrencies list 20221225 (#1536)
* Update cryptocurrencies.json

* Update changelog
2022-12-25 18:17:35 +01:00
b155666d21 Feature/improve label based on type in create or edit activity dialog (#1535)
* Improve label

* Update changelog
2022-12-25 12:41:48 +01:00
c5ee3237ed Refactoring (#1533) 2022-12-25 12:40:29 +01:00
16118d635c Bugfix/fix date conversion of two digit year (#1529)
* Fix date conversion of two digit year

* Update changelog
2022-12-25 12:38:40 +01:00
49ce4803ce Feature/add support to manage tags in create or edit activity dialog (#1532)
* Add support to manage tags

* Update changelog
2022-12-25 12:23:52 +01:00
0b65d05013 Feature/remove rakuten from data source type (#1534)
* Remove RAKUTEN

* Update changelog
2022-12-25 12:20:09 +01:00
8793284e75 Feature/add tags to admin control panel (#1530)
* Add tags

* Update changelog
2022-12-24 12:28:17 +01:00
1c5e4050a8 Release 1.220.0 (#1528) 2022-12-23 11:08:22 +01:00
4f187e1a9f Feature/increase fear and greed index to 365 days (#1519)
* Increase fear and greed index to 365 days

* Update changelog
2022-12-23 11:06:52 +01:00
b56111ae85 Feature/add dry run to import api endpoint (#1526)
* Add dry run to import API endpoint

* Update changelog
2022-12-23 10:51:49 +01:00
61dfc1f819 Feature/upgrade prettier to version 2.8.1 (#1523)
* Upgrade prettier to version 2.8.1

* Update changelog
2022-12-23 10:50:57 +01:00
6137f228a8 Upgrade @types/lodash to version 4.14.191 (#1527) 2022-12-23 10:50:38 +01:00
5293de14cd Bugfix/fix rounding of y axis ticks in benchmark comparator (#1521)
* Fix rounding

* Update changelog
2022-12-22 12:27:34 +01:00
7340a674b5 Feature/upgrade color to version 4.2.3 (#1524)
* Upgrade color to version 4.2.3

* Update changelog
2022-12-21 19:40:29 +01:00
42cb3e2c73 Harmonize style of symbol (#1520) 2022-12-20 13:29:57 +01:00
e8a4a53c9f Feature/support position detail dialog in top performers of analysis page (#1522)
* Add support for position detail dialog

* Update changelog
2022-12-19 09:36:34 +01:00
629f002074 Release 1.219.0 (#1518) 2022-12-17 21:22:26 +01:00
7c65cf6ddd Update locales (#1517) 2022-12-17 21:20:51 +01:00
c38ebec3be Feature/add name to activities table (#1516)
* Add name to symbol column

* Update changelog
2022-12-17 21:14:58 +01:00
2b8ab26e7e Feature/combine name and symbol column in holdings table (#1514)
* Combine name and symbol column

* Update changelog
2022-12-17 20:50:39 +01:00
60f52bb209 Handle user signup for OAuth and Internet Identity (#1515)
Co-Authored-By: gobdevel <99349192+gobdevel@users.noreply.github.com>
2022-12-17 16:18:00 +01:00
616d168a7c Feature/add signup permission (#1512)
* Added support to disable user sign up in the admin control panel

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2022-12-17 12:39:21 +01:00
b13e4425d3 Feature/extend glossary by deflation inflation and stagflation (#1511)
* Extend glossary by deflation, inflation and stagflation

* Update changelog
2022-12-16 17:38:19 +01:00
1424236c48 Add "Buy me a coffee" badge (#1509) 2022-12-15 17:26:30 +01:00
2a605f850d Refactor requests and responses (#1507) 2022-12-14 08:03:38 +01:00
88ffbfead0 Add purpose (#1508) 2022-12-13 12:56:10 +01:00
5f4a8d505b Release 1.218.0 (#1510) 2022-12-12 20:15:32 +01:00
e87b93f19c Feature/add logo endpoint (#1506)
* Add logo endpoint

* Update changelog
2022-12-12 20:13:45 +01:00
49dcade964 Feature/add date of first activity to holdings (#1505)
* Add date of first activity

* Update changelog
2022-12-11 10:19:35 +01:00
7cd65eed39 Feature/improve asset profile details dialog (#1504)
* Improve asset profile details dialog

* Update changelog
2022-12-11 09:37:07 +01:00
a51b210f79 Feature/upgrade chart.js to version 4.0.1 (#1503)
* Upgrade chart.js to version 4.0.1 (including plugins)

* Migrate border attributes

* Update changelog
2022-12-10 16:47:09 +01:00
285f2220f3 Release 1.217.0 (#1502) 2022-12-10 14:32:28 +01:00
d72123246d Feature/upgrade cheerio to version 1.0.0 rc.12 (#1501)
* Upgrade cheerio to version 1.0.0-rc.12

* Update changelog
2022-12-10 12:19:45 +01:00
3a78d6c3f1 Feature/add dividend timeline (#1498)
* Add dividend timeline

* Update changelog

Co-Authored-By: João Pereira <joao@jpereira.me>
2022-12-10 11:54:49 +01:00
d5e3ff5717 Add locale (#1500) 2022-12-09 15:06:15 +01:00
2efb331370 Feature/improve value redaction interceptor (#1495)
* Improve value redaction interceptor

* Update changelog
2022-12-07 17:48:46 +01:00
f521fe99c5 Feature/upgrade prisma to version 4.7.1 (#1493)
* Upgrade prisma to version 4.7.1

* Update changelog
2022-12-06 09:03:28 +01:00
42306530b8 New strings translated to Spanish (#1496)
* Update messages.es.xlf

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2022-12-04 20:02:02 +01:00
68c9d1b266 Bugfix/fix activities sorting in account detail dialog (#1494)
* Fix activities sorting

* Update changelog
2022-12-04 18:54:41 +01:00
1ce90a0c06 Release 1.216.0 (#1492) 2022-12-03 19:19:10 +01:00
50f6d154e5 Feature/extend sorting in tables (#1491)
* Extend sorting

* Update changelog
2022-12-03 19:17:30 +01:00
e4c44faee4 Fix sorting by performance field in positions table (#1489) 2022-12-03 18:25:53 +01:00
5209f82cca Feature/upgrade replace in file to version 6.3.5 (#1486)
* Upgrade replace-in-file to version 6.3.5

* Update changelog
2022-12-03 18:23:40 +01:00
292d345ce0 Feature/support manual currency for fee (#1490)
* Support manual currency for fee

* Update changelog
2022-12-03 18:22:19 +01:00
d58400788a Feature/upgrade big.js to version 6.2.1 (#1488)
* Upgrade big.js to version 6.2.1

* Update changelog
2022-12-02 17:56:11 +01:00
7ff61ae839 Feature/upgrade date fns to version 2.29.3 (#1487)
* Upgrade date-fns to version 2.29.3

* Update changelog
2022-12-01 17:14:45 +01:00
b5b7af7741 Feature/improve asset profile management (#1485)
* Improve asset profile management (Add note, fix filter)

* Update changelog
2022-11-30 20:01:17 +01:00
de3e0fad83 Remove link (#1484) 2022-11-29 23:19:07 +01:00
8c8273c4d4 Release 1.215.0 (#1483) 2022-11-27 10:34:17 +01:00
b406bcd17d Feature/update browserslist database 20221127 (#1482)
* Update browserslist database

* Update changelog
2022-11-27 10:32:22 +01:00
fb496431e8 Clean up (#1481) 2022-11-27 10:21:21 +01:00
441b251536 Setup form (#1474)
* Setup form to patch asset profile (for symbolMapping)
2022-11-27 10:19:34 +01:00
1dbb5db611 Feature/improve wording in single account rule (#1479)
* Improve wording

* Update changelog
2022-11-26 08:53:01 +01:00
8567efcd89 Feature/upgrade ionicons to version 6.0.4 (#1478)
* Upgrade ionicons to version 6.0.4

* Update changelog
2022-11-25 20:49:26 +01:00
1cda5dcc0a Feature/upgrade UUID to version 9.0.0 (#1476)
* Upgrade uuid to version 9.0.0

* Update changelog
2022-11-24 20:33:29 +01:00
3fb01c6dcf Added yarn cache to .gitignore (#1477)
* Added yarn cache (.yarn) to .gitignore
2022-11-24 11:46:26 +01:00
6a764fe893 Convert between ZAc and ZAR (#1471)
* Convert between ZAc and ZAR

* Update changelog
2022-11-22 20:18:38 +01:00
d2b75a244c Feature/improve language selector (#1466)
* Improve language selector

* Update changelog
2022-11-21 20:39:52 +01:00
3611684f17 Feature/extend asset profile details dialog (#1469)
* Extend asset profile details dialog

* Update changelog
2022-11-21 20:14:36 +01:00
4b74be50da Release 1.214.0 (#1465) 2022-11-19 12:15:22 +01:00
0d338bb083 Bugfix/fix dynamic decimal places for cryptocurrencies in position detail dialog (#1464)
* Fix dynamic decimal places

* Update changelog
2022-11-19 12:13:02 +01:00
b0d708fb82 Feature/change activities icons (#1463)
* Improve icons

* Update changelog
2022-11-19 11:28:26 +01:00
be14458437 Bugfix/fix division by zero error in cash positions calculation (#1462)
* Handle division by zero

* Update changelog
2022-11-19 10:19:01 +01:00
5978ddb80f Feature/improve support for manual data source (#1460)
* Improve support for MANUAL data source

* Update changelog
2022-11-19 10:06:05 +01:00
18638dd1b7 Bugfix/Fix matsort not working in position detail dialog (#1457)
* Fix matsort in position detail dialog

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2022-11-19 10:02:36 +01:00
81db3852e6 Feature/add sorting to accounts table (#1459)
* Add sorting

* Update changelog
2022-11-19 09:57:28 +01:00
af27781234 Feature/upgrade prisma to version 4.6.1 (#1456)
* Upgrade prisma to version 4.6.1

* Update changelog
2022-11-18 20:19:14 +01:00
608e7a774d Feature/improve activities tab icon (#1461)
* Improve icon

* Update changelog
2022-11-17 21:37:26 +01:00
ed15eb76fd Feature/upgrade yahoo finance2 to version 2.3.10 (#1458)
* Upgrade yahoo-finance2 to version 2.3.10

* Update changelog
2022-11-16 19:33:07 +01:00
39905e5046 Feature/upgrade yahoo finance2 to version 2.3.7 (#1455)
* Upgrade yahoo-finance2 to version 2.3.7

* Update changelog
2022-11-15 21:33:22 +01:00
7cd3f235df Release 1.213.0 (#1454) 2022-11-14 20:47:21 +01:00
3b4f8c69bb Feature/setup black friday 2022 deal (#1452)
* Setup Black Friday 2022 deal

* Update changelog
2022-11-14 20:45:41 +01:00
c9bdf46b2b Add PWA as feature (#1445) 2022-11-12 17:21:37 +01:00
4169de580b Feature/add indicator for excluded accounts (#1450)
* Add indicator for excluded accounts

* Update changelog
2022-11-12 16:46:17 +01:00
3317fe7c46 Fix missing comma in json body (API example) (#1446) 2022-11-12 08:59:55 +01:00
c8f6fdbaa3 Release 1.212.0 (#1444) 2022-11-11 20:02:20 +01:00
d95fc82f95 Feature/change view mode selector to slide toggle (#1443)
* Change view mode selector to slide toggle

* Update changelog
2022-11-11 20:01:08 +01:00
31c949f9d2 Feature/upgrade nx to version 15.0.13 (#1442)
* Upgrade Nx to version 15.0.13

* Update changelog
2022-11-11 19:44:32 +01:00
f68f40fcc6 Release 1.211.0 (#1441) 2022-11-11 16:53:36 +01:00
9623a363ed Feature/convert into pwa (#1436)
* Setup @angular/pwa
2022-11-11 16:50:23 +01:00
2d42549967 Feature/improvements in pricing page (#1440)
* Improve pricing page

* Update changelog
2022-11-11 16:42:51 +01:00
c934c5088b Add ghostfolio-cli to community projects (#1437) 2022-11-11 09:02:43 +01:00
678b3cc57e Add Buy me a coffee button (#1438) 2022-11-10 19:31:00 +01:00
cd5eb64a4c Removed margin-bottom tag from the body element (#1435)
- This only changes the behaviour in Firefox
- All browsers now show the UI in a single page view
2022-11-09 20:48:13 +01:00
fc1507de4f Clean up (#1423) 2022-11-09 10:08:36 +01:00
d147a66dcd Release 1.210.0 (#1434) 2022-11-08 17:35:43 +01:00
33fd1282e5 Bugfix/fix cash position in user currency (#1433)
* Initialize cash positions with user currency

* Update changelog
2022-11-08 17:32:50 +01:00
693ff9d3ea Setup pt (#1432) 2022-11-07 17:17:07 +01:00
21e87a0055 Clean up (#1431) 2022-11-07 17:03:34 +01:00
43426c9b01 Feature/restructure portfolio page (#1429)
* Restructure portfolio page

* Update changelog
2022-11-07 16:57:26 +01:00
3b4da72ea3 Feature/tighten validation rule of base currency (#1428)
* Tighten validation rule of BASE_CURRENCY

* Update changelog
2022-11-06 17:51:31 +01:00
8d8e55fd0b Release 1.209.0 (#1427) 2022-11-05 13:39:26 +01:00
ca18621ce8 Improve locales (#1426) 2022-11-05 12:49:17 +01:00
b8574d24b2 Feature/improve usability of import (#1425)
* Improve usability by adding expected file format

* Update changelog
2022-11-05 12:22:24 +01:00
6d12c27f9c Feature/rename transactions to activities page (#1424)
* Rename transactions to activities

* Update changelog
2022-11-05 10:42:40 +01:00
c2c5326049 Feature/add buy me a coffee badge to about page (#1422)
* Add button: Buy me a coffee

* Update changelog
2022-11-05 10:22:18 +01:00
2a1339b61e Feature/improve usage of premium indicator component (#1421)
* Improve usage of premium indicator component

* Update changelog
2022-11-05 09:12:41 +01:00
c8a2579624 Feature/remove intro image in dark mode (#1420)
* Remove intro image in dark mode

* Update changelog
2022-11-05 09:06:40 +01:00
832ae063df Release 1.208.0 (#1415) 2022-11-03 20:21:12 +01:00
b5e026934f Harmonize extension (before and after) (#1414) 2022-11-03 20:18:40 +01:00
901c997908 Add pagination to activities table (#1404)
* Add pagination to activities table
2022-11-03 20:16:30 +01:00
3b6e0b20e2 Add test case (#1399)
* Add test case

* Fix calculation for portfolio evolution chart

* Update changelog

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>
2022-11-03 20:06:16 +01:00
e449d51c3c Feature/restructure actions in admin control panel (#1410)
* Restructure actions

* Update changelog
2022-11-02 17:47:03 +01:00
f72d31bab3 Release 1.207.0 (#1409) 2022-10-31 20:27:52 +01:00
4c893c4dcc Bugfix/fix public page (#1408)
* Fix public page

* Update changelog
2022-10-31 20:25:42 +01:00
ffb11cd10e Add instructions for API Authorization (#1406)
* Add instructions for API Authorization
2022-10-31 20:12:42 +01:00
d424b7731e Feature/change background color of dark mode (#1396)
* Darken background color

* Update changelog
2022-10-30 18:48:28 +01:00
6043c87481 Simplify lead (#1401) 2022-10-30 17:02:01 +01:00
fca0a688b6 parse csv date in ISO format (#1303)
* Handle various date formats

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2022-10-28 11:26:19 +02:00
5c6cc4fed5 Handle division by zero (#1398) 2022-10-26 21:28:26 +02:00
64a7d38ff9 Add more translations (#1394) 2022-10-23 10:28:08 +02:00
68d0d39161 Feature/translate asset and asset sub classes (#1393)
* Translate asset and asset sub classes

* Update changelog
2022-10-22 07:47:05 +02:00
233a8a8a18 Bugfix/improve loading indicator of investment chart (#1392)
* Improve loading indicator

* Update changelog
2022-10-21 20:01:32 +02:00
190779ee35 Release 1.206.2 (#1391) 2022-10-20 23:40:42 +02:00
6ef8121561 Release 1.206.1 (#1390) 2022-10-20 21:49:12 +02:00
58bf57d1e6 Release 1.206.0 (#1389) 2022-10-20 20:58:33 +02:00
71c5412dd5 Add test case: sell partially with huge gain (#1380)
* Add test case: sell partially with huge gain
2022-10-20 20:56:58 +02:00
ae85398c3d Fix test description (#1379) 2022-10-20 20:48:40 +02:00
048900d01b Bugfix/improve performance calculation for sell activitities (#1388)
* Improve performance calculation for SELL activities

* Update changelog

Co-authored-by: gizmodus <11334553+gizmodus@users.noreply.github.com>
2022-10-20 20:47:57 +02:00
074b09b543 Add space (#1381) 2022-10-19 07:53:58 +02:00
f9e04022f4 Remove TWR calculation (#1377) 2022-10-18 21:06:28 +02:00
8fd1fbd44a Feature/upgrade nx to version 15 (#1375)
* Upgrade to Nx 15

* Update changelog
2022-10-18 20:05:58 +02:00
0fb33ae71c Feature/migrate angular.json to project.json (#1374)
* Migrate angular.json to project.json

* Update changelog
2022-10-17 20:41:13 +02:00
3a35d72ec2 Fix disabled placeholder color (#1365) 2022-10-17 17:25:50 +02:00
32fe3e195f Release 1.205.2 (#1369) 2022-10-16 20:49:28 +02:00
805f4b05be Release 1.205.1 (#1368) 2022-10-16 20:19:27 +02:00
5b51a6840a Release 1.205.0 (#1367) 2022-10-16 19:36:51 +02:00
36bd6164e6 Feature/improve wording on landing page (#1366)
* Improve wording

* Update changelog
2022-10-16 19:35:09 +02:00
eac52a215b Feature/refactor appearance to color scheme (#1364)
* Refactor appearance to colorScheme

* Update changelog
2022-10-16 14:54:26 +02:00
9ff8cd5471 Feature/improve portfolio evolution chart (#1362)
* Switch inputs

* Update changelog
2022-10-16 10:01:31 +02:00
33cc7e4e7e Feature/remove rakuten from data source (#1361)
* Remove Rakuten

* Update changelog
2022-10-16 09:42:18 +02:00
47f84dab06 Remove postfix (#1360) 2022-10-16 09:41:41 +02:00
384d18b2a6 Feature/persist user language on url change (#1359)
* Persist user language

* Update changelog
2022-10-16 08:45:52 +02:00
2363983bdc Release 1.204.1 (#1357) 2022-10-15 18:07:18 +02:00
4af76764be Release 1.204.0 (#1356) 2022-10-15 17:47:35 +02:00
a65424aafa Feature/add total amount chart to investment timeline (#1344)
* Add total amount chart

* Update changelog
2022-10-15 17:45:34 +02:00
f9cd629470 Update messages.es.xlf (#1355) 2022-10-15 17:44:20 +02:00
ccb8c86596 Feature/minor improvements in chart components (#1353)
* Move y axis to the right

* Update changelog
2022-10-15 11:27:55 +02:00
246de7aa86 Consider current month in FIRE calculation (#1349) 2022-10-15 10:45:11 +02:00
a323313c71 Bugfix/fix alignment of value component on allocation page (#1351)
* Fix alignment

* Update changelog
2022-10-15 10:32:08 +02:00
538c8947cd Feature/rename data source from rakuten to rapid api (#1350)
* Rename Rakuten to Rapid API

* Update changelog
2022-10-15 10:31:46 +02:00
1ec5fd12fe Feature/setup prettier plugin organize attributes (#1346)
* Setup prettier plugin: prettier-plugin-organize-attributes

* Update changelog
2022-10-15 10:30:12 +02:00
4376b8903e Bugifx/fix url in blog posts (#1347)
* Fix url

* Update changelog
2022-10-14 19:52:00 +02:00
a8e096f9ac Feature/minor improvements for appearance selector (#1345)
* Improve appearance selector

* Update changelog
2022-10-12 13:38:58 +02:00
8e577592f6 Fix issue with $localize in storybook (#1343) 2022-10-12 09:48:33 +02:00
c896bf9199 Add appearance option in settings (#1342)
* Add appearance option in settings
2022-10-11 21:34:52 +02:00
16145f18d9 Improve template (#1324)
* Improve template
2022-10-09 10:42:02 +02:00
5398da0dc8 Feature/simplify admin settings management (#1340)
* Simplify settings management

* Update changelog
2022-10-08 17:35:18 +02:00
2466f4ff5d Release 1.203.0 (#1338) 2022-10-08 14:22:43 +02:00
8f3a9bdfbb Feature/refactor animation configuration (#1337)
* Refactor animation configuration

* Update changelog
2022-10-08 14:21:17 +02:00
44dfd2bd48 Add animation to line chart (#1328) 2022-10-08 13:47:48 +02:00
3fc2228f1d Feature/switch to new performance calculation (#1336)
* Switch to new performance calculation

* Update changelog
2022-10-08 13:20:52 +02:00
b018819a1f Bugfix/fix todays performance and chart calculation (#1333)
* Fix today's performance and chart calculation

* Update changelog
2022-10-08 13:20:25 +02:00
ac9311d783 Bugfix/fix alignment in users table (#1335)
* Fix alignment

* Update changelog
2022-10-08 11:38:58 +02:00
e23ce0f35d Feature/improve gui of benchmark comparator (#1334)
* Improve GUI

* Update changelog
2022-10-08 11:07:42 +02:00
f4b52aa41c Add Italian localization for the 4% rule (#1329)
* Update messages.it.xlf
2022-10-08 11:05:53 +02:00
655b040d4d Add missing title (#1332) 2022-10-07 20:54:05 +02:00
0f637a5d0f Release 1.202.0 (#1331) 2022-10-07 20:49:57 +02:00
3f85c327f5 Bugfix/fix text truncation in value component (#1330)
* Fix text truncation

* Update changelog
2022-10-07 20:48:39 +02:00
c2df99072d Feature/refactor filters (#1299)
* Refactor filters

Co-Authored-By: Zakaria YAHI <9142557+ZakYahi@users.noreply.github.com>
2022-10-07 20:39:29 +02:00
e8afbcad9c Feature/localize 4 percentage rule (#1327)
* Setup translation for 4% rule

* Update changelog
2022-10-07 20:21:52 +02:00
e6d8de781b Feature/improve wording in twitter bot service (#1326)
* Improve wording

* Update changelog
2022-10-06 20:52:34 +02:00
6e1935899f Bugfix/fix cryptocurrency symbols with less than 3 characters (#1325)
* Fix cryptocurrency symbols with less than 3 characters

* Update changelog
2022-10-06 15:15:36 +02:00
169cb85b66 Improve Italian translation (#1318)
* Update messages.it.xlf
2022-10-05 07:53:03 +02:00
fe6658d0ac Update messages.es.xlf (#1319) 2022-10-04 17:43:25 +02:00
1f0381228e Feature/improve caching of benchmarks (#1320)
* Improve caching

* Update changelog
2022-10-04 17:39:51 +02:00
f4b63b5de5 Release 1.201.0 (#1313) 2022-10-01 18:39:15 +02:00
e45a0ad068 Spanish (#1312)
* Update messages.es.xlf
2022-10-01 18:36:41 +02:00
81c6cc021d Feature/add blog post hacktoberfest 2022 (#1310)
* Add blog post: Hacktoberfest 2022

* Update changelog
2022-10-01 18:35:55 +02:00
859b24aa5b Fix alignment (#1311) 2022-10-01 18:35:27 +02:00
2bc325f182 Update messages.es.xlf (#1305)
* Update messages.es.xlf

Co-authored-by: fdp10381 <63880387+fdp10381@users.noreply.github.com>
2022-10-01 16:45:44 +02:00
a6186c23e2 Feature/improve usage of value component (#1308)
* Improve usage of value component

* Update changelog
2022-10-01 13:53:43 +02:00
cf234003ec Release 1.200.0 (#1307) 2022-10-01 11:18:15 +02:00
8d3954304e Feature/add statistics section to landing page (#1306)
* Add pulls on Docker Hub to statistics

* Add statistics to landing page

* Update changelog
2022-10-01 11:16:43 +02:00
9562139fa6 Feature/upgrade prisma to version 4.4.0 (#1304)
* Upgrade prisma to version 4.4.0

* Update changelog
2022-10-01 09:42:07 +02:00
c857ea9a8f Feature/add as seen in section on landing page (#1302)
* Add as seen in section

* Update changelog
2022-09-29 21:59:51 +02:00
5c9fa71d95 Release 1.199.1 (#1301) 2022-09-27 20:44:32 +02:00
fefbfa31d1 Release 1.199.0 (#1300) 2022-09-27 20:28:46 +02:00
93a1fae51c Feature/support sectors of mutual funds (#1298)
* Support sectors

* Update changelog

Co-authored-by: Mitchell <5503199+m11tch@users.noreply.github.com>
2022-09-27 17:38:53 +02:00
3715edd9ba Extract locales (#1297) 2022-09-26 19:44:19 +02:00
e3916e1ba3 Feature/setup espanol (#1293)
* Setup Español

* Update changelog
2022-09-26 18:39:11 +02:00
76ceac4edc Add spanish translation (#1296)
Co-Authored-By: alfredonodo <41476198+alfredonodo@users.noreply.github.com>
Co-Authored-By: casitu <25199636+casitu@users.noreply.github.com>
2022-09-26 18:14:53 +02:00
333b63bfe2 Release 1.198.0 (#1294) 2022-09-25 21:46:19 +02:00
3006c21b12 Add dutch translation (#1291)
* Add dutch translation
2022-09-25 18:12:33 +02:00
f01a3f893d Exclude accounts (#1289)
* Exclude accounts

* Update changelog
2022-09-25 18:02:46 +02:00
72974e888f Clean up spaces (#1288) 2022-09-25 15:14:51 +02:00
0cee7a0b35 Release 1.197.0 (#1287) 2022-09-24 13:16:47 +02:00
f3d337b044 Feature/add value of active filters on allocations page (#1286)
* Add value

* Update changelog
2022-09-24 13:15:16 +02:00
7667af059c Feature/combine performance and chart calculation (#1285)
* Combine performance and chart calculation endpoints

* Update changelog
2022-09-24 13:12:40 +02:00
1095b47f45 Feature/add multi language support to feature overview (#1284)
* Add multi-language support

* Update changelog
2022-09-24 12:29:36 +02:00
dacd7271eb Feature/improve density of various selectors (#1283)
* Improve density

* Update changelog
2022-09-24 09:58:09 +02:00
e093041184 Release 1.196.0 (#1281) 2022-09-22 21:05:05 +02:00
8f2caa508a Feature/extend landing page (#1279)
* Extend landing page

* Update changelog
2022-09-22 20:52:46 +02:00
862f670ccf Feature/setup italiano (#1276)
* Setup italiano

* Update changelog
2022-09-22 20:52:03 +02:00
54bf4c7a43 Update messages.it.xlf (#1280) 2022-09-22 20:51:31 +02:00
c0ace51ee9 Release 1.195.0 (#1277) 2022-09-20 20:23:41 +02:00
b1b5689242 Feature/improve performance of chart calculation (#1271)
* Improve performance chart calculation

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>

* Update changelog

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>

* Improve chart tooltip of benchmark comparator

* Update changelog

Co-authored-by: gizmodus <11334553+gizmodus@users.noreply.github.com>
2022-09-20 20:22:01 +02:00
b68cdaf8ea Add issue template (#1275) 2022-09-19 21:22:54 +02:00
b387a80a0d Add bullet (#1270) 2022-09-17 21:29:37 +02:00
6e4660295a Release 1.194.0 (#1269) 2022-09-17 21:26:08 +02:00
d4c3a9d1e8 Feature/add percentage visualization of the current filter (#1268)
* Add percentage visualization of the active filter

* Update changelog
2022-09-17 21:24:09 +02:00
263f6b32f2 Bugfix/fix performance chart calculation (#1267)
* Respect end date in performance chart calculation

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>

* Update changelog

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>
2022-09-17 08:33:04 +02:00
637f31ae3b Add instruction for NODE_ENV: production (#1266) 2022-09-17 08:31:56 +02:00
547e27c7a1 Feature/set node env in docker compose files (#1261)
* Set NODE_ENV to production

* Update changelog
2022-09-15 17:20:03 +02:00
f10dc176f2 Feature/clean up german localization (#1260)
* Clean up German localization

* Set up Italian

* Update changelog
2022-09-15 16:58:00 +02:00
0a966e46cd Improve translation (#1258) 2022-09-15 10:37:07 +02:00
4f281d25e1 Release 1.193.0 (#1257) 2022-09-14 20:11:47 +02:00
aaba8c35c2 Feature/extend pricing page with referral section (#1256)
* Add referral section

* Update changelog
2022-09-14 20:10:35 +02:00
7d27cb3398 Bugfix/use base currency in exchange rate service instead of usd (#1255)
* Change from USD to base currency

* Update changelog
2022-09-14 19:56:18 +02:00
91678028b5 Bugfix/fix missing assets during local development (#1253)
* Extend setup for development (missing assets)

* Update changelog
2022-09-14 19:26:59 +02:00
5e3cac8ac9 Feature/sort benchmarks by name (#1250)
* Sort benchmarks by name

* Update changelog
2022-09-12 13:34:06 +02:00
33f20b6b48 Release 1.192.0 (#1249) 2022-09-11 09:21:48 +02:00
e4fd255dd7 Bugfix/improve loading indicator of benchmark comparator (#1247)
* Improve loading indicator

* Update changelog
2022-09-11 09:20:15 +02:00
e320aa91f7 Feature/simplify benchmark configuration (#1248)
* Simplify benchmark configuration

* Update changelog
2022-09-11 09:19:50 +02:00
0fcfa6c1bd Bugfix/improve error handling in benchmark calculation (#1246)
* Improve error handling

* Update changelog
2022-09-10 18:58:31 +02:00
42d32ed652 Feature/upgrade yahoo finance2 to version 2.3.6 (#1245)
* Upgrade yahoo-finance2 to version 2.3.6

* Update changelog
2022-09-10 18:57:41 +02:00
21b4b0ef24 Release 1.191.0 (#1244) 2022-09-10 16:13:22 +02:00
4f8fe83a16 Feature/clean up user database schema (#1242)
* Clean up user database schema

* Update changelog
2022-09-10 16:11:49 +02:00
980ad1028c Feature/allow date range change for demo user (#1243)
* Allow date range change

* Update changelog
2022-09-10 16:10:57 +02:00
0d5bc3f51b Release 1.190.0 (#1241) 2022-09-10 13:36:39 +02:00
aece76d98f Feature/add date range component to benchmark comparator (#1240)
* Add date range component

* Update changelog
2022-09-10 13:33:37 +02:00
fc4bb71184 Feature/migrate date range setting to user settings (#1239)
* Migrate date range to user settings

* Refactor currency and view mode in the user user settings

* Update changelog
2022-09-10 11:38:06 +02:00
20bc7ef99c Feature/improve benchmark comparator on mobile (#1238)
* Improve layout for mobile

* Update changelog
2022-09-09 19:44:36 +02:00
7a733ae49b Release 1.189.0 (#1237) 2022-09-08 21:06:37 +02:00
376ce88492 Bugfix/fix benchmark chart (#1236)
* Fix benchmark chart

* Distinguish between currency and unit in tooltip

* Update changelog
2022-09-08 21:05:19 +02:00
c4d83aabe7 Setup tests (#1234) 2022-09-08 17:32:55 +02:00
d4e2cec77e Release 1.188.0 (#1233) 2022-09-06 20:40:59 +02:00
75db7bf79a Bugfix/fix asset profile details dialog (#1232)
* Fix dialog for assets without (first) activity

* Update changelog
2022-09-06 20:39:45 +02:00
3ad99c9991 Add data management (#1230)
* Add data management for benchmarks
2022-09-06 20:39:27 +02:00
00e402d286 Add translations 2022-09-04 09:48:18 +02:00
4ac0484025 Update changelog 2022-09-04 09:45:29 +02:00
75d61bff6d Setup benchmark comparator 2022-09-04 09:45:22 +02:00
0de28d733e Release 1.187.0 (#1227) 2022-09-03 21:42:23 +02:00
3b2f13850c Feature/improve chart calculation (#1226)
* Improve chart calculation

* Update changelog
2022-09-03 21:41:06 +02:00
0cc42ffd7c Add end date parameter (#1224)
* Add end date parameter
2022-09-03 21:28:57 +02:00
3ccb812ac3 Release 1.186.2 (#1223) 2022-09-03 11:37:14 +02:00
0a8549db3e Release 1.186.1 (#1222) 2022-09-03 11:19:31 +02:00
c95e90ff31 Release 1.186.0 (#1221) 2022-09-03 10:28:20 +02:00
b59af0d864 Feature/upgrade nx to version 14.6.4 (#1220)
* Upgrade nx and angular

* Update changelog
2022-09-03 10:26:56 +02:00
408bdbd187 Bugfix/fix GitHub contributors count (#1219)
* Fix GitHub contributors count

* Update changelog
2022-09-03 10:06:16 +02:00
a3bfa46fb0 Feature/remove alias from user (#1218)
* Remove alias

* Update changelog
2022-09-03 09:47:18 +02:00
8cb1b3f925 Feature/decrease rate limiter duration (#1217)
* Decrease rate limiter duration

* Update changelog
2022-09-03 09:09:57 +02:00
15c650f951 Bugfix/fix blog post link (#1216)
* Fix link

* Update sitemap.xml
2022-09-03 09:09:37 +02:00
c198bd78da Add architectures (#1205) 2022-09-03 08:32:01 +02:00
35963580bc Bugfix/improve error handling in portfolio calculations (#1215)
* Improve error handling

* Update changelog
2022-09-03 08:31:47 +02:00
cf2c5bad02 Bugfix/change environment variables redis host and port to mandatory (#1211)
* Change REDIS_HOST and REDIS_PORT to mandatory

* Update changelog
2022-09-01 15:35:37 +02:00
f332aea9b4 Release 1.185.0 (#1207) 2022-08-30 20:53:26 +02:00
7a9fd18407 Feature/improve markets overview (#1206)
* Improve markets overview

* Update changelog
2022-08-30 20:52:13 +02:00
ca08d3154a Bugfix/disable language selector for demo user (#1204)
* Disable language selector for demo user

* Update changelog
2022-08-28 21:11:27 +02:00
01d4ae8757 Feature/move build pipeline from travis to GitHub actions (#1203)
* Remove travis configurations

* Update changelog
2022-08-28 21:10:25 +02:00
43ce2786c1 Fetch all history for all tags and branches (#1202) 2022-08-28 11:01:54 +02:00
de2092c4d2 Release 1.184.2 (#1201) 2022-08-28 10:09:59 +02:00
435a180e54 Release 1.184.1 (#1200) 2022-08-28 09:44:09 +02:00
0ad30ffabe Add version to docker tag (#1199) 2022-08-28 09:43:06 +02:00
0cc5e558f1 Release 1.184.0 (#1198) 2022-08-28 09:12:03 +02:00
63b183cc6f Feature/finalize GitHub action to create arm64 docker image (#1197)
* Change order and refactor secrets

* Update changelog
2022-08-28 09:10:18 +02:00
10bae24c5c Build arm64 docker image and use github actions (#1134)
* Setup build pipeline for arm64 docker images using GitHub Actions
2022-08-27 14:37:22 +02:00
0e29278e96 Feature/add alias to access (#1193)
* Add alias to access

* Update changelog
2022-08-27 11:29:09 +02:00
2db46e5bbf Feature/support localization in date fns (#1195)
* Add locale to date-fns (formatDistanceToNow)

* Update changelog
2022-08-27 10:54:59 +02:00
e757e90e5a Improve translation (#1196) 2022-08-27 10:42:54 +02:00
184ddc6209 Bugfix/fix missing assets in local development (#1194)
* Fix missing assets in local development

* Update changelog
2022-08-27 10:29:50 +02:00
e3662a143c Improve localization (#1191)
* Improve localization

* Update changelog
2022-08-26 20:10:59 +02:00
25afd7e07b Remove database:baseline (#1192) 2022-08-26 18:08:05 +02:00
7fceaa1350 Add language to urls (#1187) 2022-08-26 17:52:42 +02:00
7c8530483c Release 1.183.0 (#1189) 2022-08-24 20:55:38 +02:00
539d3ff754 Feature/add asset sub class filter (#1188)
* Add asset sub class filter

* Update changelog
2022-08-24 20:53:50 +02:00
9d28b63da6 Release 1.182.0 (#1186) 2022-08-23 21:40:57 +02:00
24abbd85e6 Feature/move asset profile details to dialog (#1185)
* Introduce asset profile dialog

* Update changelog
2022-08-23 21:39:04 +02:00
b6f395fd3b Feature/improve i18n (#1183)
* Improve i18n

* Update changelog
2022-08-22 19:57:48 +02:00
04d894cf88 Release 1.181.2 (#1182) 2022-08-21 21:42:05 +02:00
b4d2c4109e Release 1.181.1 (#1181) 2022-08-21 20:58:51 +02:00
823093f4d7 Release 1.181.0 (#1180) 2022-08-21 18:08:54 +02:00
56bf422407 Consider language from user settings (#1179) 2022-08-21 18:06:31 +02:00
df0e9ad03b Bugfix/fix division by zero in benchmarks calculation (#1177)
* Fix division by zero error

* Update changelog
2022-08-21 17:03:03 +02:00
0e3702c2be Feature/improve german translation (#1178)
* Simplify and translate locales

* Add support for translated labels

* Update changelog
2022-08-21 17:02:43 +02:00
11136ae4f8 Eliminate duplicate locales (#1176) 2022-08-20 14:01:15 +02:00
2e6a7d5a91 Extract locales (#1175) 2022-08-20 11:00:53 +02:00
83845c256a Feature/add language selector (#1174)
* Add language selector

* Add translations (german)

* Update changelog
2022-08-20 10:55:27 +02:00
34c9703716 Remove locale (#1173) 2022-08-20 10:25:53 +02:00
48903238c5 Feature/improve documentation of database migration (#1172)
* Improve documentation

* Update changelog
2022-08-20 10:22:02 +02:00
57a14bd945 automate database setup and upgrade (#1163)
* Automate database setup and schema upgrade
2022-08-20 08:54:50 +02:00
4fd0622114 Fix build:dev script (#1171) 2022-08-19 20:39:21 +02:00
52f0fb5ab8 Release 1.180.1 (#1170) 2022-08-19 18:24:41 +02:00
20195b2b1a Release 1.180.0 (#1169) 2022-08-18 21:15:17 +02:00
7fa4e6ebd2 Feature/resolve feature graphic of blog post (#1168)
* Resolve feature graphic of blog post

* Update changelog
2022-08-18 21:13:39 +02:00
d8531ddfcb Bugfix/fix links to blog posts (#1167)
* Fix links

* Update changelog
2022-08-18 21:11:10 +02:00
70d670b711 Bugfix/fix license (#1160)
* Fix license

* Update changelog
2022-08-17 23:23:39 +02:00
27b0663a80 Add translations (#1159) 2022-08-16 22:16:15 +02:00
874dfb0235 Improve links (#1158) 2022-08-16 21:52:37 +02:00
072db0d558 Add translations (#1157) 2022-08-16 21:40:51 +02:00
12e692429a Regenerate xlf (#1156) 2022-08-16 21:30:12 +02:00
e22b8b78b8 Feature/tag route titles with template literal strings (#1155)
* Tagged template literal strings

* Update changelog
2022-08-16 21:03:05 +02:00
dc5052f7dc Feature/set up language localization for german (#1153)
* Set up language localization for German

* Update changelog
2022-08-16 20:58:08 +02:00
335553e891 Feature/tag template literal strings (#1152)
* Tagged template literal strings

* Update changelog
2022-08-16 20:53:14 +02:00
d480ad1023 Extract locales (#1151) 2022-08-15 19:56:42 +02:00
7320751056 Feature/set up ng extract i18n merge (#1149)
* Set up ng-extract-i18n-merge

* Update changelog
2022-08-15 19:52:43 +02:00
108c0c13c4 Release 1.179.5 (#1150) 2022-08-15 18:17:57 +02:00
053a5cc5b5 Release 1.179.4 (#1148) 2022-08-14 10:10:54 +02:00
c456a8bcfe Release/1.179.3 (#1147)
* Clean up

* Release 1.179.3
2022-08-13 20:33:43 +02:00
6fcecb5bc6 Release 1.179.2 (#1145) 2022-08-13 13:39:37 +02:00
e4e0a7d9f0 Release 1.179.1 (#1144) 2022-08-13 12:16:39 +02:00
c7173761a3 Release 1.179.0 (#1143) 2022-08-13 10:44:38 +02:00
185e130d9f Feature/add blog post 500 stars on GitHub (#1138)
* Add blog post

* Update changelog
2022-08-13 10:42:56 +02:00
81245635af Feature/setup i18n (#1139)
* Setup i18n

* Update changelog
2022-08-13 10:29:36 +02:00
55182ac1af Feature/reduce maximum width of performance chart (#1137)
* Reduce maximum width

* Update changelog
2022-08-10 17:26:34 +02:00
0b446a30ae Release 1.178.0 (#1136) 2022-08-09 21:28:02 +02:00
c5e6602102 Improve filter by asset class (#1135) 2022-08-09 21:25:07 +02:00
573038f407 Feature/add default values for countries and sectors (#1133)
* Add default values

* Add database validation script

* Update changelog
2022-08-09 19:31:13 +02:00
dbc38e705e Feature/add url to symbol profile overrides (#1132)
* Add url to symbol profile overrides

* Improve filter by asset class

* Update changelog
2022-08-09 19:29:26 +02:00
f127e7c61a Feature/improve styling of benchmarks (#1131)
* Harmonize benchmark table styling

* Update changelog
2022-08-09 19:28:13 +02:00
4ccabde251 Feature/simplify exchange rate service initialization (#1128)
* Simplify initialization

* Update changelog
2022-08-08 19:25:38 +02:00
86ae88f90f Release 1.177.0 (#1127) 2022-08-04 13:38:25 +02:00
69bc1d67e1 Bugfix/fix database connection error handling (#1125)
* Fix database connection error handling

* Update changelog
2022-08-04 13:36:32 +02:00
03942aecda Feature/upgrade to prisma 4.1.1 (#1126)
* Upgrade prisma to 4.1.1

* Update changelog
2022-08-04 13:35:58 +02:00
7ec9170c0d baseline prisma bdd at first setup (#1124)
* Baseline database (migrations) in setup

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
2022-08-04 13:34:32 +02:00
51431a7fb2 Feature/add ghostfolio as default to data sources (#1122)
* Add GHOSTFOLIO

* Update changelog
2022-08-03 21:36:12 +02:00
4adda6783d Feature/add agplv3 logo to landing page (#1121)
* Add AGPLv3 logo

* Update changelog
2022-08-02 22:14:27 +02:00
d5cd4c0dea Feature/upgrade nx to version 14.5.1 (#1120)
* Upgrade Nx including angular and nestjs

* Update changelog
2022-08-01 19:56:44 +02:00
34be10d755 Release 1.176.2 (#1119) 2022-07-31 08:16:30 +02:00
51f586e160 Feature/upgrade node from 14 to 16 (#1118)
* Upgrade to Node.js 16

* Update changelog
2022-07-31 08:14:52 +02:00
ff64a00196 Release 1.176.1 (#1117) 2022-07-31 00:19:40 +02:00
148f6f8762 Feature/refactor env variables access (#1116)
* Refactor env variables access

* Update changelog
2022-07-31 00:18:09 +02:00
bf2c4d1e9e Release 1.176.0 (#1115) 2022-07-30 21:27:35 +02:00
eee1f1c722 Feature/add page titles (#1114)
* Add page titles

* Update changelog
2022-07-30 21:26:08 +02:00
9f2a49a1c7 Feature/introduce max number of symbols per data provider request (#1111)
* Introduce maximum number of symbols per request

* Set log level settings

* Update changelog
2022-07-30 15:48:45 +02:00
44058b2d7a Add descriptions (#1113) 2022-07-30 15:46:58 +02:00
23634f3404 Add environment variables (#1112) 2022-07-30 15:21:14 +02:00
f93dab6086 Add FAQ (#1108) 2022-07-29 19:25:41 +02:00
207859cc22 Release 1.175.0 (#1107) 2022-07-29 18:34:05 +02:00
77181aaaff Feature/setup faq page (#1106)
* Set up FAQ page

* Update changelog
2022-07-29 18:32:26 +02:00
412039badf Bugfix/show symbols of activities in account detail dialog (#1105)
* Show symbols

* Update changelog
2022-07-29 17:04:30 +02:00
7619442895 Feature/add savings rate to investment timeline (#1104)
* Add line for savings rate

* Update changelog
2022-07-29 17:03:23 +02:00
61ecd66e0f Release 1.174.0 (#1098) 2022-07-27 21:09:03 +02:00
81217b35ef Feature/add comment to activity (#1097)
* Add comment to activity

* Update changelog
2022-07-27 21:07:27 +02:00
678f1f0051 Release 1.173.0 (#1095) 2022-07-23 20:38:53 +02:00
71c7e37b5a Bugfix/fix currency inconsistency with usx (#1094)
* Support USX

* Update changelog
2022-07-23 20:37:26 +02:00
80459371f3 Release 1.172.0 (#1093) 2022-07-23 12:09:38 +02:00
35f1f348a8 Feature/add blog post ghostfolio meets internet identity (#1092)
* Add blog post: Ghostfolio meets Internet Identity

* Update changelog
2022-07-23 12:06:49 +02:00
0bb0b12991 Release 1.171.0 (#1091) 2022-07-22 19:57:04 +02:00
d887de50d2 Feature/setup internet identity (#1080)
* Setup Internet Identity as social login provider

* Update changelog
2022-07-22 19:55:33 +02:00
2571e5b8c0 Feature/improve empty states (#1090)
* Improve empty states

* Update changelog
2022-07-22 19:33:06 +02:00
e444d717e5 Add tests for investments by month (#1089)
* Fix investments by month

* Add tests for investments and investments by month

* Update changelog
2022-07-22 19:00:36 +02:00
1866e26c1d Fix distorted tooltip (#1088)
* Fix distorted tooltip

* Update changelog
2022-07-21 20:36:16 +02:00
9923074e04 Add script to open prisma studio with prod env variables (#1087) 2022-07-20 09:16:02 +02:00
c367e61b85 Release 1.170.0 (#1086) 2022-07-19 20:30:59 +02:00
364f1ad9b9 Feature/add support for ust usd (#1085)
* Add UST

* Update changelog
2022-07-19 20:29:42 +02:00
2394cbd6fe Feature/support tags in create and update order dto (#1084)
* Support tags in create or update order

* Update changelog
2022-07-19 20:24:01 +02:00
a74d5cce20 Feature/remove activities import limit for premium users (#1082)
* Remove activities import limit for premium users

* Update changelog
2022-07-17 20:44:28 +02:00
95bcc3f32d Feature/remove alias from user interface (#1083)
* Remove alias from user interface

* Update changelog
2022-07-17 11:05:23 +02:00
e9dbd4a55d Add blog post to resources (#1081) 2022-07-17 10:09:11 +02:00
d440b09dc9 Improve quotes (#1078) 2022-07-15 09:04:44 +02:00
cc16ba5dc8 Release 1.169.0 (#1077) 2022-07-14 16:31:03 +02:00
d10227bc39 Feature/add support for luna2 and songbird cryptocurrencies (#1075)
* Add LUNA2 and SGB1

* Update changelog
2022-07-14 16:28:50 +02:00
4e214c32e8 Feature/update cryptocurrencies.json 20220714 (#1074)
* Update cryptocurrencies.json

* Update changelog
2022-07-14 16:27:32 +02:00
49e2862e03 Feature/add blog post about personal finances (#1073)
* Add blog post

* Update changelog
2022-07-14 16:16:07 +02:00
34e33a2400 Feature/upgrade date fns to version 2.28.0 (#1070)
* Upgrade date-fns

* Update changelog
2022-07-11 19:39:03 +02:00
ec9bc984af Release 1.168.0 (#1071) 2022-07-10 22:25:58 +02:00
2388c494df Feature/handle currency pair inconsistency in yahoo finance service (#1069)
* Handle occasional currency pair inconsistency: GBP=X instead of USDGBP=X

* Update changelog
2022-07-10 22:24:27 +02:00
d71ab10eed Bugfix/fix content height of account detail dialog (#1068)
* Fix height

* Update changelog
2022-07-10 21:44:23 +02:00
0e0592180f Add current month (#1067) 2022-07-10 09:41:48 +02:00
60e2aff488 Extend investment timeline by month (#1066)
* Extend investment timeline grouped by month

* Update changelog
2022-07-09 21:18:05 +02:00
7b5454e7de Release 1.167.0 (#1065) 2022-07-07 21:08:32 +02:00
30835ced88 Bugfix/fix holdings for basic users (#1064)
* Fix holdings for basic users

* Update changelog
2022-07-07 21:06:12 +02:00
8897f32bc5 Clean up modules (#1063) 2022-07-07 07:04:23 +02:00
abaa6b5f27 Feature/improve create account link in live demo (#1061)
* Improve router link

* Update changelog
2022-07-06 17:49:19 +02:00
2060fcaf0b Feature/add markets to public pages (#1062)
* Add Markets to public pages

* Update changelog
2022-07-05 21:45:27 +02:00
fd2408dd62 fix: add git when building docker image (#1052) 2022-07-03 19:57:04 +02:00
31cca024f1 Feature/upgrade ngx markdown to version 14.0.1 (#1055)
* Upgrade ngx-markdown

* Update changelog
2022-07-02 10:58:46 +02:00
b535122945 Make use of demo route (#1060) 2022-07-01 20:07:49 +02:00
5113e4e3ad Release 1.166.0 (#1059) 2022-06-30 21:09:58 +02:00
35e039748f Feature/refactor demo account as route (#1058)
* Refactor demo account as route

* Update changelog
2022-06-30 21:07:35 +02:00
c6b9e0aa5b Feature/upgrade zone.js to version 0.11.6 (#1054)
* Upgrade zone.js

* Update changelog
2022-06-30 19:38:33 +02:00
b250491ca5 Feature/upgrade yahoo finance2 to version 2.3.3 (#1053)
* Upgrade yahoo-finance2 to version 2.3.3

* Update changelog
2022-06-30 08:04:39 +02:00
61e501c659 Feature/fix version of @angular/cli (#1056)
* Fix version

* Update yarn.lock
2022-06-29 21:07:55 +02:00
c0f19d56ec Feature/add account detail dialog (#1047)
* Add account detail dialog

* Update changelog
2022-06-28 21:08:34 +02:00
8e2b235b1f Feature/improve search label (#1048)
* Improve search label

* Update changelog
2022-06-28 13:33:59 +02:00
c3407e9b34 Feature/upgrade prisma to version 3.15.2 (#1046)
* Upgrade prisma to version 3.15.2

* Update changelog
2022-06-25 19:04:01 +02:00
74193e4ee2 Feature/upgrade nestjs dependencies to version 8.4.7 (#1045)
* Upgrade nestjs dependencies to version 8.4.7

* Update changelog
2022-06-25 19:03:25 +02:00
3fe8f9c882 Release 1.165.0 (#1044) 2022-06-25 17:34:06 +02:00
d130efad47 Clean up comments (#1043)
* Clean up comments
2022-06-25 17:30:43 +02:00
109f0ebd70 Feature/move positions table to holdings section (#1042)
* Move positions table to holdings section

* Update changelog
2022-06-25 14:47:20 +02:00
069ddcc6b2 Feature/add reusable premium indicator component (#1041)
* Add premium indicator component

* Update changelog
2022-06-25 12:38:15 +02:00
f7bf6e652b Feature/add icon and name to positions table (#1040)
* Add icon and name

* Update changelog
2022-06-25 11:33:59 +02:00
eb059a024a Feature/delete data in data gathering by symbol (#1039)
* Delete market data

* Update changelog
2022-06-25 11:15:01 +02:00
ad88acff1c Release 1.164.0 (#1038) 2022-06-23 19:25:46 +02:00
1ff736537c Feature/add positions table to public page (#1037)
* Add positions table

* Update changelog
2022-06-23 19:24:24 +02:00
1fa65e1efd Release 1.163.0 (#1036) 2022-06-22 20:32:06 +02:00
df6bb489c2 Feature/improve onboarding for ios (#1035)
* Improve onboarding for iOS

* Update changelog
2022-06-22 20:30:03 +02:00
928a13310d Improve title (#1031) 2022-06-21 17:58:13 +02:00
2384861953 Remove experimental (#1032) 2022-06-20 20:32:51 +02:00
fe90bda6fb Release 1.162.0 (#1029) 2022-06-18 17:48:54 +02:00
d4b29ff11c Feature/add privacy policy page (#1028)
* Add privacy policy page

* Update changelog
2022-06-18 17:46:51 +02:00
a0a26cfa58 Feature/simplify header (#1027)
* Hide pricing page link for Premium users

* Harmonize content

* Update changelog
2022-06-18 11:57:27 +02:00
1610150427 Bugfix/fix currency conversion of ila to ils (#1026)
* Fix currency conversion: ILA to ILS

* Update changelog
2022-06-17 20:34:41 +02:00
cff8acd7b1 Clean up (#1022) 2022-06-17 20:04:52 +02:00
0f36d6cbdb Release 1.161.1 (#1025) 2022-06-16 17:20:44 +02:00
046e28b521 Release 1.161.0 (#1024) 2022-06-16 16:51:57 +02:00
aba562cb35 Bugfix/fix error handling for missing market prices (#1023)
* Add fallback for missing market price

* Update changelog
2022-06-16 16:49:29 +02:00
03f2f33344 Feature/restructure landing page (#1021)
* Restructure landing page

* Update changelog
2022-06-16 16:29:35 +02:00
a996dd7ed5 Feature/add vertical hover line to performance chart (#1020)
* Add vertical hover line

* Update changelog
2022-06-16 14:16:53 +02:00
002b883668 Feature/upgrade to angular 14 (#1019)
* Upgrade to angular 14

* Migrate UntypedFormControl to FormControl

* Update changelog
2022-06-16 10:28:23 +02:00
0b06823893 Release 1.160.0 (#1018) 2022-06-15 20:18:13 +02:00
2dfd779444 Bugfix/fix no data provider has been found in search (#1017)
* Fix default value of DATA_SOURCES

* Update changelog
2022-06-15 20:16:38 +02:00
1824413379 Release 1.159.0 (#1015) 2022-06-15 13:24:07 +02:00
3332ade3d3 Feature/filter public endpoint by equity asset class (#1014)
* Filter by ASSET_CLASS: EQUITY

* Update changelog
2022-06-15 13:21:47 +02:00
8d2e110e3d Feature/change default host (#1013)
* Change default host to 0.0.0.0

* Update changelog
2022-06-14 17:43:42 +02:00
a8fcf09380 Fix link (#1012) 2022-06-13 17:44:55 +02:00
1071f446a8 Release 1.158.1 (#1010) 2022-06-12 19:21:02 +02:00
03b050d1ac Release 1.158.0 (#1009) 2022-06-12 17:53:09 +02:00
58eeff7001 Feature/expose the environment variable host (#1007)
* Expose environment variable: HOST

* Update changelog
2022-06-12 17:51:26 +02:00
76fb8825e4 Feature/restructure self hosting documentation (#1008)
* Restructure self-hosting documentation

* Update changelog
2022-06-12 17:50:45 +02:00
0f9d142afe Feature/decrease number of attempts of queue jobs (#1006)
* Decrease number of attempts

* Update changelog
2022-06-12 17:39:55 +02:00
bd33855a27 Feature/improve message for data provider errors (#1003)
* Add title

* Update changelog
2022-06-12 17:39:02 +02:00
5329e45e2c Feature/add data dialog to queue jobs view (#1005)
* Add data dialog

* Update changelog
2022-06-12 17:30:26 +02:00
e990ecd12c Feature/improve cash balance label (#1002)
* Improve label

* Update changelog
2022-06-12 10:06:18 +02:00
a4fcf64f13 Release 1.157.0 (#1000) 2022-06-11 13:41:39 +02:00
557e3a0676 Feature/migrate historical market data gathering to queue design pattern (#991)
* Migrate historical market data gathering to queue

* Filter and delete jobs

* Detect duplicate jobs

* Update changelog
2022-06-11 13:40:15 +02:00
2abe399ebd Bugfix/force reload accounts of user after change (#994)
* Force reload of accounts after change

* Update changelog
2022-06-11 12:56:34 +02:00
74fe90906a Bugfix/exclude emtpy items in activities filter (#999)
* Exclude empty items

* Update changelog
2022-06-11 12:55:32 +02:00
4cb9a3b142 Feature/refresh cryptocurrencies list (#998)
* Use cryptocurrencies list instead of outdated npm package

* Update changelog
2022-06-11 12:54:58 +02:00
0da9368e0c Fix date (#992) 2022-06-10 17:42:56 +02:00
d2f8e3d645 Feature/increase fear and greed index to 180 days (#993)
* Increase number of days to 180

* Update changelog
2022-06-09 18:56:29 +02:00
5263fba64e Feature/upgrade envalid to version 7.3.1 (#990)
* Upgrade envalid to version 7.3.1

* Update changelog
2022-06-06 17:42:32 +02:00
e3689c48f8 Feature/upgrade chart.js to version 3.8.0 (#989)
* Upgrade chart.js to version 3.8.0

* Update changelog
2022-06-06 08:27:34 +02:00
787efdb33b Release 1.156.0 (#988) 2022-06-05 19:03:11 +02:00
e63578d8ce Feature/add guards to local comparison (#986)
* Add guards

* Improve labels
2022-06-05 19:00:45 +02:00
7cf0cdc4ce Feature/add jobs of queue to admin control panel (#987)
* Add jobs of queue to admin control panel

* Update changelog
2022-06-05 19:00:20 +02:00
14a0eeab29 Bugfix/fix docker compose files to resolve variables correctly (#983)
* Fix variable resolving

* Update changelog
2022-06-04 10:02:38 +02:00
6774c48dff Feature/restructure fire page (#982)
* Restructure fire page

* Update changelog
2022-06-04 09:06:53 +02:00
565947e752 Feature/upgrade simplewebauthn browser and server to version 5.2.1 (#981)
* Upgrade @simplewebauthn/browser and @simplewebauthn/server

* Update changelog
2022-06-03 18:44:53 +02:00
2cc7c6fa1c Feature/add user id to account page (#980)
* Add user id

* Update changelog
2022-06-03 06:51:08 +02:00
023a7147e2 Feature/simplify feature page (#978)
* Simplify page

* Update changelog
2022-05-31 18:01:29 +02:00
a96e89a86e Release 1.155.0 (#977) 2022-05-29 15:39:57 +02:00
b9c9443899 Bugfix/fix empty state of proportion chart (#976)
* Fix empty state (chart with two levels)

* Update changelog
2022-05-29 15:38:13 +02:00
f1e06347d3 Feature/add data source eod historical data (#974)
* Add EOD Historical Data as a data source

* Update changelog
2022-05-29 15:37:40 +02:00
697e92f818 Feature/finalize exposing redis password env variable (#975)
* Add hints

* Update changelog
2022-05-29 14:54:53 +02:00
b678998801 Feature/add-redis-password (#947)
* Expose REDIS_PASSWORD
2022-05-29 14:18:57 +02:00
de53cf1884 Release 1.154.0 (#973) 2022-05-28 21:12:42 +02:00
bbe30218bd Feature/remove dependency round to (#972)
* Remove round-to dependency

* Update changelog
2022-05-28 21:10:45 +02:00
15dda886a0 Feature/add vertical hover line to line chart component (#963)
* Add vertical hover line

* Improve tooltips of charts

* Update changelog
2022-05-28 20:53:54 +02:00
34d4212f55 Feature/modernize pricing page (#967)
* Simplify pricing page

* Update changelog
2022-05-28 18:52:30 +02:00
f7060230b7 Update dates (#966) 2022-05-28 18:46:22 +02:00
0fdafcb7e4 Release 1.153.0 (#962) 2022-05-27 11:53:18 +02:00
e79be9f2d6 Feature/do not tweet on weekend (#961)
* Do not tweet on the weekend

* Update changelog
2022-05-27 11:37:48 +02:00
69088b93a6 Feature/add value redaction as interceptor (#960)
* Add value redaction as interceptor

* Update changelog
2022-05-27 11:21:47 +02:00
c3768a882d Feature/add benchmarks to twitter bot service (#959)
* Extend benchmarks with market condition and adapt twitter bot service

* Update changelog
2022-05-27 10:03:37 +02:00
3498ed8549 Feature/upgrade prisma to version 3.14.0 (#958)
* Upgrade prisma dependencies to version 3.14.0

* Update changelog
2022-05-27 09:50:38 +02:00
c07c300fef Move @simplewebauthn/typescript-types to devDependencies (#957) 2022-05-27 09:49:57 +02:00
c62a5af9eb Bugfix/fix width of skeleton loader in benchmark component (#956)
* Fix width

* Update changelog
2022-05-27 09:49:37 +02:00
0c04f10e19 Release 1.152.0 (#955) 2022-05-26 19:01:08 +02:00
2c4c16ec99 Feature/extend markets overview by benchmarks (#953)
* Add benchmarks to markets overview

* Update changelog
2022-05-26 18:59:29 +02:00
4711b0d1ed Improve instructions for Unraid (#954) 2022-05-26 17:59:09 +02:00
a8521e0ecf Update README.md (#943)
* Update README.md for Unraid users
2022-05-26 17:52:14 +02:00
424748ae90 Feature/add ghostfolio trailer to landing page (#952)
* Add link to Ghostfolio trailer

* Update changelog
2022-05-26 10:30:13 +02:00
9c4d8bdf4b Release 1.151.0 (#949) 2022-05-24 20:58:24 +02:00
332203b9e2 Feature/add support to set the base currency via env variable (#948)
* Set base currency via environment variable

* Update changelog
2022-05-24 20:55:55 +02:00
f48832c671 Bugfix/add missing conversion of countries (#941)
* Add missing conversion of countries for SymbolProfileOverrides

* Update changelog
2022-05-23 18:04:09 +02:00
ae8a203526 Add type (#939) 2022-05-22 21:14:22 +02:00
d0c1506ded Release 1.150.0 (#940) 2022-05-21 20:00:34 +02:00
af0863d193 Bugfix/fix currency conversion in accounts (#937)
* Fix currency conversion in accounts

* Update changelog
2022-05-21 19:58:47 +02:00
f5819cc399 Bugfix/fix countries in symbol profile overrides (#936)
* Fix countries

* Update changelog
2022-05-20 20:16:23 +02:00
977c5a9544 Feature/skip data enhancement if data is inaccurate (#935)
* Skip data enhancer if data is inaccurate

* Update changelog
2022-05-20 20:15:19 +02:00
b9cd42cd53 Move dependencies to devDependencies (#934) 2022-05-20 20:14:33 +02:00
379977008d Simplify intro text (#933) 2022-05-19 21:05:14 +02:00
38f9d54705 Release 1.149.0 (#927) 2022-05-16 21:50:43 +02:00
5cb6e5dec6 Feature/support filtering by asset class on the allocations page (#926)
* Support filtering by asset class

* Update changelog
2022-05-16 21:49:22 +02:00
4a123c38f2 Refactor placeholder (#925) 2022-05-16 21:17:58 +02:00
160335302a Feature/group filters by type (#922)
* Add groups to activities filter component

* Update changelog
2022-05-15 21:51:31 +02:00
f1483569a2 Release 1.148.0 (#921) 2022-05-14 13:55:25 +02:00
5391b88c42 Feature/add report data glitch button (#920)
* Add report data glitch button

* Update changelog
2022-05-14 13:53:43 +02:00
2b63f7e707 Feature/support enter to submit create or update transaction dialog form (#913)
* Support enter key press to submit form

* Update changelog
2022-05-14 10:56:07 +02:00
d5c96d1cb7 Bugfix/fix date picker date format (#912)
* Fix date picker date format

* Update changelog
2022-05-14 10:55:09 +02:00
1a4dc51825 Bugfix/fix state of delete account button (#911)
* Fix disable state

* Update changelog
2022-05-13 06:48:40 +02:00
d094bae7de Bugfix/fix issue in activities filter component with typing (#910)
* Handle filter (selecting) or search term (typing)

* Update changelog
2022-05-12 07:48:12 +02:00
57bf10e7e7 Release 1.147.0 (#904) 2022-05-10 21:25:25 +02:00
c1d460cead Improve filtering (#901) 2022-05-10 21:24:36 +02:00
dfa67b275c Feature/improve filtering on allocations page (#900)
* Include cash positions on allocations page (with no filtering)

* Update changelog
2022-05-10 19:22:57 +02:00
80862e5c2a Release 1.146.3 (#899) 2022-05-08 22:54:07 +02:00
904d4db219 Refactor build:all and build:dev scripts (#898) 2022-05-08 22:52:46 +02:00
10f13eec48 Release 1.146.2 (#897) 2022-05-08 22:18:00 +02:00
ea3a9d3b79 Feature/eliminate circular dependencies in common library (#896)
* Eliminate circular dependencies

* Update changelog
2022-05-08 22:16:47 +02:00
e55b05fe3d Release 1.146.1 (#895) 2022-05-08 16:57:23 +02:00
32dd76be5f Fix path to jest files (#894) 2022-05-08 16:55:14 +02:00
ff9b6bb4df Release 1.146.0 (#893) 2022-05-08 16:04:43 +02:00
5be95b7b63 Feature/simplify about page (#892)
* Simplify about page

* Update changelog
2022-05-08 16:02:44 +02:00
b3e07c8446 Feature/support permissions in fire calculator (#891)
* Support hasPermissionToUpdateUserSettings

* Update changelog
2022-05-08 15:59:19 +02:00
eb9cece4e4 Update browserslist database (#890) 2022-05-08 15:56:39 +02:00
b331f5f04d Feature/setup nx cloud (#889)
* Upgrade angular, Nx and storybook

* Setup Nx Cloud

* Update changelog
2022-05-08 15:52:21 +02:00
34cbdd7c2a Upgrade angular, Nx and storybook (#888)
* Upgrade angular, Nx and storybook

* Update changelog
2022-05-08 09:26:33 +02:00
57314d62ee Feature/improve allocations page with no filter (#887)
* Improve accounts for no filters

* Update changelog
2022-05-07 22:33:57 +02:00
40380346e6 Feature/setup bull queue system (#886)
* Setup @nestjs/bull and asset profile data gathering job

* Update changelog
2022-05-07 20:00:51 +02:00
5622c4cf7e Feature/harmonize no data available label (#885)
* Harmonize label for UNKNOWN_KEY

* Update changelog
2022-05-07 14:11:42 +02:00
21173bed21 Release 1.145.0 (#884) 2022-05-07 11:47:28 +02:00
16dd8f7652 Feature/refactor filters with interface (#883)
* Refactor filtering with an interface

* Filter by accounts

* Update changelog
2022-05-07 11:44:29 +02:00
ce6b5fb7cb Bugfix/fix tooltip in proportion chart after update (#882)
* Keep tooltip configuration up to date

* Update changelog
2022-05-01 08:38:57 +02:00
f6f62db830 Feature/add support for private equity (#881)
* Add support for private equity

* Update changelog
2022-04-30 22:16:13 +02:00
01103f3db4 Feature/add asset and asset sub class to wealth items form (#880)
* Add asset and asset sub class

* Update changelog
2022-04-30 21:47:10 +02:00
e9e9f1a124 Release 1.144.0 (#879) 2022-04-30 11:51:49 +02:00
751256f158 Feature/add support for real estate and precious metal (#878)
* Add support for real estate and precious metal

* Update changelog
2022-04-30 11:49:58 +02:00
c2a1cbd20f Feature/improve layout of position detail dialog (#877)
* Improve layout

* Update changelog
2022-04-30 10:48:02 +02:00
04044f8720 Support futures (#845)
* Support futures

* Upgrade yahoo-finance2 to version 2.3.2

* Update changelog
2022-04-30 09:55:24 +02:00
4dc76817ce Bugfix/fix import validation for numbers equal zero (#875)
* Fix import validation for numbers equal 0

* Update changelog
2022-04-29 13:10:45 +02:00
1f0bd5a7db Bugfix/fix color of spinner in filter component (#873)
* Fix color for dark mode

* Update changelog
2022-04-27 17:30:57 +02:00
b6cd007ad4 Release/1.143.0 (#871)
* Release 1.143.0
  * Improve filtering by tags
2022-04-26 22:31:53 +02:00
b4bc72c6f9 Release 1.142.0 (#869) 2022-04-25 22:41:02 +02:00
899fa0370e Feature/improve users table of admin control panel (#866)
* Improve users table

* Update changelog
2022-04-25 22:39:08 +02:00
da27504aa1 Add orderBy statement to make debugging easier (#868) 2022-04-25 22:37:56 +02:00
b7bbc029ac Feature/render tags in dialogs (#864)
* Render tags

* Update changelog
2022-04-25 22:37:34 +02:00
c61a415fb2 Bugfix/change date to utc in data gathering service (#867)
* Change date to UTC

* Update changelog
2022-04-25 18:12:42 +02:00
8ff811ed28 Release/1.141.1 (#863) 2022-04-24 17:27:00 +02:00
9a2ea0a4ed Release 1.141.0 (#862) 2022-04-24 16:24:21 +02:00
bad9d17c44 Setup allocations page and endpoint (#859)
* Setup tagging system

* Update changelog
2022-04-24 16:23:03 +02:00
ea89ca5734 Feature/simplify ids in database schema (#861)
* Simplify ids in database schema
  * Access
  * Order
  * Subscription

* Update changelog
2022-04-24 09:35:01 +02:00
8f61f7c169 Feature/extract activities table filter component (#858)
* Extract activities table component

* Update changelog
2022-04-23 19:22:20 +02:00
edca05f542 Feature/change get started url of public page (#857)
* Change url

* Update changelog
2022-04-23 16:48:23 +02:00
283f054ee2 Feature/upgrade prisma to version 3.12.0 (#856)
* Upgrade prisma to version 3.12.0

* Update changelog
2022-04-23 13:38:41 +02:00
e9a46cb224 Release 1.140.2 (#854) 2022-04-23 09:52:22 +02:00
4a75c6d483 Release 1.140.1 (#853) 2022-04-22 21:45:50 +02:00
bbe9183fb0 Release 1.140.0 (#852) 2022-04-22 21:29:08 +02:00
1b03ddc586 Feature/add symbol profile overrides model (#851)
* Add symbol profile overrides model

* Update changelog
2022-04-22 21:27:55 +02:00
beb12637ce Bugfix/fix total calculation for sell and dividend (#850)
* Fix calculation for sell and dividend activities

* Update changelog
2022-04-22 19:29:18 +02:00
20358d9105 Feature/persist savings rate (#849)
* Persist savings rate

* Update changelog
2022-04-21 23:07:19 +02:00
0e4c39d145 Feature/reuse value component in ghostfolio in numbers section (#846)
* Reuse value component

* Update changelog
2022-04-19 17:06:12 +02:00
83ebacbb06 Add Buy me a coffee link (#848) 2022-04-18 21:32:54 +02:00
7c58c5fb7f Setup funding.yml (#847) 2022-04-18 21:20:30 +02:00
f3271ab1ff Feature/upgrade yahoo finance2 to version 2.3.1 (#844)
* Upgrade yahoo-finance2 to version 2.3.1

* Update changelog
2022-04-18 17:10:00 +02:00
9f597cbff1 Release 1.139.0 (#843) 2022-04-18 11:59:16 +02:00
90efc2ac51 Feature/beautify etf names in asset profile (#842)
* Beautify ETF names

* Update changelog
2022-04-18 11:57:57 +02:00
056b318d86 Bugfix/fix end date in ics files (#841)
* Fix end date

* Update changelog
2022-04-18 11:31:16 +02:00
82ede2fe32 Bugfix/fix fear and greed data source (#840)
* Fix data source of Fear & Greed Index

* Update changelog
2022-04-18 10:49:02 +02:00
8ae041faa0 Bugfix/fix issue in fire calculator after changing investment horizon (#839)
* Properly update chart datasets and improve tooltip

* Update changelog
2022-04-18 10:31:16 +02:00
bd4608e521 Release 1.138.0 (#838) 2022-04-16 21:03:28 +02:00
0d8362ca8f Feature/separate deposit and savings in fire calculator (#837)
* Separate deposit and savings

* Update changelog
2022-04-16 21:01:55 +02:00
638ae3f7fa Feature/add boringly getting rich guide to resources page (#836)
* Add "Boringly Getting Rich" guide

* Update changelog
2022-04-16 15:35:31 +02:00
6e7cf0380b Feature/export single draft (#835)
* Export single draft

* Update changelog
2022-04-16 11:33:01 +02:00
ec2ecab751 Clean up name (#834) 2022-04-16 10:21:32 +02:00
598fe41b8c Release 1.137.0 (#831) 2022-04-15 18:58:33 +02:00
ba7c98d325 Add test case for BUY and SELL (partially) (#826)
* Add test case for BUY and SELL (partially)

* Fix investment calculation for sell activities

* Do not show total value if sell activity

* Update changelog
2022-04-15 18:56:23 +02:00
65e062ad26 Simplify search (#828)
* Simplify search

* Update changelog
2022-04-15 12:39:33 +02:00
8526b5a027 Feature/export draft activities as ics (#830)
* Export draft activities as ICS

* Update changelog
2022-04-15 10:53:40 +02:00
f1feb04f29 Release 1.136.0 (#829) 2022-04-13 07:46:35 +02:00
500e09d95a Bugfix/fix loading state in fire calculator (#824)
* Fix loading state

* Update changelog
2022-04-12 19:57:23 +02:00
aef91d3e30 Feature/improve label in summary (#827)
* Improve label

* Update changelog
2022-04-12 18:04:48 +02:00
70723f8d5f Bugfix/fix projected total amount in fire calculator (#825)
* Fix calculation of projected total amount

* Update changelog
2022-04-11 22:10:45 +02:00
6cfd052781 Release 1.135.0 (#823) 2022-04-10 20:03:39 +02:00
23f2ac472e Feature/add fire calculator (#822)
* Add fire calculator

* Update changelog
2022-04-10 20:02:31 +02:00
d5ba624403 Feature/add support for terra (#820)
* Add Terra (LUNA1-USD)

* Update changelog
2022-04-10 19:38:27 +02:00
9b49ed77f7 Feature/add support for thor chain (#819)
* Add THORChain (RUNE-USD)

* Update changelog
2022-04-09 20:16:36 +02:00
08405d14d5 Release 1.134.0 (#818) 2022-04-09 14:50:13 +02:00
56b169e1c4 Feature/make header background solid (#817)
* Remove alpha

* Update changelog
2022-04-09 10:28:07 +02:00
67f2b326f3 Switch to new calculation engine (#814)
* Switch to new calculation engine

* Clean up old portfolio calculation engine (#815)

* Rename new portfolio calculation engine (#816)

* Update changelog
2022-04-09 10:17:31 +02:00
3d3a6c1204 Feature/improve fire section (#813)
* Improve FIRE section

* Update changelog
2022-04-09 09:03:39 +02:00
bfc8f87d88 Release 1.133.0 (#812) 2022-04-07 22:15:09 +02:00
957200854c Feature/improve empty state of proportion chart (#811)
* Improve empty state

* Update changelog
2022-04-07 22:13:41 +02:00
6575440877 Bugfix/fix dates in value component (#810)
* Fix dates

* Update changelog
2022-04-07 17:20:12 +02:00
255af6a6e9 Release 1.132.1 (#809) 2022-04-06 22:02:40 +02:00
795a6a6799 Release 1.132.0 (#808) 2022-04-06 21:23:20 +02:00
2a854e2574 Various improvements (#807) 2022-04-06 21:21:53 +02:00
52d113e71f Feature/improve label of average price (#805)
* Improve label

* Update changelog
2022-04-06 18:02:21 +02:00
204c7360c3 Feature/prepare for localized date format (#803)
* Support localized date and number format

* Update changelog
2022-04-05 21:02:07 +02:00
fa41e25c8f Release/1.131.1 (#804)
* Add API version

* Update changelog
2022-04-04 07:30:17 +02:00
ba765b9de6 Release 1.131.0 (#797) 2022-04-02 19:32:47 +02:00
fa79196278 Feature/display values in base currency on mobile (#796)
* Display values in base currency on mobile

* Update changelog
2022-04-02 19:28:32 +02:00
d1230ca3ad Support coupon codes on Google Play (#795) 2022-04-02 18:47:53 +02:00
69a1316cfe Feature/upgrade prisma to 3.11.1 (#794)
* Upgrade prisma dependencies to version 3.11.1

* Update changelog
2022-04-02 17:38:47 +02:00
a256b783bc Feature/upgrade yahoo finance to 2.3.0 (#792)
* Upgrade yahoo-finance2

* Update changelog
2022-04-02 17:36:49 +02:00
ebbdd47fa2 Exclude google login callback endpoint from versioning (#793) 2022-04-02 17:36:15 +02:00
3d21e2eac6 Improve upgrade guide (#780) 2022-04-02 12:06:06 +02:00
bc117fe601 Fix port (#788) 2022-04-02 10:47:26 +02:00
65f6bcb166 Feature/harmonize algebraic sign of gross and net performance percent (#776)
* Harmonize algebraic sign

* Update changelog
2022-04-02 10:44:19 +02:00
b8c43ecf89 Add documentation for import API (#787) 2022-04-02 10:29:41 +02:00
1214127ec0 Feature/rename orders to activities in import and export (#786)
* Rename orders to activities

* Update changelog
2022-04-02 10:26:17 +02:00
e986310302 Improve price (#785) 2022-04-02 09:52:59 +02:00
6762572658 Feature/setup api versioning (#783)
* Setup API versioning

* Update changelog
2022-04-02 08:46:24 +02:00
eb77652d6a Clean up import (#784) 2022-04-02 08:03:17 +02:00
a7b59f4ec6 Extend prerequisites (#781) 2022-04-02 06:47:58 +02:00
dd71f2be45 Feature/improve pricing page (#778)
* Improve pricing page

* Update changelog
2022-04-01 19:58:33 +02:00
d530cb38fa Feature/add 7 days in coupon system (#777)
* Add 7 days

* Update changelog
2022-04-01 19:49:05 +02:00
16b79a7e60 Release 1.130.0 (#774) 2022-03-30 21:16:43 +02:00
7f0c98cae6 Feature/setup fire page with 4 percent rule (#773)
* Setup page for FIRE

* Update changelog
2022-03-30 21:14:49 +02:00
57e4163848 Feature/add 14 days in coupon system (#772)
* Add 14 days

* Update changelog
2022-03-29 20:49:44 +02:00
14773bf1aa Bugfix/fix duplicate currency conversion in account calculations (#771)
* Fix currency conversion (duplicate)

* Update changelog
2022-03-29 17:47:08 +02:00
1a8fc5757a Release 1.129.0 (#770) 2022-03-26 13:13:07 +01:00
b4848be914 Feature/add developed vs emerging markets calculation (#767)
* Add allocations by market

* Update changelog
2022-03-26 13:11:30 +01:00
2b4319454d Feature/add bonds and emergency fund to feature overview (#768)
* Add bonds and emergency fund

* Update changelog
2022-03-25 23:37:06 +01:00
e2faaf6faa Feature/add hover effect to page tabs (#764)
* Add hover effect

* Update changelog
2022-03-21 18:59:00 +01:00
86a1589834 Release/1.128.0 (#763) 2022-03-19 14:39:25 +01:00
9f67993c03 Feature/fix issue with recent transactions (#750)
* Fix percentage performance issue with recent transactions

Co-authored-by: Reto Kaul <retokaul@sublimd.com>
2022-03-19 14:33:43 +01:00
32fb3551dc Feature/add default market price to scraper configuration (#762)
* Add default market price to scraper configuration

* Update changelog
2022-03-19 12:17:28 +01:00
30411b1502 Feature/add hover to table (#760)
* Add hover

* Update changelog
2022-03-19 09:56:50 +01:00
eb0444603b Bugfix/fix user currency of public page (#761)
* Fix user currency

* Update changelog
2022-03-19 09:25:20 +01:00
6e582fe505 Release 1.127.0 (#759) 2022-03-16 22:08:48 +01:00
402d73a12c Bugfix/fix get quotes for multiple ghostfolio symbols (#758)
* Support multiple symbols in getQuotes()

* Update changelog
2022-03-16 22:07:18 +01:00
4826a51199 Feature/improve handling of scraper configuration (#757)
* Improve handling of missing scraper configuration

* Update changelog
2022-03-15 17:13:42 +01:00
5356bf568e Release 1.126.0 (#756) 2022-03-14 17:41:23 +01:00
d8da574ae4 Feature/add support for bonds (#755)
* Add support for bonds

* Update changelog
2022-03-14 17:39:09 +01:00
e769fabbae Feature/add multilines to tooltips in proportion chart (#753)
* Introduce multilines for tooltips

* Update changelog
2022-03-13 21:39:06 +01:00
5a369f29d4 Feature/restructure portfolio summary tab (#754)
* Restructure portfolio summary

* Update changelog
2022-03-13 21:01:15 +01:00
122ba9046f Add type (#751)
* Add type

* Refactor import to import type
2022-03-13 21:00:40 +01:00
f781eb207c Release 1.125.0 (#752) 2022-03-12 18:40:09 +01:00
7b6893b5ed Feature/add support for emergency fund (#749)
* Add support for emergency fund

* Update changelog
2022-03-12 13:44:47 +01:00
07799573cb Harmonize button style (#748) 2022-03-10 20:11:32 +01:00
9cdef6a7cb Feature/upgrade nx to version 13.8.5 (#747)
* Upgrade Nx to version 13.8.5

* Update changelog
2022-03-09 18:19:48 +01:00
0d897bc461 Improve table header (#746) 2022-03-08 20:15:59 +01:00
e4908b51aa Feature/add context to logger (#745)
* Add contexts

* Update changelog
2022-03-07 17:20:07 +01:00
718b0de0a7 Release 1.124.0 (#744) 2022-03-06 13:48:17 +01:00
99655604d9 Feature/add support for coupon duration (#743)
* Add support for coupon duration

* Update changelog
2022-03-06 12:26:04 +01:00
b602e7690b Feature/upgrade prisma to version 3.10.0 (#742)
* Upgrade prisma to version 3.10.0

* Update changelog
2022-03-06 12:06:07 +01:00
7745dafe48 Feature/upgrade yahoo finance2 to 2.2.0 (#741)
* Upgrade yahoo-finance2 to version 2.2.0

* Update changelog
2022-03-06 12:04:38 +01:00
50184284e1 Feature/upgrade ngx skeleton loader to 5.0.0 (#740)
* Upgrade ngx-skeleton-loader to version 5.0.0

* Update changelog
2022-03-06 10:47:30 +01:00
f46533107d Release 1.123.0 (#739) 2022-03-05 11:09:07 +01:00
c216ab1d76 Eliminate data source from order model (#730)
* Eliminate currency, data source and symbol from order model

* Remove prefix for symbols with data source GHOSTFOLIO

* Update changelog
2022-03-05 11:07:27 +01:00
86acbf06f4 Feature/add data provider errors to api response (#738)
* Add data provider error details

* Update changelog
2022-03-05 11:00:02 +01:00
3de7d3f60e Bugfix/improve account calculations (#737)
* Improve account calculations

* Update changelog
2022-03-04 21:31:31 +01:00
63ed227f3f Release 1.122.0 (#732) 2022-03-01 21:36:09 +01:00
5bb20f6d5f Bugfix/fix undefined currencies after creating an activity (#731)
* Fix issue with undefined currencies after creating an activity

* Update changelog
2022-03-01 21:32:19 +01:00
b3e58d182a Feature/add support for click in portfolio proportion chart (#729)
* Add support for click

* Update changelog
2022-02-28 21:35:52 +01:00
93d6746739 Refactoring (#727) 2022-02-28 20:42:14 +01:00
e3f8b0cf52 Release 1.121.0 (#726) 2022-02-27 17:07:30 +01:00
c02bcd9bd8 Feature/migrate to yahoo finance2 (#722)
* Migrate to yahoo-finance2

* Add support for mutual funds

* Add url to symbol profile

* Clean up
2022-02-27 17:03:00 +01:00
6a4f1c0188 Release 1.120.0 (#724) 2022-02-25 08:06:01 +01:00
745ba978a3 Bugfix/fix zen mode (#723)
* Fix Zen mode

* Update changelog
2022-02-25 08:04:42 +01:00
46b91d3c3b Feature/improve portfolio page (#721)
* Improve page

* Update changelog
2022-02-23 19:00:13 +01:00
1dd670a7c3 Feature/improve labels in portfolio proportion chart (#720)
* Improve labels for OTHER and UNKNOWN

* Update changelog
2022-02-22 21:12:02 +01:00
68d07cc8d4 Release 1.119.0 (#719) 2022-02-22 20:05:13 +01:00
02809a529e Reorder (#718) 2022-02-21 20:06:39 +01:00
fd60569716 Release 1.118.0 (#717) 2022-02-20 17:01:06 +01:00
fed771525e Feature/extend market data in admin panel (#716)
* Extend market data view

* Update changelog
2022-02-20 16:57:46 +01:00
a5771f601d Improve calculation of overall performance percentage (#701)
* Improve calculation of overall performance percentage

Co-authored-by: Reto Kaul <retokaul@sublimd.com>
2022-02-20 16:39:43 +01:00
2a2a5f4da5 Feature/display features based on permissions (#714)
* Display features based on permissions

* Update changelog
2022-02-20 14:16:46 +01:00
06d5ec9182 Release 1.117.0 (#713) 2022-02-19 19:48:51 +01:00
122107c8a1 Feature/distinguish today s data point in admin panel (#711)
* Distinguish today's data point

* Update changelog
2022-02-19 19:46:36 +01:00
ca46a9827a Do not tweet on Sunday (#712) 2022-02-19 19:29:49 +01:00
4ec351369b Bugfix/add fallback to default account in import (#709)
* Add fallback to default account if account id is invalid

* Update changelog
2022-02-19 18:51:16 +01:00
dced06ebb5 Bugfix/improve allocations (#710)
* Fix allocations by account for non-unique account names

* Refactor calculations with big.js

* Update changelog
2022-02-19 18:41:12 +01:00
baa6a3d0f0 Feature/restructure api modules (#706)
* Restructure modules

* Update changelog
2022-02-18 19:32:25 +01:00
d3382f0809 Update time (#707) 2022-02-17 21:31:08 +01:00
1eb4041837 Feature/move countries and sectors chart (#704)
* Move countries and sectors charts

* Update changelog
2022-02-17 20:40:28 +01:00
5a869a90da Release 1.116.0 (#703) 2022-02-16 21:23:13 +01:00
280030ae7f Feature/add twitter bot for fear and greed index (#702)
* Add twitter bot for fear and greed index

* Update changelog
2022-02-16 21:17:11 +01:00
52e4504de9 Fix time in market (#700)
* Fix time in market

* Update changelog
2022-02-15 09:47:56 +01:00
20356f6931 Bugfix/fix max items attribute (#698)
* Fix maxItems

* Update changelog
2022-02-15 09:36:55 +01:00
e0bb2b1c78 Feature/improve position detail dialog (#697)
* Improve mobile layout

* Update changelog
2022-02-14 21:20:07 +01:00
ec806be45f Add detached mode (#696) 2022-02-14 18:51:16 +01:00
809ee97f6f Add tests (#693) 2022-02-14 18:50:47 +01:00
893ca83d3a Release 1.115.0 (#695) 2022-02-13 18:14:55 +01:00
23da1bd293 Feature/add feature page (#694)
* Add feature page

* Update changelog
2022-02-13 18:12:59 +01:00
fa66cd5bce Feature/add countries and sectors to position detail dialog (#692)
* Add asset and asset sub class

* Add countries and sectors to position detail dialog

* Update changelog
2022-02-12 11:22:03 +01:00
9344dcd26e Feature/upgrade nx to 13.8.1 (#691)
* Upgrade angular, nx and storybook dependencies

* Update changelog
2022-02-11 10:01:15 +01:00
90ad22cccf Add import and export (#690) 2022-02-11 10:00:49 +01:00
dcc7ef89fe Release/1.114.1 (#689)
* Fix creation of wealth items

* Release 1.114.1
2022-02-10 11:16:25 +01:00
e355847f40 Release 1.114.0 (#688) 2022-02-10 10:33:58 +01:00
76f70598e2 Feature/add support for wealth items (#666)
* Add support for wealth items

* Update changelog
2022-02-10 09:39:10 +01:00
7af5cd244a Release 1.113.0 (#687) 2022-02-09 09:58:46 +01:00
86943a5f5b Feature/harmonize big.js operators (#686)
* Harmonize big.js operators

* Update changelog
2022-02-09 09:36:54 +01:00
6eb4eae4a9 Feature/fix twr performance 2 (#684)
* Fix TWR performance
* Weight holding period returns according to their investment value

Co-authored-by: Reto Kaul <retokaul@sublimd.com>
2022-02-09 09:29:43 +01:00
6ac693dd39 Feature/improve position of currency column (#685)
* Move position of currency column

* Update changelog
2022-02-09 09:25:22 +01:00
e29f7f8976 Release 1.112.1 (#683) 2022-02-06 21:41:51 +01:00
82069da4e2 Bugfix/fix user account creation (#682)
* Fix the user account creation

* Update changelog
2022-02-06 21:40:26 +01:00
07656c6a95 Release 1.112.0 (#681) 2022-02-06 17:18:28 +01:00
16f0743353 Bugfix/fix total value of activities table (#680)
* Fix total value (absolute value)

* Update changelog
2022-02-06 17:14:04 +01:00
9b5ec0c56d Feature/fix twr performance (#679)
* Fix TWR performance

Co-authored-by: Reto Kaul <retokaul@sublimd.com>
2022-02-06 16:54:14 +01:00
8d2fcc6b42 Feature/upgrade prisma to version 3.9.1 (#677)
* Upgrade prisma to version 3.9.1

* Update changelog
2022-02-06 15:47:08 +01:00
e625e55784 Move currency column (#678) 2022-02-06 15:46:14 +01:00
bed3e5aae2 Bugfix/fix horizontal overflow in activities table (#676)
* Fix horizontal overflow in tables

* Update changelog
2022-02-06 15:45:39 +01:00
65bfe52db4 Feature/simplify admin user sign up (#675)
* Simplify admin user sign up

* Update changelog
2022-02-06 09:32:41 +01:00
48b524de5a Feature/add export functionality to position detail dialog (#672)
* Add export functionality to the position detail dialog

* Respect filters in activities export

* Update changelog
2022-02-05 20:26:10 +01:00
67d40333f6 Move currency column (#674) 2022-02-05 10:17:09 +01:00
48f6b8d353 Release 1.111.0 (#671) 2022-02-03 21:00:53 +01:00
f369996912 Bugfix/fix symbol selection of 7d data gathering (#670)
* Fix symbol selection of 7d data gathering

* Update changelog
2022-02-03 20:58:59 +01:00
dc424a86ec Feature/support deleting symbol profile data (#669)
* Add support for deleting symbol profile data

* Update changelog
2022-02-03 20:56:39 +01:00
5d8bde5a70 Feature/access data source and symbol from symbol profile (#668)
* Access dataSource and symbol from SymbolProfile

* Update changelog
2022-02-03 19:21:55 +01:00
16360c0c67 Feature/minor code cleanup (#667)
* Sort imports

* Update changelog
2022-02-02 22:06:34 +01:00
526a6b2030 Release 1.110.0 (#665) 2022-02-02 20:31:23 +01:00
5000e9c79b Feature/update database schema of order (#664)
* Add schema migrations

* Update changelog
2022-02-02 20:29:19 +01:00
161cb82820 Bugfix/fix data source of fear and greed index (#663)
* Encode data source

* Update changelog
2022-02-02 20:07:33 +01:00
fed28f29d1 Release 1.109.0 (#662) 2022-02-01 21:05:14 +01:00
8bd9330acc Feature/improve usability of create or edit transaction dialog (#661)
* Move the fee to the bottom

* Update changelog
2022-02-01 20:35:44 +01:00
155c08d665 Transform data source (#658)
* Transform data source

* Update changelog
2022-02-01 20:35:25 +01:00
b8ad6d6662 Feature/improve import (#657)
* Improve import

* Update changelog

Co-Authored-By: Ronald Konjer <ronaldkonjer@gmail.com>
2022-02-01 19:12:00 +01:00
9d6977e3f7 Feature/support cryptocurrency mina protocol (#659)
* Support Mina Protocol (MINA-USD)

* Update changelog
2022-02-01 10:58:34 +01:00
919b20197f import csv with account name or id (#654)
* import csv with account id
2022-01-29 17:27:33 +01:00
62885ea890 Feature/improve consistent use of symbol with data source (#656)
* Improve the consistent use of symbol with dataSource

* Update changelog
2022-01-29 16:51:37 +01:00
035d8ad9eb Update copyright (#655) 2022-01-29 09:15:11 +01:00
9676f96e97 Release 1.108.0 (#653) 2022-01-27 21:33:59 +01:00
65e151151b Feature/increase fear and greed index to 90 days (#652)
* Increase fear and greed index to 90 days

* Update changelog
2022-01-27 21:01:38 +01:00
5d3bbb8f30 Feature/improve annualized performance (#651)
* Improve annualized performance calculation

* Update changelog
2022-01-27 20:56:20 +01:00
b464fefc57 Release 1.107.0 (#650) 2022-01-24 21:43:37 +01:00
bcb7f5f522 Feature/add feature toggle for new calculation engine (#649)
* Add feature toggle for new calculation engine

* Update changelog
2022-01-24 21:38:59 +01:00
f15b33e950 Portfolio calculator rework (#632)
* Portfolio calculator rework

Co-authored-by: Reto Kaul <retokaul@sublimd.com>
2022-01-24 20:35:13 +01:00
ca64492e77 Bugfix/fix styling in activities table footer (#648)
* Fix styling

* Update changelog
2022-01-24 20:12:54 +01:00
761376d72d Release 1.106.0 (#647) 2022-01-23 17:41:54 +01:00
9c086edffe Feature/extend historical data view in admin control (#646)
* Extend market data view

* Update changelog
2022-01-23 17:02:12 +01:00
585f99e4df Feature/add summary row to activities table (#645)
* Add summary row to activities table

* Update changelog
2022-01-23 11:39:30 +01:00
9d907b5eb5 Bugfix/improve the redirection on logout (#642)
* Improve logout

* Update changelog
2022-01-22 09:38:01 +01:00
ba05f5ba30 Feature/upgrade prisma to version 3.8.1 (#640)
* Upgrade prisma to version 3.8.1

* Update changelog
2022-01-22 09:36:58 +01:00
3261e3ee59 Feature/upgrade stripe dependencies (#641)
* Upgrade stripe dependencies

* Update changelog
2022-01-21 20:30:41 +01:00
5607c6bb52 Update blog url (#639) 2022-01-21 20:07:56 +01:00
1c6050d3e3 Release 1.105.0 (#638) 2022-01-20 21:35:56 +01:00
38f2930ec6 Feature/improve data provider service (#637)
* Improve data provider service

* Update changelog
2022-01-20 21:34:23 +01:00
556be61fff Bugfix/fix unresolved account names in reports (#636)
* Fix unresolved account names

* Update changelog
2022-01-19 21:28:15 +01:00
651b4bcff7 Release 1.104.0 (#631) 2022-01-16 15:45:28 +01:00
0a8d159f78 Bugfix/fix missing symbol profile data connection in import (#630)
* Fix missing symbol profile data connection in import

* Update changelog
2022-01-16 15:31:56 +01:00
1a4109ebaa Bugfix/fix fallback to load currencies directly from data provider (#629)
* Fix fallback

* Update changelog
2022-01-16 13:46:00 +01:00
92e502e1c2 Release 1.103.0 (#628) 2022-01-13 20:33:31 +01:00
e344c43a5a Bugfix/fix currency of value in position detail dialog (#627)
* Fix currency

* Update changelog
2022-01-13 20:25:21 +01:00
d6b78f3457 Feature/add links to statistics section (#626)
* Add links and clean up style

* Update changelog
2022-01-13 19:07:23 +01:00
9bbb856f66 Release 1.102.0 (#625) 2022-01-11 19:53:23 +01:00
d3707bbb87 Bugfix/fix preselected default account in create activity dialog (#624)
* Fix preselected default account

* Update changelog
2022-01-11 19:50:22 +01:00
7df53896f3 Feature/start eliminating data source from order (#622)
* Start eliminating dataSource from order

* Update changelog
2022-01-11 19:49:45 +01:00
b2b3fde80e Bugfix/support multiple accounts with the same name (#623)
* Support multiple accounts with the same name

* Update changelog
2022-01-10 21:23:47 +01:00
a83441b3ba Release 1.101.0 (#621) 2022-01-08 18:21:33 +01:00
075431d868 Feature/add google sheets as data source (#620)
* Add google sheets as data source

* Update changelog
2022-01-08 18:19:25 +01:00
0168c1c4e8 Feature/exclude url pattern of shared portfolios in robots.txt (#619)
* Exclude shared portfolios

* Update changelog
2022-01-08 09:37:54 +01:00
07de8f87fc Set market prices explicitly (#618)
* Set market prices explicitly

* Set comments explicitly
2022-01-07 08:09:12 +01:00
3e16041c16 Release 1.100.0 (#617) 2022-01-05 20:24:34 +01:00
5882b7914d Feature/add first months in open source blog post (#616)
* Add blog post

* Update changelog
2022-01-05 20:22:59 +01:00
69c9e259b1 Bugfix/fix routing of create activity dialog (#615)
* Fix routing of create activity dialog

* Update changelog
2022-01-03 21:31:55 +01:00
aca37a27f9 Feature/add top performers to analysis page (#613)
* Add Top 3 / Bottom 3 performers

* Update changelog
2022-01-02 13:29:45 +01:00
313d2a2f79 Release 1.99.0 (#612) 2022-01-01 16:40:55 +01:00
9ac67b0af2 Feature/expose profile data gathering by symbol endpoint (#611)
* Expose profile data gathering by symbol endpoint

* Update changelog
2022-01-01 16:18:18 +01:00
1e526852a7 Bugfix/fix mapping for russia in trackinsight data enhancer (#610)
* Fix mapping for Russia

* Update changelog
2022-01-01 13:55:53 +01:00
e54638a684 Feature/improve analysis page (#609)
* Improve analysis page (show y-axis, extend chart in relation to days in market)

* Update changelog
2022-01-01 12:09:49 +01:00
0179823ad9 Feature/restructure about page (#608)
* Restructure about page: introduce pages for blog and changelog

* Update changelog
2022-01-01 10:10:37 +01:00
029b7bed9a Bugfix/improve error handling in position api endpoint (#607)
* Add guards

* Update changelog
2021-12-31 22:00:58 +01:00
635f10e2d0 Bugfix/hide data provider warning while loading (#605)
* Hide data provider warning while loading

* Update changelog
2021-12-31 10:21:41 +01:00
cebf879d67 Feature/refactor demo user (#604)
* Refactor demo user id

* Update changelog
2021-12-31 09:52:03 +01:00
124bdc028d Bugfix/fix reload of position detail dialog (#603)
* Fix reload of position detail dialog

* Update changelog
2021-12-31 09:51:30 +01:00
d69a69ce18 Bugfix/fix exception with market state (#602)
* Fix exception with market state

* Update changelog
2021-12-30 22:19:08 +01:00
15344513ce Feature/upgrade chart.js to version 3.7.0 (#601)
* Upgrade chart.js from version 3.5.0 to 3.7.0

* Update changelog
2021-12-30 22:18:39 +01:00
b291d9e031 Feature/refactor transactions to activities table (#600)
* Refactor transactions to activities table with attributes and routes

* Update changelog
2021-12-30 18:56:51 +01:00
bee702302f Upgrade angular and Nx dependencies (#599) 2021-12-30 17:31:07 +01:00
bb56e09a13 Clean up preview feature flag (#598) 2021-12-30 10:15:30 +01:00
0873f539c5 Release 1.98.0 (#597) 2021-12-29 18:40:18 +01:00
6dcd801d05 Feature/extend statistics section with users in slack community (#596)
* Extend statistics with users in Slack community

* Update changelog
2021-12-29 18:38:55 +01:00
77065dac50 Feature/add date range selector to holdings tab (#595)
* Add date range selector to holdings tab

* Update changelog
2021-12-29 18:14:24 +01:00
438484879d Bugfix/fix creation of historical data (#594)
* Fix creation of historical data (upsert instead of update)

* Update changelog
2021-12-29 17:03:37 +01:00
e37a650c70 Bugfix/fix scrolling issue in position detail dialog on mobile (#593)
* Fix scrolling in position detail dialog on mobile

* Update changelog
2021-12-29 10:51:11 +01:00
6e8c90b3fc Release 1.97.0 (#592) 2021-12-28 21:40:10 +01:00
9e1a7fc981 Feature/dividend (#547)
* Add dividend to order type

* Support dividend in transactions table

* Support dividend in transaction dialog

* Extend import file with dividend

* Add dividend to portfolio summary

* Update changelog

Co-authored-by: Fly Man <fly.man.opensim@gmail.com>
2021-12-28 21:12:12 +01:00
ff638adf03 Feature/add transactions to position detail dialog (#591)
* Add transactions to position detail dialog

* Update changelog
2021-12-28 17:45:04 +01:00
fa44cee781 Release 1.96.0 (#590) 2021-12-27 21:08:33 +01:00
db1d474ddf Feature/more discreet data provider warning (#589)
* Upgrade http-status-codes to version 2.2.0

* Make the data provider warning more discreet

* Update changelog
2021-12-27 12:14:41 +01:00
994275e093 Feature/upgrade angular 3rd party dependencies (#588)
* Upgrade angular 3rd party dependencies
  * ngx-device-detector
  * ngx-markdown
  * ngx-stripe

* Update changelog
2021-12-26 21:58:56 +01:00
ee397c8047 Bugfix/fix file type detection for import (#587)
* Fix file type detection ("application/vnd.ms-excel" instead of "text/csv")

* Update changelog
2021-12-26 20:54:53 +01:00
7203939c42 Feature/upgrade prisma to version 3.7.0 (#586)
* Upgrade prisma from version 3.6.0 to 3.7.0

* Update changelog
2021-12-26 17:30:26 +01:00
9725f16c81 Clean up schema.prisma (#584) 2021-12-26 14:33:18 +01:00
bb8b1e4f43 Release 1.95.0 (#583) 2021-12-26 10:14:13 +01:00
9d3610331a Add guard (#582) 2021-12-26 10:07:51 +01:00
0043b44670 Feature/improve data gathering for currencies (#581)
* Improve data gathering for currencies, add warning if it fails

* Update changelog
2021-12-26 09:15:10 +01:00
bbc4e64cb4 Bugfix/filter currencies with null value (#579)
* Filter currencies with null value

* Update changelog
2021-12-25 17:08:56 +01:00
c7f4825499 Release 1.94.0 (#578) 2021-12-25 14:45:58 +01:00
8f583709ef Feature/add support for cosmos and polkadot (#577)
* Add support for cryptocurrencies ATOM and DOT

* Update changelog
2021-12-25 14:23:07 +01:00
4c30212a72 Feature/improve data gathering (#576)
* Eliminate benchmarks to gather

* Optimize 7d data gathering

* Update changelog
2021-12-25 14:18:46 +01:00
cade2f6a5e Feature/upgrade prettier to version 2.5.1 (#575)
* Upgrade prettier from version 2.3.2 to 2.5.1

* Update changelog
2021-12-25 10:29:56 +01:00
3b9a8fabb5 Clean up (#574) 2021-12-24 18:42:30 +01:00
3435b3a348 Feature/make the csv import more flexible (#573)
* Make the csv import more flexible

* Update changelog
2021-12-24 18:21:27 +01:00
5d39b267ab write portfolio calculator test case for symbol BALN.SW (refs #554) (#572) 2021-12-24 17:52:08 +01:00
ffaaa14dba Feature/increase fear and greed index to 30 days (#571)
* Increase Fear & Greed index to 30 days

* Update changelog
2021-12-24 09:40:24 +01:00
c65746d119 Simplify instructions for development setup (#570) 2021-12-22 18:09:00 +01:00
1a6840f1f6 Fix instruction for database setup (#568) 2021-12-21 20:59:01 +01:00
fb7fb886f6 Fix anchor link (#567) 2021-12-21 20:53:15 +01:00
127abb8f4e Release 1.93.0 (#566) 2021-12-21 20:24:24 +01:00
ed1136999a Feature/extend documentation for self hosting (#565)
* Extend documentation for self-hosting

* Add tag "latest" to docker image

* Update changelog
2021-12-21 20:22:58 +01:00
9f545e3e2b Feature/add cryptocurrency solana (#563)
* Add support for Solana and clean up symbol conversion (SOL1USD)

* Update changelog
2021-12-20 21:24:58 +01:00
1602f976f0 Feature/convert errors to warnings in portfolio calculator (#562)
* Convert errors to warnings

* Update changelog
2021-12-20 21:03:12 +01:00
4bf4c1a8a3 Release 1.92.0 (#561) 2021-12-19 16:57:22 +01:00
e78755c280 Replace OrderType with Type (prisma) (#560) 2021-12-19 16:52:35 +01:00
7772684413 Bugfix/fix permission for system status page (#559)
* Fix permission

* Update changelog
2021-12-19 15:55:03 +01:00
955302666e Bugfix/improve redirection on logout (#558)
* Improve redirection on logout

* Update changelog
2021-12-19 13:45:28 +01:00
ddce8cc7f9 Feature/support update of historical data (#557)
* Support update of historical data

* Update changelog
2021-12-19 10:57:50 +01:00
aca0d77e91 Feature/add line chart to historical data view (#555)
* Add line chart

* Update changelog
2021-12-18 16:43:34 +01:00
8b9379f5ce Release 1.91.0 (#553) 2021-12-18 11:41:13 +01:00
0806d0dc92 Bugfix/fix hidden statistics values (#552)
* Fix hidden values (0)

* Update changelog
2021-12-18 11:13:32 +01:00
e518bc3779 Feature/clean up all time high and low from performance endpoint (#551)
* Remove isAllTimeHigh / isAllTimeLow

* Update changelog
2021-12-18 09:20:42 +01:00
eff807dd9a Add tests for yahoo Finance symbol conversion (#550)
* Add tests for yahoo Finance symbol conversion

* Refactoring
2021-12-18 09:04:52 +01:00
155bf67f60 Bugfix/fix symbol conversion from yahoo finance with hyphen (#549)
* Fix symbol conversion from Yahoo Finance

* Update changelog
2021-12-16 18:44:08 +01:00
9aefe3747e Feature/migrate database schema to prisma 3 (#546)
* Add migration after upgrade to prisma 3

* Update changelog
2021-12-15 21:56:29 +01:00
0878febded build(prisma): fix populating database when attributes have null value (#545) 2021-12-15 21:04:26 +01:00
f953c6ea64 Release/1.90.0 (#544)
* Update stage labels

* Release 1.90.0
2021-12-14 20:04:17 +01:00
76dbf78279 implement docker build on tags (#542)
Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>

Co-authored-by: Valentin Zickner <github@zickner.ch>
2021-12-14 19:45:55 +01:00
f12866b9ec Update changelog (#543) 2021-12-13 22:31:49 +01:00
83ba5f3d9f Feature/add intro image for dark mode (#540)
* Add intro image for dark mode

* Update changelog
2021-12-13 22:26:16 +01:00
7439c1bf54 fix(custom-cryptocurrencies): add uniswap (uni3) (#541) 2021-12-13 22:25:09 +01:00
e255b76053 Feature/check currency in import validation (#538)
* Check currency in import validation (avoid importing transactions in wrong currency)

* Update changelog
2021-12-13 19:45:23 +01:00
ed7209fb53 Feature/remove transactions import limit (#536)
* Remove default transactions import limit

* Update changelog
2021-12-11 21:44:54 +01:00
008a2ab123 Support arm64 (#465)
* Support arm64

* Move prisma from devDependencies to dependencies (Fix "/bin/sh: prisma: not found" in docker build)

* Update changelog

Co-authored-by: Valentin Zickner <ghostfolio@zickner.ch>
2021-12-11 20:58:09 +01:00
2aedd74480 Release 1.89.0 (#534) 2021-12-11 17:23:29 +01:00
11076592d1 Do not log coupon code (#533) 2021-12-11 17:12:24 +01:00
ebee851b23 Feature/support data gathering by symbol and date (#532)
* Support data gathering by symbol and date

* Update changelog
2021-12-11 11:45:13 +01:00
7d3f1832b4 Feature/upgrade nx and storybook dependencies (#531)
* Upgrade dependencies
  * nx
  * storybook

* Update changelog
2021-12-10 19:50:46 +01:00
39e6abfc8c Clean up preview features (#530) 2021-12-10 19:43:53 +01:00
78e0fdb0ca Add coupon system (#529)
* Add coupon system

* Update changelog
2021-12-09 21:14:44 +01:00
606350b2ff Release 1.87.0 (#526) 2021-12-07 20:34:52 +01:00
d09cad4e05 Feature/read only mode (#520)
* Setup read only mode and update permissions dynamically

* Update changelog
2021-12-07 20:24:15 +01:00
069660afe4 Feature/increase fear and greed index to 10 days (#525)
* Increase to 10 days

* Update changelog
2021-12-07 19:10:40 +01:00
4d9a223491 Feature/add system message (#519)
* Add system message

* Update changelog
2021-12-06 20:51:38 +01:00
aed8f5cf04 Feature/upgrade prisma to version 3.6.0 (#518)
* Upgrade prisma from version 2.30.2 to 3.6.0

* Update changelog
2021-12-05 16:52:24 +01:00
1beb4de62f Feature/support additional currencies (#517)
* Support additional currencies

* Update changelog
2021-12-04 21:05:11 +01:00
9bc3505ded Release 1.86.0 (#516) 2021-12-04 11:51:06 +01:00
3e82de6b21 Feature/add historical data chart of fear and greed index (#515)
* Add historical data chart of market mood

* Update changelog
2021-12-04 11:49:00 +01:00
563f354e7e Feature/symbol to uppercase to avoid duplicates (#514)
* Convert the symbol to uppercase to avoid case-sensitive duplicates

* Update changelog
2021-12-04 11:40:12 +01:00
e96e6c717c Feature/enable import by default (#513)
* Enable import by default

* Update changelog
2021-12-04 08:57:22 +01:00
961774ce9f Feature/improve market data detail (#511)
* Improve historical data view (hide invalid and future dates)

* Update changelog
2021-12-03 21:24:05 +01:00
49f46e1a1e Bugfix/improve allocations by currency with cash balances (#508)
* Improve allocations by currency in combination with cash balances

* Update changelog
2021-12-02 21:53:38 +01:00
050c0a4da7 Release 1.85.0 (#506) 2021-12-01 21:20:33 +01:00
4908e6d35d Bugfix/fix data gathering of fear and greed index (#505)
* Fix data gathering of fear and greed index

* Update changelog
2021-12-01 21:18:46 +01:00
fe4013830d Release 1.84.0 (#504) 2021-11-30 21:07:17 +01:00
11be6f630f Feature/expose data gathering by symbol (#503)
* Expose data gathering by symbol as endpoint

* Update changelog
2021-11-30 21:06:10 +01:00
85d123e1b1 Fix colspan (#502) 2021-11-29 21:39:54 +01:00
c5e9804c25 Release 1.83.0 (#501) 2021-11-29 21:17:20 +01:00
1f042ee791 Feature/eliminate redundant storage of historical exchange rates (#500)
* Eliminate redundant storage of historical exchange rates

* Clean up experimental API

* Update changelog
2021-11-29 21:08:58 +01:00
da6eaa0d77 Harmonize error log (#499) 2021-11-29 21:01:53 +01:00
3f31cec859 Release 1.82.0 (#498) 2021-11-28 19:58:00 +01:00
6c07759eb7 Feature/add market data tab to admin control panel (#497)
* Add market data tab

* Update changelog
2021-11-28 19:46:34 +01:00
fcf07a0fd1 Clean up (#496) 2021-11-28 13:26:26 +01:00
2f402c0c8e Feature/introduce tabs with routing to home page (#495)
* Introduce tabs with routing

* Update changelog
2021-11-28 12:52:37 +01:00
a24a094407 Feature/introduce tabs to admin control panel (#494)
* Add tabs

* Update changelog
2021-11-28 12:34:10 +01:00
dc9b2ce194 Release 1.81.0 (#493) 2021-11-27 20:57:32 +01:00
72067459d6 Feature/add value to position detail dialog (#492)
* Add value to position detail dialog

* Update changelog
2021-11-27 09:51:08 +01:00
705441ecf8 Bugfix/fix line chart labels (#491)
* Fix line chart labels

* Fix click event for drafts

* Update changelog
2021-11-26 20:41:44 +01:00
fbd1475402 Feature/upgrade core dependencies (#490)
* Upgrade core dependencies

  * angular
  * nestjs
  * Nx
  * rxjs
  * storybook

* Temporarily fix imports for storybook

* Update changelog
2021-11-25 18:05:02 +01:00
4dc4f13f40 fix storybook after currency changed to string (#488) 2021-11-23 20:58:41 +01:00
3b857aa8bb Release 1.80.0 (#487) 2021-11-23 20:28:50 +01:00
1c2ca5b96b Feature/accentuate all time high and low (#428)
* Accentuate all time high and all time low

* Update changelog

Co-authored-by: Valentin Zickner <ghostfolio@zickner.ch>
2021-11-22 21:28:32 +01:00
572bfc59b8 Add guards (#486) 2021-11-22 20:35:25 +01:00
147f0162b7 Release 1.79.0 (#485) 2021-11-21 18:04:28 +01:00
f6acf5207b Feature/add value to positions table (#484)
* Add value column

* Update changelog
2021-11-21 17:55:58 +01:00
80782f1098 add support for euro cryptocurrencies, ALGO and remove unknown crypto… (#480)
* add support for euro cryptocurrencies, ALGO and remove unknown cryptocurrencies from list

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-11-21 17:38:48 +01:00
bc58ee86ca Feature/usability improvements in the create or edit transaction dialog (#483)
* Usability improvements
  * Disable the symbol input in edit mode
  * Filter accounts by type (SECURITIES)

* Update changelog
2021-11-20 20:41:33 +01:00
0cb632b165 Improve wording (#482) 2021-11-20 10:38:30 +01:00
fca3a659d0 Release 1.78.0 (#481) 2021-11-20 10:31:45 +01:00
904dec040e Feature/add testimonial (#479)
* Add testimonials

* Update changelog
2021-11-20 10:28:05 +01:00
fc6c81fe02 Bugfix/fix footer row border in dark mode (#478)
* Fix border color in dark mode

* Update changelog
2021-11-17 23:23:32 +01:00
634171e4e3 Release 1.77.0 (#476) 2021-11-16 21:58:41 +01:00
f8f36e4f4e Bugfix/fix accounts table footer on mobile (#475)
* Fix footer on mobile

* Update changelog
2021-11-16 21:32:04 +01:00
5e7cf9d0b6 Feature/hide get started button on registration page (#474)
* Hide button

* Update changelog
2021-11-15 20:49:03 +01:00
e1932eb5a1 Bugfix/exclude drafts from transaction count (#473)
* Fix transactions count (exclude drafts)

* Improve wording (summary page)

* Update changelog
2021-11-14 19:06:54 +01:00
dba47d59e3 Release 1.76.0 (#472) 2021-11-14 17:16:06 +01:00
3032126508 Feature/add footer row to accounts table (#471)
* Add footer row to accounts table with total balance and value

* Update changelog
2021-11-14 17:04:52 +01:00
a50b55da75 Clean up (#470) 2021-11-14 16:57:46 +01:00
5422df05b3 Release 1.75.0 (#469) 2021-11-13 20:50:02 +01:00
d2fabe7ce4 Feature/add value column to accounts table (#468)
* Add value column

* Update changelog
2021-11-13 20:38:29 +01:00
a42700b9fe Feature/introduce data gathering progress (#467)
* Add data gathering progress

* Update changelog
2021-11-13 11:32:28 +01:00
9df8541145 Feature/log logo on server start (#466)
* Log logo on server start

* Update changelog
2021-11-12 22:50:40 +01:00
0b2252755c Release 1.74.0 (#464) 2021-11-11 21:52:57 +01:00
239bd09cbd Feature/move market mood to tab (#463)
* Move market mood to tab

* Update changelog
2021-11-11 21:43:17 +01:00
cd76f89902 Feature/increase decimal places for cryptocurrencies (#462)
* Calculate quantity precision

* Update changelog
2021-11-11 21:21:37 +01:00
7425ba94f1 Release 1.73.0 (#461) 2021-11-10 21:16:06 +01:00
b9522307c4 Feature/various client improvements (#460)
* Various improvements
  * info messages
  * skeleton loader of portfolio holdings

* Update changelog
2021-11-10 21:03:25 +01:00
e01e039a00 Release 1.72.0 (#459) 2021-11-08 20:55:38 +01:00
6d3513c17f Update version 2021-11-07 21:27:37 +01:00
d60b444324 Release/1.71.0 (#458)
* Refactor logger

* Fix exception

* Update changelog
2021-11-07 21:25:18 +01:00
2873130259 Release 1.70.0 (#457) 2021-11-07 18:38:29 +01:00
d999a27159 Feature/move scraper configuration to symbol profile (#456)
* Move scraper configuration

* Update changelog
2021-11-07 18:36:28 +01:00
b6902e10ea Feature/improve import json file validation (#455)
* Improve import validation

* Update changelog
2021-11-07 17:16:19 +01:00
7f3f75386d Release 1.69.0 (#454) 2021-11-07 09:45:19 +01:00
678544748a Add symbol mapping (#452)
* Add symbol mapping

* Update changelog
2021-11-07 09:42:36 +01:00
632f3e3872 Add ok.csv (#453) 2021-11-06 21:09:06 +01:00
87301ddbd5 Feature/improve registration page (#451)
* Improve registration page

* Update changelog
2021-11-02 21:49:57 +01:00
7d03c373ac Release 1.68.0 (#450) 2021-11-01 21:27:23 +01:00
edb66bb166 Feature/extend statistics (#449)
* Extend statistics

* Update changelog
2021-11-01 21:15:09 +01:00
54bbc8446b Feature/prettify scraper symbol in chart (#448)
* Prettify scraper symbol in chart

* Update changelog
2021-11-01 20:29:16 +01:00
9933967e42 Release 1.67.0 (#447) 2021-11-01 19:33:45 +01:00
5618513d07 Update changelog (#446) 2021-10-31 17:02:41 +01:00
1397cd62a8 Feature/extend public page (#445)
* Extend public page

* Update changelog
2021-10-31 17:00:59 +01:00
e7fb31d1a6 add Dockerfile with all in one docker image (#431)
* Add Dockerfile with all in one docker image

* Change to alpine image and reduce node_modules size

* Improve documentation and fix changelog and license

* Update changelog

Co-authored-by: Valentin Zickner <ghostfolio@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-10-30 18:54:14 +02:00
51e7b94ad0 Release 1.66.0 (#443) 2021-10-30 11:22:50 +02:00
9133ea38f3 Feature/order accesses (#441)
* Order accesses

* Update changelog
2021-10-30 10:49:17 +02:00
a864c617b9 Feature/improve landing page (#442)
* Add intro screen

* Update changelog
2021-10-30 10:46:54 +02:00
442df9d6f8 Release 1.65.0 (#440) 2021-10-25 21:03:36 +02:00
2de0e75cb8 Feature/add user interface for granting and revoking public access (#439)
* Add user interface for granting and revoking public access

* Update changelog
2021-10-25 20:46:36 +02:00
1296f95602 Feature/move data enhancer from data provider to data gathering (#437)
* Move data enhancers from data provider to data enhancer module

* Update changelog
2021-10-24 10:49:17 +02:00
e98dff877a Add link to Visual Studio Code (#436) 2021-10-22 20:20:59 +02:00
964b37af30 Add Visual Studio Code link (#435) 2021-10-22 20:17:33 +02:00
2d89d12d31 Release 1.64.0 (#434) 2021-10-21 22:25:58 +02:00
19e2d54791 Refactor custom cryptocurrencies (#433) 2021-10-21 22:11:12 +02:00
e24e5e1c44 Add custom crypto currencies (#432)
* Add custom cryptocurrencies
2021-10-21 21:20:01 +02:00
a0d4ff7920 Clean up ts-node script (#430)
Co-Authored-By: Davin <52686922+Frozies@users.noreply.github.com>
2021-10-20 22:46:58 +02:00
099ad18aaf refactor data-provider.service.ts to have dynamic list of services (#429)
* Refactor data-provider.service.ts to have dynamic list of services

* Update changelog

Co-authored-by: Valentin Zickner <ghostfolio@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-10-20 21:41:40 +02:00
e3cd99f5d2 Release 1.63.0 (#427) 2021-10-19 18:49:11 +02:00
6dea9093ba Feature/add public portfolio (#426)
* Setup public portfolio page

* Update changelog
2021-10-19 18:27:50 +02:00
43104f81d0 Add test data (#424) 2021-10-17 11:56:35 +02:00
6d2e3b6e40 Feature/improve skeleton loader size (#425)
* Improve skeleton loader size

* Update changelog
2021-10-17 11:53:27 +02:00
1d9a31dbb8 Improve styling (#423) 2021-10-17 11:42:27 +02:00
ea0e92220c Release 1.62.0 (#422) 2021-10-17 10:52:08 +02:00
b57301ef50 Feature/extend import validation message (#421)
* Extend import validation message

* Update changelog
2021-10-16 21:23:15 +02:00
67dbc6b014 Release 1.61.0 (#420) 2021-10-15 22:30:44 +02:00
2e5176bacf Feature/extend import by csv files (#419)
* Support import of csv files

* Update changelog
2021-10-15 22:22:45 +02:00
060846023f Release 1.60.0 (#418) 2021-10-13 12:10:12 +02:00
f06a0fbbee Feature/validate duplicate orders for import (#416)
* Validate duplicate orders

* Update changelog
2021-10-13 11:52:04 +02:00
4ab6a1a071 Feature/harmonize page layouts (#417)
* Harmonize page layouts

* Update changelog
2021-10-13 11:51:33 +02:00
93dcbeb6c7 Feature/add validation for import (#415)
* Valid data types
* Maximum number of orders
* Data provider service returns data for the dataSource / symbol pair
2021-10-12 22:19:32 +02:00
b9f0a57522 Bugfix/unregister chartjs plugin datalabels (#414)
* Unregister chartjs-plugin-datalabels

* Update changelog
2021-10-12 09:23:37 +02:00
174c1d1a62 Release 1.59.0 (#413) 2021-10-11 20:35:06 +02:00
f308ae7a13 add sectors and countries for ETFs (#410)
* Update changelog

Co-Authored-By: Valentin Zickner <valentin@coderworks.de>

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
2021-10-11 19:32:21 +02:00
a7a6b0608b Add ghostfolio-docker (#411)
Co-Authored-By: psychowood <115389+psychowood@users.noreply.github.com>
2021-10-09 10:45:58 +02:00
15a61b7a20 Feature/improve values of global heat map (#408)
* Convert value

* Update changelog
2021-10-04 21:22:42 +02:00
d1eedf9726 Bugfix/various fixes (#407)
* Fix links

* Update column

* Fix impersonation mode

* Update changelog
2021-10-03 22:04:23 +02:00
30a592b524 Release 1.58.1 (#406) 2021-10-03 10:50:01 +02:00
de94494aa0 Bugfix/fix symbol conversion for yahoo finance (#405)
* Fix symbol conversion for Yahoo Finance

* Update changelog
2021-10-03 10:40:21 +02:00
d3c6788ad5 Release 1.58.0 (#404) 2021-10-02 20:45:02 +02:00
3ec4a73b35 Feature/improve tooltips (#403)
* Improve tooltips

* Update changelog
2021-10-02 20:38:41 +02:00
1050bfa098 Feature/improve yahoo finance symbol conversion (#402)
* Improve symbol conversion

* Update changelog
2021-10-02 10:28:06 +02:00
595ec1d7b4 Feature/upgrade envalid to version 7.2.1 (#401)
* Upgrade envalid

* Update changelog
2021-09-30 21:54:58 +02:00
c8389599b6 Release 1.57.0 (#400) 2021-09-29 21:34:50 +02:00
8769fe4c90 Improve styling (#399) 2021-09-29 21:10:04 +02:00
4219e1121e Improve style (#398) 2021-09-29 21:05:01 +02:00
f558eb8de8 Fix template (#397) 2021-09-29 21:04:41 +02:00
fe2bd6eea8 Feature/protect endpoints (#396)
* Protect endpoints

* Update changelog
2021-09-28 21:37:01 +02:00
035052be99 Feature/improve exchange rates table (#394)
* Improve exchange rates table

* Update changelog
2021-09-26 20:57:37 +02:00
bcdd2780b3 Release 1.56.0 (#393) 2021-09-25 18:02:02 +02:00
22d1ed7920 Extend data (#392)
* assetClass
* assetSubClass
* currency
* name
2021-09-25 17:42:45 +02:00
39d9828f9f Feature/respect account currency in exchange rate data service (#391)
* Respect the accounts' currencies

* Update changelog
2021-09-25 16:45:21 +02:00
6333aa972d Bugfix/fix data gathering after seed (#390)
* Fix data gathering after seed

* Update changelog
2021-09-25 16:44:24 +02:00
554f2f861f Upgrade @types (#389) 2021-09-25 09:20:47 +02:00
dcee651098 Feature/support unlimited currencies (#387)
* Support unlimited currencies

* Update changelog
2021-09-24 21:09:48 +02:00
508a48f4c3 Feature/hide actions in presenter view (#380)
* Hide actions if restricted view is active

* Update changelog
2021-09-24 20:15:10 +02:00
8466e3d73f Feature/always show adapted portfolio page (#388)
* Always show adapted portfolio page

* Update changelog
2021-09-24 20:12:17 +02:00
9ae9904389 Feature/add story for line chart component (#385)
* Add story for line chart component

* Update changelog
2021-09-20 21:44:47 +02:00
af022ae316 Feature/add story for portfolio proportion chart component (#384)
* Add story

* Use new component

* Update changelog
2021-09-20 20:45:58 +02:00
5cd6edaf3a Release 1.55.0 (#383) 2021-09-20 11:58:48 +02:00
98be8745d9 Bugfix/fix create or edit transaction dialog (#382)
* Fix create or edit transaction dialog

* Update changelog
2021-09-20 11:49:50 +02:00
861dff9210 Feature/upgrade storybook dependencies (#381)
* Upgrade storybook dependencies
2021-09-19 18:01:41 +02:00
f2364eed10 Feature/remove default value of data source (#379)
* Remove default value of dataSource

* Update changelog
2021-09-19 17:16:29 +02:00
d5392de7c9 Release 1.54.0 (#378) 2021-09-18 20:21:43 +02:00
0f72673ef4 Feature/respect data source in symbol data endpoint (#370)
* Respect data source in symbol data endpoint

* Respect data source in the data provider service

* Combine symbol with data source in get() of data provider service

* Improve search functionality for multiple data sources

* Update changelog
2021-09-18 19:32:22 +02:00
641fe4e8f4 Bugfix/net performance in positions endpoint (#377)
* Nullify net performance

* Update changelog
2021-09-18 19:20:14 +02:00
18e06bb6e6 Feature/hide sign if performance is zero (#376)
* Hide sign if performance is zero

* Update changelog
2021-09-18 13:36:49 +02:00
5b588c2000 Bugfix/hide the current net performance (#373)
* Hide the current net performance

* Update changelog
2021-09-15 22:27:18 +02:00
162d19fa44 Release 1.53.0 (#369) 2021-09-13 21:37:18 +02:00
4a815d2031 Feature/change data gathering selection (#368)
* Change data gathering selection from distinct orders to symbol profiles

* Update changelog
2021-09-13 21:26:23 +02:00
d2aeeb3e88 optimize annual performance calculation (#367)
* Optimize annual performance calculation

* Update changelog

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-09-12 20:08:42 +02:00
ba926ffcf2 Release 1.52.0 (#366) 2021-09-11 21:36:22 +02:00
5ea455b98b Feature/upgrade simplewebauthn dependencies to version 4.1.0 (#365)
* Upgrade @simplewebauthn dependencies to version 4.1.0
  * @simplewebauthn/browser
  * @simplewebauthn/server

* Update changelog
2021-09-11 21:23:06 +02:00
39f315aba0 Feature/add annualized performance (#364)
* Add annualized performance

* Update changelog
2021-09-11 20:16:53 +02:00
df2dfc20a1 Feature/add slack channel (#363)
* Add Slack channel

* Update changelog
2021-09-11 12:06:28 +02:00
81e83d4cea Release 1.51.0 (#362) 2021-09-11 11:25:07 +02:00
5d4156ecec Feature/refactor position detail dialog (#355)
* Add name to portfolio position endpoint

* Update changelog
2021-09-11 11:23:47 +02:00
4693a8baa2 Release 1.50.0 (#361) 2021-09-11 11:21:53 +02:00
773444b1e2 Bugfix/fix home button overlap on ios (#360)
* Fix overlap

* Update changelog
2021-09-11 11:17:49 +02:00
3c46bde8d5 Bugfix/fix fear and greed index (#359)
* Fix fear and greed index
* Refactor fear and greed index symbol
   * GF.FEAR_AND_GREED_INDEX -> _GF_FEAR_AND_GREED_INDEX

* Update changelog
2021-09-11 11:14:55 +02:00
63ee33b685 Use 'import type' to import types, eliminate webpack warnings (#358) 2021-09-11 09:27:22 +02:00
bc87c0a3e1 Add slack (#357) 2021-09-10 18:11:35 +02:00
caa9fc3efa Release 1.49.0 (#354) 2021-09-08 22:19:53 +02:00
9ed82ac82b Feature/improve labels of allocation chart by symbol (#353)
* Improve labels

* Update changelog
2021-09-08 22:03:33 +02:00
9c9ca4ab1e Add labels to allocation piecharts (#337) 2021-09-08 21:31:34 +02:00
b0b0942162 Release 1.48.0 (#352)
* Nullify netPerformance

* Introduce precision

* Update changelog
2021-09-07 22:23:07 +02:00
9cbf789c22 Bugfix/fix values in position detail dialog (#351)
* Nullify netPerformance

* Introduce precision

* Update changelog
2021-09-07 22:11:38 +02:00
ee5ab05d8a Release 1.47.1 (#350) 2021-09-06 22:55:08 +02:00
20731c67cb Release 1.47.0 (#349) 2021-09-06 22:34:17 +02:00
bf8856ad19 Bugfix/fix search for cryptocurrencies (#348)
* Fix the search for cryptocurrency symbols

* Update changelog
2021-09-06 22:02:49 +02:00
a31d79821d Release 1.46.0 (#347) 2021-09-05 22:15:21 +02:00
48ab862bb6 net performance for current positions (#330)
* implement fees for transaction points #324

* add net performance to current positions #324

* add net performance to calculate timeline #324

* make timeline fee accumulated by default #324

* Update changelog

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-09-05 21:21:22 +02:00
ba234a470e Feature/add storybook story for trend indicator component (#346)
* Add storybook story for trend indicator component

* Update changelog
2021-09-05 08:49:06 +02:00
ccae660104 Feature/add storybook story for no transactions info component (#345)
* Add storybook story for no transactions info component

* Update changelog
2021-09-05 08:34:02 +02:00
21ed91d184 Feature/add storybook story for logo component (#344)
* Add storybook story for logo component

* Update changelog
2021-09-05 08:03:32 +02:00
5fd413e57e Feature/setup storybook (#332)
* Setup ui library with storybook

* Add value component with story

* Update changelog
2021-09-04 22:12:54 +02:00
4c194c938a Feature/add contributors count to statistics (#342)
* Add contributors count to statistics

* Update changelog
2021-09-04 19:46:24 +02:00
a4d049e53d Release 1.45.0 (#340) 2021-09-04 19:20:09 +02:00
f9c4408126 Update yarn start:server to watch for changes (#338) 2021-09-04 19:05:04 +02:00
d046f1d498 Feature/upgrade nx to version 12.8.0 (#331)
* Upgrade angular and Nx

* Update changelog
2021-09-04 11:25:40 +02:00
ad96d6e53e Feature/upgrade prisma from version 2.24.1 to 2.30.2 (#325)
* Upgrade prisma

* Update changelog
2021-09-04 10:49:09 +02:00
747e5b63fa Feature/restructure allocations page including allocations by symbol (#333)
* Restructure allocations page

* Update changelog
2021-09-03 23:20:30 +02:00
b1187cf880 Add a new symbol allocation chart (#326)
* Add a new symbol allocation chart

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-09-03 17:48:35 +02:00
ba9e6eab58 Feature/add link to transactions below holdings (#329)
* Add link: Manage transactions

* Update changelog
2021-09-02 21:17:01 +02:00
01feead017 Show decimal transactionCount and singular for 1 transaction (#327)
* Show decimal `transactionCount` and singular for 1 transaction

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-09-02 20:47:27 +02:00
6a0cfb8f77 Release 1.44.0 (#323) 2021-08-30 18:22:41 +02:00
6386786ac0 Bugfix/improve symbol lookup (#322)
* Improve symbol lookup

* Update changelog
2021-08-30 18:08:21 +02:00
d3be6577c8 Add feature: time-weighted rate of return (TWR) (#321) 2021-08-29 17:09:11 +02:00
73a967a7e5 Feature/add cash as asset sub class (#319)
* Add cash as asset sub class

* Update changelog
2021-08-26 21:49:02 +02:00
836ff6ec13 Feature/upgrade svgmap to version 2.6.0 (#318)
* Upgrade svgmap

* Update changelog
2021-08-26 21:23:49 +02:00
c5bb3023d3 Bugfix/filter out positions without quantity (#317)
* Filter out positions without any quantity

* Update changelog
2021-08-26 17:45:04 +02:00
695c378b48 Release 1.43.0 (#316) 2021-08-24 21:31:19 +02:00
fe975945d1 Feature/add fallback for loading currencies (#315)
* Add fallback for loading currencies

* Update changelog
2021-08-24 21:09:02 +02:00
d8782b0d4c Feature/automate countries for stocks in symbol profile data (#314)
* Automate countries for stocks in symbol profile data

* Update changelog
2021-08-24 20:24:18 +02:00
e14f08a8fb Release 1.42.0 (#313) 2021-08-22 22:37:44 +02:00
72c065a59d Feature/introduce asset sub class (#312)
* Introduce asset sub class

* Update changelog
2021-08-22 22:19:10 +02:00
98dac4052a Feature/add subscription type to the admin user table (#311)
* Add the subscription type to the user table in the admin control panel

* Update changelog
2021-08-22 22:11:05 +02:00
2083d28d02 Feature/minor improvements in the page components (#310)
* Move permissions to constructor

* Sort imports
2021-08-22 10:25:34 +02:00
addd5c36d9 Release 1.41.0 (#309) 2021-08-21 15:35:59 +02:00
aad8f77093 Feature/improve allocations by account (#308)
* Improve allocations by account

* Eliminate accounts from PortfolioPosition

* Ignore cash assets in the allocation chart by sector, continent and country

* Add missing accounts to portfolio details

* Update changelog
2021-08-21 15:03:55 +02:00
a904208d06 Feature/improve table styling (#307)
* Improve table styling

* Update changelog
2021-08-21 14:56:50 +02:00
2733b78044 Minor improvement (#306) 2021-08-21 14:56:11 +02:00
b43b515df1 Feature/add link to system status page (#305)
* Add link to system status page

* Update changelog
2021-08-21 08:57:12 +02:00
70e14b4d3c Feature/improve restricted view mode (#304)
* Improve wording and padding

* Update changelog
2021-08-20 20:58:33 +02:00
0f7d1b7d59 Release 1.40.0 (#303) 2021-08-19 21:56:21 +02:00
c2ab6a6c44 Feature/improve portfolio details endpoint (#302)
* Make details endpoint fault tolerant (do not throw error)

* Update changelog
2021-08-19 21:44:10 +02:00
c71a4c078e Bugfix/convert g bp to gbp in yahoo finance service (#301)
* Fix currency inconsistency in the yahoo finance service (GBp to GBP)

* Update changelog
2021-08-18 18:22:01 +02:00
e17b217032 Bugfix/fix issue on buy date in position detail dialog (#297)
* Fix issue on buy date

* Update changelog
2021-08-17 21:31:32 +02:00
408e08d43c Bugfix/fix node engine version mismatch (#299)
* Fix node engine version mismatch

* Update changelog
2021-08-17 20:32:12 +02:00
2fa324702f Release 1.39.0 (#296) 2021-08-16 21:57:51 +02:00
05b0efef82 Feature/add restricted view (#295)
* Add restricted view

* Update changelog
2021-08-16 21:40:29 +02:00
7c91727eb1 Feature/restructure allocations page (#294)
* Restructure allocations page

* Update changelog
2021-08-15 09:55:46 +02:00
0ee2258af8 Feature/improve impersonation mode (#293)
* Improve the impersonation mode

* Update changelog
2021-08-14 19:15:26 +02:00
308b218487 introduce basic module structure for data provider (#278)
* introduce basic module structure for data provider

* introduce DataGatheringModule

* introduce ExchangeRateDataModule

* introduce ImpersonationModule

* move RulesService

* cleanup portfolio module

* Sort imports

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-08-14 16:55:40 +02:00
26694adb8e Release 1.38.0 (#292) 2021-08-14 11:25:50 +02:00
77936e3bf3 Feature/improve users table (#291)
* Improve users table
  * Engagement / Day
  * Registration

* Update changelog
2021-08-14 11:12:08 +02:00
13090bf6b5 Feature/add overview menu item on mobile (#290)
* Add overview menu item

* Update changelog
2021-08-14 11:09:53 +02:00
b898c0678d Feature/refactor exchange rate service (#289)
* Refactor exchange rate service

* Update changelog
2021-08-14 11:06:21 +02:00
3330ae70b6 Release 1.37.0 (#288) 2021-08-13 20:49:05 +02:00
96a615dc5d Bugfix/only allow supported currencies in symbol search (#287)
* Only allow supported currencies in symbol search

* Update changelog
2021-08-13 20:29:57 +02:00
98f44323da Feature/improve usability of tabs on home page (#283)
* Improve usability: lazy load endpoints on tab change

* Feature/improve portfolio summary (#285)

* Update changelog
2021-08-13 19:26:48 +02:00
8adacd9760 Feature/upgrade angular material css vars to version 2.1.2 (#282)
* Upgrade angular-material-css-vars from version 2.1.0 to 2.1.2

* Update changelog
2021-08-13 19:05:22 +02:00
908aba170d Fix position chart for missing historical data (#284)
* Fix position chart for missing historical data

* Update changelog
2021-08-12 23:30:04 +02:00
d599797a65 Release 1.36.0 (#281) 2021-08-09 22:06:19 +02:00
8ac1272a9d Feature/eliminate name from scraper config (#277)
* Eliminate name from scraper config

* Update changelog
2021-08-09 21:33:58 +02:00
0a85a56c67 Respect cash balance in allocations, do not hide cryptocurrency holdings (#280)
* Respect cash balance in allocations, do not hide cryptocurrency holdings

* Update changelog
2021-08-09 21:26:41 +02:00
4ad5590838 Feature/improve data gathering (#276)
* Improve data gathering
  * Refactoring
  * On server restart, only reset if hanging in LOCKED_DATA_GATHERING state

* Update changelog
2021-08-09 21:11:35 +02:00
5b8af68e71 Release 1.35.0 (#279) 2021-08-08 21:23:48 +02:00
80d043729d Feature/replace type with asset class (#274)
* Improved the asset classification
  * Add assetClass to symbolProfile
  * Remove type from position

* Update changelog
2021-08-08 19:27:58 +02:00
178166d86b Extend investment chart by three months (#273) 2021-08-08 19:25:38 +02:00
37358fb480 Bugfix/add fallback if exchange service is not initialized (#264)
* Add fallback and log error

* Update changelog
2021-08-08 19:24:51 +02:00
616d601cf6 Improve wording (#275) 2021-08-08 12:20:30 +02:00
e88b889fdd Feature/optimize accounts table for mobile (#271)
* Optimize accounts table

* Update changelog
2021-08-08 09:24:47 +02:00
f6cdc4ff47 Feature/disable pagination of tabs (#272)
* Disable pagination

* Update changelog
2021-08-08 09:24:30 +02:00
818c40fc61 Feature/upgrade chart.js to version 3.5.0 (#268)
* Upgrade chart.js

* Update changelog
2021-08-08 08:33:55 +02:00
3589e72aea Harmonize prisma service (#266) 2021-08-07 22:38:07 +02:00
e68aa1fa68 Clean up imports (#267) 2021-08-07 22:37:39 +02:00
bb76ace95d Feature/improve support for draft transactions (#265)
* Improve support for draft transactions

* Update changelog
2021-08-07 20:52:55 +02:00
2bd9309827 Release 1.34.0 (#263) 2021-08-07 07:28:16 +02:00
f743c03e17 Feature/restructure page hierarchy (#259)
* Introduce portfolio group, remove tools

* Extract allocations to separate page

* Update changelog
2021-08-07 07:12:40 +02:00
dfcf826b4f implement support for buy-sell(-buy) scenario (#262)
Co-authored-by: Valentin Zickner <github@zickner.ch>
2021-08-07 07:07:12 +02:00
218efbb5bd fix position diagram for sold items (#261)
* fix position diagram for sold items

* Add missing data to position from the past

* Update changelog

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-08-07 07:03:35 +02:00
ba3b4564cd Bugfix/dashboard currencies (#260)
* filter list of orders only for relevant orders

* Update changelog

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-08-06 21:47:27 +02:00
3723a5c725 Release 1.33.0 (#257) 2021-08-05 22:54:12 +02:00
bf256ae50c fix position currency conversion (#256)
Co-authored-by: Valentin Zickner <github@zickner.ch>
2021-08-05 22:36:18 +02:00
c4b6273886 Clean up viewport scroller (#255) 2021-08-05 22:32:55 +02:00
7f047362cc Bugfix/fix division by zero in calculate overall gross performance (#253)
* Fix error with division by zero

* Update changelog
2021-08-05 07:49:25 +02:00
66900ffa24 Release 1.32.0 (#254) 2021-08-04 21:42:50 +02:00
33e05e949a Add links (#252)
* Ghostfolio Premium
* Blog
* Twitter
2021-08-04 20:59:59 +02:00
94d2310217 Bugfix/fix spike of positions with differing currency of user (#248)
* Convert market price back to currency of position

* Update changelog
2021-08-04 20:43:00 +02:00
b7d950f3f9 Bugfix/fix missing market state in positions (#250)
* Fix missing market state in positions

* Update changelog
2021-08-04 17:55:34 +02:00
b7943889da Feature/add screenshot (#251)
* Add screenshot

* Update changelog
2021-08-03 21:02:51 +02:00
84aa542560 Extend changelog (#247) 2021-08-02 21:56:36 +02:00
4bd41ffa41 Feature/eliminate name from timeline position (#245)
* Eliminate name from timeline position: Get the name from the symbolProfileService instead

* Update changelog
2021-08-01 20:43:12 +02:00
56c332c59a Release 1.31.1 (#246) 2021-08-01 17:58:10 +02:00
423ceec317 Feature/remove symbol icon from dialog header (#244)
* Remove symbol icon from dialog header

* Clean up code
2021-08-01 17:26:39 +02:00
5b4a1785ae fix investment values for portfolio.service.ts (#243)
* fix investment values for portfolio.service.ts

* Update changelog

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-08-01 16:26:25 +02:00
56a5664c87 Release 1.31.0 (#239) 2021-08-01 10:05:32 +02:00
823501f43e Sort imports (#242) 2021-08-01 09:41:44 +02:00
331ac7ded2 remove unused method in portfolio.service.ts (#240)
Co-authored-by: Valentin Zickner <github@zickner.ch>
2021-08-01 09:39:41 +02:00
92b44ba658 Update changelog 2021-08-01 00:55:10 +02:00
982ba7377a remove legacy portfolio.ts 2021-08-01 00:55:10 +02:00
4bbd17a37a fix tests 2021-08-01 00:55:10 +02:00
6a7def6c48 Clean up 2021-08-01 00:55:10 +02:00
ea219f0b88 Add guard 2021-08-01 00:55:10 +02:00
23b2e03923 refactor rule evaluation 2021-08-01 00:55:10 +02:00
04e6518226 move report generation to PortfolioService 2021-08-01 00:55:10 +02:00
72dbe00091 change rule service interface 2021-08-01 00:55:10 +02:00
9834c52739 refactor rule settings 2021-08-01 00:55:10 +02:00
c47578bd3e Clean up 2021-08-01 00:55:10 +02:00
9e4a49d811 Remove fallback 2021-08-01 00:55:10 +02:00
a3a98c68a5 Fix missing currency conversion 2021-08-01 00:55:10 +02:00
21570cca19 Fix impersonation in performance endpoint 2021-08-01 00:55:10 +02:00
71a3115fc6 refactor get position endpoint
Co-authored-by: Thomas <dotsilver@gmail.com>
2021-08-01 00:55:10 +02:00
de83dc7b84 create investment endpoint for analysis timeline
Co-authored-by: Thomas <dotsilver@gmail.com>
2021-08-01 00:55:10 +02:00
4a0695613e refactor PortfolioService#getOverview
Co-authored-by: Thomas <dotsilver@gmail.com>
2021-08-01 00:55:10 +02:00
328d814922 remove getPerformance from portfolio.ts
Co-authored-by: Thomas <dotsilver@gmail.com>
2021-08-01 00:55:10 +02:00
d23addb673 change getDetails to portfolio-calculator.ts
Co-authored-by: Thomas <dotsilver@gmail.com>
2021-08-01 00:55:10 +02:00
fb15cebb64 Add test (#237)
* Add test

* fix calculation for overall gross performance percentage

Co-authored-by: Valentin Zickner <github@zickner.ch>
2021-08-01 00:55:10 +02:00
9c51a257ae fix performance of combination of investments 2021-08-01 00:55:10 +02:00
f9b9dc32cb Always show positions title 2021-08-01 00:55:10 +02:00
e7194ef3ce Use new positions service 2021-08-01 00:55:10 +02:00
ec5523b459 Handle empty portfolio 2021-08-01 00:55:10 +02:00
c8c21a016a Handle empty portfolio 2021-08-01 00:55:10 +02:00
9821b7f8f0 Add TWR test scenario 1 (#236)
* Add TWR test scenario 1

* fix second transaction item

* change time-weighted rate algorithm

Co-authored-by: Valentin Zickner <valentin.zickner@flowable.com>
2021-08-01 00:55:10 +02:00
ed731afc66 fix market price fetch in case symbols are missing 2021-08-01 00:55:10 +02:00
ff15d5cbc4 Rename performance labels 2021-08-01 00:55:10 +02:00
3c4949de35 Hide net performance 2021-08-01 00:55:10 +02:00
bd0e53525b Refactoring 2021-08-01 00:55:10 +02:00
cbdb68e2f8 Refactoring 2021-08-01 00:55:10 +02:00
8571709014 Refactoring 2021-08-01 00:55:10 +02:00
e7ef1d426e Refactoring 2021-08-01 00:55:10 +02:00
39cba0a8eb Refactoring 2021-08-01 00:55:10 +02:00
a90c314e30 Refactoring 2021-08-01 00:55:10 +02:00
47d71405e1 Refactoring 2021-08-01 00:55:10 +02:00
5e9cecc6c1 Refactoring 2021-08-01 00:55:10 +02:00
fb9e66318f Improve test 2021-08-01 00:55:10 +02:00
b8194eb64f Clean up code 2021-08-01 00:55:10 +02:00
cbb81916ee Sort imports 2021-08-01 00:55:10 +02:00
9b1e9397a8 add errors in case gross performance is not set 2021-08-01 00:55:10 +02:00
b779964adb add error handling to performance aggregation 2021-08-01 00:55:10 +02:00
409afac2a9 fix rate conversion for todays symbols 2021-08-01 00:55:10 +02:00
e0a4e16ea1 Improve error handling 2021-08-01 00:55:10 +02:00
dc84abdc0a change performance report to portfolio calculator 2021-08-01 00:55:10 +02:00
b031b028f1 add today to getValues 2021-08-01 00:55:10 +02:00
3b7e0a0106 remove unnecessary if condition 2021-08-01 00:55:10 +02:00
ea66081073 move interfaces to separate files 2021-08-01 00:55:10 +02:00
602a770a09 fix typo 2021-08-01 00:55:10 +02:00
e522722aa6 Update comment 2021-08-01 00:55:10 +02:00
03ca5d7663 add further tests for portfolio-calculator with one transaction 2021-08-01 00:55:10 +02:00
136563c949 fix single buy test 2021-08-01 00:55:10 +02:00
948c45c602 Update test 2021-08-01 00:55:10 +02:00
e0be792e46 Re-enable all tests 2021-08-01 00:55:10 +02:00
c3d010135f Add test for single buy 2021-08-01 00:55:10 +02:00
d6a16a6093 Improve error handling 2021-08-01 00:55:10 +02:00
34c13c80ec add error handling for current positions 2021-08-01 00:55:10 +02:00
f65a108436 change hardcoded timestamp in test to timezone specific 2021-08-01 00:55:10 +02:00
993f066e08 remove console.time statements during portfolio calls 2021-08-01 00:55:10 +02:00
852902d1ab add current position calculation with holding period return calculation 2021-08-01 00:55:10 +02:00
ee89822bfe Fix tests 2021-08-01 00:55:10 +02:00
e0435e5cad Add name to position 2021-08-01 00:55:10 +02:00
e2c23703dc Fix tests 2021-08-01 00:55:10 +02:00
1226c26a9d Refactor positions 2021-08-01 00:55:10 +02:00
fdc89f7182 optimize performance of positions endpoint 2021-08-01 00:55:10 +02:00
1e368d6e2d Extend type 2021-08-01 00:55:10 +02:00
04e03bd080 add multi-date fetch for current values 2021-08-01 00:55:10 +02:00
66e7ad3fd2 prepare for multi-day database fetch 2021-08-01 00:55:10 +02:00
b4dc21dd61 Implement new positions endpoint 2021-08-01 00:55:10 +02:00
8a482e63b9 fix gross performance number type 2021-08-01 00:55:10 +02:00
aabfb39e8f optimize portfolio calculator to fetch all symbols for one day 2021-08-01 00:55:10 +02:00
cdc8faff7f add current position gross performance (percentage) 2021-08-01 00:55:10 +02:00
7b696e39de add sorting and symbol as a result 2021-08-01 00:55:10 +02:00
c88ad2c225 change getValues to multiple symbols 2021-08-01 00:55:10 +02:00
fbc9269abf disable naming convention eslint 2021-08-01 00:55:10 +02:00
cbe079ae66 ignore missing values 2021-08-01 00:55:10 +02:00
8e4ee7feea optimize order loading 2021-08-01 00:55:10 +02:00
f1b3c61675 add additional time measurements 2021-08-01 00:55:10 +02:00
24dc312367 Add logs to benchmark 2021-08-01 00:55:10 +02:00
7ac7442f73 Declare functions explicitly as public 2021-08-01 00:55:10 +02:00
099571437e Extend current rate service with getRange() 2021-08-01 00:55:10 +02:00
7dac059a55 Sort imports 2021-08-01 00:55:10 +02:00
48fbeda72d Sort imports 2021-08-01 00:55:10 +02:00
19007cdc34 Optimize market data query 2021-08-01 00:55:10 +02:00
5037393866 Support today in current rate service 2021-08-01 00:55:10 +02:00
ddf24163b4 optimize database query execution for portfolio chart 2021-08-01 00:55:10 +02:00
b26521c4bd add workaround for database date search 2021-08-01 00:55:10 +02:00
cfee6c1ddd add draft integration of new portfolio calculator to chart 2021-08-01 00:55:10 +02:00
19bcd601d1 add gross performance 2021-08-01 00:55:10 +02:00
836df69e68 add calculation of current investment 2021-08-01 00:55:10 +02:00
dd86adcea1 add investment to timeline calculation 2021-08-01 00:55:10 +02:00
4f7628921d add timeline time point calculation 2021-08-01 00:55:10 +02:00
88f0cb095d make it pretty 2021-08-01 00:55:10 +02:00
7538133d09 implement getCurrentPositions of PortfolioCalculator 2021-08-01 00:55:10 +02:00
50b280c5a6 Fix version of big.js 2021-08-01 00:55:10 +02:00
67606e4026 Extend current rate service test 2021-08-01 00:55:10 +02:00
9de56c32ac create base structure for portfolio rewrite
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-08-01 00:55:10 +02:00
5c0f710563 Release 1.30.0 (#238) 2021-07-31 11:29:06 +02:00
1c65599a16 Feature/add blog (#218)
* Setup blog

* Add german blog post

* Add english blog post

* Update changelog
2021-07-31 11:21:32 +02:00
61e667213e Feature/add date range selector to positions tab (#234)
* Add date range selector

* Update changelog
2021-07-27 22:38:55 +02:00
ba47212057 Release 1.29.0 (#233) 2021-07-26 22:14:04 +02:00
f0c6517019 Improve tabs layout (#232) 2021-07-26 21:36:52 +02:00
80ba112bc0 Feature/change menu icon if menu open (#231)
* Change menu icon

* Update changelog
2021-07-26 21:06:10 +02:00
40696b425e Feature/add tabs on the home page (#230)
* Add tabs

* Update changelog
2021-07-26 20:23:32 +02:00
6dbdf23a68 Release 1.28.0 (#229) 2021-07-24 21:33:38 +02:00
cdcbe3ab71 Feature/various layout improvements (#224)
* Various layout improvements

* Update changelog
2021-07-24 21:17:06 +02:00
6996e5a140 Feature/add data gathering for symbol profile data (#228)
* Implement profile data gathering

* Update changelog
2021-07-24 21:13:48 +02:00
be8d60968d Feature/improve active menu button style (#227)
* Improve text decoration style

* Update changelog
2021-07-24 13:04:37 +02:00
d53e5c4da5 Refactor auth guard (#226) 2021-07-24 10:57:03 +02:00
a3a9957196 Release 1.27.0 (#223) 2021-07-18 20:05:46 +02:00
9072cbdba1 Feature/add no transactions info on zen page (#222)
* Add no transactions info to zen page

* Update changelog
2021-07-18 17:34:28 +02:00
120b691336 Bugfix/fix url to fear and greed index (#221)
* Fix url

* Update changelog
2021-07-18 09:33:23 +02:00
bd4ad76953 Feature/remove pause in onboarding (#220)
* Improve onboarding
  * Remove pause
  * Add icon

* Update changelog
2021-07-18 08:19:05 +02:00
94d56f553f Bugfix/fix chart on landing page (#219)
* Fix chart on landing page

* Update changelog
2021-07-17 20:48:08 +02:00
ecdd325228 Release 1.26.0 (#217) 2021-07-17 11:06:14 +02:00
51fbc538ca Feature/set public stripe key dynamically (#216)
* Set public Stripe key dynamically

* Update changelog
2021-07-17 11:04:43 +02:00
39a76f7f40 Feature/add robots.txt (#215)
* Add robots.txt

* Update changelog
2021-07-16 21:32:02 +02:00
e4d325daab Feature/various style improvements (#214)
* Improve styles

* Update changelog
2021-07-15 18:10:18 +02:00
b765df65d6 Improve wording (#213) 2021-07-14 20:54:23 +02:00
c7b7efae3b Feature/import transactions (#212)
* Implement import transactions functionality

* Update changelog
2021-07-14 20:54:05 +02:00
be5b58f49a Bugfix/fix warn color (#211)
* Fix warn color

* Update changelog
2021-07-13 20:29:22 +02:00
91c748c7ad Release 1.25.0 (#210) 2021-07-11 17:21:24 +02:00
ecfe694f0b Feature/export transactions (#209)
* Export functionality for transactions

* Update changelog
2021-07-11 17:05:58 +02:00
1491bf7f76 Update changelog (#208) 2021-07-11 10:37:02 +02:00
b3b9a051c3 Shorten slogan (#207) 2021-07-11 10:31:36 +02:00
bf1146bfd6 Feature/change slogan to wealth management (#206)
* Harmonize slogans to "Open Source Wealth Management Software"

* Update changelog
2021-07-10 19:20:02 +02:00
0774ca91a1 Improve settings selectors layout (#205) 2021-07-10 18:17:17 +02:00
f403807f2d Bugfix/fix average buy price calculation (#204)
* Fix average buy price calculation

* Update changelog
2021-07-10 18:16:46 +02:00
f22991b090 Feature/respect cash balance in analysis (#203)
* Respect cash balance in in analysis

* Update changelog
2021-07-10 14:57:03 +02:00
1135a5b335 Fix rendering of currency and platform in dialogs and clean up observables (#202) 2021-07-08 21:28:28 +02:00
d9ea255c17 Release 1.24.0 (#201) 2021-07-07 21:45:57 +02:00
2c19d8c8e7 Feature/add balance to account (#193)
* Add balance attribute and calculate total balance

* Update changelog
2021-07-07 21:23:36 +02:00
db090229ce Feature/add total value in the create or edit transaction dialog (#192)
* Display total value

* Update changelog
2021-07-07 21:14:01 +02:00
fbe590ddb9 Feature/upgrade angular material css vars to 2.0.0 (#200)
* Upgrade angular-material-css-vars

* Update changelog
2021-07-05 21:53:30 +02:00
0d65136a9e Revert "Remove unneeded dependencies (#197)" (#199)
This reverts commit a062a3cee4.
2021-07-05 20:35:10 +02:00
dea87cc3cf Improve README.md (#198) 2021-07-04 22:19:09 +02:00
a062a3cee4 Remove unneeded dependencies (#197) 2021-07-04 22:11:47 +02:00
5b1b207a6f Feature/upgrade angular dependencies to version 12.0.x (#196)
* Update angular dependencies to version 12.0.X

* Update changelog
2021-07-04 21:55:25 +02:00
63cc7b2871 Feature/upgrade nestjs dependencies (#195)
* Upgrade @nestjs dependencies

* Update changelog
2021-07-04 21:45:53 +02:00
3986e8f879 Upgrade Nx to version 12.5.4 (#194) 2021-07-04 21:31:15 +02:00
290e93bbd7 Release 1.23.1 (#191) 2021-07-03 12:20:36 +02:00
b08ecd1b18 Release 1.23.0 (#190) 2021-07-03 11:51:26 +02:00
92d321a001 Drafts for orders (#187)
* Render the future with a dashed border

* Update changelog
2021-07-03 11:32:03 +02:00
ce2d8d519d Change from travis-ci.org to travis-ci.com (#188) 2021-06-30 21:52:29 +02:00
f32bef071e Add contributing section (#186) 2021-06-27 10:23:51 +02:00
4aa7365d9b Release 1.22.0 (#185) 2021-06-25 17:34:49 +02:00
367f25a975 Feature/set user id in stripe callback (#184)
* Set user id as description

* Update changelog
2021-06-24 21:52:41 +02:00
9832334da1 Move @types/lodash to dev dependencies (#183) 2021-06-23 17:36:40 +02:00
e126f9ec54 Release 1.21.0 (#182) 2021-06-22 21:55:00 +02:00
09bbda3502 Change from subscription to one time payment (#181) 2021-06-22 21:53:29 +02:00
ee9a521813 Bugfix/fix base currency in pricing page (#180)
* Fix base currency

* Update changelog
2021-06-21 20:52:01 +02:00
169c151547 Feature/improve style of about page (#177)
* Improve style

* Update changelog
2021-06-21 20:08:45 +02:00
3a95ec0f81 Release 1.20.0 (#179) 2021-06-21 20:05:54 +02:00
ad00cd9d81 Feature/setup subscription with stripe (#178)
* Set up stripe for subscriptions

* Update permissions and add discount

* Update changelog
2021-06-21 20:03:36 +02:00
373a2015c0 Release 1.19.0 (#176) 2021-06-17 23:01:59 +02:00
66c955ad6c Feature/ghostfolio in numbers (#175)
* Add Ghostfolio in numbers section

* Update changelog
2021-06-17 22:59:48 +02:00
a2440fc067 Release 1.18.0 (#174) 2021-06-16 17:34:43 +02:00
3d7624d997 Feature/improve twa onboarding (#173)
* Improve TWA onboarding (Redirect to the account registration page)

* Update changelog
2021-06-16 17:31:31 +02:00
0264b592b9 Feature/improve investments by sector (#172)
* Improve investments analysis by sector

* Update changelog
2021-06-16 17:05:43 +02:00
198eaf57d3 Release 1.17.0 (#171) 2021-06-15 21:17:41 +02:00
6783ea2ebb Feature/upgrade various frontend dependencies (#170)
* Upgrade frontend dependencies

* Update changelog
2021-06-15 21:15:48 +02:00
a35701fe24 Feature/upgrade to angular 12 (#169)
* Upgrade to Angular 12

* Update changelog
2021-06-15 21:03:55 +02:00
5db90f1787 Feature/improve error page of fingerprint sign in (#167)
* Improve error page

* Update changelog
2021-06-15 09:47:18 +02:00
81fe538484 Order attribute 2021-06-15 09:43:48 +02:00
51884913be Feature/disable fingerprint sign in in demo account page (#163)
* Disable fingerprint toggle for demo user

* Update changelog
2021-06-15 09:21:53 +02:00
8886082dfa Feature/upgrade eslint and prettier dependencies (#164)
* Upgrade eslint and prettier dependencies

* Feature/upgrade date fns to version 2.22.1 (#165)

* Feature/upgrade chart.js to version 3.3.2 (#166)

* Update changelog
2021-06-15 09:17:27 +02:00
3b12e5b85b Release 1.16.0 (#162) 2021-06-14 22:00:00 +02:00
6c1119caec Restrict webauthn to fingerprint only and improve UX (#161)
* Restrict webauthn to fingerprint only

* Move webauthn login to separate page /webauthn

* Stay signed in with social login

* Update changelog

Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-06-14 21:57:09 +02:00
698d5ec3b7 Release 1.15.0 (#160) 2021-06-14 16:15:50 +02:00
e87c942cb8 Add webauthn (#82)
* Add webauthn

* Complete WebAuthn device sign up and login

* Move device registration to account page
* Replace the token login with a WebAuthn prompt if the current device has been registered
* Mark the current device in the list of registered auth devices

* Fix after rebase

* Fix tests

* Disable "Add current device" button if current device is registered

* Add option to "Stay signed in"

* Remove device list feature, sign in with deviceId instead

* Improve usability

* Update changelog

Co-authored-by: Matthias Frey <mfrey43@gmail.com>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
2021-06-14 16:09:40 +02:00
f7860a9799 Feature/introduce max items in portfolio proportion chart (#159)
* Add option to limit items

* Update changelog
2021-06-14 14:18:02 +02:00
c519eb0e99 Bugfix/fix last activity column (#158)
* Fix last activity (only values in the past)

* Update changelog
2021-06-14 14:06:24 +02:00
8314b98f81 Feature/improve tables (#157)
* Improve tables

* Update changelog
2021-06-14 14:02:25 +02:00
194cf1ddcc Feature/clean up analysis page (#155)
* Clean up analysis page

* Update changelog
2021-06-14 13:55:15 +02:00
7da6478699 Improve server start instructions (#156) 2021-06-12 22:29:56 +02:00
4f2bbba782 Release 1.14.0 (#154) 2021-06-09 20:36:31 +02:00
9eb25f6c9e Feature/connect or create logic for symbol profile (#153)
* Add connectOrCreate logic

* Extend seed

* Update changelog
2021-06-09 20:35:02 +02:00
f74b00446c Feature/improve world map chart (#152)
* Improve world map chart

* Update changelog
2021-06-09 20:32:39 +02:00
beb7e6ec34 Release 1.13.0 (#151) 2021-06-08 22:02:11 +02:00
2eafc042ad Feature/add world map (#150)
* Add a global heat map

* Update changelog
2021-06-08 21:59:46 +02:00
74954bc51d Release 1.12.0 (#149) 2021-06-06 15:33:20 +02:00
6a03120225 Feature/add symbol profile model (#148)
* Add symbol profile model and positions by country chart

* Add positions by continent chart

* Fix tests

* Extend seed

* Update changelog
2021-06-06 15:31:28 +02:00
21504573b4 Release 1.11.0 (#147) 2021-06-05 17:30:59 +02:00
fabd912fba Setup initial prisma migration (#146) 2021-06-05 17:20:52 +02:00
00b42855b6 Feature/upgrade prisma to 2.24.1 (#145)
* Upgrade prisma

* Update changelog

* Update database push script
2021-06-05 17:19:38 +02:00
ef272360fb Feature/render average prices in position detail chart (#144)
* Render average buy prices

* Update changelog
2021-06-05 17:17:53 +02:00
026a5011d4 Feature/add account registration page (#141)
* Add account registration page

* Update changelog
2021-06-05 17:16:07 +02:00
aa4206af0e Feature/various frontend improvements 2 (#140)
* Change buttons to links

* Update changelog
2021-06-05 17:11:03 +02:00
7788465272 Release 1.10.1 (#139) 2021-06-02 21:24:27 +02:00
3066dfd805 Release 1.10.0 (#138) 2021-06-02 20:56:06 +02:00
34303163bc Various frontend improvements (#137)
* Various frontend improvements

* Clean up import
2021-06-02 20:54:12 +02:00
e7fbcd4fa0 Feature/extend pricing page (#130)
* Extend pricing page

* Feature/align pricing page with subscription model (#135)

* Align pricing page with subscription model

* Update changelog
2021-06-02 20:15:53 +02:00
7c22969de1 Feature/move tools to sub path (#125)
* Move tools to sub path

* Update changelog
2021-06-02 20:10:44 +02:00
6623bc0113 Release 1.9.0 (#136) 2021-06-01 21:44:46 +02:00
146b5201b5 Feature/make x ray rules order consistent (#134)
* Make order of X-ray rules consistent

* Update changelog
2021-06-01 21:40:32 +02:00
b021fbde59 Feature/refactor to format distance to now strict (#133)
* Change from formatDistanceToNow to formatDistanceToNowStrict

* Update changelog
2021-06-01 21:38:55 +02:00
ec046b81a7 Fix style (#132) 2021-06-01 21:35:47 +02:00
aea497154a Feature/prettify symbols in transaction filtering component (#131)
* Prettify generic scraper symbols

* Update changelog
2021-06-01 21:34:53 +02:00
dc736d53b4 Fix sorting (#129)
* Fix sorting
2021-06-01 21:33:56 +02:00
5957b33779 Feature/enable labels on the x axis of the investment chart (#128)
* Enable x-axis labels

* Update changelog
2021-05-30 20:39:37 +02:00
bafdce56ad add yarn build:all to .travis.yml (#127) 2021-05-27 22:32:10 +02:00
42a2d404e4 Fix type errors (#126) 2021-05-27 21:12:55 +02:00
11b2379d98 Feature/respect data source in data gathering (#107)
* Respect data source in data gathering

* Update changelog

* optimize fetching from multiple data sources (#123)

* optimize fetching from multiple data sources

* improve performance by executing data gathering promises in parallel

* removed unused imports

* rename hasHistoricalData to canHandle

* Sort imports

* Clean up

Co-authored-by: Valentin Zickner <3200232+vzickner@users.noreply.github.com>
2021-05-27 20:50:10 +02:00
c0657a2e9e Extend README.md (#124)
* Add contributions welcome badge
* Add features
* State technology stack precisely
2021-05-24 21:11:06 +02:00
646dcb91c5 Release 1.8.0 (#122) 2021-05-24 16:32:11 +02:00
ad961f3039 Bugfix/fix missing header of public pages (#121)
* Fix missing header of public pages

* Update changelog
2021-05-24 16:28:42 +02:00
c16f743b07 Feature/add tools section (#120)
* Add tools section

* Update changelog
2021-05-24 16:25:59 +02:00
8e13f6ef9b Bugfix/fix performance chart (#119)
* Fix value of performance chart

* Update changelog
2021-05-24 16:24:54 +02:00
95bcdea69b Refactor cd to changeDetectorRef (#118) 2021-05-24 10:12:53 +02:00
0d6fe4a232 Feature/refactor user service as observable store (#117)
* Implement user service as observable store

* Clean up tokenStorageService usage

* Update changelog
2021-05-24 09:38:44 +02:00
ced4519412 Reorder (#116) 2021-05-22 16:14:16 +02:00
ef8b7718b1 Release 1.7.0 (#115) 2021-05-22 13:49:25 +02:00
b4762dc463 Bugfix/fix internal navigation with query param (#114)
* Fix internal navigation with query parameter

* Add guard

* Update changelog
2021-05-22 13:48:06 +02:00
9851cce382 Feature/hide footer on mobile (#113)
* Hide footer on mobile

* Improve about text

* Update changelog
2021-05-22 13:45:50 +02:00
a1460a98fd Release 1.6.0 (#112) 2021-05-22 10:22:46 +02:00
1a553a296f Feature/improve user table of admin control panel (#109)
* Improve user table

* Add index
* Increase limit
* Improve alignment of cell content

* Update changelog
2021-05-22 10:17:12 +02:00
f5bd6b0d58 Release 1.5.0 (#111) 2021-05-22 10:11:01 +02:00
78a4946e8b Feature/zen mode (#110)
* Start with implementation
* Refactor AuthGuard, persist displayMode in user settings
* Refactor DisplayMode to ViewMode
* Update changelog
2021-05-22 10:04:56 +02:00
702ee956a2 Release 1.4.0 (#108) 2021-05-20 20:43:48 +02:00
200a7d2d65 Feature/refactor search functionality (#105)
* Refactor search functionality

* Update changelog

* Improvements after code review
2021-05-20 20:36:08 +02:00
79edc09710 Store utm_source in local storage (#106) 2021-05-19 20:36:44 +02:00
77255df4be Feature/disable base currency selector for demo user (#104)
* Disable base currency selector based on permission

* Update changelog
2021-05-18 19:36:24 +02:00
277133fa1a Clean up services (#103)
* LanguageService
* TokenStorageService
2021-05-17 19:55:10 +02:00
abd0e08566 Introduce @ghostfolio/common lib (#102) 2021-05-16 22:11:14 +02:00
561d8dbc70 Feature/rename account link (#101)
* Rename account link

* Update changelog
2021-05-16 21:22:03 +02:00
c973ffd3ba Feature/reorganize helper lib (#100)
Reorganize helper lib (Move interfaces and types)
* InfoItem
* PortfolioItem
* PortfolioOverview
* PortfolioPerformance
* Position
* PortfolioPosition
* PortfolioReport
* PortfolioReportRule
* User
* UserSettings
* DateRange
* AdminData
* AccessWithGranteeUser
* OrderWithAccount
* Granularity
* UserWithSettings
* RequestWithUser
2021-05-16 21:20:59 +02:00
368de7dedc Extend unit tests (#99) 2021-05-16 21:19:14 +02:00
e56514629f Refactor postgres variables (#98)
Co-Authored-By: Valentin Zickner <valentin@coderworks.de>
2021-05-16 09:31:49 +02:00
7a8a25c4c0 Feature/filter by year in transaction table (#97)
* Filter by year

Co-Authored-By: Valentin Zickner <valentin@coderworks.de>
2021-05-16 09:31:28 +02:00
5d36d3a6bb remove database dependencies for test execution (#96)
Co-Authored-By: Valentin Zickner <valentin@coderworks.de>
2021-05-15 18:57:27 +02:00
0ef35fd31f Feature/hide unknown exchange (#95)
* Hide unknown exchange

* Update changelog
2021-05-15 17:50:28 +02:00
c1c22c195d Release 1.3.0 (#94) 2021-05-15 10:15:29 +02:00
111d8d8e3c Feature/rename share to allocation in columns of positions table (#93)
* Rename columns

* Initial Share -> Initial Allocation
* Current Share -> Current Allocation

* Update changelog
2021-05-15 10:12:12 +02:00
b0a24e4fc0 Desaturate background color (#92) 2021-05-15 10:09:07 +02:00
694b9b8991 Feature/refactor active menu item state (#91)
* Refactor active menu item state

* Update changelog
2021-05-15 10:05:03 +02:00
fada347aa5 Fix pricing page link (#90) 2021-05-15 08:36:13 +02:00
1248 changed files with 225752 additions and 20566 deletions

16
.env
View File

@ -1,16 +0,0 @@
COMPOSE_PROJECT_NAME=ghostfolio-development
# CACHE
REDIS_HOST=localhost
REDIS_PORT=6379
# POSTGRES
POSTGRES_USER=user
POSTGRES_PASSWORD=password
POSTGRES_DB=ghostfolio-db
ACCESS_TOKEN_SALT=GHOSTFOLIO
ALPHA_VANTAGE_API_KEY=
DATABASE_URL=postgresql://user:password@localhost:5432/ghostfolio-db?sslmode=prefer
JWT_SECRET_KEY=123456
PORT=3333

15
.env.example Normal file
View File

@ -0,0 +1,15 @@
COMPOSE_PROJECT_NAME=ghostfolio-development
# CACHE
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=<INSERT_REDIS_PASSWORD>
# POSTGRES
POSTGRES_DB=ghostfolio-db
POSTGRES_USER=user
POSTGRES_PASSWORD=<INSERT_POSTGRES_PASSWORD>
ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING>
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer
JWT_SECRET_KEY=<INSERT_RANDOM_STRING>

View File

@ -1,12 +1,12 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nrwl/nx"],
"plugins": ["@nx"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@nrwl/nx/enforce-module-boundaries": [
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
@ -23,12 +23,12 @@
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
"extends": ["plugin:@nx/typescript"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
},
{
@ -44,7 +44,7 @@
}
],
"@typescript-eslint/member-ordering": "error",
"@typescript-eslint/naming-convention": "error",
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-inferrable-types": [
@ -113,5 +113,6 @@
"radix": "error"
}
}
]
],
"extends": [null, "plugin:storybook/recommended"]
}

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: ['https://www.buymeacoffee.com/ghostfolio']

53
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,53 @@
---
name: Bug report
about: Create a report to help us improve
title: '[BUG]'
labels: ''
assignees: ''
---
**Important Notice**
The issue tracker is **ONLY** used for reporting bugs. New features should be discussed in our [Slack](https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg) community or in [Discussions](https://github.com/ghostfolio/ghostfolio/discussions).
Incomplete or non-reproducible issues may be closed, but we are here to help! If you encounter difficulties reproducing the bug or need assistance, please reach out to our community channels mentioned above.
Thank you for your understanding and cooperation!
**Bug Description**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
<!-- Steps to reproduce the behavior -->
1.
2.
3.
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Logs**
<!-- If applicable, add logs to help explain your problem. -->
**Environment**
<!-- Please complete the following information -->
- Ghostfolio Version X.Y.Z
- Cloud or Self-hosted
- Experimental Features enabled or disabled
- Browser
- OS
**Additional context**
<!-- Add any other context about the problem here. -->

39
.github/workflows/build-code.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Build code
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node_version:
- 18
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node_version }}
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Check formatting
run: yarn format:check
- name: Execute tests
run: yarn test
- name: Build application
run: yarn build:production

50
.github/workflows/docker-image.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: Docker image CD
on:
push:
tags:
- '*.*.*'
pull_request:
branches:
- 'main'
jobs:
build_and_push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ghostfolio/ghostfolio
tags: |
type=semver,pattern={{major}}
type=semver,pattern={{version}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.output.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

7
.gitignore vendored
View File

@ -5,6 +5,7 @@
/tmp
# dependencies
/.yarn
/node_modules
# IDEs and editors
@ -23,15 +24,19 @@
!.vscode/settings.json
# misc
/.angular/cache
.env
.env.prod
.nx/cache
/.sass-cache
/connect.lock
/coverage
/dist
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
yarn-error.log
# System Files
.DS_Store

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v18

View File

@ -1 +1,3 @@
/.nx/cache
/dist
/test/import

View File

@ -1,5 +1,15 @@
{
"attributeGroups": [
"$ANGULAR_ELEMENT_REF",
"$ANGULAR_STRUCTURAL_DIRECTIVE",
"$DEFAULT",
"$ANGULAR_INPUT",
"$ANGULAR_TWO_WAY_BINDING",
"$ANGULAR_OUTPUT"
],
"attributeSort": "ASC",
"endOfLine": "auto",
"plugins": ["prettier-plugin-organize-attributes"],
"printWidth": 80,
"singleQuote": true,
"tabWidth": 2,

View File

@ -1,9 +0,0 @@
language: node_js
git:
depth: false
node_js:
- 14
before_script:
- yarn
script:
- yarn format:check

30
.vscode/launch.json vendored
View File

@ -2,31 +2,33 @@
"version": "0.2.0",
"configurations": [
{
"name": "Debug Jest File",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/@angular/cli/bin/ng",
"args": [
"test",
"--codeCoverage=false",
"--testFile=${workspaceFolder}/apps/api/src/models/portfolio.spec.ts"
"--testFile=${workspaceFolder}/apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell.spec.ts"
],
"console": "internalConsole",
"cwd": "${workspaceFolder}",
"console": "internalConsole"
"name": "Debug Jest",
"program": "${workspaceFolder}/node_modules/@nrwl/cli/bin/nx",
"request": "launch",
"type": "node"
},
{
"envFile": "${workspaceFolder}/.env",
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/apps/api/src/main.ts",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"outFiles": ["${workspaceFolder}/dist/apps/api/**/*.js"],
"autoAttachChildProcesses": true,
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/apps/api",
"envFile": "${workspaceFolder}/.env",
"name": "Debug API",
"outFiles": ["${workspaceFolder}/dist/apps/api/**/*.js"],
"program": "${workspaceFolder}/apps/api/src/main.ts",
"request": "launch",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"skipFiles": [
"${workspaceFolder}/node_modules/**/*.js",
"<node_internals>/**/*.js"
]
],
"type": "node"
}
]
}

File diff suppressed because it is too large Load Diff

55
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,55 @@
# Ghostfolio Development Guide
## Experimental Features
New functionality can be enabled using a feature flag switch from the user settings.
### Backend
Remove permission in `UserService` using `without()`
### Frontend
Use `*ngIf="user?.settings?.isExperimentalFeatures"` in HTML template
## Git
### Rebase
`git rebase -i --autosquash main`
## Dependencies
### Angular
#### Upgrade (minor versions)
1. Run `npx npm-check-updates --upgrade --target "minor" --filter "/@angular.*/"`
### Nx
#### Upgrade
1. Run `yarn nx migrate latest`
1. Make sure `package.json` changes make sense and then run `yarn install`
1. Run `yarn nx migrate --run-migrations` (Run `YARN_NODE_LINKER="node-modules" NX_MIGRATE_SKIP_INSTALL=1 yarn nx migrate --run-migrations` due to https://github.com/nrwl/nx/issues/16338)
### Prisma
#### Access database via GUI
Run `yarn database:gui`
https://www.prisma.io/studio
#### Synchronize schema with database for prototyping
Run `yarn database:push`
https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push
#### Create schema migration
Run `yarn prisma migrate dev --name added_job_title`
https://www.prisma.io/docs/concepts/components/prisma-migrate#getting-started-with-prisma-migrate

61
Dockerfile Normal file
View File

@ -0,0 +1,61 @@
FROM --platform=$BUILDPLATFORM node:18-slim as builder
# Build application and add additional files
WORKDIR /ghostfolio
# Only add basic files without the application itself to avoid rebuilding
# layers when files (package.json etc.) have not changed
COPY ./CHANGELOG.md CHANGELOG.md
COPY ./LICENSE LICENSE
COPY ./package.json package.json
COPY ./yarn.lock yarn.lock
COPY ./.yarnrc .yarnrc
COPY ./prisma/schema.prisma prisma/schema.prisma
RUN apt update && apt install -y \
git \
g++ \
make \
openssl \
python3 \
&& rm -rf /var/lib/apt/lists/*
RUN yarn install
# See https://github.com/nrwl/nx/issues/6586 for further details
COPY ./decorate-angular-cli.js decorate-angular-cli.js
RUN node decorate-angular-cli.js
COPY ./nx.json nx.json
COPY ./replace.build.js replace.build.js
COPY ./jest.preset.js jest.preset.js
COPY ./jest.config.ts jest.config.ts
COPY ./tsconfig.base.json tsconfig.base.json
COPY ./libs libs
COPY ./apps apps
RUN yarn build:production
# Prepare the dist image with additional node_modules
WORKDIR /ghostfolio/dist/apps/api
# package.json was generated by the build process, however the original
# yarn.lock needs to be used to ensure the same versions
COPY ./yarn.lock /ghostfolio/dist/apps/api/yarn.lock
RUN yarn
COPY prisma /ghostfolio/dist/apps/api/prisma
# Overwrite the generated package.json with the original one to ensure having
# all the scripts
COPY package.json /ghostfolio/dist/apps/api
RUN yarn database:generate-typings
# Image to run, copy everything needed from builder
FROM node:18-slim
RUN apt update && apt install -y \
openssl \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /ghostfolio/dist/apps /ghostfolio/apps
WORKDIR /ghostfolio/apps/api
EXPOSE ${PORT:-3333}
CMD [ "yarn", "start:production" ]

277
README.md
View File

@ -1,52 +1,67 @@
<div align="center">
<h1>Ghostfolio</h1>
<p>
<strong>Open Source Portfolio Tracker</strong>
</p>
<p>
<a href="https://ghostfol.io"><strong>Live Demo</strong></a>
</p>
<p>
<a href="https://travis-ci.org/github/ghostfolio/ghostfolio" rel="nofollow">
<img src="https://travis-ci.org/ghostfolio/ghostfolio.svg?branch=main" alt="Build Status"/>
</a>
<a href="https://www.gnu.org/licenses/agpl-3.0" rel="nofollow">
<img src="https://img.shields.io/badge/License-AGPL%20v3-blue.svg" alt="License: AGPL v3"/>
</a>
</p>
[<img src="https://avatars.githubusercontent.com/u/82473144?s=200" width="100" alt="Ghostfolio logo">](https://ghostfol.io)
# Ghostfolio
**Open Source Wealth Management Software**
[**Ghostfol.io**](https://ghostfol.io) | [**Live Demo**](https://ghostfol.io/en/demo) | [**Ghostfolio Premium**](https://ghostfol.io/en/pricing) | [**FAQ**](https://ghostfol.io/en/faq) |
[**Blog**](https://ghostfol.io/en/blog) | [**Slack**](https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg) | [**Twitter**](https://twitter.com/ghostfolio_)
[![Shield: Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-Support-yellow?logo=buymeacoffee)](https://www.buymeacoffee.com/ghostfolio)
[![Shield: Contributions Welcome](https://img.shields.io/badge/Contributions-Welcome-orange.svg)](#contributing)
[![Shield: License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
New: [Ghostfolio 2.0](https://ghostfol.io/en/blog/2023/09/ghostfolio-2)
</div>
**Ghostfolio** is an open source portfolio tracker. The software empowers busy folks to have a sharp look of their financial assets and to make solid, data-driven investment decisions by evaluating automated static portfolio analysis rules.
**Ghostfolio** is an open source wealth management software built with web technology. The application empowers busy people to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions. The software is designed for personal use in continuous operation.
<div align="center">
[<img src="./apps/client/src/assets/images/video-preview.jpg" width="600" alt="Preview image of the Ghostfolio video trailer">](https://www.youtube.com/watch?v=yY6ObSQVJZk)
</div>
## Ghostfolio Premium
Our official **[Ghostfolio Premium](https://ghostfol.io/en/pricing)** cloud offering is the easiest way to get started. Due to the time it saves, this will be the best option for most people. Revenue is used to cover the costs of the hosting infrastructure and to fund ongoing development.
If you prefer to run Ghostfolio on your own infrastructure, please find further instructions in the [Self-hosting](#self-hosting) section.
## Why Ghostfolio?
Ghostfolio is for you if you are...
- 💼 trading stocks, ETFs or cryptocurrencies on multiple platforms
- 🏦 pursuing a buy & hold strategy
- 🎯 interested in getting insights of your portfolio composition
- 👻 valuing privacy and data ownership
- 🧘 into minimalism
- 🧺 caring about diversifying your financial resources
- 🆓 interested in financial independence
- 🙅 saying no to spreadsheets in 2021
- 🙅 saying no to spreadsheets
- 😎 still reading this list
## Features
- ✅ Create, update and delete transactions
-Portfolio performance (`Today`, `YTD`, `1Y`, `5Y`, `Max`)
-Multi account management
- ✅ Portfolio performance: Time-weighted rate of return (TWR) for `Today`, `YTD`, `1Y`, `5Y`, `Max`
- ✅ Various charts
- ✅ Static analysis to identify potential risks in your portfolio
- ✅ Import and export transactions
- ✅ Dark Mode
- ✅ Zen Mode
- ✅ Progressive Web App (PWA) with a mobile-first design
<div align="center">
<img src="./apps/client/src/assets/images/screenshot.png" width="300" alt="Image of a phone showing the Ghostfolio app open">
</div>
## Technology Stack
@ -54,51 +69,219 @@ Ghostfolio is a modern web application written in [TypeScript](https://www.types
### Backend
The backend is based on [NestJS](https://nestjs.com) using [PostgreSQL](https://www.postgresql.org) as a database and [Redis](https://redis.io) for caching.
The backend is based on [NestJS](https://nestjs.com) using [PostgreSQL](https://www.postgresql.org) as a database together with [Prisma](https://www.prisma.io) and [Redis](https://redis.io) for caching.
### Frontend
The frontend is built with [Angular](https://angular.io).
The frontend is built with [Angular](https://angular.io) and uses [Angular Material](https://material.angular.io) with utility classes from [Bootstrap](https://getbootstrap.com).
## Getting Started
## Self-hosting
We provide official container images hosted on [Docker Hub](https://hub.docker.com/r/ghostfolio/ghostfolio) for `linux/amd64`, `linux/arm/v7` and `linux/arm64`.
<div align="center">
[<img src="./apps/client/src/assets/images/button-buy-me-a-coffee.png" width="150" alt="Buy me a coffee button"/>](https://www.buymeacoffee.com/ghostfolio)
</div>
### Supported Environment Variables
| Name | Default Value | Description |
| ------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `ACCESS_TOKEN_SALT` | | A random string used as salt for access tokens |
| `API_KEY_COINGECKO_DEMO` |   | The _CoinGecko_ Demo API key |
| `API_KEY_COINGECKO_PRO` |   | The _CoinGecko_ Pro API |
| `DATABASE_URL` | | The database connection URL, e.g. `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?sslmode=prefer` |
| `HOST` | `0.0.0.0` | The host where the Ghostfolio application will run on |
| `JWT_SECRET_KEY` | | A random string used for _JSON Web Tokens_ (JWT) |
| `PORT` | `3333` | The port where the Ghostfolio application will run on |
| `POSTGRES_DB` | | The name of the _PostgreSQL_ database |
| `POSTGRES_PASSWORD` | | The password of the _PostgreSQL_ database |
| `POSTGRES_USER` | | The user of the _PostgreSQL_ database |
| `REDIS_HOST` | | The host where _Redis_ is running |
| `REDIS_PASSWORD` | | The password of _Redis_ |
| `REDIS_PORT` | | The port where _Redis_ is running |
| `REQUEST_TIMEOUT` | `2000` | The timeout of network requests to data providers in milliseconds |
### Run with Docker Compose
#### Prerequisites
- Basic knowledge of Docker
- Installation of [Docker](https://www.docker.com/products/docker-desktop)
- Create a local copy of this Git repository (clone)
- Copy the file `.env.example` to `.env` and populate it with your data (`cp .env.example .env`)
#### a. Run environment
Run the following command to start the Docker images from [Docker Hub](https://hub.docker.com/r/ghostfolio/ghostfolio):
```bash
docker compose --env-file ./.env -f docker/docker-compose.yml up -d
```
#### b. Build and run environment
Run the following commands to build and start the Docker images:
```bash
docker compose --env-file ./.env -f docker/docker-compose.build.yml build
docker compose --env-file ./.env -f docker/docker-compose.build.yml up -d
```
#### Setup
1. Open http://localhost:3333 in your browser
1. Create a new user via _Get Started_ (this first user will get the role `ADMIN`)
#### Upgrade Version
1. Increase the version of the `ghostfolio/ghostfolio` Docker image in `docker/docker-compose.yml`
1. Run the following command to start the new Docker image: `docker compose --env-file ./.env -f docker/docker-compose.yml up -d`
At each start, the container will automatically apply the database schema migrations if needed.
### Home Server Systems (Community)
Ghostfolio is available for various home server systems, including [Runtipi](https://www.runtipi.io/docs/apps-available), [TrueCharts](https://truecharts.org/charts/stable/ghostfolio), [Umbrel](https://apps.umbrel.com/app/ghostfolio), and [Unraid](https://unraid.net/community/apps?q=ghostfolio).
## Development
### Prerequisites
- [Node.js](https://nodejs.org/en/download) (version 14+)
- [Yarn](https://yarnpkg.com/en/docs/install)
- [Docker](https://www.docker.com/products/docker-desktop)
- [Node.js](https://nodejs.org/en/download) (version 18+)
- [Yarn](https://yarnpkg.com/en/docs/install)
- Create a local copy of this Git repository (clone)
- Copy the file `.env.example` to `.env` and populate it with your data (`cp .env.example .env`)
### Setup
1. Run `yarn install`
1. Run `cd docker`
1. Run `docker compose up -d` to start [PostgreSQL](https://www.postgresql.org) and [Redis](https://redis.io)
1. Run `cd -` to go back to the project root directory
1. Run `yarn setup:database` to initialize the database schema and populate your database with (example) data
1. Start server and client (see [_Development_](#Development))
1. Login as _Admin_ with the following _Security Token_: `ae76872ae8f3419c6d6f64bf51888ecbcc703927a342d815fafe486acdb938da07d0cf44fca211a0be74a423238f535362d390a41e81e633a9ce668a6e31cdf9`
1. Go to the _Admin Control Panel_ and press _Gather All Data_ to fetch historical data
1. Press _Sign out_ and check out the _Live Demo_
1. Run `docker compose --env-file ./.env -f docker/docker-compose.dev.yml up -d` to start [PostgreSQL](https://www.postgresql.org) and [Redis](https://redis.io)
1. Run `yarn database:setup` to initialize the database schema
1. Run `git config core.hooksPath ./git-hooks/` to setup git hooks
1. Start the server and the client (see [_Development_](#Development))
1. Open http://localhost:4200/en in your browser
1. Create a new user via _Get Started_ (this first user will get the role `ADMIN`)
## Development
### Start Server
Please make sure you have completed the instructions from [_Setup_](#Setup)
#### Debug
### Start server
Run `yarn watch:server` and click _Debug API_ in [Visual Studio Code](https://code.visualstudio.com)
- Debug: Run `yarn watch:server` and click "Launch Program" in _Visual Studio Code_
- Serve: Run `yarn start:server`
#### Serve
### Start client
Run `yarn start:server`
- Run `yarn start:client`
### Start Client
Run `yarn start:client` and open http://localhost:4200/en in your browser
### Start _Storybook_
Run `yarn start:storybook`
### Migrate Database
With the following command you can keep your database schema in sync:
```bash
yarn database:push
```
## Testing
Run `yarn test`
## Public API
### Authorization: Bearer Token
Set the header for each request as follows:
```
"Authorization": "Bearer eyJh..."
```
You can get the _Bearer Token_ via `POST http://localhost:3333/api/v1/auth/anonymous` (Body: `{ accessToken: <INSERT_SECURITY_TOKEN_OF_ACCOUNT> }`)
Deprecated: `GET http://localhost:3333/api/v1/auth/anonymous/<INSERT_SECURITY_TOKEN_OF_ACCOUNT>` or `curl -s http://localhost:3333/api/v1/auth/anonymous/<INSERT_SECURITY_TOKEN_OF_ACCOUNT>`.
### Import Activities
#### Request
`POST http://localhost:3333/api/v1/import`
#### Body
```
{
"activities": [
{
"currency": "USD",
"dataSource": "YAHOO",
"date": "2021-09-15T00:00:00.000Z",
"fee": 19,
"quantity": 5,
"symbol": "MSFT",
"type": "BUY",
"unitPrice": 298.58
}
]
}
```
| Field | Type | Description |
| ---------- | ------------------- | ----------------------------------------------------------------------------- |
| accountId | string (`optional`) | Id of the account |
| comment | string (`optional`) | Comment of the activity |
| currency | string | `CHF` \| `EUR` \| `USD` etc. |
| dataSource | string | `COINGECKO` \| `MANUAL` (for type `ITEM`) \| `YAHOO` |
| date | string | Date in the format `ISO-8601` |
| fee | number | Fee of the activity |
| quantity | number | Quantity of the activity |
| symbol | string | Symbol of the activity (suitable for `dataSource`) |
| type | string | `BUY` \| `DIVIDEND` \| `FEE` \| `INTEREST` \| `ITEM` \| `LIABILITY` \| `SELL` |
| unitPrice | number | Price per unit of the activity |
#### Response
##### Success
`201 Created`
##### Error
`400 Bad Request`
```
{
"error": "Bad Request",
"message": [
"activities.1 is a duplicate activity"
]
}
```
## Community Projects
Discover a variety of community projects for Ghostfolio: https://github.com/topics/ghostfolio
Are you building your own project? Add the `ghostfolio` topic to your _GitHub_ repository to get listed as well. [Learn more →](https://docs.github.com/en/articles/classifying-your-repository-with-topics)
## Contributing
Ghostfolio is **100% free** and **open source**. We encourage and support an active and healthy community that accepts contributions from the public - including you.
Not sure what to work on? We have got some ideas. Please join the Ghostfolio [Slack](https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg) channel or post to [@ghostfolio\_](https://twitter.com/ghostfolio_) on _X_. We would love to hear from you.
If you like to support this project, get [**Ghostfolio Premium**](https://ghostfol.io/en/pricing) or [**Buy me a coffee**](https://www.buymeacoffee.com/ghostfolio).
## License
© 2021 [Ghostfolio](https://ghostfol.io)
© 2021 - 2024 [Ghostfolio](https://ghostfol.io)
Licensed under the [AGPLv3 License](https://www.gnu.org/licenses/agpl-3.0.html).

View File

@ -1,233 +0,0 @@
{
"version": 1,
"cli": {
"defaultCollection": "@nrwl/nest"
},
"defaultProject": "api",
"schematics": {
"@nrwl/angular:application": {
"unitTestRunner": "jest",
"e2eTestRunner": "cypress"
},
"@nrwl/angular:library": {
"unitTestRunner": "jest"
},
"@nrwl/nest": {}
},
"projects": {
"api": {
"root": "apps/api",
"sourceRoot": "apps/api/src",
"projectType": "application",
"prefix": "api",
"schematics": {},
"architect": {
"build": {
"builder": "@nrwl/node:build",
"options": {
"outputPath": "dist/apps/api",
"main": "apps/api/src/main.ts",
"tsConfig": "apps/api/tsconfig.app.json",
"assets": ["apps/api/src/assets"]
},
"configurations": {
"production": {
"optimization": true,
"extractLicenses": true,
"inspect": false,
"fileReplacements": [
{
"replace": "apps/api/src/environments/environment.ts",
"with": "apps/api/src/environments/environment.prod.ts"
}
]
}
},
"outputs": ["{options.outputPath}"]
},
"serve": {
"builder": "@nrwl/node:execute",
"options": {
"buildTarget": "api:build"
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["apps/api/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/api/jest.config.js",
"passWithNoTests": true
},
"outputs": ["coverage/apps/api"]
}
}
},
"client": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "apps/client",
"sourceRoot": "apps/client/src",
"prefix": "gf",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/client",
"index": "apps/client/src/index.html",
"main": "apps/client/src/main.ts",
"polyfills": "apps/client/src/polyfills.ts",
"tsConfig": "apps/client/tsconfig.app.json",
"aot": true,
"assets": [
"apps/client/src/assets",
{
"glob": "assetlinks.json",
"input": "apps/client/src/assets",
"output": "./.well-known"
},
{
"glob": "CHANGELOG.md",
"input": "",
"output": "./"
},
{
"glob": "LICENSE",
"input": "",
"output": "./"
},
{
"glob": "sitemap.xml",
"input": "apps/client/src/assets",
"output": "./"
},
{
"glob": "**/*",
"input": "node_modules/ionicons/dist/ionicons",
"output": "./ionicons"
},
{
"glob": "**/*.js",
"input": "node_modules/ionicons/dist/",
"output": "./"
}
],
"styles": ["apps/client/src/styles.scss"],
"scripts": ["node_modules/marked/lib/marked.js"]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/client/src/environments/environment.ts",
"with": "apps/client/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
},
"outputs": ["{options.outputPath}"]
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "client:build",
"proxyConfig": "apps/client/proxy.conf.json"
},
"configurations": {
"production": {
"browserTarget": "client:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "client:build"
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["apps/client/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/client/jest.config.js",
"passWithNoTests": true
},
"outputs": ["coverage/apps/client"]
}
}
},
"client-e2e": {
"root": "apps/client-e2e",
"sourceRoot": "apps/client-e2e/src",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/client-e2e/cypress.json",
"tsConfig": "apps/client-e2e/tsconfig.e2e.json",
"devServerTarget": "client:serve"
},
"configurations": {
"production": {
"devServerTarget": "client:serve:production"
}
}
}
}
},
"helper": {
"root": "libs/helper",
"sourceRoot": "libs/helper/src",
"projectType": "library",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/helper/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/helper"],
"options": {
"jestConfig": "libs/helper/jest.config.js",
"passWithNoTests": true
}
}
}
}
}
}

View File

@ -1,15 +0,0 @@
module.exports = {
displayName: 'api',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
}
},
transform: {
'^.+\\.[tj]s$': 'ts-jest'
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/api',
testTimeout: 10000
};

19
apps/api/jest.config.ts Normal file
View File

@ -0,0 +1,19 @@
/* eslint-disable */
export default {
displayName: 'api',
globals: {},
transform: {
'^.+\\.[tj]s$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json'
}
]
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/api',
testTimeout: 10000,
testEnvironment: 'node',
preset: '../../jest.preset.js'
};

57
apps/api/project.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "api",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/api/src",
"projectType": "application",
"prefix": "api",
"generators": {},
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"options": {
"outputPath": "dist/apps/api",
"main": "apps/api/src/main.ts",
"tsConfig": "apps/api/tsconfig.app.json",
"assets": ["apps/api/src/assets"],
"target": "node",
"compiler": "tsc",
"webpackConfig": "apps/api/webpack.config.js"
},
"configurations": {
"production": {
"generatePackageJson": true,
"optimization": true,
"extractLicenses": true,
"inspect": false,
"fileReplacements": [
{
"replace": "apps/api/src/environments/environment.ts",
"with": "apps/api/src/environments/environment.prod.ts"
}
]
}
},
"outputs": ["{options.outputPath}"]
},
"serve": {
"executor": "@nx/js:node",
"options": {
"buildTarget": "api:build"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"options": {
"lintFilePatterns": ["apps/api/**/*.ts"]
}
},
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "apps/api/jest.config.ts"
},
"outputs": ["{workspaceRoot}/coverage/apps/api"]
}
},
"tags": []
}

View File

@ -1,32 +1,113 @@
import { RequestWithUser } from '@ghostfolio/api/app/interfaces/request-with-user.type';
import { Controller, Get, Inject, UseGuards } from '@nestjs/common';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { Access } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Body,
Controller,
Delete,
Get,
HttpException,
Inject,
Param,
Post,
UseGuards
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Access as AccessModel } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { AccessService } from './access.service';
import { Access } from './interfaces/access.interface';
import { CreateAccessDto } from './create-access.dto';
@Controller('access')
export class AccessController {
public constructor(
private readonly accessService: AccessService,
private readonly configurationService: ConfigurationService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Get()
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getAllAccesses(): Promise<Access[]> {
const accessesWithGranteeUser = await this.accessService.accesses({
include: {
GranteeUser: true
},
orderBy: { granteeUserId: 'asc' },
where: { userId: this.request.user.id }
});
return accessesWithGranteeUser.map((access) => {
if (access.GranteeUser) {
return {
alias: access.alias,
grantee: access.GranteeUser?.id,
id: access.id,
type: 'RESTRICTED_VIEW'
};
}
return {
granteeAlias: access.GranteeUser.alias
alias: access.alias,
grantee: 'Public',
id: access.id,
type: 'PUBLIC'
};
});
}
@HasPermission(permissions.createAccess)
@Post()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async createAccess(
@Body() data: CreateAccessDto
): Promise<AccessModel> {
if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Basic'
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
try {
return await this.accessService.createAccess({
alias: data.alias || undefined,
GranteeUser: data.granteeUserId
? { connect: { id: data.granteeUserId } }
: undefined,
User: { connect: { id: this.request.user.id } }
});
} catch {
throw new HttpException(
getReasonPhrase(StatusCodes.BAD_REQUEST),
StatusCodes.BAD_REQUEST
);
}
}
@Delete(':id')
@HasPermission(permissions.deleteAccess)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteAccess(@Param('id') id: string): Promise<AccessModel> {
const access = await this.accessService.access({ id });
if (!access || access.userId !== this.request.user.id) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
return this.accessService.deleteAccess({
id
});
}
}

View File

@ -1,12 +1,14 @@
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common';
import { AccessController } from './access.controller';
import { AccessService } from './access.service';
@Module({
imports: [],
controllers: [AccessController],
providers: [AccessService, PrismaService]
exports: [AccessService],
imports: [ConfigurationModule, PrismaModule],
providers: [AccessService]
})
export class AccessModule {}

View File

@ -1,12 +1,22 @@
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { AccessWithGranteeUser } from '@ghostfolio/common/types';
import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { AccessWithGranteeUser } from './interfaces/access-with-grantee-user.type';
import { Access, Prisma } from '@prisma/client';
@Injectable()
export class AccessService {
public constructor(private prisma: PrismaService) {}
public constructor(private readonly prismaService: PrismaService) {}
public async access(
accessWhereInput: Prisma.AccessWhereInput
): Promise<AccessWithGranteeUser | null> {
return this.prismaService.access.findFirst({
include: {
GranteeUser: true
},
where: accessWhereInput
});
}
public async accesses(params: {
include?: Prisma.AccessInclude;
@ -14,11 +24,11 @@ export class AccessService {
take?: number;
cursor?: Prisma.AccessWhereUniqueInput;
where?: Prisma.AccessWhereInput;
orderBy?: Prisma.AccessOrderByInput;
orderBy?: Prisma.AccessOrderByWithRelationInput;
}): Promise<AccessWithGranteeUser[]> {
const { include, skip, take, cursor, where, orderBy } = params;
return this.prisma.access.findMany({
return this.prismaService.access.findMany({
cursor,
include,
orderBy,
@ -27,4 +37,18 @@ export class AccessService {
where
});
}
public async createAccess(data: Prisma.AccessCreateInput): Promise<Access> {
return this.prismaService.access.create({
data
});
}
public async deleteAccess(
where: Prisma.AccessWhereUniqueInput
): Promise<Access> {
return this.prismaService.access.delete({
where
});
}
}

View File

@ -0,0 +1,15 @@
import { IsOptional, IsString, IsUUID } from 'class-validator';
export class CreateAccessDto {
@IsOptional()
@IsString()
alias?: string;
@IsOptional()
@IsUUID()
granteeUserId?: string;
@IsOptional()
@IsString()
type?: 'PUBLIC';
}

View File

@ -1,3 +0,0 @@
export interface Access {
granteeAlias: string;
}

View File

@ -0,0 +1,48 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Controller,
Delete,
HttpException,
Inject,
Param,
UseGuards
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { AccountBalance } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { AccountBalanceService } from './account-balance.service';
@Controller('account-balance')
export class AccountBalanceController {
public constructor(
private readonly accountBalanceService: AccountBalanceService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@HasPermission(permissions.deleteAccountBalance)
@Delete(':id')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteAccountBalance(
@Param('id') id: string
): Promise<AccountBalance> {
const accountBalance = await this.accountBalanceService.accountBalance({
id
});
if (!accountBalance || accountBalance.userId !== this.request.user.id) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
return this.accountBalanceService.deleteAccountBalance({
id
});
}
}

View File

@ -0,0 +1,14 @@
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common';
import { AccountBalanceController } from './account-balance.controller';
import { AccountBalanceService } from './account-balance.service';
@Module({
controllers: [AccountBalanceController],
exports: [AccountBalanceService],
imports: [ExchangeRateDataModule, PrismaModule],
providers: [AccountBalanceService]
})
export class AccountBalanceModule {}

View File

@ -0,0 +1,91 @@
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { AccountBalancesResponse, Filter } from '@ghostfolio/common/interfaces';
import { UserWithSettings } from '@ghostfolio/common/types';
import { Injectable } from '@nestjs/common';
import { AccountBalance, Prisma } from '@prisma/client';
@Injectable()
export class AccountBalanceService {
public constructor(
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly prismaService: PrismaService
) {}
public async accountBalance(
accountBalanceWhereInput: Prisma.AccountBalanceWhereInput
): Promise<AccountBalance | null> {
return this.prismaService.accountBalance.findFirst({
include: {
Account: true
},
where: accountBalanceWhereInput
});
}
public async createAccountBalance(
data: Prisma.AccountBalanceCreateInput
): Promise<AccountBalance> {
return this.prismaService.accountBalance.create({
data
});
}
public async deleteAccountBalance(
where: Prisma.AccountBalanceWhereUniqueInput
): Promise<AccountBalance> {
return this.prismaService.accountBalance.delete({
where
});
}
public async getAccountBalances({
filters,
user,
withExcludedAccounts
}: {
filters?: Filter[];
user: UserWithSettings;
withExcludedAccounts?: boolean;
}): Promise<AccountBalancesResponse> {
const where: Prisma.AccountBalanceWhereInput = { userId: user.id };
const accountFilter = filters?.find(({ type }) => {
return type === 'ACCOUNT';
});
if (accountFilter) {
where.accountId = accountFilter.id;
}
if (withExcludedAccounts === false) {
where.Account = { isExcluded: false };
}
const balances = await this.prismaService.accountBalance.findMany({
where,
orderBy: {
date: 'asc'
},
select: {
Account: true,
date: true,
id: true,
value: true
}
});
return {
balances: balances.map((balance) => {
return {
...balance,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
balance.value,
balance.Account.currency,
user.Settings.settings.baseCurrency
)
};
})
};
}
}

View File

@ -1,7 +1,19 @@
import { RequestWithUser } from '@ghostfolio/api/app/interfaces/request-with-user.type';
import { nullifyValuesInObjects } from '@ghostfolio/api/helper/object.helper';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service';
import { getPermissions, hasPermission, permissions } from '@ghostfolio/helper';
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { RedactValuesInResponseInterceptor } from '@ghostfolio/api/interceptors/redact-values-in-response.interceptor';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config';
import {
AccountBalancesResponse,
Accounts
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import type {
AccountWithValue,
RequestWithUser
} from '@ghostfolio/common/types';
import {
Body,
Controller,
@ -13,40 +25,33 @@ import {
Param,
Post,
Put,
UseGuards
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Account as AccountModel, Order } from '@prisma/client';
import { Account as AccountModel } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { AccountService } from './account.service';
import { CreateAccountDto } from './create-account.dto';
import { TransferBalanceDto } from './transfer-balance.dto';
import { UpdateAccountDto } from './update-account.dto';
@Controller('account')
export class AccountController {
public constructor(
private readonly accountBalanceService: AccountBalanceService,
private readonly accountService: AccountService,
private readonly impersonationService: ImpersonationService,
private readonly portfolioService: PortfolioService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Delete(':id')
@UseGuards(AuthGuard('jwt'))
@HasPermission(permissions.deleteAccount)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteAccount(@Param('id') id: string): Promise<AccountModel> {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.deleteAccount
)
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
const account = await this.accountService.accountWithOrders(
{
id_userId: {
@ -76,66 +81,58 @@ export class AccountController {
}
@Get()
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor)
public async getAllAccounts(
@Headers('impersonation-id') impersonationId
): Promise<AccountModel[]> {
const impersonationUserId = await this.impersonationService.validateImpersonationId(
impersonationId,
this.request.user.id
);
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId
): Promise<Accounts> {
const impersonationUserId =
await this.impersonationService.validateImpersonationId(impersonationId);
let accounts = await this.accountService.accounts({
include: { Order: true, Platform: true },
orderBy: { name: 'asc' },
where: { userId: impersonationUserId || this.request.user.id }
return this.portfolioService.getAccountsWithAggregations({
userId: impersonationUserId || this.request.user.id,
withExcludedAccounts: true
});
if (
impersonationUserId &&
!hasPermission(
getPermissions(this.request.user.role),
permissions.readForeignPortfolio
)
) {
accounts = nullifyValuesInObjects(accounts, [
'fee',
'quantity',
'unitPrice'
]);
}
return accounts;
}
@Get(':id')
@UseGuards(AuthGuard('jwt'))
public async getAccountById(@Param('id') id: string): Promise<AccountModel> {
return this.accountService.account({
id_userId: {
id,
userId: this.request.user.id
}
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor)
public async getAccountById(
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId,
@Param('id') id: string
): Promise<AccountWithValue> {
const impersonationUserId =
await this.impersonationService.validateImpersonationId(impersonationId);
const accountsWithAggregations =
await this.portfolioService.getAccountsWithAggregations({
filters: [{ id, type: 'ACCOUNT' }],
userId: impersonationUserId || this.request.user.id,
withExcludedAccounts: true
});
return accountsWithAggregations.accounts[0];
}
@Get(':id/balances')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor)
public async getAccountBalancesById(
@Param('id') id: string
): Promise<AccountBalancesResponse> {
return this.accountBalanceService.getAccountBalances({
filters: [{ id, type: 'ACCOUNT' }],
user: this.request.user
});
}
@HasPermission(permissions.createAccount)
@Post()
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async createAccount(
@Body() data: CreateAccountDto
): Promise<AccountModel> {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.createAccount
)
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
if (data.platformId) {
const platformId = data.platformId;
delete data.platformId;
@ -161,21 +158,64 @@ export class AccountController {
}
}
@Put(':id')
@UseGuards(AuthGuard('jwt'))
public async update(@Param('id') id: string, @Body() data: UpdateAccountDto) {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.updateAccount
)
) {
@HasPermission(permissions.updateAccount)
@Post('transfer-balance')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async transferAccountBalance(
@Body() { accountIdFrom, accountIdTo, balance }: TransferBalanceDto
) {
const accountsOfUser = await this.accountService.getAccounts(
this.request.user.id
);
const accountFrom = accountsOfUser.find(({ id }) => {
return id === accountIdFrom;
});
const accountTo = accountsOfUser.find(({ id }) => {
return id === accountIdTo;
});
if (!accountFrom || !accountTo) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
if (accountFrom.id === accountTo.id) {
throw new HttpException(
getReasonPhrase(StatusCodes.BAD_REQUEST),
StatusCodes.BAD_REQUEST
);
}
if (accountFrom.balance < balance) {
throw new HttpException(
getReasonPhrase(StatusCodes.BAD_REQUEST),
StatusCodes.BAD_REQUEST
);
}
await this.accountService.updateAccountBalance({
accountId: accountFrom.id,
amount: -balance,
currency: accountFrom.currency,
userId: this.request.user.id
});
await this.accountService.updateAccountBalance({
accountId: accountTo.id,
amount: balance,
currency: accountFrom.currency,
userId: this.request.user.id
});
}
@HasPermission(permissions.updateAccount)
@Put(':id')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async update(@Param('id') id: string, @Body() data: UpdateAccountDto) {
const originalAccount = await this.accountService.account({
id_userId: {
id,

View File

@ -1,30 +1,31 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service';
import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { AccountBalanceModule } from '@ghostfolio/api/app/account-balance/account-balance.module';
import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common';
import { RedisCacheModule } from '../redis-cache/redis-cache.module';
import { AccountController } from './account.controller';
import { AccountService } from './account.service';
@Module({
imports: [RedisCacheModule],
controllers: [AccountController],
providers: [
AccountService,
AlphaVantageService,
ConfigurationService,
DataProviderService,
GhostfolioScraperApiService,
ImpersonationService,
PrismaService,
RakutenRapidApiService,
YahooFinanceService
]
exports: [AccountService],
imports: [
AccountBalanceModule,
ConfigurationModule,
DataProviderModule,
ExchangeRateDataModule,
ImpersonationModule,
PortfolioModule,
PrismaModule,
RedisCacheModule,
UserModule
],
providers: [AccountService]
})
export class AccountModule {}

View File

@ -1,22 +1,32 @@
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { Filter } from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common';
import { Account, Order, Prisma } from '@prisma/client';
import { Account, Order, Platform, Prisma } from '@prisma/client';
import Big from 'big.js';
import { groupBy } from 'lodash';
import { RedisCacheService } from '../redis-cache/redis-cache.service';
import { CashDetails } from './interfaces/cash-details.interface';
@Injectable()
export class AccountService {
public constructor(
private readonly redisCacheService: RedisCacheService,
private prisma: PrismaService
private readonly accountBalanceService: AccountBalanceService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly prismaService: PrismaService
) {}
public async account(
accountWhereUniqueInput: Prisma.AccountWhereUniqueInput
): Promise<Account | null> {
return this.prisma.account.findUnique({
where: accountWhereUniqueInput
public async account({
id_userId
}: Prisma.AccountWhereUniqueInput): Promise<Account | null> {
const { id, userId } = id_userId;
const [account] = await this.accounts({
where: { id, userId }
});
return account;
}
public async accountWithOrders(
@ -27,7 +37,7 @@ export class AccountService {
Order?: Order[];
}
> {
return this.prisma.account.findUnique({
return this.prismaService.account.findUnique({
include: accountInclude,
where: accountWhereUniqueInput
});
@ -39,11 +49,18 @@ export class AccountService {
take?: number;
cursor?: Prisma.AccountWhereUniqueInput;
where?: Prisma.AccountWhereInput;
orderBy?: Prisma.AccountOrderByInput;
}): Promise<Account[]> {
const { include, skip, take, cursor, where, orderBy } = params;
orderBy?: Prisma.AccountOrderByWithRelationInput;
}): Promise<
(Account & {
Order?: Order[];
Platform?: Platform;
})[]
> {
const { include = {}, skip, take, cursor, where, orderBy } = params;
return this.prisma.account.findMany({
include.balances = { orderBy: { date: 'desc' }, take: 1 };
const accounts = await this.prismaService.account.findMany({
cursor,
include,
orderBy,
@ -51,28 +68,126 @@ export class AccountService {
take,
where
});
return accounts.map((account) => {
account = { ...account, balance: account.balances[0]?.value ?? 0 };
delete account.balances;
return account;
});
}
public async createAccount(
data: Prisma.AccountCreateInput,
aUserId: string
): Promise<Account> {
return this.prisma.account.create({
const account = await this.prismaService.account.create({
data
});
await this.prismaService.accountBalance.create({
data: {
Account: {
connect: {
id_userId: { id: account.id, userId: aUserId }
}
},
value: data.balance
}
});
return account;
}
public async deleteAccount(
where: Prisma.AccountWhereUniqueInput,
aUserId: string
): Promise<Account> {
this.redisCacheService.remove(`${aUserId}.portfolio`);
return this.prisma.account.delete({
return this.prismaService.account.delete({
where
});
}
public async getAccounts(aUserId: string): Promise<Account[]> {
const accounts = await this.accounts({
include: { Order: true, Platform: true },
orderBy: { name: 'asc' },
where: { userId: aUserId }
});
return accounts.map((account) => {
let transactionCount = 0;
for (const order of account.Order) {
if (!order.isDraft) {
transactionCount += 1;
}
}
const result = { ...account, transactionCount };
delete result.Order;
return result;
});
}
public async getCashDetails({
currency,
filters = [],
userId,
withExcludedAccounts = false
}: {
currency: string;
filters?: Filter[];
userId: string;
withExcludedAccounts?: boolean;
}): Promise<CashDetails> {
let totalCashBalanceInBaseCurrency = new Big(0);
const where: Prisma.AccountWhereInput = {
userId
};
if (withExcludedAccounts === false) {
where.isExcluded = false;
}
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
});
if (filtersByAccount?.length > 0) {
where.id = {
in: filtersByAccount.map(({ id }) => {
return id;
})
};
}
const accounts = await this.accounts({ where });
for (const account of accounts) {
totalCashBalanceInBaseCurrency = totalCashBalanceInBaseCurrency.plus(
this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
currency
)
);
}
return {
accounts,
balanceInBaseCurrency: totalCashBalanceInBaseCurrency.toNumber()
};
}
public async updateAccount(
params: {
where: Prisma.AccountWhereUniqueInput;
@ -81,9 +196,65 @@ export class AccountService {
aUserId: string
): Promise<Account> {
const { data, where } = params;
return this.prisma.account.update({
await this.prismaService.accountBalance.create({
data: {
Account: {
connect: {
id_userId: where.id_userId
}
},
value: <number>data.balance
}
});
return this.prismaService.account.update({
data,
where
});
}
public async updateAccountBalance({
accountId,
amount,
currency,
date = new Date(),
userId
}: {
accountId: string;
amount: number;
currency: string;
date?: Date;
userId: string;
}) {
const { balance, currency: currencyOfAccount } = await this.account({
id_userId: {
userId,
id: accountId
}
});
const amountInCurrencyOfAccount =
await this.exchangeRateDataService.toCurrencyAtDate(
amount,
currency,
currencyOfAccount,
date
);
if (amountInCurrencyOfAccount) {
await this.accountBalanceService.createAccountBalance({
date,
Account: {
connect: {
id_userId: {
userId,
id: accountId
}
}
},
value: new Big(balance).plus(amountInCurrencyOfAccount).toNumber()
});
}
}
}

View File

@ -1,9 +1,34 @@
import { AccountType } from '@prisma/client';
import { IsString, ValidateIf } from 'class-validator';
import { Transform, TransformFnParams } from 'class-transformer';
import {
IsBoolean,
IsNumber,
IsOptional,
IsString,
ValidateIf
} from 'class-validator';
import { isString } from 'lodash';
export class CreateAccountDto {
@IsNumber()
balance: number;
@IsOptional()
@IsString()
accountType: AccountType;
@Transform(({ value }: TransformFnParams) =>
isString(value) ? value.trim() : value
)
comment?: string;
@IsString()
currency: string;
@IsOptional()
@IsString()
id?: string;
@IsBoolean()
@IsOptional()
isExcluded?: boolean;
@IsString()
name: string;

View File

@ -0,0 +1,6 @@
import { Account } from '@prisma/client';
export interface CashDetails {
accounts: Account[];
balanceInBaseCurrency: number;
}

View File

@ -0,0 +1,13 @@
import { IsNumber, IsPositive, IsString } from 'class-validator';
export class TransferBalanceDto {
@IsString()
accountIdFrom: string;
@IsString()
accountIdTo: string;
@IsNumber()
@IsPositive()
balance: number;
}

View File

@ -1,13 +1,34 @@
import { AccountType } from '@prisma/client';
import { IsString, ValidateIf } from 'class-validator';
import { Transform, TransformFnParams } from 'class-transformer';
import {
IsBoolean,
IsNumber,
IsOptional,
IsString,
ValidateIf
} from 'class-validator';
import { isString } from 'lodash';
export class UpdateAccountDto {
@IsNumber()
balance: number;
@IsOptional()
@IsString()
accountType: AccountType;
@Transform(({ value }: TransformFnParams) =>
isString(value) ? value.trim() : value
)
comment?: string;
@IsString()
currency: string;
@IsString()
id: string;
@IsBoolean()
@IsOptional()
isExcluded?: boolean;
@IsString()
name: string;

View File

@ -1,64 +1,343 @@
import { RequestWithUser } from '@ghostfolio/api/app/interfaces/request-with-user.type';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { getPermissions, hasPermission, permissions } from '@ghostfolio/helper';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import { ApiService } from '@ghostfolio/api/services/api/api.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering/data-gathering.service';
import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
import {
GATHER_ASSET_PROFILE_PROCESS,
GATHER_ASSET_PROFILE_PROCESS_OPTIONS
} from '@ghostfolio/common/config';
import {
getAssetProfileIdentifier,
resetHours
} from '@ghostfolio/common/helper';
import {
AdminData,
AdminMarketData,
AdminMarketDataDetails,
EnhancedSymbolProfile
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import type {
MarketDataPreset,
RequestWithUser
} from '@ghostfolio/common/types';
import {
Body,
Controller,
Delete,
Get,
HttpException,
Inject,
Logger,
Param,
Patch,
Post,
UseGuards
Put,
Query,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { DataSource, MarketData, Prisma, SymbolProfile } from '@prisma/client';
import { isDate, parseISO } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { AdminService } from './admin.service';
import { AdminData } from './interfaces/admin-data.interface';
import { UpdateAssetProfileDto } from './update-asset-profile.dto';
import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto';
import { UpdateMarketDataDto } from './update-market-data.dto';
@Controller('admin')
export class AdminController {
public constructor(
private readonly adminService: AdminService,
private readonly apiService: ApiService,
private readonly dataGatheringService: DataGatheringService,
private readonly manualService: ManualService,
private readonly marketDataService: MarketDataService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Get()
@UseGuards(AuthGuard('jwt'))
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getAdminData(): Promise<AdminData> {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.accessAdminControl
)
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
return this.adminService.get();
}
@HasPermission(permissions.accessAdminControl)
@Post('gather')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async gather7Days(): Promise<void> {
this.dataGatheringService.gather7Days();
}
@HasPermission(permissions.accessAdminControl)
@Post('gather/max')
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async gatherMax(): Promise<void> {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.accessAdminControl
)
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
const uniqueAssets = await this.dataGatheringService.getUniqueAssets();
await this.dataGatheringService.addJobsToQueue(
uniqueAssets.map(({ dataSource, symbol }) => {
return {
data: {
dataSource,
symbol
},
name: GATHER_ASSET_PROFILE_PROCESS,
opts: {
...GATHER_ASSET_PROFILE_PROCESS_OPTIONS,
jobId: getAssetProfileIdentifier({ dataSource, symbol })
}
};
})
);
this.dataGatheringService.gatherMax();
}
@HasPermission(permissions.accessAdminControl)
@Post('gather/profile-data')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async gatherProfileData(): Promise<void> {
const uniqueAssets = await this.dataGatheringService.getUniqueAssets();
await this.dataGatheringService.addJobsToQueue(
uniqueAssets.map(({ dataSource, symbol }) => {
return {
data: {
dataSource,
symbol
},
name: GATHER_ASSET_PROFILE_PROCESS,
opts: {
...GATHER_ASSET_PROFILE_PROCESS_OPTIONS,
jobId: getAssetProfileIdentifier({ dataSource, symbol })
}
};
})
);
}
@HasPermission(permissions.accessAdminControl)
@Post('gather/profile-data/:dataSource/:symbol')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async gatherProfileDataForSymbol(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<void> {
await this.dataGatheringService.addJobToQueue({
data: {
dataSource,
symbol
},
name: GATHER_ASSET_PROFILE_PROCESS,
opts: {
...GATHER_ASSET_PROFILE_PROCESS_OPTIONS,
jobId: getAssetProfileIdentifier({ dataSource, symbol })
}
});
}
@Post('gather/:dataSource/:symbol')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@HasPermission(permissions.accessAdminControl)
public async gatherSymbol(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<void> {
this.dataGatheringService.gatherSymbol({ dataSource, symbol });
return;
}
@HasPermission(permissions.accessAdminControl)
@Post('gather/:dataSource/:symbol/:dateString')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async gatherSymbolForDate(
@Param('dataSource') dataSource: DataSource,
@Param('dateString') dateString: string,
@Param('symbol') symbol: string
): Promise<MarketData> {
const date = parseISO(dateString);
if (!isDate(date)) {
throw new HttpException(
getReasonPhrase(StatusCodes.BAD_REQUEST),
StatusCodes.BAD_REQUEST
);
}
return this.dataGatheringService.gatherSymbolForDate({
dataSource,
date,
symbol
});
}
@Get('market-data')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getMarketData(
@Query('assetSubClasses') filterByAssetSubClasses?: string,
@Query('presetId') presetId?: MarketDataPreset,
@Query('query') filterBySearchQuery?: string,
@Query('skip') skip?: number,
@Query('sortColumn') sortColumn?: string,
@Query('sortDirection') sortDirection?: Prisma.SortOrder,
@Query('take') take?: number
): Promise<AdminMarketData> {
const filters = this.apiService.buildFiltersFromQueryParams({
filterByAssetSubClasses,
filterBySearchQuery
});
return this.adminService.getMarketData({
filters,
presetId,
sortColumn,
sortDirection,
skip: isNaN(skip) ? undefined : skip,
take: isNaN(take) ? undefined : take
});
}
@Get('market-data/:dataSource/:symbol')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getMarketDataBySymbol(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<AdminMarketDataDetails> {
return this.adminService.getMarketDataBySymbol({ dataSource, symbol });
}
@HasPermission(permissions.accessAdminControl)
@Post('market-data/:dataSource/:symbol/test')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async testMarketData(
@Body() data: { scraperConfiguration: string },
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<{ price: number }> {
try {
const scraperConfiguration = JSON.parse(data.scraperConfiguration);
const price = await this.manualService.test(scraperConfiguration);
if (price) {
return { price };
}
throw new Error('Could not parse the current market price');
} catch (error) {
Logger.error(error);
throw new HttpException(error.message, StatusCodes.BAD_REQUEST);
}
}
@HasPermission(permissions.accessAdminControl)
@Post('market-data/:dataSource/:symbol')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async updateMarketData(
@Body() data: UpdateBulkMarketDataDto,
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
) {
const dataBulkUpdate: Prisma.MarketDataUpdateInput[] = data.marketData.map(
({ date, marketPrice }) => ({
dataSource,
marketPrice,
symbol,
date: resetHours(parseISO(date)),
state: 'CLOSE'
})
);
return this.marketDataService.updateMany({
data: dataBulkUpdate
});
}
/**
* @deprecated
*/
@HasPermission(permissions.accessAdminControl)
@Put('market-data/:dataSource/:symbol/:dateString')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async update(
@Param('dataSource') dataSource: DataSource,
@Param('dateString') dateString: string,
@Param('symbol') symbol: string,
@Body() data: UpdateMarketDataDto
) {
const date = parseISO(dateString);
return this.marketDataService.updateMarketData({
data: { marketPrice: data.marketPrice, state: 'CLOSE' },
where: {
dataSource_date_symbol: {
dataSource,
date,
symbol
}
}
});
}
@HasPermission(permissions.accessAdminControl)
@Post('profile-data/:dataSource/:symbol')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async addProfileData(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<SymbolProfile | never> {
return this.adminService.addAssetProfile({
dataSource,
symbol,
currency: this.request.user.Settings.settings.baseCurrency
});
}
@Delete('profile-data/:dataSource/:symbol')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteProfileData(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<void> {
return this.adminService.deleteProfileData({ dataSource, symbol });
}
@HasPermission(permissions.accessAdminControl)
@Patch('profile-data/:dataSource/:symbol')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async patchAssetProfileData(
@Body() assetProfileData: UpdateAssetProfileDto,
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<EnhancedSymbolProfile> {
return this.adminService.patchAssetProfileData({
...assetProfileData,
dataSource,
symbol
});
}
@HasPermission(permissions.accessAdminControl)
@Put('settings/:key')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async updateProperty(
@Param('key') key: string,
@Body() data: PropertyDto
) {
return await this.adminService.putSetting(key, data.value);
}
}

View File

@ -1,31 +1,35 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service';
import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module';
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { Module } from '@nestjs/common';
import { AdminController } from './admin.controller';
import { AdminService } from './admin.service';
import { QueueModule } from './queue/queue.module';
@Module({
imports: [],
imports: [
ApiModule,
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
ExchangeRateDataModule,
MarketDataModule,
PrismaModule,
PropertyModule,
QueueModule,
SubscriptionModule,
SymbolProfileModule
],
controllers: [AdminController],
providers: [
AdminService,
AlphaVantageService,
ConfigurationService,
DataGatheringService,
DataProviderService,
ExchangeRateDataService,
GhostfolioScraperApiService,
PrismaService,
RakutenRapidApiService,
YahooFinanceService
]
providers: [AdminService],
exports: [AdminService]
})
export class AdminModule {}

View File

@ -1,120 +1,470 @@
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Injectable } from '@nestjs/common';
import { Currency } from '@prisma/client';
import { AdminData } from './interfaces/admin-data.interface';
import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service';
import { environment } from '@ghostfolio/api/environments/environment';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import {
DEFAULT_CURRENCY,
PROPERTY_CURRENCIES,
PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_IS_USER_SIGNUP_ENABLED
} from '@ghostfolio/common/config';
import {
AdminData,
AdminMarketData,
AdminMarketDataDetails,
AdminMarketDataItem,
Filter,
UniqueAsset
} from '@ghostfolio/common/interfaces';
import { MarketDataPreset } from '@ghostfolio/common/types';
import { BadRequestException, Injectable } from '@nestjs/common';
import {
AssetSubClass,
DataSource,
Prisma,
Property,
SymbolProfile
} from '@prisma/client';
import { differenceInDays } from 'date-fns';
import { groupBy } from 'lodash';
@Injectable()
export class AdminService {
public constructor(
private exchangeRateDataService: ExchangeRateDataService,
private prisma: PrismaService
private readonly configurationService: ConfigurationService,
private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly marketDataService: MarketDataService,
private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService,
private readonly subscriptionService: SubscriptionService,
private readonly symbolProfileService: SymbolProfileService
) {}
public async addAssetProfile({
currency,
dataSource,
symbol
}: UniqueAsset & { currency?: string }): Promise<SymbolProfile | never> {
try {
if (dataSource === 'MANUAL') {
return this.symbolProfileService.add({
currency,
dataSource,
symbol
});
}
const assetProfiles = await this.dataProviderService.getAssetProfiles([
{ dataSource, symbol }
]);
if (!assetProfiles[symbol]?.currency) {
throw new BadRequestException(
`Asset profile not found for ${symbol} (${dataSource})`
);
}
return await this.symbolProfileService.add(
assetProfiles[symbol] as Prisma.SymbolProfileCreateInput
);
} catch (error) {
if (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === 'P2002'
) {
throw new BadRequestException(
`Asset profile of ${symbol} (${dataSource}) already exists`
);
}
throw error;
}
}
public async deleteProfileData({ dataSource, symbol }: UniqueAsset) {
await this.marketDataService.deleteMany({ dataSource, symbol });
await this.symbolProfileService.delete({ dataSource, symbol });
}
public async get(): Promise<AdminData> {
return {
exchangeRates: [
{
label1: Currency.EUR,
label2: Currency.CHF,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.EUR,
Currency.CHF
)
},
{
label1: Currency.GBP,
label2: Currency.CHF,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.GBP,
Currency.CHF
)
},
{
label1: Currency.USD,
label2: Currency.CHF,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.USD,
Currency.CHF
)
},
{
label1: Currency.USD,
label2: Currency.EUR,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.USD,
Currency.EUR
)
},
{
label1: Currency.USD,
label2: Currency.GBP,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.USD,
Currency.GBP
)
}
],
lastDataGathering: await this.getLastDataGathering(),
transactionCount: await this.prisma.order.count(),
userCount: await this.prisma.user.count(),
users: await this.getUsersWithAnalytics()
exchangeRates: this.exchangeRateDataService
.getCurrencies()
.filter((currency) => {
return currency !== DEFAULT_CURRENCY;
})
.map((currency) => {
const label1 = DEFAULT_CURRENCY;
const label2 = currency;
return {
label1,
label2,
dataSource:
DataSource[
this.configurationService.get('DATA_SOURCE_EXCHANGE_RATES')
],
symbol: `${label1}${label2}`,
value: this.exchangeRateDataService.toCurrency(
1,
DEFAULT_CURRENCY,
currency
)
};
}),
settings: await this.propertyService.get(),
transactionCount: await this.prismaService.order.count(),
userCount: await this.prismaService.user.count(),
users: await this.getUsersWithAnalytics(),
version: environment.version
};
}
private async getLastDataGathering() {
const lastDataGathering = await this.prisma.property.findUnique({
where: { key: 'LAST_DATA_GATHERING' }
});
public async getMarketData({
filters,
presetId,
sortColumn,
sortDirection,
skip,
take = Number.MAX_SAFE_INTEGER
}: {
filters?: Filter[];
presetId?: MarketDataPreset;
skip?: number;
sortColumn?: string;
sortDirection?: Prisma.SortOrder;
take?: number;
}): Promise<AdminMarketData> {
let orderBy: Prisma.Enumerable<Prisma.SymbolProfileOrderByWithRelationInput> =
[{ symbol: 'asc' }];
const where: Prisma.SymbolProfileWhereInput = {};
if (lastDataGathering?.value) {
return new Date(lastDataGathering.value);
if (presetId === 'CURRENCIES') {
return this.getMarketDataForCurrencies();
} else if (
presetId === 'ETF_WITHOUT_COUNTRIES' ||
presetId === 'ETF_WITHOUT_SECTORS'
) {
filters = [{ id: 'ETF', type: 'ASSET_SUB_CLASS' }];
}
const dataGatheringInProgress = await this.prisma.property.findUnique({
where: { key: 'LOCKED_DATA_GATHERING' }
const searchQuery = filters.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
const { ASSET_SUB_CLASS: filtersByAssetSubClass } = groupBy(
filters,
({ type }) => {
return type;
}
);
const marketDataItems = await this.prismaService.marketData.groupBy({
_count: true,
by: ['dataSource', 'symbol']
});
if (dataGatheringInProgress) {
return 'IN_PROGRESS';
if (filtersByAssetSubClass) {
where.assetSubClass = AssetSubClass[filtersByAssetSubClass[0].id];
}
return null;
if (searchQuery) {
where.OR = [
{ id: { mode: 'insensitive', startsWith: searchQuery } },
{ isin: { mode: 'insensitive', startsWith: searchQuery } },
{ name: { mode: 'insensitive', startsWith: searchQuery } },
{ symbol: { mode: 'insensitive', startsWith: searchQuery } }
];
}
if (sortColumn) {
orderBy = [{ [sortColumn]: sortDirection }];
if (sortColumn === 'activitiesCount') {
orderBy = {
Order: {
_count: sortDirection
}
};
}
}
let [assetProfiles, count] = await Promise.all([
this.prismaService.symbolProfile.findMany({
orderBy,
skip,
take,
where,
select: {
_count: {
select: { Order: true }
},
assetClass: true,
assetSubClass: true,
comment: true,
countries: true,
currency: true,
dataSource: true,
name: true,
Order: {
orderBy: [{ date: 'asc' }],
select: { date: true },
take: 1
},
scraperConfiguration: true,
sectors: true,
symbol: true
}
}),
this.prismaService.symbolProfile.count({ where })
]);
let marketData = assetProfiles.map(
({
_count,
assetClass,
assetSubClass,
comment,
countries,
currency,
dataSource,
name,
Order,
sectors,
symbol
}) => {
const countriesCount = countries ? Object.keys(countries).length : 0;
const marketDataItemCount =
marketDataItems.find((marketDataItem) => {
return (
marketDataItem.dataSource === dataSource &&
marketDataItem.symbol === symbol
);
})?._count ?? 0;
const sectorsCount = sectors ? Object.keys(sectors).length : 0;
return {
assetClass,
assetSubClass,
comment,
currency,
countriesCount,
dataSource,
name,
symbol,
marketDataItemCount,
sectorsCount,
activitiesCount: _count.Order,
date: Order?.[0]?.date
};
}
);
if (presetId) {
if (presetId === 'ETF_WITHOUT_COUNTRIES') {
marketData = marketData.filter(({ countriesCount }) => {
return countriesCount === 0;
});
} else if (presetId === 'ETF_WITHOUT_SECTORS') {
marketData = marketData.filter(({ sectorsCount }) => {
return sectorsCount === 0;
});
}
count = marketData.length;
}
return {
count,
marketData
};
}
private async getUsersWithAnalytics() {
return await this.prisma.user.findMany({
orderBy: {
public async getMarketDataBySymbol({
dataSource,
symbol
}: UniqueAsset): Promise<AdminMarketDataDetails> {
const [[assetProfile], marketData] = await Promise.all([
this.symbolProfileService.getSymbolProfiles([
{
dataSource,
symbol
}
]),
this.marketDataService.marketDataItems({
orderBy: {
date: 'asc'
},
where: {
dataSource,
symbol
}
})
]);
return {
marketData,
assetProfile: assetProfile ?? {
symbol,
currency: '-'
}
};
}
public async patchAssetProfileData({
assetClass,
assetSubClass,
comment,
currency,
dataSource,
name,
scraperConfiguration,
symbol,
symbolMapping
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
await this.symbolProfileService.updateSymbolProfile({
assetClass,
assetSubClass,
comment,
currency,
dataSource,
name,
scraperConfiguration,
symbol,
symbolMapping
});
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles([
{
dataSource,
symbol
}
]);
return symbolProfile;
}
public async putSetting(key: string, value: string) {
let response: Property;
if (value) {
response = await this.propertyService.put({ key, value });
} else {
response = await this.propertyService.delete({ key });
}
if (key === PROPERTY_IS_READ_ONLY_MODE && value === 'true') {
await this.putSetting(PROPERTY_IS_USER_SIGNUP_ENABLED, 'false');
} else if (key === PROPERTY_CURRENCIES) {
await this.exchangeRateDataService.initialize();
}
return response;
}
private async getMarketDataForCurrencies(): Promise<AdminMarketData> {
const marketDataItems = await this.prismaService.marketData.groupBy({
_count: true,
by: ['dataSource', 'symbol']
});
const marketData: AdminMarketDataItem[] = this.exchangeRateDataService
.getCurrencyPairs()
.map(({ dataSource, symbol }) => {
const marketDataItemCount =
marketDataItems.find((marketDataItem) => {
return (
marketDataItem.dataSource === dataSource &&
marketDataItem.symbol === symbol
);
})?._count ?? 0;
return {
dataSource,
marketDataItemCount,
symbol,
assetClass: 'CASH',
countriesCount: 0,
currency: symbol.replace(DEFAULT_CURRENCY, ''),
name: symbol,
sectorsCount: 0
};
});
return { marketData, count: marketData.length };
}
private async getUsersWithAnalytics(): Promise<AdminData['users']> {
let orderBy: any = {
createdAt: 'desc'
};
let where;
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
orderBy = {
Analytics: {
updatedAt: 'desc'
}
},
};
where = {
NOT: {
Analytics: null
}
};
}
const usersWithAnalytics = await this.prismaService.user.findMany({
orderBy,
where,
select: {
_count: {
select: { Account: true, Order: true }
},
alias: true,
Analytics: {
select: {
activityCount: true,
country: true,
updatedAt: true
}
},
createdAt: true,
id: true
id: true,
Subscription: true
},
take: 20,
where: {
NOT: {
Analytics: null
}
}
take: 30
});
return usersWithAnalytics.map(
({ _count, Analytics, createdAt, id, Subscription }) => {
const daysSinceRegistration =
differenceInDays(new Date(), createdAt) + 1;
const engagement = Analytics
? Analytics.activityCount / daysSinceRegistration
: undefined;
const subscription = this.configurationService.get(
'ENABLE_FEATURE_SUBSCRIPTION'
)
? this.subscriptionService.getSubscription(Subscription)
: undefined;
return {
createdAt,
engagement,
id,
subscription,
accountCount: _count.Account || 0,
country: Analytics?.country,
lastActivity: Analytics?.updatedAt,
transactionCount: _count.Order || 0
};
}
);
}
}

View File

@ -1,15 +0,0 @@
export interface AdminData {
exchangeRates: { label1: string; label2: string; value: number }[];
lastDataGathering: Date | 'IN_PROGRESS';
transactionCount: number;
userCount: number;
users: {
alias: string;
createdAt: Date;
Analytics: {
activityCount: number;
updatedAt: Date;
};
id: string;
}[];
}

View File

@ -0,0 +1,48 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { AdminJobs } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import {
Controller,
Delete,
Get,
Param,
Query,
UseGuards
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { JobStatus } from 'bull';
import { QueueService } from './queue.service';
@Controller('admin/queue')
export class QueueController {
public constructor(private readonly queueService: QueueService) {}
@Delete('job')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteJobs(
@Query('status') filterByStatus?: string
): Promise<void> {
const status = <JobStatus[]>filterByStatus?.split(',') ?? undefined;
return this.queueService.deleteJobs({ status });
}
@Get('job')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getJobs(
@Query('status') filterByStatus?: string
): Promise<AdminJobs> {
const status = <JobStatus[]>filterByStatus?.split(',') ?? undefined;
return this.queueService.getJobs({ status });
}
@Delete('job/:id')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteJob(@Param('id') id: string): Promise<void> {
return this.queueService.deleteJob(id);
}
}

View File

@ -0,0 +1,12 @@
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { Module } from '@nestjs/common';
import { QueueController } from './queue.controller';
import { QueueService } from './queue.service';
@Module({
controllers: [QueueController],
imports: [DataGatheringModule],
providers: [QueueService]
})
export class QueueModule {}

View File

@ -0,0 +1,67 @@
import {
DATA_GATHERING_QUEUE,
QUEUE_JOB_STATUS_LIST
} from '@ghostfolio/common/config';
import { AdminJobs } from '@ghostfolio/common/interfaces';
import { InjectQueue } from '@nestjs/bull';
import { Injectable } from '@nestjs/common';
import { JobStatus, Queue } from 'bull';
@Injectable()
export class QueueService {
public constructor(
@InjectQueue(DATA_GATHERING_QUEUE)
private readonly dataGatheringQueue: Queue
) {}
public async deleteJob(aId: string) {
return (await this.dataGatheringQueue.getJob(aId))?.remove();
}
public async deleteJobs({
status = QUEUE_JOB_STATUS_LIST
}: {
status?: JobStatus[];
}) {
for (const statusItem of status) {
await this.dataGatheringQueue.clean(
300,
statusItem === 'waiting' ? 'wait' : statusItem
);
}
}
public async getJobs({
limit = 1000,
status = QUEUE_JOB_STATUS_LIST
}: {
limit?: number;
status?: JobStatus[];
}): Promise<AdminJobs> {
const jobs = await this.dataGatheringQueue.getJobs(status);
const jobsWithState = await Promise.all(
jobs
.filter((job) => {
return job;
})
.slice(0, limit)
.map(async (job) => {
return {
attemptsMade: job.attemptsMade + 1,
data: job.data,
finishedOn: job.finishedOn,
id: job.id,
name: job.name,
stacktrace: job.stacktrace,
state: await job.getState(),
timestamp: job.timestamp
};
})
);
return {
jobs: jobsWithState
};
}
}

View File

@ -0,0 +1,34 @@
import { AssetClass, AssetSubClass, Prisma } from '@prisma/client';
import { IsEnum, IsObject, IsOptional, IsString } from 'class-validator';
export class UpdateAssetProfileDto {
@IsEnum(AssetClass, { each: true })
@IsOptional()
assetClass?: AssetClass;
@IsEnum(AssetSubClass, { each: true })
@IsOptional()
assetSubClass?: AssetSubClass;
@IsString()
@IsOptional()
comment?: string;
@IsString()
@IsOptional()
currency?: string;
@IsString()
@IsOptional()
name?: string;
@IsObject()
@IsOptional()
scraperConfiguration?: Prisma.InputJsonObject;
@IsObject()
@IsOptional()
symbolMapping?: {
[dataProvider: string]: string;
};
}

View File

@ -0,0 +1,11 @@
import { Type } from 'class-transformer';
import { ArrayNotEmpty, IsArray, isNotEmptyObject } from 'class-validator';
import { UpdateMarketDataDto } from './update-market-data.dto';
export class UpdateBulkMarketDataDto {
@ArrayNotEmpty()
@IsArray()
@Type(() => UpdateMarketDataDto)
marketData: UpdateMarketDataDto[];
}

View File

@ -0,0 +1,10 @@
import { IsISO8601, IsNumber, IsOptional } from 'class-validator';
export class UpdateMarketDataDto {
@IsISO8601()
@IsOptional()
date?: string;
@IsNumber()
marketPrice: number;
}

View File

@ -1,31 +1,17 @@
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { Controller } from '@nestjs/common';
import { PrismaService } from '../services/prisma.service';
import { RedisCacheService } from './redis-cache/redis-cache.service';
@Controller()
export class AppController {
public constructor(
private prisma: PrismaService,
private readonly redisCacheService: RedisCacheService
private readonly exchangeRateDataService: ExchangeRateDataService
) {
this.initialize();
}
private async initialize() {
this.redisCacheService.reset();
const isDataGatheringLocked = await this.prisma.property.findUnique({
where: { key: 'LOCKED_DATA_GATHERING' }
});
if (!isDataGatheringLocked) {
// Prepare for automatical data gather if not locked
await this.prisma.property.deleteMany({
where: {
OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }]
}
});
}
try {
await this.exchangeRateDataService.initialize();
} catch {}
}
}

View File

@ -1,32 +1,45 @@
import { join } from 'path';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { CronService } from '@ghostfolio/api/services/cron.service';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module';
import {
DEFAULT_LANGUAGE_CODE,
SUPPORTED_LANGUAGE_CODES
} from '@ghostfolio/common/config';
import { BullModule } from '@nestjs/bull';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { ServeStaticModule } from '@nestjs/serve-static';
import { StatusCodes } from 'http-status-codes';
import { ConfigurationService } from '../services/configuration.service';
import { CronService } from '../services/cron.service';
import { DataGatheringService } from '../services/data-gathering.service';
import { DataProviderService } from '../services/data-provider.service';
import { AlphaVantageService } from '../services/data-provider/alpha-vantage/alpha-vantage.service';
import { GhostfolioScraperApiService } from '../services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '../services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '../services/data-provider/yahoo-finance/yahoo-finance.service';
import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
import { PrismaService } from '../services/prisma.service';
import { AccessModule } from './access/access.module';
import { AccountModule } from './account/account.module';
import { AdminModule } from './admin/admin.module';
import { AppController } from './app.controller';
import { AuthDeviceModule } from './auth-device/auth-device.module';
import { AuthModule } from './auth/auth.module';
import { BenchmarkModule } from './benchmark/benchmark.module';
import { CacheModule } from './cache/cache.module';
import { ExperimentalModule } from './experimental/experimental.module';
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
import { ExportModule } from './export/export.module';
import { HealthModule } from './health/health.module';
import { ImportModule } from './import/import.module';
import { InfoModule } from './info/info.module';
import { LogoModule } from './logo/logo.module';
import { OrderModule } from './order/order.module';
import { PlatformModule } from './platform/platform.module';
import { PortfolioModule } from './portfolio/portfolio.module';
import { RedisCacheModule } from './redis-cache/redis-cache.module';
import { SitemapModule } from './sitemap/sitemap.module';
import { SubscriptionModule } from './subscription/subscription.module';
import { SymbolModule } from './symbol/symbol.module';
import { TagModule } from './tag/tag.module';
import { UserModule } from './user/user.module';
@Module({
@ -34,44 +47,66 @@ import { UserModule } from './user/user.module';
AdminModule,
AccessModule,
AccountModule,
AuthDeviceModule,
AuthModule,
BenchmarkModule,
BullModule.forRoot({
redis: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT ?? '6379', 10),
password: process.env.REDIS_PASSWORD
}
}),
CacheModule,
ConfigModule.forRoot(),
ExperimentalModule,
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
ExchangeRateModule,
ExchangeRateDataModule,
ExportModule,
HealthModule,
ImportModule,
InfoModule,
LogoModule,
OrderModule,
PlatformModule,
PortfolioModule,
PrismaModule,
RedisCacheModule,
ScheduleModule.forRoot(),
ServeStaticModule.forRoot({
serveStaticOptions: {
/*etag: false // Disable etag header to fix PWA
setHeaders: (res, path) => {
if (path.includes('ngsw.json')) {
// Disable cache (https://stackoverflow.com/questions/22632593/how-to-disable-webpage-caching-in-expressjs-nodejs/39775595)
// https://gertjans.home.xs4all.nl/javascript/cache-control.html#no-cache
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
}
}*/
},
exclude: ['/api*', '/sitemap.xml'],
rootPath: join(__dirname, '..', 'client'),
exclude: ['/api*']
serveStaticOptions: {
setHeaders: (res) => {
if (res.req?.path === '/') {
let languageCode = DEFAULT_LANGUAGE_CODE;
try {
const code = res.req.headers['accept-language']
.split(',')[0]
.split('-')[0];
if (SUPPORTED_LANGUAGE_CODES.includes(code)) {
languageCode = code;
}
} catch {}
res.set('Location', `/${languageCode}`);
res.statusCode = StatusCodes.MOVED_PERMANENTLY;
}
}
}
}),
SitemapModule,
SubscriptionModule,
SymbolModule,
TagModule,
TwitterBotModule,
UserModule
],
controllers: [AppController],
providers: [
AlphaVantageService,
ConfigurationService,
CronService,
DataGatheringService,
DataProviderService,
ExchangeRateDataService,
GhostfolioScraperApiService,
PrismaService,
RakutenRapidApiService,
YahooFinanceService
]
providers: [CronService]
})
export class AppModule {}

View File

@ -0,0 +1,18 @@
import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { permissions } from '@ghostfolio/common/permissions';
import { Controller, Delete, Param, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth-device')
export class AuthDeviceController {
public constructor(private readonly authDeviceService: AuthDeviceService) {}
@Delete(':id')
@HasPermission(permissions.deleteAuthDevice)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteAuthDevice(@Param('id') id: string): Promise<void> {
await this.authDeviceService.deleteAuthDevice({ id });
}
}

View File

@ -0,0 +1,4 @@
export interface AuthDeviceDto {
createdAt: string;
id: string;
}

View File

@ -0,0 +1,20 @@
import { AuthDeviceController } from '@ghostfolio/api/app/auth-device/auth-device.controller';
import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
@Module({
controllers: [AuthDeviceController],
imports: [
ConfigurationModule,
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '180 days' }
}),
PrismaModule
],
providers: [AuthDeviceService]
})
export class AuthDeviceModule {}

View File

@ -0,0 +1,65 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { Injectable } from '@nestjs/common';
import { AuthDevice, Prisma } from '@prisma/client';
@Injectable()
export class AuthDeviceService {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly prismaService: PrismaService
) {}
public async authDevice(
where: Prisma.AuthDeviceWhereUniqueInput
): Promise<AuthDevice | null> {
return this.prismaService.authDevice.findUnique({
where
});
}
public async authDevices(params: {
skip?: number;
take?: number;
cursor?: Prisma.AuthDeviceWhereUniqueInput;
where?: Prisma.AuthDeviceWhereInput;
orderBy?: Prisma.AuthDeviceOrderByWithRelationInput;
}): Promise<AuthDevice[]> {
const { skip, take, cursor, where, orderBy } = params;
return this.prismaService.authDevice.findMany({
skip,
take,
cursor,
where,
orderBy
});
}
public async createAuthDevice(
data: Prisma.AuthDeviceCreateInput
): Promise<AuthDevice> {
return this.prismaService.authDevice.create({
data
});
}
public async updateAuthDevice(params: {
data: Prisma.AuthDeviceUpdateInput;
where: Prisma.AuthDeviceWhereUniqueInput;
}): Promise<AuthDevice> {
const { data, where } = params;
return this.prismaService.authDevice.update({
data,
where
});
}
public async deleteAuthDevice(
where: Prisma.AuthDeviceWhereUniqueInput
): Promise<AuthDevice> {
return this.prismaService.authDevice.delete({
where
});
}
}

View File

@ -1,30 +1,65 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
import { OAuthResponse } from '@ghostfolio/common/interfaces';
import {
Body,
Controller,
Get,
HttpException,
Param,
Post,
Req,
Res,
UseGuards
UseGuards,
VERSION_NEUTRAL,
Version
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Request, Response } from 'express';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { AuthService } from './auth.service';
import {
AssertionCredentialJSON,
AttestationCredentialJSON
} from './interfaces/simplewebauthn';
@Controller('auth')
export class AuthController {
public constructor(
private readonly authService: AuthService,
private readonly configurationService: ConfigurationService
private readonly configurationService: ConfigurationService,
private readonly webAuthService: WebAuthService
) {}
/**
* @deprecated
*/
@Get('anonymous/:accessToken')
public async accessTokenLogin(@Param('accessToken') accessToken: string) {
public async accessTokenLoginGet(
@Param('accessToken') accessToken: string
): Promise<OAuthResponse> {
try {
const authToken =
await this.authService.validateAnonymousLogin(accessToken);
return { authToken };
} catch {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
}
@Post('anonymous')
public async accessTokenLogin(
@Body() body: { accessToken: string }
): Promise<OAuthResponse> {
try {
const authToken = await this.authService.validateAnonymousLogin(
accessToken
body.accessToken
);
return { authToken };
} catch {
@ -43,14 +78,83 @@ export class AuthController {
@Get('google/callback')
@UseGuards(AuthGuard('google'))
public googleLoginCallback(@Req() req, @Res() res) {
@Version(VERSION_NEUTRAL)
public googleLoginCallback(
@Req() request: Request,
@Res() response: Response
) {
// Handles the Google OAuth2 callback
const jwt: string = req.user.jwt;
const jwt: string = (<any>request.user).jwt;
if (jwt) {
res.redirect(`${this.configurationService.get('ROOT_URL')}/auth/${jwt}`);
response.redirect(
`${this.configurationService.get(
'ROOT_URL'
)}/${DEFAULT_LANGUAGE_CODE}/auth/${jwt}`
);
} else {
res.redirect(`${this.configurationService.get('ROOT_URL')}/auth`);
response.redirect(
`${this.configurationService.get(
'ROOT_URL'
)}/${DEFAULT_LANGUAGE_CODE}/auth`
);
}
}
@Post('internet-identity')
public async internetIdentityLogin(
@Body() body: { principalId: string }
): Promise<OAuthResponse> {
try {
const authToken = await this.authService.validateInternetIdentityLogin(
body.principalId
);
return { authToken };
} catch {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
}
@Get('webauthn/generate-registration-options')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async generateRegistrationOptions() {
return this.webAuthService.generateRegistrationOptions();
}
@Post('webauthn/verify-attestation')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async verifyAttestation(
@Body() body: { deviceName: string; credential: AttestationCredentialJSON }
) {
return this.webAuthService.verifyAttestation(
body.deviceName,
body.credential
);
}
@Post('webauthn/generate-assertion-options')
public async generateAssertionOptions(@Body() body: { deviceId: string }) {
return this.webAuthService.generateAssertionOptions(body.deviceId);
}
@Post('webauthn/verify-assertion')
public async verifyAssertion(
@Body() body: { deviceId: string; credential: AssertionCredentialJSON }
) {
try {
const authToken = await this.webAuthService.verifyAssertion(
body.deviceId,
body.credential
);
return { authToken };
} catch {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
}
}

View File

@ -1,9 +1,13 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service';
import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service';
import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { UserService } from '../user/user.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { GoogleStrategy } from './google.strategy';
@ -12,18 +16,22 @@ import { JwtStrategy } from './jwt.strategy';
@Module({
controllers: [AuthController],
imports: [
ConfigurationModule,
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '180 days' }
})
}),
PrismaModule,
PropertyModule,
SubscriptionModule,
UserModule
],
providers: [
AuthDeviceService,
AuthService,
ConfigurationService,
GoogleStrategy,
JwtStrategy,
PrismaService,
UserService
WebAuthService
]
})
export class AuthModule {}

View File

@ -1,8 +1,10 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { UserService } from '@ghostfolio/api/app/user/user.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Provider } from '@prisma/client';
import { UserService } from '../user/user.service';
import { ValidateOAuthLoginParams } from './interfaces/interfaces';
@Injectable()
@ -10,10 +12,11 @@ export class AuthService {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly jwtService: JwtService,
private readonly propertyService: PropertyService,
private readonly userService: UserService
) {}
public async validateAnonymousLogin(accessToken: string) {
public async validateAnonymousLogin(accessToken: string): Promise<string> {
return new Promise(async (resolve, reject) => {
try {
const hashedAccessToken = this.userService.createAccessToken(
@ -26,7 +29,7 @@ export class AuthService {
});
if (user) {
const jwt: string = this.jwtService.sign({
const jwt = this.jwtService.sign({
id: user.id
});
@ -40,6 +43,42 @@ export class AuthService {
});
}
public async validateInternetIdentityLogin(principalId: string) {
try {
const provider: Provider = 'INTERNET_IDENTITY';
let [user] = await this.userService.users({
where: { provider, thirdPartyId: principalId }
});
if (!user) {
const isUserSignupEnabled =
await this.propertyService.isUserSignupEnabled();
if (!isUserSignupEnabled || true) {
throw new Error('Sign up forbidden');
}
// Create new user if not found
user = await this.userService.createUser({
data: {
provider,
thirdPartyId: principalId
}
});
}
return this.jwtService.sign({
id: user.id
});
} catch (error) {
throw new InternalServerErrorException(
'validateInternetIdentityLogin',
error.message
);
}
}
public async validateOAuthLogin({
provider,
thirdPartyId
@ -50,20 +89,30 @@ export class AuthService {
});
if (!user) {
const isUserSignupEnabled =
await this.propertyService.isUserSignupEnabled();
if (!isUserSignupEnabled) {
throw new Error('Sign up forbidden');
}
// Create new user if not found
user = await this.userService.createUser({
provider,
thirdPartyId
data: {
provider,
thirdPartyId
}
});
}
const jwt: string = this.jwtService.sign({
return this.jwtService.sign({
id: user.id
});
return jwt;
} catch (err) {
throw new InternalServerErrorException('validateOAuthLogin', err.message);
} catch (error) {
throw new InternalServerErrorException(
'validateOAuthLogin',
error.message
);
}
}
}

View File

@ -1,5 +1,5 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { Injectable } from '@nestjs/common';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { Injectable, Logger } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Provider } from '@prisma/client';
import { Strategy } from 'passport-google-oauth20';
@ -41,9 +41,9 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
};
done(null, user);
} catch (err) {
console.error(err);
done(err, false);
} catch (error) {
Logger.error(error, 'GoogleStrategy');
done(error, false);
}
}
}

View File

@ -1,5 +1,10 @@
import { AuthDeviceDto } from '@ghostfolio/api/app/auth-device/auth-device.dto';
import { Provider } from '@prisma/client';
export interface AuthDeviceDialogParams {
authDevice: AuthDeviceDto;
}
export interface ValidateOAuthLoginParams {
provider: Provider;
thirdPartyId: string;

View File

@ -0,0 +1,226 @@
export interface AuthenticatorAssertionResponse extends AuthenticatorResponse {
readonly authenticatorData: ArrayBuffer;
readonly signature: ArrayBuffer;
readonly userHandle: ArrayBuffer | null;
}
export interface AuthenticatorAttestationResponse
extends AuthenticatorResponse {
readonly attestationObject: ArrayBuffer;
}
export interface AuthenticationExtensionsClientInputs {
appid?: string;
appidExclude?: string;
credProps?: boolean;
uvm?: boolean;
}
export interface AuthenticationExtensionsClientOutputs {
appid?: boolean;
credProps?: CredentialPropertiesOutput;
uvm?: UvmEntries;
}
export interface AuthenticatorSelectionCriteria {
authenticatorAttachment?: AuthenticatorAttachment;
requireResidentKey?: boolean;
residentKey?: ResidentKeyRequirement;
userVerification?: UserVerificationRequirement;
}
export interface PublicKeyCredential extends Credential {
readonly rawId: ArrayBuffer;
readonly response: AuthenticatorResponse;
getClientExtensionResults(): AuthenticationExtensionsClientOutputs;
}
export interface PublicKeyCredentialCreationOptions {
attestation?: AttestationConveyancePreference;
authenticatorSelection?: AuthenticatorSelectionCriteria;
challenge: BufferSource;
excludeCredentials?: PublicKeyCredentialDescriptor[];
extensions?: AuthenticationExtensionsClientInputs;
pubKeyCredParams: PublicKeyCredentialParameters[];
rp: PublicKeyCredentialRpEntity;
timeout?: number;
user: PublicKeyCredentialUserEntity;
}
export interface PublicKeyCredentialDescriptor {
id: BufferSource;
transports?: AuthenticatorTransport[];
type: PublicKeyCredentialType;
}
export interface PublicKeyCredentialParameters {
alg: COSEAlgorithmIdentifier;
type: PublicKeyCredentialType;
}
export interface PublicKeyCredentialRequestOptions {
allowCredentials?: PublicKeyCredentialDescriptor[];
challenge: BufferSource;
extensions?: AuthenticationExtensionsClientInputs;
rpId?: string;
timeout?: number;
userVerification?: UserVerificationRequirement;
}
export interface PublicKeyCredentialUserEntity
extends PublicKeyCredentialEntity {
displayName: string;
id: BufferSource;
}
export interface AuthenticatorResponse {
readonly clientDataJSON: ArrayBuffer;
}
export interface CredentialPropertiesOutput {
rk?: boolean;
}
export interface Credential {
readonly id: string;
readonly type: string;
}
export interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity {
id?: string;
}
export interface PublicKeyCredentialEntity {
name: string;
}
export declare type AttestationConveyancePreference =
| 'direct'
| 'enterprise'
| 'indirect'
| 'none';
export declare type AuthenticatorTransport = 'ble' | 'internal' | 'nfc' | 'usb';
export declare type COSEAlgorithmIdentifier = number;
export declare type UserVerificationRequirement =
| 'discouraged'
| 'preferred'
| 'required';
export declare type UvmEntries = UvmEntry[];
export declare type AuthenticatorAttachment = 'cross-platform' | 'platform';
export declare type ResidentKeyRequirement =
| 'discouraged'
| 'preferred'
| 'required';
export declare type BufferSource = ArrayBufferView | ArrayBuffer;
export declare type PublicKeyCredentialType = 'public-key';
export declare type UvmEntry = number[];
export interface PublicKeyCredentialCreationOptionsJSON
extends Omit<
PublicKeyCredentialCreationOptions,
'challenge' | 'user' | 'excludeCredentials'
> {
user: PublicKeyCredentialUserEntityJSON;
challenge: Base64URLString;
excludeCredentials: PublicKeyCredentialDescriptorJSON[];
extensions?: AuthenticationExtensionsClientInputs;
}
/**
* A variant of PublicKeyCredentialRequestOptions suitable for JSON transmission to the browser to
* (eventually) get passed into navigator.credentials.get(...) in the browser.
*/
export interface PublicKeyCredentialRequestOptionsJSON
extends Omit<
PublicKeyCredentialRequestOptions,
'challenge' | 'allowCredentials'
> {
challenge: Base64URLString;
allowCredentials?: PublicKeyCredentialDescriptorJSON[];
extensions?: AuthenticationExtensionsClientInputs;
}
export interface PublicKeyCredentialDescriptorJSON
extends Omit<PublicKeyCredentialDescriptor, 'id'> {
id: Base64URLString;
}
export interface PublicKeyCredentialUserEntityJSON
extends Omit<PublicKeyCredentialUserEntity, 'id'> {
id: string;
}
/**
* The value returned from navigator.credentials.create()
*/
export interface AttestationCredential extends PublicKeyCredential {
response: AuthenticatorAttestationResponseFuture;
}
/**
* A slightly-modified AttestationCredential to simplify working with ArrayBuffers that
* are Base64URL-encoded in the browser so that they can be sent as JSON to the server.
*/
export interface AttestationCredentialJSON
extends Omit<
AttestationCredential,
'response' | 'rawId' | 'getClientExtensionResults'
> {
rawId: Base64URLString;
response: AuthenticatorAttestationResponseJSON;
clientExtensionResults: AuthenticationExtensionsClientOutputs;
transports?: AuthenticatorTransport[];
}
/**
* The value returned from navigator.credentials.get()
*/
export interface AssertionCredential extends PublicKeyCredential {
response: AuthenticatorAssertionResponse;
}
/**
* A slightly-modified AssertionCredential to simplify working with ArrayBuffers that
* are Base64URL-encoded in the browser so that they can be sent as JSON to the server.
*/
export interface AssertionCredentialJSON
extends Omit<
AssertionCredential,
'response' | 'rawId' | 'getClientExtensionResults'
> {
rawId: Base64URLString;
response: AuthenticatorAssertionResponseJSON;
clientExtensionResults: AuthenticationExtensionsClientOutputs;
}
/**
* A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that
* are Base64URL-encoded in the browser so that they can be sent as JSON to the server.
*/
export interface AuthenticatorAttestationResponseJSON
extends Omit<
AuthenticatorAttestationResponseFuture,
'clientDataJSON' | 'attestationObject'
> {
clientDataJSON: Base64URLString;
attestationObject: Base64URLString;
}
/**
* A slightly-modified AuthenticatorAssertionResponse to simplify working with ArrayBuffers that
* are Base64URL-encoded in the browser so that they can be sent as JSON to the server.
*/
export interface AuthenticatorAssertionResponseJSON
extends Omit<
AuthenticatorAssertionResponse,
'authenticatorData' | 'clientDataJSON' | 'signature' | 'userHandle'
> {
authenticatorData: Base64URLString;
clientDataJSON: Base64URLString;
signature: Base64URLString;
userHandle?: string;
}
/**
* A WebAuthn-compatible device and the information needed to verify assertions by it
*/
export declare type AuthenticatorDevice = {
credentialPublicKey: Buffer;
credentialID: Buffer;
counter: number;
transports?: AuthenticatorTransport[];
};
/**
* An attempt to communicate that this isn't just any string, but a Base64URL-encoded string
*/
export declare type Base64URLString = string;
/**
* AuthenticatorAttestationResponse in TypeScript's DOM lib is outdated (up through v3.9.7).
* Maintain an augmented version here so we can implement additional properties as the WebAuthn
* spec evolves.
*
* See https://www.w3.org/TR/webauthn-2/#iface-authenticatorattestationresponse
*
* Properties marked optional are not supported in all browsers.
*/
export interface AuthenticatorAttestationResponseFuture
extends AuthenticatorAttestationResponse {
getTransports?: () => AuthenticatorTransport[];
getAuthenticatorData?: () => ArrayBuffer;
getPublicKey?: () => ArrayBuffer;
getPublicKeyAlgorithm?: () => COSEAlgorithmIdentifier[];
}

View File

@ -1,34 +1,46 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { UserService } from '@ghostfolio/api/app/user/user.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { HEADER_KEY_TIMEZONE } from '@ghostfolio/common/config';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import * as countriesAndTimezones from 'countries-and-timezones';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UserService } from '../user/user.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
public constructor(
readonly configurationService: ConfigurationService,
private prisma: PrismaService,
private readonly configurationService: ConfigurationService,
private readonly prismaService: PrismaService,
private readonly userService: UserService
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
passReqToCallback: true,
secretOrKey: configurationService.get('JWT_SECRET_KEY')
});
}
public async validate({ id }: { id: string }) {
public async validate(request: Request, { id }: { id: string }) {
try {
const timezone = request.headers[HEADER_KEY_TIMEZONE.toLowerCase()];
const user = await this.userService.user({ id });
if (user) {
await this.prisma.analytics.upsert({
create: { User: { connect: { id: user.id } } },
update: { activityCount: { increment: 1 }, updatedAt: new Date() },
where: { userId: user.id }
});
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
const country =
countriesAndTimezones.getCountryForTimezone(timezone)?.id;
await this.prismaService.analytics.upsert({
create: { country, User: { connect: { id: user.id } } },
update: {
country,
activityCount: { increment: 1 },
updatedAt: new Date()
},
where: { userId: user.id }
});
}
return user;
} else {

View File

@ -0,0 +1,229 @@
import { AuthDeviceDto } from '@ghostfolio/api/app/auth-device/auth-device.dto';
import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service';
import { UserService } from '@ghostfolio/api/app/user/user.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Inject,
Injectable,
InternalServerErrorException,
Logger
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import {
GenerateAuthenticationOptionsOpts,
GenerateRegistrationOptionsOpts,
VerifiedAuthenticationResponse,
VerifiedRegistrationResponse,
VerifyAuthenticationResponseOpts,
VerifyRegistrationResponseOpts,
generateAuthenticationOptions,
generateRegistrationOptions,
verifyAuthenticationResponse,
verifyRegistrationResponse
} from '@simplewebauthn/server';
import {
AssertionCredentialJSON,
AttestationCredentialJSON
} from './interfaces/simplewebauthn';
@Injectable()
export class WebAuthService {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly deviceService: AuthDeviceService,
private readonly jwtService: JwtService,
private readonly userService: UserService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
get rpID() {
return this.configurationService.get('WEB_AUTH_RP_ID');
}
get expectedOrigin() {
return this.configurationService.get('ROOT_URL');
}
public async generateRegistrationOptions() {
const user = this.request.user;
const opts: GenerateRegistrationOptionsOpts = {
rpName: 'Ghostfolio',
rpID: this.rpID,
userID: user.id,
userName: '',
timeout: 60000,
attestationType: 'indirect',
authenticatorSelection: {
authenticatorAttachment: 'platform',
requireResidentKey: false,
userVerification: 'required'
}
};
const options = await generateRegistrationOptions(opts);
await this.userService.updateUser({
data: {
authChallenge: options.challenge
},
where: {
id: user.id
}
});
return options;
}
public async verifyAttestation(
deviceName: string,
credential: AttestationCredentialJSON
): Promise<AuthDeviceDto> {
const user = this.request.user;
const expectedChallenge = user.authChallenge;
let verification: VerifiedRegistrationResponse;
try {
const opts: VerifyRegistrationResponseOpts = {
expectedChallenge,
expectedOrigin: this.expectedOrigin,
expectedRPID: this.rpID,
response: {
clientExtensionResults: credential.clientExtensionResults,
id: credential.id,
rawId: credential.rawId,
response: credential.response,
type: 'public-key'
}
};
verification = await verifyRegistrationResponse(opts);
} catch (error) {
Logger.error(error, 'WebAuthService');
throw new InternalServerErrorException(error.message);
}
const { registrationInfo, verified } = verification;
const devices = await this.deviceService.authDevices({
where: { userId: user.id }
});
if (registrationInfo && verified) {
const { counter, credentialID, credentialPublicKey } = registrationInfo;
let existingDevice = devices.find(
(device) => device.credentialId === credentialID
);
if (!existingDevice) {
/**
* Add the returned device to the user's list of devices
*/
existingDevice = await this.deviceService.createAuthDevice({
counter,
credentialId: Buffer.from(credentialID),
credentialPublicKey: Buffer.from(credentialPublicKey),
User: { connect: { id: user.id } }
});
}
return {
createdAt: existingDevice.createdAt.toISOString(),
id: existingDevice.id
};
}
throw new InternalServerErrorException('An unknown error occurred');
}
public async generateAssertionOptions(deviceId: string) {
const device = await this.deviceService.authDevice({ id: deviceId });
if (!device) {
throw new Error('Device not found');
}
const opts: GenerateAuthenticationOptionsOpts = {
allowCredentials: [
{
id: device.credentialId,
transports: ['internal'],
type: 'public-key'
}
],
rpID: this.rpID,
timeout: 60000,
userVerification: 'preferred'
};
const options = await generateAuthenticationOptions(opts);
await this.userService.updateUser({
data: {
authChallenge: options.challenge
},
where: {
id: device.userId
}
});
return options;
}
public async verifyAssertion(
deviceId: string,
credential: AssertionCredentialJSON
) {
const device = await this.deviceService.authDevice({ id: deviceId });
if (!device) {
throw new Error('Device not found');
}
const user = await this.userService.user({ id: device.userId });
let verification: VerifiedAuthenticationResponse;
try {
const opts: VerifyAuthenticationResponseOpts = {
authenticator: {
credentialID: device.credentialId,
credentialPublicKey: device.credentialPublicKey,
counter: device.counter
},
expectedChallenge: `${user.authChallenge}`,
expectedOrigin: this.expectedOrigin,
expectedRPID: this.rpID,
response: {
clientExtensionResults: credential.clientExtensionResults,
id: credential.id,
rawId: credential.rawId,
response: credential.response,
type: 'public-key'
}
};
verification = await verifyAuthenticationResponse(opts);
} catch (error) {
Logger.error(error, 'WebAuthService');
throw new InternalServerErrorException({ error: error.message });
}
const { verified, authenticationInfo } = verification;
if (verified) {
device.counter = authenticationInfo.newCounter;
await this.deviceService.updateAuthDevice({
data: device,
where: { id: device.id }
});
return this.jwtService.sign({
id: user.id
});
}
throw new Error();
}
}

View File

@ -0,0 +1,120 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import type {
BenchmarkMarketDataDetails,
BenchmarkResponse,
UniqueAsset
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Body,
Controller,
Delete,
Get,
HttpException,
Inject,
Param,
Post,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { DataSource } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { BenchmarkService } from './benchmark.service';
@Controller('benchmark')
export class BenchmarkController {
public constructor(
private readonly benchmarkService: BenchmarkService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@HasPermission(permissions.accessAdminControl)
@Post()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async addBenchmark(@Body() { dataSource, symbol }: UniqueAsset) {
try {
const benchmark = await this.benchmarkService.addBenchmark({
dataSource,
symbol
});
if (!benchmark) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
return benchmark;
} catch {
throw new HttpException(
getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR),
StatusCodes.INTERNAL_SERVER_ERROR
);
}
}
@Delete(':dataSource/:symbol')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteBenchmark(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
) {
try {
const benchmark = await this.benchmarkService.deleteBenchmark({
dataSource,
symbol
});
if (!benchmark) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
return benchmark;
} catch {
throw new HttpException(
getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR),
StatusCodes.INTERNAL_SERVER_ERROR
);
}
}
@Get()
@UseInterceptors(TransformDataSourceInRequestInterceptor)
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getBenchmark(): Promise<BenchmarkResponse> {
return {
benchmarks: await this.benchmarkService.getBenchmarks()
};
}
@Get(':dataSource/:symbol/:startDateString')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async getBenchmarkMarketDataBySymbol(
@Param('dataSource') dataSource: DataSource,
@Param('startDateString') startDateString: string,
@Param('symbol') symbol: string
): Promise<BenchmarkMarketDataDetails> {
const startDate = new Date(startDateString);
const userCurrency = this.request.user.Settings.settings.baseCurrency;
return this.benchmarkService.getMarketDataBySymbol({
dataSource,
startDate,
symbol,
userCurrency
});
}
}

View File

@ -0,0 +1,31 @@
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { Module } from '@nestjs/common';
import { BenchmarkController } from './benchmark.controller';
import { BenchmarkService } from './benchmark.service';
@Module({
controllers: [BenchmarkController],
exports: [BenchmarkService],
imports: [
ConfigurationModule,
DataProviderModule,
ExchangeRateDataModule,
MarketDataModule,
PrismaModule,
PropertyModule,
RedisCacheModule,
SymbolModule,
SymbolProfileModule
],
providers: [BenchmarkService]
})
export class BenchmarkModule {}

View File

@ -0,0 +1,24 @@
import { BenchmarkService } from './benchmark.service';
describe('BenchmarkService', () => {
let benchmarkService: BenchmarkService;
beforeAll(async () => {
benchmarkService = new BenchmarkService(
null,
null,
null,
null,
null,
null,
null,
null
);
});
it('calculateChangeInPercentage', async () => {
expect(benchmarkService.calculateChangeInPercentage(1, 2)).toEqual(1);
expect(benchmarkService.calculateChangeInPercentage(2, 2)).toEqual(0);
expect(benchmarkService.calculateChangeInPercentage(2, 1)).toEqual(-0.5);
});
});

View File

@ -0,0 +1,415 @@
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import {
MAX_CHART_ITEMS,
PROPERTY_BENCHMARKS
} from '@ghostfolio/common/config';
import {
DATE_FORMAT,
calculateBenchmarkTrend,
parseDate
} from '@ghostfolio/common/helper';
import {
Benchmark,
BenchmarkMarketDataDetails,
BenchmarkProperty,
BenchmarkResponse,
UniqueAsset
} from '@ghostfolio/common/interfaces';
import { BenchmarkTrend } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common';
import { SymbolProfile } from '@prisma/client';
import Big from 'big.js';
import { format, isSameDay, subDays } from 'date-fns';
import { isNumber, last, uniqBy } from 'lodash';
import ms from 'ms';
@Injectable()
export class BenchmarkService {
private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS';
public constructor(
private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly marketDataService: MarketDataService,
private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService,
private readonly redisCacheService: RedisCacheService,
private readonly symbolProfileService: SymbolProfileService,
private readonly symbolService: SymbolService
) {}
public calculateChangeInPercentage(baseValue: number, currentValue: number) {
if (baseValue && currentValue) {
return new Big(currentValue).div(baseValue).minus(1).toNumber();
}
return 0;
}
public async getBenchmarkTrends({ dataSource, symbol }: UniqueAsset) {
const historicalData = await this.marketDataService.marketDataItems({
orderBy: {
date: 'desc'
},
where: {
dataSource,
symbol,
date: { gte: subDays(new Date(), 400) }
}
});
const fiftyDayAverage = calculateBenchmarkTrend({
historicalData,
days: 50
});
const twoHundredDayAverage = calculateBenchmarkTrend({
historicalData,
days: 200
});
return { trend50d: fiftyDayAverage, trend200d: twoHundredDayAverage };
}
public async getBenchmarks({
enableSharing = false,
useCache = true
} = {}): Promise<BenchmarkResponse['benchmarks']> {
let benchmarks: BenchmarkResponse['benchmarks'];
if (useCache) {
try {
benchmarks = JSON.parse(
await this.redisCacheService.get(this.CACHE_KEY_BENCHMARKS)
);
if (benchmarks) {
return benchmarks;
}
} catch {}
}
const benchmarkAssetProfiles = await this.getBenchmarkAssetProfiles({
enableSharing
});
const promisesAllTimeHighs: Promise<{ date: Date; marketPrice: number }>[] =
[];
const promisesBenchmarkTrends: Promise<{
trend50d: BenchmarkTrend;
trend200d: BenchmarkTrend;
}>[] = [];
const quotes = await this.dataProviderService.getQuotes({
items: benchmarkAssetProfiles.map(({ dataSource, symbol }) => {
return { dataSource, symbol };
})
});
for (const { dataSource, symbol } of benchmarkAssetProfiles) {
promisesAllTimeHighs.push(
this.marketDataService.getMax({ dataSource, symbol })
);
promisesBenchmarkTrends.push(
this.getBenchmarkTrends({ dataSource, symbol })
);
}
const [allTimeHighs, benchmarkTrends] = await Promise.all([
Promise.all(promisesAllTimeHighs),
Promise.all(promisesBenchmarkTrends)
]);
let storeInCache = true;
benchmarks = allTimeHighs.map((allTimeHigh, index) => {
const { marketPrice } =
quotes[benchmarkAssetProfiles[index].symbol] ?? {};
let performancePercentFromAllTimeHigh = 0;
if (allTimeHigh?.marketPrice && marketPrice) {
performancePercentFromAllTimeHigh = this.calculateChangeInPercentage(
allTimeHigh.marketPrice,
marketPrice
);
} else {
storeInCache = false;
}
return {
marketCondition: this.getMarketCondition(
performancePercentFromAllTimeHigh
),
name: benchmarkAssetProfiles[index].name,
performances: {
allTimeHigh: {
date: allTimeHigh?.date,
performancePercent: performancePercentFromAllTimeHigh
}
},
trend50d: benchmarkTrends[index].trend50d,
trend200d: benchmarkTrends[index].trend200d
};
});
if (storeInCache) {
await this.redisCacheService.set(
this.CACHE_KEY_BENCHMARKS,
JSON.stringify(benchmarks),
ms('4 hours') / 1000
);
}
return benchmarks;
}
public async getBenchmarkAssetProfiles({
enableSharing = false
} = {}): Promise<Partial<SymbolProfile>[]> {
const symbolProfileIds: string[] = (
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as BenchmarkProperty[]) ?? []
)
.filter((benchmark) => {
if (enableSharing) {
return benchmark.enableSharing;
}
return true;
})
.map(({ symbolProfileId }) => {
return symbolProfileId;
});
const assetProfiles =
await this.symbolProfileService.getSymbolProfilesByIds(symbolProfileIds);
return assetProfiles
.map(({ dataSource, id, name, symbol }) => {
return {
dataSource,
id,
name,
symbol
};
})
.sort((a, b) => a.name.localeCompare(b.name));
}
public async getMarketDataBySymbol({
dataSource,
startDate,
symbol,
userCurrency
}: {
startDate: Date;
userCurrency: string;
} & UniqueAsset): Promise<BenchmarkMarketDataDetails> {
const marketData: { date: string; value: number }[] = [];
const [currentSymbolItem, marketDataItems] = await Promise.all([
this.symbolService.get({
dataGatheringItem: {
dataSource,
symbol
}
}),
this.marketDataService.marketDataItems({
orderBy: {
date: 'asc'
},
where: {
dataSource,
symbol,
date: {
gte: startDate
}
}
})
]);
const exchangeRates =
await this.exchangeRateDataService.getExchangeRatesByCurrency({
startDate,
currencies: [currentSymbolItem.currency],
targetCurrency: userCurrency
});
const exchangeRateAtStartDate =
exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[
format(startDate, DATE_FORMAT)
];
const marketPriceAtStartDate = marketDataItems?.find(({ date }) => {
return isSameDay(date, startDate);
})?.marketPrice;
if (!marketPriceAtStartDate) {
Logger.error(
`No historical market data has been found for ${symbol} (${dataSource}) at ${format(
startDate,
DATE_FORMAT
)}`,
'BenchmarkService'
);
return { marketData };
}
const step = Math.round(
marketDataItems.length / Math.min(marketDataItems.length, MAX_CHART_ITEMS)
);
let i = 0;
for (let marketDataItem of marketDataItems) {
if (i % step !== 0) {
continue;
}
const exchangeRate =
exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[
format(marketDataItem.date, DATE_FORMAT)
];
const exchangeRateFactor =
isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate)
? exchangeRate / exchangeRateAtStartDate
: 1;
marketData.push({
date: format(marketDataItem.date, DATE_FORMAT),
value:
marketPriceAtStartDate === 0
? 0
: this.calculateChangeInPercentage(
marketPriceAtStartDate,
marketDataItem.marketPrice * exchangeRateFactor
) * 100
});
}
const includesToday = isSameDay(
parseDate(last(marketData).date),
new Date()
);
if (currentSymbolItem?.marketPrice && !includesToday) {
const exchangeRate =
exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[
format(new Date(), DATE_FORMAT)
];
const exchangeRateFactor =
isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate)
? exchangeRate / exchangeRateAtStartDate
: 1;
marketData.push({
date: format(new Date(), DATE_FORMAT),
value:
this.calculateChangeInPercentage(
marketPriceAtStartDate,
currentSymbolItem.marketPrice * exchangeRateFactor
) * 100
});
}
return {
marketData
};
}
public async addBenchmark({
dataSource,
symbol
}: UniqueAsset): Promise<Partial<SymbolProfile>> {
const assetProfile = await this.prismaService.symbolProfile.findFirst({
where: {
dataSource,
symbol
}
});
if (!assetProfile) {
return;
}
let benchmarks =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as BenchmarkProperty[]) ?? [];
benchmarks.push({ symbolProfileId: assetProfile.id });
benchmarks = uniqBy(benchmarks, 'symbolProfileId');
await this.propertyService.put({
key: PROPERTY_BENCHMARKS,
value: JSON.stringify(benchmarks)
});
return {
dataSource,
symbol,
id: assetProfile.id,
name: assetProfile.name
};
}
public async deleteBenchmark({
dataSource,
symbol
}: UniqueAsset): Promise<Partial<SymbolProfile>> {
const assetProfile = await this.prismaService.symbolProfile.findFirst({
where: {
dataSource,
symbol
}
});
if (!assetProfile) {
return null;
}
let benchmarks =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as BenchmarkProperty[]) ?? [];
benchmarks = benchmarks.filter(({ symbolProfileId }) => {
return symbolProfileId !== assetProfile.id;
});
await this.propertyService.put({
key: PROPERTY_BENCHMARKS,
value: JSON.stringify(benchmarks)
});
return {
dataSource,
symbol,
id: assetProfile.id,
name: assetProfile.name
};
}
private getMarketCondition(
aPerformanceInPercent: number
): Benchmark['marketCondition'] {
if (aPerformanceInPercent === 0) {
return 'ALL_TIME_HIGH';
} else if (aPerformanceInPercent <= -0.2) {
return 'BEAR_MARKET';
} else {
return 'NEUTRAL_MARKET';
}
}
}

View File

@ -1,26 +1,18 @@
import { RequestWithUser } from '@ghostfolio/api/app/interfaces/request-with-user.type';
import { Controller, Inject, Param, Post, UseGuards } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { permissions } from '@ghostfolio/common/permissions';
import { Controller, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { RedisCacheService } from '../redis-cache/redis-cache.service';
import { CacheService } from './cache.service';
@Controller('cache')
export class CacheController {
public constructor(
private readonly cacheService: CacheService,
private readonly redisCacheService: RedisCacheService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {
this.redisCacheService.reset();
}
public constructor(private readonly redisCacheService: RedisCacheService) {}
@HasPermission(permissions.accessAdminControl)
@Post('flush')
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async flushCache(): Promise<void> {
this.redisCacheService.reset();
return this.cacheService.flush(this.request.user.id);
return this.redisCacheService.reset();
}
}

View File

@ -1,13 +1,24 @@
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { Module } from '@nestjs/common';
import { RedisCacheModule } from '../redis-cache/redis-cache.module';
import { CacheController } from './cache.controller';
import { CacheService } from './cache.service';
@Module({
imports: [RedisCacheModule],
controllers: [CacheController],
providers: [CacheService, PrismaService]
imports: [
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
ExchangeRateDataModule,
PrismaModule,
RedisCacheModule,
SymbolProfileModule
]
})
export class CacheModule {}

View File

@ -1,17 +0,0 @@
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Injectable } from '@nestjs/common';
@Injectable()
export class CacheService {
public constructor(private prisma: PrismaService) {}
public async flush(aUserId: string): Promise<void> {
await this.prisma.property.deleteMany({
where: {
OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }]
}
});
return;
}
}

View File

@ -0,0 +1,44 @@
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
import {
Controller,
Get,
HttpException,
Param,
UseGuards
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { parseISO } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { ExchangeRateService } from './exchange-rate.service';
@Controller('exchange-rate')
export class ExchangeRateController {
public constructor(
private readonly exchangeRateService: ExchangeRateService
) {}
@Get(':symbol/:dateString')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getExchangeRate(
@Param('dateString') dateString: string,
@Param('symbol') symbol: string
): Promise<IDataProviderHistoricalResponse> {
const date = parseISO(dateString);
const exchangeRate = await this.exchangeRateService.getExchangeRate({
date,
symbol
});
if (exchangeRate) {
return { marketPrice: exchangeRate };
}
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
}

View File

@ -0,0 +1,13 @@
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { Module } from '@nestjs/common';
import { ExchangeRateController } from './exchange-rate.controller';
import { ExchangeRateService } from './exchange-rate.service';
@Module({
controllers: [ExchangeRateController],
exports: [ExchangeRateService],
imports: [ExchangeRateDataModule],
providers: [ExchangeRateService]
})
export class ExchangeRateModule {}

View File

@ -0,0 +1,26 @@
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { Injectable } from '@nestjs/common';
@Injectable()
export class ExchangeRateService {
public constructor(
private readonly exchangeRateDataService: ExchangeRateDataService
) {}
public async getExchangeRate({
date,
symbol
}: {
date: Date;
symbol: string;
}): Promise<number> {
const [currency1, currency2] = symbol.split('-');
return this.exchangeRateDataService.toCurrencyAtDate(
1,
currency1,
currency2,
date
);
}
}

View File

@ -1,22 +0,0 @@
import { Currency, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
export class CreateOrderDto {
@IsString()
currency: Currency;
@IsISO8601()
date: string;
@IsNumber()
quantity: number;
@IsString()
symbol: string;
@IsString()
type: Type;
@IsNumber()
unitPrice: number;
}

View File

@ -1,91 +0,0 @@
import { RequestWithUser } from '@ghostfolio/api/app/interfaces/request-with-user.type';
import {
baseCurrency,
benchmarks,
isApiTokenAuthorized
} from '@ghostfolio/helper';
import {
Body,
Controller,
Get,
Headers,
HttpException,
Inject,
Param,
Post
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { parse } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { CreateOrderDto } from './create-order.dto';
import { ExperimentalService } from './experimental.service';
import { Data } from './interfaces/data.interface';
@Controller('experimental')
export class ExperimentalController {
public constructor(
private readonly experimentalService: ExperimentalService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Get('benchmarks')
public async getBenchmarks(
@Headers('Authorization') apiToken: string
): Promise<string[]> {
if (!isApiTokenAuthorized(apiToken)) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
return benchmarks;
}
@Get('benchmarks/:symbol')
public async getBenchmark(
@Headers('Authorization') apiToken: string,
@Param('symbol') symbol: string
): Promise<{ date: Date; marketPrice: number }[]> {
if (!isApiTokenAuthorized(apiToken)) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
const marketData = await this.experimentalService.getBenchmark(symbol);
if (marketData?.length === 0) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
return marketData;
}
@Post('value/:dateString?')
public async getValue(
@Body() orders: CreateOrderDto[],
@Headers('Authorization') apiToken: string,
@Param('dateString') dateString: string
): Promise<Data> {
if (!isApiTokenAuthorized(apiToken)) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
let date = new Date();
if (dateString) {
date = parse(dateString, 'yyyy-MM-dd', new Date());
}
return this.experimentalService.getValue(orders, date, baseCurrency);
}
}

View File

@ -1,31 +0,0 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service';
import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { RulesService } from '@ghostfolio/api/services/rules.service';
import { Module } from '@nestjs/common';
import { ExperimentalController } from './experimental.controller';
import { ExperimentalService } from './experimental.service';
@Module({
imports: [],
controllers: [ExperimentalController],
providers: [
AlphaVantageService,
ConfigurationService,
DataProviderService,
ExchangeRateDataService,
ExperimentalService,
GhostfolioScraperApiService,
PrismaService,
RakutenRapidApiService,
RulesService,
YahooFinanceService
]
})
export class ExperimentalModule {}

View File

@ -1,65 +0,0 @@
import { Portfolio } from '@ghostfolio/api/models/portfolio';
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { RulesService } from '@ghostfolio/api/services/rules.service';
import { Injectable } from '@nestjs/common';
import { Currency, Type } from '@prisma/client';
import { parseISO } from 'date-fns';
import { OrderWithAccount } from '../order/interfaces/order-with-account.type';
import { CreateOrderDto } from './create-order.dto';
import { Data } from './interfaces/data.interface';
@Injectable()
export class ExperimentalService {
public constructor(
private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private prisma: PrismaService,
private readonly rulesService: RulesService
) {}
public async getBenchmark(aSymbol: string) {
return this.prisma.marketData.findMany({
orderBy: { date: 'asc' },
select: { date: true, marketPrice: true },
where: { symbol: aSymbol }
});
}
public async getValue(
aOrders: CreateOrderDto[],
aDate: Date,
aBaseCurrency: Currency
): Promise<Data> {
const ordersWithPlatform: OrderWithAccount[] = aOrders.map((order) => {
return {
...order,
accountId: undefined,
accountUserId: undefined,
createdAt: new Date(),
dataSource: undefined,
date: parseISO(order.date),
fee: 0,
id: undefined,
platformId: undefined,
type: Type.BUY,
updatedAt: undefined,
userId: undefined
};
});
const portfolio = new Portfolio(
this.dataProviderService,
this.exchangeRateDataService,
this.rulesService
);
await portfolio.setOrders(ordersWithPlatform);
return {
currency: aBaseCurrency,
value: portfolio.getValue(aDate)
};
}
}

View File

@ -1,6 +0,0 @@
import { Currency } from '@prisma/client';
export interface Data {
currency: Currency;
value: number;
}

View File

@ -0,0 +1,28 @@
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { Export } from '@ghostfolio/common/interfaces';
import type { RequestWithUser } from '@ghostfolio/common/types';
import { Controller, Get, Inject, Query, UseGuards } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { ExportService } from './export.service';
@Controller('export')
export class ExportController {
public constructor(
private readonly exportService: ExportService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Get()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async export(
@Query('activityIds') activityIds?: string[]
): Promise<Export> {
return this.exportService.export({
activityIds,
userCurrency: this.request.user.Settings.settings.baseCurrency,
userId: this.request.user.id
});
}
}

View File

@ -0,0 +1,24 @@
import { AccountModule } from '@ghostfolio/api/app/account/account.module';
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { Module } from '@nestjs/common';
import { ExportController } from './export.controller';
import { ExportService } from './export.service';
@Module({
imports: [
AccountModule,
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
OrderModule,
RedisCacheModule
],
controllers: [ExportController],
providers: [ExportService]
})
export class ExportModule {}

View File

@ -0,0 +1,97 @@
import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { environment } from '@ghostfolio/api/environments/environment';
import { Export } from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common';
@Injectable()
export class ExportService {
public constructor(
private readonly accountService: AccountService,
private readonly orderService: OrderService
) {}
public async export({
activityIds,
userCurrency,
userId
}: {
activityIds?: string[];
userCurrency: string;
userId: string;
}): Promise<Export> {
const accounts = (
await this.accountService.accounts({
orderBy: {
name: 'asc'
},
where: { userId }
})
).map(
({ balance, comment, currency, id, isExcluded, name, platformId }) => {
return {
balance,
comment,
currency,
id,
isExcluded,
name,
platformId
};
}
);
let { activities } = await this.orderService.getOrders({
userCurrency,
userId,
includeDrafts: true,
sortColumn: 'date',
sortDirection: 'asc',
withExcludedAccounts: true
});
if (activityIds) {
activities = activities.filter((activity) => {
return activityIds.includes(activity.id);
});
}
return {
meta: { date: new Date().toISOString(), version: environment.version },
accounts,
activities: activities.map(
({
accountId,
comment,
date,
fee,
id,
quantity,
SymbolProfile,
type,
unitPrice
}) => {
return {
accountId,
comment,
fee,
id,
quantity,
type,
unitPrice,
currency: SymbolProfile.currency,
dataSource: SymbolProfile.dataSource,
date: date.toISOString(),
symbol:
type === 'FEE' ||
type === 'INTEREST' ||
type === 'ITEM' ||
type === 'LIABILITY'
? SymbolProfile.name
: SymbolProfile.symbol
};
}
)
};
}
}

View File

@ -0,0 +1,56 @@
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import {
Controller,
Get,
HttpException,
Param,
UseInterceptors
} from '@nestjs/common';
import { DataSource } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { HealthService } from './health.service';
@Controller('health')
export class HealthController {
public constructor(private readonly healthService: HealthService) {}
@Get()
public async getHealth() {}
@Get('data-enhancer/:name')
public async getHealthOfDataEnhancer(@Param('name') name: string) {
const hasResponse =
await this.healthService.hasResponseFromDataEnhancer(name);
if (hasResponse !== true) {
throw new HttpException(
getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE),
StatusCodes.SERVICE_UNAVAILABLE
);
}
}
@Get('data-provider/:dataSource')
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async getHealthOfDataProvider(
@Param('dataSource') dataSource: DataSource
) {
if (!DataSource[dataSource]) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
const hasResponse =
await this.healthService.hasResponseFromDataProvider(dataSource);
if (hasResponse !== true) {
throw new HttpException(
getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE),
StatusCodes.SERVICE_UNAVAILABLE
);
}
}
}

View File

@ -0,0 +1,14 @@
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataEnhancerModule } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
import { HealthService } from './health.service';
@Module({
controllers: [HealthController],
imports: [ConfigurationModule, DataEnhancerModule, DataProviderModule],
providers: [HealthService]
})
export class HealthModule {}

View File

@ -0,0 +1,20 @@
import { DataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { Injectable } from '@nestjs/common';
import { DataSource } from '@prisma/client';
@Injectable()
export class HealthService {
public constructor(
private readonly dataEnhancerService: DataEnhancerService,
private readonly dataProviderService: DataProviderService
) {}
public async hasResponseFromDataEnhancer(aName: string) {
return this.dataEnhancerService.enhance(aName);
}
public async hasResponseFromDataProvider(aDataSource: DataSource) {
return this.dataProviderService.checkQuote(aDataSource);
}
}

View File

@ -0,0 +1,17 @@
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Type } from 'class-transformer';
import { IsArray, IsOptional, ValidateNested } from 'class-validator';
export class ImportDataDto {
@IsOptional()
@IsArray()
@Type(() => CreateAccountDto)
@ValidateNested({ each: true })
accounts: CreateAccountDto[];
@IsArray()
@Type(() => CreateOrderDto)
@ValidateNested({ each: true })
activities: CreateOrderDto[];
}

View File

@ -0,0 +1,111 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ImportResponse } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Body,
Controller,
Get,
HttpException,
Inject,
Logger,
Param,
Post,
Query,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { DataSource } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { ImportDataDto } from './import-data.dto';
import { ImportService } from './import.service';
@Controller('import')
export class ImportController {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly importService: ImportService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Post()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@HasPermission(permissions.createOrder)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async import(
@Body() importData: ImportDataDto,
@Query('dryRun') isDryRun?: boolean
): Promise<ImportResponse> {
if (
!hasPermission(this.request.user.permissions, permissions.createAccount)
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
let maxActivitiesToImport = this.configurationService.get(
'MAX_ACTIVITIES_TO_IMPORT'
);
if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Premium'
) {
maxActivitiesToImport = Number.MAX_SAFE_INTEGER;
}
const userCurrency = this.request.user.Settings.settings.baseCurrency;
try {
const activities = await this.importService.import({
isDryRun,
maxActivitiesToImport,
userCurrency,
accountsDto: importData.accounts ?? [],
activitiesDto: importData.activities,
userId: this.request.user.id
});
return { activities };
} catch (error) {
Logger.error(error, ImportController);
throw new HttpException(
{
error: getReasonPhrase(StatusCodes.BAD_REQUEST),
message: [error.message]
},
StatusCodes.BAD_REQUEST
);
}
}
@Get('dividends/:dataSource/:symbol')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async gatherDividends(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
): Promise<ImportResponse> {
const userCurrency = this.request.user.Settings.settings.baseCurrency;
const activities = await this.importService.getDividends({
dataSource,
symbol,
userCurrency
});
return { activities };
}
}

View File

@ -0,0 +1,36 @@
import { AccountModule } from '@ghostfolio/api/app/account/account.module';
import { CacheModule } from '@ghostfolio/api/app/cache/cache.module';
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module';
import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { Module } from '@nestjs/common';
import { ImportController } from './import.controller';
import { ImportService } from './import.service';
@Module({
controllers: [ImportController],
imports: [
AccountModule,
CacheModule,
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
ExchangeRateDataModule,
OrderModule,
PlatformModule,
PortfolioModule,
PrismaModule,
RedisCacheModule,
SymbolProfileModule
],
providers: [ImportService]
})
export class ImportModule {}

View File

@ -0,0 +1,616 @@
import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import {
Activity,
ActivityError
} from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { PlatformService } from '@ghostfolio/api/app/platform/platform.service';
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering/data-gathering.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import {
DATE_FORMAT,
getAssetProfileIdentifier,
parseDate
} from '@ghostfolio/common/helper';
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import {
AccountWithPlatform,
OrderWithAccount
} from '@ghostfolio/common/types';
import { Injectable } from '@nestjs/common';
import { DataSource, Prisma, SymbolProfile } from '@prisma/client';
import Big from 'big.js';
import { endOfToday, format, isAfter, isSameSecond, parseISO } from 'date-fns';
import { uniqBy } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
@Injectable()
export class ImportService {
public constructor(
private readonly accountService: AccountService,
private readonly configurationService: ConfigurationService,
private readonly dataGatheringService: DataGatheringService,
private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly orderService: OrderService,
private readonly platformService: PlatformService,
private readonly portfolioService: PortfolioService,
private readonly symbolProfileService: SymbolProfileService
) {}
public async getDividends({
dataSource,
symbol,
userCurrency
}: UniqueAsset & { userCurrency: string }): Promise<Activity[]> {
try {
const { firstBuyDate, historicalData, orders } =
await this.portfolioService.getPosition(dataSource, undefined, symbol);
const [[assetProfile], dividends] = await Promise.all([
this.symbolProfileService.getSymbolProfiles([
{
dataSource,
symbol
}
]),
await this.dataProviderService.getDividends({
dataSource,
symbol,
from: parseDate(firstBuyDate),
granularity: 'day',
to: new Date()
})
]);
const accounts = orders.map((order) => {
return order.Account;
});
const Account = this.isUniqueAccount(accounts) ? accounts[0] : undefined;
return Object.entries(dividends).map(([dateString, { marketPrice }]) => {
const quantity =
historicalData.find((historicalDataItem) => {
return historicalDataItem.date === dateString;
})?.quantity ?? 0;
const value = new Big(quantity).mul(marketPrice).toNumber();
const date = parseDate(dateString);
const isDuplicate = orders.some((activity) => {
return (
activity.accountId === Account?.id &&
activity.SymbolProfile.currency === assetProfile.currency &&
activity.SymbolProfile.dataSource === assetProfile.dataSource &&
isSameSecond(activity.date, date) &&
activity.quantity === quantity &&
activity.SymbolProfile.symbol === assetProfile.symbol &&
activity.type === 'DIVIDEND' &&
activity.unitPrice === marketPrice
);
});
const error: ActivityError = isDuplicate
? { code: 'IS_DUPLICATE' }
: undefined;
return {
Account,
date,
error,
quantity,
value,
accountId: Account?.id,
accountUserId: undefined,
comment: undefined,
createdAt: undefined,
fee: 0,
feeInBaseCurrency: 0,
id: assetProfile.id,
isDraft: false,
SymbolProfile: <SymbolProfile>(<unknown>assetProfile),
symbolProfileId: assetProfile.id,
type: 'DIVIDEND',
unitPrice: marketPrice,
updatedAt: undefined,
userId: Account?.userId,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
value,
assetProfile.currency,
userCurrency
)
};
});
} catch {
return [];
}
}
public async import({
accountsDto,
activitiesDto,
isDryRun = false,
maxActivitiesToImport,
userCurrency,
userId
}: {
accountsDto: Partial<CreateAccountDto>[];
activitiesDto: Partial<CreateOrderDto>[];
isDryRun?: boolean;
maxActivitiesToImport: number;
userCurrency: string;
userId: string;
}): Promise<Activity[]> {
const accountIdMapping: { [oldAccountId: string]: string } = {};
if (!isDryRun && accountsDto?.length) {
const [existingAccounts, existingPlatforms] = await Promise.all([
this.accountService.accounts({
where: {
id: {
in: accountsDto.map(({ id }) => {
return id;
})
}
}
}),
this.platformService.getPlatforms()
]);
for (const account of accountsDto) {
// Check if there is any existing account with the same ID
const accountWithSameId = existingAccounts.find(
(existingAccount) => existingAccount.id === account.id
);
// If there is no account or if the account belongs to a different user then create a new account
if (!accountWithSameId || accountWithSameId.userId !== userId) {
let oldAccountId: string;
const platformId = account.platformId;
delete account.platformId;
if (accountWithSameId) {
oldAccountId = account.id;
delete account.id;
}
let accountObject: Prisma.AccountCreateInput = {
...account,
User: { connect: { id: userId } }
};
if (
existingPlatforms.some(({ id }) => {
return id === platformId;
})
) {
accountObject = {
...accountObject,
Platform: { connect: { id: platformId } }
};
}
const newAccount = await this.accountService.createAccount(
accountObject,
userId
);
// Store the new to old account ID mappings for updating activities
if (accountWithSameId && oldAccountId) {
accountIdMapping[oldAccountId] = newAccount.id;
}
}
}
}
for (const activity of activitiesDto) {
if (!activity.dataSource) {
if (activity.type === 'ITEM' || activity.type === 'LIABILITY') {
activity.dataSource = DataSource.MANUAL;
} else {
activity.dataSource =
this.dataProviderService.getDataSourceForImport();
}
}
// If a new account is created, then update the accountId in all activities
if (!isDryRun) {
if (Object.keys(accountIdMapping).includes(activity.accountId)) {
activity.accountId = accountIdMapping[activity.accountId];
}
}
}
const assetProfiles = await this.validateActivities({
activitiesDto,
maxActivitiesToImport
});
const activitiesExtendedWithErrors = await this.extendActivitiesWithErrors({
activitiesDto,
userCurrency,
userId
});
const accounts = (await this.accountService.getAccounts(userId)).map(
({ id, name }) => {
return { id, name };
}
);
if (isDryRun) {
accountsDto.forEach(({ id, name }) => {
accounts.push({ id, name });
});
}
const activities: Activity[] = [];
for (let [
index,
{
accountId,
comment,
date,
error,
fee,
quantity,
SymbolProfile,
type,
unitPrice
}
] of activitiesExtendedWithErrors.entries()) {
const assetProfile = assetProfiles[
getAssetProfileIdentifier({
dataSource: SymbolProfile.dataSource,
symbol: SymbolProfile.symbol
})
] ?? {
currency: SymbolProfile.currency,
dataSource: SymbolProfile.dataSource,
symbol: SymbolProfile.symbol
};
const {
assetClass,
assetSubClass,
countries,
createdAt,
currency,
dataSource,
figi,
figiComposite,
figiShareClass,
id,
isin,
name,
scraperConfiguration,
sectors,
symbol,
symbolMapping,
url,
updatedAt
} = assetProfile;
const validatedAccount = accounts.find(({ id }) => {
return id === accountId;
});
let order:
| OrderWithAccount
| (Omit<OrderWithAccount, 'Account'> & {
Account?: { id: string; name: string };
});
if (SymbolProfile.currency !== assetProfile.currency) {
// Convert the unit price and fee to the asset currency if the imported
// activity is in a different currency
unitPrice = await this.exchangeRateDataService.toCurrencyAtDate(
unitPrice,
SymbolProfile.currency,
assetProfile.currency,
date
);
if (!unitPrice) {
throw new Error(
`activities.${index} historical exchange rate at ${format(
date,
DATE_FORMAT
)} is not available from "${SymbolProfile.currency}" to "${
assetProfile.currency
}"`
);
}
fee = await this.exchangeRateDataService.toCurrencyAtDate(
fee,
SymbolProfile.currency,
assetProfile.currency,
date
);
}
if (isDryRun) {
order = {
comment,
date,
fee,
quantity,
type,
unitPrice,
userId,
accountId: validatedAccount?.id,
accountUserId: undefined,
createdAt: new Date(),
id: uuidv4(),
isDraft: isAfter(date, endOfToday()),
SymbolProfile: {
assetClass,
assetSubClass,
countries,
createdAt,
currency,
dataSource,
figi,
figiComposite,
figiShareClass,
id,
isin,
name,
scraperConfiguration,
sectors,
symbol,
symbolMapping,
updatedAt,
url,
comment: assetProfile.comment
},
Account: validatedAccount,
symbolProfileId: undefined,
updatedAt: new Date()
};
} else {
if (error) {
continue;
}
order = await this.orderService.createOrder({
comment,
date,
fee,
quantity,
type,
unitPrice,
userId,
accountId: validatedAccount?.id,
SymbolProfile: {
connectOrCreate: {
create: {
currency,
dataSource,
symbol
},
where: {
dataSource_symbol: {
dataSource,
symbol
}
}
}
},
updateAccountBalance: false,
User: { connect: { id: userId } }
});
}
const value = new Big(quantity).mul(unitPrice).toNumber();
activities.push({
...order,
error,
value,
feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
fee,
currency,
userCurrency
),
// @ts-ignore
SymbolProfile: assetProfile,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
value,
currency,
userCurrency
)
});
}
activities.sort((activity1, activity2) => {
return Number(activity1.date) - Number(activity2.date);
});
if (!isDryRun) {
// Gather symbol data in the background, if not dry run
const uniqueActivities = uniqBy(activities, ({ SymbolProfile }) => {
return getAssetProfileIdentifier({
dataSource: SymbolProfile.dataSource,
symbol: SymbolProfile.symbol
});
});
this.dataGatheringService.gatherSymbols(
uniqueActivities.map(({ date, SymbolProfile }) => {
return {
date,
dataSource: SymbolProfile.dataSource,
symbol: SymbolProfile.symbol
};
})
);
}
return activities;
}
private async extendActivitiesWithErrors({
activitiesDto,
userCurrency,
userId
}: {
activitiesDto: Partial<CreateOrderDto>[];
userCurrency: string;
userId: string;
}): Promise<Partial<Activity>[]> {
let { activities: existingActivities } = await this.orderService.getOrders({
userCurrency,
userId,
includeDrafts: true,
withExcludedAccounts: true
});
return activitiesDto.map(
({
accountId,
comment,
currency,
dataSource,
date: dateString,
fee,
quantity,
symbol,
type,
unitPrice
}) => {
const date = parseISO(dateString);
const isDuplicate = existingActivities.some((activity) => {
return (
activity.accountId === accountId &&
activity.SymbolProfile.currency === currency &&
activity.SymbolProfile.dataSource === dataSource &&
isSameSecond(activity.date, date) &&
activity.fee === fee &&
activity.quantity === quantity &&
activity.SymbolProfile.symbol === symbol &&
activity.type === type &&
activity.unitPrice === unitPrice
);
});
const error: ActivityError = isDuplicate
? { code: 'IS_DUPLICATE' }
: undefined;
return {
accountId,
comment,
date,
error,
fee,
quantity,
type,
unitPrice,
SymbolProfile: {
currency,
dataSource,
symbol,
assetClass: null,
assetSubClass: null,
comment: null,
countries: null,
createdAt: undefined,
figi: null,
figiComposite: null,
figiShareClass: null,
id: undefined,
isin: null,
name: null,
scraperConfiguration: null,
sectors: null,
symbolMapping: null,
updatedAt: undefined,
url: null
}
};
}
);
}
private isUniqueAccount(accounts: AccountWithPlatform[]) {
const uniqueAccountIds = new Set<string>();
for (const account of accounts) {
uniqueAccountIds.add(account.id);
}
return uniqueAccountIds.size === 1;
}
private async validateActivities({
activitiesDto,
maxActivitiesToImport
}: {
activitiesDto: Partial<CreateOrderDto>[];
maxActivitiesToImport: number;
}) {
if (activitiesDto?.length > maxActivitiesToImport) {
throw new Error(`Too many activities (${maxActivitiesToImport} at most)`);
}
const assetProfiles: {
[assetProfileIdentifier: string]: Partial<SymbolProfile>;
} = {};
const uniqueActivitiesDto = uniqBy(
activitiesDto,
({ dataSource, symbol }) => {
return getAssetProfileIdentifier({ dataSource, symbol });
}
);
for (const [
index,
{ currency, dataSource, symbol }
] of uniqueActivitiesDto.entries()) {
if (!this.configurationService.get('DATA_SOURCES').includes(dataSource)) {
throw new Error(
`activities.${index}.dataSource ("${dataSource}") is not valid`
);
}
const assetProfile = (
await this.dataProviderService.getAssetProfiles([
{ dataSource, symbol }
])
)?.[symbol];
if (!assetProfile?.name) {
throw new Error(
`activities.${index}.symbol ("${symbol}") is not valid for the specified data source ("${dataSource}")`
);
}
if (
assetProfile.currency !== currency &&
!this.exchangeRateDataService.hasCurrencyPair(
currency,
assetProfile.currency
)
) {
throw new Error(
`activities.${index}.currency ("${currency}") does not match with "${assetProfile.currency}" and no exchange rate is available from "${currency}" to "${assetProfile.currency}"`
);
}
assetProfiles[getAssetProfileIdentifier({ dataSource, symbol })] =
assetProfile;
}
return assetProfiles;
}
}

View File

@ -1,13 +1,15 @@
import { Controller, Get } from '@nestjs/common';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import { InfoItem } from '@ghostfolio/common/interfaces';
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { InfoService } from './info.service';
import { InfoItem } from './interfaces/info-item.interface';
@Controller('info')
export class InfoController {
public constructor(private readonly infoService: InfoService) {}
@Get()
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getInfo(): Promise<InfoItem> {
return this.infoService.get();
}

View File

@ -1,5 +1,15 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { BenchmarkModule } from '@ghostfolio/api/app/benchmark/benchmark.module';
import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { TagModule } from '@ghostfolio/api/services/tag/tag.module';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
@ -7,13 +17,24 @@ import { InfoController } from './info.controller';
import { InfoService } from './info.service';
@Module({
controllers: [InfoController],
imports: [
BenchmarkModule,
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
ExchangeRateDataModule,
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '30 days' }
})
}),
PlatformModule,
PropertyModule,
RedisCacheModule,
SymbolProfileModule,
TagModule,
UserModule
],
controllers: [InfoController],
providers: [ConfigurationService, InfoService, PrismaService]
providers: [InfoService]
})
export class InfoModule {}

View File

@ -1,58 +1,370 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { permissions } from '@ghostfolio/helper';
import { Injectable } from '@nestjs/common';
import { BenchmarkService } from '@ghostfolio/api/app/benchmark/benchmark.service';
import { PlatformService } from '@ghostfolio/api/app/platform/platform.service';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { UserService } from '@ghostfolio/api/app/user/user.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import {
DEFAULT_CURRENCY,
PROPERTY_BETTER_UPTIME_MONITOR_ID,
PROPERTY_COUNTRIES_OF_SUBSCRIBERS,
PROPERTY_DEMO_USER_ID,
PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_SLACK_COMMUNITY_USERS,
PROPERTY_STRIPE_CONFIG,
ghostfolioFearAndGreedIndexDataSource
} from '@ghostfolio/common/config';
import {
DATE_FORMAT,
encodeDataSource,
extractNumberFromString
} from '@ghostfolio/common/helper';
import {
InfoItem,
Statistics,
Subscription
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import { SubscriptionOffer } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Currency } from '@prisma/client';
import { InfoItem } from './interfaces/info-item.interface';
import * as cheerio from 'cheerio';
import { format, subDays } from 'date-fns';
import got from 'got';
@Injectable()
export class InfoService {
private static DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f';
private static CACHE_KEY_STATISTICS = 'STATISTICS';
public constructor(
private readonly benchmarkService: BenchmarkService,
private readonly configurationService: ConfigurationService,
private jwtService: JwtService,
private prisma: PrismaService
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly jwtService: JwtService,
private readonly platformService: PlatformService,
private readonly propertyService: PropertyService,
private readonly redisCacheService: RedisCacheService,
private readonly tagService: TagService,
private readonly userService: UserService
) {}
public async get(): Promise<InfoItem> {
const platforms = await this.prisma.platform.findMany({
orderBy: { name: 'asc' },
select: { id: true, name: true }
const info: Partial<InfoItem> = {};
let isReadOnlyMode: boolean;
const platforms = await this.platformService.getPlatforms({
orderBy: { name: 'asc' }
});
const globalPermissions: string[] = [];
if (this.configurationService.get('ENABLE_FEATURE_BLOG')) {
globalPermissions.push(permissions.enableBlog);
}
if (this.configurationService.get('ENABLE_FEATURE_FEAR_AND_GREED_INDEX')) {
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
info.fearAndGreedDataSource = encodeDataSource(
ghostfolioFearAndGreedIndexDataSource
);
} else {
info.fearAndGreedDataSource = ghostfolioFearAndGreedIndexDataSource;
}
globalPermissions.push(permissions.enableFearAndGreedIndex);
}
if (this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE')) {
isReadOnlyMode = (await this.propertyService.getByKey(
PROPERTY_IS_READ_ONLY_MODE
)) as boolean;
}
if (this.configurationService.get('ENABLE_FEATURE_SOCIAL_LOGIN')) {
globalPermissions.push(permissions.enableSocialLogin);
}
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
globalPermissions.push(permissions.enableSubscription);
if (this.configurationService.get('ENABLE_FEATURE_STATISTICS')) {
globalPermissions.push(permissions.enableStatistics);
}
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
globalPermissions.push(permissions.enableSubscription);
info.countriesOfSubscribers =
((await this.propertyService.getByKey(
PROPERTY_COUNTRIES_OF_SUBSCRIBERS
)) as string[]) ?? [];
info.stripePublicKey = this.configurationService.get('STRIPE_PUBLIC_KEY');
}
if (this.configurationService.get('ENABLE_FEATURE_SYSTEM_MESSAGE')) {
globalPermissions.push(permissions.enableSystemMessage);
}
const isUserSignupEnabled =
await this.propertyService.isUserSignupEnabled();
if (isUserSignupEnabled) {
globalPermissions.push(permissions.createUserAccount);
}
const [benchmarks, demoAuthToken, statistics, subscriptions, tags] =
await Promise.all([
this.benchmarkService.getBenchmarkAssetProfiles(),
this.getDemoAuthToken(),
this.getStatistics(),
this.getSubscriptions(),
this.tagService.get()
]);
return {
...info,
benchmarks,
demoAuthToken,
globalPermissions,
isReadOnlyMode,
platforms,
currencies: Object.values(Currency),
demoAuthToken: this.getDemoAuthToken(),
lastDataGathering: await this.getLastDataGathering()
statistics,
subscriptions,
tags,
baseCurrency: DEFAULT_CURRENCY,
currencies: this.exchangeRateDataService.getCurrencies()
};
}
private getDemoAuthToken() {
return this.jwtService.sign({
id: InfoService.DEMO_USER_ID
private async countActiveUsers(aDays: number) {
return this.userService.count({
where: {
AND: [
{
NOT: {
Analytics: null
}
},
{
Analytics: {
updatedAt: {
gt: subDays(new Date(), aDays)
}
}
}
]
}
});
}
private async getLastDataGathering() {
const lastDataGathering = await this.prisma.property.findUnique({
where: { key: 'LAST_DATA_GATHERING' }
});
private async countDockerHubPulls(): Promise<number> {
try {
const abortController = new AbortController();
return lastDataGathering?.value ? new Date(lastDataGathering.value) : null;
setTimeout(() => {
abortController.abort();
}, this.configurationService.get('REQUEST_TIMEOUT'));
const { pull_count } = await got(
`https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio`,
{
headers: { 'User-Agent': 'request' },
// @ts-ignore
signal: abortController.signal
}
).json<any>();
return pull_count;
} catch (error) {
Logger.error(error, 'InfoService - DockerHub');
return undefined;
}
}
private async countGitHubContributors(): Promise<number> {
try {
const abortController = new AbortController();
setTimeout(() => {
abortController.abort();
}, this.configurationService.get('REQUEST_TIMEOUT'));
const { body } = await got('https://github.com/ghostfolio/ghostfolio', {
// @ts-ignore
signal: abortController.signal
});
const $ = cheerio.load(body);
return extractNumberFromString({
value: $(
`a[href="/ghostfolio/ghostfolio/graphs/contributors"] .Counter`
).text()
});
} catch (error) {
Logger.error(error, 'InfoService - GitHub');
return undefined;
}
}
private async countGitHubStargazers(): Promise<number> {
try {
const abortController = new AbortController();
setTimeout(() => {
abortController.abort();
}, this.configurationService.get('REQUEST_TIMEOUT'));
const { stargazers_count } = await got(
`https://api.github.com/repos/ghostfolio/ghostfolio`,
{
headers: { 'User-Agent': 'request' },
// @ts-ignore
signal: abortController.signal
}
).json<any>();
return stargazers_count;
} catch (error) {
Logger.error(error, 'InfoService - GitHub');
return undefined;
}
}
private async countNewUsers(aDays: number) {
return this.userService.count({
where: {
AND: [
{
NOT: {
Analytics: null
}
},
{
createdAt: {
gt: subDays(new Date(), aDays)
}
}
]
}
});
}
private async countSlackCommunityUsers() {
return (await this.propertyService.getByKey(
PROPERTY_SLACK_COMMUNITY_USERS
)) as string;
}
private async getDemoAuthToken() {
const demoUserId = (await this.propertyService.getByKey(
PROPERTY_DEMO_USER_ID
)) as string;
if (demoUserId) {
return this.jwtService.sign({
id: demoUserId
});
}
return undefined;
}
private async getStatistics() {
if (!this.configurationService.get('ENABLE_FEATURE_STATISTICS')) {
return undefined;
}
let statistics: Statistics;
try {
statistics = JSON.parse(
await this.redisCacheService.get(InfoService.CACHE_KEY_STATISTICS)
);
if (statistics) {
return statistics;
}
} catch {}
const activeUsers1d = await this.countActiveUsers(1);
const activeUsers30d = await this.countActiveUsers(30);
const newUsers30d = await this.countNewUsers(30);
const dockerHubPulls = await this.countDockerHubPulls();
const gitHubContributors = await this.countGitHubContributors();
const gitHubStargazers = await this.countGitHubStargazers();
const slackCommunityUsers = await this.countSlackCommunityUsers();
const uptime = await this.getUptime();
statistics = {
activeUsers1d,
activeUsers30d,
dockerHubPulls,
gitHubContributors,
gitHubStargazers,
newUsers30d,
slackCommunityUsers,
uptime
};
await this.redisCacheService.set(
InfoService.CACHE_KEY_STATISTICS,
JSON.stringify(statistics)
);
return statistics;
}
private async getSubscriptions(): Promise<{
[offer in SubscriptionOffer]: Subscription;
}> {
if (!this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
return undefined;
}
return (
((await this.propertyService.getByKey(PROPERTY_STRIPE_CONFIG)) as any) ??
{}
);
}
private async getUptime(): Promise<number> {
{
try {
const monitorId = (await this.propertyService.getByKey(
PROPERTY_BETTER_UPTIME_MONITOR_ID
)) as string;
const abortController = new AbortController();
setTimeout(() => {
abortController.abort();
}, this.configurationService.get('REQUEST_TIMEOUT'));
const { data } = await got(
`https://uptime.betterstack.com/api/v2/monitors/${monitorId}/sla?from=${format(
subDays(new Date(), 90),
DATE_FORMAT
)}&to${format(new Date(), DATE_FORMAT)}`,
{
headers: {
Authorization: `Bearer ${this.configurationService.get(
'BETTER_UPTIME_API_KEY'
)}`
},
// @ts-ignore
signal: abortController.signal
}
).json<any>();
return data.attributes.availability / 100;
} catch (error) {
Logger.error(error, 'InfoService - Better Stack');
return undefined;
}
}
}
}

View File

@ -1,13 +0,0 @@
import { Currency } from '@prisma/client';
export interface InfoItem {
currencies: Currency[];
demoAuthToken: string;
globalPermissions: string[];
lastDataGathering?: Date;
message?: {
text: string;
type: string;
};
platforms: { id: string; name: string }[];
}

View File

@ -1,6 +0,0 @@
import { Account, Settings, User } from '@prisma/client';
export type UserWithSettings = User & {
Account: Account[];
Settings: Settings;
};

View File

@ -0,0 +1,54 @@
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import {
Controller,
Get,
HttpStatus,
Param,
Query,
Res,
UseInterceptors
} from '@nestjs/common';
import { DataSource } from '@prisma/client';
import { Response } from 'express';
import { LogoService } from './logo.service';
@Controller('logo')
export class LogoController {
public constructor(private readonly logoService: LogoService) {}
@Get(':dataSource/:symbol')
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async getLogoByDataSourceAndSymbol(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string,
@Res() response: Response
) {
try {
const buffer = await this.logoService.getLogoByDataSourceAndSymbol({
dataSource,
symbol
});
response.contentType('image/png');
response.send(buffer);
} catch {
response.status(HttpStatus.NOT_FOUND).send();
}
}
@Get()
public async getLogoByUrl(
@Query('url') url: string,
@Res() response: Response
) {
try {
const buffer = await this.logoService.getLogoByUrl(url);
response.contentType('image/png');
response.send(buffer);
} catch {
response.status(HttpStatus.NOT_FOUND).send();
}
}
}

View File

@ -0,0 +1,13 @@
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { Module } from '@nestjs/common';
import { LogoController } from './logo.controller';
import { LogoService } from './logo.service';
@Module({
controllers: [LogoController],
imports: [ConfigurationModule, SymbolProfileModule],
providers: [LogoService]
})
export class LogoModule {}

View File

@ -0,0 +1,61 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import { HttpException, Injectable } from '@nestjs/common';
import { DataSource } from '@prisma/client';
import got from 'got';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
@Injectable()
export class LogoService {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly symbolProfileService: SymbolProfileService
) {}
public async getLogoByDataSourceAndSymbol({
dataSource,
symbol
}: UniqueAsset) {
if (!DataSource[dataSource]) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([
{ dataSource, symbol }
]);
if (!assetProfile) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
return this.getBuffer(assetProfile.url);
}
public async getLogoByUrl(aUrl: string) {
return this.getBuffer(aUrl);
}
private getBuffer(aUrl: string) {
const abortController = new AbortController();
setTimeout(() => {
abortController.abort();
}, this.configurationService.get('REQUEST_TIMEOUT'));
return got(
`https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${aUrl}&size=64`,
{
headers: { 'User-Agent': 'request' },
// @ts-ignore
signal: abortController.signal
}
).buffer();
}
}

View File

@ -1,31 +1,76 @@
import { Currency, DataSource, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
import {
AssetClass,
AssetSubClass,
DataSource,
Tag,
Type
} from '@prisma/client';
import { Transform, TransformFnParams } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsEnum,
IsISO8601,
IsNumber,
IsOptional,
IsString,
Min
} from 'class-validator';
import { isString } from 'lodash';
export class CreateOrderDto {
@IsOptional()
@IsString()
accountId: string;
accountId?: string;
@IsOptional()
@IsEnum(AssetClass, { each: true })
assetClass?: AssetClass;
@IsOptional()
@IsEnum(AssetSubClass, { each: true })
assetSubClass?: AssetSubClass;
@IsOptional()
@IsString()
@Transform(({ value }: TransformFnParams) =>
isString(value) ? value.trim() : value
)
comment?: string;
@IsString()
currency: Currency;
currency: string;
@IsString()
dataSource: DataSource;
@IsOptional()
@IsEnum(DataSource, { each: true })
dataSource?: DataSource;
@IsISO8601()
date: string;
@IsNumber()
@Min(0)
fee: number;
@IsNumber()
@Min(0)
quantity: number;
@IsString()
symbol: string;
@IsString()
@IsArray()
@IsOptional()
tags?: Tag[];
@IsEnum(Type, { each: true })
type: Type;
@IsNumber()
@Min(0)
unitPrice: number;
@IsBoolean()
@IsOptional()
updateAccountBalance?: boolean;
}

View File

@ -0,0 +1,19 @@
import { OrderWithAccount } from '@ghostfolio/common/types';
export interface Activities {
activities: Activity[];
count: number;
}
export interface Activity extends OrderWithAccount {
error?: ActivityError;
feeInBaseCurrency: number;
updateAccountBalance?: boolean;
value: number;
valueInBaseCurrency: number;
}
export interface ActivityError {
code: 'IS_DUPLICATE';
message?: string;
}

View File

@ -1,5 +0,0 @@
import { Account, Order, Platform } from '@prisma/client';
type AccountWithPlatform = Account & { Platform?: Platform };
export type OrderWithAccount = Order & { Account?: AccountWithPlatform };

View File

@ -1,7 +1,14 @@
import { RequestWithUser } from '@ghostfolio/api/app/interfaces/request-with-user.type';
import { nullifyValuesInObjects } from '@ghostfolio/api/helper/object.helper';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service';
import { getPermissions, hasPermission, permissions } from '@ghostfolio/helper';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { RedactValuesInResponseInterceptor } from '@ghostfolio/api/interceptors/redact-values-in-response.interceptor';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import { ApiService } from '@ghostfolio/api/services/api/api.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering/data-gathering.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Body,
Controller,
@ -13,34 +20,49 @@ import {
Param,
Post,
Put,
UseGuards
Query,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Order as OrderModel } from '@prisma/client';
import { Order as OrderModel, Prisma } from '@prisma/client';
import { parseISO } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { CreateOrderDto } from './create-order.dto';
import { Activities } from './interfaces/activities.interface';
import { OrderService } from './order.service';
import { UpdateOrderDto } from './update-order.dto';
@Controller('order')
export class OrderController {
public constructor(
private readonly apiService: ApiService,
private readonly dataGatheringService: DataGatheringService,
private readonly impersonationService: ImpersonationService,
private readonly orderService: OrderService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Delete()
@HasPermission(permissions.deleteOrder)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteOrders(): Promise<number> {
return this.orderService.deleteOrders({
userId: this.request.user.id
});
}
@Delete(':id')
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async deleteOrder(@Param('id') id: string): Promise<OrderModel> {
const order = await this.orderService.order({ id });
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.deleteOrder
)
!hasPermission(this.request.user.permissions, permissions.deleteOrder) ||
!order ||
order.userId !== this.request.user.id
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
@ -48,72 +70,102 @@ export class OrderController {
);
}
return this.orderService.deleteOrder(
{
id_userId: {
id,
userId: this.request.user.id
}
},
this.request.user.id
);
return this.orderService.deleteOrder({
id
});
}
@Get()
@UseGuards(AuthGuard('jwt'))
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor)
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getAllOrders(
@Headers('impersonation-id') impersonationId
): Promise<OrderModel[]> {
const impersonationUserId = await this.impersonationService.validateImpersonationId(
impersonationId,
this.request.user.id
);
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId,
@Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string,
@Query('skip') skip?: number,
@Query('sortColumn') sortColumn?: string,
@Query('sortDirection') sortDirection?: Prisma.SortOrder,
@Query('tags') filterByTags?: string,
@Query('take') take?: number
): Promise<Activities> {
const filters = this.apiService.buildFiltersFromQueryParams({
filterByAccounts,
filterByAssetClasses,
filterByTags
});
let orders = await this.orderService.orders({
include: {
Account: {
include: {
Platform: true
const impersonationUserId =
await this.impersonationService.validateImpersonationId(impersonationId);
const userCurrency = this.request.user.Settings.settings.baseCurrency;
const { activities, count } = await this.orderService.getOrders({
filters,
sortColumn,
sortDirection,
userCurrency,
includeDrafts: true,
skip: isNaN(skip) ? undefined : skip,
take: isNaN(take) ? undefined : take,
userId: impersonationUserId || this.request.user.id,
withExcludedAccounts: true
});
return { activities, count };
}
@HasPermission(permissions.createOrder)
@Post()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async createOrder(@Body() data: CreateOrderDto): Promise<OrderModel> {
const order = await this.orderService.createOrder({
...data,
date: parseISO(data.date),
SymbolProfile: {
connectOrCreate: {
create: {
currency: data.currency,
dataSource: data.dataSource,
symbol: data.symbol
},
where: {
dataSource_symbol: {
dataSource: data.dataSource,
symbol: data.symbol
}
}
}
},
orderBy: { date: 'desc' },
where: { userId: impersonationUserId || this.request.user.id }
User: { connect: { id: this.request.user.id } },
userId: this.request.user.id
});
if (
impersonationUserId &&
!hasPermission(
getPermissions(this.request.user.role),
permissions.readForeignPortfolio
)
) {
orders = nullifyValuesInObjects(orders, ['fee', 'quantity', 'unitPrice']);
if (data.dataSource && !order.isDraft) {
// Gather symbol data in the background, if data source is set
// (not MANUAL) and not draft
this.dataGatheringService.gatherSymbols([
{
dataSource: data.dataSource,
date: order.date,
symbol: data.symbol
}
]);
}
return orders;
return order;
}
@Get(':id')
@UseGuards(AuthGuard('jwt'))
public async getOrderById(@Param('id') id: string): Promise<OrderModel> {
return this.orderService.order({
id_userId: {
id,
userId: this.request.user.id
}
@HasPermission(permissions.updateOrder)
@Put(':id')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async update(@Param('id') id: string, @Body() data: UpdateOrderDto) {
const originalOrder = await this.orderService.order({
id
});
}
@Post()
@UseGuards(AuthGuard('jwt'))
public async createOrder(@Body() data: CreateOrderDto): Promise<OrderModel> {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.createOrder
)
) {
if (!originalOrder || originalOrder.userId !== this.request.user.id) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
@ -125,8 +177,8 @@ export class OrderController {
const accountId = data.accountId;
delete data.accountId;
return this.orderService.createOrder(
{
return this.orderService.updateOrder({
data: {
...data,
date,
Account: {
@ -134,66 +186,24 @@ export class OrderController {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
User: { connect: { id: this.request.user.id } }
},
this.request.user.id
);
}
@Put(':id')
@UseGuards(AuthGuard('jwt'))
public async update(@Param('id') id: string, @Body() data: UpdateOrderDto) {
if (
!hasPermission(
getPermissions(this.request.user.role),
permissions.updateOrder
)
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
const originalOrder = await this.orderService.order({
id_userId: {
id,
userId: this.request.user.id
}
});
if (!originalOrder) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
const date = parseISO(data.date);
const accountId = data.accountId;
delete data.accountId;
return this.orderService.updateOrder(
{
data: {
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
SymbolProfile: {
connect: {
dataSource_symbol: {
dataSource: data.dataSource,
symbol: data.symbol
}
},
User: { connect: { id: this.request.user.id } }
},
where: {
id_userId: {
id,
userId: this.request.user.id
update: {
assetClass: data.assetClass,
assetSubClass: data.assetSubClass,
name: data.symbol
}
}
},
User: { connect: { id: this.request.user.id } }
},
this.request.user.id
);
where: {
id
}
});
}
}

View File

@ -1,34 +1,37 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service';
import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { CacheModule } from '@ghostfolio/api/app/cache/cache.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { Module } from '@nestjs/common';
import { CacheService } from '../cache/cache.service';
import { RedisCacheModule } from '../redis-cache/redis-cache.module';
import { OrderController } from './order.controller';
import { OrderService } from './order.service';
@Module({
imports: [RedisCacheModule],
controllers: [OrderController],
providers: [
AlphaVantageService,
CacheService,
ConfigurationService,
DataGatheringService,
DataProviderService,
GhostfolioScraperApiService,
ImpersonationService,
OrderService,
PrismaService,
RakutenRapidApiService,
YahooFinanceService
]
exports: [OrderService],
imports: [
ApiModule,
CacheModule,
ConfigurationModule,
DataGatheringModule,
DataProviderModule,
ExchangeRateDataModule,
ImpersonationModule,
PrismaModule,
RedisCacheModule,
SymbolProfileModule,
UserModule
],
providers: [AccountBalanceService, AccountService, OrderService]
})
export class OrderModule {}

View File

@ -1,40 +1,452 @@
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering/data-gathering.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import {
GATHER_ASSET_PROFILE_PROCESS,
GATHER_ASSET_PROFILE_PROCESS_OPTIONS
} from '@ghostfolio/common/config';
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
import { Filter } from '@ghostfolio/common/interfaces';
import { OrderWithAccount } from '@ghostfolio/common/types';
import { Injectable } from '@nestjs/common';
import { Order, Prisma } from '@prisma/client';
import {
AssetClass,
AssetSubClass,
DataSource,
Order,
Prisma,
Tag,
Type as TypeOfOrder
} from '@prisma/client';
import Big from 'big.js';
import { endOfToday, isAfter } from 'date-fns';
import { groupBy } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import { CacheService } from '../cache/cache.service';
import { RedisCacheService } from '../redis-cache/redis-cache.service';
import { OrderWithAccount } from './interfaces/order-with-account.type';
import { Activities } from './interfaces/activities.interface';
@Injectable()
export class OrderService {
public constructor(
private readonly cacheService: CacheService,
private readonly accountService: AccountService,
private readonly dataGatheringService: DataGatheringService,
private readonly redisCacheService: RedisCacheService,
private prisma: PrismaService
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly prismaService: PrismaService,
private readonly symbolProfileService: SymbolProfileService
) {}
public async createOrder(
data: Prisma.OrderCreateInput & {
accountId?: string;
assetClass?: AssetClass;
assetSubClass?: AssetSubClass;
currency?: string;
dataSource?: DataSource;
symbol?: string;
tags?: Tag[];
updateAccountBalance?: boolean;
userId: string;
}
): Promise<Order> {
let Account;
if (data.accountId) {
Account = {
connect: {
id_userId: {
userId: data.userId,
id: data.accountId
}
}
};
}
const accountId = data.accountId;
let currency = data.currency;
const tags = data.tags ?? [];
const updateAccountBalance = data.updateAccountBalance ?? false;
const userId = data.userId;
if (
data.type === 'FEE' ||
data.type === 'INTEREST' ||
data.type === 'ITEM' ||
data.type === 'LIABILITY'
) {
const assetClass = data.assetClass;
const assetSubClass = data.assetSubClass;
currency = data.SymbolProfile.connectOrCreate.create.currency;
const dataSource: DataSource = 'MANUAL';
const id = uuidv4();
const name = data.SymbolProfile.connectOrCreate.create.symbol;
data.id = id;
data.SymbolProfile.connectOrCreate.create.assetClass = assetClass;
data.SymbolProfile.connectOrCreate.create.assetSubClass = assetSubClass;
data.SymbolProfile.connectOrCreate.create.currency = currency;
data.SymbolProfile.connectOrCreate.create.dataSource = dataSource;
data.SymbolProfile.connectOrCreate.create.name = name;
data.SymbolProfile.connectOrCreate.create.symbol = id;
data.SymbolProfile.connectOrCreate.where.dataSource_symbol = {
dataSource,
symbol: id
};
}
if (data.SymbolProfile.connectOrCreate.create.dataSource !== 'MANUAL') {
this.dataGatheringService.addJobToQueue({
data: {
dataSource: data.SymbolProfile.connectOrCreate.create.dataSource,
symbol: data.SymbolProfile.connectOrCreate.create.symbol
},
name: GATHER_ASSET_PROFILE_PROCESS,
opts: {
...GATHER_ASSET_PROFILE_PROCESS_OPTIONS,
jobId: getAssetProfileIdentifier({
dataSource: data.SymbolProfile.connectOrCreate.create.dataSource,
symbol: data.SymbolProfile.connectOrCreate.create.symbol
})
}
});
}
delete data.accountId;
delete data.assetClass;
delete data.assetSubClass;
if (!data.comment) {
delete data.comment;
}
delete data.currency;
delete data.dataSource;
delete data.symbol;
delete data.tags;
delete data.updateAccountBalance;
delete data.userId;
const orderData: Prisma.OrderCreateInput = data;
const isDraft =
data.type === 'FEE' ||
data.type === 'INTEREST' ||
data.type === 'ITEM' ||
data.type === 'LIABILITY'
? false
: isAfter(data.date as Date, endOfToday());
const order = await this.prismaService.order.create({
data: {
...orderData,
Account,
isDraft,
tags: {
connect: tags.map(({ id }) => {
return { id };
})
}
}
});
if (updateAccountBalance === true) {
let amount = new Big(data.unitPrice)
.mul(data.quantity)
.plus(data.fee)
.toNumber();
if (data.type === 'BUY') {
amount = new Big(amount).mul(-1).toNumber();
}
await this.accountService.updateAccountBalance({
accountId,
amount,
currency,
userId,
date: data.date as Date
});
}
return order;
}
public async deleteOrder(
where: Prisma.OrderWhereUniqueInput
): Promise<Order> {
const order = await this.prismaService.order.delete({
where
});
if (
order.type === 'FEE' ||
order.type === 'INTEREST' ||
order.type === 'ITEM' ||
order.type === 'LIABILITY'
) {
await this.symbolProfileService.deleteById(order.symbolProfileId);
}
return order;
}
public async deleteOrders(where: Prisma.OrderWhereInput): Promise<number> {
const { count } = await this.prismaService.order.deleteMany({
where
});
return count;
}
public async getOrders({
filters,
includeDrafts = false,
skip,
sortColumn,
sortDirection,
take = Number.MAX_SAFE_INTEGER,
types,
userCurrency,
userId,
withExcludedAccounts = false
}: {
filters?: Filter[];
includeDrafts?: boolean;
skip?: number;
sortColumn?: string;
sortDirection?: Prisma.SortOrder;
take?: number;
types?: TypeOfOrder[];
userCurrency: string;
userId: string;
withExcludedAccounts?: boolean;
}): Promise<Activities> {
let orderBy: Prisma.Enumerable<Prisma.OrderOrderByWithRelationInput> = [
{ date: 'asc' }
];
const where: Prisma.OrderWhereInput = { userId };
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
});
if (filtersByAccount?.length > 0) {
where.accountId = {
in: filtersByAccount.map(({ id }) => {
return id;
})
};
}
if (includeDrafts === false) {
where.isDraft = false;
}
if (filtersByAssetClass?.length > 0) {
where.SymbolProfile = {
OR: [
{
AND: [
{
OR: filtersByAssetClass.map(({ id }) => {
return { assetClass: AssetClass[id] };
})
},
{
OR: [
{ SymbolProfileOverrides: { is: null } },
{ SymbolProfileOverrides: { assetClass: null } }
]
}
]
},
{
SymbolProfileOverrides: {
OR: filtersByAssetClass.map(({ id }) => {
return { assetClass: AssetClass[id] };
})
}
}
]
};
}
if (filtersByTag?.length > 0) {
where.tags = {
some: {
OR: filtersByTag.map(({ id }) => {
return { id };
})
}
};
}
if (sortColumn) {
orderBy = [{ [sortColumn]: sortDirection }];
}
if (types) {
where.OR = types.map((type) => {
return {
type: {
equals: type
}
};
});
}
const [orders, count] = await Promise.all([
this.orders({
orderBy,
skip,
take,
where,
include: {
// eslint-disable-next-line @typescript-eslint/naming-convention
Account: {
include: {
Platform: true
}
},
// eslint-disable-next-line @typescript-eslint/naming-convention
SymbolProfile: true,
tags: true
}
}),
this.prismaService.order.count({ where })
]);
const activities = orders
.filter((order) => {
return (
withExcludedAccounts ||
!order.Account ||
order.Account?.isExcluded === false
);
})
.map((order) => {
const value = new Big(order.quantity).mul(order.unitPrice).toNumber();
return {
...order,
value,
feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
order.fee,
order.SymbolProfile.currency,
userCurrency
),
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
value,
order.SymbolProfile.currency,
userCurrency
)
};
});
return { activities, count };
}
public async order(
orderWhereUniqueInput: Prisma.OrderWhereUniqueInput
): Promise<Order | null> {
return this.prisma.order.findUnique({
return this.prismaService.order.findUnique({
where: orderWhereUniqueInput
});
}
public async orders(params: {
public async updateOrder({
data,
where
}: {
data: Prisma.OrderUpdateInput & {
assetClass?: AssetClass;
assetSubClass?: AssetSubClass;
currency?: string;
dataSource?: DataSource;
symbol?: string;
tags?: Tag[];
};
where: Prisma.OrderWhereUniqueInput;
}): Promise<Order> {
if (data.Account.connect.id_userId.id === null) {
delete data.Account;
}
if (!data.comment) {
data.comment = null;
}
const tags = data.tags ?? [];
let isDraft = false;
if (
data.type === 'FEE' ||
data.type === 'INTEREST' ||
data.type === 'ITEM' ||
data.type === 'LIABILITY'
) {
delete data.SymbolProfile.connect;
} else {
delete data.SymbolProfile.update;
isDraft = isAfter(data.date as Date, endOfToday());
if (!isDraft) {
// Gather symbol data of order in the background, if not draft
this.dataGatheringService.gatherSymbols([
{
dataSource: data.SymbolProfile.connect.dataSource_symbol.dataSource,
date: <Date>data.date,
symbol: data.SymbolProfile.connect.dataSource_symbol.symbol
}
]);
}
}
delete data.assetClass;
delete data.assetSubClass;
delete data.currency;
delete data.dataSource;
delete data.symbol;
delete data.tags;
// Remove existing tags
await this.prismaService.order.update({
data: { tags: { set: [] } },
where
});
return this.prismaService.order.update({
data: {
...data,
isDraft,
tags: {
connect: tags.map(({ id }) => {
return { id };
})
}
},
where
});
}
private async orders(params: {
include?: Prisma.OrderInclude;
skip?: number;
take?: number;
cursor?: Prisma.OrderWhereUniqueInput;
where?: Prisma.OrderWhereInput;
orderBy?: Prisma.OrderOrderByInput;
orderBy?: Prisma.Enumerable<Prisma.OrderOrderByWithRelationInput>;
}): Promise<OrderWithAccount[]> {
const { include, skip, take, cursor, where, orderBy } = params;
return this.prisma.order.findMany({
return this.prismaService.order.findMany({
cursor,
include,
orderBy,
@ -43,63 +455,4 @@ export class OrderService {
where
});
}
public async createOrder(
data: Prisma.OrderCreateInput,
aUserId: string
): Promise<Order> {
this.redisCacheService.remove(`${aUserId}.portfolio`);
// Gather symbol data of order in the background
this.dataGatheringService.gatherSymbols([
{
date: <Date>data.date,
symbol: data.symbol
}
]);
await this.cacheService.flush(aUserId);
return this.prisma.order.create({
data
});
}
public async deleteOrder(
where: Prisma.OrderWhereUniqueInput,
aUserId: string
): Promise<Order> {
this.redisCacheService.remove(`${aUserId}.portfolio`);
return this.prisma.order.delete({
where
});
}
public async updateOrder(
params: {
where: Prisma.OrderWhereUniqueInput;
data: Prisma.OrderUpdateInput;
},
aUserId: string
): Promise<Order> {
const { data, where } = params;
this.redisCacheService.remove(`${aUserId}.portfolio`);
// Gather symbol data of order in the background
this.dataGatheringService.gatherSymbols([
{
date: <Date>data.date,
symbol: <string>data.symbol
}
]);
await this.cacheService.flush(aUserId);
return this.prisma.order.update({
data,
where
});
}
}

View File

@ -1,12 +1,44 @@
import { Currency, DataSource, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
import {
AssetClass,
AssetSubClass,
DataSource,
Tag,
Type
} from '@prisma/client';
import { Transform, TransformFnParams } from 'class-transformer';
import {
IsArray,
IsEnum,
IsISO8601,
IsNumber,
IsOptional,
IsString,
Min
} from 'class-validator';
import { isString } from 'lodash';
export class UpdateOrderDto {
@IsOptional()
@IsString()
accountId: string;
accountId?: string;
@IsEnum(AssetClass, { each: true })
@IsOptional()
assetClass?: AssetClass;
@IsEnum(AssetSubClass, { each: true })
@IsOptional()
assetSubClass?: AssetSubClass;
@IsOptional()
@IsString()
@Transform(({ value }: TransformFnParams) =>
isString(value) ? value.trim() : value
)
comment?: string;
@IsString()
currency: Currency;
currency: string;
@IsString()
dataSource: DataSource;
@ -15,20 +47,27 @@ export class UpdateOrderDto {
date: string;
@IsNumber()
@Min(0)
fee: number;
@IsString()
id: string;
@IsNumber()
@Min(0)
quantity: number;
@IsString()
symbol: string;
@IsArray()
@IsOptional()
tags?: Tag[];
@IsString()
type: Type;
@IsNumber()
@Min(0)
unitPrice: number;
}

Some files were not shown because too many files have changed in this diff Show More