diff --git a/demos/index.html b/demos/index.html index 836ac72..ef8a61d 100644 --- a/demos/index.html +++ b/demos/index.html @@ -11,6 +11,7 @@

Morningstar Connectors Demos

  • Highcharts Stock + Morningstar TimeSeries
  • Highcharts Stock + Morningstar OHLCV TimeSeries
  • Highcharts Stock + Morningstar Security Details
  • +
  • Morningstar Security Details Asset Allocations
  • Highcharts Dashboards + Morningstar Risk Score
  • Highcharts Dashboards + Morningstar Investment Screener
  • Using fetch without connectors
  • diff --git a/demos/stock-securitydetails-asset-allocations/demo.html b/demos/stock-securitydetails-asset-allocations/demo.html new file mode 100644 index 0000000..8785fae --- /dev/null +++ b/demos/stock-securitydetails-asset-allocations/demo.html @@ -0,0 +1,23 @@ + + + + + + + Morningstar Security Details Asset Allocations + + + +

    Morningstar Security Details Asset Allocations

    +

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

    +
    + + + diff --git a/demos/stock-securitydetails-asset-allocations/demo.js b/demos/stock-securitydetails-asset-allocations/demo.js new file mode 100644 index 0000000..1fe811e --- /dev/null +++ b/demos/stock-securitydetails-asset-allocations/demo.js @@ -0,0 +1,75 @@ +async function displayAssetAllocations (postmanJSON) { + const securityId = 'US4642898674'; + + const connector = new HighchartsConnectors.Morningstar.SecurityDetailsConnector({ + postman: { + environmentJSON: postmanJSON + }, + security: { + id: securityId, + idType: 'ISIN' + }, + converter: { + type: 'AssetAllocations' + } + }); + + await connector.load(); + + const typeMapping = { + '1': 'Stocks', + '2': 'Bonds', + '3': 'Cash', + '4': 'Other Instruments', + '99': 'Unclassified' + }; + + const chartData = connector.table.getRowObjects().map(item => ({ + name: typeMapping[item.AssetAllocations_Type], + y: item.AssetAllocations_MorningstarEUR3_N + })); + + Highcharts.chart('container', { + title: { + text: 'iShares Core Growth Allocation ETF (AOR) Asset Allocation' + }, + subtitle: { + text: 'Type: MorningstarEUR3 | Sale Position: Net (N)' + }, + series: [{ + type: 'pie', + name: 'VTI Asset Allocation', + data: chartData + }] + }); +} + +async function handleSelectEnvironment (evt) { + const target = evt.target; + const postmanJSON = await getPostmanJSON(target); + + target.parentNode.style.display = 'none'; + + displayAssetAllocations(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) { + // fail silently + } + } + + return fileJSON; +} diff --git a/demos/stock-securitydetails/demo.js b/demos/stock-securitydetails/demo.js index 21d10b3..b19c8cb 100644 --- a/demos/stock-securitydetails/demo.js +++ b/demos/stock-securitydetails/demo.js @@ -50,7 +50,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/security-details.md b/docs/connectors/morningstar/security-details.md index 28ce82b..78a1319 100644 --- a/docs/connectors/morningstar/security-details.md +++ b/docs/connectors/morningstar/security-details.md @@ -18,6 +18,33 @@ Supported id-types are: `CUSIP`, `FundCode`, `ISIN`, `MSID`, `PerformanceId`, `S If any securities are invalid, the connector will still yield results. The invalid securities will appear in the connector's `metadata` after load. +#### Security Details Types + +You can specify the type of data to retrieve by using the `type` option in the connector. The following types are available: + +- **TrailingPerformance** (default) +- **AssetAllocations** +- **RegionalExposure** +- **GlobalStockSectorBreakdown** +- **CountryExposure** + +Example usage: + +```js +const securityDetailsConnector = new HighchartsConnectors.Morningstar.SecurityDetailsConnector({ + postman: { + environmentJSON: postmanJSON + }, + security: { + id: 'F0GBR050DD', + idType: 'MSID' + }, + converter: { + type: 'AssetAllocations' // Specify the type of data to retrieve + } +}); +``` + For more details, see [Morningstar’s Security Details API]. ### Security Details with Morningstar standalone for Highcharts: diff --git a/src/SecurityDetails/Converters/AssetAllocationsConverter.ts b/src/SecurityDetails/Converters/AssetAllocationsConverter.ts new file mode 100644 index 0000000..3ed157b --- /dev/null +++ b/src/SecurityDetails/Converters/AssetAllocationsConverter.ts @@ -0,0 +1,151 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * - Pawel Lysy + * - Askel Eirik Johansson + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import { + SecurityDetailsConverterOptions, + SecurityDetailsMetadata +} from '../SecurityDetailsOptions'; +import SecurityDetailsJSON from '../SecurityDetailsJSON'; +import SecurityDetailsConverter from '../SecurityDetailsConverter'; + +/* * + * + * Class + * + * */ + + +export class AssetAllocationsConverter extends SecurityDetailsConverter { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + options?: SecurityDetailsConverterOptions + ) { + super(options); + + this.metadata = { + columns: {} + }; + } + + + /* * + * + * Properties + * + * */ + + + public readonly metadata: SecurityDetailsMetadata; + + + /* * + * + * Functions + * + * */ + + + public override parse ( + options: SecurityDetailsConverterOptions + ): void { + const metadata = this.metadata; + const table = this.table; + const userOptions = { + ...this.options, + ...options + }; + const json = userOptions.json; + + // Validate JSON + + if (!SecurityDetailsJSON.isSecurityDetailsResponse(json)) { + throw new Error('Invalid data'); + } + + // Prepare table + + table.deleteColumns(); + table.setColumn('AssetAllocations_Type'); + + // Add asset allocations to table + + if (json.length) { + + // Update table + + const securityDetails = json[0]; + const assetAllocations = securityDetails.Portfolios[0].AssetAllocations; + + table.setColumn('AssetAllocations_Type'); + + for (let i = 0; i < assetAllocations.length; i++) { + const asset = assetAllocations[i]; + + table.setColumn(`AssetAllocations_${asset.Type}_${asset.SalePosition}`); + + for (let j = 0; j < asset.BreakdownValues.length; j++) { + table.setCell( + `AssetAllocations_${asset.Type}_${asset.SalePosition}`, + j, + asset.BreakdownValues[j].Value + ); + + table.setCell( + 'AssetAllocations_Type', + j, + asset.BreakdownValues[j].Type + ); + } + } + + // Update meta data + + metadata.id = securityDetails.Id; + metadata.isin = securityDetails.Isin; + } + + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default AssetAllocationsConverter; diff --git a/src/SecurityDetails/Converters/CountryExposureConverter.ts b/src/SecurityDetails/Converters/CountryExposureConverter.ts new file mode 100644 index 0000000..902de47 --- /dev/null +++ b/src/SecurityDetails/Converters/CountryExposureConverter.ts @@ -0,0 +1,151 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * - Pawel Lysy + * - Askel Eirik Johansson + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import { + SecurityDetailsConverterOptions, + SecurityDetailsMetadata +} from '../SecurityDetailsOptions'; +import SecurityDetailsJSON from '../SecurityDetailsJSON'; +import SecurityDetailsConverter from '../SecurityDetailsConverter'; + +/* * + * + * Class + * + * */ + + +export class CountryExposureConverter extends SecurityDetailsConverter { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + options?: SecurityDetailsConverterOptions + ) { + super(options); + + this.metadata = { + columns: {} + }; + } + + + /* * + * + * Properties + * + * */ + + + public readonly metadata: SecurityDetailsMetadata; + + + /* * + * + * Functions + * + * */ + + + public override parse ( + options: SecurityDetailsConverterOptions + ): void { + const metadata = this.metadata; + const table = this.table; + const userOptions = { + ...this.options, + ...options + }; + const json = userOptions.json; + + // Validate JSON + + if (!SecurityDetailsJSON.isSecurityDetailsResponse(json)) { + throw new Error('Invalid data'); + } + + // Prepare table + + table.deleteColumns(); + table.setColumn('CountryExposure_Type'); + + // Add country exposure to table + + if (json.length) { + + // Update table + + const securityDetails = json[0]; + const CountryExposure = securityDetails.Portfolios[0].CountryExposure; + + table.setColumn('CountryExposure_Type'); + + for (let i = 0; i < CountryExposure.length; i++) { + const asset = CountryExposure[i]; + + table.setColumn(`CountryExposure_${asset.Type}_${asset.SalePosition}_${asset.NotClassified}`); + + for (let j = 0; j < asset.BreakdownValues.length; j++) { + table.setCell( + `CountryExposure_${asset.Type}_${asset.SalePosition}_${asset.NotClassified}`, + j, + asset.BreakdownValues[j].Value + ); + + table.setCell( + 'CountryExposure_Type', + j, + asset.BreakdownValues[j].Type + ); + } + } + + // Update meta data + + metadata.id = securityDetails.Id; + metadata.isin = securityDetails.Isin; + } + + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default CountryExposureConverter; diff --git a/src/SecurityDetails/Converters/GlobalStockSectorBreakdownConverter.ts b/src/SecurityDetails/Converters/GlobalStockSectorBreakdownConverter.ts new file mode 100644 index 0000000..ac35417 --- /dev/null +++ b/src/SecurityDetails/Converters/GlobalStockSectorBreakdownConverter.ts @@ -0,0 +1,150 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * - Pawel Lysy + * - Askel Eirik Johansson + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import { + SecurityDetailsConverterOptions, + SecurityDetailsMetadata +} from '../SecurityDetailsOptions'; +import SecurityDetailsJSON from '../SecurityDetailsJSON'; +import SecurityDetailsConverter from '../SecurityDetailsConverter'; + +/* * + * + * Class + * + * */ + + +export class GlobalStockSectorBreakdownConverter extends SecurityDetailsConverter { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + options?: SecurityDetailsConverterOptions + ) { + super(options); + + this.metadata = { + columns: {} + }; + } + + + /* * + * + * Properties + * + * */ + + + public readonly metadata: SecurityDetailsMetadata; + + + /* * + * + * Functions + * + * */ + + + public override parse ( + options: SecurityDetailsConverterOptions + ): void { + const metadata = this.metadata; + const table = this.table; + const userOptions = { + ...this.options, + ...options + }; + const json = userOptions.json; + + // Validate JSON + + if (!SecurityDetailsJSON.isSecurityDetailsResponse(json)) { + throw new Error('Invalid data'); + } + + // Prepare table + + table.deleteColumns(); + table.setColumn('GlobalStockSectorBreakdown_Type'); + + // Add global stock sector breakdown to table + + if (json.length) { + + // Update table + + const securityDetails = json[0]; + const GlobalStockSectorBreakdown = + securityDetails.Portfolios[0].GlobalStockSectorBreakdown; + + for (let i = 0; i < GlobalStockSectorBreakdown.length; i++) { + const asset = GlobalStockSectorBreakdown[i]; + + table.setColumn(`GlobalStockSectorBreakdown_${asset.SalePosition}_${asset.NotClassified}`); + + for (let j = 0; j < asset.BreakdownValues.length; j++) { + table.setCell( + `GlobalStockSectorBreakdown_${asset.SalePosition}_${asset.NotClassified}`, + j, + asset.BreakdownValues[j].Value + ); + + table.setCell( + 'GlobalStockSectorBreakdown_Type', + j, + asset.BreakdownValues[j].Type + ); + } + } + + // Update meta data + + metadata.id = securityDetails.Id; + metadata.isin = securityDetails.Isin; + } + + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default GlobalStockSectorBreakdownConverter; diff --git a/src/SecurityDetails/Converters/RegionalExposureConverter.ts b/src/SecurityDetails/Converters/RegionalExposureConverter.ts new file mode 100644 index 0000000..39dee37 --- /dev/null +++ b/src/SecurityDetails/Converters/RegionalExposureConverter.ts @@ -0,0 +1,149 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * - Pawel Lysy + * - Askel Eirik Johansson + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import { + SecurityDetailsConverterOptions, + SecurityDetailsMetadata +} from '../SecurityDetailsOptions'; +import SecurityDetailsJSON from '../SecurityDetailsJSON'; +import SecurityDetailsConverter from '../SecurityDetailsConverter'; + +/* * + * + * Class + * + * */ + + +export class RegionalExposureConverter extends SecurityDetailsConverter { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + options?: SecurityDetailsConverterOptions + ) { + super(options); + + this.metadata = { + columns: {} + }; + } + + + /* * + * + * Properties + * + * */ + + + public readonly metadata: SecurityDetailsMetadata; + + + /* * + * + * Functions + * + * */ + + + public override parse ( + options: SecurityDetailsConverterOptions + ): void { + const metadata = this.metadata; + const table = this.table; + const userOptions = { + ...this.options, + ...options + }; + const json = userOptions.json; + + // Validate JSON + + if (!SecurityDetailsJSON.isSecurityDetailsResponse(json)) { + throw new Error('Invalid data'); + } + + // Prepare table + + table.deleteColumns(); + table.setColumn('RegionalExposure_Type'); + + // Add regional exposure to table + + if (json.length) { + + // Update table + + const securityDetails = json[0]; + const regionalExposure = securityDetails.Portfolios[0].RegionalExposure; + + for (let i = 0; i < regionalExposure.length; i++) { + const asset = regionalExposure[i]; + + table.setColumn(`RegionalExposure_${asset.SalePosition}_${asset.NotClassified}`); + + for (let j = 0; j < asset.BreakdownValues.length; j++) { + table.setCell( + `RegionalExposure_${asset.SalePosition}_${asset.NotClassified}`, + j, + asset.BreakdownValues[j].Value + ); + + table.setCell( + 'RegionalExposure_Type', + j, + asset.BreakdownValues[j].Type + ); + } + } + + // Update meta data + + metadata.id = securityDetails.Id; + metadata.isin = securityDetails.Isin; + } + + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default RegionalExposureConverter; diff --git a/src/SecurityDetails/Converters/TrailingPerformanceConverter.ts b/src/SecurityDetails/Converters/TrailingPerformanceConverter.ts new file mode 100644 index 0000000..05199d9 --- /dev/null +++ b/src/SecurityDetails/Converters/TrailingPerformanceConverter.ts @@ -0,0 +1,143 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * - Pawel Lysy + * - Askel Eirik Johansson + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import { + SecurityDetailsConverterOptions, + SecurityDetailsMetadata +} from '../SecurityDetailsOptions'; +import SecurityDetailsJSON from '../SecurityDetailsJSON'; +import SecurityDetailsConverter from '../SecurityDetailsConverter'; + +/* * + * + * Class + * + * */ + + +export class TrailingPerformanceConverter extends SecurityDetailsConverter { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + options?: SecurityDetailsConverterOptions + ) { + super(options); + + this.metadata = { + columns: {} + }; + } + + + /* * + * + * Properties + * + * */ + + + public readonly metadata: SecurityDetailsMetadata; + + + /* * + * + * Functions + * + * */ + + + public override parse ( + options: SecurityDetailsConverterOptions + ): void { + const metadata = this.metadata; + const table = this.table; + const userOptions = { + ...this.options, + ...options + }; + const json = userOptions.json; + + // Validate JSON + + if (!SecurityDetailsJSON.isSecurityDetailsResponse(json)) { + throw new Error('Invalid data'); + } + + // Prepare table + + table.deleteColumns(); + table.setColumn('SecurityDetails_TrailingPerformance_TimePeriod'); + table.setColumn('SecurityDetails_TrailingPerformance_Value'); + + // Add trailing performance to table + + if (json.length) { + + // Update table + + const securityDetails = json[0]; + const trailingPerformanceReturn = securityDetails.TrailingPerformance[0].Return; + + for (let i = 0, iEnd = trailingPerformanceReturn.length; i < iEnd; ++i) { + table.setCell( + 'SecurityDetails_TrailingPerformance_TimePeriod', + i, + trailingPerformanceReturn[i].TimePeriod + ); + table.setCell( + 'SecurityDetails_TrailingPerformance_Value', + i, + trailingPerformanceReturn[i].Value + ); + } + + // Update meta data + + metadata.id = securityDetails.Id; + metadata.isin = securityDetails.Isin; + } + + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default TrailingPerformanceConverter; diff --git a/src/SecurityDetails/Converters/index.ts b/src/SecurityDetails/Converters/index.ts new file mode 100644 index 0000000..6095bda --- /dev/null +++ b/src/SecurityDetails/Converters/index.ts @@ -0,0 +1,59 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * + * */ + + +'use strict'; + +import TrailingPerformanceConverter from './TrailingPerformanceConverter'; +import AssetAllocationsConverter from './AssetAllocationsConverter'; +import RegionalExposureConverter from './RegionalExposureConverter'; +import GlobalStockSectorBreakdownConverter from './GlobalStockSectorBreakdownConverter'; +import CountryExposureConverter from './CountryExposureConverter'; + + +/* * + * + * Imports + * + * */ + + + + +/* * + * + * Exports + * + * */ + + +export * from './TrailingPerformanceConverter'; +export * from './AssetAllocationsConverter'; +export * from './RegionalExposureConverter'; +export * from './GlobalStockSectorBreakdownConverter'; +export * from './CountryExposureConverter'; + +/* * + * + * Default Export + * + * */ + + +export default { + TrailingPerformanceConverter, + AssetAllocationsConverter, + RegionalExposureConverter, + GlobalStockSectorBreakdownConverter, + CountryExposureConverter +}; diff --git a/src/SecurityDetails/SecurityDetailsConnector.ts b/src/SecurityDetails/SecurityDetailsConnector.ts index 0ab6603..a50fa63 100644 --- a/src/SecurityDetails/SecurityDetailsConnector.ts +++ b/src/SecurityDetails/SecurityDetailsConnector.ts @@ -23,14 +23,20 @@ import External from '../Shared/External'; -import SecurityDetailsConverter from './SecurityDetailsConverter'; +import { + AssetAllocationsConverter, + TrailingPerformanceConverter, + RegionalExposureConverter, + GlobalStockSectorBreakdownConverter, + CountryExposureConverter +} from './Converters'; import SecurityDetailsOptions, { SecurityDetailsMetadata } from './SecurityDetailsOptions'; import MorningstarAPI from '../Shared/MorningstarAPI'; import MorningstarConnector from '../Shared/MorningstarConnector'; import MorningstarURL from '../Shared/MorningstarURL'; - +import SecurityDetailsConverter from './SecurityDetailsConverter'; /* * * @@ -54,12 +60,43 @@ export class SecurityDetailsConnector extends MorningstarConnector { ) { super(options); - this.converter = new SecurityDetailsConverter(options.converter); + switch (options.converter?.type) { + case 'TrailingPerformance': + default: + this.converter = new TrailingPerformanceConverter({ + ...options.converter + }); + break; + + case 'AssetAllocations': + this.converter = new AssetAllocationsConverter({ + ...options.converter + }); + break; + + case 'RegionalExposure': + this.converter = new RegionalExposureConverter({ + ...options.converter + }); + break; + + case 'GlobalStockSectorBreakdown': + this.converter = new GlobalStockSectorBreakdownConverter({ + ...options.converter + }); + break; + + case 'CountryExposure': + this.converter = new CountryExposureConverter({ + ...options.converter + }); + break; + } + this.metadata = this.converter.metadata; this.options = options; } - /* * * * Properties diff --git a/src/SecurityDetails/SecurityDetailsConverter.ts b/src/SecurityDetails/SecurityDetailsConverter.ts index b8de4fc..8d46696 100644 --- a/src/SecurityDetails/SecurityDetailsConverter.ts +++ b/src/SecurityDetails/SecurityDetailsConverter.ts @@ -7,9 +7,7 @@ * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: - * - Sophie Bremer - * - Pawel Lysy - * - Askel Eirik Johansson + * - Eskil Gjerde Sviggum * * */ @@ -24,12 +22,8 @@ * */ -import { - SecurityDetailsConverterOptions, - SecurityDetailsMetadata -} from './SecurityDetailsOptions'; -import SecurityDetailsJSON from './SecurityDetailsJSON'; import MorningstarConverter from '../Shared/MorningstarConverter'; +import { SecurityDetailsConverterOptions, SecurityDetailsMetadata } from './SecurityDetailsOptions'; /* * @@ -39,26 +33,7 @@ import MorningstarConverter from '../Shared/MorningstarConverter'; * */ -export class SecurityDetailsConverter extends MorningstarConverter { - - - /* * - * - * Constructor - * - * */ - - - public constructor ( - options?: SecurityDetailsConverterOptions - ) { - super(options); - - this.metadata = { - columns: {} - }; - } - +export abstract class SecurityDetailsConverter extends MorningstarConverter { /* * * @@ -66,9 +41,7 @@ export class SecurityDetailsConverter extends MorningstarConverter { * * */ - - public readonly metadata: SecurityDetailsMetadata; - + public abstract metadata: SecurityDetailsMetadata; /* * * @@ -76,60 +49,8 @@ export class SecurityDetailsConverter extends MorningstarConverter { * * */ - - public parse ( - options: SecurityDetailsConverterOptions - ): void { - const metadata = this.metadata; - const table = this.table; - const userOptions = { - ...this.options, - ...options - }; - const json = userOptions.json; - - // Validate JSON - - if (!SecurityDetailsJSON.isSecurityDetailsResponse(json)) { - throw new Error('Invalid data'); - } - - // Prepare table - - table.deleteColumns(); - table.setColumn('SecurityDetails_TrailingPerformance_TimePeriod'); - table.setColumn('SecurityDetails_TrailingPerformance_Value'); - - // Add trailing performance to table - - if (json.length) { - - // Update table - - const securityDetails = json[0]; - const trailingPerformanceReturn = securityDetails.TrailingPerformance[0].Return; - - for (let i = 0, iEnd = trailingPerformanceReturn.length; i < iEnd; ++i) { - table.setCell( - 'SecurityDetails_TrailingPerformance_TimePeriod', - i, - trailingPerformanceReturn[i].TimePeriod - ); - table.setCell( - 'SecurityDetails_TrailingPerformance_Value', - i, - trailingPerformanceReturn[i].Value - ); - } - - // Update meta data - - metadata.id = securityDetails.Id; - metadata.isin = securityDetails.Isin; - } - - } - + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public parse (options: SecurityDetailsConverterOptions) {} } diff --git a/src/SecurityDetails/SecurityDetailsJSON.ts b/src/SecurityDetails/SecurityDetailsJSON.ts index e178158..0d079dd 100644 --- a/src/SecurityDetails/SecurityDetailsJSON.ts +++ b/src/SecurityDetails/SecurityDetailsJSON.ts @@ -42,11 +42,49 @@ namespace SecurityDetailsJSON { Domicile: string; Currency: CurrencyType; TrailingPerformance: SecurityDetailsTrailingPerformance[]; + Portfolios:PortfoliosType[] Type: string; CurrencyId: string; Date: string; } + export type PortfoliosType = { + AssetAllocations: AssetAllocationType[] + RegionalExposure: RegionalExposureType[] + GlobalStockSectorBreakdown: GlobalStockSectorBreakdownType[] + CountryExposure: CountryExposureType[] + }; + + export type AssetAllocationType = { + BreakdownValues: BreakDownValues[] + SalePosition: string + Type: string + }; + + export type BreakDownValues = { + Value: number; + Type: string; + }; + + export type RegionalExposureType = { + BreakdownValues: BreakDownValues[] + SalePosition: string + NotClassified: number + }; + + export type GlobalStockSectorBreakdownType = { + BreakdownValues: BreakDownValues[] + SalePosition: string + NotClassified: number + }; + + export type CountryExposureType = { + BreakdownValues: BreakDownValues[] + SalePosition: string + NotClassified: number + Type: string + }; + interface SecurityDetailsTrailingPerformance { ReturnType: string; Return: SecurityDetailsReturn[]; diff --git a/src/SecurityDetails/SecurityDetailsOptions.ts b/src/SecurityDetails/SecurityDetailsOptions.ts index 32ce410..7fcb6d2 100644 --- a/src/SecurityDetails/SecurityDetailsOptions.ts +++ b/src/SecurityDetails/SecurityDetailsOptions.ts @@ -35,12 +35,21 @@ import type { * * API Options * - * + * * */ export interface SecurityDetailsConverterOptions extends MorningstarConverterOptions { - // Nothing to add yet + + /** + * Specifies the type of data to retrieve for the security details. + * Available types: 'TrailingPerformance', 'AssetAllocations', + * 'RegionalExposure', 'GlobalStockSectorBreakdown', 'CountryExposure'. + * + * @default 'TrailingPerformance' + */ + type?: SecurityDetailsConverterType + } @@ -59,8 +68,17 @@ export interface SecurityDetailsMetadata extends MorningstarMetadata { export interface SecurityDetailsOptions extends MorningstarOptions { security?: MorningstarSecurityOptions, viewId?: string, + converter?: SecurityDetailsConverterOptions } +export type SecurityDetailsConverterType = ( + | 'TrailingPerformance' + | 'AssetAllocations' + | 'RegionalExposure' + | 'GlobalStockSectorBreakdown' + | 'CountryExposure' + ); + /* * * diff --git a/src/SecurityDetails/index.ts b/src/SecurityDetails/index.ts index f1c78bc..da42c80 100644 --- a/src/SecurityDetails/index.ts +++ b/src/SecurityDetails/index.ts @@ -33,8 +33,9 @@ import SecurityDetailsConnector from './SecurityDetailsConnector'; export * from './SecurityDetailsConnector'; -export * from './SecurityDetailsConverter'; export * from './SecurityDetailsOptions'; +export * from './SecurityDetailsConverter'; +export * from './Converters'; /* * diff --git a/src/api.d.ts b/src/api.d.ts index b9d0844..7ac84df 100644 --- a/src/api.d.ts +++ b/src/api.d.ts @@ -22,6 +22,7 @@ import GoalAnalysisOptions from './GoalAnalysis/GoalAnalysisOptions'; import RiskScoreOptions from './RiskScore/RiskScoreOptions'; import RNANewsOptions from './RNANews/RNANewsOptions'; +import SecurityDetailsOptions from './SecurityDetails/SecurityDetailsOptions'; import TimeSeriesOptions from './TimeSeries/TimeSeriesOptions'; import XRayOptions from './XRay/XRayOptions'; import InvestmentScreenerOptions from './Screeners/InvestmentScreener/InvestmentScreenerOptions'; @@ -40,6 +41,7 @@ interface Options { GoalAnalysis: GoalAnalysisOptions; RiskScore: RiskScoreOptions; RNANews: RNANewsOptions; + SecurityDetails: SecurityDetailsOptions; TimeSeries: TimeSeriesOptions; XRay: XRayOptions; InvestmentScreener: InvestmentScreenerOptions; diff --git a/tests/SecurityDetails/SecurityDetailsConnector.test.ts b/tests/SecurityDetails/SecurityDetailsConnector.test.ts index c817dc0..0070972 100644 --- a/tests/SecurityDetails/SecurityDetailsConnector.test.ts +++ b/tests/SecurityDetails/SecurityDetailsConnector.test.ts @@ -47,6 +47,102 @@ export async function securityDetailsLoad ( id: 'F0GBR050DD', isin: 'GB0004460357' } - ) + ); +} + +export async function assetAllocationsLoad ( + api: MC.Shared.MorningstarAPIOptions +) { + const connector = new MC.SecurityDetailsConnector({ + api, + security: { + id: 'F0GBR050DD', + idType: 'MSID' + }, + converter: { + type: 'AssetAllocations' + } + }); + + await connector.load(); + + Assert.deepStrictEqual( + connector.table.getColumnNames(), + [ + 'AssetAllocations_Type', + 'AssetAllocations_MorningstarEUR3_L', + 'AssetAllocations_MorningstarEUR3_S', + 'AssetAllocations_MorningstarEUR3_N' + ], + 'Asset allocations table should exist of expected columns.' + ); +} +export async function regionalExposureLoad ( + api: MC.Shared.MorningstarAPIOptions +) { + const connector = new MC.SecurityDetailsConnector({ + api, + security: { + id: 'F0GBR050DD', + idType: 'MSID' + }, + converter: { + type: 'RegionalExposure' + } + }); + + await connector.load(); + + Assert.deepStrictEqual( + connector.table.getColumnNames()[0], + 'RegionalExposure_Type', + 'Regional exposure table should exist of expected columns.' + ); +} + +export async function globalStockSectorBreakdownLoad ( + api: MC.Shared.MorningstarAPIOptions +) { + const connector = new MC.SecurityDetailsConnector({ + api, + security: { + id: 'F0GBR050DD', + idType: 'MSID' + }, + converter: { + type: 'GlobalStockSectorBreakdown' + } + }); + + await connector.load(); + + Assert.deepStrictEqual( + connector.table.getColumnNames()[0], + 'GlobalStockSectorBreakdown_Type', + 'Global stock sector breakdown table should exist of expected columns.' + ); +} + +export async function countryExposureLoad ( + api: MC.Shared.MorningstarAPIOptions +) { + const connector = new MC.SecurityDetailsConnector({ + api, + security: { + id: 'F0GBR050DD', + idType: 'MSID' + }, + converter: { + type: 'CountryExposure' + } + }); + + await connector.load(); + + Assert.deepStrictEqual( + connector.table.getColumnNames()[0], + 'CountryExposure_Type', + 'Country exposure table should exist of expected columns.' + ); }