From 982ba7377a0f99485c625a62c2f99b88ebdb471b Mon Sep 17 00:00:00 2001
From: Valentin Zickner <github@zickner.ch>
Date: Sun, 1 Aug 2021 00:38:49 +0200
Subject: [PATCH] remove legacy portfolio.ts

---
 .../experimental/experimental.controller.ts   |  22 -
 .../app/experimental/experimental.service.ts  |  44 -
 apps/api/src/models/portfolio.spec.ts         | 478 ----------
 apps/api/src/models/portfolio.ts              | 872 ------------------
 4 files changed, 1416 deletions(-)
 delete mode 100644 apps/api/src/models/portfolio.spec.ts
 delete mode 100644 apps/api/src/models/portfolio.ts

diff --git a/apps/api/src/app/experimental/experimental.controller.ts b/apps/api/src/app/experimental/experimental.controller.ts
index 0736a0012..0851e4be4 100644
--- a/apps/api/src/app/experimental/experimental.controller.ts
+++ b/apps/api/src/app/experimental/experimental.controller.ts
@@ -66,26 +66,4 @@ export class ExperimentalController {
 
     return marketData;
   }
-
-  @Post('value/:dateString?')
-  public async getValue(
-    @Body() orders: CreateOrderDto[],
-    @Headers('Authorization') apiToken: string,
-    @Param('dateString') dateString: string
-  ): Promise<Data> {
-    if (!isApiTokenAuthorized(apiToken)) {
-      throw new HttpException(
-        getReasonPhrase(StatusCodes.FORBIDDEN),
-        StatusCodes.FORBIDDEN
-      );
-    }
-
-    let date = new Date();
-
-    if (dateString) {
-      date = parse(dateString, DATE_FORMAT, new Date());
-    }
-
-    return this.experimentalService.getValue(orders, date, baseCurrency);
-  }
 }
diff --git a/apps/api/src/app/experimental/experimental.service.ts b/apps/api/src/app/experimental/experimental.service.ts
index eda248f52..edbe7bce3 100644
--- a/apps/api/src/app/experimental/experimental.service.ts
+++ b/apps/api/src/app/experimental/experimental.service.ts
@@ -1,16 +1,9 @@
 import { AccountService } from '@ghostfolio/api/app/account/account.service';
-import { Portfolio } from '@ghostfolio/api/models/portfolio';
 import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
 import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
 import { PrismaService } from '@ghostfolio/api/services/prisma.service';
 import { RulesService } from '@ghostfolio/api/services/rules.service';
-import { OrderWithAccount } from '@ghostfolio/common/types';
 import { Injectable } from '@nestjs/common';
-import { Currency, Type } from '@prisma/client';
-import { parseISO } from 'date-fns';
-
-import { CreateOrderDto } from './create-order.dto';
-import { Data } from './interfaces/data.interface';
 
 @Injectable()
 export class ExperimentalService {
@@ -29,41 +22,4 @@ export class ExperimentalService {
       where: { symbol: aSymbol }
     });
   }
-
-  public async getValue(
-    aOrders: CreateOrderDto[],
-    aDate: Date,
-    aBaseCurrency: Currency
-  ): Promise<Data> {
-    const ordersWithPlatform: OrderWithAccount[] = aOrders.map((order) => {
-      return {
-        ...order,
-        accountId: undefined,
-        accountUserId: undefined,
-        createdAt: new Date(),
-        dataSource: undefined,
-        date: parseISO(order.date),
-        fee: 0,
-        id: undefined,
-        platformId: undefined,
-        symbolProfileId: undefined,
-        type: Type.BUY,
-        updatedAt: undefined,
-        userId: undefined
-      };
-    });
-
-    const portfolio = new Portfolio(
-      this.accountService,
-      this.dataProviderService,
-      this.exchangeRateDataService,
-      this.rulesService
-    );
-    await portfolio.setOrders(ordersWithPlatform);
-
-    return {
-      currency: aBaseCurrency,
-      value: portfolio.getValue(aDate)
-    };
-  }
 }
diff --git a/apps/api/src/models/portfolio.spec.ts b/apps/api/src/models/portfolio.spec.ts
deleted file mode 100644
index cf28763a7..000000000
--- a/apps/api/src/models/portfolio.spec.ts
+++ /dev/null
@@ -1,478 +0,0 @@
-import { AccountService } from '@ghostfolio/api/app/account/account.service';
-import { UNKNOWN_KEY, baseCurrency } from '@ghostfolio/common/config';
-import { DATE_FORMAT, getUtc, getYesterday } from '@ghostfolio/common/helper';
-import {
-  AccountType,
-  Currency,
-  DataSource,
-  Role,
-  Type,
-  ViewMode
-} from '@prisma/client';
-import { format } from 'date-fns';
-
-import { DataProviderService } from '../services/data-provider.service';
-import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
-import { MarketState } from '../services/interfaces/interfaces';
-import { RulesService } from '../services/rules.service';
-import { Portfolio } from './portfolio';
-
-jest.mock('../app/account/account.service', () => {
-  return {
-    AccountService: jest.fn().mockImplementation(() => {
-      return {
-        getCashDetails: () => Promise.resolve({ accounts: [], balance: 0 })
-      };
-    })
-  };
-});
-
-jest.mock('../services/data-provider.service', () => {
-  return {
-    DataProviderService: jest.fn().mockImplementation(() => {
-      const today = format(new Date(), DATE_FORMAT);
-      const yesterday = format(getYesterday(), DATE_FORMAT);
-
-      return {
-        get: () => {
-          return Promise.resolve({
-            BTCUSD: {
-              currency: Currency.USD,
-              dataSource: DataSource.YAHOO,
-              exchange: UNKNOWN_KEY,
-              marketPrice: 57973.008,
-              marketState: MarketState.open,
-              name: 'Bitcoin USD',
-              type: 'Cryptocurrency'
-            },
-            ETHUSD: {
-              currency: Currency.USD,
-              dataSource: DataSource.YAHOO,
-              exchange: UNKNOWN_KEY,
-              marketPrice: 3915.337,
-              marketState: MarketState.open,
-              name: 'Ethereum USD',
-              type: 'Cryptocurrency'
-            }
-          });
-        },
-        getHistorical: () => {
-          return Promise.resolve({
-            BTCUSD: {
-              [yesterday]: 56710.122,
-              [today]: 57973.008
-            },
-            ETHUSD: {
-              [yesterday]: 3641.984,
-              [today]: 3915.337
-            }
-          });
-        }
-      };
-    })
-  };
-});
-
-jest.mock('../services/exchange-rate-data.service', () => {
-  return {
-    ExchangeRateDataService: jest.fn().mockImplementation(() => {
-      return {
-        initialize: () => Promise.resolve(),
-        toCurrency: (value: number) => {
-          return 1 * value;
-        }
-      };
-    })
-  };
-});
-
-jest.mock('../services/rules.service');
-
-const DEFAULT_ACCOUNT_ID = '693a834b-eb89-42c9-ae47-35196c25d269';
-const USER_ID = 'ca6ce867-5d31-495a-bce9-5942bbca9237';
-
-describe('Portfolio', () => {
-  let accountService: AccountService;
-  let dataProviderService: DataProviderService;
-  let exchangeRateDataService: ExchangeRateDataService;
-  let portfolio: Portfolio;
-  let rulesService: RulesService;
-
-  beforeAll(async () => {
-    accountService = new AccountService(null, null, null);
-    dataProviderService = new DataProviderService(
-      null,
-      null,
-      null,
-      null,
-      null,
-      null
-    );
-    exchangeRateDataService = new ExchangeRateDataService(null);
-    rulesService = new RulesService();
-
-    await exchangeRateDataService.initialize();
-
-    portfolio = new Portfolio(
-      accountService,
-      dataProviderService,
-      exchangeRateDataService,
-      rulesService
-    );
-    portfolio.setUser({
-      accessToken: null,
-      Account: [
-        {
-          accountType: AccountType.SECURITIES,
-          balance: 0,
-          createdAt: new Date(),
-          currency: Currency.USD,
-          id: DEFAULT_ACCOUNT_ID,
-          isDefault: true,
-          name: 'Default Account',
-          platformId: null,
-          updatedAt: new Date(),
-          userId: USER_ID
-        }
-      ],
-      alias: 'Test',
-      authChallenge: null,
-      createdAt: new Date(),
-      id: USER_ID,
-      provider: null,
-      role: Role.USER,
-      Settings: {
-        currency: Currency.CHF,
-        updatedAt: new Date(),
-        userId: USER_ID,
-        viewMode: ViewMode.DEFAULT
-      },
-      thirdPartyId: null,
-      updatedAt: new Date()
-    });
-  });
-
-  describe('works with no orders', () => {
-    it('should return []', () => {
-      expect(portfolio.get(new Date())).toEqual([]);
-      expect(portfolio.getFees()).toEqual(0);
-      expect(portfolio.getPositions(new Date())).toEqual({});
-    });
-
-    it('should return empty details', async () => {
-      const details = await portfolio.getDetails('1d');
-      expect(details).toMatchObject({
-        _GF_CASH: {
-          accounts: {},
-          allocationCurrent: NaN, // TODO
-          allocationInvestment: NaN, // TODO
-          countries: [],
-          currency: 'CHF',
-          grossPerformance: 0,
-          grossPerformancePercent: 0,
-          investment: 0,
-          marketPrice: 0,
-          marketState: 'open',
-          name: 'Cash',
-          quantity: 0,
-          sectors: [],
-          symbol: '_GF_CASH',
-          transactionCount: 0,
-          type: 'Cash',
-          value: 0
-        }
-      });
-    });
-
-    it('should return empty details', async () => {
-      const details = await portfolio.getDetails('max');
-      expect(details).toMatchObject({
-        _GF_CASH: {
-          accounts: {},
-          allocationCurrent: NaN, // TODO
-          allocationInvestment: NaN, // TODO
-          countries: [],
-          currency: 'CHF',
-          grossPerformance: 0,
-          grossPerformancePercent: 0,
-          investment: 0,
-          marketPrice: 0,
-          marketState: 'open',
-          name: 'Cash',
-          quantity: 0,
-          sectors: [],
-          symbol: '_GF_CASH',
-          transactionCount: 0,
-          type: 'Cash',
-          value: 0
-        }
-      });
-    });
-  });
-
-  describe('works with orders', () => {
-    it('should return ["ETHUSD"]', async () => {
-      await portfolio.setOrders([
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 0,
-          date: new Date(getUtc('2018-01-05')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fb',
-          quantity: 0.2,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 991.49,
-          updatedAt: null,
-          userId: USER_ID
-        }
-      ]);
-
-      expect(portfolio.getFees()).toEqual(0);
-
-      expect(portfolio.getPositions(getYesterday())).toMatchObject({
-        ETHUSD: {
-          averagePrice: 991.49,
-          currency: Currency.USD,
-          firstBuyDate: '2018-01-05T00:00:00.000Z',
-          investment: exchangeRateDataService.toCurrency(
-            0.2 * 991.49,
-            Currency.USD,
-            baseCurrency
-          ),
-          investmentInOriginalCurrency: 0.2 * 991.49,
-          // marketPrice: 3915.337,
-          quantity: 0.2
-        }
-      });
-
-      expect(portfolio.getSymbols(getYesterday())).toEqual(['ETHUSD']);
-    });
-
-    it('should return ["ETHUSD"]', async () => {
-      await portfolio.setOrders([
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 0,
-          date: new Date(getUtc('2018-01-05')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fb',
-          quantity: 0.2,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 991.49,
-          updatedAt: null,
-          userId: USER_ID
-        },
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 0,
-          date: new Date(getUtc('2018-01-28')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fc',
-          quantity: 0.3,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 1050,
-          updatedAt: null,
-          userId: USER_ID
-        }
-      ]);
-
-      expect(portfolio.getFees()).toEqual(0);
-
-      expect(portfolio.getPositions(getYesterday())).toMatchObject({
-        ETHUSD: {
-          averagePrice: (0.2 * 991.49 + 0.3 * 1050) / (0.2 + 0.3),
-          currency: Currency.USD,
-          firstBuyDate: '2018-01-05T00:00:00.000Z',
-          investment:
-            exchangeRateDataService.toCurrency(
-              0.2 * 991.49,
-              Currency.USD,
-              baseCurrency
-            ) +
-            exchangeRateDataService.toCurrency(
-              0.3 * 1050,
-              Currency.USD,
-              baseCurrency
-            ),
-          investmentInOriginalCurrency: 0.2 * 991.49 + 0.3 * 1050,
-          // marketPrice: 3641.984,
-          quantity: 0.5
-        }
-      });
-
-      expect(portfolio.getSymbols(getYesterday())).toEqual(['ETHUSD']);
-    });
-
-    it('should return ["BTCUSD", "ETHUSD"]', async () => {
-      await portfolio.setOrders([
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.EUR,
-          dataSource: DataSource.YAHOO,
-          date: new Date(getUtc('2017-08-16')),
-          fee: 2.99,
-          id: 'd96795b2-6ae6-420e-aa21-fabe5e45d475',
-          quantity: 0.05614682,
-          symbol: 'BTCUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 3562.089535970158,
-          updatedAt: null,
-          userId: USER_ID
-        },
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 2.99,
-          date: new Date(getUtc('2018-01-05')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fb',
-          quantity: 0.2,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 991.49,
-          updatedAt: null,
-          userId: USER_ID
-        }
-      ]);
-
-      expect(portfolio.getFees()).toEqual(
-        exchangeRateDataService.toCurrency(2.99, Currency.EUR, baseCurrency) +
-          exchangeRateDataService.toCurrency(2.99, Currency.USD, baseCurrency)
-      );
-
-      expect(portfolio.getPositions(getYesterday())).toMatchObject({
-        BTCUSD: {
-          averagePrice: 3562.089535970158,
-          currency: Currency.EUR,
-          firstBuyDate: '2017-08-16T00:00:00.000Z',
-          investment: exchangeRateDataService.toCurrency(
-            0.05614682 * 3562.089535970158,
-            Currency.EUR,
-            baseCurrency
-          ),
-          investmentInOriginalCurrency: 0.05614682 * 3562.089535970158,
-          // marketPrice: 0,
-          quantity: 0.05614682
-        },
-        ETHUSD: {
-          averagePrice: 991.49,
-          currency: Currency.USD,
-          firstBuyDate: '2018-01-05T00:00:00.000Z',
-          investment: exchangeRateDataService.toCurrency(
-            0.2 * 991.49,
-            Currency.USD,
-            baseCurrency
-          ),
-          investmentInOriginalCurrency: 0.2 * 991.49,
-          // marketPrice: 0,
-          quantity: 0.2
-        }
-      });
-
-      expect(portfolio.getSymbols(getYesterday())).toEqual([
-        'BTCUSD',
-        'ETHUSD'
-      ]);
-    });
-
-    it('should work with buy and sell', async () => {
-      await portfolio.setOrders([
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 1.0,
-          date: new Date(getUtc('2018-01-05')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fb',
-          quantity: 0.2,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 991.49,
-          updatedAt: null,
-          userId: USER_ID
-        },
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 1.0,
-          date: new Date(getUtc('2018-01-28')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fc',
-          quantity: 0.1,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.SELL,
-          unitPrice: 1050,
-          updatedAt: null,
-          userId: USER_ID
-        },
-        {
-          accountId: DEFAULT_ACCOUNT_ID,
-          accountUserId: USER_ID,
-          createdAt: null,
-          currency: Currency.USD,
-          dataSource: DataSource.YAHOO,
-          fee: 1.0,
-          date: new Date(getUtc('2018-01-31')),
-          id: '4a5a5c6e-659d-45cc-9fd4-fd6c873b50fc',
-          quantity: 0.2,
-          symbol: 'ETHUSD',
-          symbolProfileId: null,
-          type: Type.BUY,
-          unitPrice: 1050,
-          updatedAt: null,
-          userId: USER_ID
-        }
-      ]);
-
-      expect(portfolio.getFees()).toEqual(
-        exchangeRateDataService.toCurrency(3, Currency.USD, baseCurrency)
-      );
-
-      expect(portfolio.getPositions(getYesterday())).toMatchObject({
-        ETHUSD: {
-          averagePrice:
-            (0.2 * 991.49 - 0.1 * 1050 + 0.2 * 1050) / (0.2 - 0.1 + 0.2),
-          currency: Currency.USD,
-          firstBuyDate: '2018-01-05T00:00:00.000Z',
-          investment: exchangeRateDataService.toCurrency(
-            0.2 * 991.49 - 0.1 * 1050 + 0.2 * 1050,
-            Currency.USD,
-            baseCurrency
-          ),
-          investmentInOriginalCurrency: 0.2 * 991.49 - 0.1 * 1050 + 0.2 * 1050,
-          // marketPrice: 0,
-          quantity: 0.2 - 0.1 + 0.2
-        }
-      });
-
-      expect(portfolio.getSymbols(getYesterday())).toEqual(['ETHUSD']);
-    });
-  });
-});
diff --git a/apps/api/src/models/portfolio.ts b/apps/api/src/models/portfolio.ts
deleted file mode 100644
index b416f9872..000000000
--- a/apps/api/src/models/portfolio.ts
+++ /dev/null
@@ -1,872 +0,0 @@
-import { AccountService } from '@ghostfolio/api/app/account/account.service';
-import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface';
-import { UNKNOWN_KEY, ghostfolioCashSymbol } from '@ghostfolio/common/config';
-import {
-  DATE_FORMAT,
-  getToday,
-  getYesterday,
-  resetHours
-} from '@ghostfolio/common/helper';
-import {
-  PortfolioItem,
-  PortfolioPerformance,
-  PortfolioPosition,
-  PortfolioReport,
-  Position,
-  UserWithSettings
-} from '@ghostfolio/common/interfaces';
-import { Country } from '@ghostfolio/common/interfaces/country.interface';
-import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
-import { DateRange, OrderWithAccount } from '@ghostfolio/common/types';
-import { Currency, Prisma } from '@prisma/client';
-import { continents, countries } from 'countries-list';
-import {
-  add,
-  format,
-  getDate,
-  getMonth,
-  getYear,
-  isAfter,
-  isBefore,
-  isSameDay,
-  isToday,
-  isYesterday,
-  parseISO,
-  setDate,
-  setMonth,
-  sub
-} from 'date-fns';
-import { cloneDeep, isEmpty } from 'lodash';
-import * as roundTo from 'round-to';
-
-import { DataProviderService } from '../services/data-provider.service';
-import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
-import { IOrder, MarketState, Type } from '../services/interfaces/interfaces';
-import { RulesService } from '../services/rules.service';
-import { PortfolioInterface } from './interfaces/portfolio.interface';
-import { Order } from './order';
-import { OrderType } from './order-type';
-import { AccountClusterRiskCurrentInvestment } from './rules/account-cluster-risk/current-investment';
-import { AccountClusterRiskInitialInvestment } from './rules/account-cluster-risk/initial-investment';
-import { AccountClusterRiskSingleAccount } from './rules/account-cluster-risk/single-account';
-import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from './rules/currency-cluster-risk/base-currency-current-investment';
-import { CurrencyClusterRiskBaseCurrencyInitialInvestment } from './rules/currency-cluster-risk/base-currency-initial-investment';
-import { CurrencyClusterRiskCurrentInvestment } from './rules/currency-cluster-risk/current-investment';
-import { CurrencyClusterRiskInitialInvestment } from './rules/currency-cluster-risk/initial-investment';
-import { FeeRatioInitialInvestment } from './rules/fees/fee-ratio-initial-investment';
-
-export class Portfolio implements PortfolioInterface {
-  private orders: Order[] = [];
-  private portfolioItems: PortfolioItem[] = [];
-  private user: UserWithSettings;
-
-  public constructor(
-    private accountService: AccountService,
-    private dataProviderService: DataProviderService,
-    private exchangeRateDataService: ExchangeRateDataService,
-    private rulesService: RulesService
-  ) {}
-
-  public async addCurrentPortfolioItems() {
-    const currentData = await this.dataProviderService.get(this.getSymbols());
-
-    const currentDate = new Date();
-
-    const year = getYear(currentDate);
-    const month = getMonth(currentDate);
-    const day = getDate(currentDate);
-
-    const today = new Date(Date.UTC(year, month, day));
-    const yesterday = getYesterday();
-
-    const [portfolioItemsYesterday] = this.get(yesterday);
-
-    const positions: { [symbol: string]: Position } = {};
-
-    this.getSymbols().forEach((symbol) => {
-      positions[symbol] = {
-        symbol,
-        averagePrice: portfolioItemsYesterday?.positions[symbol]?.averagePrice,
-        currency: portfolioItemsYesterday?.positions[symbol]?.currency,
-        firstBuyDate: portfolioItemsYesterday?.positions[symbol]?.firstBuyDate,
-        investment: portfolioItemsYesterday?.positions[symbol]?.investment,
-        investmentInOriginalCurrency:
-          portfolioItemsYesterday?.positions[symbol]
-            ?.investmentInOriginalCurrency,
-        marketPrice:
-          currentData[symbol]?.marketPrice ??
-          portfolioItemsYesterday.positions[symbol]?.marketPrice,
-        quantity: portfolioItemsYesterday?.positions[symbol]?.quantity,
-        transactionCount:
-          portfolioItemsYesterday?.positions[symbol]?.transactionCount
-      };
-    });
-
-    if (portfolioItemsYesterday?.investment) {
-      const portfolioItemsLength = this.portfolioItems.push(
-        cloneDeep({
-          date: today.toISOString(),
-          grossPerformancePercent: 0,
-          investment: portfolioItemsYesterday?.investment,
-          positions: positions,
-          value: 0
-        })
-      );
-
-      // Set value after pushing today's portfolio items
-      this.portfolioItems[portfolioItemsLength - 1].value =
-        this.getValue(today);
-    }
-
-    return this;
-  }
-
-  public async addFuturePortfolioItems() {
-    let investment = this.getInvestment(new Date());
-
-    this.getOrders()
-      .filter((order) => order.getIsDraft() === true)
-      .forEach((order) => {
-        investment += this.exchangeRateDataService.toCurrency(
-          order.getTotal(),
-          order.getCurrency(),
-          this.user.Settings.currency
-        );
-
-        const portfolioItem = this.portfolioItems.find((item) => {
-          return item.date === order.getDate();
-        });
-
-        if (portfolioItem) {
-          portfolioItem.investment = investment;
-        } else {
-          this.portfolioItems.push({
-            investment,
-            date: order.getDate(),
-            grossPerformancePercent: 0,
-            positions: {},
-            value: 0
-          });
-        }
-      });
-
-    return this;
-  }
-
-  public createFromData({
-    orders,
-    portfolioItems,
-    user
-  }: {
-    orders: IOrder[];
-    portfolioItems: PortfolioItem[];
-    user: UserWithSettings;
-  }): Portfolio {
-    orders.forEach(
-      ({
-        account,
-        currency,
-        fee,
-        date,
-        id,
-        quantity,
-        symbol,
-        symbolProfile,
-        type,
-        unitPrice
-      }) => {
-        this.orders.push(
-          new Order({
-            account,
-            currency,
-            fee,
-            date,
-            id,
-            quantity,
-            symbol,
-            symbolProfile,
-            type,
-            unitPrice
-          })
-        );
-      }
-    );
-
-    portfolioItems.forEach(
-      ({ date, grossPerformancePercent, investment, positions, value }) => {
-        this.portfolioItems.push({
-          date,
-          grossPerformancePercent,
-          investment,
-          positions,
-          value
-        });
-      }
-    );
-
-    this.setUser(user);
-
-    return this;
-  }
-
-  public get(aDate?: Date): PortfolioItem[] {
-    if (aDate) {
-      const filteredPortfolio = this.portfolioItems.find((item) => {
-        return isSameDay(aDate, new Date(item.date));
-      });
-
-      if (filteredPortfolio) {
-        return [cloneDeep(filteredPortfolio)];
-      }
-
-      return [];
-    }
-
-    return cloneDeep(this.portfolioItems);
-  }
-
-  public async getDetails(
-    aDateRange: DateRange = 'max'
-  ): Promise<{ [symbol: string]: PortfolioPosition }> {
-    const dateRangeDate = this.convertDateRangeToDate(
-      aDateRange,
-      this.getMinDate()
-    );
-
-    const [portfolioItemsBefore] = this.get(dateRangeDate);
-
-    const [portfolioItemsNow] = await this.get(new Date());
-
-    const cashDetails = await this.accountService.getCashDetails(
-      this.user.id,
-      this.user.Settings.currency
-    );
-    const investment = this.getInvestment(new Date()) + cashDetails.balance;
-    const portfolioItems = this.get(new Date());
-    const symbols = this.getSymbols(new Date());
-    const value = this.getValue() + cashDetails.balance;
-
-    const details: { [symbol: string]: PortfolioPosition } = {};
-
-    const data = await this.dataProviderService.get(symbols);
-
-    symbols.forEach((symbol) => {
-      const accounts: PortfolioPosition['accounts'] = {};
-      let countriesOfSymbol: Country[];
-      let sectorsOfSymbol: Sector[];
-      const [portfolioItem] = portfolioItems;
-
-      const ordersBySymbol = this.getOrders().filter((order) => {
-        return order.getSymbol() === symbol;
-      });
-
-      ordersBySymbol.forEach((orderOfSymbol) => {
-        let currentValueOfSymbol = this.exchangeRateDataService.toCurrency(
-          orderOfSymbol.getQuantity() *
-            portfolioItemsNow.positions[symbol].marketPrice,
-          orderOfSymbol.getCurrency(),
-          this.user.Settings.currency
-        );
-        let originalValueOfSymbol = this.exchangeRateDataService.toCurrency(
-          orderOfSymbol.getQuantity() * orderOfSymbol.getUnitPrice(),
-          orderOfSymbol.getCurrency(),
-          this.user.Settings.currency
-        );
-
-        if (orderOfSymbol.getType() === 'SELL') {
-          currentValueOfSymbol *= -1;
-          originalValueOfSymbol *= -1;
-        }
-
-        if (
-          accounts[orderOfSymbol.getAccount()?.name || UNKNOWN_KEY]?.current
-        ) {
-          accounts[orderOfSymbol.getAccount()?.name || UNKNOWN_KEY].current +=
-            currentValueOfSymbol;
-          accounts[orderOfSymbol.getAccount()?.name || UNKNOWN_KEY].original +=
-            originalValueOfSymbol;
-        } else {
-          accounts[orderOfSymbol.getAccount()?.name || UNKNOWN_KEY] = {
-            current: currentValueOfSymbol,
-            original: originalValueOfSymbol
-          };
-        }
-
-        countriesOfSymbol = (
-          (orderOfSymbol.getSymbolProfile()?.countries as Prisma.JsonArray) ??
-          []
-        ).map((country) => {
-          const { code, weight } = country as Prisma.JsonObject;
-
-          return {
-            code: code as string,
-            continent:
-              continents[countries[code as string]?.continent] ?? UNKNOWN_KEY,
-            name: countries[code as string]?.name ?? UNKNOWN_KEY,
-            weight: weight as number
-          };
-        });
-
-        sectorsOfSymbol = (
-          (orderOfSymbol.getSymbolProfile()?.sectors as Prisma.JsonArray) ?? []
-        ).map((sector) => {
-          const { name, weight } = sector as Prisma.JsonObject;
-
-          return {
-            name: (name as string) ?? UNKNOWN_KEY,
-            weight: weight as number
-          };
-        });
-      });
-
-      let now = portfolioItemsNow.positions[symbol].marketPrice;
-
-      // 1d
-      let before = portfolioItemsBefore?.positions[symbol].marketPrice;
-
-      if (aDateRange === 'ytd') {
-        before =
-          portfolioItemsBefore.positions[symbol].marketPrice ||
-          portfolioItemsNow.positions[symbol].averagePrice;
-      } else if (
-        aDateRange === '1y' ||
-        aDateRange === '5y' ||
-        aDateRange === 'max'
-      ) {
-        before = portfolioItemsNow.positions[symbol].averagePrice;
-      }
-
-      if (
-        !isBefore(
-          parseISO(portfolioItemsNow.positions[symbol].firstBuyDate),
-          parseISO(portfolioItemsBefore?.date)
-        )
-      ) {
-        // Trade was not before the date of portfolioItemsBefore, then override it with average price
-        // (e.g. on same day)
-        before = portfolioItemsNow.positions[symbol].averagePrice;
-      }
-
-      if (isToday(parseISO(portfolioItemsNow.positions[symbol].firstBuyDate))) {
-        now = portfolioItemsNow.positions[symbol].averagePrice;
-      }
-
-      details[symbol] = {
-        ...data[symbol],
-        accounts,
-        symbol,
-        allocationCurrent:
-          this.exchangeRateDataService.toCurrency(
-            portfolioItem.positions[symbol].quantity * now,
-            data[symbol]?.currency,
-            this.user.Settings.currency
-          ) / value,
-        allocationInvestment:
-          portfolioItem.positions[symbol].investment / investment,
-        countries: countriesOfSymbol,
-        grossPerformance: roundTo(
-          portfolioItemsNow.positions[symbol].quantity * (now - before),
-          2
-        ),
-        grossPerformancePercent: roundTo((now - before) / before, 4),
-        investment: portfolioItem.positions[symbol].investment,
-        quantity: portfolioItem.positions[symbol].quantity,
-        sectors: sectorsOfSymbol,
-        transactionCount: portfolioItem.positions[symbol].transactionCount,
-        value: this.exchangeRateDataService.toCurrency(
-          portfolioItem.positions[symbol].quantity * now,
-          data[symbol]?.currency,
-          this.user.Settings.currency
-        )
-      };
-    });
-
-    details[ghostfolioCashSymbol] = await this.getCashPosition({
-      cashDetails,
-      investment,
-      value
-    });
-
-    return details;
-  }
-
-  public getFees(aDate = new Date(0)) {
-    return this.orders
-      .filter((order) => {
-        // Filter out all orders before given date
-        return isBefore(aDate, new Date(order.getDate()));
-      })
-      .map((order) => {
-        return this.exchangeRateDataService.toCurrency(
-          order.getFee(),
-          order.getCurrency(),
-          this.user.Settings.currency
-        );
-      })
-      .reduce((previous, current) => previous + current, 0);
-  }
-
-  public getInvestment(aDate: Date): number {
-    return this.get(aDate)[0]?.investment || 0;
-  }
-
-  public getMinDate() {
-    const orders = this.getOrders().filter(
-      (order) => order.getIsDraft() === false
-    );
-
-    if (orders.length > 0) {
-      return new Date(this.orders[0].getDate());
-    }
-
-    return null;
-  }
-
-  public getPositions(aDate: Date) {
-    const [portfolioItem] = this.get(aDate);
-
-    if (portfolioItem) {
-      return portfolioItem.positions;
-    }
-
-    return {};
-  }
-
-  public getPortfolioItems() {
-    return this.portfolioItems;
-  }
-
-  public getSymbols(aDate?: Date) {
-    let symbols: string[] = [];
-
-    if (aDate) {
-      const positions = this.getPositions(aDate);
-
-      for (const symbol in positions) {
-        if (positions[symbol].quantity > 0) {
-          symbols.push(symbol);
-        }
-      }
-    } else {
-      symbols = this.orders
-        .filter((order) => order.getIsDraft() === false)
-        .map((order) => {
-          return order.getSymbol();
-        });
-    }
-
-    // unique values
-    return Array.from(new Set(symbols));
-  }
-
-  public getTotalBuy() {
-    return this.orders
-      .filter(
-        (order) => order.getIsDraft() === false && order.getType() === 'BUY'
-      )
-      .map((order) => {
-        return this.exchangeRateDataService.toCurrency(
-          order.getTotal(),
-          order.getCurrency(),
-          this.user.Settings.currency
-        );
-      })
-      .reduce((previous, current) => previous + current, 0);
-  }
-
-  public getTotalSell() {
-    return this.orders
-      .filter(
-        (order) => order.getIsDraft() === false && order.getType() === 'SELL'
-      )
-      .map((order) => {
-        return this.exchangeRateDataService.toCurrency(
-          order.getTotal(),
-          order.getCurrency(),
-          this.user.Settings.currency
-        );
-      })
-      .reduce((previous, current) => previous + current, 0);
-  }
-
-  public getOrders(aSymbol?: string) {
-    if (aSymbol) {
-      return this.orders.filter((order) => {
-        return order.getSymbol() === aSymbol;
-      });
-    }
-
-    return this.orders;
-  }
-
-  public getValue(aDate = getToday()) {
-    const positions = this.getPositions(aDate);
-    let value = 0;
-
-    const [portfolioItem] = this.get(aDate);
-
-    for (const symbol in positions) {
-      if (portfolioItem.positions[symbol]?.quantity > 0) {
-        if (
-          isBefore(
-            aDate,
-            parseISO(portfolioItem.positions[symbol]?.firstBuyDate)
-          ) ||
-          portfolioItem.positions[symbol]?.marketPrice === 0
-        ) {
-          value += this.exchangeRateDataService.toCurrency(
-            portfolioItem.positions[symbol]?.quantity *
-              portfolioItem.positions[symbol]?.averagePrice,
-            portfolioItem.positions[symbol]?.currency,
-            this.user.Settings.currency
-          );
-        } else {
-          value += this.exchangeRateDataService.toCurrency(
-            portfolioItem.positions[symbol]?.quantity *
-              portfolioItem.positions[symbol]?.marketPrice,
-            portfolioItem.positions[symbol]?.currency,
-            this.user.Settings.currency
-          );
-        }
-      }
-    }
-
-    return isFinite(value) ? value : null;
-  }
-
-  public async setOrders(aOrders: OrderWithAccount[]) {
-    this.orders = [];
-
-    // Map data
-    aOrders.forEach((order) => {
-      this.orders.push(
-        new Order({
-          account: order.Account,
-          currency: order.currency,
-          date: order.date.toISOString(),
-          fee: order.fee,
-          quantity: order.quantity,
-          symbol: order.symbol,
-          symbolProfile: order.SymbolProfile,
-          type: <OrderType>order.type,
-          unitPrice: order.unitPrice
-        })
-      );
-    });
-
-    await this.update();
-
-    return this;
-  }
-
-  public setUser(aUser: UserWithSettings) {
-    this.user = aUser;
-
-    return this;
-  }
-
-  private async getCashPosition({
-    cashDetails,
-    investment,
-    value
-  }: {
-    cashDetails: CashDetails;
-    investment: number;
-    value: number;
-  }) {
-    const accounts = {};
-    const cashValue = cashDetails.balance;
-
-    cashDetails.accounts.forEach((account) => {
-      accounts[account.name] = {
-        current: account.balance,
-        original: account.balance
-      };
-    });
-
-    return {
-      accounts,
-      allocationCurrent: cashValue / value,
-      allocationInvestment: cashValue / investment,
-      countries: [],
-      currency: Currency.CHF,
-      grossPerformance: 0,
-      grossPerformancePercent: 0,
-      investment: cashValue,
-      marketPrice: 0,
-      marketState: MarketState.open,
-      name: Type.Cash,
-      quantity: 0,
-      sectors: [],
-      symbol: ghostfolioCashSymbol,
-      type: Type.Cash,
-      transactionCount: 0,
-      value: cashValue
-    };
-  }
-
-  /**
-   * TODO: Refactor
-   */
-  private async update() {
-    this.portfolioItems = [];
-
-    let currentDate = this.getMinDate();
-
-    if (!currentDate) {
-      return;
-    }
-
-    // Set current date to first of month
-    currentDate = setDate(currentDate, 1);
-
-    const historicalData = await this.dataProviderService.getHistorical(
-      this.getSymbols(),
-      'month',
-      currentDate,
-      new Date()
-    );
-
-    while (isBefore(currentDate, Date.now())) {
-      const positions: { [symbol: string]: Position } = {};
-      this.getSymbols().forEach((symbol) => {
-        positions[symbol] = {
-          symbol,
-          averagePrice: 0,
-          currency: undefined,
-          firstBuyDate: null,
-          investment: 0,
-          investmentInOriginalCurrency: 0,
-          marketPrice:
-            historicalData[symbol]?.[format(currentDate, DATE_FORMAT)]
-              ?.marketPrice || 0,
-          quantity: 0,
-          transactionCount: 0
-        };
-      });
-
-      if (!isYesterday(currentDate) && !isToday(currentDate)) {
-        // Add to portfolio (ignore yesterday and today because they are added later)
-        this.portfolioItems.push(
-          cloneDeep({
-            date: currentDate.toISOString(),
-            grossPerformancePercent: 0,
-            investment: 0,
-            positions: positions,
-            value: 0
-          })
-        );
-      }
-
-      const year = getYear(currentDate);
-      const month = getMonth(currentDate);
-      const day = getDate(currentDate);
-
-      // Count month one up for iteration
-      currentDate = new Date(Date.UTC(year, month + 1, day, 0));
-    }
-
-    const yesterday = getYesterday();
-
-    const positions: { [symbol: string]: Position } = {};
-
-    if (isAfter(yesterday, this.getMinDate())) {
-      // Add yesterday
-      this.getSymbols().forEach((symbol) => {
-        positions[symbol] = {
-          symbol,
-          averagePrice: 0,
-          currency: undefined,
-          firstBuyDate: null,
-          investment: 0,
-          investmentInOriginalCurrency: 0,
-          marketPrice:
-            historicalData[symbol]?.[format(yesterday, DATE_FORMAT)]
-              ?.marketPrice || 0,
-          name: '',
-          quantity: 0,
-          transactionCount: 0
-        };
-      });
-
-      this.portfolioItems.push(
-        cloneDeep({
-          positions,
-          date: yesterday.toISOString(),
-          grossPerformancePercent: 0,
-          investment: 0,
-          value: 0
-        })
-      );
-    }
-
-    this.updatePortfolioItems();
-  }
-
-  private convertDateRangeToDate(aDateRange: DateRange, aMinDate: Date) {
-    let currentDate = new Date();
-
-    const normalizedMinDate =
-      getDate(aMinDate) === 1
-        ? aMinDate
-        : add(setDate(aMinDate, 1), { months: 1 });
-
-    const year = getYear(currentDate);
-    const month = getMonth(currentDate);
-    const day = getDate(currentDate);
-
-    currentDate = new Date(Date.UTC(year, month, day, 0));
-
-    switch (aDateRange) {
-      case '1d':
-        return sub(currentDate, {
-          days: 1
-        });
-      case 'ytd':
-        currentDate = setDate(currentDate, 1);
-        currentDate = setMonth(currentDate, 0);
-        return isAfter(currentDate, normalizedMinDate)
-          ? currentDate
-          : undefined;
-      case '1y':
-        currentDate = setDate(currentDate, 1);
-        currentDate = sub(currentDate, {
-          years: 1
-        });
-        return isAfter(currentDate, normalizedMinDate)
-          ? currentDate
-          : undefined;
-      case '5y':
-        currentDate = setDate(currentDate, 1);
-        currentDate = sub(currentDate, {
-          years: 5
-        });
-        return isAfter(currentDate, normalizedMinDate)
-          ? currentDate
-          : undefined;
-      default:
-        // Gets handled as all data
-        return undefined;
-    }
-  }
-
-  private updatePortfolioItems() {
-    let currentDate = new Date();
-
-    const year = getYear(currentDate);
-    const month = getMonth(currentDate);
-    const day = getDate(currentDate);
-
-    currentDate = new Date(Date.UTC(year, month, day, 0));
-
-    if (this.portfolioItems?.length === 1) {
-      // At least one portfolio items is needed, keep it but change the date to today.
-      // This happens if there are only orders from today
-      this.portfolioItems[0].date = currentDate.toISOString();
-    } else {
-      // Only keep entries which are not before first buy date
-      this.portfolioItems = this.portfolioItems.filter((portfolioItem) => {
-        return (
-          isSameDay(parseISO(portfolioItem.date), this.getMinDate()) ||
-          isAfter(parseISO(portfolioItem.date), this.getMinDate())
-        );
-      });
-    }
-
-    this.orders.forEach((order) => {
-      if (order.getIsDraft() === false) {
-        let index = this.portfolioItems.findIndex((item) => {
-          const dateOfOrder = setDate(parseISO(order.getDate()), 1);
-          return isSameDay(parseISO(item.date), dateOfOrder);
-        });
-
-        if (index === -1) {
-          // if not found, we only have one order, which means we do not loop below
-          index = 0;
-        }
-
-        for (let i = index; i < this.portfolioItems.length; i++) {
-          // Set currency
-          this.portfolioItems[i].positions[order.getSymbol()].currency =
-            order.getCurrency();
-
-          this.portfolioItems[i].positions[
-            order.getSymbol()
-          ].transactionCount += 1;
-
-          if (order.getType() === 'BUY') {
-            if (
-              !this.portfolioItems[i].positions[order.getSymbol()].firstBuyDate
-            ) {
-              this.portfolioItems[i].positions[order.getSymbol()].firstBuyDate =
-                resetHours(parseISO(order.getDate())).toISOString();
-            }
-
-            this.portfolioItems[i].positions[order.getSymbol()].quantity +=
-              order.getQuantity();
-            this.portfolioItems[i].positions[order.getSymbol()].investment +=
-              this.exchangeRateDataService.toCurrency(
-                order.getTotal(),
-                order.getCurrency(),
-                this.user.Settings.currency
-              );
-            this.portfolioItems[i].positions[
-              order.getSymbol()
-            ].investmentInOriginalCurrency += order.getTotal();
-
-            this.portfolioItems[i].investment +=
-              this.exchangeRateDataService.toCurrency(
-                order.getTotal(),
-                order.getCurrency(),
-                this.user.Settings.currency
-              );
-          } else if (order.getType() === 'SELL') {
-            this.portfolioItems[i].positions[order.getSymbol()].quantity -=
-              order.getQuantity();
-
-            if (
-              this.portfolioItems[i].positions[order.getSymbol()].quantity === 0
-            ) {
-              this.portfolioItems[i].positions[
-                order.getSymbol()
-              ].investment = 0;
-              this.portfolioItems[i].positions[
-                order.getSymbol()
-              ].investmentInOriginalCurrency = 0;
-            } else {
-              this.portfolioItems[i].positions[order.getSymbol()].investment -=
-                this.exchangeRateDataService.toCurrency(
-                  order.getTotal(),
-                  order.getCurrency(),
-                  this.user.Settings.currency
-                );
-              this.portfolioItems[i].positions[
-                order.getSymbol()
-              ].investmentInOriginalCurrency -= order.getTotal();
-            }
-
-            this.portfolioItems[i].investment -=
-              this.exchangeRateDataService.toCurrency(
-                order.getTotal(),
-                order.getCurrency(),
-                this.user.Settings.currency
-              );
-          }
-
-          this.portfolioItems[i].positions[order.getSymbol()].averagePrice =
-            this.portfolioItems[i].positions[order.getSymbol()]
-              .investmentInOriginalCurrency /
-            this.portfolioItems[i].positions[order.getSymbol()].quantity;
-
-          const currentValue = this.getValue(
-            parseISO(this.portfolioItems[i].date)
-          );
-
-          this.portfolioItems[i].grossPerformancePercent =
-            currentValue / this.portfolioItems[i].investment - 1 || 0;
-          this.portfolioItems[i].value = currentValue;
-        }
-      }
-    });
-  }
-}