Introduce @ghostfolio/common lib (#102)

This commit is contained in:
Thomas
2021-05-16 22:11:14 +02:00
committed by GitHub
parent 561d8dbc70
commit abd0e08566
104 changed files with 133 additions and 134 deletions

View File

@@ -0,0 +1,34 @@
import { Currency } from '.prisma/client';
export const baseCurrency = Currency.CHF;
export const benchmarks = ['VOO'];
export const currencyPairs = [
`${Currency.USD}${Currency.EUR}`,
`${Currency.USD}${Currency.GBP}`,
`${Currency.USD}${Currency.CHF}`
];
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
export const locale = 'de-CH';
export const primaryColorHex = '#36cfcc';
export const primaryColorRgb = {
r: 54,
g: 207,
b: 204
};
export const secondaryColorHex = '#3686cf';
export const secondaryColorRgb = {
r: 54,
g: 134,
b: 207
};
export const DEFAULT_DATE_FORMAT = 'dd.MM.yyyy';
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
export const UNKNOWN_KEY = 'UNKNOWN';

View File

@@ -0,0 +1,135 @@
import { Currency } from '@prisma/client';
import { getDate, getMonth, getYear, subDays } from 'date-fns';
import { ghostfolioScraperApiSymbolPrefix } from './config';
const cryptocurrencies = require('cryptocurrencies');
export const DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f';
export function capitalize(aString: string) {
return aString.charAt(0).toUpperCase() + aString.slice(1).toLowerCase();
}
export function getBackgroundColor() {
return getCssVariable(
window.matchMedia('(prefers-color-scheme: dark)').matches
? '--dark-background'
: '--light-background'
);
}
export function getCssVariable(aCssVariable: string) {
return getComputedStyle(document.documentElement).getPropertyValue(
aCssVariable
);
}
export function getTextColor() {
return getCssVariable(
window.matchMedia('(prefers-color-scheme: dark)').matches
? '--light-primary-text'
: '--dark-primary-text'
);
}
export function getToday() {
const year = getYear(new Date());
const month = getMonth(new Date());
const day = getDate(new Date());
return new Date(Date.UTC(year, month, day));
}
export function getUtc(aDateString: string) {
const [yearString, monthString, dayString] = aDateString.split('-');
return new Date(
Date.UTC(
parseInt(yearString),
parseInt(monthString) - 1,
parseInt(dayString)
)
);
}
export function getYesterday() {
const year = getYear(new Date());
const month = getMonth(new Date());
const day = getDate(new Date());
return subDays(new Date(Date.UTC(year, month, day)), 1);
}
export function groupBy<T, K extends keyof T>(
key: K,
arr: T[]
): Map<T[K], T[]> {
const map = new Map<T[K], T[]>();
arr.forEach((t) => {
if (!map.has(t[key])) {
map.set(t[key], []);
}
map.get(t[key])!.push(t);
});
return map;
}
export function isCrypto(aSymbol = '') {
const cryptocurrencySymbol = aSymbol.substring(0, aSymbol.length - 3);
return cryptocurrencies.symbols().includes(cryptocurrencySymbol);
}
export function isCurrency(aSymbol = '') {
return (
(aSymbol.includes(Currency.CHF) ||
aSymbol.includes(Currency.EUR) ||
aSymbol.includes(Currency.USD)) &&
aSymbol.length >= 6
);
}
export function isGhostfolioScraperApiSymbol(aSymbol = '') {
return aSymbol.startsWith(ghostfolioScraperApiSymbolPrefix);
}
export function isRakutenRapidApiSymbol(aSymbol = '') {
return aSymbol === 'GF.FEAR_AND_GREED_INDEX';
}
export function parseCurrency(aString: string): Currency {
if (aString?.toLowerCase() === 'chf') {
return Currency.CHF;
} else if (aString?.toLowerCase() === 'eur') {
return Currency.EUR;
} else if (aString?.toLowerCase() === 'gbp') {
return Currency.GBP;
} else if (aString?.toLowerCase() === 'usd') {
return Currency.USD;
}
return undefined;
}
export function resetHours(aDate: Date) {
const year = getYear(aDate);
const month = getMonth(aDate);
const day = getDate(aDate);
return new Date(Date.UTC(year, month, day));
}
export function resolveFearAndGreedIndex(aValue: number) {
if (aValue <= 25) {
return { emoji: '🥵', text: 'Extreme Fear' };
} else if (aValue <= 45) {
return { emoji: '😨', text: 'Fear' };
} else if (aValue <= 55) {
return { emoji: '😐', text: 'Neutral' };
} else if (aValue < 75) {
return { emoji: '😜', text: 'Greed' };
} else if (aValue >= 75) {
return { emoji: '🤪', text: 'Extreme Greed' };
}
}

View File

@@ -0,0 +1,3 @@
export interface Access {
granteeAlias: string;
}

View File

@@ -0,0 +1,15 @@
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,29 @@
import { Access } from './access.interface';
import { AdminData } from './admin-data.interface';
import { InfoItem } from './info-item.interface';
import { PortfolioItem } from './portfolio-item.interface';
import { PortfolioOverview } from './portfolio-overview.interface';
import { PortfolioPerformance } from './portfolio-performance.interface';
import { PortfolioPosition } from './portfolio-position.interface';
import { PortfolioReportRule } from './portfolio-report-rule.interface';
import { PortfolioReport } from './portfolio-report.interface';
import { Position } from './position.interface';
import { UserSettings } from './user-settings.interface';
import { UserWithSettings } from './user-with-settings';
import { User } from './user.interface';
export {
Access,
AdminData,
InfoItem,
PortfolioItem,
PortfolioOverview,
PortfolioPerformance,
PortfolioPosition,
PortfolioReport,
PortfolioReportRule,
Position,
User,
UserSettings,
UserWithSettings
};

View File

@@ -0,0 +1,13 @@
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

@@ -0,0 +1,9 @@
import { Position } from '@ghostfolio/common/interfaces';
export interface PortfolioItem {
date: string;
grossPerformancePercent: number;
investment: number;
positions: { [symbol: string]: Position };
value: number;
}

View File

@@ -0,0 +1,7 @@
export interface PortfolioOverview {
committedFunds: number;
fees: number;
ordersCount: number;
totalBuy: number;
totalSell: number;
}

View File

@@ -0,0 +1,7 @@
export interface PortfolioPerformance {
currentGrossPerformance: number;
currentGrossPerformancePercent: number;
currentNetPerformance: number;
currentNetPerformancePercent: number;
currentValue: number;
}

View File

@@ -0,0 +1,27 @@
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
import { Currency } from '@prisma/client';
export interface PortfolioPosition {
accounts: {
[name: string]: { current: number; original: number };
};
allocationCurrent: number;
allocationInvestment: number;
currency: Currency;
exchange?: string;
grossPerformance: number;
grossPerformancePercent: number;
industry?: string;
investment: number;
marketChange?: number;
marketChangePercent?: number;
marketPrice: number;
marketState: MarketState;
name: string;
quantity: number;
sector?: string;
transactionCount: number;
symbol: string;
type?: string;
url?: string;
}

View File

@@ -0,0 +1,5 @@
export interface PortfolioReportRule {
evaluation: string;
name: string;
value: boolean;
}

View File

@@ -0,0 +1,5 @@
import { PortfolioReportRule } from './portfolio-report-rule.interface';
export interface PortfolioReport {
rules: { [group: string]: PortfolioReportRule[] };
}

View File

@@ -0,0 +1,12 @@
import { Currency } from '@prisma/client';
export interface Position {
averagePrice: number;
currency: Currency;
firstBuyDate: string;
investment: number;
investmentInOriginalCurrency?: number;
marketPrice?: number;
quantity: number;
transactionCount: number;
}

View File

@@ -0,0 +1,6 @@
import { Currency } from '@prisma/client';
export interface UserSettings {
baseCurrency: Currency;
locale: string;
}

View File

@@ -0,0 +1,6 @@
import { Account, Settings, User } from '@prisma/client';
export type UserWithSettings = User & {
Account: Account[];
Settings: Settings;
};

View File

@@ -0,0 +1,17 @@
import { Access } from '@ghostfolio/api/app/user/interfaces/access.interface';
import { Account } from '@prisma/client';
import { UserSettings } from './user-settings.interface';
export interface User {
access: Access[];
accounts: Account[];
alias?: string;
id: string;
permissions: string[];
settings: UserSettings;
subscription: {
expiresAt: Date;
type: 'Trial';
};
}

View File

@@ -0,0 +1,64 @@
import { Role } from '@prisma/client';
export function isApiTokenAuthorized(aApiToken: string) {
return aApiToken === 'Bearer fc804dead6ff45b98da4e5530f6aa3cb';
}
export const permissions = {
accessAdminControl: 'accessAdminControl',
accessFearAndGreedIndex: 'accessFearAndGreedIndex',
createAccount: 'createAccount',
createOrder: 'createOrder',
createUserAccount: 'createUserAccount',
deleteAccount: 'deleteAcccount',
deleteOrder: 'deleteOrder',
deleteUser: 'deleteUser',
enableSocialLogin: 'enableSocialLogin',
enableSubscription: 'enableSubscription',
readForeignPortfolio: 'readForeignPortfolio',
updateAccount: 'updateAccount',
updateOrder: 'updateOrder',
updateUserSettings: 'updateUserSettings'
};
export function hasPermission(
aPermissions: string[] = [],
aPermission: string
) {
return aPermissions.includes(aPermission);
}
export function getPermissions(aRole: Role): string[] {
switch (aRole) {
case 'ADMIN':
return [
permissions.accessAdminControl,
permissions.createAccount,
permissions.createOrder,
permissions.deleteAccount,
permissions.deleteOrder,
permissions.deleteUser,
permissions.readForeignPortfolio,
permissions.updateAccount,
permissions.updateOrder,
permissions.updateUserSettings
];
case 'DEMO':
return [permissions.createUserAccount];
case 'USER':
return [
permissions.createAccount,
permissions.createOrder,
permissions.deleteAccount,
permissions.deleteOrder,
permissions.updateAccount,
permissions.updateOrder,
permissions.updateUserSettings
];
default:
return [];
}
}

View File

@@ -0,0 +1,3 @@
import { Access, User } from '@prisma/client';
export type AccessWithGranteeUser = Access & { GranteeUser?: User };

View File

@@ -0,0 +1 @@
export type DateRange = '1d' | '1y' | '5y' | 'max' | 'ytd';

View File

@@ -0,0 +1 @@
export type Granularity = 'day' | 'month';

View File

@@ -0,0 +1,13 @@
import { AccessWithGranteeUser } from './access-with-grantee-user.type';
import { DateRange } from './date-range.type';
import { Granularity } from './granularity.type';
import { OrderWithAccount } from './order-with-account.type';
import { RequestWithUser } from './request-with-user.type';
export {
AccessWithGranteeUser,
DateRange,
Granularity,
OrderWithAccount,
RequestWithUser
};

View File

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

View File

@@ -0,0 +1,3 @@
import { UserWithSettings } from '@ghostfolio/common/interfaces';
export type RequestWithUser = Request & { user: UserWithSettings };