@Override
  public Client filter(Client client) {
    ReadOnlyClient pseudoClient = new ReadOnlyClient(client);

    Map<Account, ReadOnlyAccount> account2pseudo = new HashMap<>();
    Set<Security> usedSecurities = new HashSet<>();

    for (Portfolio portfolio : portfolios) {
      ReadOnlyAccount pseudoAccount =
          account2pseudo.computeIfAbsent(
              portfolio.getReferenceAccount(),
              a -> {
                ReadOnlyAccount pa = new ReadOnlyAccount(a);
                pseudoClient.internalAddAccount(pa);
                return pa;
              });

      ReadOnlyPortfolio pseudoPortfolio = new ReadOnlyPortfolio(portfolio);
      pseudoPortfolio.setReferenceAccount(pseudoAccount);
      pseudoClient.internalAddPortfolio(pseudoPortfolio);

      adaptPortfolioTransactions(portfolio, pseudoPortfolio, usedSecurities);

      if (!accounts.contains(portfolio.getReferenceAccount()))
        collectDividends(portfolio, pseudoAccount, usedSecurities);
    }

    for (Account account : accounts) {
      ReadOnlyAccount pseudoAccount =
          account2pseudo.computeIfAbsent(
              account,
              a -> {
                ReadOnlyAccount pa = new ReadOnlyAccount(a);
                pseudoClient.internalAddAccount(pa);
                return pa;
              });

      adaptAccountTransactions(account, pseudoAccount, usedSecurities);
    }

    for (Security security : usedSecurities) pseudoClient.internalAddSecurity(security);

    return pseudoClient;
  }
  private void adaptPortfolioTransactions(
      Portfolio portfolio, ReadOnlyPortfolio pseudoPortfolio, Set<Security> usedSecurities) {
    for (PortfolioTransaction t : portfolio.getTransactions()) {
      usedSecurities.add(t.getSecurity());

      switch (t.getType()) {
        case BUY:
          if (accounts.contains(t.getCrossEntry().getCrossOwner(t)))
            pseudoPortfolio.internalAddTransaction(t);
          else
            pseudoPortfolio.internalAddTransaction(
                convertTo(t, PortfolioTransaction.Type.DELIVERY_INBOUND));
          break;
        case TRANSFER_IN:
          if (portfolios.contains(t.getCrossEntry().getCrossOwner(t)))
            pseudoPortfolio.internalAddTransaction(t);
          else
            pseudoPortfolio.internalAddTransaction(
                convertTo(t, PortfolioTransaction.Type.DELIVERY_INBOUND));
          break;
        case SELL:
          if (accounts.contains(t.getCrossEntry().getCrossOwner(t)))
            pseudoPortfolio.internalAddTransaction(t);
          else
            pseudoPortfolio.internalAddTransaction(
                convertTo(t, PortfolioTransaction.Type.DELIVERY_OUTBOUND));
          break;
        case TRANSFER_OUT:
          if (portfolios.contains(t.getCrossEntry().getCrossOwner(t)))
            pseudoPortfolio.internalAddTransaction(t);
          else
            pseudoPortfolio.internalAddTransaction(
                convertTo(t, PortfolioTransaction.Type.DELIVERY_OUTBOUND));
          break;
        case DELIVERY_INBOUND:
        case DELIVERY_OUTBOUND:
          pseudoPortfolio.internalAddTransaction(t);
          break;
        default:
          throw new UnsupportedOperationException();
      }
    }
  }