From 25695d08438b1378ad0d37cf864f4ade22f03917 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Wed, 8 Jan 2025 19:26:45 +0100 Subject: [PATCH 01/19] Created investor preferences screener, added demo. --- .../dashboards-investor-preferences/demo.css | 71 ++++ .../dashboards-investor-preferences/demo.html | 70 ++++ demos/dashboards-investor-preferences/demo.js | 352 ++++++++++++++++++ demos/index.html | 1 + .../InvestmentScreenerOptions.ts | 5 +- .../InvestorPreferencesConnector.ts | 220 +++++++++++ .../InvestorPreferencesConverter.ts | 99 +++++ .../InvestorPreferencesJSON.ts | 72 ++++ .../InvestorPreferencesOptions.ts | 244 ++++++++++++ src/Screeners/InvestorPreferences/README.md | 8 + src/Screeners/InvestorPreferences/index.ts | 47 +++ src/index.ts | 2 + 12 files changed, 1189 insertions(+), 2 deletions(-) create mode 100644 demos/dashboards-investor-preferences/demo.css create mode 100644 demos/dashboards-investor-preferences/demo.html create mode 100644 demos/dashboards-investor-preferences/demo.js create mode 100644 src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts create mode 100644 src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts create mode 100644 src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts create mode 100644 src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts create mode 100644 src/Screeners/InvestorPreferences/README.md create mode 100644 src/Screeners/InvestorPreferences/index.ts diff --git a/demos/dashboards-investor-preferences/demo.css b/demos/dashboards-investor-preferences/demo.css new file mode 100644 index 0000000..82a01cd --- /dev/null +++ b/demos/dashboards-investor-preferences/demo.css @@ -0,0 +1,71 @@ +@import url("https://code.highcharts.com/dashboards/css/datagrid.css"); +@import url("https://code.highcharts.com/css/highcharts.css"); +@import url("https://code.highcharts.com/dashboards/css/dashboards.css"); + +body { + font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif; +} + +.row { + display: flex; + flex-wrap: wrap; +} + +.cell { + flex: 1; + min-width: 20px; +} + +.cell>.highcharts-dashboards-component { + position: relative; + margin: 10px; + background-clip: border-box; +} + +.highcharts-dashboards-component-title { + padding: 10px; + margin: 0; + background-color: var(--highcharts-neutral-color-5); + color: var(--highcharts-neutral-color-100); + border: solid 1px var(--highcharts-neutral-color-20); + border-bottom: none; +} + +@media screen and (max-width: 1000px) { + .row { + flex-direction: column; + } +} + +.filters-row { + flex-direction: row; + justify-content: space-evenly; +} + +#dashboard-col-1 { + height: 500px; +} + +.filters-row>.input-wrapper { + display: block; + margin-bottom: 0.5rem; +} +.filters-row>.input-wrapper>input, .filters-row>.input-wrapper>select { + margin-top: 0.5rem; +} + +.filters-row>button, .filters-row>select { + background: #f2f2f2; + border: none; + border-radius: 4px; + cursor: pointer; + display: inline-block; + font-size: 0.8rem; + padding: 0.5rem 1.5rem; + margin: 0.5rem -5px 0.5rem 10px; + transition: background 250ms; +} + +.filters-row>button:hover { + background: #e6e6e6; +} diff --git a/demos/dashboards-investor-preferences/demo.html b/demos/dashboards-investor-preferences/demo.html new file mode 100644 index 0000000..b94a448 --- /dev/null +++ b/demos/dashboards-investor-preferences/demo.html @@ -0,0 +1,70 @@ + + + + + + + + + + + Highcharts Dashboards + Morningstar Portfolio Investment Details + + +

Highcharts Dashboards + Morningstar Portfolio Investment Details

+

+ Add your Postman environment file from Morningstar to start the demo: + +

+ +
+
+
+ +
+
+ + 0
+ +
+
+
+ +
+
+ SFDR Classification
+ + + + + + +
+ +
+
+ Current filter: + +
+
+
+
+
+ + - + - + - + +
+ + + diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js new file mode 100644 index 0000000..33b85c2 --- /dev/null +++ b/demos/dashboards-investor-preferences/demo.js @@ -0,0 +1,352 @@ +const loadingLabel = document.getElementById('loading-label'); + +function displayInvestorPreferences (postmanJSON) { + const secIds = [ + "SecId", + "LegalName", + "DomicileId", + "EET_PAI_GHGEmissions3Considered", + "EET_PAI_GHGEmissions1And2Considered", + "EET_PAI_EnergyConsumptionNaceAConsidered", + "EET_PAI_EnergyConsumptionNaceBConsidered", + "EET_PAI_FossilFuelConsidered", + "EET_PAI_SocialViolationsPercentageConsidered" + ]; + + const columns = secIds.map(id => ({ + id: `InvestorPreferences_${id}`, + header: { + format: id + } + })); + + const board = Dashboards.board('container', { + dataPool: { + connectors: [ + { + id: 'investor-preferences', + type: 'MorningstarInvestorPreferences', + options: { + page: 1, + pageSize: 20, + languageId: 'en-GB', + currencyId: 'USD', + securityDataPoints: secIds, + universeIds: ['FOGBR$$ALL'], + calculatedDataPoints: [ + { + "name": "UserPref0", + "type": "bool", + "condition": { + "and": [ + { + "fields": [ + { + "name": "StarRatingM255", + "op": "gt", + "value": 3 + }, + { + "name": "StarRatingM255", + "op": "gt", + "value": 3 + } + ] + } + ] + } + }, + { + "name": "UserPref1", + "type": "bool", + "condition": { + "and": [ + { + "fields": [ + { + "name": "EET_EUSFDRType", + "op": "eq", + "value": 8 + }, + { + "name": "EET_PAI_GHGEmissions1Considered", + "op": "eq", + "value": "Y" + } + ], + "or": [ + { + "fields": [ + { + "name": "EET_EUSFDRType", + "op": "gte", + "value": 9 + }, + { + "name": "EET_PAI_GHGEmissions1And2Considered", + "op": "eq", + "value": "Y" + } + ], + "and": [ + { + "fields": [ + { + "name": "EET_PAI_EnergyConsumptionNaceAConsidered", + "op": "eq", + "value": "Y" + }, + { + "name": "EET_PAI_EnergyConsumptionNaceBConsidered", + "op": "eq", + "value": "Y" + } + ] + } + ] + } + ] + } + ] + } + }, + { + "name": "UserPref2", + "type": "bool", + "condition": { + "and": [ + { + "or": [ + { + "fields": [ + { + "name": "EET_EUSFDRType", + "op": "eq", + "value": 9 + }, + { + "name": "EET_EUSFDRType", + "op": "eq", + "value": 10 + } + ] + } + ] + }, + { + "or": [ + { + "fields": [ + { + "name": "EET_PAI_GHGEmissions3Considered", + "op": "eq", + "value": "Y" + }, + { + "name": "EET_PAI_GHGEmissions1Considered", + "op": "eq", + "value": "Y" + } + ] + } + ] + } + ] + } + } + ], + filters: [{dataPointId: 'UserPref0', comparatorCode: 'EQ', value: 'True'}], + postman: { + environmentJSON: postmanJSON + } + } + } + ] + }, + components: [ + { + renderTo: 'dashboard-col-1', + connector: { + id: 'investor-preferences' + }, + type: 'DataGrid', + + dataGridOptions: { + editable: false, + columns + }, + title: 'Investor Preferences' + } + ] + }); + + board.dataPool.getConnector('investor-preferences').then(connector => { + loadingLabel.style.display = 'none'; + document.getElementById('total').innerHTML = + `total - ${connector.metadata.total}`; + document.getElementById('page').innerHTML = + `page - ${connector.metadata.page}`; + document.getElementById('total-pages').innerHTML = + `out of ${Math.ceil(connector.metadata.total / connector.metadata.pageSize)}`; + }); + + + /** + * Set prev/next page + */ + function changePage (next = true) { + loadingLabel.style.display = 'block'; + board.dataPool.getConnector('investor-preferences').then(connector => { + if ( + connector.options.page < 1 || + connector.options.page > connector.metadata.total + ) { + loadingLabel.style.display = 'none'; + return; + } + connector.options.page += next ? 1 : -1; + const options = { + page: connector.options.page + }; + + connector.load(options).then(() => { + loadingLabel.style.display = 'none'; + document.getElementById('total').innerHTML = + `total - ${connector.metadata.total}`; + document.getElementById('page').innerHTML = + `page - ${connector.metadata.page}`; + document.getElementById('total-pages').innerHTML = + `out of ${Math.ceil(connector.metadata.total / connector.metadata.pageSize)}`; + }); + }); + } + /** + * Add filter to a connector + * + * @param {InvestorPreferencesFilter[]} filters + */ + function setFilter (filters) { + loadingLabel.style.display = 'block'; + board.dataPool.getConnector('investor-preferences').then(connector => { + const options = { + filters + }; + connector.load(options).then(() => { + loadingLabel.style.display = 'none'; + document.getElementById('total').innerHTML = + `total - ${connector.metadata.total}`; + document.getElementById('page').innerHTML = + `page - ${connector.metadata.page}`; + document.getElementById('total-pages').innerHTML = + `out of ${Math.ceil(connector.metadata.total / connector.metadata.pageSize)}`; + }); + }); + } + + document.getElementById('filter-1').addEventListener('click', () => { + const currentFilter = document.getElementById('current-filter'), + euTaxAlignment = document.getElementById('tax-alignment'), + sfdrTypes = document.querySelectorAll('.sfdr:checked'), + filters = []; + + currentFilter.innerHTML = ''; + + // Create a filter that will check if the investment is within EU tax + // alignment or SFDR classification + if (euTaxAlignment.value) { + const minPortion = document.getElementById('min-portion').value, + isOptional = document.getElementById('optional'); + + currentFilter.innerHTML = 'EU Tax alignment: ' + + euTaxAlignment.selectedOptions[0].innerHTML + + ', Minimum portion: ' + minPortion + '%, ' + + isOptional.selectedOptions[0].innerHTML + '.'; + + filters.push( + { + dataPointId: euTaxAlignment.value, + comparatorCode: 'IN', + value: isOptional.value + }, + { + dataPointId: 'EET_PlannedInvtsSustInvEUTaxonomy', + comparatorCode: 'GT', + value: minPortion + } + ); + } + if(sfdrTypes.length) { + const values = [], + types = []; + + sfdrTypes.forEach(type => { + values.push(type.value); + types.push( + document.querySelector(`label[for="${type.id}"`).innerHTML + ); + }); + currentFilter.innerHTML += + 'SFDR Classification: ' + types.join(', '); + + filters.push({ + dataPointId: 'EET_EUSFDRType', + comparatorCode: sfdrTypes.length > 1 ? 'IN' : 'EQ', + value: values.join(':') + }); + } + + setFilter(filters); + }); + + document.getElementById('prev').addEventListener('click', () => { + changePage(false); + }); + + document.getElementById('next').addEventListener('click', () => { + changePage(); + }); + +} + +async function handleSelectEnvironment (evt) { + const target = evt.target; + const postmanJSON = await getPostmanJSON(target); + + if (!postmanJSON) { + loadingLabel.textContent = + 'The provided file is not a Postman Environment Configuration.'; + loadingLabel.style.display = 'block'; + + return; + } + + target.parentNode.style.display = 'none'; + + loadingLabel.style.display = 'block'; + loadingLabel.textContent = 'Loading data…'; + + displayInvestorPreferences(postmanJSON); +} + +document + .getElementById('postman-json') + .addEventListener('change', handleSelectEnvironment); + +async function getPostmanJSON (htmlInputFile) { + let file; + let fileJSON; + + for (file of htmlInputFile.files) { + try { + fileJSON = JSON.parse(await file.text()); + if (HighchartsConnectors.Morningstar.isPostmanEnvironmentJSON(fileJSON)) { + break; + } + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } + } + + return fileJSON; +} diff --git a/demos/index.html b/demos/index.html index 836ac72..ea8c8aa 100644 --- a/demos/index.html +++ b/demos/index.html @@ -13,6 +13,7 @@

Morningstar Connectors Demos

  • Highcharts Stock + Morningstar Security Details
  • Highcharts Dashboards + Morningstar Risk Score
  • Highcharts Dashboards + Morningstar Investment Screener
  • +
  • Highcharts Dashboards + Morningstar Investor Preferences
  • Using fetch without connectors
  • diff --git a/src/Screeners/InvestmentScreener/InvestmentScreenerOptions.ts b/src/Screeners/InvestmentScreener/InvestmentScreenerOptions.ts index f4963f5..9b28aa0 100644 --- a/src/Screeners/InvestmentScreener/InvestmentScreenerOptions.ts +++ b/src/Screeners/InvestmentScreener/InvestmentScreenerOptions.ts @@ -101,7 +101,7 @@ export interface InvestmentScreenerConverterOptions // Nothing to add yet } -export type ComparatorCode = +export type ComparatorCode = ( | 'IN' | 'NIN' | 'EQ' @@ -116,7 +116,8 @@ export type ComparatorCode = | 'LTEN' | 'CONTAINS' | 'BTW' - | 'STARTSWITH'; + | 'STARTSWITH' +); export type SecurityDataPointType = string; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts new file mode 100644 index 0000000..a665f07 --- /dev/null +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts @@ -0,0 +1,220 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Jedrzej Ruta + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import InvestorPreferencesOptions from './InvestorPreferencesOptions'; +import InvestorPreferencesConverter from './InvestorPreferencesConverter'; +import type { + InvestorPreferencesFilter, + InvestorPreferencesMetadata +} from './InvestorPreferencesOptions'; +import External from '../../Shared/External'; +import MorningstarAPI from '../../Shared/MorningstarAPI'; +import MorningstarConnector from '../../Shared/MorningstarConnector'; +import MorningstarURL from '../../Shared/MorningstarURL'; + +/** + * + * Constants + * + */ + +const UTF_PIPE = '|'; +const UTF_COLON = ':'; + + +/* * + * + * Class + * + * */ + + +export class InvestorPreferencesConnector extends MorningstarConnector { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + options: InvestorPreferencesOptions = { universeIds: [] } + ) { + super(options); + + this.converter = new InvestorPreferencesConverter(options.converter); + this.metadata = this.converter.metadata; + this.options = options; + + } + + + /* * + * + * Properties + * + * */ + + + public override readonly converter: InvestorPreferencesConverter; + + public override readonly options: InvestorPreferencesOptions; + + public override readonly metadata: InvestorPreferencesMetadata; + + + /* * + * + * Functions + * + * */ + + + public override async load ( + options?: InvestorPreferencesOptions + ): Promise { + + await super.load(); + + const userOptions = { ...this.options, ...options }; + const api = this.api = this.api || new MorningstarAPI(userOptions.api); + const url = new MorningstarURL('ecint/v1/screener', api.baseURL); + + const body: any = { + universeIds: userOptions.universeIds.join(UTF_PIPE), + outputType: 'json' + }; + + if (userOptions.page) { + body.page = `${userOptions.page}` + } + + if (userOptions.pageSize) { + body.pageSize = `${userOptions.pageSize}`; + } + + if (userOptions.sortOrder) { + body.sortOrder = `${userOptions.sortOrder}`; + } + + if (userOptions.languageId) { + body.languageId = `${userOptions.languageId}`; + } + + if (userOptions.currencyId) { + body.currencyId = `${userOptions.currencyId}`; + } + + if (userOptions.securityDataPoints) { + body.securityDataPoints = userOptions.securityDataPoints; + } + + if (userOptions.calculatedDataPoints) { + this.metadata.calculatedDataPointNames = userOptions.calculatedDataPoints.map(value => value.name); + body.calculatedDataPoints = userOptions.calculatedDataPoints.map(value => ({...value, type: 'bool'})); + } + + if (userOptions.filters) { + body.filters = userOptions.filters.reduce( + (prev, curr) => + prev === '' ? + this.getFilter(curr) : + prev + UTF_PIPE + this.getFilter(curr), + '' + ); + } + + if (userOptions.filterDataPoints) { + body.filterDataPoints = userOptions.filterDataPoints.join(UTF_PIPE); + } + + if (userOptions.term) { + body.term = userOptions.term; + } + + if (userOptions.countryId) { + body.countryId = userOptions.countryId; + } + + if(userOptions.applyTrackRecordExtension) { + body.applyTrackRecordExtension = userOptions.applyTrackRecordExtension; + } + + if(userOptions.ignoreRestructure) { + body.ignoreRestructure = userOptions.ignoreRestructure; + } + + const response = await api.fetch(url, { + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json' + }, + method: 'POST' + }); + + const json = await response.json() as unknown; + + this.converter.parse({ json }); + + this.table.deleteColumns(); + this.table.setColumns(this.converter.getTable().getColumns()); + + console.log(this.table); + + return this; + } + + private getFilter (filter: InvestorPreferencesFilter): string { + return `${filter.dataPointId}${UTF_COLON}${filter.comparatorCode}${UTF_COLON}${filter.value}`; + } +} + + +/* * + * + * Registry + * + * */ + + +declare module '@highcharts/dashboards/es-modules/Data/Connectors/DataConnectorType' { + interface DataConnectorTypes { + MorningstarInvestorPreferences: typeof InvestorPreferencesConnector + } +} + + +External.DataConnector.registerType('MorningstarInvestorPreferences', InvestorPreferencesConnector); + + +/* * + * + * Default Export + * + * */ + + +export default InvestorPreferencesConnector; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts new file mode 100644 index 0000000..9be8912 --- /dev/null +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts @@ -0,0 +1,99 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Pawel Lysy + * + * */ + +'use strict'; + +/* * + * + * Imports + * + * */ + +import type { InvestorPreferencesConverterOptions, InvestorPreferencesMetadata } from './InvestorPreferencesOptions'; +import type { DataTable } from '@highcharts/dashboards'; + +import MorningstarConverter from '../../Shared/MorningstarConverter'; +import InvestorPreferencesJSON from './InvestorPreferencesJSON'; + +/* * + * + * Class + * + * */ + +export class InvestorPreferencesConverter extends MorningstarConverter { + public constructor (options?: InvestorPreferencesConverterOptions) { + super(options); + + this.metadata = { + columns: {} + }; + } + /* * + * + * Properties + * + * */ + + public readonly metadata: InvestorPreferencesMetadata; + + /* * + * + * Functions + * + * */ + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public parse (options: InvestorPreferencesConverterOptions): void { + const metadata = this.metadata; + const table = this.table; + const userOptions = { + ...this.options, + ...options + }; + const json = userOptions.json; + + if (!InvestorPreferencesJSON.isInvestorPreferencesResponse(json)) { + throw new Error('Invalid data'); + } + table.deleteColumns(); + const rows = json.rows; + if (rows.length > 0) { + const row = rows[0]; + for (const element of Object.keys(row)) { + table.setColumn(`InvestorPreferences_${element}`); + } + + for (let i = 0; i < rows.length; i++) { + for (const [key, val] of Object.entries(rows[i])) { + table.setCell( + `InvestorPreferences_${key}`, + i, + val as DataTable.CellType + ); + } + } + metadata.page = json.page; + metadata.total = json.total; + metadata.pageSize = json.pageSize; + } + } +} + +/* * + * + * Default Export + * + * */ + +export default InvestorPreferencesConverter; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts new file mode 100644 index 0000000..f04fd5f --- /dev/null +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts @@ -0,0 +1,72 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Jedrzej Ruta + * + * */ + + +'use strict'; + + +/* * + * + * Namespace + * + * */ + + +namespace InvestorPreferencesJSON { + + export interface Response { + InvestorPreferences: InvestorPreferencesResponse; + } + export interface InvestorPreferencesResponse { + total: number; + page: number; + pageSize: number; + rows: InvestorPreferenceRow[]; + } + + export interface InvestorPreferenceRow { + [key: string]: any; + }; + + + export function isInvestorPreferencesResponse ( + json?: unknown + ): json is InvestorPreferencesResponse { + return ( + !!json && + typeof json === 'object' && + typeof (json as InvestorPreferencesResponse).rows === 'object' && + (json as InvestorPreferencesResponse).rows instanceof Array && + ((json as InvestorPreferencesResponse).rows.length === 0 || + isInvestorPreferenceRow( + (json as InvestorPreferencesResponse).rows[0] + )) + ); + } + + export function isInvestorPreferenceRow ( + json?: unknown + ): json is InvestorPreferenceRow { + return !!json && typeof json === 'object'; + } + +} + +/* * + * + * Default Export + * + * */ + + +export default InvestorPreferencesJSON; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts new file mode 100644 index 0000000..11e4911 --- /dev/null +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts @@ -0,0 +1,244 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Jedrzej Ruta + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + +import type { + MorningstarConverterOptions, + MorningstarMetadata, + MorningstarOptions +} from '../../Shared/MorningstarOptions'; + + +/* * + * + * API Options + * + * */ + + +export interface InvestorPreferencesConverterOptions extends MorningstarConverterOptions { + + // Nothing to add yet. + +} + + +export interface InvestorPreferencesOptions extends MorningstarOptions { + /** + * + * When true, returns are calculated using extended performance/Track Record + * Extension." + * + */ + applyTrackRecordExtension?: boolean; + /** + * + * A list of calculated data points that is used to determinate whether + * a security matches investor preferences. + * + */ + calculatedDataPoints?: Array; + + //converter?: InvestorPreferencesConverterOptions; + /** + * + * ISO alpha-3 country code. + * + */ + countryId?: string; + /** + * + * ISO alpha-3 currency code. + * The currency to be used for calculated data points such as performance + * returns. To return list of securities with a specific base currency, + * you must pass the following filter in the request: + * CURRENCY:EQ:{currency code}. For example: Currency:EQ:EUR. See the + * Filters section for more information. + * + */ + currencyId?: string; + /** + * + * A list of filter data points. Custom data points can be configured. See + * Filters for information about how to get a list of filter data points. + * + */ + filterDataPoints?: string[]; + /** + * + * A list of criteria a security must meet to be included in the results. + * + */ + filters?: InvestorPreferencesFilter[]; + /** + * + * When true, returns will not be calculated using the restructure date. + * + */ + ignoreRestructure?: boolean; + /** + * + * ISO culture codes. + * If not provided, defaults to the language defined in the settings. + * + */ + languageId?: string; + /** + * + * The number of output pages. + * + */ + page?: number; + /** + * + * The number of rows per page. + * + */ + pageSize?: number; + /** + * + * A list of security data points to return in the response. + * + */ + securityDataPoints?: Array; + /** + * + * Data points to sort on and the order in which results are sorted + * + */ + sortOrder?: string; + /** + * + * Search string to use to search for securities by name, identifiers, or + * symbols. Can be used with filter to run a combined search. + * + */ + term?: string; + /** + * + * A list of investment universe identifiers to query. Values may end with + * `:1` to signify that a custom universe is not only client funds. + * + */ + universeIds: Array; +}; + +export interface CalculatedDataPoint { + /** + * + * Name of the calculated data point. This data point is returned + * in the response. + * + */ + name: string; + /** + * + * Object containing the conditions that a security must meet to align + * with an investor’s preferences. + * + */ + condition: CalculatedDataPointCondition; +}; + +export interface CalculatedDataPointCondition { + /** + * + * Array of fields objects that define the criteria to use when searching + * for securities. A security must meet all the criteria defined in the + * `and` object to be considered aligned with an investor’s preferences. + * + */ + and: Array; + /** + * + * Array of fields objects that define the criteria to use when searching + * for securities. A security must meet at least one of the criteria + * defined in the `or` object to be considered aligned with + * an investor’s preferences. + * + */ + or: Array; +}; + +export interface CalculatedDataPointConditionField { + /** Data point that condition is applied to. */ + name: string; + /** Conditional operator to use. */ + op: ConditionalOperator; + /** Value to base condition calculation on. */ + value: string|number; +}; + +export type ConditionalOperator = ( + | 'eq' + | 'ne' + | 'gt' + | 'gte' + | 'lt' + | 'lte' + | 'in' + | 'nin' +); + +export interface InvestorPreferencesFilter { + /** The data point to filter on. */ + dataPointId: string; + /** The comparator to use. */ + comparatorCode: ComparatorCode; + /** The value to compare against. */ + value: any; +} + +export type ComparatorCode = ( + | 'IN' + | 'NIN' + | 'EQ' + | 'NE' + | 'GT' + | 'GTN' + | 'GTE' + | 'GTEN' + | 'LT' + | 'LTN' + | 'LTE' + | 'LTEN' + | 'CONTAINS' + | 'BTW' + | 'STARTSWITH' +); + +export interface InvestorPreferencesMetadata extends MorningstarMetadata { + page?: number; + total?: number; + pageSize?: number; + calculatedDataPointNames?: string[]; +}; + + +/* * + * + * Default Export + * + * */ + + +export default InvestorPreferencesOptions; diff --git a/src/Screeners/InvestorPreferences/README.md b/src/Screeners/InvestorPreferences/README.md new file mode 100644 index 0000000..3002817 --- /dev/null +++ b/src/Screeners/InvestorPreferences/README.md @@ -0,0 +1,8 @@ +Morningstar Investor Preferences Connector +================================ + +This Highcharts Dashboards connector provides data from Morningstar’s [Investor Preferences API]. The JSON from the API is converted into a DataTable. + + + +[Investor Preferences API]: https://developer.morningstar.com/direct-web-services/documentation/api-reference/screener/Investor-preferences diff --git a/src/Screeners/InvestorPreferences/index.ts b/src/Screeners/InvestorPreferences/index.ts new file mode 100644 index 0000000..1ff8832 --- /dev/null +++ b/src/Screeners/InvestorPreferences/index.ts @@ -0,0 +1,47 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Jedrzej Ruta + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import InvestorPreferencesConnector from './InvestorPreferencesConnector'; + + +/* * + * + * Exports + * + * */ + + +export * from './InvestorPreferencesConnector'; +export * from './InvestorPreferencesConverter'; +export * from './InvestorPreferencesOptions'; + + +/* * + * + * Default Export + * + * */ + + +export default InvestorPreferencesConnector; diff --git a/src/index.ts b/src/index.ts index 83c0fc6..e682941 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,7 @@ import GoalAnalysis from './GoalAnalysis/index'; import SecurityDetails from './SecurityDetails/index'; import RNANews from './RNANews/index'; import InvestmentScreener from './Screeners/InvestmentScreener/index'; +import InvestorPreferences from './Screeners/InvestorPreferences/index'; import RiskScore from './RiskScore/index'; import * as Shared from './Shared/index'; import TimeSeries from './TimeSeries/index'; @@ -62,5 +63,6 @@ export default { TimeSeries, XRay, InvestmentScreener, + InvestorPreferences, version }; From 45c897846906242863452a81a249ea3224d40c23 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Fri, 10 Jan 2025 13:03:05 +0100 Subject: [PATCH 02/19] Added tests, improves linting and add typing in connector body. --- demos/dashboards-investor-preferences/demo.js | 272 +++++++++--------- .../InvestorPreferencesConnector.ts | 98 +++++-- .../InvestorPreferencesConverter.ts | 9 +- .../InvestorPreferencesJSON.ts | 8 +- .../InvestorPreferencesOptions.ts | 30 +- src/Screeners/InvestorPreferences/index.ts | 2 +- src/api.d.ts | 4 +- src/index.ts | 1 + tests/Screeners/InvestorPreferences.test.ts | 68 +++++ 9 files changed, 298 insertions(+), 194 deletions(-) create mode 100644 tests/Screeners/InvestorPreferences.test.ts diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js index 33b85c2..fe3b2ee 100644 --- a/demos/dashboards-investor-preferences/demo.js +++ b/demos/dashboards-investor-preferences/demo.js @@ -2,17 +2,140 @@ const loadingLabel = document.getElementById('loading-label'); function displayInvestorPreferences (postmanJSON) { const secIds = [ - "SecId", - "LegalName", - "DomicileId", - "EET_PAI_GHGEmissions3Considered", - "EET_PAI_GHGEmissions1And2Considered", - "EET_PAI_EnergyConsumptionNaceAConsidered", - "EET_PAI_EnergyConsumptionNaceBConsidered", - "EET_PAI_FossilFuelConsidered", - "EET_PAI_SocialViolationsPercentageConsidered" + 'SecId', + 'LegalName', + 'DomicileId', + 'EET_PAI_GHGEmissions3Considered', + 'EET_PAI_GHGEmissions1And2Considered', + 'EET_PAI_EnergyConsumptionNaceAConsidered', + 'EET_PAI_EnergyConsumptionNaceBConsidered', + 'EET_PAI_FossilFuelConsidered', + 'EET_PAI_SocialViolationsPercentageConsidered' ]; + const calculatedDataPoints = [ + { + 'name': 'UserPref0', + 'type': 'bool', + 'condition': { + 'and': [ + { + 'fields': [ + { + 'name': 'StarRatingM255', + 'op': 'gt', + 'value': 3 + }, + { + 'name': 'StarRatingM255', + 'op': 'gt', + 'value': 3 + } + ] + } + ] + } + }, + { + 'name': 'UserPref1', + 'type': 'bool', + 'condition': { + 'and': [ + { + 'fields': [ + { + 'name': 'EET_EUSFDRType', + 'op': 'eq', + 'value': 8 + }, + { + 'name': 'EET_PAI_GHGEmissions1Considered', + 'op': 'eq', + 'value': 'Y' + } + ], + 'or': [ + { + 'fields': [ + { + 'name': 'EET_EUSFDRType', + 'op': 'gte', + 'value': 9 + }, + { + 'name': 'EET_PAI_GHGEmissions1And2Considered', + 'op': 'eq', + 'value': 'Y' + } + ], + 'and': [ + { + 'fields': [ + { + 'name': 'EET_PAI_EnergyConsumptionNaceAConsidered', + 'op': 'eq', + 'value': 'Y' + }, + { + 'name': 'EET_PAI_EnergyConsumptionNaceBConsidered', + 'op': 'eq', + 'value': 'Y' + } + ] + } + ] + } + ] + } + ] + } + }, + { + 'name': 'UserPref2', + 'type': 'bool', + 'condition': { + 'and': [ + { + 'or': [ + { + 'fields': [ + { + 'name': 'EET_EUSFDRType', + 'op': 'eq', + 'value': 9 + }, + { + 'name': 'EET_EUSFDRType', + 'op': 'eq', + 'value': 10 + } + ] + } + ] + }, + { + 'or': [ + { + 'fields': [ + { + 'name': 'EET_PAI_GHGEmissions3Considered', + 'op': 'eq', + 'value': 'Y' + }, + { + 'name': 'EET_PAI_GHGEmissions1Considered', + 'op': 'eq', + 'value': 'Y' + } + ] + } + ] + } + ] + } + } + ] + const columns = secIds.map(id => ({ id: `InvestorPreferences_${id}`, header: { @@ -33,129 +156,12 @@ function displayInvestorPreferences (postmanJSON) { currencyId: 'USD', securityDataPoints: secIds, universeIds: ['FOGBR$$ALL'], - calculatedDataPoints: [ - { - "name": "UserPref0", - "type": "bool", - "condition": { - "and": [ - { - "fields": [ - { - "name": "StarRatingM255", - "op": "gt", - "value": 3 - }, - { - "name": "StarRatingM255", - "op": "gt", - "value": 3 - } - ] - } - ] - } - }, - { - "name": "UserPref1", - "type": "bool", - "condition": { - "and": [ - { - "fields": [ - { - "name": "EET_EUSFDRType", - "op": "eq", - "value": 8 - }, - { - "name": "EET_PAI_GHGEmissions1Considered", - "op": "eq", - "value": "Y" - } - ], - "or": [ - { - "fields": [ - { - "name": "EET_EUSFDRType", - "op": "gte", - "value": 9 - }, - { - "name": "EET_PAI_GHGEmissions1And2Considered", - "op": "eq", - "value": "Y" - } - ], - "and": [ - { - "fields": [ - { - "name": "EET_PAI_EnergyConsumptionNaceAConsidered", - "op": "eq", - "value": "Y" - }, - { - "name": "EET_PAI_EnergyConsumptionNaceBConsidered", - "op": "eq", - "value": "Y" - } - ] - } - ] - } - ] - } - ] - } - }, - { - "name": "UserPref2", - "type": "bool", - "condition": { - "and": [ - { - "or": [ - { - "fields": [ - { - "name": "EET_EUSFDRType", - "op": "eq", - "value": 9 - }, - { - "name": "EET_EUSFDRType", - "op": "eq", - "value": 10 - } - ] - } - ] - }, - { - "or": [ - { - "fields": [ - { - "name": "EET_PAI_GHGEmissions3Considered", - "op": "eq", - "value": "Y" - }, - { - "name": "EET_PAI_GHGEmissions1Considered", - "op": "eq", - "value": "Y" - } - ] - } - ] - } - ] - } - } - ], - filters: [{dataPointId: 'UserPref0', comparatorCode: 'EQ', value: 'True'}], + calculatedDataPoints, + filters: [{ + dataPointId: 'UserPref0', + comparatorCode: 'EQ', + value: 'True' + }], postman: { environmentJSON: postmanJSON } @@ -282,7 +288,7 @@ function displayInvestorPreferences (postmanJSON) { sfdrTypes.forEach(type => { values.push(type.value); types.push( - document.querySelector(`label[for="${type.id}"`).innerHTML + document.querySelector(`label[for='${type.id}'`).innerHTML ); }); currentFilter.innerHTML += diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts index a665f07..29f9f1f 100644 --- a/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesConnector.ts @@ -1,6 +1,6 @@ /* * * - * (c) 2009-2024 Highsoft AS + * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * @@ -11,17 +11,14 @@ * * */ - 'use strict'; - /* * * * Imports * * */ - import InvestorPreferencesOptions from './InvestorPreferencesOptions'; import InvestorPreferencesConverter from './InvestorPreferencesConverter'; import type { @@ -42,6 +39,56 @@ import MorningstarURL from '../../Shared/MorningstarURL'; const UTF_PIPE = '|'; const UTF_COLON = ':'; +/** + * + * Declarations + * + */ + +type NonStringOptions = ( + 'universeIds' | 'page' | 'pageSize' | 'filters' | 'filterDataPoints' +); + +interface InvestorPreferencesBody extends Omit { + /** + * + * A list of filter data points. Custom data points can be configured. See + * Filters for information about how to get a list of filter data points. + * + */ + filterDataPoints?: string; + /** + * + * A list of criteria a security must meet to be included in the results. + * + */ + filters?: string; + /** + * + * Output type of the connector response + * + */ + outputType: string; + /** + * + * The number of output pages. + * + */ + page?: string; + /** + * + * The number of rows per page. + * + */ + pageSize?: string; + /** + * + * A list of investment universe identifiers to query. Values may end with + * `:1` to signify that a custom universe is not only client funds. + * + */ + universeIds: string; +} /* * * @@ -49,17 +96,13 @@ const UTF_COLON = ':'; * * */ - export class InvestorPreferencesConnector extends MorningstarConnector { - - /* * * * Constructor * * */ - public constructor ( options: InvestorPreferencesOptions = { universeIds: [] } ) { @@ -68,48 +111,42 @@ export class InvestorPreferencesConnector extends MorningstarConnector { this.converter = new InvestorPreferencesConverter(options.converter); this.metadata = this.converter.metadata; this.options = options; - } - /* * * * Properties * * */ - public override readonly converter: InvestorPreferencesConverter; public override readonly options: InvestorPreferencesOptions; public override readonly metadata: InvestorPreferencesMetadata; - /* * * * Functions * * */ - public override async load ( options?: InvestorPreferencesOptions ): Promise { - await super.load(); const userOptions = { ...this.options, ...options }; - const api = this.api = this.api || new MorningstarAPI(userOptions.api); + const api = (this.api = this.api || new MorningstarAPI(userOptions.api)); const url = new MorningstarURL('ecint/v1/screener', api.baseURL); - const body: any = { + const body: InvestorPreferencesBody = { universeIds: userOptions.universeIds.join(UTF_PIPE), outputType: 'json' }; if (userOptions.page) { - body.page = `${userOptions.page}` + body.page = `${userOptions.page}`; } if (userOptions.pageSize) { @@ -133,12 +170,15 @@ export class InvestorPreferencesConnector extends MorningstarConnector { } if (userOptions.calculatedDataPoints) { - this.metadata.calculatedDataPointNames = userOptions.calculatedDataPoints.map(value => value.name); - body.calculatedDataPoints = userOptions.calculatedDataPoints.map(value => ({...value, type: 'bool'})); + this.metadata.calculatedDataPointNames = + userOptions.calculatedDataPoints.map((value) => value.name); + body.calculatedDataPoints = userOptions.calculatedDataPoints.map( + (value) => ({ ...value, type: 'bool' }) + ); } if (userOptions.filters) { - body.filters = userOptions.filters.reduce( + body.filters = userOptions.filters.reduce( (prev, curr) => prev === '' ? this.getFilter(curr) : @@ -159,11 +199,11 @@ export class InvestorPreferencesConnector extends MorningstarConnector { body.countryId = userOptions.countryId; } - if(userOptions.applyTrackRecordExtension) { + if (userOptions.applyTrackRecordExtension) { body.applyTrackRecordExtension = userOptions.applyTrackRecordExtension; } - if(userOptions.ignoreRestructure) { + if (userOptions.ignoreRestructure) { body.ignoreRestructure = userOptions.ignoreRestructure; } @@ -182,8 +222,6 @@ export class InvestorPreferencesConnector extends MorningstarConnector { this.table.deleteColumns(); this.table.setColumns(this.converter.getTable().getColumns()); - console.log(this.table); - return this; } @@ -192,23 +230,22 @@ export class InvestorPreferencesConnector extends MorningstarConnector { } } - /* * * * Registry * * */ - declare module '@highcharts/dashboards/es-modules/Data/Connectors/DataConnectorType' { interface DataConnectorTypes { - MorningstarInvestorPreferences: typeof InvestorPreferencesConnector + MorningstarInvestorPreferences: typeof InvestorPreferencesConnector; } } - -External.DataConnector.registerType('MorningstarInvestorPreferences', InvestorPreferencesConnector); - +External.DataConnector.registerType( + 'MorningstarInvestorPreferences', + InvestorPreferencesConnector +); /* * * @@ -216,5 +253,4 @@ External.DataConnector.registerType('MorningstarInvestorPreferences', InvestorPr * * */ - export default InvestorPreferencesConnector; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts index 9be8912..855bcfb 100644 --- a/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesConverter.ts @@ -1,13 +1,13 @@ /* * * - * (c) 2009-2024 Highsoft AS + * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: - * - Pawel Lysy + * - Jedrzej Ruta * * */ @@ -19,7 +19,10 @@ * * */ -import type { InvestorPreferencesConverterOptions, InvestorPreferencesMetadata } from './InvestorPreferencesOptions'; +import type { + InvestorPreferencesConverterOptions, + InvestorPreferencesMetadata +} from './InvestorPreferencesOptions'; import type { DataTable } from '@highcharts/dashboards'; import MorningstarConverter from '../../Shared/MorningstarConverter'; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts index f04fd5f..ad311d8 100644 --- a/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesJSON.ts @@ -1,6 +1,6 @@ /* * * - * (c) 2009-2024 Highsoft AS + * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * @@ -11,17 +11,14 @@ * * */ - 'use strict'; - /* * * * Namespace * * */ - namespace InvestorPreferencesJSON { export interface Response { @@ -36,7 +33,7 @@ namespace InvestorPreferencesJSON { export interface InvestorPreferenceRow { [key: string]: any; - }; + } export function isInvestorPreferencesResponse ( @@ -68,5 +65,4 @@ namespace InvestorPreferencesJSON { * * */ - export default InvestorPreferencesJSON; diff --git a/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts b/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts index 11e4911..ee2b50e 100644 --- a/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts +++ b/src/Screeners/InvestorPreferences/InvestorPreferencesOptions.ts @@ -1,6 +1,6 @@ /* * * - * (c) 2009-2024 Highsoft AS + * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * @@ -11,10 +11,8 @@ * * */ - 'use strict'; - /* * * * Imports @@ -27,26 +25,21 @@ import type { MorningstarOptions } from '../../Shared/MorningstarOptions'; - /* * * * API Options * * */ - export interface InvestorPreferencesConverterOptions extends MorningstarConverterOptions { - // Nothing to add yet. - } - export interface InvestorPreferencesOptions extends MorningstarOptions { /** * * When true, returns are calculated using extended performance/Track Record - * Extension." + * Extension. * */ applyTrackRecordExtension?: boolean; @@ -58,7 +51,7 @@ export interface InvestorPreferencesOptions extends MorningstarOptions { */ calculatedDataPoints?: Array; - //converter?: InvestorPreferencesConverterOptions; + converter?: InvestorPreferencesConverterOptions; /** * * ISO alpha-3 country code. @@ -89,7 +82,7 @@ export interface InvestorPreferencesOptions extends MorningstarOptions { * */ filters?: InvestorPreferencesFilter[]; - /** + /** * * When true, returns will not be calculated using the restructure date. * @@ -140,7 +133,7 @@ export interface InvestorPreferencesOptions extends MorningstarOptions { * */ universeIds: Array; -}; +} export interface CalculatedDataPoint { /** @@ -157,7 +150,7 @@ export interface CalculatedDataPoint { * */ condition: CalculatedDataPointCondition; -}; +} export interface CalculatedDataPointCondition { /** @@ -177,7 +170,7 @@ export interface CalculatedDataPointCondition { * */ or: Array; -}; +} export interface CalculatedDataPointConditionField { /** Data point that condition is applied to. */ @@ -186,7 +179,7 @@ export interface CalculatedDataPointConditionField { op: ConditionalOperator; /** Value to base condition calculation on. */ value: string|number; -}; +} export type ConditionalOperator = ( | 'eq' @@ -208,7 +201,8 @@ export interface InvestorPreferencesFilter { value: any; } -export type ComparatorCode = ( +// TODO: move this type as a shared one in Screener directory +type ComparatorCode = ( | 'IN' | 'NIN' | 'EQ' @@ -231,8 +225,7 @@ export interface InvestorPreferencesMetadata extends MorningstarMetadata { total?: number; pageSize?: number; calculatedDataPointNames?: string[]; -}; - +} /* * * @@ -240,5 +233,4 @@ export interface InvestorPreferencesMetadata extends MorningstarMetadata { * * */ - export default InvestorPreferencesOptions; diff --git a/src/Screeners/InvestorPreferences/index.ts b/src/Screeners/InvestorPreferences/index.ts index 1ff8832..a8e3cb0 100644 --- a/src/Screeners/InvestorPreferences/index.ts +++ b/src/Screeners/InvestorPreferences/index.ts @@ -1,6 +1,6 @@ /* * * - * (c) 2009-2024 Highsoft AS + * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * diff --git a/src/api.d.ts b/src/api.d.ts index b9d0844..bfa289c 100644 --- a/src/api.d.ts +++ b/src/api.d.ts @@ -1,6 +1,6 @@ /* * * - * (c) 2009-2024 Highsoft AS + * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * @@ -25,6 +25,7 @@ import RNANewsOptions from './RNANews/RNANewsOptions'; import TimeSeriesOptions from './TimeSeries/TimeSeriesOptions'; import XRayOptions from './XRay/XRayOptions'; import InvestmentScreenerOptions from './Screeners/InvestmentScreener/InvestmentScreenerOptions'; +import InvestorPreferencesOptions from './Screeners/InvestorPreferences/InvestorPreferencesOptions'; /* * * @@ -43,4 +44,5 @@ interface Options { TimeSeries: TimeSeriesOptions; XRay: XRayOptions; InvestmentScreener: InvestmentScreenerOptions; + InvestorPreferences: InvestorPreferencesOptions; } diff --git a/src/index.ts b/src/index.ts index e682941..3463690 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,7 @@ import XRay from './XRay/index'; export * from './GoalAnalysis/index'; export * from './Screeners/InvestmentScreener/index'; +export * from './Screeners/InvestorPreferences/index'; export * from './SecurityDetails/index'; export * from './RNANews/index'; export * from './RiskScore/index'; diff --git a/tests/Screeners/InvestorPreferences.test.ts b/tests/Screeners/InvestorPreferences.test.ts new file mode 100644 index 0000000..8f0a846 --- /dev/null +++ b/tests/Screeners/InvestorPreferences.test.ts @@ -0,0 +1,68 @@ +import * as Assert from 'node:assert/strict'; +import * as MC from '../../code/connectors-morningstar.src'; +import InvestorPreferencesJSON from +'../../code/es-modules/Screeners/InvestorPreferences/InvestorPreferencesJSON'; + +export async function InvestorPreferencesLoad ( + api: MC.Shared.MorningstarAPIOptions +) { + const secIds = [ + 'SecId', + 'LegalName', + 'DomicileId', + 'StarRatingM255' + ]; + const connector = new MC.InvestorPreferencesConnector({ + api, + page: 1, + pageSize: 10, + sortOrder: 'name asc', + currencyId: 'GBP', + securityDataPoints: secIds, + universeIds: ['FOGBR$$ALL'] + }); + + Assert.ok( + connector instanceof MC.InvestorPreferencesConnector, + 'Connector should be instance of InvestorPreferencesConnector class.' + ); + + Assert.ok( + connector.converter instanceof MC.InvestorPreferencesConverter, + 'Converter should be instance of InvestorPreferencesConverter.' + ); + + await connector.load(); + + Assert.deepStrictEqual( + connector.table.getColumnNames(), + secIds.map(id => `InvestorPreferences_${id}`), + 'Connector table should exist of expected columns.' + ); + + Assert.strictEqual( + connector.table.getRowCount(), + 10, + 'Connector table should have 15 rows.' + ); +} + +export function InvestorPreferencesResponseValidation () { + const exampleResponse = { + total: 18238, + page: 1, + pageSize: 20, + rows: [ + { + SecId: 'F000015O6T', + LegalName: '1OAK Multi Asset 80 UCITS Fund A GBP Acc', + StarRatingM255: 3 + } + ] + }; + + Assert.ok( + InvestorPreferencesJSON.isInvestorPreferencesResponse(exampleResponse), + 'InvestorPreferencesJSON should validate correct response.' + ); +} From 75642a4aa9b11688c95a92565d5966caedfa6197 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Mon, 13 Jan 2025 12:02:19 +0100 Subject: [PATCH 03/19] Change way of DOM manipulation in demo. --- demos/dashboards-investor-preferences/demo.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js index fe3b2ee..4ec4a6f 100644 --- a/demos/dashboards-investor-preferences/demo.js +++ b/demos/dashboards-investor-preferences/demo.js @@ -255,7 +255,7 @@ function displayInvestorPreferences (postmanJSON) { sfdrTypes = document.querySelectorAll('.sfdr:checked'), filters = []; - currentFilter.innerHTML = ''; + currentFilter.textContent = ''; // Create a filter that will check if the investment is within EU tax // alignment or SFDR classification @@ -263,10 +263,10 @@ function displayInvestorPreferences (postmanJSON) { const minPortion = document.getElementById('min-portion').value, isOptional = document.getElementById('optional'); - currentFilter.innerHTML = 'EU Tax alignment: ' + - euTaxAlignment.selectedOptions[0].innerHTML + + currentFilter.textContent = 'EU Tax alignment: ' + + euTaxAlignment.selectedOptions[0].textContent + ', Minimum portion: ' + minPortion + '%, ' + - isOptional.selectedOptions[0].innerHTML + '.'; + isOptional.selectedOptions[0].textContent + '.'; filters.push( { @@ -288,10 +288,10 @@ function displayInvestorPreferences (postmanJSON) { sfdrTypes.forEach(type => { values.push(type.value); types.push( - document.querySelector(`label[for='${type.id}'`).innerHTML + document.querySelector(`label[for='${type.id}'`).textContent ); }); - currentFilter.innerHTML += + currentFilter.textContent += 'SFDR Classification: ' + types.join(', '); filters.push({ From d079dcb03abedbacfa54aa3d007f32f55aac7087 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Tue, 14 Jan 2025 16:14:47 +0100 Subject: [PATCH 04/19] Added missing docs page, refined filters in demo. --- .../dashboards-investor-preferences/demo.css | 18 +++-- .../dashboards-investor-preferences/demo.html | 28 +++---- demos/dashboards-investor-preferences/demo.js | 7 ++ .../screeners/investor-preferences.md | 76 +++++++++++++++++++ 4 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 docs/connectors/morningstar/screeners/investor-preferences.md diff --git a/demos/dashboards-investor-preferences/demo.css b/demos/dashboards-investor-preferences/demo.css index 82a01cd..991f8a2 100644 --- a/demos/dashboards-investor-preferences/demo.css +++ b/demos/dashboards-investor-preferences/demo.css @@ -46,15 +46,19 @@ body { height: 500px; } +.filters-row>#slider>span#slider-value { + width: 30px; +} .filters-row>.input-wrapper { - display: block; - margin-bottom: 0.5rem; + display: inline-block; + margin-right: 0.5rem; } -.filters-row>.input-wrapper>input, .filters-row>.input-wrapper>select { - margin-top: 0.5rem; +.filters-row>#slider { + display: inline-flex; + align-items: center; } -.filters-row>button, .filters-row>select { +.filters-row>button { background: #f2f2f2; border: none; border-radius: 4px; @@ -69,3 +73,7 @@ body { .filters-row>button:hover { background: #e6e6e6; } + +.filters-row>button[disabled]:hover { + background: #f2f2f2; +} diff --git a/demos/dashboards-investor-preferences/demo.html b/demos/dashboards-investor-preferences/demo.html index b94a448..824fb08 100644 --- a/demos/dashboards-investor-preferences/demo.html +++ b/demos/dashboards-investor-preferences/demo.html @@ -8,10 +8,10 @@ - Highcharts Dashboards + Morningstar Portfolio Investment Details + Highcharts Dashboards + Morningstar Investor Preferences Screener -

    Highcharts Dashboards + Morningstar Portfolio Investment Details

    +

    Highcharts Dashboards + Morningstar Investor Preferences Screener

    Add your Postman environment file from Morningstar to start the demo: @@ -19,8 +19,8 @@

    Highcharts Dashboards + Morningstar Portfolio Investment Details

    -
    - @@ -28,28 +28,28 @@

    Highcharts Dashboards + Morningstar Portfolio Investment Details

    - 0
    - 0 +
    -
    -
    - SFDR Classification
    - + SFDR Classification + - + - +
    - +
    Current filter: diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js index 4ec4a6f..c561af0 100644 --- a/demos/dashboards-investor-preferences/demo.js +++ b/demos/dashboards-investor-preferences/demo.js @@ -317,6 +317,9 @@ function displayInvestorPreferences (postmanJSON) { async function handleSelectEnvironment (evt) { const target = evt.target; const postmanJSON = await getPostmanJSON(target); + const filters = document.querySelectorAll( + '.input-wrapper input, .input-wrapper select, button#filter-1' + ); if (!postmanJSON) { loadingLabel.textContent = @@ -326,6 +329,10 @@ async function handleSelectEnvironment (evt) { return; } + for (const filter of filters) { + filter.disabled = false; + } + target.parentNode.style.display = 'none'; loadingLabel.style.display = 'block'; diff --git a/docs/connectors/morningstar/screeners/investor-preferences.md b/docs/connectors/morningstar/screeners/investor-preferences.md new file mode 100644 index 0000000..2301c76 --- /dev/null +++ b/docs/connectors/morningstar/screeners/investor-preferences.md @@ -0,0 +1,76 @@ +Investor Preferences +============================= + +Using Morningstar Investor Preferences screener allows you to filter Morningstar's database of global investments for securities that match a set of criteria unique to an investor. + +This connector allows you to configure and pass a set of criteria an investment must satisfy in order to be considered aligned with an investor’s preferences. + +Securities that match the criteria in the request are tagged with a data point, making it easy to identify and highlight them for use in investment lists, comparisons, portfolio analysis, and building portfolios. + +How to use Investor Preferences +---------------- + +The Investor Preferences endpoint allows you to highlight securities matching on user-defined criteria like risk tolerance, sustainability score, objectives, and exclusions. Calculated data points, represented as boolean flags, make it straightforward to identify investments aligned with user preferences for comparison, portfolio construction, or detailed analysis. + +Additionally, you can filter the returned securities list to ensure that only portfolios strictly meeting investor preferences are included. + +For more details, see [Morningstar's Investor Preferences API] + +This connector is designed to be interacted with using external buttons, that might filter data on the backend, provide pagination as well as sorting. + +Here is an example of how to use the Investor Preferences connector: + +```js +const investorPreferencesConnector = new HighchartsConnectors.Morningstar.InvestorPreferencesConnector({ + page: 1, + pageSize: 20, + languageId: 'en-GB', + currencyId: 'USD', + filters: [ + { + dataPointId: 'StarRatingM255', + comparatorCode: 'IN', + value: 5 + } + ], + securityDataPoints: [ + 'secId', + 'name', + 'sustainabilityRank' + ], + calculatedDataPoints: [{ + name: 'ClientPreferences', + type: 'bool', + condition: { + and: [{ + fields: [ + { + name: 'EET_EUSFDRType', + op: 'eq', + value: 8 + }, + { + name: 'EET_PAI_GHGEmissions1Considered', + op: 'eq', + value: 'Y' + }, + { + name: 'EET_PAI_GHGEmissions3Considered', + op: 'in', + value: ['N','I'] + } + ] + }] + } + }], + universeIds: ['FOALL$$ALL'], + postman: { + environmentJSON: postmanJSON + } +}); +``` + +For details see [Morningstar's Investor Preferences API]. + + +[Morningstar's Investor Preferences API]: https://developer.morningstar.com/direct-web-services/documentation/direct-web-services/screener/investor-preferences From cb49a7708133df9786732bbed5a3790fa1052663 Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Thu, 16 Jan 2025 21:27:15 +0100 Subject: [PATCH 05/19] Add ESG screener docs and demo. --- demos/dashboards-esg-screener/demo.css | 63 +++++++++ demos/dashboards-esg-screener/demo.html | 28 ++++ demos/dashboards-esg-screener/demo.js | 126 ++++++++++++++++++ demos/index.html | 1 + .../morningstar/screeners/esg-screener.md | 60 +++++++++ 5 files changed, 278 insertions(+) create mode 100644 demos/dashboards-esg-screener/demo.css create mode 100644 demos/dashboards-esg-screener/demo.html create mode 100644 demos/dashboards-esg-screener/demo.js create mode 100644 docs/connectors/morningstar/screeners/esg-screener.md diff --git a/demos/dashboards-esg-screener/demo.css b/demos/dashboards-esg-screener/demo.css new file mode 100644 index 0000000..d3d6439 --- /dev/null +++ b/demos/dashboards-esg-screener/demo.css @@ -0,0 +1,63 @@ +@import url("https://code.highcharts.com/dashboards/css/datagrid.css"); +@import url("https://code.highcharts.com/css/highcharts.css"); +@import url("https://code.highcharts.com/dashboards/css/dashboards.css"); + +body { + font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif; +} + +.row { + display: flex; + flex-wrap: wrap; +} + +.cell { + flex: 1; + min-width: 20px; +} + +.cell>.highcharts-dashboards-component { + position: relative; + margin: 10px; + background-clip: border-box; +} + +.highcharts-dashboards-component-title { + padding: 10px; + margin: 0; + background-color: var(--highcharts-neutral-color-5); + color: var(--highcharts-neutral-color-100); + border: solid 1px var(--highcharts-neutral-color-20); + border-bottom: none; +} + +@media screen and (max-width: 1000px) { + .row { + flex-direction: column; + } +} + +.filters-row { + flex-direction: row; + justify-content: space-evenly; +} + +#dashboard-col-1 { + height: 500px; +} + +.filters-row>button { + background: #f2f2f2; + border: none; + border-radius: 4px; + cursor: pointer; + display: inline-block; + font-size: 0.8rem; + padding: 0.5rem 1.5rem; + margin: 0.5rem -5px 0.5rem 10px; + transition: background 250ms; +} + +.filters-row>button:hover { + background: #e6e6e6; +} diff --git a/demos/dashboards-esg-screener/demo.html b/demos/dashboards-esg-screener/demo.html new file mode 100644 index 0000000..a9f8807 --- /dev/null +++ b/demos/dashboards-esg-screener/demo.html @@ -0,0 +1,28 @@ + + + + + + + + + + + Highcharts Dashboards + Morningstar Portfolio ESG Details + + +

    Highcharts Dashboards + Morningstar Portfolio ESG Details

    +

    + Add your Postman environment file from Morningstar to start the demo: + +

    + +
    +
    +
    + - + - + - + + + diff --git a/demos/dashboards-esg-screener/demo.js b/demos/dashboards-esg-screener/demo.js new file mode 100644 index 0000000..03dbeb3 --- /dev/null +++ b/demos/dashboards-esg-screener/demo.js @@ -0,0 +1,126 @@ +const loadingLabel = document.getElementById('loading-label'); + +function displayESGScreener (postmanJSON) { + const secIds = [ + 'secid', + 'name', + 'sustainableinvestmentoverall', + 'historicalsustainabilityscore', + 'sustainabilitypercentrank', + 'average12monthcarbonriskscore', + 'average12monthfossilfuelexposure', + 'tobacco', + 'controversialweapons', + 'renewableenergyproductioninvolvement' + ]; + + const headerFormats = { + SecId: 'Security ID', + Name: 'Investment Name', + SustainableInvestmentOverall: 'Overall Sustainability', + HistoricalSustainabilityScore: 'Historical Sustainability Score', + SustainabilityPercentRank: 'Sustainability Percent Rank', + Average12MonthCarbonRiskScore: '12-Month Avg Carbon Risk', + Average12MonthFossilFuelExposure: '12-Month Avg Fossil Fuel Exposure', + Tobacco: 'Tobacco Involvement', + ControversialWeapons: 'Controversial Weapons', + RenewableEnergyProductionInvolvement: 'Renewable Energy Production' + }; + + const columns = secIds.map(id => ({ + id: `InvestmentScreener_${id}`, + header: { + format: headerFormats[id] || id + } + })); + + const board = Dashboards.board('container', { + dataPool: { + connectors: [ + { + id: 'esg-screener', + type: 'MorningstarInvestmentScreener', + options: { + page: 1, + pageSize: 20, + langageId: 'en-GB', + currencyId: 'USD', + securityDataPoints: secIds, + universeIds: ['FOALL$$ALL'], + postman: { + environmentJSON: postmanJSON + } + } + } + ] + }, + components: [ + { + renderTo: 'dashboard-col-1', + connector: { + id: 'esg-screener' + }, + type: 'DataGrid', + + dataGridOptions: { + editable: false, + columns + }, + title: 'ESG Screener' + } + ] + }); + + board.dataPool.getConnector('esg-screener').then(connector => { + loadingLabel.style.display = 'none'; + document.getElementById('total').innerHTML = + `total - ${connector.metadata.total}`; + document.getElementById('page').innerHTML = + `page - ${connector.metadata.page}`; + document.getElementById('total-pages').innerHTML = + `out of ${Math.ceil(connector.metadata.total / connector.metadata.pageSize)}`; + }); +} + +async function handleSelectEnvironment (evt) { + const target = evt.target; + const postmanJSON = await getPostmanJSON(target); + + if (!postmanJSON) { + loadingLabel.textContent = + 'The provided file is not a Postman Environment Configuration.'; + loadingLabel.style.display = 'block'; + + return; + } + + target.parentNode.style.display = 'none'; + + loadingLabel.style.display = 'block'; + loadingLabel.textContent = 'Loading data…'; + + displayESGScreener(postmanJSON); +} + +document + .getElementById('postman-json') + .addEventListener('change', handleSelectEnvironment); + +async function getPostmanJSON (htmlInputFile) { + let file; + let fileJSON; + + for (file of htmlInputFile.files) { + try { + fileJSON = JSON.parse(await file.text()); + if (HighchartsConnectors.Morningstar.Shared.isPostmanEnvironmentJSON(fileJSON)) { + break; + } + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } + } + + return fileJSON; +} diff --git a/demos/index.html b/demos/index.html index 836ac72..9db5adb 100644 --- a/demos/index.html +++ b/demos/index.html @@ -13,6 +13,7 @@

    Morningstar Connectors Demos

  • Highcharts Stock + Morningstar Security Details
  • Highcharts Dashboards + Morningstar Risk Score
  • Highcharts Dashboards + Morningstar Investment Screener
  • +
  • Highcharts Dashboards + Morningstar ESG Screener
  • Using fetch without connectors
  • diff --git a/docs/connectors/morningstar/screeners/esg-screener.md b/docs/connectors/morningstar/screeners/esg-screener.md new file mode 100644 index 0000000..7bbc4bd --- /dev/null +++ b/docs/connectors/morningstar/screeners/esg-screener.md @@ -0,0 +1,60 @@ +# ESG Screener + +Using the Morningstar ESG Screener endpoint allows you to filter Morningstar's database of global investments with a focus on data related to sustainable investing. + +## How to use ESG Screener + +Data can be filtered on any data point comprehensively covered by Morningstar, including proprietary data such as: + + * Morningstar ESG Ratings + * Carbon Risk + * Fossil Fuel Exposure + * Controversial Weapons Exposure + +With this solution, you can develop a sophisticated screening tool offering dozens of filters for advanced users. + +You can also use it to power predefined screeners for common ESG research interests such as Sustainable Investment Leaders, Low Carbon Risk Funds, and more. + +For more details, see [Morningstar's ESG Screener API]. + + +[Morningstar's ESG Screener API]: https://developer.morningstar.com/direct-web-services/documentation/direct-web-services/screener/esg-screener + + +This connector is designed to be interacted with using external buttons, that might filter data on the backend, provide pagination as well as sorting. + +Here is an example of how to use the ESG Screener connector: + +```js +const screenerConnector = new HighchartsConnectors.Morningstar.ESGScreenerConnector({ + page: 1, + pageSize: 20, + languageId: 'en-GB', + currencyId: 'USD', + filters: [ + { + dataPointId: 'SustainableInvestmentOverall', + comparatorCode: 'EQ', + value: true + } + ], + securityDataPoints: [ + 'secid', + 'name', + 'sustainableinvestmentoverall', + 'historicalsustainabilityscore', + 'sustainabilitypercentrank', + 'average12monthcarbonriskscore', + 'average12monthfossilfuelexposure', + 'tobacco', + 'controversialweapons', + 'renewableenergyproductioninvolvement', + ], + universeIds: ['FOALL$$ALL'], + postman: { + environmentJSON: postmanJSON + } +}); +``` + +For more details, see [Morningstar's ESG Screener API]. \ No newline at end of file From b105c2a09453955d3c2d1751095d72f2209a8cc4 Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Thu, 16 Jan 2025 21:47:04 +0100 Subject: [PATCH 06/19] Add Find Similar Screener. --- .../dashboards-find-similar-screener/demo.css | 63 ++++++++++ .../demo.html | 28 +++++ .../dashboards-find-similar-screener/demo.js | 112 ++++++++++++++++++ demos/index.html | 1 + .../screeners/find-similar-screener.md | 49 ++++++++ 5 files changed, 253 insertions(+) create mode 100644 demos/dashboards-find-similar-screener/demo.css create mode 100644 demos/dashboards-find-similar-screener/demo.html create mode 100644 demos/dashboards-find-similar-screener/demo.js create mode 100644 docs/connectors/morningstar/screeners/find-similar-screener.md diff --git a/demos/dashboards-find-similar-screener/demo.css b/demos/dashboards-find-similar-screener/demo.css new file mode 100644 index 0000000..d3d6439 --- /dev/null +++ b/demos/dashboards-find-similar-screener/demo.css @@ -0,0 +1,63 @@ +@import url("https://code.highcharts.com/dashboards/css/datagrid.css"); +@import url("https://code.highcharts.com/css/highcharts.css"); +@import url("https://code.highcharts.com/dashboards/css/dashboards.css"); + +body { + font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif; +} + +.row { + display: flex; + flex-wrap: wrap; +} + +.cell { + flex: 1; + min-width: 20px; +} + +.cell>.highcharts-dashboards-component { + position: relative; + margin: 10px; + background-clip: border-box; +} + +.highcharts-dashboards-component-title { + padding: 10px; + margin: 0; + background-color: var(--highcharts-neutral-color-5); + color: var(--highcharts-neutral-color-100); + border: solid 1px var(--highcharts-neutral-color-20); + border-bottom: none; +} + +@media screen and (max-width: 1000px) { + .row { + flex-direction: column; + } +} + +.filters-row { + flex-direction: row; + justify-content: space-evenly; +} + +#dashboard-col-1 { + height: 500px; +} + +.filters-row>button { + background: #f2f2f2; + border: none; + border-radius: 4px; + cursor: pointer; + display: inline-block; + font-size: 0.8rem; + padding: 0.5rem 1.5rem; + margin: 0.5rem -5px 0.5rem 10px; + transition: background 250ms; +} + +.filters-row>button:hover { + background: #e6e6e6; +} diff --git a/demos/dashboards-find-similar-screener/demo.html b/demos/dashboards-find-similar-screener/demo.html new file mode 100644 index 0000000..bb20e0f --- /dev/null +++ b/demos/dashboards-find-similar-screener/demo.html @@ -0,0 +1,28 @@ + + + + + + + + + + + Highcharts Dashboards + Morningstar Portfolio Find Similar Investments + + +

    Highcharts Dashboards + Morningstar Portfolio Find Similar Investments

    +

    + Add your Postman environment file from Morningstar to start the demo: + +

    + +
    +
    +
    + - + - + - + + + diff --git a/demos/dashboards-find-similar-screener/demo.js b/demos/dashboards-find-similar-screener/demo.js new file mode 100644 index 0000000..ba44f4c --- /dev/null +++ b/demos/dashboards-find-similar-screener/demo.js @@ -0,0 +1,112 @@ +const loadingLabel = document.getElementById('loading-label'); + +function displayFindSimilarScreener (postmanJSON) { + const secIds = [ + 'SecId', + 'Name', + 'riskrating' + ]; + + const headerFormats = { + SecId: 'Security ID', + Name: 'Investment Name', + riskrating: 'Risk Rating' + }; + + const columns = secIds.map(id => ({ + id: `InvestmentScreener_${id}`, + header: { + format: headerFormats[id] || id + } + })); + + const board = Dashboards.board('container', { + dataPool: { + connectors: [ + { + id: 'find-similar-screener', + type: 'MorningstarInvestmentScreener', + options: { + page: 1, + pageSize: 20, + langageId: 'en-GB', + currencyId: 'USD', + securityDataPoints: secIds, + universeIds: ['FOALL$$ALL'], + postman: { + environmentJSON: postmanJSON + } + } + } + ] + }, + components: [ + { + renderTo: 'dashboard-col-1', + connector: { + id: 'find-similar-screener' + }, + type: 'DataGrid', + + dataGridOptions: { + editable: false, + columns + }, + title: 'Find Similar Screener' + } + ] + }); + + board.dataPool.getConnector('find-similar-screener').then(connector => { + loadingLabel.style.display = 'none'; + document.getElementById('total').innerHTML = + `total - ${connector.metadata.total}`; + document.getElementById('page').innerHTML = + `page - ${connector.metadata.page}`; + document.getElementById('total-pages').innerHTML = + `out of ${Math.ceil(connector.metadata.total / connector.metadata.pageSize)}`; + }); +} + +async function handleSelectEnvironment (evt) { + const target = evt.target; + const postmanJSON = await getPostmanJSON(target); + + if (!postmanJSON) { + loadingLabel.textContent = + 'The provided file is not a Postman Environment Configuration.'; + loadingLabel.style.display = 'block'; + + return; + } + + target.parentNode.style.display = 'none'; + + loadingLabel.style.display = 'block'; + loadingLabel.textContent = 'Loading data…'; + + displayFindSimilarScreener(postmanJSON); +} + +document + .getElementById('postman-json') + .addEventListener('change', handleSelectEnvironment); + +async function getPostmanJSON (htmlInputFile) { + let file; + let fileJSON; + + for (file of htmlInputFile.files) { + try { + fileJSON = JSON.parse(await file.text()); + if (HighchartsConnectors.Morningstar.Shared.isPostmanEnvironmentJSON(fileJSON)) { + break; + } + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } + } + + return fileJSON; +} diff --git a/demos/index.html b/demos/index.html index 9db5adb..d6d166c 100644 --- a/demos/index.html +++ b/demos/index.html @@ -14,6 +14,7 @@

    Morningstar Connectors Demos

  • Highcharts Dashboards + Morningstar Risk Score
  • Highcharts Dashboards + Morningstar Investment Screener
  • Highcharts Dashboards + Morningstar ESG Screener
  • +
  • Highcharts Dashboards + Morningstar Find Similar Screener
  • Using fetch without connectors
  • diff --git a/docs/connectors/morningstar/screeners/find-similar-screener.md b/docs/connectors/morningstar/screeners/find-similar-screener.md new file mode 100644 index 0000000..43b6d0e --- /dev/null +++ b/docs/connectors/morningstar/screeners/find-similar-screener.md @@ -0,0 +1,49 @@ +# Find Similar Screener + +Using the Morningstar Find Similar Screener endpoint allows you to find investments with similar characteristics to one you are already familiar with. + +## How to use Find Similar Screener + +The Find Similar capability helps identify alternative investments with comparable characteristics. For example, if a preferred investment is closed to new investors or has high minimum requirements, this tool can help you find suitable alternatives using customizable filters. + +For more details, see [Morningstar's Find Similar Screener API]. + + +[Morningstar's Find Similar Screener API]: https://developer.morningstar.com/direct-web-services/documentation/direct-web-services/screener/find-similar + +This connector is designed to be interacted with using external buttons, that might filter data on the backend, provide pagination as well as sorting. + +Here is an example of how to use the Find Similar Screener connector: + +```js +const screenerConnector = new HighchartsConnectors.Morningstar.FindSimilarScreenerConnector({ + page: 1, + pageSize: 20, + languageId: 'en-GB', + currencyId: 'GBP', + filters: [ + { + dataPointId: 'CategoryId', + comparatorCode: 'EQ', + value: 'EUCA000591' + }, + { + dataPointId: 'OngoingCharge', + comparatorCode: 'LT', + value: 0 + } + ], + securityDataPoints: [ + 'SecId', + 'Name', + 'riskrating' + ], + sortOrder: 'Name Asc', + universeIds: ['FOESP$$ALL'], + postman: { + environmentJSON: postmanJSON + } +}); +``` + +For more details, see [Morningstar's Find Similar Screener API]. \ No newline at end of file From 0e347878473f3874f78b1cad38e9e332fad9107b Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Fri, 17 Jan 2025 10:53:24 +0100 Subject: [PATCH 07/19] Add Regulatory Screener docs and demo. --- demos/dashboards-esg-screener/demo.html | 6 +- .../demo.html | 6 +- demos/dashboards-regulatory-screener/demo.css | 63 ++++++++++ .../dashboards-regulatory-screener/demo.html | 28 +++++ demos/dashboards-regulatory-screener/demo.js | 116 ++++++++++++++++++ demos/index.html | 1 + .../screeners/regulatory-screener.md | 47 +++++++ 7 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 demos/dashboards-regulatory-screener/demo.css create mode 100644 demos/dashboards-regulatory-screener/demo.html create mode 100644 demos/dashboards-regulatory-screener/demo.js create mode 100644 docs/connectors/morningstar/screeners/regulatory-screener.md diff --git a/demos/dashboards-esg-screener/demo.html b/demos/dashboards-esg-screener/demo.html index a9f8807..671824a 100644 --- a/demos/dashboards-esg-screener/demo.html +++ b/demos/dashboards-esg-screener/demo.html @@ -20,9 +20,9 @@

    Highcharts Dashboards + Morningstar Portfolio ESG Details

    - - - - - - + + + diff --git a/demos/dashboards-find-similar-screener/demo.html b/demos/dashboards-find-similar-screener/demo.html index bb20e0f..da4e70e 100644 --- a/demos/dashboards-find-similar-screener/demo.html +++ b/demos/dashboards-find-similar-screener/demo.html @@ -20,9 +20,9 @@

    Highcharts Dashboards + Morningstar Portfolio Find Similar Investments

    - - - - - - + + + diff --git a/demos/dashboards-regulatory-screener/demo.css b/demos/dashboards-regulatory-screener/demo.css new file mode 100644 index 0000000..d3d6439 --- /dev/null +++ b/demos/dashboards-regulatory-screener/demo.css @@ -0,0 +1,63 @@ +@import url("https://code.highcharts.com/dashboards/css/datagrid.css"); +@import url("https://code.highcharts.com/css/highcharts.css"); +@import url("https://code.highcharts.com/dashboards/css/dashboards.css"); + +body { + font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif; +} + +.row { + display: flex; + flex-wrap: wrap; +} + +.cell { + flex: 1; + min-width: 20px; +} + +.cell>.highcharts-dashboards-component { + position: relative; + margin: 10px; + background-clip: border-box; +} + +.highcharts-dashboards-component-title { + padding: 10px; + margin: 0; + background-color: var(--highcharts-neutral-color-5); + color: var(--highcharts-neutral-color-100); + border: solid 1px var(--highcharts-neutral-color-20); + border-bottom: none; +} + +@media screen and (max-width: 1000px) { + .row { + flex-direction: column; + } +} + +.filters-row { + flex-direction: row; + justify-content: space-evenly; +} + +#dashboard-col-1 { + height: 500px; +} + +.filters-row>button { + background: #f2f2f2; + border: none; + border-radius: 4px; + cursor: pointer; + display: inline-block; + font-size: 0.8rem; + padding: 0.5rem 1.5rem; + margin: 0.5rem -5px 0.5rem 10px; + transition: background 250ms; +} + +.filters-row>button:hover { + background: #e6e6e6; +} diff --git a/demos/dashboards-regulatory-screener/demo.html b/demos/dashboards-regulatory-screener/demo.html new file mode 100644 index 0000000..7ae4d1c --- /dev/null +++ b/demos/dashboards-regulatory-screener/demo.html @@ -0,0 +1,28 @@ + + + + + + + + + + + Highcharts Dashboards + Morningstar Portfolio Regulatory Screener + + +

    Highcharts Dashboards + Morningstar Portfolio Regulatory Screener

    +

    + Add your Postman environment file from Morningstar to start the demo: + +

    + +
    +
    +
    + + + + + + diff --git a/demos/dashboards-regulatory-screener/demo.js b/demos/dashboards-regulatory-screener/demo.js new file mode 100644 index 0000000..c1d355e --- /dev/null +++ b/demos/dashboards-regulatory-screener/demo.js @@ -0,0 +1,116 @@ +const loadingLabel = document.getElementById('loading-label'); + +function displayRegulatoryScreener (postmanJSON) { + const secIds = [ + 'SecId', + 'name', + 'ticker', + 'EETTemplateDate', + 'EETGeneralDate' + ]; + + const headerFormats = { + SecId: 'Security ID', + name: 'Investment Name', + ticker: 'Ticker Symbol', + EETTemplateDate: 'EET Template Date', + EETGeneralDate: 'EET General Date' + }; + + const columns = secIds.map(id => ({ + id: `InvestmentScreener_${id}`, + header: { + format: headerFormats[id] || id + } + })); + + const board = Dashboards.board('container', { + dataPool: { + connectors: [ + { + id: 'regulatory-screener', + type: 'MorningstarInvestmentScreener', + options: { + page: 1, + pageSize: 20, + langageId: 'en-GB', + currencyId: 'USD', + securityDataPoints: secIds, + universeIds: ['FOALL$$ALL'], + postman: { + environmentJSON: postmanJSON + } + } + } + ] + }, + components: [ + { + renderTo: 'dashboard-col-1', + connector: { + id: 'regulatory-screener' + }, + type: 'DataGrid', + + dataGridOptions: { + editable: false, + columns + }, + title: 'Regulatory Screener' + } + ] + }); + + board.dataPool.getConnector('regulatory-screener').then(connector => { + loadingLabel.style.display = 'none'; + document.getElementById('total').innerHTML = + `total - ${connector.metadata.total}`; + document.getElementById('page').innerHTML = + `page - ${connector.metadata.page}`; + document.getElementById('total-pages').innerHTML = + `out of ${Math.ceil(connector.metadata.total / connector.metadata.pageSize)}`; + }); +} + +async function handleSelectEnvironment (evt) { + const target = evt.target; + const postmanJSON = await getPostmanJSON(target); + + if (!postmanJSON) { + loadingLabel.textContent = + 'The provided file is not a Postman Environment Configuration.'; + loadingLabel.style.display = 'block'; + + return; + } + + target.parentNode.style.display = 'none'; + + loadingLabel.style.display = 'block'; + loadingLabel.textContent = 'Loading data…'; + + displayRegulatoryScreener(postmanJSON); +} + +document + .getElementById('postman-json') + .addEventListener('change', handleSelectEnvironment); + +async function getPostmanJSON (htmlInputFile) { + let file; + let fileJSON; + + for (file of htmlInputFile.files) { + try { + fileJSON = JSON.parse(await file.text()); + if (HighchartsConnectors.Morningstar.Shared.isPostmanEnvironmentJSON(fileJSON)) { + break; + } + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } + } + + return fileJSON; +} diff --git a/demos/index.html b/demos/index.html index d6d166c..67faa96 100644 --- a/demos/index.html +++ b/demos/index.html @@ -15,6 +15,7 @@

    Morningstar Connectors Demos

  • Highcharts Dashboards + Morningstar Investment Screener
  • Highcharts Dashboards + Morningstar ESG Screener
  • Highcharts Dashboards + Morningstar Find Similar Screener
  • +
  • Highcharts Dashboards + Morningstar Regulatory Screener
  • Using fetch without connectors
  • diff --git a/docs/connectors/morningstar/screeners/regulatory-screener.md b/docs/connectors/morningstar/screeners/regulatory-screener.md new file mode 100644 index 0000000..e5e6b60 --- /dev/null +++ b/docs/connectors/morningstar/screeners/regulatory-screener.md @@ -0,0 +1,47 @@ +# Regulatory Screener + +Using the Morningstar Regulatory Screener endpoint allows you to access key regulatory data on global investments, supporting your processes for meeting regulatory requirements. + +## How to use Regulatory Screener + +The Regulatory Screener provides a comprehensive solution for accessing regulatory data from a single source. It enables you to retrieve essential compliance information and legal filings, such as KIIDs and prospectuses, in a format tailored to your needs. + +Data can be filtered on any data point comprehensively covered by Morningstar, including proprietary data such as: + + * Sustainability Preferences Considered + * EU SFDR Classification + * Principal Adverse Impacts (PAI) + * Planned Investments and Allocations + +For more details, see [Morningstar's Regulatory Screener API]. + +This connector is designed to be interacted with using external buttons, that might filter data on the backend, provide pagination as well as sorting. + +Here is an example of how to use the Regulatory Screener connector: + +```js +const screenerConnector = new HighchartsConnectors.Morningstar.RegulatoryScreenerConnector({ + page: 1, + pageSize: 20, + languageId: 'en-AU', + currencyId: 'AUD', + filters: [ + { + dataPointId: 'SustainableInvestmentOverall', + comparatorCode: 'EQ', + value: true + } + ], + securityDataPoints: [ + 'SecId', + 'name', + 'ticker', + 'EET_TemplateDate', + 'EET_GeneralDate' + ], + universeIds: ['FOEUR$$ALL_5791'], + sortOrder: 'name asc', + postman: { + environmentJSON: postmanJSON + } +}); From 7fb03b924959544b30ded4dc7f6c3b87b07e66f2 Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Fri, 17 Jan 2025 10:56:28 +0100 Subject: [PATCH 08/19] Add missing morningstar docs link for regulatory screener. --- .../connectors/morningstar/screeners/regulatory-screener.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/connectors/morningstar/screeners/regulatory-screener.md b/docs/connectors/morningstar/screeners/regulatory-screener.md index e5e6b60..ffe34fd 100644 --- a/docs/connectors/morningstar/screeners/regulatory-screener.md +++ b/docs/connectors/morningstar/screeners/regulatory-screener.md @@ -15,6 +15,9 @@ Data can be filtered on any data point comprehensively covered by Morningstar, i For more details, see [Morningstar's Regulatory Screener API]. + +[Morningstar's Regulatory Screener API]: https://developer.morningstar.com/direct-web-services/documentation/direct-web-services/screener/regulatory-screener + This connector is designed to be interacted with using external buttons, that might filter data on the backend, provide pagination as well as sorting. Here is an example of how to use the Regulatory Screener connector: @@ -45,3 +48,6 @@ const screenerConnector = new HighchartsConnectors.Morningstar.RegulatoryScreene environmentJSON: postmanJSON } }); +``` + +For more details, see [Morningstar's Regulatory Screener API]. \ No newline at end of file From 7c647378d422cb1f3cae0acd469f20bae1c5be81 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Fri, 17 Jan 2025 18:10:28 +0100 Subject: [PATCH 09/19] Added human readable header formats in dashboard. --- demos/dashboards-investor-preferences/demo.js | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js index c561af0..dca460f 100644 --- a/demos/dashboards-investor-preferences/demo.js +++ b/demos/dashboards-investor-preferences/demo.js @@ -13,6 +13,33 @@ function displayInvestorPreferences (postmanJSON) { 'EET_PAI_SocialViolationsPercentageConsidered' ]; + const headerFormats = { + 'SecId': 'Security Id', + 'LegalName': 'Legal Name', + 'DomicileId': 'Domicile Id', + 'EET_PAI_GHGEmissions3Considered': + 'European ESG Template Principal Adverse Impact Greenhouse Gas Emissions 3 Considered', + 'EET_PAI_GHGEmissions1And2Considered': + `European ESG Template Principal Adverse Impact Greenhouse Gas Emissions + 1 And 2 Considered`, + 'EET_PAI_EnergyConsumptionNaceAConsidered': + 'Energy Consumption Intensity Per High Impact Climate Sector NACE A Considered', + 'EET_PAI_EnergyConsumptionNaceBConsidered': + 'Energy Consumption Intensity Per High Impact Climate Sector NACE B Considered', + 'EET_PAI_FossilFuelConsidered': + 'European ESG Template Principal Adverse Impact Fossil Fuel Considered', + 'EET_PAI_SocialViolationsPercentageConsidered': + `European ESG Template Principal Adverse Impact Social Violations + Percentage Considered`, + 'StarRatingM255': 'Star Rating M255', + 'UserPref0': 'UserPref0', + 'UserPref1': 'UserPref1', + 'UserPref2': 'UserPref2', + 'EET_EUSFDRType': 'Financial Instrument SFDR Product Type', + 'EET_PAI_GHGEmissions1Considered': + 'European ESG Template Principal Adverse Impact Greenhouse Gas Emissions 1 Considered' + }; + const calculatedDataPoints = [ { 'name': 'UserPref0', @@ -134,14 +161,17 @@ function displayInvestorPreferences (postmanJSON) { ] } } - ] + ]; - const columns = secIds.map(id => ({ - id: `InvestorPreferences_${id}`, - header: { - format: id - } - })); + const columns = []; + for (const key in headerFormats) { + columns.push({ + id: `InvestorPreferences_${key}`, + header: { + format: headerFormats[key] || key + } + }); + } const board = Dashboards.board('container', { dataPool: { From d6cd84bca682b356966f7dc1c02c95fb266ca6f2 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Mon, 20 Jan 2025 20:34:13 +0100 Subject: [PATCH 10/19] Add SVG icon, remove Highcharts import and apply suggestions. --- .../dashboards-investor-preferences/demo.css | 14 ++++++++++++- .../dashboards-investor-preferences/demo.html | 1 - demos/dashboards-investor-preferences/demo.js | 20 +++++++++++++++---- .../screeners/investor-preferences.md | 2 +- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/demos/dashboards-investor-preferences/demo.css b/demos/dashboards-investor-preferences/demo.css index 991f8a2..9ea0c40 100644 --- a/demos/dashboards-investor-preferences/demo.css +++ b/demos/dashboards-investor-preferences/demo.css @@ -1,5 +1,4 @@ @import url("https://code.highcharts.com/dashboards/css/datagrid.css"); -@import url("https://code.highcharts.com/css/highcharts.css"); @import url("https://code.highcharts.com/dashboards/css/dashboards.css"); body { @@ -77,3 +76,16 @@ body { .filters-row>button[disabled]:hover { background: #f2f2f2; } + +.st0 { + fill: none; + stroke: var(--highcharts-neutral-color-60); + stroke-width: 2; + stroke-miterlimit: 10; +} + +@media (prefers-color-scheme: dark) { + .st0 { + stroke: var(--highcharts-neutral-color-100); + } +} \ No newline at end of file diff --git a/demos/dashboards-investor-preferences/demo.html b/demos/dashboards-investor-preferences/demo.html index 824fb08..f222711 100644 --- a/demos/dashboards-investor-preferences/demo.html +++ b/demos/dashboards-investor-preferences/demo.html @@ -4,7 +4,6 @@ - diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js index dca460f..04ddcc2 100644 --- a/demos/dashboards-investor-preferences/demo.js +++ b/demos/dashboards-investor-preferences/demo.js @@ -1,10 +1,12 @@ const loadingLabel = document.getElementById('loading-label'); function displayInvestorPreferences (postmanJSON) { + // eslint-disable-next-line + DataGrid.AST.allowedAttributes.push('viewBox'); + const secIds = [ 'SecId', 'LegalName', - 'DomicileId', 'EET_PAI_GHGEmissions3Considered', 'EET_PAI_GHGEmissions1And2Considered', 'EET_PAI_EnergyConsumptionNaceAConsidered', @@ -13,10 +15,20 @@ function displayInvestorPreferences (postmanJSON) { 'EET_PAI_SocialViolationsPercentageConsidered' ]; + const iconSVG = ` + + + `; + const headerFormats = { 'SecId': 'Security Id', 'LegalName': 'Legal Name', - 'DomicileId': 'Domicile Id', 'EET_PAI_GHGEmissions3Considered': 'European ESG Template Principal Adverse Impact Greenhouse Gas Emissions 3 Considered', 'EET_PAI_GHGEmissions1And2Considered': @@ -31,7 +43,7 @@ function displayInvestorPreferences (postmanJSON) { 'EET_PAI_SocialViolationsPercentageConsidered': `European ESG Template Principal Adverse Impact Social Violations Percentage Considered`, - 'StarRatingM255': 'Star Rating M255', + 'StarRatingM255': iconSVG, 'UserPref0': 'UserPref0', 'UserPref1': 'UserPref1', 'UserPref2': 'UserPref2', @@ -382,7 +394,7 @@ async function getPostmanJSON (htmlInputFile) { for (file of htmlInputFile.files) { try { fileJSON = JSON.parse(await file.text()); - if (HighchartsConnectors.Morningstar.isPostmanEnvironmentJSON(fileJSON)) { + if (HighchartsConnectors.Morningstar.Shared.isPostmanEnvironmentJSON(fileJSON)) { break; } } catch (error) { diff --git a/docs/connectors/morningstar/screeners/investor-preferences.md b/docs/connectors/morningstar/screeners/investor-preferences.md index 2301c76..84c099d 100644 --- a/docs/connectors/morningstar/screeners/investor-preferences.md +++ b/docs/connectors/morningstar/screeners/investor-preferences.md @@ -1,7 +1,7 @@ Investor Preferences ============================= -Using Morningstar Investor Preferences screener allows you to filter Morningstar's database of global investments for securities that match a set of criteria unique to an investor. +Using Morningstar Investor Preferences screener allows you to filter Morningstar's database of global investments for securities that match a set of criteria unique to an investor. This connector allows you to configure and pass a set of criteria an investment must satisfy in order to be considered aligned with an investor’s preferences. From 37887ab90c8145bd47a754389966c48fe4ea7b3d Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Wed, 22 Jan 2025 09:18:55 +0100 Subject: [PATCH 11/19] remove core import from screeners --- demos/dashboards-esg-screener/demo.html | 1 - demos/dashboards-find-similar-screener/demo.html | 1 - demos/dashboards-investment-screener/demo.html | 1 - demos/dashboards-regulatory-screener/demo.html | 1 - 4 files changed, 4 deletions(-) diff --git a/demos/dashboards-esg-screener/demo.html b/demos/dashboards-esg-screener/demo.html index 671824a..a6ba6b6 100644 --- a/demos/dashboards-esg-screener/demo.html +++ b/demos/dashboards-esg-screener/demo.html @@ -4,7 +4,6 @@ - diff --git a/demos/dashboards-find-similar-screener/demo.html b/demos/dashboards-find-similar-screener/demo.html index da4e70e..4da6d91 100644 --- a/demos/dashboards-find-similar-screener/demo.html +++ b/demos/dashboards-find-similar-screener/demo.html @@ -4,7 +4,6 @@ - diff --git a/demos/dashboards-investment-screener/demo.html b/demos/dashboards-investment-screener/demo.html index ac9053a..29e7107 100644 --- a/demos/dashboards-investment-screener/demo.html +++ b/demos/dashboards-investment-screener/demo.html @@ -4,7 +4,6 @@ - diff --git a/demos/dashboards-regulatory-screener/demo.html b/demos/dashboards-regulatory-screener/demo.html index 7ae4d1c..f489654 100644 --- a/demos/dashboards-regulatory-screener/demo.html +++ b/demos/dashboards-regulatory-screener/demo.html @@ -4,7 +4,6 @@ - From d3417ace1dcf14b3f31eccbdc076901406dcd0bc Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Wed, 22 Jan 2025 09:31:43 +0100 Subject: [PATCH 12/19] add screener.md --- docs/connectors/morningstar.md | 2 +- docs/connectors/morningstar/screeners/screener.md | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 docs/connectors/morningstar/screeners/screener.md diff --git a/docs/connectors/morningstar.md b/docs/connectors/morningstar.md index 422e090..2702284 100644 --- a/docs/connectors/morningstar.md +++ b/docs/connectors/morningstar.md @@ -54,5 +54,5 @@ types. * [Risk Score](morningstar/risk-score.md) * [TimeSeries](morningstar/time-series/time-series.md) * [XRay](morningstar/x-ray.md) -* [Investment Screener](morningstar/screeners/investment-screener.md) +* [Screener](morningstar/screeners/screener.md) * [Security Details](morningstar/security-details.md) diff --git a/docs/connectors/morningstar/screeners/screener.md b/docs/connectors/morningstar/screeners/screener.md new file mode 100644 index 0000000..1a92205 --- /dev/null +++ b/docs/connectors/morningstar/screeners/screener.md @@ -0,0 +1,10 @@ +# Screener + +Filter thousands of investments to quickly find those matching your criteria. Over a hundred data points can be used to fine-tune results for specific business requirements. + +## Capabilities + +- [Investment Screener](investment-screener.md) +- [ESG Screener](esg-screener.md) +- [Regulatory Screener](regulatory-screener.md) +- [Find Similar](find-similar-screener.md) From 049c612774d30f23af04fd70340d891d13dfd5fe Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Wed, 22 Jan 2025 10:28:48 +0100 Subject: [PATCH 13/19] add missing find-similar screener filters and fix typo --- .../dashboards-find-similar-screener/demo.js | 19 +++++++++++++++++-- .../morningstar/screeners/esg-screener.md | 2 +- .../screeners/find-similar-screener.md | 2 +- .../screeners/regulatory-screener.md | 2 +- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/demos/dashboards-find-similar-screener/demo.js b/demos/dashboards-find-similar-screener/demo.js index ba44f4c..6c438e3 100644 --- a/demos/dashboards-find-similar-screener/demo.js +++ b/demos/dashboards-find-similar-screener/demo.js @@ -2,8 +2,8 @@ const loadingLabel = document.getElementById('loading-label'); function displayFindSimilarScreener (postmanJSON) { const secIds = [ - 'SecId', - 'Name', + 'secId', + 'name', 'riskrating' ]; @@ -33,6 +33,21 @@ function displayFindSimilarScreener (postmanJSON) { currencyId: 'USD', securityDataPoints: secIds, universeIds: ['FOALL$$ALL'], + sortOrder: 'Name+Asc', + filters: [ + { + dataPointId: 'CategoryId', + comparatorCode: 'EQ', + // The returned list will be similar to the + // chosen fund + value: 'EUCA000591' + }, + { + dataPointId: 'OngoingCharge', + comparatorCode: 'LT', + value: '0' + } + ], postman: { environmentJSON: postmanJSON } diff --git a/docs/connectors/morningstar/screeners/esg-screener.md b/docs/connectors/morningstar/screeners/esg-screener.md index 7bbc4bd..f7a2587 100644 --- a/docs/connectors/morningstar/screeners/esg-screener.md +++ b/docs/connectors/morningstar/screeners/esg-screener.md @@ -26,7 +26,7 @@ This connector is designed to be interacted with using external buttons, that mi Here is an example of how to use the ESG Screener connector: ```js -const screenerConnector = new HighchartsConnectors.Morningstar.ESGScreenerConnector({ +const screenerConnector = new HighchartsConnectors.Morningstar.InvestmentScreenerConnector({ page: 1, pageSize: 20, languageId: 'en-GB', diff --git a/docs/connectors/morningstar/screeners/find-similar-screener.md b/docs/connectors/morningstar/screeners/find-similar-screener.md index 43b6d0e..f31d087 100644 --- a/docs/connectors/morningstar/screeners/find-similar-screener.md +++ b/docs/connectors/morningstar/screeners/find-similar-screener.md @@ -16,7 +16,7 @@ This connector is designed to be interacted with using external buttons, that mi Here is an example of how to use the Find Similar Screener connector: ```js -const screenerConnector = new HighchartsConnectors.Morningstar.FindSimilarScreenerConnector({ +const screenerConnector = new HighchartsConnectors.Morningstar.InvestmentScreenerConnector({ page: 1, pageSize: 20, languageId: 'en-GB', diff --git a/docs/connectors/morningstar/screeners/regulatory-screener.md b/docs/connectors/morningstar/screeners/regulatory-screener.md index ffe34fd..e571c29 100644 --- a/docs/connectors/morningstar/screeners/regulatory-screener.md +++ b/docs/connectors/morningstar/screeners/regulatory-screener.md @@ -23,7 +23,7 @@ This connector is designed to be interacted with using external buttons, that mi Here is an example of how to use the Regulatory Screener connector: ```js -const screenerConnector = new HighchartsConnectors.Morningstar.RegulatoryScreenerConnector({ +const screenerConnector = new HighchartsConnectors.Morningstar.InvestmentScreenerConnector({ page: 1, pageSize: 20, languageId: 'en-AU', From f91557359aacf752cbfd78c0f26ee052a2f644a0 Mon Sep 17 00:00:00 2001 From: Mateusz Bernacik Date: Wed, 22 Jan 2025 10:43:01 +0100 Subject: [PATCH 14/19] requested changes --- demos/dashboards-esg-screener/demo.js | 36 +++++++++---------- .../dashboards-find-similar-screener/demo.js | 8 ++--- demos/dashboards-regulatory-screener/demo.js | 12 +++---- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/demos/dashboards-esg-screener/demo.js b/demos/dashboards-esg-screener/demo.js index 03dbeb3..55e26c3 100644 --- a/demos/dashboards-esg-screener/demo.js +++ b/demos/dashboards-esg-screener/demo.js @@ -2,29 +2,29 @@ const loadingLabel = document.getElementById('loading-label'); function displayESGScreener (postmanJSON) { const secIds = [ - 'secid', + 'secId', 'name', - 'sustainableinvestmentoverall', - 'historicalsustainabilityscore', - 'sustainabilitypercentrank', - 'average12monthcarbonriskscore', - 'average12monthfossilfuelexposure', + 'sustainableInvestmentOverall', + 'historicalSustainabilityScore', + 'sustainabilityPercentRank', + 'average12MonthCarbonRiskScore', + 'average12MonthFossilFuelExposure', 'tobacco', - 'controversialweapons', - 'renewableenergyproductioninvolvement' + 'controversialWeapons', + 'renewableEnergyProductionInvolvement' ]; const headerFormats = { - SecId: 'Security ID', - Name: 'Investment Name', - SustainableInvestmentOverall: 'Overall Sustainability', - HistoricalSustainabilityScore: 'Historical Sustainability Score', - SustainabilityPercentRank: 'Sustainability Percent Rank', - Average12MonthCarbonRiskScore: '12-Month Avg Carbon Risk', - Average12MonthFossilFuelExposure: '12-Month Avg Fossil Fuel Exposure', - Tobacco: 'Tobacco Involvement', - ControversialWeapons: 'Controversial Weapons', - RenewableEnergyProductionInvolvement: 'Renewable Energy Production' + secId: 'Security ID', + name: 'Investment Name', + sustainableInvestmentOverall: 'Overall Sustainability', + historicalSustainabilityScore: 'Historical Sustainability Score', + sustainabilityPercentRank: 'Sustainability Percent Rank', + average12MonthCarbonRiskScore: '12-Month Avg Carbon Risk', + average12MonthFossilFuelExposure: '12-Month Avg Fossil Fuel Exposure', + tobacco: 'Tobacco Involvement', + controversialWeapons: 'Controversial Weapons', + renewableEnergyProductionInvolvement: 'Renewable Energy Production' }; const columns = secIds.map(id => ({ diff --git a/demos/dashboards-find-similar-screener/demo.js b/demos/dashboards-find-similar-screener/demo.js index 6c438e3..519bb4d 100644 --- a/demos/dashboards-find-similar-screener/demo.js +++ b/demos/dashboards-find-similar-screener/demo.js @@ -4,13 +4,13 @@ function displayFindSimilarScreener (postmanJSON) { const secIds = [ 'secId', 'name', - 'riskrating' + 'riskRating' ]; const headerFormats = { - SecId: 'Security ID', - Name: 'Investment Name', - riskrating: 'Risk Rating' + secId: 'Security ID', + name: 'Investment Name', + riskRating: 'Risk Rating' }; const columns = secIds.map(id => ({ diff --git a/demos/dashboards-regulatory-screener/demo.js b/demos/dashboards-regulatory-screener/demo.js index c1d355e..890ba40 100644 --- a/demos/dashboards-regulatory-screener/demo.js +++ b/demos/dashboards-regulatory-screener/demo.js @@ -2,19 +2,19 @@ const loadingLabel = document.getElementById('loading-label'); function displayRegulatoryScreener (postmanJSON) { const secIds = [ - 'SecId', + 'secId', 'name', 'ticker', - 'EETTemplateDate', - 'EETGeneralDate' + 'eetTemplateDate', + 'eetGeneralDate' ]; const headerFormats = { - SecId: 'Security ID', + secId: 'Security ID', name: 'Investment Name', ticker: 'Ticker Symbol', - EETTemplateDate: 'EET Template Date', - EETGeneralDate: 'EET General Date' + eetTemplateDate: 'EET Template Date', + eetGeneralDate: 'EET General Date' }; const columns = secIds.map(id => ({ From 2c9c7880f01652176f350de3fa46ca92487164a3 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Wed, 22 Jan 2025 13:09:05 +0100 Subject: [PATCH 15/19] Added cell format for userPref and star rating, included legend. --- .../dashboards-investor-preferences/demo.css | 30 +++++++++++++++++ .../dashboards-investor-preferences/demo.html | 33 +++++++++++++++++-- demos/dashboards-investor-preferences/demo.js | 28 +++++++++------- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/demos/dashboards-investor-preferences/demo.css b/demos/dashboards-investor-preferences/demo.css index 9ea0c40..4d86fa2 100644 --- a/demos/dashboards-investor-preferences/demo.css +++ b/demos/dashboards-investor-preferences/demo.css @@ -88,4 +88,34 @@ body { .st0 { stroke: var(--highcharts-neutral-color-100); } +} + +.dashboard-legend { + border: 1px solid #ccc; + padding: 10px; + margin: 10px 0; + background-color: #f9f9f9; + border-radius: 5px; + max-width: fit-content; +} + +.dashboard-legend h4 { + margin: 0 0 10px; + font-size: 1rem; +} + +.dashboard-legend ul { + list-style: none; + padding: 0; + margin: 0; +} + +.dashboard-legend li { + margin-bottom: 8px; + font-size: .925rem; +} + +.dashboard-legend li ul { + margin-left: 20px; + margin-top: 5px; } \ No newline at end of file diff --git a/demos/dashboards-investor-preferences/demo.html b/demos/dashboards-investor-preferences/demo.html index f222711..c993eb1 100644 --- a/demos/dashboards-investor-preferences/demo.html +++ b/demos/dashboards-investor-preferences/demo.html @@ -3,9 +3,9 @@ - - - + + + Highcharts Dashboards + Morningstar Investor Preferences Screener @@ -57,6 +57,33 @@

    Highcharts Dashboards + Morningstar Investor Preferences Screener

    +
    +

    Legend

    +
      +
    • + User Preference 1: +
        +
      • Morningstar Star Rating Above 3
      • +
      +
    • +
    • + User Preference 2: +
        +
      • SFDR Type 8 and Greenhouse Gas Emissions 1 Considered
      • +
      • OR
      • +
      • + SFDR Type 9 with Greenhouse Gas Emissions 1 and 2 and
        + Energy Consumption under NACE A & B Considered +
      • +
      +
    • + User Preference 3: +
        +
      • SFDR Type 9 or 10 OR Greenhouse Gas Emissions 1 & 3 Considered
      • +
      +
    • +
    +
    - diff --git a/demos/dashboards-investor-preferences/demo.js b/demos/dashboards-investor-preferences/demo.js index 04ddcc2..57c9e8f 100644 --- a/demos/dashboards-investor-preferences/demo.js +++ b/demos/dashboards-investor-preferences/demo.js @@ -15,7 +15,7 @@ function displayInvestorPreferences (postmanJSON) { 'EET_PAI_SocialViolationsPercentageConsidered' ]; - const iconSVG = ` + const starIcon = ` - Highcharts Dashboards + Morningstar Portfolio ESG Details + Highcharts Dashboards + Morningstar ESG Screener -

    Highcharts Dashboards + Morningstar Portfolio ESG Details

    +

    Highcharts Dashboards + Morningstar ESG Screener

    Add your Postman environment file from Morningstar to start the demo: diff --git a/demos/dashboards-esg-screener/demo.js b/demos/dashboards-esg-screener/demo.js index 55e26c3..4bab011 100644 --- a/demos/dashboards-esg-screener/demo.js +++ b/demos/dashboards-esg-screener/demo.js @@ -43,7 +43,7 @@ function displayESGScreener (postmanJSON) { options: { page: 1, pageSize: 20, - langageId: 'en-GB', + languageId: 'en-GB', currencyId: 'USD', securityDataPoints: secIds, universeIds: ['FOALL$$ALL'], diff --git a/demos/dashboards-find-similar-screener/demo.html b/demos/dashboards-find-similar-screener/demo.html index 4da6d91..e0ab4fa 100644 --- a/demos/dashboards-find-similar-screener/demo.html +++ b/demos/dashboards-find-similar-screener/demo.html @@ -7,10 +7,10 @@ - Highcharts Dashboards + Morningstar Portfolio Find Similar Investments + Highcharts Dashboards + Morningstar Find Similar Screener -

    Highcharts Dashboards + Morningstar Portfolio Find Similar Investments

    +

    Highcharts Dashboards + Morningstar Find Similar Screener

    Add your Postman environment file from Morningstar to start the demo: diff --git a/demos/dashboards-find-similar-screener/demo.js b/demos/dashboards-find-similar-screener/demo.js index 519bb4d..005a4c9 100644 --- a/demos/dashboards-find-similar-screener/demo.js +++ b/demos/dashboards-find-similar-screener/demo.js @@ -4,13 +4,15 @@ function displayFindSimilarScreener (postmanJSON) { const secIds = [ 'secId', 'name', - 'riskRating' + 'riskRating', + 'ongoingCharge' ]; const headerFormats = { secId: 'Security ID', name: 'Investment Name', - riskRating: 'Risk Rating' + riskRating: 'Risk Rating', + ongoingCharge: 'Ongoing Charge' }; const columns = secIds.map(id => ({ @@ -29,10 +31,10 @@ function displayFindSimilarScreener (postmanJSON) { options: { page: 1, pageSize: 20, - langageId: 'en-GB', + languageId: 'en-GB', currencyId: 'USD', securityDataPoints: secIds, - universeIds: ['FOALL$$ALL'], + universeIds: ['FOESP$$ALL'], sortOrder: 'Name+Asc', filters: [ { @@ -45,7 +47,7 @@ function displayFindSimilarScreener (postmanJSON) { { dataPointId: 'OngoingCharge', comparatorCode: 'LT', - value: '0' + value: 3 } ], postman: { diff --git a/demos/dashboards-investment-screener/demo.html b/demos/dashboards-investment-screener/demo.html index 29e7107..6688a09 100644 --- a/demos/dashboards-investment-screener/demo.html +++ b/demos/dashboards-investment-screener/demo.html @@ -7,10 +7,10 @@ - Highcharts Dashboards + Morningstar Portfolio Investment Details + Highcharts Dashboards + Morningstar Investment Screener -

    Highcharts Dashboards + Morningstar Portfolio Investment Details

    +

    Highcharts Dashboards + Morningstar Investment Screener

    Add your Postman environment file from Morningstar to start the demo: diff --git a/demos/dashboards-investment-screener/demo.js b/demos/dashboards-investment-screener/demo.js index 8d000ba..749d1f7 100644 --- a/demos/dashboards-investment-screener/demo.js +++ b/demos/dashboards-investment-screener/demo.js @@ -32,7 +32,7 @@ function displayInvestmentScreener (postmanJSON) { options: { page: 1, pageSize: 20, - langageId: 'en-GB', + languageId: 'en-GB', currencyId: 'USD', securityDataPoints: secIds, universeIds: ['FOALL$$ALL'], diff --git a/demos/dashboards-regulatory-screener/demo.css b/demos/dashboards-regulatory-screener/demo.css index d3d6439..3594f0c 100644 --- a/demos/dashboards-regulatory-screener/demo.css +++ b/demos/dashboards-regulatory-screener/demo.css @@ -61,3 +61,13 @@ body { .filters-row>button:hover { background: #e6e6e6; } + + +.highcharts-datagrid-column { + width: 200px; +} + +.highcharts-datagrid-row td { + text-align: center; + vertical-align: middle; +} \ No newline at end of file diff --git a/demos/dashboards-regulatory-screener/demo.html b/demos/dashboards-regulatory-screener/demo.html index f489654..d90d359 100644 --- a/demos/dashboards-regulatory-screener/demo.html +++ b/demos/dashboards-regulatory-screener/demo.html @@ -7,10 +7,10 @@ - Highcharts Dashboards + Morningstar Portfolio Regulatory Screener + Highcharts Dashboards + Morningstar Regulatory Screener -

    Highcharts Dashboards + Morningstar Portfolio Regulatory Screener

    +

    Highcharts Dashboards + Morningstar Regulatory Screener

    Add your Postman environment file from Morningstar to start the demo: diff --git a/demos/dashboards-regulatory-screener/demo.js b/demos/dashboards-regulatory-screener/demo.js index 890ba40..4d71e43 100644 --- a/demos/dashboards-regulatory-screener/demo.js +++ b/demos/dashboards-regulatory-screener/demo.js @@ -4,24 +4,227 @@ function displayRegulatoryScreener (postmanJSON) { const secIds = [ 'secId', 'name', - 'ticker', - 'eetTemplateDate', - 'eetGeneralDate' + 'EET_SustPreferencesConsidered', + 'EET_PAIConsidered', + 'EET_SustInv_A8', + 'EET_PAI_GHGEmissions1Considered', + 'EET_PAI_GHGEmissions2Considered', + 'EET_PAI_GHGEmissions3Considered', + 'EET_PAI_GHGEmissions1And2Considered', + 'EET_PAI_GHGEmissions1And2And3Considered', + 'EET_PAI_CarbonFootprint1And2Considered', + 'EET_PAI_CarbonFootprint1And2And3Considered', + 'EET_PAI_GHGIntensity1And2Considered', + 'EET_PAI_GHGIntensity1And2And3Considered', + 'EET_PAI_FossilFuelConsidered', + 'EET_PAI_NonRenewableEnergyConsumptionConsidered', + 'EET_PAI_NonRenewableEnergyProductionConsidered', + 'EET_PAI_EnergyConsumptionNaceAConsidered', + 'EET_PAI_EnergyConsumptionNaceBConsidered', + 'EET_PAI_EnergyConsumptionNaceCConsidered', + 'EET_PAI_EnergyConsumptionNaceDConsidered', + 'EET_PAI_EnergyConsumptionNaceEConsidered', + 'EET_PAI_EnergyConsumptionNaceFConsidered', + 'EET_PAI_EnergyConsumptionNaceGConsidered', + 'EET_PAI_EnergyConsumptionNaceHConsidered', + 'EET_PAI_EnergyConsumptionNaceLConsidered', + 'EET_PAI_NegativeAffectConsidered', + 'EET_PAI_EmissionsToWaterConsidered', + 'EET_PAI_HazardousWasteConsidered', + 'EET_PAI_UNGCViolationsConsidered', + 'EET_PAI_UNGCLackOfComplianceConsidered', + 'EET_PAI_GenderPayGapConsidered', + 'EET_PAI_FemaleBoardMembersConsidered', + 'EET_PAI_ControversialWeaponsConsidered', + 'EET_PAI_CarbonIntensityConsidered', + 'EET_PAI_SocialViolationsPercentageConsidered' ]; - const headerFormats = { - secId: 'Security ID', - name: 'Investment Name', - ticker: 'Ticker Symbol', - eetTemplateDate: 'EET Template Date', - eetGeneralDate: 'EET General Date' - }; + const header = [ + { + columnId: 'InvestmentScreener_secId', + format: 'Security ID' + }, + { + columnId: 'InvestmentScreener_name', + format: 'Investment Name' + }, + { + columnId: 'InvestmentScreener_EET_SustPreferencesConsidered', + format: 'Sust Preferences Considered' + }, + { + columnId: 'InvestmentScreener_EET_PAIConsidered', + format: 'PAI Considered' + }, + { + columnId: 'InvestmentScreener_EET_SustInv_A8', + format: 'Sust Inv A8' + }, + { + format: 'GHG Emissions Considered', + columns: [ + { + columnId: 'InvestmentScreener_EET_PAI_GHGEmissions1Considered', + format: '1' + }, + { + columnId: 'InvestmentScreener_EET_PAI_GHGEmissions2Considered', + format: '2' + }, + { + columnId: 'InvestmentScreener_EET_PAI_GHGEmissions3Considered', + format: '3' + }, + { + columnId: 'InvestmentScreener_EET_PAI_GHGEmissions1And2Considered', + format: '1 And 2' + }, + { + columnId: 'InvestmentScreener_EET_PAI_GHGEmissions1And2And3Considered', + format: '1 And 2 And 3' + } + ] + }, + { + format: 'Carbon Footprint Considered', + columns: [ + { + columnId: 'InvestmentScreener_EET_PAI_CarbonFootprint1And2Considered', + format: '1 And 2' + }, + { + columnId: 'InvestmentScreener_EET_PAI_CarbonFootprint1And2And3Considered', + format: '1 And 2 And 3' + } + ] + }, + { + format: 'GHG Intensity Considered', + columns: [ + { + columnId: 'InvestmentScreener_EET_PAI_GHGIntensity1And2Considered', + format: '1 And 2' + }, + { + columnId: 'InvestmentScreener_EET_PAI_GHGIntensity1And2And3Considered', + format: '1 And 2 And 3' + } + ] + }, + { + columnId: + 'InvestmentScreener_EET_PAI_FossilFuelConsidered', + format: 'Fossil Fuel Considered' + }, + { + format: 'Non Renewable Energy Considered', + columns: [ + { + columnId: 'InvestmentScreener_EET_PAI_NonRenewableEnergyConsumptionConsidered', + format: 'Non Renewable Energy Consumption' + }, + { + columnId: 'InvestmentScreener_EET_PAI_NonRenewableEnergyProductionConsidered', + format: 'Non Renewable Energy Production' + } + ] + }, + { + format: 'Energy Consumption Nace Considered', + columns: [ + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceAConsidered', + format: 'A' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceBConsidered', + format: 'B' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceCConsidered', + format: 'C' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceDConsidered', + format: 'D' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceEConsidered', + format: 'E' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceFConsidered', + format: 'F' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceGConsidered', + format: 'G' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceHConsidered', + format: 'H' + + }, + { + columnId: 'InvestmentScreener_EET_PAI_EnergyConsumptionNaceLConsidered', + format: 'L' + + } + ] + }, + { + columnId: 'InvestmentScreener_EET_PAI_NegativeAffectConsidered', + format: 'Negative Affect' + }, + { + columnId: 'InvestmentScreener_EET_PAI_EmissionsToWaterConsidered', + format: 'Emissions To Water' + }, + { + columnId: 'InvestmentScreener_EET_PAI_HazardousWasteConsidered', + format: 'Hazardous Waste' + }, + { + columnId: 'InvestmentScreener_EET_PAI_UNGCViolationsConsidered', + format: 'UNGC Violations' + }, + { + columnId: 'InvestmentScreener_EET_PAI_UNGCLackOfComplianceConsidered', + format: 'UNGC Lack Of Compliance' + }, + { + columnId: 'InvestmentScreener_EET_PAI_GenderPayGapConsidered', + format: 'Gender Pay Gap' + }, + { + columnId: 'InvestmentScreener_EET_PAI_FemaleBoardMembersConsidered', + format: 'Female Board Members' + }, + { + columnId: 'InvestmentScreener_EET_PAI_ControversialWeaponsConsidered', + format: 'Controversial Weapons' + }, + { + columnId: 'InvestmentScreener_EET_PAI_CarbonIntensityConsidered', + format: 'Carbon Intensity' + }, + { + columnId: 'InvestmentScreener_EET_PAI_SocialViolationsPercentageConsidered', + format: 'Social Violations Percentage' - const columns = secIds.map(id => ({ - id: `InvestmentScreener_${id}`, - header: { - format: headerFormats[id] || id } + ]; + + const columns = secIds.map(id => ({ + id: `InvestmentScreener_${id}` })); const board = Dashboards.board('container', { @@ -33,10 +236,10 @@ function displayRegulatoryScreener (postmanJSON) { options: { page: 1, pageSize: 20, - langageId: 'en-GB', + languageId: 'en-GB', currencyId: 'USD', securityDataPoints: secIds, - universeIds: ['FOALL$$ALL'], + universeIds: ['FOEUR$$ALL_5791'], postman: { environmentJSON: postmanJSON } @@ -53,10 +256,30 @@ function displayRegulatoryScreener (postmanJSON) { type: 'DataGrid', dataGridOptions: { + columnDefaults: { + cells: { + formatter () { + if (this.value === 'Y') { + return '✅'; + } else if (this.value === 'N') { + return '❌'; + } + + return this.value === undefined ? '' : this.value; + } + } + }, editable: false, - columns + columns, + rendering: { + columns: { + distribution: 'fixed' + } + }, + header }, title: 'Regulatory Screener' + } ] }); diff --git a/docs/connectors/morningstar/screeners/esg-screener.md b/docs/connectors/morningstar/screeners/esg-screener.md index f7a2587..254be26 100644 --- a/docs/connectors/morningstar/screeners/esg-screener.md +++ b/docs/connectors/morningstar/screeners/esg-screener.md @@ -39,16 +39,16 @@ const screenerConnector = new HighchartsConnectors.Morningstar.InvestmentScreene } ], securityDataPoints: [ - 'secid', + 'secId', 'name', - 'sustainableinvestmentoverall', - 'historicalsustainabilityscore', - 'sustainabilitypercentrank', - 'average12monthcarbonriskscore', - 'average12monthfossilfuelexposure', + 'sustainableInvestmentOverall', + 'historicalSustainabilityScore', + 'sustainabilityPercentRank', + 'average12MonthCarbonRiskScore', + 'average12MonthFossilFuelExposure', 'tobacco', - 'controversialweapons', - 'renewableenergyproductioninvolvement', + 'controversialWeapons', + 'renewableEnergyProductionInvolvement' ], universeIds: ['FOALL$$ALL'], postman: { diff --git a/docs/connectors/morningstar/screeners/find-similar-screener.md b/docs/connectors/morningstar/screeners/find-similar-screener.md index f31d087..220e5ef 100644 --- a/docs/connectors/morningstar/screeners/find-similar-screener.md +++ b/docs/connectors/morningstar/screeners/find-similar-screener.md @@ -25,20 +25,21 @@ const screenerConnector = new HighchartsConnectors.Morningstar.InvestmentScreene { dataPointId: 'CategoryId', comparatorCode: 'EQ', - value: 'EUCA000591' + value: '0P00002D7X' }, { dataPointId: 'OngoingCharge', comparatorCode: 'LT', - value: 0 + value: 3 } ], securityDataPoints: [ - 'SecId', - 'Name', - 'riskrating' + 'secId', + 'name', + 'riskRating' + 'ongoingCharge' ], - sortOrder: 'Name Asc', + sortOrder: 'Name+Asc', universeIds: ['FOESP$$ALL'], postman: { environmentJSON: postmanJSON diff --git a/docs/connectors/morningstar/screeners/regulatory-screener.md b/docs/connectors/morningstar/screeners/regulatory-screener.md index e571c29..86cc8b2 100644 --- a/docs/connectors/morningstar/screeners/regulatory-screener.md +++ b/docs/connectors/morningstar/screeners/regulatory-screener.md @@ -30,17 +30,17 @@ const screenerConnector = new HighchartsConnectors.Morningstar.InvestmentScreene currencyId: 'AUD', filters: [ { - dataPointId: 'SustainableInvestmentOverall', + dataPointId: 'EET_PAIConsidered', comparatorCode: 'EQ', value: true } ], securityDataPoints: [ - 'SecId', + 'secId', 'name', - 'ticker', - 'EET_TemplateDate', - 'EET_GeneralDate' + 'EET_SustPreferencesConsidered', + 'EET_PAIConsidered', + 'EET_SustInv_A8', ], universeIds: ['FOEUR$$ALL_5791'], sortOrder: 'name asc', diff --git a/src/Screeners/InvestmentScreener/InvestmentScreenerConnector.ts b/src/Screeners/InvestmentScreener/InvestmentScreenerConnector.ts index 066d746..2b0e6b7 100644 --- a/src/Screeners/InvestmentScreener/InvestmentScreenerConnector.ts +++ b/src/Screeners/InvestmentScreener/InvestmentScreenerConnector.ts @@ -36,8 +36,8 @@ import MorningstarURL from '../../Shared/MorningstarURL'; * * */ -const UTF_PIPE = '|'; -const UTF_COLON = ':'; +const UTF_PIPE = '%7C'; +const UTF_COLON = '%3A'; /* * * @@ -90,6 +90,7 @@ export class InvestmentScreenerConnector extends MorningstarConnector { prev + UTF_PIPE + this.getFilter(curr), '' ); + searchParams.set('filters', filters); } From cde46114118011bc243058aceea120e934011d03 Mon Sep 17 00:00:00 2001 From: jedrzejruta Date: Wed, 29 Jan 2025 16:37:05 +0100 Subject: [PATCH 19/19] Add investor-preferences to screener docs list. --- docs/connectors/morningstar/screeners/screener.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/connectors/morningstar/screeners/screener.md b/docs/connectors/morningstar/screeners/screener.md index 1a92205..3cf4c81 100644 --- a/docs/connectors/morningstar/screeners/screener.md +++ b/docs/connectors/morningstar/screeners/screener.md @@ -5,6 +5,7 @@ Filter thousands of investments to quickly find those matching your criteria. Ov ## Capabilities - [Investment Screener](investment-screener.md) +- [Investor Preferences](investor-preferences.md) - [ESG Screener](esg-screener.md) - [Regulatory Screener](regulatory-screener.md) - [Find Similar](find-similar-screener.md)