Introduce market states (#38)

* closed
* delayed (no live data)
* open
pull/39/head
Thomas 3 years ago committed by GitHub
parent cf582b2e98
commit 5cb69291f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,4 @@
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
import { Currency } from '@prisma/client';
export interface PortfolioPosition {
@ -7,10 +8,10 @@ export interface PortfolioPosition {
grossPerformancePercent: number;
industry?: string;
investment: number;
isMarketOpen: boolean;
marketChange?: number;
marketChangePercent?: number;
marketPrice: number;
marketState: MarketState;
name: string;
platforms: {
[name: string]: { current: number; original: number };

@ -5,9 +5,11 @@ import { Currency, Role, Type } from '@prisma/client';
import { ConfigurationService } from '../services/configuration.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 { MarketState } from '../services/interfaces/interfaces';
import { PrismaService } from '../services/prisma.service';
import { RulesService } from '../services/rules.service';
import { Portfolio } from './portfolio';
@ -17,6 +19,7 @@ describe('Portfolio', () => {
let configurationService: ConfigurationService;
let dataProviderService: DataProviderService;
let exchangeRateDataService: ExchangeRateDataService;
let ghostfolioScraperApiService: GhostfolioScraperApiService;
let portfolio: Portfolio;
let prismaService: PrismaService;
let rakutenRapidApiService: RakutenRapidApiService;
@ -31,6 +34,7 @@ describe('Portfolio', () => {
ConfigurationService,
DataProviderService,
ExchangeRateDataService,
GhostfolioScraperApiService,
PrismaService,
RakutenRapidApiService,
RulesService,
@ -44,6 +48,9 @@ describe('Portfolio', () => {
exchangeRateDataService = app.get<ExchangeRateDataService>(
ExchangeRateDataService
);
ghostfolioScraperApiService = app.get<GhostfolioScraperApiService>(
GhostfolioScraperApiService
);
prismaService = app.get<PrismaService>(PrismaService);
rakutenRapidApiService = app.get<RakutenRapidApiService>(
RakutenRapidApiService
@ -154,8 +161,8 @@ describe('Portfolio', () => {
Currency.USD,
baseCurrency
),
isMarketOpen: true,
// marketPrice: 57973.008,
marketState: MarketState.open,
name: 'Bitcoin USD',
platforms: {
Other: {

@ -8,7 +8,8 @@ import { DataProviderInterface } from '../../interfaces/data-provider.interface'
import { Granularity } from '../../interfaces/granularity.type';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
IDataProviderResponse,
MarketState
} from '../../interfaces/interfaces';
import { PrismaService } from '../../prisma.service';
@ -41,7 +42,7 @@ export class GhostfolioScraperApiService implements DataProviderInterface {
[symbol]: {
marketPrice,
currency: scraperConfig?.currency,
isMarketOpen: false,
marketState: MarketState.delayed,
name: scraperConfig?.name
}
};

@ -8,7 +8,8 @@ import { DataProviderInterface } from '../../interfaces/data-provider.interface'
import { Granularity } from '../../interfaces/granularity.type';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
IDataProviderResponse,
MarketState
} from '../../interfaces/interfaces';
import { PrismaService } from '../../prisma.service';
@ -38,8 +39,8 @@ export class RakutenRapidApiService implements DataProviderInterface {
return {
'GF.FEAR_AND_GREED_INDEX': {
currency: undefined,
isMarketOpen: true,
marketPrice: fgi.now.value,
marketState: MarketState.open,
name: RakutenRapidApiService.FEAR_AND_GREED_INDEX_NAME
}
};

@ -9,6 +9,7 @@ import {
IDataProviderHistoricalResponse,
IDataProviderResponse,
Industry,
MarketState,
Sector,
Type
} from '../../interfaces/interfaces';
@ -49,8 +50,10 @@ export class YahooFinanceService implements DataProviderInterface {
response[symbol] = {
currency: parseCurrency(value.price?.currency),
exchange: this.parseExchange(value.price?.exchangeName),
isMarketOpen:
value.price?.marketState === 'REGULAR' || isCrypto(symbol),
marketState:
value.price?.marketState === 'REGULAR' || isCrypto(symbol)
? MarketState.open
: MarketState.closed,
marketPrice: value.price?.regularMarketPrice || 0,
name: value.price?.longName || value.price?.shortName || symbol,
type: this.parseType(this.getType(symbol, value))

@ -12,6 +12,12 @@ export const Industry = {
Software: 'Software'
};
export const MarketState = {
closed: 'closed',
delayed: 'delayed',
open: 'open'
};
export const Sector = {
Consumer: 'Consumer',
Healthcare: 'Healthcare',
@ -47,10 +53,10 @@ export interface IDataProviderResponse {
currency: Currency;
exchange?: string;
industry?: Industry;
isMarketOpen: boolean;
marketChange?: number;
marketChangePercent?: number;
marketPrice: number;
marketState: MarketState;
name: string;
sector?: Sector;
type?: Type;
@ -59,6 +65,8 @@ export interface IDataProviderResponse {
export type Industry = typeof Industry[keyof typeof Industry];
export type MarketState = typeof MarketState[keyof typeof MarketState];
export type Sector = typeof Sector[keyof typeof Sector];
export type Type = typeof Type[keyof typeof Type];

@ -9,7 +9,7 @@
<gf-trend-indicator
class="d-flex"
[isLoading]="isLoading"
[isPaused]="!position?.isMarketOpen"
[marketState]="position?.marketState"
[range]="range"
[value]="position?.grossPerformancePercent"
></gf-trend-indicator>

@ -6,6 +6,7 @@ import {
OnInit
} from '@angular/core';
import { PortfolioPosition } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position.interface';
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
@Component({
selector: 'gf-positions',
@ -40,7 +41,10 @@ export class PositionsComponent implements OnChanges, OnInit {
this.positionsWithPriority = [];
for (const [, portfolioPosition] of Object.entries(this.positions)) {
if (portfolioPosition.isMarketOpen || this.range !== '1d') {
if (
portfolioPosition.marketState === MarketState.open ||
this.range !== '1d'
) {
// Only show positions where the market is open in today's view
this.positionsWithPriority.push(portfolioPosition);
} else {

@ -10,12 +10,21 @@
<ng-template #other>
<ion-icon
*ngIf="isPaused && range === '1d'; else trend"
*ngIf="marketState === 'closed' && range === '1d'; else delayed"
class="text-muted"
name="pause-circle-outline"
size="large"
>
</ion-icon>
<ng-template #delayed>
<ion-icon
*ngIf="marketState === 'delayed' && range === '1d'; else trend"
class="text-muted"
name="time-outline"
size="large"
>
</ion-icon>
</ng-template>
<ng-template #trend>
<ng-container>
<ion-icon

@ -4,6 +4,7 @@ import {
Input,
OnInit
} from '@angular/core';
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
@Component({
selector: 'gf-trend-indicator',
@ -13,7 +14,7 @@ import {
})
export class TrendIndicatorComponent implements OnInit {
@Input() isLoading: boolean;
@Input() isPaused: boolean;
@Input() marketState: MarketState;
@Input() range: string;
@Input() value: number;

Loading…
Cancel
Save