-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implemented investment screener. * Implemented InvestmentScreenerConnector together with the demo. * Fixed demo and changed the eslint config. * Implemented investment screener tests. * Fixed tests. * Fixed test. * Fixed linting errors. * Added investment screener demo to list of demos. * Fixed typo * Added docs for Investment Screener. * Added comments to demo. * Fixed docs. * Fix typo * Update docs/connectors/morningstar/screeners/investment-screener.md Co-authored-by: Kamil Musiałowski <[email protected]> * Added console warn on error in demo. * Added missing typo fixes. --------- Co-authored-by: Kamil Musiałowski <[email protected]> Co-authored-by: Kamil Musiałowski <[email protected]>
- Loading branch information
1 parent
608f4f8
commit a9ef98d
Showing
15 changed files
with
983 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="stylesheet" href="demo.css" /> | ||
<script src="https://code.highcharts.com/dashboards/datagrid.src.js"></script> | ||
<script src="https://code.highcharts.com/highcharts.src.js"></script> | ||
<script src="https://code.highcharts.com/dashboards/dashboards.src.js"></script> | ||
<script src="../../code/connectors-morningstar.src.js"></script> | ||
|
||
<title>Highcharts Dashboards + Morningstar Portfolio Investment Details</title> | ||
</head> | ||
<body> | ||
<h1>Highcharts Dashboards + Morningstar Portfolio Investment Details</h1> | ||
<p> | ||
Add your Postman environment file from Morningstar to start the demo: | ||
<input type="file" id="postman-json" accept=".json,application/json" /> | ||
</p> | ||
<p id="loading-label" style="display: none;">Loading data…</p> | ||
<div class="filters-row"> | ||
<button class="cell" id='filter-1'>Highly Rated</button> | ||
<button class="cell" id='filter-2'>Top Performer funds</button> | ||
<button class="cell" id='filter-3'>Highly Sustainalble</button> | ||
<button class="cell" id='filter-4'>Low Expenses</button> | ||
</div> | ||
<div> | ||
Current filter: | ||
<span id='current-filter'></span> | ||
</div> | ||
<div class="row" id="container"> | ||
<div class="cell" id="dashboard-col-1"></div> | ||
</div> | ||
<span id='total'>-</span> | ||
<span id='page'>-</span> | ||
<span id='total-pages'>-</span> | ||
<script src="./demo.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
const loadingLabel = document.getElementById('loading-label'); | ||
|
||
function displayInvestmentScreener (postmanJSON) { | ||
const secIds = [ | ||
'secId', | ||
'tenforeId', | ||
'name', | ||
'closePrice', | ||
'ongoingCharge', | ||
'initialPurchase', | ||
'maxFrontEndLoad', | ||
'analystRatingScale', | ||
'average12MonthCarbonRiskScore', | ||
'investmentType', | ||
'holdingTypeId', | ||
'universe' | ||
]; | ||
|
||
const columns = secIds.map(id => ({ | ||
id: `InvestmentScreener_${id}`, | ||
header: { | ||
format: id | ||
} | ||
})); | ||
|
||
const board = Dashboards.board('container', { | ||
dataPool: { | ||
connectors: [ | ||
{ | ||
id: 'investment-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: 'investment-screener' | ||
}, | ||
type: 'DataGrid', | ||
|
||
dataGridOptions: { | ||
editable: false, | ||
columns | ||
}, | ||
title: 'Investment Screener' | ||
} | ||
] | ||
}); | ||
|
||
board.dataPool.getConnector('investment-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)}`; | ||
}); | ||
|
||
/** | ||
* Add filter to a connector | ||
* | ||
* @param {InvestmentScreenerFilter[]} filters | ||
*/ | ||
function setFilter (filters) { | ||
loadingLabel.style.display = 'block'; | ||
board.dataPool.getConnector('investment-screener').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', e => { | ||
e.target.classList.add('button-active'); | ||
document.getElementById('current-filter').innerHTML = | ||
e.target.innerHTML; | ||
// Create a filter that will check if the star rating is equal to 5 | ||
// and if the analyst rating is equal to 5 | ||
setFilter([ | ||
{ | ||
dataPointId: 'StarRatingM255', | ||
comparatorCode: 'IN', | ||
value: 5 | ||
}, | ||
{ | ||
dataPointId: 'AnalystRatingScale', | ||
comparatorCode: 'IN', | ||
value: 5 | ||
} | ||
]); | ||
}); | ||
|
||
document.getElementById('filter-2').addEventListener('click', e => { | ||
document.getElementById('current-filter').innerHTML = | ||
e.target.innerHTML; | ||
// Create a filter that will check if the GBR return is between | ||
// 39 and 60 | ||
setFilter([ | ||
{ | ||
dataPointId: 'GBRReturnM0', | ||
comparatorCode: 'BTW', | ||
value: '40:60' | ||
} | ||
]); | ||
}); | ||
|
||
document.getElementById('filter-3').addEventListener('click', e => { | ||
document.getElementById('current-filter').innerHTML = | ||
e.target.innerHTML; | ||
// Create a filter that will filter on the Low Carbon Designation | ||
// and Carbon Risk Score. These Investments are considered highly | ||
// sustainable | ||
setFilter([ | ||
{ | ||
dataPointId: 'LowCarbonDesignation', | ||
comparatorCode: 'IN', | ||
value: 'TRUE' | ||
}, | ||
{ | ||
dataPointId: 'CarbonRiskScore', | ||
comparatorCode: 'EQ', | ||
value: 0 | ||
}, | ||
{ | ||
dataPointId: 'SustainabilityRank', | ||
comparatorCode: 'IN', | ||
value: 5 | ||
} | ||
]); | ||
}); | ||
|
||
document.getElementById('filter-4').addEventListener('click', e => { | ||
document.getElementById('current-filter').innerHTML = | ||
e.target.innerHTML; | ||
// Create a filter that will check if the investment is considered | ||
// as "low expenses" | ||
setFilter([ | ||
{ | ||
dataPointId: 'StarRatingM255', | ||
comparatorCode: 'IN', | ||
value: 5 | ||
}, | ||
{ dataPointId: 'OngoingCharge', comparatorCode: 'LT', value: 0.5 }, | ||
{ | ||
dataPointId: 'initialPurchase', | ||
comparatorCode: 'LT', | ||
value: 500000000 | ||
}, | ||
{ dataPointId: 'maxFrontEndLoad', comparatorCode: 'LT', value: 5 }, | ||
{ dataPointId: 'maxDeferredLoad', comparatorCode: 'LT', value: 5 } | ||
]); | ||
}); | ||
} | ||
|
||
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…'; | ||
|
||
displayInvestmentScreener(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 (Connectors.Morningstar.isPostmanEnvironmentJSON(fileJSON)) { | ||
break; | ||
} | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn(error); | ||
} | ||
} | ||
|
||
return fileJSON; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.