Client Portfolio Position

How to follow this chapter

Inputs: userId, portfolioId and a CLIENT token from the earlier steps.

Steps and expected result: List portfolios, select one, then retrieve its snapshot and associated data. Download the document selected from metadata using its returned reference and portfolio ID.

Alternative or later actions: Values, cash accounts and documents can arrive asynchronously. An empty list or unavailable data can occur before portfolio processing completes.

DTO source: BankAccountTypeOutputModel, BookingCodeOutputModel, CashAccountMovementOutputModel, CashAccountOutputModel, PortfolioOutputModel, PortfolioValuesContainerOutputModel, UserBankDocumentInfoOutputModel

Variables assigned in an earlier block are reused by later blocks. Client-entered values come from your UI; keep identifiers and lookup results in the current client context. Do not create a fresh context between steps.

The snippets use shared helper classes. See Shared helper classes: complete C# and Java source before copying an example.
Before you start: The userId, selected portfolioId and a CLIENT token or an administrative token authorized to read those resources.
Keep for the next step: Portfolio snapshots, performance values, cash movements and downloadable bank documents.
In this section we will look at how to retrieve the client's portfolio position.

Portfolios list

Inputs: userId and CLIENT token.
Action: Read the current portfolio list using Snapshot.
Keep / expected result: Keep the portfolioId selected by the client.
For this operation, the call must have the authorization HTTP header set with the token with CLIENT or ADMIN role.

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Assuming this is our client ID
// Use the userId returned by registration.
var clientPortfolios = await httpClient.GetFromJsonAsync<IList<PortfolioOutputModel>>($"api/v1/user-portfolios/user-id/{userId}?portfolio-view-as=Snapshot");

								
// Assuming this is our client ID
// Use the userId returned by registration.

HttpRequest portfolioRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-portfolios/user-id/" + userId + "?portfolio-view-as=Snapshot"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

var response = httpClient.send(portfolioRequest, BodyHandlers.ofString());
Api.requireSuccess(response);

Type listType = new TypeToken<ArrayList<PortfolioOutputModel>>(){}.getType();
List<PortfolioOutputModel> portfolioObjs = new Gson().fromJson(response.body(), listType);

Portfolio by Id

Inputs: The selected portfolioId.
Action: Read the current portfolio snapshot.
Keep / expected result: Use the returned state and HasPendingOrder when presenting available actions.
Retrieve the selected portfolio snapshot, including its current attributes and available values. These examples explicitly request Snapshot: omitting portfolio-view-as uses the API's Forecast default.

// Assuming this is the portfolio ID
// portfolioId is selected from the returned client portfolio list.
var clientPortfolio = await httpClient.GetFromJsonAsync<PortfolioOutputModel>($"api/v1/user-portfolios/portfolio-id/{portfolioId}?portfolio-view-as=Snapshot");

								
// Assuming this is the portfolio ID
// portfolioId is selected from the returned client portfolio list.

HttpRequest portfolioRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-portfolios/portfolio-id/" + portfolioId + "?portfolio-view-as=Snapshot"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(portfolioRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
PortfolioOutputModel portfolioObj = new Gson().fromJson(response.body(), PortfolioOutputModel.class);

With the information received above, we can create a first portfolio view:
Example portfolio details

Portfolio values

Inputs: portfolioId and the desired date range.
Action: Read the performance/value container.
Keep / expected result: Read its Values collection; no data may yet be available for the requested period.
Historical portfolio performance, market values, total assets, profit and loss, and cash amounts can be retrieved using the performance API endpoint. The response is a PortfolioValuesContainerOutputModel whose Values collection contains dated points for these charts. Optional startingDate and endingDate query parameters use yyyyMMdd; response dates follow the JSON schema. HTTP 404 can mean that performance data is not yet available for the portfolio.
Performances Example portfolio performance chart
Market values Example portfolio market value chart

// Assuming this is the portfolio ID we want to display
// portfolioId is selected from the returned client portfolio list.
var portfolioValues = await httpClient.GetFromJsonAsync<PortfolioValuesContainerOutputModel>($"api/v1/user-portfolios/portfolio-id/{portfolioId}/performances");

								
// Assuming this is the portfolio ID we want to display
// Use the same selected portfolioId.

HttpRequest portfolioPerformanceRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-portfolios/portfolio-id/" + portfolioId + "/performances"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(portfolioPerformanceRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
PortfolioValuesContainerOutputModel portfolioPerformanceObj = new Gson().fromJson(response.body(), PortfolioValuesContainerOutputModel.class);

Portfolio cash amount

Inputs: portfolioId.
Action: Find the associated cash account and read its balance and movements.
Keep / expected result: Keep the relevant cash-account ID; display the balance together with its date.
Retrieve the cash accounts associated with the selected portfolio. Use the IBAN and balance returned for the relevant CASH-ACCOUNT entry; handle an absent cash account as a portfolio-lifecycle or configuration condition. This IBAN corresponds to the cash account used by the client to make payments, for example, the annual payment for plan 3A. Balances reflect the accounting date returned by the service and can lag a payment or trade while custody-institution data is being processed. The following snippets find the portfolio's cash account:

// Get bank account types: Cash account/Custody account
var bankAccountTypes = await httpClient.GetFromJsonAsync<List<BankAccountTypeOutputModel>>($"api/v1/cash-account-types");
var cashAccountType = bankAccountTypes.First(x => x.Code == "CASH-ACCOUNT");
// Get portfolio's bank accounts
var bankAccounts = await httpClient.GetFromJsonAsync<List<CashAccountOutputModel>>($"api/v1/user-cash-accounts/portfolio-id/{portfolioId}");
var cashAccount = bankAccounts.First(x => x.AccountTypeId == cashAccountType.Id);

								
// Portfolio cash amount

HttpRequest bankAccountTypesRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("cash-account-types"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

// Get bank account types: Cash account/Custody account
response = httpClient.send(bankAccountTypesRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type listBankAccountTypes = new TypeToken<ArrayList<BankAccountTypeOutputModel>>(){}.getType();
List<BankAccountTypeOutputModel> bankAccountTypes = new Gson().fromJson(response.body(), listBankAccountTypes);
var bankAccountType = bankAccountTypes.stream().filter(x -> "CASH-ACCOUNT".equals(x.Code)).findFirst().orElseThrow(() -> new IllegalStateException("Required reference data is unavailable"));

// Get portfolio's bank accounts
HttpRequest portfolioBankAccountRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-cash-accounts/portfolio-id/" + portfolioId))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(portfolioBankAccountRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type cashAccountType = new TypeToken<ArrayList<CashAccountOutputModel>>(){}.getType();
List<CashAccountOutputModel> cashAccountObjs = new Gson().fromJson(response.body(), cashAccountType);
CashAccountOutputModel cashAccountObj = cashAccountObjs.stream().filter(x -> x.AccountTypeId == bankAccountType.Id).findFirst().orElseThrow(() -> new IllegalStateException("Required reference data is unavailable"));

CashAccountOutputModel contains Iban, Balance and BalanceDate. A cash account also has its own identifier; use that Id when requesting its movements.
In addition, the cash account provides a list of movements. For example, booked portfolio purchases and sales can appear in the movements returned for the cash account. The following code snippet shows how to retrieve cash account movements:
									
var bankAccountMovements = await httpClient.GetFromJsonAsync<List<CashAccountMovementOutputModel>>($"api/v1/user-cash-accounts/cash-account-id/{cashAccount.Id}/movements");

									
HttpRequest cashAccountMovementsRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-cash-accounts/cash-account-id/" + cashAccountObj.Id + "/movements"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(cashAccountMovementsRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type cashAccountMovementType = new TypeToken<ArrayList<CashAccountMovementOutputModel>>(){}.getType();
List<CashAccountMovementOutputModel> cashAccountMovementObjs = new Gson().fromJson(response.body(), cashAccountMovementType);

Note that the payload of "CashAccountMovementOutputModel" has a property called "BookingCodeId" that can be decoded to retrieve the description of the movement type:
									
foreach (var bankAccountMovement in bankAccountMovements)
{
    var bookingCode = await httpClient.GetFromJsonAsync<BookingCodeOutputModel>($"api/v1/booking-codes/id/{bankAccountMovement.BookingCodeId}");
    var bookingCodeValue = bookingCode.Code;
    var bookingCodeDescription = bookingCode.Description;
// Continue with the data returned by the API.
}

	
									
HttpRequest bookingCodeRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("booking-codes"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(bookingCodeRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type bookingCodeTypes = new TypeToken<ArrayList<BookingCodeOutputModel>>(){}.getType();
List<BookingCodeOutputModel> bookingCodeTypeObjs = new Gson().fromJson(response.body(), bookingCodeTypes);

cashAccountMovementObjs.forEach(bankAccountMovement -> {
    BookingCodeOutputModel bookingCodeTypeObj = bookingCodeTypeObjs.stream().filter(x -> x.Id == bankAccountMovement.BookingCodeId).findFirst().orElseThrow(() -> new IllegalStateException("Required reference data is unavailable"));

    var bookingCode = bookingCodeTypeObj.Code;
    var bookingCodeDescription = bookingCodeTypeObj.Description;
    // Continue with the data returned by the API.
});

Example cash account movements

Portfolio bank documents

Inputs: userId, then the document ID selected from metadata.
Action: List documents and download the selected binary PDF.
Keep / expected result: Use the metadata UserPortfolioId and Reference for the download, rather than assuming the current portfolio owns every document.
PDF documents issued/provided by the Bank are available for download by the client.

// User bank documents
var url = $"api/v1/user-bank-documents/user-id/{userId}/list?starting-from=20000101";
var bankDocumentsForAllPortfolios = await httpClient.GetFromJsonAsync<IList<UserBankDocumentInfoOutputModel>>(url);

								
// User bank documents
HttpRequest userDocumentsMetadataRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-bank-documents/user-id/" + userId + "/list?starting-from=20000101"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + accessToken)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(userDocumentsMetadataRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type userBankDocumentInfoType = new TypeToken<ArrayList<UserBankDocumentInfoOutputModel>>(){}.getType();
List<UserBankDocumentInfoOutputModel> userDocumentsMetadataObjs = new Gson().fromJson(response.body(), userBankDocumentInfoType);

Select a document from the returned metadata and use its matching portfolio identifier and reference. The metadata field UserPortfolioId identifies the document's portfolio. Select selectedDocumentId from the metadata list, and use the document Reference used to download the PDF.

// selectedDocumentId identifies an item chosen from the returned metadata.
var selectedDocument = bankDocumentsForAllPortfolios.Single(x => x.Id == selectedDocumentId);
var documentPortfolioId = selectedDocument.UserPortfolioId;
var reference = selectedDocument.Reference;
var url = $"api/v1/user-bank-documents/user-id/{userId}/portfolio-id/{documentPortfolioId}/download-bank-document/reference/{Uri.EscapeDataString(reference)}";
var byteArray = await httpClient.GetByteArrayAsync(url);
// Save or display the PDF after the HTTP request succeeds.

								
// selectedDocumentId identifies an item chosen from the returned metadata.
var selectedDocument = userDocumentsMetadataObjs.stream()
    .filter(x -> x.Id == selectedDocumentId).findFirst()
    .orElseThrow(() -> new IllegalArgumentException("Select a listed document"));
var documentPortfolioId = selectedDocument.UserPortfolioId;
var reference = selectedDocument.Reference;
var encodedReference = java.net.URLEncoder.encode(reference, StandardCharsets.UTF_8).replace("+", "%20");
HttpRequest request = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("user-bank-documents/user-id/" + userId
        + "/portfolio-id/" + documentPortfolioId + "/download-bank-document/reference/" + encodedReference))
    .header("Authorization", "Bearer " + accessToken).GET().build();
var response = httpClient.send(request, BodyHandlers.ofByteArray());
Api.requireSuccess(response);
byte[] pdfBytes = response.body();
// Save or display pdfBytes as a PDF.

Below is an example of a bank document:
Example PDF contract