Feature/add benchmarks to twitter bot service (#959)

* Extend benchmarks with market condition and adapt twitter bot service

* Update changelog
pull/961/head
Thomas Kaul 2 years ago committed by GitHub
parent 3498ed8549
commit c3768a882d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Extended the benchmarks of the markets overview by the current market condition (bear and bull market)
- Extended the twitter bot service by benchmarks
### Changed
- Upgraded `prisma` from version `3.12.0` to `3.14.0`

@ -11,6 +11,7 @@ import { BenchmarkService } from './benchmark.service';
@Module({
controllers: [BenchmarkController],
exports: [BenchmarkService],
imports: [
ConfigurationModule,
DataProviderModule,

@ -53,6 +53,9 @@ export class BenchmarkService {
.minus(1);
return {
marketCondition: this.getMarketCondition(
performancePercentFromAllTimeHigh
),
name: assetProfiles.find(({ dataSource, symbol }) => {
return (
dataSource === benchmarkAssets[index].dataSource &&
@ -74,4 +77,8 @@ export class BenchmarkService {
return benchmarks;
}
private getMarketCondition(aPerformanceInPercent: Big) {
return aPerformanceInPercent.lte(-0.2) ? 'BEAR_MARKET' : 'NEUTRAL_MARKET';
}
}

@ -1,11 +1,13 @@
import { BenchmarkModule } from '@ghostfolio/api/app/benchmark/benchmark.module';
import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { TwitterBotService } from '@ghostfolio/api/services/twitter-bot/twitter-bot.service';
import { Module } from '@nestjs/common';
@Module({
exports: [TwitterBotService],
imports: [ConfigurationModule, SymbolModule],
imports: [BenchmarkModule, ConfigurationModule, PropertyModule, SymbolModule],
providers: [TwitterBotService]
})
export class TwitterBotModule {}

@ -1,12 +1,20 @@
import { BenchmarkService } from '@ghostfolio/api/app/benchmark/benchmark.service';
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import {
PROPERTY_BENCHMARKS,
ghostfolioFearAndGreedIndexDataSource,
ghostfolioFearAndGreedIndexSymbol
} from '@ghostfolio/common/config';
import { resolveFearAndGreedIndex } from '@ghostfolio/common/helper';
import {
resolveFearAndGreedIndex,
resolveMarketCondition
} from '@ghostfolio/common/helper';
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import { Injectable, Logger } from '@nestjs/common';
import { isSunday } from 'date-fns';
import * as roundTo from 'round-to';
import { TwitterApi, TwitterApiReadWrite } from 'twitter-api-v2';
@Injectable()
@ -14,7 +22,9 @@ export class TwitterBotService {
private twitterClient: TwitterApiReadWrite;
public constructor(
private readonly benchmarkService: BenchmarkService,
private readonly configurationService: ConfigurationService,
private readonly propertyService: PropertyService,
private readonly symbolService: SymbolService
) {
this.twitterClient = new TwitterApi({
@ -48,7 +58,16 @@ export class TwitterBotService {
symbolItem.marketPrice
);
const status = `Current Market Mood: ${emoji} ${text} (${symbolItem.marketPrice}/100)\n\n#FearAndGreed #Markets #ServiceTweet`;
let status = `Current Market Mood: ${emoji} ${text} (${symbolItem.marketPrice}/100)`;
const benchmarkListing = await this.getBenchmarkListing(3);
if (benchmarkListing?.length > 1) {
status += '\n\n';
status += '±% from ATH\n';
status += benchmarkListing;
}
const { data: createdTweet } = await this.twitterClient.v2.tweet(
status
);
@ -62,4 +81,36 @@ export class TwitterBotService {
Logger.error(error, 'TwitterBotService');
}
}
private async getBenchmarkListing(aMax: number) {
const benchmarkAssets: UniqueAsset[] =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as UniqueAsset[]) ?? [];
const benchmarks = await this.benchmarkService.getBenchmarks(
benchmarkAssets
);
const benchmarkListing: string[] = [];
for (const [index, benchmark] of benchmarks.entries()) {
if (index > aMax - 1) {
break;
}
benchmarkListing.push(
`${benchmark.name} ${roundTo(
benchmark.performances.allTimeHigh.performancePercent * 100,
1
)}%${
benchmark.marketCondition !== 'NEUTRAL_MARKET'
? ' ' + resolveMarketCondition(benchmark.marketCondition).emoji
: ''
}`
);
}
return benchmarkListing.join('\n');
}
}

@ -3,6 +3,7 @@ import { DataSource } from '@prisma/client';
import { getDate, getMonth, getYear, parse, subDays } from 'date-fns';
import { ghostfolioScraperApiSymbolPrefix, locale } from './config';
import { Benchmark } from './interfaces';
export function capitalize(aString: string) {
return aString.charAt(0).toUpperCase() + aString.slice(1).toLowerCase();
@ -178,6 +179,18 @@ export function resolveFearAndGreedIndex(aValue: number) {
}
}
export function resolveMarketCondition(
aMarketCondition: Benchmark['marketCondition']
) {
if (aMarketCondition === 'BEAR_MARKET') {
return { emoji: '🐻' };
} else if (aMarketCondition === 'BULL_MARKET') {
return { emoji: '🐮' };
} else {
return { emoji: '⚪' };
}
}
export const DATE_FORMAT = 'yyyy-MM-dd';
export function parseDate(date: string) {

@ -1,6 +1,7 @@
import { EnhancedSymbolProfile } from './enhanced-symbol-profile.interface';
export interface Benchmark {
marketCondition: 'BEAR_MARKET' | 'BULL_MARKET' | 'NEUTRAL_MARKET';
name: EnhancedSymbolProfile['name'];
performances: {
allTimeHigh: {

@ -29,4 +29,21 @@
<small class="d-none d-sm-block text-nowrap" i18n>from All Time High</small
><small class="d-block d-sm-none text-nowrap" i18n>from ATH</small>
</div>
<div class="ml-2">
<div
*ngIf="benchmark?.marketCondition"
[title]="benchmark?.marketCondition"
>
{{ resolveMarketCondition(benchmark.marketCondition).emoji }}
</div>
<ngx-skeleton-loader
*ngIf="!benchmark?.marketCondition"
animation="pulse"
appearance="circle"
[theme]="{
height: '1rem',
width: '1rem'
}"
></ngx-skeleton-loader>
</div>
</div>

@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { resolveMarketCondition } from '@ghostfolio/common/helper';
import { Benchmark } from '@ghostfolio/common/interfaces';
@Component({
@ -11,5 +12,7 @@ export class BenchmarkComponent {
@Input() benchmark: Benchmark;
@Input() locale: string;
public resolveMarketCondition = resolveMarketCondition;
public constructor() {}
}

Loading…
Cancel
Save