ALM app: improving the front end
Now we can tidy up and improve some elements of the GUI to make things look a bit nicer.
The majority of this section will see changes made in the front-end folder, which is found under ALM/client.
Fixing capitalisation on front-end grids
Genesis Create saves with coding case, rather than visual case.
So, to start, let's do something very easy. We shall update some of the names in the front-end grid.
-
Open the file ALM\client\src\routes\config.ts.
-
Change the capitalisation of the tab names (titles) in the GUI:
//Replace the following lines
title: 'Fx blotter',
title: 'Sourced trades',
//with
title: 'FX Blotter',
title: 'Sourced Trades',
Remove zero values from the position grid
OK, we are actually going to change the back-end code here - but don't tell anyone.
-
Open the file ALM\server\ALM-app\src\main\genesis\scripts\ALM-reqrep.kts.
-
Replace the
POSITION
request reply with thewhere
clause, as below:
requestReply(POSITION) {
where { row, parameters ->
row.amount != 0.0
}
}
Formatting on positive/negative numbers
The code snippet below will make positive numbers green and negative numbers red.
-
In the folder ALM\client\src\utils, create a new file called util-formatters.ts.
-
Add the code below to the file. (We'll need to use this function in a few other files later on.):
export function cellStyle(params){
if(params.value > 0){
return {color: '#7ACC79'}
}else if (params.value < 0){
return {color: '#F9644D'}
} else {
return {color: '#FFFFFF'}
}
};
Format the columns
Now insert this function into some of the column selections.
-
Open the file ALM\client\src\routes\sourced-trades\loans-manager\loans.column.defs.ts.
-
To include this new function add the following import statement to the top of the file:
import { cellStyle } from '../../../utils/util-formatters';
- Under the
PAYMENT_AMOUNT
column description, add the below code snippet:
cellStyle: (params) => cellStyle(params),
The PAYMENT_AMOUNT
section should now look like this:
{
field: "PAYMENT_AMOUNT",
valueFormatter: getNumberFormatter("0,0.00", null),
cellStyle: (params) => cellStyle(params),
},
- Do the same for
DRAW_DOWN_AMOUNT
in the same resource file:
{
field: "DRAWDOWN_AMOUNT",
valueFormatter: getNumberFormatter("0,0.00", null),
cellStyle: (params) => cellStyle(params),
},
- Make similar changes in the file ALM\client\src\routes\sourced-trades\c-ds-manager\c-ds.column.defs.ts.
Adjust the import statement and add the same line to DEPOSIT_AMOUNT
and MATURITY_AMOUNT
:
import { cellStyle } from '../../../utils/util-formatters';
...
{
field: "MATURITY_AMOUNT",
valueFormatter: getNumberFormatter("0,0.00", null),
cellStyle: (params) => cellStyle(params),
},
...
{
field: "DEPOSIT_AMOUNT",
valueFormatter: getNumberFormatter("0,0.00", null),
cellStyle: (params) => cellStyle(params),
},
Format the positions grid
We can also add this function to the positions grid.
-
Open the file ALM\client\src\routes\fx-blotter\position-grid\position.gridOptions.ts.
-
Add the import statement in the same way as you did before:
import { cellStyle } from '../../../utils/util-formatters';
- Within the
AMOUNT
column definition, add:
cellStyle: (params) => cellStyle(params)
The block should look like this:
{
field: "AMOUNT",
valueFormatter: getNumberFormatter("0,0.00", null),
cellStyle: (params) => cellStyle(params),
},
Improving the Trade Entry/Modify pop ups
By default, the trade entry screens in our application are displayed in a vertical list. When we have many fields, this can be difficult to navigate. We can change this through the template.
- In the folder **ALM\client\src\routes\fx-blotter\fx-trades-manager`, adjust the forms for creating and updating an FX Trade.
Edit both fx-trades.create.form.schema.ts and fx-trades.update.form.schema.ts, replacing "type": "VerticalLayout",
with:
"type": "LayoutVertical2Columns",
If you are replacing the whole line, make sure to keep the comma at the end of this line.
Pivot positions
Let’s create a consolidated view of your FX trades.
To do this, we need to create a new custom element and replace the existing grid pro. This new element will subscribe to a stream from the consolidator output table and transform the output.
This class will subscribe to the new ALL_POSITIONS
stream. It will transform the array and group trades by date and create an aggregate daily amount for each currency.
It will also dynamically update the column definitions on the grid so that there is a column for each currency.
- Create your new custom element in a new file: client/src/components/positions-grid.ts. Add the content below.
import { customElement, FASTElement, html, ref } from '@microsoft/fast-element';
import { Connect } from '@genesislcap/foundation-comms';
import { getDateFormatter, getNumberFormatter } from '@genesislcap/foundation-utils';
import { GridPro } from '@genesislcap/rapid-grid-pro';
import { cellStyle } from '../utils/util-formatters';
@customElement({
name: 'positions-grid',
template: html<PositionsGrid>`
<rapid-grid-pro
${ref('grid')}
@onGridReady="${(x, e) => x.onGridReady(e)}"
>
</rapid-grid-pro>
`
})
export class PositionsGrid extends FASTElement {
@Connect connect!: Connect;
grid: GridPro;
connectedCallback() {
super.connectedCallback();
this.grid.gridOptions = {};
}
onGridReady(e): void {
this.connect.stream('ALL_POSITIONS', async () => {
const rows = await this.getPositionsData();
const currencies = this.getAllCurrenciesFromStream(rows);
const colDefs = this.createColDefs(rows, currencies);
const rowData = this.mapRowData(rows, currencies)
this.grid.gridApi.setColumnDefs(colDefs);
this.grid.gridApi.setRowData(rowData)
}, console.error);
}
async getPositionsData() {
const message = await this.connect.request('POSITION')
return message?.REPLY;
}
private mapRowData(message: any[], currencies: string[]): any[] {
if (!message) {
return [];
}
const rowMap: { [key: string] : any } = message.reduce((acc, row) => {
const { SETTLEMENT_DATE, AMOUNT, CURRENCY } = row;
if (!acc[SETTLEMENT_DATE]) {
acc[SETTLEMENT_DATE] = {
settlementDate: SETTLEMENT_DATE,
[CURRENCY]: AMOUNT
}
} else {
const existingValue = acc[SETTLEMENT_DATE][CURRENCY] !== undefined ? acc[SETTLEMENT_DATE][CURRENCY] : 0;
acc[SETTLEMENT_DATE][CURRENCY] = existingValue + AMOUNT
}
return acc;
}, {});
return Object.keys(rowMap).map(key => rowMap[key]).sort((a,b) => a.settlementDate > b.settlementDate ? 1 : -1);
}
private getAllCurrenciesFromStream(message: any[]): string[] {
if (!message) {
return [];
}
return message.reduce((currencies: string[], row) => {
const { CURRENCY } = row;
if (!currencies.includes(CURRENCY)) {
currencies.push(CURRENCY)
}
return currencies;
}, []);
}
private createColDefs(message: any[], currencies: string[]) {
const colDefs: any[] = [
{
field: "settlementDate",
headerName: "Settlement Date",
hide: false,
valueFormatter: getDateFormatter("en-GB", {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}
]
if (!message || message.length) {
currencies.forEach(c => {
colDefs.push({
field: c,
headerName: c,
hide: false,
type: 'rightAligned',
valueFormatter: getNumberFormatter("0,0.00", null),
cellStyle: (params) => cellStyle(params)
})
});
return colDefs;
}
}
}
- Add your new custom element to the client/src/components/components.ts.
This ensures that the browser knows how to render it. To do this, add two lines to the top of the file, as shown below:
import { EntityManagement } from '@genesislcap/foundation-entity-management';
import { Form } from '@genesislcap/foundation-forms';
import { foundationLayoutComponents } from '@genesislcap/foundation-layout';
import { getApp } from '@genesislcap/foundation-shell/app';
import { FoundationRouter } from '@genesislcap/foundation-ui';
import * as zeroDesignSystem from '@genesislcap/foundation-zero';
import { g2plotChartsComponents } from '@genesislcap/g2plot-chart';
import * as rapidDesignSystem from '@genesislcap/rapid-design-system';
import { rapidGridComponents } from '@genesislcap/rapid-grid-pro';
import { NotPermittedComponent } from './not-permitted-component';
import { PositionsGrid } from './positions-grid';
/**
* Ensure tree shaking doesn't remove these.
*/
FoundationRouter;
EntityManagement;
Form;
NotPermittedComponent;
PositionsGrid;
- Update your route template in client/src/routes/fx-blotter/fx-blotter.template.ts; find the
rapid-layout-item
forPosition
:
<rapid-layout-item title="Position">
<fx-blotter-position-grid></fx-blotter-position-grid>
</rapid-layout-item>
... and replace it with the following code:
<rapid-layout-item title="Positions">
<positions-grid></positions-grid>
</rapid-layout-item>
- Add the following to the imports:
import { PositionsGrid } from '../../components/positions-grid';
- Add PositionsGrid to the lines just below:
FxBlotterFxTradesManager;
FxBlotterPositionGrid;
FxBlotterRatesGrid;
PositionsGrid;
This will use the positions grid we added in the new positions-grid.ts file.
Adding a new chart
We won't be inputting loan data until we deal with data from external sources. However, it would be useful to set up currency conversion on the loan pie chart so that it displays all data in a single currency, in this case GBP.
We'll do this using the FX_RATE
table that we created.
Update the back end
-
Go to the back-end code, inside ALM/server. We need to add a new view to provide data for this chart.
-
Open the file ALM\server\ALM-app\src\main\genesis\cfg\ALM-view-dictionary.kts.
-
Add the code below, which defines a view joining
LOAN_TRADE
to the latestFX_RATE
.
view("LOAN_PAYMENTS_GBP", LOAN_TRADE) {
joins {
joining(FX_RATE) {
on(LOAN_TRADE.PAYMENT_CURRENCY to FX_RATE { SOURCE_CURRENCY })
.and(FX_RATE { TARGET_CURRENCY } to "GBP")
}
}
fields {
derivedField("PAYMENT_AMOUNT", DOUBLE) {
withInput(LOAN_TRADE.PAYMENT_AMOUNT, FX_RATE.RATE) { amount, rate ->
if (amount == null || rate == null) amount
else amount * rate
}
}
LOAN_TRADE.CLIENT_NAME
LOAN_TRADE.PAYMENT_CURRENCY
}
}
- In the file ALM\server\ALM-app\src\main\genesis\scripts\ALM-dataserver.kts, add a new query to towards the end of the file. This enables the front end to query this data.
query("LOAN_PAYMENTS_GBP", LOAN_PAYMENTS_GBP) {
fields {
PAYMENT_AMOUNT
CLIENT_NAME
PAYMENT_CURRENCY
}
}
Update the front end
-
Go to the front-end code inside ALM\client.
-
Open the file ALM\client\src\routes\sourced-trades\sourced-trades.template.ts and find the following section:
<rapid-layout-item title="Loans by Client">
- Modify this to the following to change the title in the front end:
<rapid-layout-item title="Loans by Client - GBP">
- In the file ALM\client\src\routes\sourced-trades\loans-by-client-chart\loans-by-client.template.ts, adjust the
chart-datasource
to use the new data server and field:
<chart-datasource
resourceName="LOAN_PAYMENTS_GBP"
server-fields="CLIENT_NAME PAYMENT_AMOUNT"
></chart-datasource>
- In the file ALM\client\src\routes\sourced-trades\sourced-trades.template.ts, add a third chart, showing GBP Equivalent currency exposure from the loans. To do this, add this
rapid layout item
between the loan chartrapid layout item
and the CD chartrapid layout item
:
<rapid-layout-item title="Loans by Currency - GBP Equivalent">
<rapid-g2plot-chart
type="bar"
:config="${(x) => ({
xField: 'value',
yField: 'groupBy',
seriesField: 'groupBy',
barWidthRatio: 0.8,
})}"
>
<chart-datasource
resourceName="LOAN_PAYMENTS_GBP"
server-fields="PAYMENT_CURRENCY PAYMENT_AMOUNT"
></chart-datasource>
</rapid-g2plot-chart>
</rapid-layout-item>
We can also make the FX Blotter
look a bit neater by changing the layout slightly. In ALM\client\src\routes\fx-blotter\fx-blotter.template.ts
, just before the position rapid layout item
lines add:
<rapid-layout-region type="vertical"> //LINE TO ADD
<rapid-layout-item title="Position">
Then close the region at the end of the Rates rapid-layout-item
block:
</rapid-layout-item>
</rapid-layout-region> //LINE TO ADD
</rapid-layout-region>
</rapid-layout>
You can view a final version of the code for the ALM app, including all the modifications outlined in this guide, in the ALM app repository.