Skip to content
Browse files

Initial commit.

  • Loading branch information...
0 parents commit 0e4738b43e99ec9094cbdfbb05f0b4785dc021bf @albertosantini committed Sep 28, 2011
1 .gitignore
@@ -0,0 +1 @@
+node_modules/
3 .npmignore
@@ -0,0 +1,3 @@
+.git*
+examples/
+
5 HISTORY.markdown
@@ -0,0 +1,5 @@
+1.0.0 / 2011-09-28
+===================
+
+* Initial release.
+
23 LICENSE
@@ -0,0 +1,23 @@
+(The MIT License)
+
+Copyright (c) 2011 Alberto Santini <albertosantini@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
169 README.markdown
@@ -0,0 +1,169 @@
+CONPA
+=====
+
+This module contains an asset allocation application.
+
+The entry point is:
+
+http://my.address.com:myport/ConPA/ConPA.html
+
+It depends how the [express](http://github.com/visionmedia/express) instance
+is configured.
+
+For instance,
+
+http://localhost:8001/ConPA/ConPA.html
+
+See [welcome](http://www.youtube.com/watch?v=ia_UVHtuBTM) and
+[tutorial](http://www.youtube.com/watch?v=xIwbc6lQzNk) videos.
+
+There are two tabs: basket and dashboard.
+
+In basket tab firstly you should build a basket of assets and, then, get the
+optimal weights for each assets.
+
+"Add Asset" field is a autocomplete field: if you start to type some character,
+it will open a list of products, matching with the string.
+
+You have to select the product and press enter (or click the left button of the
+mouse) to insert the product in the basket. The first product of the list is
+automatically selected.
+
+You have to insert minimum three assets (and a maximum of ten) to get the
+weights of the optimal portfolio. Finally, you could the constraints for each
+asset and, after pushing the button "Get Optimal Portfolio", please, wait a few
+seconds to get the result: we should retrieve the historical prices,
+create the covariance matrix, set the constraints and optimize a la Markowitz
+the asset allocation.
+
+The optimized portfolio is identified with an id: hoovering on id, a tooltip
+displays performance, risk (the standard deviation of the portfolio returns)
+and return of the portfolio.
+
+The covariance matrix is built using weekly returns since two years before the
+reference date.
+
+You can set the annual target return. The default is 0%: it means the target
+return is calculated as the mean of the asset returns.
+
+If you (left) click on the symbol of the asset in the basket, you can see the
+key statistics and implied volatility graphs for that asset.
+
+If you want to check the figures, you can click on the script icon and an
+R script, corresponding to the optimal portfolio, is displayed.
+
+In dashboard tab there are stats of all portfolios created by the users:
+last created portfolios, best/worst performing porfolios, high/low risk profile
+portfolios and high/low return profile portfolios.
+
+Example
+========
+
+In the directory example, there is a simple express configuration.
+
+Installation
+============
+
+To install with [npm](http://github.com/isaacs/npm):
+
+ npm install conpa
+
+Tested with node 0.4.12.
+
+Notes
+=====
+
+Before running ConPA, you need to configure the details of the persistence
+system. The portfolios are saved on a CouchDB instance. The configuration
+allows a live and testing system, you don't need to change the source code
+when the app is delivered to a live system.
+
+Optionally, you may add a Rserve configuration (local or remote). If Rserve is
+not configured, ConPA uses a javascript implementation for the optimization.
+
+For instance,
+
+ crm: {
+ liveDomain: "x.x.x",
+ liveUrl: "http://key1:pass1@p.c.com",
+ liveDb: "myLiveDBName",
+ testingUrl: "http://key2:pass2@p.c.com",
+ testingDb: "myTestingDBName",
+ design: "designName",
+ },
+ rserve: {
+ host: "myHost",
+ port: "myPort",
+ user: "myUser",
+ password: "myPassword"
+ }
+
+ConPA [Sequence Diagram](http://www.websequencediagrams.com/cgi-bin/cdraw?lz=Q29uUEEtPk5vZGVKUzogbmF2aWdhdGlvbgphbHQgABkFIGJhY2tlbmQgd2l0aCBqcyBjYWxjCiAgICBub3RlIG92ZXIgADoGABAFABMGZGUtY29ucGEgAAYOZmluYW5jZQAbDnF1YWRwcm9nAE8FZW5kAFMFCmVsc2UAbRRSIGNsb3VkAF4jcmlvIChSc2VydmUgYWRhcHRlcikAVg4gICAAgTQHLT4AUQVudW1iZXJzLmNvbTogZ2V0IG9wdGltYWwgcG9ydGZvbGlvABEjcGVyZm9ybWFuY2VzAEAjaW1wbGllZCB2b2xhdGlsaXR5AIJODwCBChAAgl4JAIFWBgCCbQl0c2VyaQBsBwAaBUpTT05JTwCBYRIAgVsQLQCDdgsAgXwFIGNydW5jaGluZyByZXNwb25zZQplbmQKAIIlBy0-AIQyBToAEwoKCgoKCgo&s=napkin).
+
+The module adds the following routes to the express instance:
+
+- /ConPA/getOptimalPortfolio
+
+- /ConPA/getKeyStatistics
+
+- /ConPA/getImpliedVolatility
+
+- /ConPA/putPortfolioOnCRM
+
+- /ConPA/getLastCreatedPortfolios
+
+- /ConPA/getBestPerformingPortfolios
+
+- /ConPA/getWorstPerformingPortfolios
+
+- /ConPA/getHighProfileRiskPortfolios
+
+- /ConPA/getLowProfileRiskPortfolios
+
+- /ConPA/getHighProfileReturnPortfolios
+
+- /ConPA/getLowProfileReturnPortfolios
+
+- /ConPA/getPortfolioCount
+
+- /ConPA/getMostUsedAssets
+
+Methods
+=======
+
+configure(app, express, config)
+---------
+
+It creates an optimal portfolio. If *config* is defined, the method call a
+Rserve instance, otherwise a native implementation is used.
+
+- *app* an instance of the express.HTTPServer, created with createServer().
+
+- *express* an instance of the express module.
+
+- *config*
+
+ - *crm*
+
+ - *liveDomain* url for the live domain.
+
+ - *liveUrl* url for the live instance, eventually with the credentials.
+
+ - *liveDb* name of live instance.
+
+ - *testingUrl* url for the testing instance, eventually with the credentials.
+
+ - *testingDb* name of testing instance.
+
+ - *design* name of design document.
+
+ - *rserve*
+
+ - *host* hostname or ip address of R instance.
+
+ - *port* port of Rserve instance.
+
+ - *user* username for remote connection of Rserve instance.
+
+ - *password* password for remote connection of Rserve instance.
+
39 examples/server_example.js
@@ -0,0 +1,39 @@
+/*jslint node:true, sloppy:true, nomen:true */
+
+var express = require('express'),
+ conpa = require('conpa');
+
+var app = express.createServer();
+
+app.configure(function () {
+ app.use(express.favicon(__dirname + '/favicon.ico'));
+ app.use(express["static"].apply(null, [__dirname + '/public']));
+ app.set('views', __dirname + '/views');
+ app.set('view engine', 'jade');
+ app.set('view options', {layout: false});
+ app.use(express.bodyParser());
+ app.use(express.errorHandler());
+ app.use(express.cookieParser());
+ app.use(express.session({
+ secret: "no more secrets",
+ cookie: {
+ maxAge: 60000
+ }
+ }));
+});
+
+app.listen(process.env.PORT || 8001);
+
+conpa.configure(app, express, {
+ crm: {
+ liveDomain: "x.y.z",
+ liveUrl: "http://user1:pass1" +
+ "@a.b.c",
+ liveDb: "conpa",
+ testingUrl: "http://user2:pass2" +
+ "@a.b.c",
+ testingDb: "staging",
+ design: "ConPA"
+ }
+});
+
1 index.js
@@ -0,0 +1 @@
+module.exports = require('./lib/conpa');
100 lib/conpa.js
@@ -0,0 +1,100 @@
+/*jslint node:true, sloppy:true, unparam:true, nomen:true */
+
+var finance = require("finance");
+
+function configure(app, express, config) {
+ app.configure(function () {
+ app.use(express["static"].apply(null, [__dirname + '/public']));
+ });
+
+ app.post('/ConPA/getOptimalPortfolio', function (req, res) {
+ if (config.rserve) {
+ finance.portfolio.getOptimalPortfolio(req.body, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ }, config.rserve);
+ } else {
+ finance.portfolio.getOptimalPortfolio(req.body, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ }
+ });
+
+ app.post('/ConPA/getKeyStatistics', function (req, res) {
+ finance.quotes.getKeyStatistics(req.body, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+
+ app.post('/ConPA/getImpliedVolatility', function (req, res) {
+ finance.volatility.getImpliedVolatility(req.body, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+
+ finance.crm.configure(config.crm);
+
+ app.post('/ConPA/putPortfolioOnCRM', function (req, res) {
+ finance.crm.putPortfolioOnCRM(req.body, function (data) {
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getLastCreatedPortfolios', function (req, res) {
+ finance.crm.getLastCreatedPortfolios(5, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getBestPerformingPortfolios', function (req, res) {
+ finance.crm.getBestPerformingPortfolios(3, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getWorstPerformingPortfolios', function (req, res) {
+ finance.crm.getWorstPerformingPortfolios(3, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getHighProfileRiskPortfolios', function (req, res) {
+ finance.crm.getHighProfileRiskPortfolios(3, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getLowProfileRiskPortfolios', function (req, res) {
+ finance.crm.getLowProfileRiskPortfolios(3, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getHighProfileReturnPortfolios', function (req, res) {
+ finance.crm.getHighProfileReturnPortfolios(3, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getLowProfileReturnPortfolios', function (req, res) {
+ finance.crm.getLowProfileReturnPortfolios(3, function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getPortfolioCount', function (req, res) {
+ finance.crm.getPortfolioCount(function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+ app.get('/ConPA/getMostUsedAssets', function (req, res) {
+ finance.crm.getMostUsedAssets(function (data) {
+ res.contentType('application/json');
+ res.send(data);
+ });
+ });
+}
+exports.configure = configure;
125 lib/public/ConPA/ConPA.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+<head>
+ <title>ConPA</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+
+ <meta name="author" content="pro-plus.biz">
+ <meta name="description" content="Consulting Plus Advisoring">
+ <meta name="keywords" content="PROPlus, ConPA, advisory">
+
+ <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?2.9.0/build/reset-fonts-grids/reset-fonts-grids.css&2.9.0/build/base/base-min.css&2.9.0/build/assets/skins/sam/skin.css">
+ <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?3.4.0/build/widget-base/assets/skins/sam/widget-base.css&3.4.0/build/tabview/assets/skins/sam/tabview.css&3.4.0/build/autocomplete-list/assets/skins/sam/autocomplete-list.css&3.4.0/build/datatable-base/assets/skins/sam/datatable-base.css&3.4.0/build/widget-stack/assets/skins/sam/widget-stack.css">
+ <link rel="stylesheet" type="text/css" href="css/ConPA.css">
+</head>
+
+<body id="body" class="yui3-skin-sam yui-skin-sam">
+
+<div id="doc" class="yui-t3">
+
+ <div id="hd">
+ <span>Con</span>PA?<span> CON</span>sulting <span>P</span>lus <span>A</span>pp :)
+ </div>
+
+ <div id="bd">
+
+ <div id="tab_Main">
+ <ul>
+ <li><a href="#tab_Advisory">Basket</a></li>
+ <li><a href="#tab_Dashboard">Dashboard</a></li>
+ </ul>
+
+ <div>
+
+ <div id="tab_Advisory">
+
+ <div id="ysearch">
+ <label>Add Asset:</label>
+ <input id="ysearchinput" type="text">
+ <button type="button" id="mostUsedSymbols">Most Used Symbols</button>
+ </div>
+
+ <br />
+
+ <div id="basket"></div>
+
+ <br />
+
+ <button id="getOptimalPortfolio" type="button">Get Optimal Portfolio</button>
+ <span id="referenceDate"></span>
+ <span id="targetReturn"></span>
+ <img id="help" width="16" height="16" src="images/help.png" alt="Help"/>
+ <img id="getRScript" width="16" height="16" src="images/script.png" alt="Get R Script"/>
+ <span id="portfolioId"></span>
+
+ </div>
+
+ <div id="tab_Dashboard">
+ <div id="docDashboard">
+
+ <div id="bdDashboard">
+
+ <div class="yui-g">
+ <div id="lastCreatedPortfolios"></div>
+ </div>
+
+ <div class="yui-g">
+ <div class="yui-u first">
+ <div id="bestPerformingPortfolios"></div>
+ </div>
+ <div class="yui-u">
+ <div id="worstPerformingPortfolios"></div>
+ </div>
+ </div>
+
+ <div class="yui-g">
+ <div class="yui-u first">
+ <div id="highProfileRiskPortfolios"></div>
+ </div>
+ <div class="yui-u">
+ <div id="lowProfileRiskPortfolios"></div>
+ </div>
+ </div>
+
+ <div class="yui-g">
+ <div class="yui-u first">
+ <div id="highProfileReturnPortfolios"></div>
+ </div>
+ <div class="yui-u">
+ <div id="lowProfileReturnPortfolios"></div>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div id="ft">
+ P0wer3d by <a href="https://no.de/">Joyent Node</a>,
+ Relax3d by <a href="http://cloudant.com">Cloudant</a> and
+ All0cat3d by <a href="http://www.pro-plus.biz">PRO+</a>
+ <a href="http://blog.pro-plus.biz/rss.xml">
+ <img src="images/rss.png" width="16" height="16" alt="Blog">
+ </a>
+ </div>
+
+</div>
+
+<script src="http://yui.yahooapis.com/3.4.0/build/yui/yui-min.js"></script>
+<script src="js/ConPA.js"></script>
+
+<script type="text/javascript">
+(function(d,n){var e =d.createElement(n),s=d.getElementsByTagName(n)[0];e.async=true;e.src="http://commondatastorage.googleapis.com/client/observer.js#{observer:'4e68743a0af5c15c11007968'}";s.parentNode.insertBefore(e,s);})(document,"script");
+</script>
+
+</body>
+</html>
160 lib/public/ConPA/css/ConPA.css
@@ -0,0 +1,160 @@
+@charset "UTF-8";
+
+/* Layout */
+.yui3-skin-sam .yui3-tabview-panel {
+ background: #EEEEEE;
+}
+
+#hd {
+ border: 1px;
+ font-size: 256%;
+ font-weight: bold;
+ text-align: right;
+}
+
+#hd span {
+ color: #CC000E;
+}
+
+#hd img {
+ vertical-align: middle;
+}
+
+#ft {
+ text-align: right;
+ font-style: italic;
+ font-size: 10px;
+}
+
+/* DataTable */
+.yui3-skin-sam .yui3-datatable caption {
+ padding: 0em 0px;
+}
+.yui3-skin-sam .yui3-datatable table {
+ width: 100%;
+ margin-top: 5px;
+}
+.yui3-skin-sam .yui3-datatable td {
+ text-align: right;
+}
+.yui3-skin-sam tr.yui3-datatable-odd,
+.yui3-skin-sam tr.yui3-datatable-even {
+ background-color: #fff;
+}
+
+.yui-skin-sam .yui-dt table {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ width:100%;
+}
+.yui-skin-sam tr.yui-dt-odd,
+.yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
+.yui-skin-sam tr.yui-dt-odd td.yui-dt-desc,
+.yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
+.yui-skin-sam tr.yui-dt-even td.yui-dt-desc {
+ background-color: #fff;
+}
+
+/* Asset Search */
+#ysearch {
+ text-align:left;
+ z-index: 4;
+}
+#ysearchinput {
+ position: static;
+ height: 1.5em;
+ vertical-align: middle;
+ width: 25em;
+}
+
+.yui3-aclist {
+ width: 25em;
+}
+.yui3-skin-sam .yui3-aclist-content {
+ background-color: #E1E7F3;
+ color: #666;
+ font-style: normal;
+ font-weight: bold;
+ font-size: 10px;
+ text-align: left;
+}
+
+#mostUsedSymbols {
+ vertical-align: middle;
+}
+
+/* Target Return Slider */
+#slider-bg {
+ position: relative;
+ background: url(../images/bg-fader.gif) 7px 12px no-repeat;
+ height: 28px;
+ width: 117px;
+
+}
+
+#slider-thumb {
+ cursor: default;
+ position: absolute;
+ top: 4px;
+
+}
+
+#targetreturnbutton-currenttargetreturn {
+ width: 9em;
+ font-style: normal;
+ display: block;
+ text-align: left;
+}
+
+/* Key Statistics Panel */
+#formKeyStatistics {
+ font-size: 10px;
+ height: 411px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+#formKeyStatistics div.label {
+ color: #505050;
+ float: left;
+ margin: 0 0.2em 0 0;
+ text-align: right;
+ width: 190px;
+}
+
+#formKeyStatistics div.value {
+ float: left;
+}
+
+#layoutKeyStatistics #impliedVolHeader {
+ font-size: 10px;
+ margin: 0 0 0 2em;
+}
+
+/* Graph Panel */
+#graphPanel .bd {
+ background-color: white;
+}
+
+/* Help Icon */
+#help {
+ cursor: pointer;
+}
+
+/* R Script Icon */
+#getRScript {
+ cursor: pointer;
+}
+
+/* R Script Panel */
+#scriptPanel .bd {
+ font-size: 10px;
+ overflow-y: scroll;
+ overflow-x: scroll;
+}
+
+/* Portfolio Id */
+#portfolioId {
+ float: right;
+}
+
85 lib/public/ConPA/docs/ConPA_help.html
@@ -0,0 +1,85 @@
+<h2>Overview</h2>
+<p>Welcome to the beta of our asset allocation application.</p>
+<p>There are two tabs: basket and dashboard.</p>
+<p>In basket tab firstly you should build a basket of assets and, then, get the
+optimal weights for each assets.</p>
+<p>&quot;Add Asset&quot; field is a autocomplete field: if you start to type
+some character, it will open a list of products, matching with the string.</p>
+<p>You have to select the product and press enter (or click the left button of
+the mouse) to insert the product in the basket. The first product of the list is
+automatically selected.</p>
+<p>You have to insert minimum three assets (and a maximum of ten) to get the
+weights of the optimal portfolio. Finally, you could the <b>constraints</b> for
+each asset and, after pushing the button &quot;Get Optimal Portfolio&quot;,
+please, wait a few seconds to get the result: we should retrieve the
+historical prices, create the covariance matrix, set the constraints and
+<b>optimize a la Markowitz</b> the asset allocation.</p>
+<p>The optimized portfolio is identified with an id: hoovering on id, a tooltip
+displays performance, risk (the standard deviation of the portfolio returns) and
+return of the portfolio.</p>
+<p>The covariance matrix is built using weekly returns since two years before
+the reference date.</p>
+<p>You can set the annual <b>target return</b>. The default is 0%: it means the
+target return is calculated as the mean of the asset returns.</p>
+<p>If you (left) click on the symbol of the asset in the basket, you can see the
+<b>key statistics</b> and <b>implied volatility</b> graphs for that asset.</p>
+<p>If you want to check the figures, you can click on the script icon and an R
+script, corresponding to the optimal portfolio, is displayed.
+</p>
+<p>In dashboard tab there are stats of all portfolios created by the users:
+<b>last created</b> portfolios, <b>best/worst performing</b> porfolios,
+<b>high/low risk profile</b> portfolios and <b>high/low return profile</b>
+portfolios.</p>
+<p>Feel free to ask information or send your comments about the first asset
+allocation optimization written in javascript:
+<a href="http://blog.pro-plus.biz/">PROplus blog</a>.</p>
+
+<h2>Known issues</h2>
+<p>If the result cannot be displayed and you catch an error alert not listed in
+the section below, please, retry to get the optimal portfolio. There could be a
+network timeout during the dowloading of the historical time series or the
+optimization process. If the error is not erratic, please send us by email the
+information about the costituents of the basket, reference date and target
+return.</p>
+
+<h2>Alert error messages</h2>
+<p>&quot;Historical quote data is unavailable: {asset}&quot; - the historical
+prices of the asset is not found.</p>
+<p>&quot;Historical quote data is unavailable: {asset} ({length}) - {asset}
+({length})&quot; - there is a problem with the length of the time series; you
+should exclude the shorter asset because the length of the historical prices is
+not the same of the other assets.</p>
+<p>&quot;constraints are inconsistent, no solution!&quot; - with the given
+constraints or target return there is no solution.</p>
+<p>&quot;matrix D in quadratic function is not positive definite!&quot; - the
+covariance matrix is not positive definite: there is a problem with the return
+time series, hence with the historical prices. Maybe there is the same asset
+twice.</p>
+<p>&quot;{getKeyStatistics or getOptimalPortfolio}: {failed or timed out}&quot;
+- there is a network problem.</p>
+
+<h2>References</h2>
+<p>We have been using <a href="http://www.r-project.org/">R project</a> to check
+our figures (quadprog, tseries, PerformanceAnalytics and Rmetrics packages). R
+is a language and environment for statistical computing and graphics. R provides
+a wide variety of statistical (linear and nonlinear modelling, classical
+statistical tests, time series anlysis, classification, clustering, ...) and
+graphical techniques, and is highly extensible.</p>
+
+<h2>Disclaimer</h2>
+<p>The information on this site is intended to be useful and informative.
+PROplus will use reasonable care to ensure that information is accurate at the
+time it is added to the site.</p>
+<p>However, please note that PROplus cannot guarantee that the information is
+accurate and it shall not be liable for any losses or damage that anyone may
+suffer as a result of relying on this information. The information may be
+changed by PROplus at any time.</p>
+<p>Please also note that the contents of this website do not constitute an
+invitation to any investment.</p>
+
+<h2>Credits</h2>
+<a href="http://finance.yahoo.com/">Yahoo! Finance</a> for providing stock
+quotes.
+
+<h2>Motto</h2>
+Agnosco veteris vestigia flammae.
BIN lib/public/ConPA/images/PROplus_Logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/add.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/beta.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/bg-fader.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/bullet_arrow_down.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/bullet_arrow_up.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/cancel.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/delete.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/exclamation.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/help.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/rel_interstitial_loading.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/rss.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/script.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/sprite-menu.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/thumb-n.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN lib/public/ConPA/images/tick.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,488 lib/public/ConPA/js/ConPA.js
@@ -0,0 +1,1488 @@
+/*jslint unparam: true, sloppy: true */
+/*global YUI, YAHOO:true */
+
+YUI({
+ 'yui2': '2.9.0',
+ '2in3': '4',
+ 'ignore': [
+ 'yui2-skin-sam-container',
+ 'yui2-skin-sam-menu',
+ 'yui2-skin-sam-button',
+ 'yui2-skin-sam-calendar',
+ 'yui2-skin-sam-datatable',
+ 'yui2-skin-sam-layout',
+ 'yui2-skin-sam-slider',
+ 'skin-sam-widget-base',
+ 'skin-sam-tabview',
+ 'skin-sam-autocomplete-list',
+ 'skin-sam-datatable-base',
+ 'skin-sam-widget-stack'
+ ],
+ 'groups': {
+ 'js': {
+ 'base': 'js/',
+ 'modules': {
+ 'conpa-mostusedsymbols': {
+ path: 'ConPAMostUsedSymbols/ConPAMostUsedSymbols.js'
+ },
+ 'conpa-assetsearch': {
+ path: 'ConPAAssetSearch/ConPAAssetSearch.js',
+ 'requires': [
+ 'yui2-utilities',
+ 'yui2-datasource',
+ 'yui2-container',
+ 'yui2-menu',
+ 'yui2-button',
+ 'yui2-calendar',
+ 'yui2-json',
+ 'yui2-swf',
+ 'yui2-charts',
+ 'yui2-datatable',
+ 'yui2-layout',
+ 'yui2-slider',
+ 'node-base',
+ 'io-base',
+ 'querystring-stringify-simple',
+ 'get',
+ "tabview",
+ "datasource-get",
+ "datasource-jsonschema",
+ "autocomplete",
+ "datasource-local",
+ "datasource-jsonschema",
+ "datatable-datasource",
+ "charts",
+ "cookie"
+ ]
+ }
+ }
+ }
+ }
+}).use('conpa-mostusedsymbols', 'conpa-assetsearch', function (Y) {
+ YAHOO = Y.YUI2;
+
+ YAHOO.widget.Chart.SWFURL =
+ "http://yui.yahooapis.com/2.9.0/build/charts/assets/charts.swf";
+
+ YAHOO.namespace("proplus.conpa");
+
+ YAHOO.proplus.conpa.COOKIE = "proplus.conpa.basket";
+
+ YAHOO.proplus.conpa.init = function () {
+ var Dom = YAHOO.util.Dom,
+ now,
+ tabView_Main,
+ myDataTable,
+ waitPanel,
+ pieChartPanel = null,
+ performanceChartPanel = null,
+ scriptPanel = null,
+ scriptHandle = null,
+ pieChart,
+ perfDS,
+ perfChart,
+ assetStatsPanel = null,
+ assetStatsLayout = null,
+ graphPanel = null,
+ oGetOptimalPortfolioButton,
+ oReferenceDateButton,
+ nTargetReturn,
+ lastPortfolioName = "",
+ previousPortfolioName = "";
+
+ now = new Date();
+
+ tabView_Main = new Y.TabView({srcNode: '#tab_Main'});
+ tabView_Main.render();
+
+ function percentage(n) {
+ return YAHOO.util.Number.format(n * 100, {
+ prefix: "",
+ decimalPlaces: 2,
+ decimalSeparator: ".",
+ thousandsSeparator: ","
+ }) + "%";
+ }
+
+ function labelReferenceDateButton(date) {
+ return date.getDate() + " " +
+ YAHOO.widget.Calendar.DEFAULT_CONFIG.MONTHS_SHORT
+ .value[date.getMonth()] + " " +
+ date.getFullYear();
+ }
+
+ function resetOptimalWeigths(dt) {
+ var i, products, nProducts, record, recordData;
+
+ products = dt.getRecordSet();
+ nProducts = products.getLength();
+ for (i = 0; i < nProducts; i = i + 1) {
+ record = products.getRecord(i);
+ recordData = record.getData();
+ recordData.optimalWeight = 0;
+ dt.updateRow(record, recordData);
+ }
+ if (pieChartPanel !== null) {
+ pieChartPanel.hide();
+ }
+ if (performanceChartPanel !== null) {
+ performanceChartPanel.hide();
+ }
+
+ if (assetStatsPanel !== null) {
+ assetStatsPanel.hide();
+ }
+ if (scriptPanel !== null) {
+ scriptPanel.hide();
+ }
+ if (scriptHandle !== null) {
+ Y.detach(scriptHandle);
+ }
+
+ Y.one('#portfolioId').set('innerHTML', "Id: none");
+ }
+
+ function initPanels() {
+ var panel,
+ helpPanel,
+ helpHeader = "Help",
+ helpBody = "<iframe src='docs/ConPA_help.html' width='100%' height='100%' frameborder='0'/>";
+
+ panel = new YAHOO.widget.SimpleDialog('alert', {
+ fixedcenter: true,
+ visible: false,
+ modal: true,
+ width: '256px',
+ constraintoviewport: true,
+ icon: YAHOO.widget.SimpleDialog.ICON_WARN,
+ buttons: [{
+ text: 'OK',
+ handler: function () {
+ panel.hide();
+ },
+ isDefault: true
+ }]
+ });
+ panel.setHeader('Alert');
+ panel.setBody('Notta');
+ panel.render("body");
+
+ YAHOO.proplus.conpa.alert = function (str) {
+ var kl_27 = new YAHOO.util.KeyListener("body", {
+ keys: 27
+ }, {
+ fn: panel.hide,
+ scope: panel,
+ correctScope: true
+ });
+ kl_27.enable();
+
+ panel.setBody(str);
+ panel.cfg.setProperty('icon', YAHOO.widget.SimpleDialog.ICON_WARN);
+ panel.bringToTop();
+ panel.show();
+ };
+
+ waitPanel = new YAHOO.widget.Panel("waitPanel", {
+ width: "240px",
+ fixedcenter: true,
+ close: false,
+ draggable: false,
+ zIndex: 10000,
+ modal: true,
+ visible: false
+ });
+ waitPanel.setHeader("Please wait...");
+ waitPanel.setBody("<img width= \"220\" height=\"19\" " +
+ "src=\"images/rel_interstitial_loading.gif\"/>");
+ waitPanel.render("body");
+
+ helpPanel = new YAHOO.widget.Panel("helpPanel", {
+ visible: false,
+ constraintoviewport: true,
+ draggable: true,
+ close: true,
+ fixedcenter: true,
+ width: '400px',
+ height: '500px',
+ zIndex: 99
+ });
+ helpPanel.setHeader(helpHeader);
+ helpPanel.setBody(helpBody);
+ helpPanel.setFooter("");
+ helpPanel.render(Dom.get('bd'));
+
+ YAHOO.util.Event.addListener("help", "click", function () {
+ helpPanel.setBody(helpBody);
+ helpPanel.bringToTop();
+ helpPanel.show();
+ });
+
+ scriptPanel = new YAHOO.widget.Panel("scriptPanel", {
+ visible: false,
+ modal: false,
+ constraintoviewport: true,
+ draggable: true,
+ close: true,
+ fixedcenter: true,
+ width: '512px',
+ height: '394px'
+ });
+ scriptPanel.setHeader("R Script");
+ scriptPanel.setBody("");
+ scriptPanel.setFooter("");
+ scriptPanel.render(Dom.get('bd'));
+
+ Y.on("click", function () {
+ var scriptRMsg = "#R Script is empty.<br />" +
+ "#Firstly you should get an optimal portfolio.<br />";
+
+ scriptPanel.setBody(scriptRMsg);
+ scriptPanel.bringToTop();
+ scriptPanel.show();
+ }, "#getRScript");
+ }
+
+ function createPieChart() {
+ if (pieChartPanel !== null) {
+ pieChartPanel.hide();
+ pieChartPanel.destroy();
+ }
+
+ pieChartPanel = new YAHOO.widget.Panel("pieChartPanel", {
+ width: "256px",
+ fixedcenter: false,
+ x: Dom.getXY('tab_Advisory')[0] + 10 + 450,
+ y: 300,
+ constraintoviewport: true,
+ close: true,
+ dragOnly: true,
+ modal: false,
+ visible: false
+ });
+
+ pieChartPanel.setHeader("Optimal Portfolio Pie");
+ pieChartPanel.setBody("<div id=\"pieChart\" " +
+ "style=\"height:200px\"></div>");
+ pieChartPanel.render("tab_Advisory");
+ pieChartPanel.subscribe("hide", function () {
+ pieChart.destroy();
+ });
+
+ pieChart = new Y.Chart({
+ categoryKey: "asset",
+ seriesKey: ["weigth"],
+ type: "pie",
+ dataProvider: [],
+ seriesCollection: [
+ {
+ categoryKey: "asset",
+ valueKey: "weigth"
+ }
+ ],
+ tooltip: {
+ markerLabelFunction: function (cItem, vItem, iItem, s, sI) {
+ return cItem.value + " " + percentage(vItem.value);
+ }
+ }
+ });
+ }
+
+ function createPerformanceChart() {
+ var perfSeriesDef, perfTimeAxis;
+
+ if (performanceChartPanel !== null) {
+ performanceChartPanel.hide();
+ performanceChartPanel.destroy();
+ }
+
+ performanceChartPanel = new YAHOO.widget.Panel("performanceChartPanel", {
+ width: "400px",
+ fixedcenter: false,
+ x: Dom.getXY('tab_Advisory')[0] + 10,
+ y: 300,
+ constraintoviewport: true,
+ close: true,
+ dragOnly: true,
+ modal: false,
+ visible: false
+ });
+
+ performanceChartPanel.setHeader("Optimal Portfolio Performance");
+ performanceChartPanel.setBody("<div id=\"performanceChart\" " +
+ "style=\"height:200px\"></div>");
+ performanceChartPanel.render("tab_Advisory");
+
+ perfDS = new YAHOO.util.DataSource([]);
+ perfDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ perfDS.dataType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ perfDS.responseSchema = {
+ fields: ["performance", "week"]
+ };
+
+ perfSeriesDef = [{
+ displayName: "Performance",
+ yField: "performance"
+ }];
+
+ YAHOO.proplus.conpa.formatTimeData = function (value, major) {
+ var arr = value.toString().split(" ");
+ return arr[1];
+ };
+
+ perfTimeAxis = new YAHOO.widget.TimeAxis();
+ perfTimeAxis.labelFunction = YAHOO.proplus.conpa.formatTimeData;
+ perfTimeAxis.majorTimeUnit = "month";
+
+ YAHOO.proplus.conpa.getDataTipTextPerf = function (item, index, series) {
+ var toolTipText = labelReferenceDateButton(item.week);
+
+ toolTipText = toolTipText + "\n" +
+ YAHOO.util.Number.format(item[series.yField], {
+ suffix: "%",
+ thousandsSeparator: ",",
+ decimalPlaces: 2
+ });
+
+ return toolTipText;
+ };
+
+// perfChart = new Y.Chart({
+// categoryKey: "week",
+// categoryType: "time",
+// dataProvider: []
+// tooltip: {
+// markerLabelFunction: function (cItem, vItem, iItem, s, sI) {
+// return cItem.value + " " + percentage(vItem.value);
+// }
+// }
+// });
+
+ perfChart = new YAHOO.widget.LineChart("performanceChart", perfDS, {
+ xField: "week",
+ xAxis: perfTimeAxis,
+ series: perfSeriesDef,
+ dataTipFunction: YAHOO.proplus.conpa.getDataTipTextPerf,
+ expressInstall: "assets/expressinstall.swf",
+ wmode: "opaque"
+ });
+ }
+
+ function initBasketDataTable() {
+ var myColumnDefs, myDataSource, keyStats = [], volStats = [];
+
+ myColumnDefs = [
+ {
+ key: "remove",
+ label: "",
+ formatter: function (el, oRecord, oColumn, oData) {
+ el.innerHTML = "<img width=\"16\" height=\"16\" " +
+ "src=\"images/cancel.png\"/>";
+
+ el.style.cursor = 'pointer';
+ }
+ },
+ {
+ key: "symbol",
+ label: "Symbol",
+ formatter: function (el, oRecord, oColumn, oData) {
+ el.innerHTML = oData;
+ el.title = oRecord.getData('name');
+ el.style.cursor = 'pointer';
+ },
+ sortable: true
+ },
+ {
+ key: "name",
+ label: "Name",
+ width: 256,
+ sortable: true
+ },
+ {key: "type", label: "T", sortable: true},
+ {key: "exchDisp", label: "Market", sortable: true},
+ {
+ key: "optimalWeight",
+ label: "O W %",
+ width: 48,
+ formatter: function (el, oRecord, oColumn, oData) {
+ if (!isNaN(oData)) {
+ oData = oData * 100;
+ el.innerHTML = YAHOO.util.Number.format(oData, {
+ prefix: "",
+ decimalPlaces: 2,
+ decimalSeparator: ".",
+ thousandsSeparator: ","
+ });
+ el.align = "right";
+ }
+ },
+ sortable: true
+ },
+ {
+ key: "min",
+ label: "Min %",
+ width: 48,
+ formatter: function (el, oRecord, oColumn, oData) {
+ if (isNaN(oData) || oData < 0) {
+ oData = 0;
+ }
+ if (oData > 100) {
+ oData = 100;
+ }
+ el.innerHTML = YAHOO.util.Number.format(oData, {
+ prefix: "",
+ decimalPlaces: 2,
+ decimalSeparator: ".",
+ thousandsSeparator: ","
+ });
+ el.align = "right";
+
+ if (oData > 0) {
+ el.style.backgroundColor = "#FFA0A0";
+ } else {
+ el.style.backgroundColor = "#FFFFFF";
+ }
+ },
+ sortable: true,
+ editor: "textbox",
+ editorOptions: {
+ disableBtns: true,
+ validator: function (newData, oldData) {
+ if (newData !== oldData) {
+ YAHOO.widget.DataTable.validateNumber(newData);
+ }
+ return newData;
+ }
+ }
+ },
+ {
+ key: "max",
+ label: "Max %",
+ width: 48,
+ formatter: function (el, oRecord, oColumn, oData) {
+ if (isNaN(oData) || oData > 100) {
+ oData = 100;
+ }
+ if (oData < 0) {
+ oData = 0;
+ }
+ el.innerHTML = YAHOO.util.Number.format(oData, {
+ prefix: "",
+ decimalPlaces: 2,
+ decimalSeparator: ".",
+ thousandsSeparator: ","
+ });
+ el.align = "right";
+ if (oData < 100) {
+ el.style.backgroundColor = "#FFA0A0";
+ } else {
+ el.style.backgroundColor = "#FFFFFF";
+ }
+ },
+ sortable: true,
+ editor: "textbox",
+ editorOptions: {
+ disableBtns: true,
+ validator: function (newData, oldData) {
+ if (newData !== oldData) {
+ YAHOO.widget.DataTable.validateNumber(newData);
+ }
+ return newData;
+ }
+ }
+ }
+ ];
+
+ myDataSource = new YAHOO.util.DataSource([]);
+ myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ myDataSource.responseSchema = {
+ fields: ["symbol", "name", "type", "exchDisp"]
+ };
+
+ myDataTable = new YAHOO.widget.DataTable("basket",
+ myColumnDefs,
+ myDataSource,
+ {
+ caption: "",
+ selectionMode: "single"
+ }
+ );
+
+ myDataTable.subscribe("cellMouseoutEvent", function (oArgs) {
+ graphPanel.hide();
+ });
+
+ myDataTable.subscribe("cellMouseoverEvent", function (oArgs) {
+ var column, firstChar, graphUrl, imgTag, record, symbol, target;
+
+ target = oArgs.target;
+ column = this.getColumn(target);
+
+ if (column.key === "name") {
+ record = this.getRecord(target);
+ symbol = record.getData("symbol").toLowerCase();
+ graphUrl = "http://chart.finance.yahoo.com/c/bb/";
+ firstChar = symbol.substr(0, 1);
+ if (firstChar === "^") {
+ firstChar = "_";
+ }
+ firstChar.toLowerCase();
+ imgTag = "<img src=\"" + graphUrl + firstChar + "/" +
+ symbol + "\" width=\"192\" height=\"96\" alt=\"" +
+ symbol + "\">";
+
+ if (graphPanel !== null) {
+ graphPanel.destroy();
+ }
+
+ graphPanel = new YAHOO.widget.Panel("graphPanel", {
+ context: [this.getTdEl(target), "tl", "br"],
+ fixedcenter: false,
+ close: false,
+ draggable: false,
+ modal: false,
+ visible: false
+ });
+ graphPanel.setBody(imgTag);
+ graphPanel.render("tab_Advisory");
+ graphPanel.show();
+ }
+ });
+
+ myDataTable.subscribe("cellClickEvent", function (ev) {
+ var target, column, symbol, type,
+ callLineDS, callLineChart, callSeriesDef,
+ putLineDS, putLineChart, putSeriesDef;
+
+ target = ev.target;
+ column = this.getColumn(target);
+
+ if (column.key === "remove") {
+ if (this.getRecordSet().getLength() > 0) {
+
+ this.deleteRow(this.getRecord(target));
+ resetOptimalWeigths(this);
+
+ oReferenceDateButton.set("label",
+ labelReferenceDateButton(now));
+ oReferenceDateButton.date = now;
+ }
+ }
+
+ function exitGetKeyStatistics() {
+ var i, stats = "", el;
+
+ waitPanel.hide();
+
+ if (keyStats[symbol] !== undefined) {
+ for (i = 0; i < keyStats[symbol].length; i = i + 1) {
+ stats += "<div class=\"label\">" +
+ keyStats[symbol][i].inputParams.label +
+ "<\/div>";
+ stats += "<div class=\"value\">" +
+ keyStats[symbol][i].inputParams.value +
+ "<\/div>";
+ }
+ el = Dom.get('formKeyStatistics');
+ el.innerHTML = stats;
+ } else {
+ el = Dom.get('formKeyStatistics');
+ el.innerHTML = "Key Statistics not available for " + symbol;
+ }
+
+ function exitGetImpliedVolatility() {
+ var vol = volStats[symbol];
+
+ callLineDS.liveData = vol.callVolatility;
+ callLineChart.set('dataSource', callLineDS);
+ putLineDS.liveData = vol.putVolatility;
+ putLineChart.set('dataSource', putLineDS);
+ el = Dom.get('impliedVolHeader');
+ el.innerHTML = "<b>Call and Put Implied Volatilty for " +
+ symbol + "<br />" +
+ " strike " + vol.strike + " - " +
+ " riskfree " + vol.riskFree * 100 + "% 3 months - " +
+ " expire at close " + vol.expDate +
+ "</b>";
+ Dom.setStyle('loading', 'display', 'none');
+ Dom.setStyle('putVolChart', 'display', 'block');
+ Dom.setStyle('callVolChart', 'display', 'block');
+ }
+
+ if (volStats[symbol] === undefined) {
+ Y.io("/ConPA/getImpliedVolatility", {
+ method: "POST",
+ timeout: 10000,
+ data: {
+ "symbol": symbol
+ },
+ on: {
+ success: function (id, o) {
+ var res = JSON.parse(o.responseText);
+ volStats[symbol] = res;
+ if (volStats[symbol].callVolatility.length === 0) {
+ delete volStats[symbol];
+ Dom.setStyle('loading', 'display', 'none');
+ el = Dom.get('callVolChart');
+ el.innerHTML = "Vol Statistics not available for " + symbol;
+ el = Dom.get('putVolChart');
+ el.innerHTML = "Vol Statistics not available for " + symbol;
+ Dom.setStyle('putVolChart', 'display', 'block');
+ Dom.setStyle('callVolChart', 'display', 'block');
+ } else {
+ exitGetImpliedVolatility();
+ }
+ }
+ }
+ });
+ } else {
+ exitGetImpliedVolatility();
+ }
+
+ assetStatsPanel.bringToTop();
+ assetStatsPanel.show();
+ }
+
+ if (column.key === "symbol") {
+ symbol = this.getRecord(target).getData("symbol");
+ type = this.getRecord(target).getData("type");
+
+ if (type !== "S") {
+ YAHOO.proplus.conpa.alert("Key Statistics not available for " + symbol);
+ return;
+ }
+
+ if (assetStatsPanel !== null) {
+ assetStatsLayout.destroy();
+ assetStatsPanel.destroy();
+ }
+
+ assetStatsPanel = new YAHOO.widget.Panel('assetStatsPanel', {
+ x: 400,
+ y: 290,
+ width: "825px",
+ height: "460px",
+ constraintoviewport: true,
+
+ fixedcenter: false,
+ visible: false
+ });
+ assetStatsPanel.setHeader("Key Statistics for " + symbol);
+ assetStatsPanel.setBody("<div id='layoutKeyStatistics'></div>");
+ assetStatsPanel.render("tab_Advisory");
+
+ assetStatsLayout = new YAHOO.widget.Layout('layoutKeyStatistics', {
+ height: (assetStatsPanel.body.offsetHeight - 20),
+ units: [
+ {
+ position: 'left',
+ width: 290,
+ body: "<div id='formKeyStatistics'></div>",
+ gutter: '1'
+ },
+ {
+ position: 'center',
+ width: 535,
+ body: "<div id=\"impliedVolHeader\"></div>" +
+ "<div id=\"loading\"><b>Calculating implied volatility...</b><br /><img width= \"220\" height=\"19\" src=\"images/rel_interstitial_loading.gif\" /></div>" +
+ "<div id=\"callVolChart\" style=\"height:200px\">Unable to load Flash content. The YUI Charts Control requires Flash Player 9.0.45 or higher. You can install the latest version at the <a href=\"http://www.adobe.com/go/getflashplayer\">Adobe Flash Player Download Center<\/a>.<\/p><\/div>" +
+ "<div id=\"putVolChart\" style=\"height:200px\">Unable to load Flash content. The YUI Charts Control requires Flash Player 9.0.45 or higher. You can install the latest version at the <a href=\"http://www.adobe.com/go/getflashplayer\">Adobe Flash Player Download Center<\/a>.<\/p><\/div>",
+ gutter: '1'
+ }
+ ]
+ });
+ assetStatsLayout.render();
+ Dom.setStyle('putVolChart', 'display', 'none');
+ Dom.setStyle('callVolChart', 'display', 'none');
+
+ YAHOO.proplus.conpa.getDataTipTextLine = function (item, index, series) {
+ var toolTipText = "strike: " + item.strike +
+ " - ask: " +
+ (item.ask ? YAHOO.util.Number.format(item.ask, {
+ decimalPlaces: 2
+ }) : "N/A") +
+ " - bid: " +
+ (item.bid ? YAHOO.util.Number.format(item.bid, {
+ decimalPlaces: 2
+ }) : "N/A");
+
+ return toolTipText;
+ };
+
+ callLineDS = new YAHOO.util.DataSource([]);
+ callLineDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ callLineDS.dataType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ callLineDS.responseSchema = {
+ fields: ["strike", "ask", "bid"]
+ };
+
+ callSeriesDef = [
+ { displayName: "Ask Call Vol", yField: "ask" },
+ { displayName: "Bid Call Vol", yField: "bid" }
+ ];
+
+ callLineChart = new YAHOO.widget.LineChart("callVolChart", callLineDS, {
+ xField: "strike",
+ series: callSeriesDef,
+ style: {
+ xAxis: {
+ majorTicks: {
+ display: "inside",
+ length: 3,
+ size: 1
+ },
+ minorTicks: {
+ display: "inside",
+ length: 2
+ },
+ labelRotation: -45
+ },
+ padding: 20,
+ legend: {
+ display: "right",
+ padding: 10,
+ spacing: 5,
+ font: {
+ family: "Arial",
+ size: 9
+ }
+ }
+ },
+ dataTipFunction: YAHOO.proplus.conpa.getDataTipTextLine,
+ expressInstall: "assets/expressinstall.swf",
+ wmode: "opaque"
+ });
+
+ putLineDS = new YAHOO.util.DataSource([]);
+ putLineDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ putLineDS.dataType = YAHOO.util.DataSource.TYPE_JSARRAY;
+ putLineDS.responseSchema = {
+ fields: ["strike", "ask", "bid"]
+ };
+
+ putSeriesDef = [
+ { displayName: "Ask Put Vol", yField: "ask" },
+ { displayName: "Bid Put Vol", yField: "bid" }
+ ];
+
+ putLineChart = new YAHOO.widget.LineChart("putVolChart", putLineDS, {
+ xField: "strike",
+ series: putSeriesDef,
+ style: {
+ xAxis: {
+ majorTicks: {
+ display: "inside",
+ length: 3,
+ size: 1
+ },
+ minorTicks: {
+ display: "inside",
+ length: 2
+ },
+ labelRotation: -45
+ },
+ padding: 20,
+ legend: {
+ display: "right",
+ padding: 10,
+ spacing: 5,
+ font: {
+ family: "Arial",
+ size: 9
+ }
+ }
+ },
+ dataTipFunction: YAHOO.proplus.conpa.getDataTipTextLine,
+ expressInstall: "assets/expressinstall.swf",
+ wmode: "opaque"
+ });
+
+ waitPanel.setHeader("Retrieving Key Statistics...");
+ waitPanel.show();
+
+ if (keyStats[symbol] === undefined) {
+ Y.io("/ConPA/getKeyStatistics", {
+ method: "POST",
+ timeout: 10000,
+ data: {
+ "symbol": symbol
+ },
+ on: {
+ success: function (id, o) {
+ var res = JSON.parse(o.responseText);
+ keyStats[symbol] = res;
+ if (keyStats[symbol].length === 0) {
+ delete keyStats[symbol];
+ }
+ exitGetKeyStatistics();
+ },
+ failure: function (id, o) {
+ waitPanel.hide();
+ Dom.setStyle('loading', 'display', 'none');
+ YAHOO.proplus.conpa.alert("getKeyStatistics: failed");
+ }
+ }
+ });
+ } else {
+ exitGetKeyStatistics();
+ }
+ }
+ this.onEventShowCellEditor(ev);
+ });
+
+ myDataTable.subscribe("editorSaveEvent", function (oArgs) {
+ resetOptimalWeigths(this);
+ this.saveCellEditor();
+ });
+ }
+
+ function getRScriptClick(e, assets, refDate,
+ targetReturn, targetReturnA, lows, highs) {
+ var refDateStr = refDate.getFullYear() + "-" +
+ (refDate.getMonth() + 1) + "-" + refDate.getDate(),
+ refDate2YA = YAHOO.widget.DateMath.add(refDate,
+ YAHOO.widget.DateMath.YEAR, -2),
+ refDate2YAStr = refDate2YA.getFullYear() + "-" +
+ (refDate2YA.getMonth() + 1) + "-" + refDate2YA.getDate(),
+ targetReturnStr = targetReturn || "mean(x)",
+ asset,
+ i,
+ n = assets.length,
+ rScript = "# COPY & PASTE IN R CONSOLE<br /><br />",
+ rRequire = 'require(tseries)<br /><br />',
+ rHackZeroes = "hackZeroes <- function (x) {<br />" +
+ " for (i in which(x == 0)) {<br />" +
+ " if (i == 1) {<br />" +
+ " c = i<br />" +
+ " while (x[c] == 0) {<br />" +
+ " c = c + 1<br />" +
+ " }<br />" +
+ " x[i] <- x[c]<br />" +
+ " } else {<br />" +
+ " x[i] <- x[i - 1]<br />" +
+ " }<br />" +
+ " }<br />" +
+ " x<br />" +
+ "}<br /><br />",
+ rInit = 'x <- c()<br /><br />',
+ rReturns = '{ASSET} <- coredata(get.hist.quote(\"{SYMBOL}\", ' +
+ 'start=\"{STARTDATE}\", end=\"{ENDDATE}\", ' +
+ 'compression=\"w\", quote=\"Close\"))<br />' +
+ '{ASSET} <- hackZeroes({ASSET})<br />' +
+ '{ASSET}.rets <- ' +
+ 'diff(log({ASSET}[1:(length({ASSET})-1)]))<br />' +
+ 'x <- cbind(x, {ASSET}.rets)<br /><br />',
+ lowsStr,
+ highsStr,
+ rRes;
+
+ lowsStr = 'reslow = c(';
+ for (i = 0; i < lows.length; i = i + 1) {
+ if ((i + 1) === lows.length) {
+ lowsStr += lows[i];
+ } else {
+ lowsStr += lows[i] + ', ';
+ }
+ }
+ lowsStr += '), ';
+ highsStr = 'reshigh = c(';
+ for (i = 0; i < highs.length; i = i + 1) {
+ if ((i + 1) === highs.length) {
+ highsStr += (highs[i] * -1);
+ } else {
+ highsStr += (highs[i] * -1) + ', ';
+ }
+ }
+ highsStr += ')';
+
+ rRes = '#Target Return (pm) is weekly<br />';
+ rRes += 'res <- portfolio.optim(x, pm = ' + targetReturnStr +
+ ", " + lowsStr + highsStr +
+ ')<br />' + 'res$pw<br /><br />';
+
+ rScript += '<pre>' + rRequire;
+ rScript += rHackZeroes;
+ rScript += rInit;
+ for (i = 0; i < n; i = i + 1) {
+ asset = assets[i]
+ .replace("=", "_").replace("^", "i_").toLowerCase();
+ rScript += "#Asset " + assets[i] + " with weekly returns<br />";
+ rScript += YAHOO.lang.substitute(rReturns, {
+ ASSET: asset,
+ SYMBOL: assets[i],
+ STARTDATE: refDate2YAStr,
+ ENDDATE: refDateStr
+ });
+ }
+ rScript += rRes + '</pre>';
+
+ scriptPanel.setBody(rScript);
+ scriptPanel.render("tab_Advisory");
+ scriptPanel.show();
+ }
+
+ function initGetOptimalPortfolio() {
+ var referenceDate;
+
+ oGetOptimalPortfolioButton = new YAHOO.widget.Button("getOptimalPortfolio");
+
+ function onGetOptimalPortfolioClick() {
+ var i, products, nProducts, record, recordData,
+ prods = [], lows = [], highs = [],
+ targetReturn, perfData = [], pieData = [];
+
+ products = myDataTable.getRecordSet();
+ nProducts = products.getLength();
+ if (nProducts < 3) {
+ YAHOO.proplus.conpa.alert('You need at least 3 assets.');
+ return;
+ }
+ if (nProducts > 10) {
+ YAHOO.proplus.conpa.alert('You can insert maximum 10 assets.');
+ return;
+ }
+
+ for (i = 0; i < nProducts; i = i + 1) {
+ prods[i] = products.getRecord(i).getData("symbol");
+ lows[i] = products.getRecord(i).getData("min") / 100;
+ highs[i] = -products.getRecord(i).getData("max") / 100;
+ }
+
+ if (nTargetReturn !== 0) { // from annual to weekly
+ targetReturn = Math.pow((nTargetReturn + 1), (1 / 52)) - 1;
+ }
+
+ referenceDate = oReferenceDateButton.date;
+ referenceDate.setHours(12);
+ referenceDate.setMinutes(0);
+ referenceDate.setSeconds(0);
+
+ scriptPanel.hide();
+ if (scriptHandle !== null) {
+ Y.detach(scriptHandle);
+ }
+ scriptHandle = Y.on("click", getRScriptClick, "#getRScript",
+ null,
+ prods,
+ referenceDate,
+ targetReturn,
+ nTargetReturn,
+ lows,
+ highs);
+
+ waitPanel.setHeader("Optimizing portfolio...");
+ waitPanel.show();
+ oGetOptimalPortfolioButton.set("disabled", true);
+
+ function exitGetOptimalPortfolio() {
+ oGetOptimalPortfolioButton.set("disabled", false);
+ waitPanel.hide();
+ }
+
+ Y.io("/ConPA/getOptimalPortfolio", {
+ method: "POST",
+ timeout: 10000,
+ data: {
+ "prods": prods,
+ "referenceDate": referenceDate.toString(),
+ "targetReturn": targetReturn,
+ "lows": lows,
+ "highs": highs
+ },
+ on: {
+ success: function (id, o) {
+ var res = JSON.parse(o.responseText);
+
+ if (res.message !== "") {
+ YAHOO.proplus.conpa.alert(res.message);
+ exitGetOptimalPortfolio();
+ return;
+ }
+
+ for (i = 0; i < nProducts; i = i + 1) {
+ record = products.getRecord(i);
+ recordData = record.getData();
+ recordData.optimalWeight = res.optim.solution[i];
+ myDataTable.updateRow(record, recordData);
+
+ pieData[i] = {
+ asset: prods[i],
+ weigth: res.optim.solution[i]
+ };
+
+ Y.Cookie.setSubs(YAHOO.proplus.conpa.COOKIE + "." +
+ recordData.symbol, recordData, {
+ expires: YAHOO.widget.DateMath.add(new Date(),
+ YAHOO.widget.DateMath.DAY, 30)
+ });
+ }
+
+ Y.Cookie.set(YAHOO.proplus.conpa.COOKIE +
+ ".targetReturn", nTargetReturn, {
+ expires: YAHOO.widget.DateMath.add(new Date(),
+ YAHOO.widget.DateMath.DAY, 30)
+ });
+ Y.Cookie.set(YAHOO.proplus.conpa.COOKIE +
+ ".referenceDate", referenceDate, {
+ expires: YAHOO.widget.DateMath.add(new Date(),
+ YAHOO.widget.DateMath.DAY, 30)
+ });
+ Y.Cookie.set(YAHOO.proplus.conpa.COOKIE, prods, {
+ expires: YAHOO.widget.DateMath.add(new Date(),
+ YAHOO.widget.DateMath.DAY, 30)
+ });
+
+ createPieChart();
+ pieChart.set('dataProvider', pieData);
+ pieChart.render('#pieChart');
+ pieChartPanel.show();
+
+ if (res.perf.length > 0) {
+ perfData[0] = {
+ performance: 0,
+ week: referenceDate
+ };
+ for (i = 0; i < res.perf.length; i = i + 1) {
+ perfData[i + 1] = {
+ performance: res.perf[i] * 100,
+ week: YAHOO.widget.DateMath.add(referenceDate,
+ YAHOO.widget.DateMath.WEEK, i + 1)
+ };
+ }
+ createPerformanceChart();
+ perfDS.liveData = perfData;
+ // perfChart.set('dataProvider', perfData);
+ // perfChart.render('#performanceChart');
+ perfChart.set('dataSource', perfDS);
+ performanceChartPanel.show();
+ }
+
+ Y.io("/ConPA/putPortfolioOnCRM", {
+ method: "POST",
+ timeout: 10000,
+ data: {
+ "symbols": prods,
+ "weights": res.optim.solution,
+ "ref": referenceDate.toString(),
+ "ret": res.optim.pm,
+ "risk": res.optim.ps,
+ "perf": res.perf,
+ "highs": highs,
+ "lows": lows
+
+ },
+ on: {
+ success: function (ident, o) {
+ var stats, id;
+
+ id = o.responseText;
+
+ if (id !== "") {
+ previousPortfolioName = lastPortfolioName;
+ lastPortfolioName = id;
+ stats = "risk: " +
+ percentage(res.optim.ps * Math.sqrt(52)) + " " +
+ "ret: " +
+ percentage(Math.pow(1 + res.optim.pm, 52) - 1) + " " +
+ "perf: " +
+ percentage(res.perf.length > 0 ? res.perf[res.perf.length - 1] : 0);
+
+ Y.one('#portfolioId').set('innerHTML',
+ "Id: <b>" + lastPortfolioName + "</b>")
+ .set('title', stats);
+
+ Y.Cookie.set(YAHOO.proplus.conpa.COOKIE +
+ ".id", lastPortfolioName, {
+ expires: YAHOO.widget.DateMath.add(new Date(),
+ YAHOO.widget.DateMath.DAY, 30)
+ });
+
+ Y.Cookie.set(YAHOO.proplus.conpa.COOKIE +
+ ".stats", stats, {
+ expires: YAHOO.widget.DateMath.add(new Date(),
+ YAHOO.widget.DateMath.DAY, 30)
+ });
+ }
+ }
+ }
+ });
+
+ exitGetOptimalPortfolio();
+ }
+ },
+ failure: function (id, o) {
+ exitGetOptimalPortfolio();
+ YAHOO.proplus.conpa.alert("getOptimalPortfolio: failed");
+ }
+ });
+
+ }
+
+ oGetOptimalPortfolioButton.on("click", onGetOptimalPortfolioClick);
+ }
+
+ function initReferenceDate() {
+ var oCalendarMenu = new YAHOO.widget.Overlay("calendarmenu", {
+ visible: false,
+ zIndex: 4
+ });
+
+ oReferenceDateButton = new YAHOO.widget.Button({
+ type: "menu",
+ id: "calendarpicker",
+ label: labelReferenceDateButton(now),
+ menu: oCalendarMenu,
+ container: "referenceDate"
+ });
+ oReferenceDateButton.date = now;
+
+ oReferenceDateButton.on("appendTo", function () {
+ oCalendarMenu.setBody("&#32;");
+ oCalendarMenu.body.id = "calendarcontainer";
+ oCalendarMenu.render(this.get("container"));
+ });
+
+ function onReferenceDateButtonClick() {
+ var maxdate, mindate, oCalendar, sMinDate, sMaxDate;
+
+ oCalendar = new YAHOO.widget.Calendar("buttoncalendar", oCalendarMenu.body.id);
+
+ mindate = YAHOO.widget.DateMath.add(now, YAHOO.widget.DateMath.YEAR, -2);
+ maxdate = YAHOO.widget.DateMath.add(now, YAHOO.widget.DateMath.MONTH, 0);
+ sMinDate = (mindate.getMonth() + 1) + "/" +
+ mindate.getDate() +
+ "/" +
+ mindate.getFullYear();
+ sMaxDate = (maxdate.getMonth() + 1) + "/" +
+ maxdate.getDate() +
+ "/" +
+ maxdate.getFullYear();
+ oCalendar.cfg.setProperty("mindate", sMinDate);
+ oCalendar.cfg.setProperty("maxdate", sMaxDate);
+ oCalendar.cfg.setProperty("navigator", true);
+
+ oCalendar.render();
+
+ oCalendar.selectEvent.subscribe(function (p_sType, p_aArgs) {
+ var aDate, nMonth, nDay, nYear;
+
+ if (p_aArgs) {
+ resetOptimalWeigths(myDataTable);
+
+ aDate = p_aArgs[0][0];
+ nMonth = aDate[1] - 1;
+ nDay = aDate[2];
+ nYear = aDate[0];
+ aDate = new Date(nYear, nMonth, nDay);
+
+ oReferenceDateButton.set("label", labelReferenceDateButton(aDate));
+ oReferenceDateButton.date = aDate;
+ }
+
+ oCalendarMenu.hide();
+ });
+
+ oCalendar.unsubscribe("click", onReferenceDateButtonClick);
+ }
+
+ oReferenceDateButton.on("click", onReferenceDateButtonClick);
+ }
+
+ function initTargetReturn() {
+ var oTargetReturnMenu, oTargetReturnButton,
+ oCurrentTargetReturn, oHTMLButton;
+
+ oTargetReturnMenu = new YAHOO.widget.Menu("targetreturnmenu", {
+ width: "120px", // 100 + 20
+ zIndex: 4
+ });
+
+ oTargetReturnButton = new YAHOO.widget.Button({
+ type: "menu",
+ id: "targetreturnbutton",
+ label: "<em id=\"targetreturnbutton-currenttargetreturn\"></em>",
+ menu: oTargetReturnMenu,
+ container: "targetReturn"
+ });
+
+ oTargetReturnButton.on("appendTo", function () {
+ oTargetReturnMenu.setBody("<div id=\"slider-bg\" tabindex=\"1\" title=\"Slider\"><div id=\"slider-thumb\"><img width=\"17\" height=\"21\" src=\"images/thumb-n.gif\"></div></div>");
+ oTargetReturnMenu.render(this.get("container"));
+ oCurrentTargetReturn = Dom.get("targetreturnbutton-currenttargetreturn");
+ });
+
+ oHTMLButton = oTargetReturnButton.get("element")
+ .getElementsByTagName("button")[0];
+ oHTMLButton.id = "targetreturnbutton-button";
+
+ oTargetReturnMenu.subscribe("render", function () {
+ var oSlider, oSliderEl;
+
+ oSlider = YAHOO.widget.Slider.getHorizSlider("slider-bg",
+ "slider-thumb", 0, 100, 1);
+ if (nTargetReturn === undefined) {
+ nTargetReturn = 0;
+ }
+ oSlider.setValue(nTargetReturn / 0.01 / 0.15, true);
+
+ oSliderEl = Dom.get("slider-bg");
+
+ oSlider.subscribe("change", function () {
+ var nTargetReturnOld = nTargetReturn * 100 / 100,
+ nValue = Math.round(oSlider.getValue() * 0.15);
+
+
+ nTargetReturn = (nValue * 0.01);
+
+ oSliderEl.title = "Annual Target Return = " + nTargetReturn;
+ oCurrentTargetReturn.innerHTML = "Target Return " +
+ nValue + "%";
+
+ if (nTargetReturn !== nTargetReturnOld) {
+ resetOptimalWeigths(myDataTable);
+ }
+ });
+ });
+ }
+
+ function restoreBasket() {
+ var basketRestored, data, i, lastBasket, referenceDate, id, stats;
+
+ lastBasket = Y.Cookie.get(YAHOO.proplus.conpa.COOKIE);
+ if (lastBasket !== null) {
+ basketRestored = lastBasket.split(",");
+ for (i = 0; i < basketRestored.length; i = i + 1) {
+ data = Y.Cookie.getSubs(YAHOO.proplus.conpa.COOKIE +
+ "." + basketRestored[i]);
+ if (data !== null) {
+ data.symbol = basketRestored[i];
+ myDataTable.addRow(data);
+ }
+ }
+ referenceDate = new Date(Y.Cookie.get(
+ YAHOO.proplus.conpa.COOKIE + ".referenceDate"
+ ));
+ if (referenceDate !== null) {
+ oReferenceDateButton.set("label",
+ labelReferenceDateButton(referenceDate));
+ oReferenceDateButton.date = referenceDate;
+ }
+ nTargetReturn = Y.Cookie.get(
+ YAHOO.proplus.conpa.COOKIE + ".targetReturn"
+ );
+
+ id = Y.Cookie.get(YAHOO.proplus.conpa.COOKIE + ".id");
+
+ if (id) {
+ stats = Y.Cookie.get(YAHOO.proplus.conpa.COOKIE + ".stats");
+ Y.one('#portfolioId').set('innerHTML',
+ "Id: <b>" + id + "</b>").set('title', stats);
+ }
+
+ }
+ }
+
+ function initDashboard() {
+ var ptfDS, lastPtfsDT, bestPerfDT, worstPerfDT,
+ highRiskDT, lowRiskDT, highRetDT, lowRetDT;
+
+ function percentageFormatter(o) {
+ if (!isNaN(o.value)) {
+ return percentage(o.value);
+ }
+ }
+
+ function hyphenFormatter(o) {
+ var assets, weights, lowBounds, highBounds, n, i, title;
+
+ title = o.record.getValue('id') + " \n" +
+ "perf: " + percentage(o.record.getValue('value.perf')) + " " +
+ "risk: " + percentage(o.record.getValue('value.risk')) + " " +
+ "ret: " + percentage(o.record.getValue('value.ret')) + " \n" +
+ "reference date: " + o.record.getValue('value.ref') + " \n";
+ assets = o.record.getValue('value.assets');
+ weights = o.record.getValue('value.weights');
+ lowBounds = o.record.getValue('value.constraints.lowBounds');
+ highBounds = o.record.getValue('value.constraints.highBounds');
+ n = assets.length;
+ for (i = 0; i < n; i += 1) {
+ title += assets[i] + " " + percentage(weights[i]);
+
+ if (lowBounds !== undefined && highBounds !== undefined) {
+ title += " [" +
+ percentage(lowBounds[i]) + " - " +
+ percentage(highBounds[i] * -1) +
+ "]";
+ }
+ title += " \n";
+ }
+
+ this.tdTemplate = '<td title="{title}" headers="{headers}" class="{classnames}"><div class="yui3-datatable-liner">{value}</div></td>';
+ o.title = title;
+
+ return o.value.substr(0, 5) + "..." +
+ o.value.substr(o.value.length - 5, 5);
+ }
+
+ function updateDataTable(dt, liveData) {
+ ptfDS.set("source", liveData);
+ dt.datasource.load();
+ }
+
+ ptfDS = new Y.DataSource.Local({source: {}});
+ ptfDS.plug(Y.Plugin.DataSourceJSONSchema, {
+ schema: {
+ resultListLocator: "rows",
+ resultFields: ["key", "id",
+ {key: "value.created_at"},
+ {key: "value.assets"},
+ {key: "value.weights"},
+ {key: "value.ref"},
+ {key: "value.ret"},
+ {key: "value.risk"},
+ {key: "value.perf"},
+ {key: "value.constraints.lowBounds"},
+ {key: "value.constraints.highBounds"}
+ ]
+ }
+ });
+
+ lastPtfsDT = new Y.DataTable.Base({columnset: [
+ { key: "key", label: "To Date" },
+ { key: "value.ref", label: "Reference Date" },
+ { key: "id", label: "Last Created", formatter: hyphenFormatter
+ },
+ { key: "value.perf", label: "Perf",
+ formatter: percentageFormatter, width: 75
+ },
+ { key: "value.risk", label: "Risk",
+ formatter: percentageFormatter, width: 75
+ },
+ { key: "value.ret", label: "Ret",
+ formatter: percentageFormatter, width: 75
+ }
+ ]}).plug(Y.Plugin.DataTableDataSource, {datasource: ptfDS})
+ .render("#lastCreatedPortfolios");
+
+ function getDefs(keyName, idDesc) {
+ return [
+ { key: "key", label: keyName,
+ formatter: percentageFormatter, width: 50
+ },
+ { key: "value.created_at", label: "To Date" },
+ { key: "id", label: idDesc, formatter: hyphenFormatter }
+ ];
+ }
+
+ bestPerfDT = new Y.DataTable.Base({
+ columnset: getDefs("Perf", "Best Performing")
+ }).plug(Y.Plugin.DataTableDataSource, {datasource: ptfDS})
+ .render("#bestPerformingPortfolios");
+ worstPerfDT = new Y.DataTable.Base({
+ columnset: getDefs("Perf", "Worst Performing")
+ }).plug(Y.Plugin.DataTableDataSource, {datasource: ptfDS})
+ .render("#worstPerformingPortfolios");
+
+ highRiskDT = new Y.DataTable.Base({
+ columnset: getDefs("Risk", "High Profile Risk")
+ }).plug(Y.Plugin.DataTableDataSource, {datasource: ptfDS})
+ .render("#highProfileRiskPortfolios");
+ lowRiskDT = new Y.DataTable.Base({
+ columnset: getDefs("Risk", "Low Profile Risk")
+ }).plug(Y.Plugin.DataTableDataSource, {datasource: ptfDS})
+ .render("#lowProfileRiskPortfolios");
+
+ highRetDT = new Y.DataTable.Base({
+ columnset: g