diff --git a/CHANGELOG.md b/CHANGELOG.md index 51e4c9e3e..5efe84cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Moved the countries and sectors charts in the position detail dialog +- Restructured the server modules + +## 1.116.0 - 16.02.2022 + +### Added + +- Added a service to tweet the current _Fear & Greed Index_ (market mood) + +### Changed + +- Improved the mobile layout of the position detail dialog (countries and sectors charts) + +### Fixed + +- Fixed the `maxItems` attribute of the portfolio proportion chart component +- Fixed the time in market display of the portfolio summary tab on the home page + ## 1.115.0 - 13.02.2022 ### Added diff --git a/apps/api/src/app/access/access.module.ts b/apps/api/src/app/access/access.module.ts index 303989b21..d70388038 100644 --- a/apps/api/src/app/access/access.module.ts +++ b/apps/api/src/app/access/access.module.ts @@ -1,4 +1,4 @@ -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { Module } from '@nestjs/common'; import { AccessController } from './access.controller'; @@ -7,7 +7,7 @@ import { AccessService } from './access.service'; @Module({ controllers: [AccessController], exports: [AccessService], - imports: [], - providers: [AccessService, PrismaService] + imports: [PrismaModule], + providers: [AccessService] }) export class AccessModule {} diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 46b2c9060..e3d8a3bea 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -8,6 +8,7 @@ import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering.mod import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; +import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; @@ -65,6 +66,7 @@ import { UserModule } from './user/user.module'; }), SubscriptionModule, SymbolModule, + TwitterBotModule, UserModule ], controllers: [AppController], diff --git a/apps/api/src/app/auth-device/auth-device.module.ts b/apps/api/src/app/auth-device/auth-device.module.ts index 360930cf2..de6cd1cbc 100644 --- a/apps/api/src/app/auth-device/auth-device.module.ts +++ b/apps/api/src/app/auth-device/auth-device.module.ts @@ -1,18 +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 { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; +import { PrismaModule } from '@ghostfolio/api/services/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, ConfigurationService, PrismaService] + providers: [AuthDeviceService] }) export class AuthDeviceModule {} diff --git a/apps/api/src/app/auth/auth.module.ts b/apps/api/src/app/auth/auth.module.ts index 8a59ff82a..b25e4c18b 100644 --- a/apps/api/src/app/auth/auth.module.ts +++ b/apps/api/src/app/auth/auth.module.ts @@ -2,8 +2,8 @@ import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.s 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 { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -15,20 +15,20 @@ import { JwtStrategy } from './jwt.strategy'; @Module({ controllers: [AuthController], imports: [ + ConfigurationModule, JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '180 days' } }), + PrismaModule, SubscriptionModule, UserModule ], providers: [ AuthDeviceService, AuthService, - ConfigurationService, GoogleStrategy, JwtStrategy, - PrismaService, WebAuthService ] }) diff --git a/apps/api/src/app/cache/cache.module.ts b/apps/api/src/app/cache/cache.module.ts index a823c2d1e..7b427b7a0 100644 --- a/apps/api/src/app/cache/cache.module.ts +++ b/apps/api/src/app/cache/cache.module.ts @@ -1,30 +1,27 @@ import { CacheService } from '@ghostfolio/api/app/cache/cache.service'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; -import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering.module'; -import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module'; -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module'; import { Module } from '@nestjs/common'; import { CacheController } from './cache.controller'; @Module({ + exports: [CacheService], + controllers: [CacheController], imports: [ + ConfigurationModule, DataGatheringModule, DataProviderModule, ExchangeRateDataModule, + PrismaModule, RedisCacheModule, SymbolProfileModule ], - controllers: [CacheController], - providers: [ - CacheService, - ConfigurationService, - DataGatheringService, - PrismaService - ] + providers: [CacheService] }) export class CacheModule {} diff --git a/apps/api/src/app/import/import.module.ts b/apps/api/src/app/import/import.module.ts index 35781e499..03ff2d3f8 100644 --- a/apps/api/src/app/import/import.module.ts +++ b/apps/api/src/app/import/import.module.ts @@ -1,4 +1,4 @@ -import { CacheService } from '@ghostfolio/api/app/cache/cache.service'; +import { CacheModule } from '@ghostfolio/api/app/cache/cache.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.module'; @@ -11,7 +11,9 @@ import { ImportController } from './import.controller'; import { ImportService } from './import.service'; @Module({ + controllers: [ImportController], imports: [ + CacheModule, ConfigurationModule, DataGatheringModule, DataProviderModule, @@ -19,7 +21,6 @@ import { ImportService } from './import.service'; PrismaModule, RedisCacheModule ], - controllers: [ImportController], - providers: [CacheService, ImportService] + providers: [ImportService] }) export class ImportModule {} diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 50edd0eed..5828ac963 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -1,10 +1,9 @@ import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; -import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering.module'; -import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module'; -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile.module'; import { Module } from '@nestjs/common'; @@ -14,7 +13,9 @@ import { InfoController } from './info.controller'; import { InfoService } from './info.service'; @Module({ + controllers: [InfoController], imports: [ + ConfigurationModule, DataGatheringModule, DataProviderModule, ExchangeRateDataModule, @@ -22,16 +23,11 @@ import { InfoService } from './info.service'; secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } }), + PrismaModule, PropertyModule, RedisCacheModule, SymbolProfileModule ], - controllers: [InfoController], - providers: [ - ConfigurationService, - DataGatheringService, - InfoService, - PrismaService - ] + providers: [InfoService] }) export class InfoModule {} diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 203dc6dc4..f13679efc 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -9,7 +9,8 @@ import { PROPERTY_IS_READ_ONLY_MODE, PROPERTY_SLACK_COMMUNITY_USERS, PROPERTY_STRIPE_CONFIG, - PROPERTY_SYSTEM_MESSAGE + PROPERTY_SYSTEM_MESSAGE, + ghostfolioFearAndGreedIndexDataSource } from '@ghostfolio/common/config'; import { encodeDataSource } from '@ghostfolio/common/helper'; import { InfoItem } from '@ghostfolio/common/interfaces'; @@ -18,7 +19,6 @@ import { Subscription } from '@ghostfolio/common/interfaces/subscription.interfa import { permissions } from '@ghostfolio/common/permissions'; import { Injectable, Logger } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; -import { DataSource } from '@prisma/client'; import * as bent from 'bent'; import { subDays } from 'date-fns'; @@ -52,7 +52,9 @@ export class InfoService { } if (this.configurationService.get('ENABLE_FEATURE_FEAR_AND_GREED_INDEX')) { - info.fearAndGreedDataSource = encodeDataSource(DataSource.RAKUTEN); + info.fearAndGreedDataSource = encodeDataSource( + ghostfolioFearAndGreedIndexDataSource + ); } if (this.configurationService.get('ENABLE_FEATURE_IMPORT')) { diff --git a/apps/api/src/app/order/order.module.ts b/apps/api/src/app/order/order.module.ts index f2c790ce8..52ffc0266 100644 --- a/apps/api/src/app/order/order.module.ts +++ b/apps/api/src/app/order/order.module.ts @@ -1,5 +1,5 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; -import { CacheService } from '@ghostfolio/api/app/cache/cache.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 { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; @@ -15,7 +15,10 @@ import { OrderController } from './order.controller'; import { OrderService } from './order.service'; @Module({ + controllers: [OrderController], + exports: [OrderService], imports: [ + CacheModule, ConfigurationModule, DataGatheringModule, DataProviderModule, @@ -26,8 +29,6 @@ import { OrderService } from './order.service'; SymbolProfileModule, UserModule ], - controllers: [OrderController], - providers: [AccountService, CacheService, OrderService], - exports: [OrderService] + providers: [AccountService, OrderService] }) export class OrderModule {} diff --git a/apps/api/src/app/portfolio/portfolio.module.ts b/apps/api/src/app/portfolio/portfolio.module.ts index 85bc98e7a..5204f1795 100644 --- a/apps/api/src/app/portfolio/portfolio.module.ts +++ b/apps/api/src/app/portfolio/portfolio.module.ts @@ -20,6 +20,7 @@ import { PortfolioServiceNew } from './portfolio.service-new'; import { RulesService } from './rules.service'; @Module({ + controllers: [PortfolioController], exports: [PortfolioServiceStrategy], imports: [ AccessModule, @@ -34,7 +35,6 @@ import { RulesService } from './rules.service'; SymbolProfileModule, UserModule ], - controllers: [PortfolioController], providers: [ AccountService, CurrentRateService, diff --git a/apps/api/src/app/redis-cache/redis-cache.module.ts b/apps/api/src/app/redis-cache/redis-cache.module.ts index e3275276b..dcda94041 100644 --- a/apps/api/src/app/redis-cache/redis-cache.module.ts +++ b/apps/api/src/app/redis-cache/redis-cache.module.ts @@ -1,3 +1,4 @@ +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { CacheModule, Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; @@ -17,9 +18,10 @@ import { RedisCacheService } from './redis-cache.service'; store: redisStore, ttl: configurationService.get('CACHE_TTL') }) - }) + }), + ConfigurationModule ], - providers: [ConfigurationService, RedisCacheService], + providers: [RedisCacheService], exports: [RedisCacheService] }) export class RedisCacheModule {} diff --git a/apps/api/src/app/subscription/subscription.module.ts b/apps/api/src/app/subscription/subscription.module.ts index 95d16fb4d..df0861657 100644 --- a/apps/api/src/app/subscription/subscription.module.ts +++ b/apps/api/src/app/subscription/subscription.module.ts @@ -1,5 +1,5 @@ -import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { Module } from '@nestjs/common'; @@ -7,9 +7,9 @@ import { SubscriptionController } from './subscription.controller'; import { SubscriptionService } from './subscription.service'; @Module({ - imports: [PropertyModule], controllers: [SubscriptionController], - providers: [ConfigurationService, PrismaService, SubscriptionService], - exports: [SubscriptionService] + exports: [SubscriptionService], + imports: [ConfigurationModule, PrismaModule, PropertyModule], + providers: [SubscriptionService] }) export class SubscriptionModule {} diff --git a/apps/api/src/app/symbol/symbol.module.ts b/apps/api/src/app/symbol/symbol.module.ts index c5143632b..2b47334a6 100644 --- a/apps/api/src/app/symbol/symbol.module.ts +++ b/apps/api/src/app/symbol/symbol.module.ts @@ -8,13 +8,14 @@ import { SymbolController } from './symbol.controller'; import { SymbolService } from './symbol.service'; @Module({ + controllers: [SymbolController], + exports: [SymbolService], imports: [ ConfigurationModule, DataProviderModule, MarketDataModule, PrismaModule ], - controllers: [SymbolController], providers: [SymbolService] }) export class SymbolModule {} diff --git a/apps/api/src/app/user/user.module.ts b/apps/api/src/app/user/user.module.ts index ffbdc80db..976f5b6c3 100644 --- a/apps/api/src/app/user/user.module.ts +++ b/apps/api/src/app/user/user.module.ts @@ -1,6 +1,6 @@ import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; -import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; -import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; +import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -9,16 +9,18 @@ import { UserController } from './user.controller'; import { UserService } from './user.service'; @Module({ + controllers: [UserController], + exports: [UserService], imports: [ + ConfigurationModule, JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } }), + PrismaModule, PropertyModule, SubscriptionModule ], - controllers: [UserController], - providers: [ConfigurationService, PrismaService, UserService], - exports: [UserService] + providers: [UserService] }) export class UserModule {} diff --git a/apps/api/src/services/configuration.service.ts b/apps/api/src/services/configuration.service.ts index 0c358f099..abbfa6641 100644 --- a/apps/api/src/services/configuration.service.ts +++ b/apps/api/src/services/configuration.service.ts @@ -39,6 +39,10 @@ export class ConfigurationService { ROOT_URL: str({ default: 'http://localhost:4200' }), STRIPE_PUBLIC_KEY: str({ default: '' }), STRIPE_SECRET_KEY: str({ default: '' }), + TWITTER_ACCESS_TOKEN: str({ default: 'dummyAccessToken' }), + TWITTER_ACCESS_TOKEN_SECRET: str({ default: 'dummyAccessTokenSecret' }), + TWITTER_API_KEY: str({ default: 'dummyApiKey' }), + TWITTER_API_SECRET: str({ default: 'dummyApiSecret' }), WEB_AUTH_RP_ID: host({ default: 'localhost' }) }); } diff --git a/apps/api/src/services/cron.service.ts b/apps/api/src/services/cron.service.ts index be29d5a7b..b1ca2a513 100644 --- a/apps/api/src/services/cron.service.ts +++ b/apps/api/src/services/cron.service.ts @@ -3,12 +3,14 @@ import { Cron, CronExpression } from '@nestjs/schedule'; import { DataGatheringService } from './data-gathering.service'; import { ExchangeRateDataService } from './exchange-rate-data.service'; +import { TwitterBotService } from './twitter-bot/twitter-bot.service'; @Injectable() export class CronService { public constructor( private readonly dataGatheringService: DataGatheringService, - private readonly exchangeRateDataService: ExchangeRateDataService + private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly twitterBotService: TwitterBotService ) {} @Cron(CronExpression.EVERY_MINUTE) @@ -21,6 +23,11 @@ export class CronService { await this.exchangeRateDataService.loadCurrencies(); } + @Cron(CronExpression.EVERY_DAY_AT_5PM) + public async runEveryDayAtFivePM() { + this.twitterBotService.tweetFearAndGreedIndex(); + } + @Cron(CronExpression.EVERY_WEEKEND) public async runEveryWeekend() { await this.dataGatheringService.gatherProfileData(); diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index 86b049546..82cf08cbe 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -30,5 +30,9 @@ export interface Environment extends CleanedEnvAccessors { ROOT_URL: string; STRIPE_PUBLIC_KEY: string; STRIPE_SECRET_KEY: string; + TWITTER_ACCESS_TOKEN: string; + TWITTER_ACCESS_TOKEN_SECRET: string; + TWITTER_API_KEY: string; + TWITTER_API_SECRET: string; WEB_AUTH_RP_ID: string; } diff --git a/apps/api/src/services/twitter-bot/twitter-bot.module.ts b/apps/api/src/services/twitter-bot/twitter-bot.module.ts new file mode 100644 index 000000000..d74d6f10f --- /dev/null +++ b/apps/api/src/services/twitter-bot/twitter-bot.module.ts @@ -0,0 +1,11 @@ +import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; +import { TwitterBotService } from '@ghostfolio/api/services/twitter-bot/twitter-bot.service'; +import { Module } from '@nestjs/common'; + +@Module({ + exports: [TwitterBotService], + imports: [ConfigurationModule, SymbolModule], + providers: [TwitterBotService] +}) +export class TwitterBotModule {} diff --git a/apps/api/src/services/twitter-bot/twitter-bot.service.ts b/apps/api/src/services/twitter-bot/twitter-bot.service.ts new file mode 100644 index 000000000..750e4fe30 --- /dev/null +++ b/apps/api/src/services/twitter-bot/twitter-bot.service.ts @@ -0,0 +1,60 @@ +import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { + ghostfolioFearAndGreedIndexDataSource, + ghostfolioFearAndGreedIndexSymbol +} from '@ghostfolio/common/config'; +import { resolveFearAndGreedIndex } from '@ghostfolio/common/helper'; +import { Injectable, Logger } from '@nestjs/common'; +import { TwitterApi, TwitterApiReadWrite } from 'twitter-api-v2'; + +@Injectable() +export class TwitterBotService { + private twitterClient: TwitterApiReadWrite; + + public constructor( + private readonly configurationService: ConfigurationService, + private readonly symbolService: SymbolService + ) { + this.twitterClient = new TwitterApi({ + accessSecret: this.configurationService.get( + 'TWITTER_ACCESS_TOKEN_SECRET' + ), + accessToken: this.configurationService.get('TWITTER_ACCESS_TOKEN'), + appKey: this.configurationService.get('TWITTER_API_KEY'), + appSecret: this.configurationService.get('TWITTER_API_SECRET') + }).readWrite; + } + + public async tweetFearAndGreedIndex() { + if (!this.configurationService.get('ENABLE_FEATURE_FEAR_AND_GREED_INDEX')) { + return; + } + + try { + const symbolItem = await this.symbolService.get({ + dataGatheringItem: { + dataSource: ghostfolioFearAndGreedIndexDataSource, + symbol: ghostfolioFearAndGreedIndexSymbol + } + }); + + if (symbolItem?.marketPrice) { + const { emoji, text } = resolveFearAndGreedIndex( + symbolItem.marketPrice + ); + + const status = `Current Market Mood: ${emoji} ${text} (${symbolItem.marketPrice}/100)\n\n#FearAndGreed #Markets #ServiceTweet`; + const { data: createdTweet } = await this.twitterClient.v2.tweet( + status + ); + + Logger.log( + `Fear & Greed Index has been tweeted: https://twitter.com/ghostfolio_/status/${createdTweet.id}` + ); + } + } catch (error) { + Logger.error(error); + } + } +} diff --git a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts b/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts index a024b0f51..a42c80a37 100644 --- a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts +++ b/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts @@ -24,12 +24,9 @@ export class FearAndGreedIndexComponent implements OnChanges, OnInit { public ngOnInit() {} public ngOnChanges() { - this.fearAndGreedIndexEmoji = resolveFearAndGreedIndex( - this.fearAndGreedIndex - ).emoji; + const { emoji, text } = resolveFearAndGreedIndex(this.fearAndGreedIndex); - this.fearAndGreedIndexText = resolveFearAndGreedIndex( - this.fearAndGreedIndex - ).text; + this.fearAndGreedIndexEmoji = emoji; + this.fearAndGreedIndexText = text; } } diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html index dd2847c37..1fc5e1f85 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html @@ -2,7 +2,6 @@
Time in Market
- {{ timeInMarket }}
diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html index 3a4026a14..9ea26573f 100644 --- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html +++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html @@ -126,14 +126,15 @@
@@ -143,6 +144,14 @@ +
+ +
-
- -
-
-
Countries
+
+
Sectors
-
-
Sectors
+
+
Countries
diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 8f2c9d8fa..a04736620 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -1,3 +1,5 @@ +import { DataSource } from '@prisma/client'; + import { ToggleOption } from './types'; export const baseCurrency = 'USD'; @@ -14,6 +16,7 @@ export const DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f'; export const ghostfolioScraperApiSymbolPrefix = '_GF_'; export const ghostfolioCashSymbol = `${ghostfolioScraperApiSymbolPrefix}CASH`; +export const ghostfolioFearAndGreedIndexDataSource = DataSource.RAKUTEN; export const ghostfolioFearAndGreedIndexSymbol = `${ghostfolioScraperApiSymbolPrefix}FEAR_AND_GREED_INDEX`; export const locale = 'de-CH'; diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts index 470d135c2..ca0475d66 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts @@ -150,11 +150,11 @@ export class PortfolioProportionChartComponent }); if (!unknownItem) { - const index = chartDataSorted.push([ + chartDataSorted.push([ UNKNOWN_KEY, { name: UNKNOWN_KEY, subCategory: {}, value: 0 } ]); - unknownItem = chartDataSorted[index]; + unknownItem = chartDataSorted[chartDataSorted.length - 1]; } rest.forEach((restItem) => { diff --git a/package.json b/package.json index cd8c0ec80..85fcb7747 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "1.115.0", + "version": "1.116.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "scripts": { @@ -115,6 +115,7 @@ "stripe": "8.199.0", "svgmap": "2.6.0", "tslib": "2.0.0", + "twitter-api-v2": "1.10.3", "uuid": "8.3.2", "yahoo-finance": "0.3.6", "zone.js": "0.11.4" diff --git a/yarn.lock b/yarn.lock index 8671fd039..756eca7bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17714,6 +17714,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +twitter-api-v2@1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/twitter-api-v2/-/twitter-api-v2-1.10.3.tgz#07441bd9c4d27433aa0284d900cf60f6328b8239" + integrity sha512-AbCboiTOWv4DUPbAlF43Uyk4iK/QRk354pNdKgtOmv45+BWGB5Kdv6ls+C99pww/DyLBiXgQEnuyGv4d1HdRhw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"