Add symbol profile model and positions by country chart

pull/148/head
Thomas 4 years ago
parent 21504573b4
commit 52b659351d

@ -44,6 +44,7 @@ export class ExperimentalService {
fee: 0,
id: undefined,
platformId: undefined,
symbolProfileId: undefined,
type: Type.BUY,
updatedAt: undefined,
userId: undefined

@ -76,7 +76,8 @@ export class PortfolioService {
// Get portfolio from database
const orders = await this.orderService.orders({
include: {
Account: true
Account: true,
SymbolProfile: true
},
orderBy: { date: 'asc' },
where: { userId: aUserId }

@ -1,4 +1,4 @@
import { Account, Currency, Platform } from '@prisma/client';
import { Account, Currency, Platform, SymbolProfile } from '@prisma/client';
import { v4 as uuidv4 } from 'uuid';
import { IOrder } from '../services/interfaces/interfaces';
@ -12,6 +12,7 @@ export class Order {
private id: string;
private quantity: number;
private symbol: string;
private symbolProfile: SymbolProfile;
private total: number;
private type: OrderType;
private unitPrice: number;
@ -24,6 +25,7 @@ export class Order {
this.id = data.id || uuidv4();
this.quantity = data.quantity;
this.symbol = data.symbol;
this.symbolProfile = data.symbolProfile;
this.type = data.type;
this.unitPrice = data.unitPrice;
@ -58,6 +60,10 @@ export class Order {
return this.symbol;
}
getSymbolProfile() {
return this.symbolProfile;
}
public getTotal() {
return this.total;
}

@ -9,6 +9,7 @@ import {
UserWithSettings
} from '@ghostfolio/common/interfaces';
import { DateRange, OrderWithAccount } from '@ghostfolio/common/types';
import { Prisma } from '@prisma/client';
import {
add,
format,
@ -127,6 +128,7 @@ export class Portfolio implements PortfolioInterface {
id,
quantity,
symbol,
symbolProfile,
type,
unitPrice
}) => {
@ -139,6 +141,7 @@ export class Portfolio implements PortfolioInterface {
id,
quantity,
symbol,
symbolProfile,
type,
unitPrice
})
@ -204,6 +207,7 @@ export class Portfolio implements PortfolioInterface {
symbols.forEach((symbol) => {
const accounts: PortfolioPosition['accounts'] = {};
let countries: Prisma.JsonArray;
const [portfolioItem] = portfolioItems;
const ordersBySymbol = this.getOrders().filter((order) => {
@ -243,6 +247,10 @@ export class Portfolio implements PortfolioInterface {
original: originalValueOfSymbol
};
}
countries =
(orderOfSymbol.getSymbolProfile()?.countries as Prisma.JsonArray) ??
[];
});
let now = portfolioItemsNow.positions[symbol].marketPrice;
@ -280,6 +288,7 @@ export class Portfolio implements PortfolioInterface {
details[symbol] = {
...data[symbol],
accounts,
countries,
symbol,
allocationCurrent:
this.exchangeRateDataService.toCurrency(
@ -296,7 +305,12 @@ export class Portfolio implements PortfolioInterface {
grossPerformancePercent: roundTo((now - before) / before, 4),
investment: portfolioItem.positions[symbol].investment,
quantity: portfolioItem.positions[symbol].quantity,
transactionCount: portfolioItem.positions[symbol].transactionCount
transactionCount: portfolioItem.positions[symbol].transactionCount,
value: this.exchangeRateDataService.toCurrency(
portfolioItem.positions[symbol].quantity * now,
data[symbol]?.currency,
this.user.Settings.currency
)
};
});
@ -544,6 +558,7 @@ export class Portfolio implements PortfolioInterface {
fee: order.fee,
quantity: order.quantity,
symbol: order.symbol,
symbolProfile: order.SymbolProfile,
type: <OrderType>order.type,
unitPrice: order.unitPrice
})

@ -1,5 +1,5 @@
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import { Account, Currency, DataSource } from '@prisma/client';
import { Account, Currency, DataSource, SymbolProfile } from '@prisma/client';
import { OrderType } from '../../models/order-type';
@ -41,6 +41,7 @@ export interface IOrder {
id?: string;
quantity: number;
symbol: string;
symbolProfile: SymbolProfile;
type: OrderType;
unitPrice: number;
}

@ -3,11 +3,13 @@ import { ToggleOption } from '@ghostfolio/client/components/toggle/interfaces/to
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import {
PortfolioItem,
PortfolioPosition,
User
} from '@ghostfolio/common/interfaces';
import { Prisma } from '@prisma/client';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@ -21,6 +23,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
public accounts: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & { value: number };
};
public countries: {
[key: string]: { name: string; value: number };
};
public deviceType: string;
public period = 'current';
public periodOptions: ToggleOption[] = [
@ -97,6 +102,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
aPeriod: string
) {
this.accounts = {};
this.countries = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
}
};
this.positions = {};
this.positionsArray = [];
@ -122,11 +133,36 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
aPeriod === 'original' ? original : current;
} else {
this.accounts[account] = {
value: aPeriod === 'original' ? original : current,
name: account
name: account,
value: aPeriod === 'original' ? original : current
};
}
}
if (position.countries.length > 0) {
for (const country of position.countries) {
const { key, weight } = country as Prisma.JsonObject;
if (this.countries[<string>key]?.value) {
this.countries[<string>key].value +=
<number>weight * position.value;
} else {
this.countries[<string>key] = {
name: <string>key,
value:
<number>weight *
(aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value)
};
}
}
} else {
this.countries[UNKNOWN_KEY].value +=
aPeriod === 'original'
? this.portfolioPositions[symbol].investment
: this.portfolioPositions[symbol].value;
}
}
}

@ -124,6 +124,28 @@
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">
<mat-card-title i18n>By Country</mat-card-title>
<gf-toggle
[defaultValue]="period"
[isLoading]="false"
[options]="periodOptions"
(change)="onChangePeriod($event.value)"
></gf-toggle>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
key="name"
[baseCurrency]="user?.settings?.baseCurrency"
[isInPercent]="false"
[locale]="user?.settings?.locale"
[positions]="countries"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card class="mb-3">
<mat-card-header class="w-100">

@ -1,5 +1,5 @@
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
import { Currency } from '@prisma/client';
import { Currency, Prisma } from '@prisma/client';
export interface PortfolioPosition {
accounts: {
@ -7,6 +7,7 @@ export interface PortfolioPosition {
};
allocationCurrent: number;
allocationInvestment: number;
countries: Prisma.JsonArray;
currency: Currency;
exchange?: string;
grossPerformance: number;
@ -24,4 +25,5 @@ export interface PortfolioPosition {
symbol: string;
type?: string;
url?: string;
value: number;
}

@ -1,5 +1,8 @@
import { Account, Order, Platform } from '@prisma/client';
import { Account, Order, Platform, SymbolProfile } from '@prisma/client';
type AccountWithPlatform = Account & { Platform?: Platform };
export type OrderWithAccount = Order & { Account?: AccountWithPlatform };
export type OrderWithAccount = Order & {
Account?: AccountWithPlatform;
SymbolProfile?: SymbolProfile;
};

@ -0,0 +1,21 @@
-- AlterTable
ALTER TABLE "Order" ADD COLUMN "symbolProfileId" TEXT;
-- CreateTable
CREATE TABLE "SymbolProfile" (
"countries" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"dataSource" "DataSource" NOT NULL,
"id" TEXT NOT NULL,
"name" TEXT,
"updatedAt" TIMESTAMP(3) NOT NULL,
"symbol" TEXT NOT NULL,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "SymbolProfile.dataSource_symbol_unique" ON "SymbolProfile"("dataSource", "symbol");
-- AddForeignKey
ALTER TABLE "Order" ADD FOREIGN KEY ("symbolProfileId") REFERENCES "SymbolProfile"("id") ON DELETE SET NULL ON UPDATE CASCADE;

@ -59,22 +59,24 @@ model MarketData {
}
model Order {
Account Account? @relation(fields: [accountId, accountUserId], references: [id, userId])
accountId String?
accountUserId String?
createdAt DateTime @default(now())
currency Currency
dataSource DataSource @default(YAHOO)
date DateTime
fee Float
id String @default(uuid())
quantity Float
symbol String
type Type
unitPrice Float
updatedAt DateTime @updatedAt
User User @relation(fields: [userId], references: [id])
userId String
Account Account? @relation(fields: [accountId, accountUserId], references: [id, userId])
accountId String?
accountUserId String?
createdAt DateTime @default(now())
currency Currency
dataSource DataSource @default(YAHOO)
date DateTime
fee Float
id String @default(uuid())
quantity Float
symbol String
SymbolProfile SymbolProfile? @relation(fields: [symbolProfileId], references: [id])
symbolProfileId String?
type Type
unitPrice Float
updatedAt DateTime @updatedAt
User User @relation(fields: [userId], references: [id])
userId String
@@id([id, userId])
}
@ -99,6 +101,19 @@ model Settings {
userId String @id
}
model SymbolProfile {
countries Json?
createdAt DateTime @default(now())
dataSource DataSource
id String @id @default(uuid())
name String?
Order Order[]
updatedAt DateTime @updatedAt
symbol String
@@unique([dataSource, symbol])
}
model Subscription {
createdAt DateTime @default(now())
expiresAt DateTime

Loading…
Cancel
Save