├── README.md ├── app.js ├── bin └── www ├── eval.js ├── package.json ├── public ├── .DS_Store ├── css │ └── style.css ├── data │ ├── .DS_Store │ ├── AAPL.json │ ├── AMZN.json │ ├── BIDU.json │ ├── CCOI.json │ ├── FB.json │ ├── FEYE.json │ ├── NFLX.json │ ├── NICE.json │ ├── VEEV.json │ ├── WIX.json │ ├── topInternetStocks.csv │ └── train │ │ ├── SOM_WEIGHTS.json │ │ ├── train-AAPL.json │ │ ├── train-AMZN.json │ │ ├── train-BIDU.json │ │ ├── train-CCOI.json │ │ ├── train-FB.json │ │ ├── train-FEYE.json │ │ ├── train-NFLX.json │ │ ├── train-NICE.json │ │ ├── train-VEEV.json │ │ └── train-WIX.json ├── images │ ├── add.png │ ├── add_color.png │ ├── add_grey.png │ ├── expand_color.png │ ├── expand_grey.png │ ├── icons.pptx │ ├── next_button.png │ ├── remove.png │ ├── remove_color.png │ ├── remove_grey.png │ ├── settings_color.png │ ├── settings_grey.png │ ├── stockfork.png │ └── ~$icons.pptx ├── js │ ├── brain-0.6.0.min.js │ ├── d3.v3.js │ ├── d3.v3.min.js │ ├── encog-js-1.0.js │ ├── encog-js-1.0.src.js │ ├── encog-widget-js-1.0.js │ ├── encog-widget.src.js │ ├── horizon.js │ ├── jquery-ui.js │ ├── jquery.js │ ├── queue.v1.min.js │ ├── raphael-min.js │ ├── raphael.js │ ├── raphael.sketchpad.js │ ├── stockVis- neural.js │ ├── stockVis-bayesian-temporal.js │ ├── stockVis-correlationviewer.js │ ├── stockVis-linechart.js │ ├── stockVis-overviewchart.js │ ├── stockVis-overviewhorizonchart.js │ ├── stockVis-overviewlinechart.js │ ├── stockVis-prediction.js │ ├── stockVis-spatialprediction.js │ ├── stockVis-stock.js │ ├── stockVis-temporalprediction.js │ ├── stockVis-temporalprediction1.js │ ├── stockVis-tree.js │ └── stockVis.client.js └── stylesheets │ └── style.css ├── routes ├── index.js └── users.js ├── stock-forest.js ├── stock-som.js ├── stock.js ├── stock2.js └── views ├── error.jade ├── index.html └── layout.jade /README.md: -------------------------------------------------------------------------------- 1 | TimeFork For Stock Market Data 2 | ==== 3 | *For information related to our user study, please look into the [user-study branch](https://github.com/karthikbadam/TimeFork/tree/user-study).* 4 | 5 | [Please note that this repository is not being maintained. Contact the developer for updates.] 6 | 7 | **TimeFork** is a technique for interactive prediction of time-series data. To showcase this technique, we developed a stock market analytics tool (StockFork) using data from the Yahoo Finance API. With TimeFork, you can predict the future of stocks through an interactive dialogue with the interface (through 3 steps). 8 | 9 | 1. The interface first provides prediction suggestions for each stock based on its past trends (**temporal predictions**). 10 | 2. Based on these suggestions and other stock market data, an analyst can interact to make his/her own prediction for one or more stocks. 11 | 3. Following the analyst's interactions for specific stocks, the interface recalculates the predictions for other stocks (**conditional predictions**). In essence, this step answers "what if" questions that an analyst can have. For example, what if Tesla increases by 5% over the next ten days. The dialogue goes back to Step 1 or 2 after this. 12 | 13 | The analyst can go through these steps iteratively till he/she has enough information to make a decision regarding investment in the stocks. 14 | 15 | 16 | ## Repository Content 17 | 18 | In this repository, we provide two main things for developers and researchers interested in contributing to this project. 19 | 20 | 1. The "master" branch contains our implementation of the stock market analytics tool, StockFork, including its server and client components (described below). 21 | 22 | 2. The "user-study" branch contains the implementation including the code and data specifically used for a user study conducted to understand if TimeFork is successful at improving the predictions and when/how it does do. 23 | 24 | Beyond the implementations, **our repository also offers sample stock market data in [public/data/](https://github.com/karthikbadam/TimeFork/tree/master/public/data) folder and the corresponding trained neural network files (containing fitted weights) in [public/data/train/](https://github.com/karthikbadam/TimeFork/tree/master/public/data/train).** 25 | 26 | 27 | ## Evaluation: User Study 28 | 29 | For implementations, data, and training files specific to our user study, take a look into the [**user-study branch**](https://github.com/karthikbadam/TimeFork/tree/user-study). 30 | 31 | ## Build Process 32 | 33 | To deploy this application, you must have [node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/) installed. 34 | 35 | 1. Run `npm install` to install dependencies. 36 | 2. Run `node app.js` to start the application at port 3000. Now, you can try it out by opening `localhost:3000` in your browser. 37 | 38 | 39 | ## Server-Side Components 40 | 41 | To support the TimeFork technique for stock market prediction, we used neural network models for temporal and conditional predictions. More specifically, 42 | 43 | 1. The **temporal predictions** (predictions based on past performance of a stock) are generated using a **multilayer perceptron** (one model per stock) trained on sets of past 6 [relative](https://en.wikipedia.org/wiki/Relative_change_and_difference) stock price changes (input) to predict the future change. This model had five layers: input layer of 6 neurons, hidden layers of sizes (50, 60, 70), and an output layer of one neuron, with Sigmoid activation function. The models are trained on historical stock market data using the standard backpropagation algorithm. Alternative predictions for each temporal prediction are generated by changing one or more input values by a 10% fixed margin. This model is implemented using [Brain](https://www.npmjs.com/package/brain). 44 | 45 | 2. The **conditional predictions** (predictions for a stock based on a trend for other stocks) are generated using a **self-organizing map** trained on all co-occurrences in the dataset (e.g., Apple and Tesla increased by a 5% and 3% relative change respectively on a particular day). The neurons in the model capture these co-occurrence patterns and are clustered. So, we can easily look up the SOM neurons to find the ones matching a particular prediction from the analyst and generate conditional predictions for others (e.g., if the analyst says Apple might increase by 5% in the next 3 days, what will happen to Tesla?). This model is implemented using [ML-SOM](https://www.npmjs.com/package/ml-som). 46 | 47 | These models are trained on the sample stock market data provided. The server-side components train these models, which are further stored in [JSON format](http://json-schema.org/) on [public/data/train/](https://github.com/karthikbadam/TimeFork/tree/master/public/data/train). 48 | 49 | ## Client Interface 50 | 51 | The client interface provides overview and detail visualizations for stock market data built using [D3](http://d3js.org/). It also accesses the trained neural networks (recreated from the JSON files) and provides prediction suggestions while supporting user interaction. 52 | 53 | ![StockFork](https://raw.githubusercontent.com/karthikbadam/TimeFork/master/public/images/stockfork.png) 54 | 55 | 56 | ## How to use 57 | 58 | Please watch our video demo for this prediction tool. 59 | 60 | [Link to the video](https://www.youtube.com/watch?v=nUfSqqbaD5k) 61 | 62 | To learn more, [visit the wiki](https://github.com/karthikbadam/TimeFork/wiki). 63 | 64 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Karthik Badam on 6/11/15. 3 | */ 4 | 5 | var express = require('express'); 6 | var path = require('path'); 7 | var favicon = require('serve-favicon'); 8 | var logger = require('morgan'); 9 | var cookieParser = require('cookie-parser'); 10 | var bodyParser = require('body-parser'); 11 | var http = require('http'); 12 | var path = require('path'); 13 | var url = require('url'); 14 | var csv = require('fast-csv'); 15 | var fs = require('fs'); 16 | var Parallel = require('paralleljs'); 17 | var historic = require('historic'); 18 | 19 | var Stock = require('./stock2.js'); 20 | var SOM = require('./stock-som.js'); 21 | var TEMPORAL_TRAIN = false; 22 | var SPATIAL_TRAIN = false; 23 | 24 | var routes = require('./routes/index'); 25 | var users = require('./routes/users'); 26 | 27 | //stock downloader 28 | var start = new Date(); 29 | var end = new Date(); 30 | 31 | start.setMonth(1); 32 | start.setDate(02); 33 | start.setFullYear(2012); 34 | 35 | var streamingData = {}; 36 | 37 | var app = express(); 38 | app.set('port', process.env.PORT || 3000); 39 | app.set('views', path.join(__dirname, 'views')); 40 | app.set('view engine', 'jade'); 41 | 42 | app.use(logger('dev')); 43 | app.use(bodyParser.json()); 44 | app.use(bodyParser.urlencoded({ 45 | extended: false 46 | })); 47 | app.use(cookieParser()); 48 | app.use(express.static(path.join(__dirname, 'public'))); 49 | 50 | app.engine('html', require('ejs').renderFile); 51 | 52 | //app.use('/', routes); 53 | //app.use('/users', users); 54 | 55 | app.get('/', function (req, res, next) { 56 | res.render('index.html', {}); 57 | }); 58 | 59 | /* listen */ 60 | var httpserver = http.createServer(app); 61 | httpserver.listen(app.get('port'), function () { 62 | console.log('Express server listening on port ' + app.get('port')); 63 | }); 64 | 65 | 66 | //start with stock list 67 | var stream = fs.createReadStream("public/data/topInternetStocks.csv"); 68 | 69 | var allStocks = []; 70 | var allData = {}; 71 | 72 | // lets not do the training for now 73 | var csvStream = csv 74 | .fromStream(stream, { 75 | headers: true 76 | }) 77 | .on("data", function (data) { 78 | 79 | var symbol = data.symbols; 80 | 81 | allStocks.push(symbol); 82 | 83 | var companyName = symbol; 84 | 85 | historic(symbol, start, end, function (err, data) { 86 | 87 | streamingData[symbol] = data; 88 | 89 | fs.writeFile("public/data/" + symbol + ".json", JSON.stringify(data), function (err) { 90 | if (err) throw err; 91 | console.log('Stock cache saved!'); 92 | }); 93 | 94 | 95 | }); 96 | 97 | // Stock({ 98 | // company: companyName, 99 | // symbol: symbol 100 | // }); 101 | 102 | }) 103 | .on("end", function () { 104 | 105 | if (SPATIAL_TRAIN) { 106 | 107 | SOM({ 108 | symbols: allStocks 109 | }); 110 | 111 | } 112 | 113 | if (TEMPORAL_TRAIN) { 114 | var p = new Parallel(allStocks, { 115 | evalPath: 'eval.js' 116 | }); 117 | 118 | p.require(Stock); 119 | 120 | p.map(function (symbol) { 121 | 122 | var companyName = symbol; 123 | 124 | Stock({ 125 | company: companyName, 126 | symbol: symbol 127 | }); 128 | 129 | return 1; 130 | 131 | }); 132 | } 133 | }); 134 | 135 | app.get('/stockData', function (req, res, next) { 136 | 137 | var selectedURL = url.parse(req.url, true); //creates object 138 | var params = selectedURL.query; 139 | var stockId = params.stock; 140 | console.log('Accessing the Data function for ...' + stockId); 141 | 142 | res.write(fs.readFileSync("public/data/" + stockId + ".json")); 143 | res.end(); 144 | 145 | }); 146 | 147 | 148 | // development error handler 149 | // will print stacktrace 150 | if (app.get('env') === 'development') { 151 | app.use(function (err, req, res, next) { 152 | res.status(err.status || 500); 153 | res.render('error', { 154 | message: err.message, 155 | error: err 156 | }); 157 | }); 158 | } 159 | 160 | // production error handler 161 | // no stacktraces leaked to user 162 | app.use(function (err, req, res, next) { 163 | res.status(err.status || 500); 164 | res.render('error', { 165 | message: err.message, 166 | error: {} 167 | }); 168 | }); 169 | 170 | // catch 404 and forward to error handler 171 | app.use(function (req, res, next) { 172 | var err = new Error('Not Found'); 173 | err.status = 404; 174 | next(err); 175 | }); 176 | 177 | module.exports = app; -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('TimeFork-Spring2015:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /eval.js: -------------------------------------------------------------------------------- 1 | var isNode = typeof module !== 'undefined' && module.exports; 2 | 3 | if (isNode) { 4 | process.once('message', function (code) { 5 | eval(JSON.parse(code).data); 6 | }); 7 | } else { 8 | self.onmessage = function (code) { 9 | eval(code.data); 10 | }; 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TimeFork-Spring2015", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.10.2", 10 | "cookie-parser": "~1.3.3", 11 | "debug": "~2.1.1", 12 | "express": "~4.11.1", 13 | "jade": "~1.9.1", 14 | "morgan": "~1.5.1", 15 | "ejs": "*", 16 | "serve-favicon": "~2.2.0", 17 | "ml-som": "*", 18 | "fast-csv": "*", 19 | "d3": "*", 20 | "brain": "*", 21 | "paralleljs": "*", 22 | "csv-load-sync": "*", 23 | "random-forest-classifier": "*", 24 | "node-som": "*", 25 | "historic": "*" 26 | } 27 | } -------------------------------------------------------------------------------- /public/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/.DS_Store -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | /* set size of body to full page and remove margins */ 4 | 5 | width: 99%; 6 | height: 99%; 7 | padding: 5px; 8 | margin: 0; 9 | font-family: Helvetica-Light, FFDinWebPro, "Helvetica Neue", Helvetica, Arial, sans-serif; 10 | overflow: hidden; 11 | 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | display: none; 16 | } 17 | 18 | #stocklist { 19 | overflow-y: scroll; 20 | list-style-type: none; 21 | margin: 0; 22 | width: 100%; 23 | height: 100%; 24 | border: none; 25 | } 26 | 27 | #button_panel { 28 | height: 50px; 29 | } 30 | 31 | option.ui-widget-content { 32 | margin: 2px; 33 | padding: 3px; 34 | font-size: 11px; 35 | font-weight: 500; 36 | height: 18px; 37 | cursor: pointer; 38 | border: 1px solid; 39 | } 40 | 41 | #next_button { 42 | background: url('../images/next_button.png') no-repeat; 43 | background-size: 30px 30px; 44 | width: 30px; 45 | height: 30px; 46 | cursor: pointer; 47 | } 48 | 49 | #next_button:hover { 50 | background: url('../images/next_button.png') no-repeat; 51 | background-size: 30px 30px; 52 | width: 30px; 53 | height: 30px; 54 | -moz-box-shadow: 0px 0px 5px #666; 55 | -webkit-box-shadow: 0px 0px 5px #666; 56 | box-shadow: 0px 0px 5px #666; 57 | } 58 | 59 | #next_button:click { 60 | background: url('../images/next_button.png') no-repeat; 61 | background-size: 30px 30px; 62 | width: 30px; 63 | height: 30px; 64 | -moz-box-shadow: 0px 0px 5px #000; 65 | -webkit-box-shadow: 0px 0px 5px #000; 66 | box-shadow: 0px 0px 5px #000; 67 | } 68 | 69 | #add_button { 70 | background: url('../images/add_grey.png') no-repeat; 71 | width: 50px; 72 | height: 50px; 73 | position: absolute; 74 | left: 95%; 75 | top: 25%; 76 | cursor: pointer; 77 | border-radius: 50%; 78 | /* border: 10px solid #4679BD;*/ 79 | border: 10px solid #0069B2; 80 | overflow: hidden; 81 | background-color: #0069B2; 82 | box-shadow: 0 0 3px gray; 83 | } 84 | 85 | #add_button:hover { 86 | background: url('../images/add_color.png') no-repeat; 87 | background-color: #0069B2; 88 | } 89 | 90 | #remove_button { 91 | background: url('../images/remove_grey.png'); 92 | background-size: 30px 30px; 93 | width: 30px; 94 | height: 30px; 95 | cursor: pointer; 96 | } 97 | 98 | #remove_button:hover { 99 | background: url('../images/remove_color.png'); 100 | background-size: 30px 30px; 101 | width: 30px; 102 | height: 30px; 103 | } 104 | 105 | #expand_button { 106 | background: url('../images/expand_grey.png'); 107 | background-size: 30px 30px; 108 | width: 30px; 109 | height: 30px; 110 | cursor: pointer; 111 | } 112 | 113 | #expand_button:hover { 114 | background: url('../images/expand_color.png'); 115 | background-size: 30px 30px; 116 | width: 30px; 117 | height: 30px; 118 | } 119 | 120 | .expandClass { 121 | /* background: url('../images/expand_grey.png');*/ 122 | 123 | background-size: 15px 15px; 124 | width: 100%; 125 | height: 15px; 126 | cursor: pointer; 127 | color: gray; 128 | } 129 | 130 | .boundaryLine { 131 | stroke: #EEE; 132 | } 133 | 134 | .expandClass:hover { 135 | /*background: url('../images/expand_color.png');*/ 136 | 137 | color: black; 138 | } 139 | 140 | #settings_button { 141 | background: url('../images/settings_grey.png'); 142 | background-size: 30px 30px; 143 | width: 30px; 144 | height: 30px; 145 | cursor: pointer; 146 | } 147 | 148 | #settings_button:hover { 149 | background: url('../images/settings_color.png'); 150 | background-size: 30px 30px; 151 | width: 30px; 152 | height: 30px; 153 | } 154 | 155 | .axis path, 156 | .axis line { 157 | fill: none; 158 | stroke: #D9D9D9; 159 | shape-rendering: crispEdges; 160 | } 161 | 162 | .x.axis path { 163 | stroke: #D9D9D9; 164 | shape-rendering: crispEdges; 165 | } 166 | 167 | .overlay { 168 | fill: none; 169 | pointer-events: all; 170 | } 171 | 172 | .predictionRect { 173 | /* fill: rgba(255, 227, 107, 0.1);*/ 174 | fill: transparent; 175 | } 176 | 177 | g.context rect.background { 178 | fill: transparent; 179 | visibility: visible !important; 180 | } 181 | 182 | g.context g.brush rect.background { 183 | fill: rgba(1, 1, 1, 0.5); 184 | } 185 | 186 | .x.brush { 187 | fill: #4679BD; 188 | fill-opacity: 0.1; 189 | stroke: #0069B2; 190 | stroke-width: 5px; 191 | } 192 | 193 | g.context g.axis path { 194 | stroke-opacity: 0; 195 | } 196 | 197 | g.context g.brush rect.background { 198 | fill: transparent; 199 | } 200 | 201 | g.context g.brush rect.extent { 202 | stroke: #ffffff; 203 | fill-opacity: .25; 204 | shape-rendering: crispEdges; 205 | z-index: 3; 206 | } 207 | 208 | g.context g.axis line { 209 | stroke-opacity: 0.1; 210 | } 211 | 212 | svg { 213 | font-size: 9px; 214 | font-weight: 500; 215 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8); 216 | } 217 | 218 | .predictionChart { 219 | position: relative; 220 | left: 483px; 221 | top: 0px; 222 | float: left; 223 | } 224 | 225 | .Person-title { 226 | font-size: 11px; 227 | font-weight: 100; 228 | } 229 | 230 | .instructions { 231 | font-size: 11px; 232 | color: rgba(0, 0, 0, 1); 233 | } 234 | 235 | g.tick text { 236 | color: rgba(244, 0, 0, 0.5); 237 | } 238 | 239 | #dialog-confirm { 240 | visibility: visible; 241 | width: auto; 242 | height: auto; 243 | } 244 | 245 | .ui-dialog { 246 | visibility: visible; 247 | font-size: 11px; 248 | font-weight: 500; 249 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8); 250 | } 251 | 252 | .content { 253 | width: 100%; 254 | height: 100%; 255 | position: relative; 256 | } 257 | 258 | .content #linechart-viz { 259 | float: left; 260 | color: #D9D9D9; 261 | overflow: hidden; 262 | width: 80%; 263 | height: 80%; 264 | float: left; 265 | display: block; 266 | margin-left: auto; 267 | margin-right: auto; 268 | } 269 | 270 | .stockChart { 271 | width: 49%; 272 | height: 200px; 273 | padding: 0px; 274 | float: left; 275 | border: 1px solid rgba(180, 180, 180, 0.2); 276 | overflow-x: scroll; 277 | display: block; 278 | cursor: crosshair; 279 | } 280 | 281 | .expandedStockChart { 282 | width: 98%; 283 | height: 200px; 284 | display: block; 285 | } 286 | 287 | .volumeBar { 288 | float: bottom; 289 | vertical-align: bottom; 290 | } 291 | 292 | .content #linechart-viz:hover { 293 | overflow-y: scroll; 294 | } 295 | 296 | .content .bottom { 297 | width: 80%; 298 | height: 27%; 299 | position: absolute; 300 | top: 73%; 301 | font-size: 12px; 302 | font-weight: 500; 303 | /* 304 | -moz-box-shadow: 0px 0px 5px #ccc; 305 | -webkit-box-shadow: 0px 0px 5px #ccc; 306 | box-shadow: 0px 0px 5px #ccc; 307 | */ 308 | border: 1px solid black; 309 | } 310 | 311 | .content .bottom #overviewchart-viz { 312 | float: left; 313 | background-color: white; 314 | } 315 | 316 | .overviewchart { 317 | margin-left: auto; 318 | margin-right: auto; 319 | display: block; 320 | border: 1px; 321 | background-color: transparent; 322 | } 323 | 324 | .content .right { 325 | width: 19%; 326 | height: 100%; 327 | float: right; 328 | font-size: 10px; 329 | font-weight: 500; 330 | } 331 | 332 | .predictionLine { 333 | /* 334 | stroke-dasharray: 3, 3; 335 | stroke-width: 0.5px; 336 | */ 337 | } 338 | 339 | #search_box { 340 | font-size: 14px; 341 | font-weight: 500; 342 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8); 343 | padding: 0px; 344 | margin-bottom: 5px; 345 | } 346 | 347 | input#search { 348 | width: 100%; 349 | height: 3%; 350 | text-align: left; 351 | font-size: 12px; 352 | font-weight: 500; 353 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8); 354 | padding: 0px; 355 | } 356 | 357 | div#prediction_buttons { 358 | height: auto; 359 | margin-left: auto; 360 | margin-right: auto; 361 | } 362 | 363 | div#prediction_buttons table { 364 | background-color: #D9D9D9; 365 | } 366 | 367 | div#prediction_buttons table td { 368 | padding: 3px; 369 | } 370 | 371 | div.ui_button { 372 | width: auto; 373 | height: 30px; 374 | background-color: #aaa; 375 | font-size: 12px; 376 | font-weight: 500; 377 | text-align: center; 378 | line-height: 30px; 379 | border: 1px solid; 380 | padding: 3px; 381 | } 382 | 383 | div.ui_button:hover { 384 | background-color: #888; 385 | } 386 | 387 | .ui_button label { 388 | display: inline-block; 389 | vertical-align: middle; 390 | } 391 | 392 | 393 | .ui-dialog-content { 394 | background-color: #ccc; 395 | } 396 | 397 | .ui-widget-content { 398 | background-color: #ffffff; 399 | border: 1px solid #222; 400 | } 401 | 402 | .ui-dialog-titlebar { 403 | background-color: #888; 404 | } 405 | 406 | .line { 407 | fill: none; 408 | stroke-width: 1.5px; 409 | } 410 | 411 | .line.bands { 412 | stroke: black; 413 | stroke-opacity: 0.1; 414 | } 415 | 416 | .area.bands { 417 | fill: black; 418 | fill-opacity: 0.09; 419 | stroke: none; 420 | } 421 | 422 | .tprediction.bands { 423 | fill: #0069B2; 424 | fill-opacity: 0.2; 425 | stroke: none; 426 | stroke-width: 1px; 427 | pointer-events: none; 428 | display: none; 429 | } 430 | 431 | .sprediction.bands { 432 | fill: #fc8d59; 433 | fill-opacity: 0.3; 434 | stroke: none; 435 | stroke-width: 1px; 436 | pointer-events: none; 437 | display: none; 438 | } 439 | 440 | 441 | #correlationViewer { 442 | padding: 50px; 443 | } 444 | 445 | #choosestock { 446 | height: 30%; 447 | border: 1px solid black; 448 | overflow: hidden; 449 | width: 100%; 450 | margin-bottom: 5px; 451 | } 452 | 453 | #twitter-viewer { 454 | width: 100%; 455 | height: 34%; 456 | font-size: 12px; 457 | font-weight: 500; 458 | background-color: rgba(255, 255, 255, 0.8); 459 | border: 1px solid black; 460 | margin-bottom: 5px; 461 | margin-top: 5px; 462 | overflow-x: hidden; 463 | overflow-y: scroll; 464 | } 465 | 466 | #correlation-viewer { 467 | width: 100%; 468 | height: 30%; 469 | font-size: 12px; 470 | font-weight: 500; 471 | background-color: rgba(255, 255, 255, 0.8); 472 | border: 1px solid black; 473 | } 474 | 475 | .temporalPredictionLine { 476 | pointer-events: none; 477 | /* display: none;*/ 478 | } 479 | 480 | .predictionLine { 481 | pointer-events: none; 482 | } -------------------------------------------------------------------------------- /public/data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/data/.DS_Store -------------------------------------------------------------------------------- /public/data/topInternetStocks.csv: -------------------------------------------------------------------------------- 1 | company,symbols 2 | Apple,AAPL 3 | Amazon,AMZN 4 | Wix,WIX 5 | Facebook,FB 6 | Fireeye,FEYE 7 | Nice Systems,NICE 8 | Cogent Communications,CCOI 9 | Netflix,NFLX 10 | Veeva Systems,VEEV 11 | Baidu,BIDU -------------------------------------------------------------------------------- /public/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/add.png -------------------------------------------------------------------------------- /public/images/add_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/add_color.png -------------------------------------------------------------------------------- /public/images/add_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/add_grey.png -------------------------------------------------------------------------------- /public/images/expand_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/expand_color.png -------------------------------------------------------------------------------- /public/images/expand_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/expand_grey.png -------------------------------------------------------------------------------- /public/images/icons.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/icons.pptx -------------------------------------------------------------------------------- /public/images/next_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/next_button.png -------------------------------------------------------------------------------- /public/images/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/remove.png -------------------------------------------------------------------------------- /public/images/remove_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/remove_color.png -------------------------------------------------------------------------------- /public/images/remove_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/remove_grey.png -------------------------------------------------------------------------------- /public/images/settings_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/settings_color.png -------------------------------------------------------------------------------- /public/images/settings_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/settings_grey.png -------------------------------------------------------------------------------- /public/images/stockfork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthikbadam/TimeFork/743a817fa1f703332a7a24cce43344736d19f7f0/public/images/stockfork.png -------------------------------------------------------------------------------- /public/images/~$icons.pptx: -------------------------------------------------------------------------------- 1 | lupin lupin -------------------------------------------------------------------------------- /public/js/brain-0.6.0.min.js: -------------------------------------------------------------------------------- 1 | (function(){function b(){return b}function c(b,d){var e=c.resolve(b),f=c.modules[e];if(!f)throw new Error('failed to require "'+b+'" from '+d);return f.exports||(f.exports={},f.call(f.exports,f,f.exports,c.relative(e),a)),f.exports}var a=this;c.modules={},c.resolve=function(a){var b=a,d=a+".js",e=a+"/index.js";return c.modules[d]&&d||c.modules[e]&&e||b},c.register=function(a,b){c.modules[a]=b},c.relative=function(a){return function(d){if("debug"==d)return b;if("."!=d.charAt(0))return c(d);var e=a.split("/"),f=d.split("/");e.pop();for(var g=0;g0){this.biases[b]=j(c),this.weights[b]=new Array(c),this.changes[b]=new Array(c);for(var d=0;dd;o++){var p=0;for(var q=0;q=0;b--)for(var c=0;cb?1:0,p=n[0]):(o=m.indexOf(e(m).max()),p=n.indexOf(e(n).max()));if(o!=p){var q=a[l];e(q).extend({actual:o,expected:p}),i.push(q)}c&&(o==0&&p==0?h++:o==1&&p==1?g++:o==0&&p==1?f++:o==1&&p==0&&d++);var r=m.map(function(a,b){return n[b]-a});j+=k(r)}var s=j/a.length,t={error:s,misclasses:i};return c&&e(t).extend({trueNeg:h,truePos:g,falseNeg:f,falsePos:d,total:a.length,precision:g/(g+d),recall:g/(g+f),accuracy:(h+g)/a.length}),t},toJSON:function(){var a=[];for(var b=0;b<=this.outputLayer;b++){a[b]={};var c;b==0&&this.inputLookup?c=e(this.inputLookup).keys():b==this.outputLayer&&this.outputLookup?c=e(this.outputLookup).keys():c=e.range(0,this.sizes[b]);for(var d=0;d0){a[b][f].bias=this.biases[b][d],a[b][f].weights={};for(var g in a[b-1]){var h=g;b==1&&this.inputLookup&&(h=this.inputLookup[g]),a[b][f].weights[g]=this.weights[b][d][h]}}}}return{layers:a}},fromJSON:function(a){var b=a.layers.length;this.outputLayer=b-1,this.sizes=new Array(b),this.weights=new Array(b),this.biases=new Array(b),this.outputs=new Array(b);for(var c=0;c<=this.outputLayer;c++){var d=a.layers[c];c==0&&!d[0]?this.inputLookup=f.lookupFromHash(d):c==this.outputLayer&&!d[0]&&(this.outputLookup=f.lookupFromHash(d));var g=e(d).keys();this.sizes[c]=g.length,this.weights[c]=[],this.biases[c]=[],this.outputs[c]=[];for(var h in g){var i=g[h];this.biases[c][h]=d[i].bias,this.weights[c][h]=e(d[i].weights).toArray()}}return this},toFunction:function(){var a=this.toJSON();return new Function("inputs"," var net = "+JSON.stringify(a)+";\n\n for(var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var outputs = {};\n for(var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n for(var iid in node.weights)\n sum += node.weights[iid] * inputs[iid];\n outputs[id] = (1/(1 + Math.exp(-sum)));\n }\n inputs = outputs;\n }\n return outputs;")}},b.NeuralNetwork=g}),c.register("lookup.js",function(a,b,c,d){function f(a){var b=e(a).reduce(function(a,b){return e(a).extend(b)},{});return g(b)}function g(a){var b={},c=0;for(var d in a)b[d]=c++;return b}function h(a,b){var c=[];for(var d in a)c[a[d]]=b[d]||0;return c}function i(a,b){var c={};for(var d in a)c[d]=b[a[d]];return c}var e=c("underscore");a.exports={buildLookup:f,lookupFromHash:g,toArray:h,toHash:i}}),c.register("underscore",function(a,b,c,d){(function(){function C(a,b,c){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;a._chain&&(a=a._wrapped),b._chain&&(b=b._wrapped);if(a.isEqual&&y.isFunction(a.isEqual))return a.isEqual(b);if(b.isEqual&&y.isFunction(b.isEqual))return b.isEqual(a);var d=k.call(a);if(d!=k.call(b))return!1;switch(d){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object")return!1;var e=c.length;while(e--)if(c[e]==a)return!0;c.push(a);var f=0,g=!0;if(d=="[object Array]"){f=a.length,g=f==b.length;if(g)while(f--)if(!(g=f in a==f in b&&C(a[f],b[f],c)))break}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return!1;for(var h in a)if(y.has(a,h)){f++;if(!(g=y.has(b,h)&&C(a[h],b[h],c)))break}if(g){for(h in b)if(y.has(b,h)&&!(f--))break;g=!f}}return c.pop(),g}var c=this,d=c._,e={},f=Array.prototype,g=Object.prototype,h=Function.prototype,i=f.slice,j=f.unshift,k=g.toString,l=g.hasOwnProperty,m=f.forEach,n=f.map,o=f.reduce,p=f.reduceRight,q=f.filter,r=f.every,s=f.some,t=f.indexOf,u=f.lastIndexOf,v=Array.isArray,w=Object.keys,x=h.bind,y=function(a){return new K(a)};typeof b!="undefined"?(typeof a!="undefined"&&a.exports&&(b=a.exports=y),b._=y):c._=y,y.VERSION="1.3.3";var z=y.each=y.forEach=function(a,b,c){if(a==null)return;if(m&&a.forEach===m)a.forEach(b,c);else if(a.length===+a.length){for(var d=0,f=a.length;d2;a==null&&(a=[]);if(o&&a.reduce===o)return d&&(b=y.bind(b,d)),e?a.reduce(b,c):a.reduce(b);z(a,function(a,f,g){e?c=b.call(d,c,a,f,g):(c=a,e=!0)});if(!e)throw new TypeError("Reduce of empty array with no initial value");return c},y.reduceRight=y.foldr=function(a,b,c,d){var e=arguments.length>2;a==null&&(a=[]);if(p&&a.reduceRight===p)return d&&(b=y.bind(b,d)),e?a.reduceRight(b,c):a.reduceRight(b);var f=y.toArray(a).reverse();return d&&!e&&(b=y.bind(b,d)),e?y.reduce(f,b,c,d):y.reduce(f,b)},y.find=y.detect=function(a,b,c){var d;return A(a,function(a,e,f){if(b.call(c,a,e,f))return d=a,!0}),d},y.filter=y.select=function(a,b,c){var d=[];return a==null?d:q&&a.filter===q?a.filter(b,c):(z(a,function(a,e,f){b.call(c,a,e,f)&&(d[d.length]=a)}),d)},y.reject=function(a,b,c){var d=[];return a==null?d:(z(a,function(a,e,f){b.call(c,a,e,f)||(d[d.length]=a)}),d)},y.every=y.all=function(a,b,c){var d=!0;return a==null?d:r&&a.every===r?a.every(b,c):(z(a,function(a,f,g){if(!(d=d&&b.call(c,a,f,g)))return e}),!!d)};var A=y.some=y.any=function(a,b,c){b||(b=y.identity);var d=!1;return a==null?d:s&&a.some===s?a.some(b,c):(z(a,function(a,f,g){if(d||(d=b.call(c,a,f,g)))return e}),!!d)};y.include=y.contains=function(a,b){var c=!1;return a==null?c:t&&a.indexOf===t?a.indexOf(b)!=-1:(c=A(a,function(a){return a===b}),c)},y.invoke=function(a,b){var c=i.call(arguments,2);return y.map(a,function(a){return(y.isFunction(b)?b||a:a[b]).apply(a,c)})},y.pluck=function(a,b){return y.map(a,function(a){return a[b]})},y.max=function(a,b,c){if(!b&&y.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!b&&y.isEmpty(a))return-Infinity;var d={computed:-Infinity};return z(a,function(a,e,f){var g=b?b.call(c,a,e,f):a;g>=d.computed&&(d={value:a,computed:g})}),d.value},y.min=function(a,b,c){if(!b&&y.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!b&&y.isEmpty(a))return Infinity;var d={computed:Infinity};return z(a,function(a,e,f){var g=b?b.call(c,a,e,f):a;gd?1:0}),"value")},y.groupBy=function(a,b){var c={},d=y.isFunction(b)?b:function(a){return a[b]};return z(a,function(a,b){var e=d(a,b);(c[e]||(c[e]=[])).push(a)}),c},y.sortedIndex=function(a,b,c){c||(c=y.identity);var d=0,e=a.length;while(d>1;c(a[f])=0})})},y.difference=function(a){var b=y.flatten(i.call(arguments,1),!0);return y.filter(a,function(a){return!y.include(b,a)})},y.zip=function(){var a=i.call(arguments),b=y.max(y.pluck(a,"length")),c=new Array(b);for(var d=0;d=0;c--)b=[a[c].apply(this,b)];return b[0]}},y.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}},y.keys=w||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[];for(var c in a)y.has(a,c)&&(b[b.length]=c);return b},y.values=function(a){return y.map(a,y.identity)},y.functions=y.methods=function(a){var b=[];for(var c in a)y.isFunction(a[c])&&b.push(c);return b.sort()},y.extend=function(a){return z(i.call(arguments,1),function(b){for(var c in b)a[c]=b[c]}),a},y.pick=function(a){var b={};return z(y.flatten(i.call(arguments,1)),function(c){c in a&&(b[c]=a[c])}),b},y.defaults=function(a){return z(i.call(arguments,1),function(b){for(var c in b)a[c]==null&&(a[c]=b[c])}),a},y.clone=function(a){return y.isObject(a)?y.isArray(a)?a.slice():y.extend({},a):a},y.tap=function(a,b){return b(a),a},y.isEqual=function(a,b){return C(a,b,[])},y.isEmpty=function(a){if(a==null)return!0;if(y.isArray(a)||y.isString(a))return a.length===0;for(var b in a)if(y.has(a,b))return!1;return!0},y.isElement=function(a){return!!a&&a.nodeType==1},y.isArray=v||function(a){return k.call(a)=="[object Array]"},y.isObject=function(a){return a===Object(a)},y.isArguments=function(a){return k.call(a)=="[object Arguments]"},y.isArguments(arguments)||(y.isArguments=function(a){return!!a&&!!y.has(a,"callee")}),y.isFunction=function(a){return k.call(a)=="[object Function]"},y.isString=function(a){return k.call(a)=="[object String]"},y.isNumber=function(a){return k.call(a)=="[object Number]"},y.isFinite=function(a){return y.isNumber(a)&&isFinite(a)},y.isNaN=function(a){return a!==a},y.isBoolean=function(a){return a===!0||a===!1||k.call(a)=="[object Boolean]"},y.isDate=function(a){return k.call(a)=="[object Date]"},y.isRegExp=function(a){return k.call(a)=="[object RegExp]"},y.isNull=function(a){return a===null},y.isUndefined=function(a){return a===void 0},y.has=function(a,b){return l.call(a,b)},y.noConflict=function(){return c._=d,this},y.identity=function(a){return a},y.times=function(a,b,c){for(var d=0;d/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},y.result=function(a,b){if(a==null)return null;var c=a[b];return y.isFunction(c)?c.call(a):c},y.mixin=function(a){z(y.functions(a),function(b){M(b,y[b]=a[b])})};var D=0;y.uniqueId=function(a){var b=D++;return a?a+b:b},y.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var E=/.^/,F={"\\":"\\","'":"'",r:"\r",n:"\n",t:" ",u2028:"\u2028",u2029:"\u2029"};for(var G in F)F[F[G]]=G;var H=/\\|'|\r|\n|\t|\u2028|\u2029/g,I=/\\(\\|'|r|n|t|u2028|u2029)/g,J=function(a){return a.replace(I,function(a,b){return F[b]})};y.template=function(a,b,c){c=y.defaults(c||{},y.templateSettings);var d="__p+='"+a.replace(H,function(a){return"\\"+F[a]}).replace(c.escape||E,function(a,b){return"'+\n_.escape("+J(b)+")+\n'"}).replace(c.interpolate||E,function(a,b){return"'+\n("+J(b)+")+\n'"}).replace(c.evaluate||E,function(a,b){return"';\n"+J(b)+"\n;__p+='"})+"';\n";c.variable||(d="with(obj||{}){\n"+d+"}\n"),d="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+d+"return __p;\n";var e=new Function(c.variable||"obj","_",d);if(b)return e(b,y);var f=function(a){return e.call(this,a,y)};return f.source="function("+(c.variable||"obj")+"){\n"+d+"}",f},y.chain=function(a){return y(a).chain()};var K=function(a){this._wrapped=a};y.prototype=K.prototype;var L=function(a,b){return b?y(a).chain():a},M=function(a,b){K.prototype[a]=function(){var a=i.call(arguments);return j.call(a,this._wrapped),L(b.apply(y,a),this._chain)}};y.mixin(y),z(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=f[a];K.prototype[a]=function(){var c=this._wrapped;b.apply(c,arguments);var d=c.length;return(a=="shift"||a=="splice")&&d===0&&delete c[0],L(c,this._chain)}}),z(["concat","join","slice"],function(a){var b=f[a];K.prototype[a]=function(){return L(b.apply(this._wrapped,arguments),this._chain)}}),K.prototype.chain=function(){return this._chain=!0,this},K.prototype.value=function(){return this._wrapped}}).call(this)}),brain=c("neuralnetwork.js")})(); -------------------------------------------------------------------------------- /public/js/encog-js-1.0.js: -------------------------------------------------------------------------------- 1 | var ENCOG=ENCOG||{VERSION:"1.0",PLATFORM:"javascript",precision:1e-10,NEWLINE:"\n",ENCOG_TYPE_ACTIVATION:"ActivationFunction",ENCOG_TYPE_RBF:"RBF"};ENCOG.namespace=function(b){var f=b.split("."),d=window,a="",c,e;for(c=0,e=f.length;c0){return 1}else{return -1}}};ENCOG.MathUtil.euclideanDistance=function(d,c,g,b){var a=0,e,f;for(e=g;e<(g+b);e+=1){f=d[e]-c[e];a+=f*f}return Math.sqrt(a)};ENCOG.MathUtil.kNearest=function(a,d,c,e,l,g){var m=[],i=[],j=0,f=-1,h,b;while(j0){a.push(0);b-=1}return a};ENCOG.ArrayUtil.newIntArray=function(b){var a;a=[];while((b-=1)>0){a.push(0)}return a};ENCOG.ArrayUtil.fillArray2D=function(a,b){var d,c,e;for(d=0;db){b=c[d];a=d}}return a};ENCOG.Util.stripQuotes=function(b){var a=b.length;if(b[0]==='"'||b[0]==="'"){b=b.substr(1);a-=1}if(b[a-1]==='"'||b[a-1]==="'"){b=b.substr(0,a-1)}return b};ENCOG.ActivationSigmoid=function(){};ENCOG.ActivationSigmoid.prototype={NAME:"ActivationSigmoid",encogType:ENCOG.ENCOG_TYPE_ACTIVATION,activationFunction:function(a,d,c){var b;for(b=d;bENCOG.precision},calcContextCount:function(){if(this.contextFedBy===null){return 0}else{return this.contextFedBy.count}}};ENCOG.BasicLayer.create=function(d,c,b){var a;if(d.encogType!==ENCOG.ENCOG_TYPE_ACTIVATION){throw new Error("Invalid activation function.")}a=new ENCOG.BasicLayer();a.activation=d;a.count=c;a.biasActivation=b;a.contextFedBy=null;return a};ENCOG.BasicNetwork=function(){};ENCOG.BasicNetwork.prototype={NAME:"BasicNetwork",inputCount:null,outputCount:null,layerCounts:null,layerContextCount:null,weightIndex:null,layerIndex:null,activationFunctions:null,layerFeedCounts:null,contextTargetOffset:null,contextTargetSize:null,biasActivation:null,beginTraining:null,endTraining:null,weights:null,layerOutput:null,layerSums:null,connectionLimit:ENCOG.precision,clearContext:function(){var a,b,c;a=0;for(b=0;b0;d-=1){this.computeLayer(d)}e=this.contextTargetOffset[0];ENCOG.ArrayUtil.arrayCopy(this.layerOutput,0,this.layerOutput,e,this.contextTargetSize[0]);ENCOG.ArrayUtil.arrayCopy(this.layerOutput,0,b,0,this.outputCount)},evaluate:function(l,c){var f,e,h,d,b,k,a,g;b=[];a=0;g=0;for(f=0;f=0;f-=1){g=e[f];d=null;if(f>0){d=e[f-1]}m.biasActivation[k]=g.biasActivation;m.layerCounts[k]=g.calcTotalCount();m.layerFeedCounts[k]=g.count;m.layerContextCount[k]=g.calcContextCount();m.activationFunctions[k]=g.activation;b+=g.calcTotalCount();if(d!==null){h+=g.count*d.calcTotalCount()}if(k===0){m.weightIndex[k]=0;m.layerIndex[k]=0}else{m.weightIndex[k]=m.weightIndex[k-1]+(m.layerCounts[k]*m.layerFeedCounts[k-1]);m.layerIndex[k]=m.layerIndex[k-1]+m.layerCounts[k-1]}a=0;for(c=e.length-1;c>=0;c-=1){if(e[c].contextFedBy===g){m.hasContext=true;m.contextTargetSize[k]=e[c].calcContextCount();m.contextTargetOffset[k]=a+(e[c].calcTotalCount()-e[c].calcContextCount())}a+=e[c].calcTotalCount()}k+=1}m.beginTraining=0;m.endTraining=m.layerCounts.length-1;m.weights=ENCOG.ArrayUtil.allocate1D(h);m.layerOutput=ENCOG.ArrayUtil.allocate1D(b);m.layerSums=ENCOG.ArrayUtil.allocate1D(b);m.clearContext()}return m};ENCOG.PropagationTrainer=function(){};ENCOG.PropagationTrainer.prototype={NAME:"PropagationTrainer",POSITIVE_ETA:1.2,NEGATIVE_ETA:0.5,DELTA_MIN:0.000001,MAX_STEP:50,network:null,trainingInput:null,trainingIdeal:null,type:null,learningRate:null,momentum:null,layerDelta:null,gradients:null,lastGradient:null,lastDelta:null,actual:null,flatSpot:null,errorFunction:ENCOG.LinearErrorFunction.create(),updateValues:null,processLevel:function(c){var b,e,i,g,k,j,m,f,d,h,l,a,n,o;e=this.network.layerIndex[c+1];b=this.network.layerIndex[c];g=this.network.layerCounts[c+1];k=this.network.layerFeedCounts[c];i=this.network.weightIndex[c];j=this.network.activationFunctions[c+1];m=this.flatSpot[c+1];f=e;for(n=0;n0){d=this.updateValues[a]*this.POSITIVE_ETA;d=Math.min(d,this.MAX_STEP);b=ENCOG.MathUtil.sign(this.gradients[a])*d;this.updateValues[a]=d;this.lastGradient[a]=this.gradients[a]}else{if(c<0){d=this.updateValues[a]*this.NEGATIVE_ETA;d=Math.max(d,this.DELTA_MIN);this.updateValues[a]=d;b=-this.lastDelta[a];this.lastGradient[a]=0}else{if(c===0){d=this.updateValues[a];b=ENCOG.MathUtil.sign(this.gradients[a])*d;this.lastGradient[a]=this.gradients[a]}}}this.network.weights[a]+=b}},process:function(b,a,e){var d,c,f;this.network.compute(b,this.actual);for(c=0;c0){b=ENCOG.ArrayUtil.arrayMean(d,0);a=ENCOG.ArrayUtil.arrayMean(d,1);m=b-this.agents[e][0];k=a-this.agents[e][1];c=(Math.atan2(m,k)*180/Math.PI)-this.agents[e][2];c+=180}f=0;if(j.length>0){f=ENCOG.ArrayUtil.arrayMean(j,2)-this.agents[e][2]}if(this.callbackNeighbors!==null){this.callbackNeighbors(e,j)}h=0;if(j.length>0){b=ENCOG.ArrayUtil.arrayMean(this.agents,0);a=ENCOG.ArrayUtil.arrayMean(this.agents,1);m=b-this.agents[e][0];k=a-this.agents[e][1];h=(Math.atan2(m,k)*180/Math.PI)-this.agents[e][2]}l=(h*this.constCohesion)+(f*this.constAlignment)+(c*this.constSeparation);this.agents[e][2]+=l}}};ENCOG.Swarm.create=function(b){var a=new ENCOG.Swarm();a.agents=b;return a};ENCOG.Anneal=function(){};ENCOG.Anneal.prototype={NAME:"Anneal",solution:null,scoreSolution:null,randomize:null,constStartTemp:10,constStopTemp:2,constCycles:10,iteration:function(){var c,b,a,e,d;c=this.solution.slice();b=this.constStartTemp;a=this.scoreSolution(this.solution);for(d=0;dthis.constMutationPercent){this.mutate(this.population[b].data)}if(Math.random()>this.constMutationPercent){this.mutate(this.population[b].data)}this.population[b].score=this.scoreSolution(this.population[b].data);this.population[b+1].score=this.scoreSolution(this.population[b+1].data);b+=2}this.sortPopulation()},createPopulation:function(e,b){var c,f,a;this.population=[];for(c=0;cthis.inputCount){throw new Error("Can't classify SOM with input size of "+this.inputCount+" with input data of count "+d.length)}b=Number.POSITIVE_INFINITY;a=-1;for(c=0;cthis.som.inputCount){throw new Error("Can't train SOM with input size of "+this.inputCount+" with input data of count "+b.length)}d=Number.POSITIVE_INFINITY;for(c=0;cthis.worstDistance){this.worstDistance=d}return a},train:function(b,a){},applyCorrection:function(){}};ENCOG.TrainSOM.create=function(c,b){var a=new ENCOG.TrainSOM();a.som=c;a.learningRate=b;a.correctionMatrix=ENCOG.ArrayUtil.allocateBoolean2D(this.som.outputCount,this.som.inputCount);return a};ENCOG.ReadCSV=function(){};ENCOG.ReadCSV.prototype={regStr:null,inputData:null,idealData:null,inputCount:0,idealCount:0,delimiter:",",readCSV:function(c,a,f){var b,e,h,g,i;this.inputCount=a;this.idealCount=f;e=new RegExp(this.regStr,"gi");this.inputData=[[]];this.idealData=[[]];b=0;while(h=e.exec(c)){i=h[1];if(i.length&&(i!=this.delimiter)){this.inputData.push([]);this.idealData.push([]);b=0}if(h[2]){g=h[2].replace(new RegExp('""',"g"),'"')}else{g=h[3]}if(b0){a+=","}a+=b[c]}return a};ENCOG.EGFILE=function(){};ENCOG.EGFILE.save=function(e){var a="",c,d,b;c=(new Date()).getTime();a+="encog,BasicNetwork,"+ENCOG.PLATFORM+",3.1.0,1,"+c+ENCOG.NEWLINE;a+="[BASIC]"+ENCOG.NEWLINE;a+="[BASIC:PARAMS]"+ENCOG.NEWLINE;a+="[BASIC:NETWORK]"+ENCOG.NEWLINE;a+="beginTraining="+e.beginTraining+ENCOG.NEWLINE;a+="connectionLimit="+e.connectionLimit+ENCOG.NEWLINE;a+="contextTargetOffset="+ENCOG.ReadCSV.toCommaList(e.contextTargetOffset)+ENCOG.NEWLINE;a+="contextTargetSize="+ENCOG.ReadCSV.toCommaList(e.contextTargetSize)+ENCOG.NEWLINE;a+="endTraining="+e.endTraining+ENCOG.NEWLINE;a+="hasContext="+(e.hasContext?"t":"f")+ENCOG.NEWLINE;a+="inputCount="+e.inputCount+ENCOG.NEWLINE;a+="layerCounts="+ENCOG.ReadCSV.toCommaList(e.layerCounts)+ENCOG.NEWLINE;a+="layerFeedCounts="+ENCOG.ReadCSV.toCommaList(e.layerFeedCounts)+ENCOG.NEWLINE;a+="layerContextCount="+ENCOG.ReadCSV.toCommaList(e.layerContextCount)+ENCOG.NEWLINE;a+="layerIndex="+ENCOG.ReadCSV.toCommaList(e.layerIndex)+ENCOG.NEWLINE;a+="output="+ENCOG.ReadCSV.toCommaList(e.layerOutput)+ENCOG.NEWLINE;a+="outputCount="+e.outputCount+ENCOG.NEWLINE;a+="weightIndex="+ENCOG.ReadCSV.toCommaList(e.weightIndex)+ENCOG.NEWLINE;a+="weights="+ENCOG.ReadCSV.toCommaList(e.weights)+ENCOG.NEWLINE;a+="biasActivation="+ENCOG.ReadCSV.toCommaList(e.biasActivation)+ENCOG.NEWLINE;a+="[BASIC:ACTIVATION]"+ENCOG.NEWLINE;for(d=0;d";a.textarea=a.consoleDiv.getElementsByTagName("textarea")[0];return a};ENCOG.GUI.CellGrid=function(){};ENCOG.GUI.CellGrid.prototype={canvas:null,drawingContext:null,canvasDiv:null,canvasWidth:null,canvasHeight:null,gridWidth:null,gridHeight:null,determineColor:null,pointerDown:null,pointerUp:null,pointerMove:null,pointerMode:0,captureTouch:true,outline:false,NAME:"CellGrid",render:function(){var d,i,e,h,f,a,j;this.drawingContext.strokeStyle="grey";for(i=0;iBrowser not supported.';a.canvas=a.canvasDiv.getElementsByTagName("canvas")[0];a.drawingContext=a.canvas.getContext("2d");a.pixW=Math.floor(a.canvas.width/a.gridWidth);a.pixH=Math.floor(a.canvas.height/a.gridHeight);a.canvas.addEventListener("mousedown",function(g){a.ev_canvas(g)},true);a.canvas.addEventListener("mousemove",function(g){a.ev_canvas(g)},true);a.canvas.addEventListener("mouseup",function(g){a.ev_canvas(g)},true);a.canvas.addEventListener("touchstart",function(g){a.ev_canvas(g)},true);a.canvas.addEventListener("touchend",function(g){a.ev_canvas(g)},true);a.canvas.addEventListener("touchmove",function(g){a.ev_canvas(g)},true);a.canvas.addEventListener("mouseout",function(g){a.ev_canvas(g)},true);return a};ENCOG.GUI.Drawing=function(){};ENCOG.GUI.Drawing.create=function(d,b,c){var a=new ENCOG.GUI.Drawing();a.canvasDiv=document.getElementById(d);a.canvasWidth=b;a.canvasHeight=c;a.canvasDiv.innerHTML='Browser not supported.';a.canvas=a.canvasDiv.getElementsByTagName("canvas")[0];a.drawingContext=a.canvas.getContext("2d");a.canvas.addEventListener("mousedown",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mousemove",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mouseup",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchstart",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchend",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchmove",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mouseout",function(f){a.ev_canvas(f)},true);return a};ENCOG.GUI.Drawing.prototype={canvas:null,drawingContext:null,canvasDiv:null,NAME:"Drawing",canvasWidth:null,canvasHeight:null,started:false,downsampleHeight:8,downsampleWidth:5,ev_canvas:function(a){if(a.layerX||a.layerX==0){a._x=a.layerX;a._y=a.layerY}else{if(a.offsetX||a.offsetX==0){a._x=a.offsetX;a._y=a.offsetY}}if(a.type==="mousedown"){this.drawingContext.beginPath();this.drawingContext.moveTo(a._x,a._y);this.started=true}else{if(a.type==="mousemove"){if(this.started){this.drawingContext.lineTo(a._x,a._y);this.drawingContext.stroke()}}else{if(a.type==="mouseup"){if(this.started){this.drawingContext.lineTo(a._x,a._y);this.drawingContext.stroke();this.started=false}}else{if(a.type==="mouseout"){if(this.started){this.drawingContext.lineTo(a._x,a._y);this.drawingContext.stroke();this.started=false}}else{if(a.type==="touchstart"){this.drawingContext.beginPath();this.drawingContext.moveTo(a._x,a._y);this.started=true}else{if(a.type==="touchend"){if(this.started){this.started=false}}else{if(a.type==="touchmove"){if(this.started){this.drawingContext.lineTo(a._x,a._y);this.drawingContext.stroke();a.preventDefault()}}}}}}}}},isHLineClear:function(d){var a=this.drawingContext.getImageData(0,d,this.canvas.width,1);var c=a.data;for(var b=0;b0){return false}}return true},isVLineClear:function(b){var a=this.drawingContext.getImageData(b,0,1,this.canvas.height);var d=a.data;for(var c=0;c0){return false}}return true},performDownSample:function(){var m,a,e,o,f,k,r,p,q,c,h,n,l,j,g,b;m=0;while(this.isHLineClear(m)&&m0){a--}e=0;while(this.isVLineClear(e)&&e0){o--}if(a0){j=true;break}}if(j){r[p++]=1}else{r[p++]=-1}}}return r},clear:function(){this.canvas.width=this.canvas.width}};ENCOG.GUI.Agents2D=function(){};ENCOG.GUI.Agents2D.create=function(d,b,c){var a=new ENCOG.GUI.Agents2D();a.canvasDiv=document.getElementById(d);a.canvasWidth=b;a.canvasHeight=c;a.canvasDiv.innerHTML='Browser not supported.';a.canvas=a.canvasDiv.getElementsByTagName("canvas")[0];a.drawingContext=a.canvas.getContext("2d");a.canvas.addEventListener("mousedown",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mousemove",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mouseup",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchstart",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchend",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchmove",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mouseout",function(f){a.ev_canvas(f)},true);return a};ENCOG.GUI.Agents2D.prototype={canvas:null,drawingContext:null,canvasDiv:null,NAME:"Agents2D",canvasWidth:null,canvasHeight:null,agents:null,agentSize:10,agentSpeed:5,pointerDown:null,pointerUp:null,pointerMove:null,captureTouch:true,ev_canvas:function(a){},reset:function(b){this.agents=[];for(var a=0;athis.canvas.width){this.agents[u][0]=0}}if(this.agents[u][1]<0){this.agents[u][1]=this.canvas.height}else{if(this.agents[u][1]>this.canvas.height){this.agents[u][1]=0}}}},render:function(){var d,q,t,l,n,k,h,w,b,v,a,p,o,n,l,j,s,c,u;var g,f,m,e;this.canvas.width=this.canvas.width;for(u=0;uBrowser not supported.';a.canvas=a.canvasDiv.getElementsByTagName("canvas")[0];a.drawingContext=a.canvas.getContext("2d");a.canvas.addEventListener("mousedown",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mousemove",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mouseup",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchstart",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchend",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("touchmove",function(f){a.ev_canvas(f)},true);a.canvas.addEventListener("mouseout",function(f){a.ev_canvas(f)},true);return a};ENCOG.GUI.TSP.prototype={canvas:null,drawingContext:null,canvasDiv:null,NAME:"TSP",canvasWidth:null,canvasHeight:null,pointerDown:null,pointerUp:null,pointerMove:null,captureTouch:true,cities:null,currentPath:null,tspMargin:10,ev_canvas:function(a){},reset:function(b){var d,c;this.cities=[];d=this.canvas.width-(this.tspMargin*2);c=this.canvas.height-(this.tspMargin*2);for(var a=0;a xMax) xMax = xv; 35 | if (-yv > yMax) yMax = -yv; 36 | if (yv > yMax) yMax = yv; 37 | return [xv, yv]; 38 | }); 39 | 40 | // Compute the new x- and y-scales, and transform. 41 | var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]), 42 | y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]), 43 | t1 = d3_horizonTransform(bands, h, mode); 44 | 45 | // Retrieve the old scales, if this is an update. 46 | if (this.__chart__) { 47 | x0 = this.__chart__.x; 48 | y0 = this.__chart__.y; 49 | t0 = this.__chart__.t; 50 | id = this.__chart__.id; 51 | } else { 52 | x0 = x1.copy(); 53 | y0 = y1.copy(); 54 | t0 = t1; 55 | id = ++d3_horizonId; 56 | } 57 | 58 | // We'll use a defs to store the area path and the clip path. 59 | var defs = g.selectAll("defs") 60 | .data([null]); 61 | 62 | // The clip path is a simple rect. 63 | defs.enter().append("defs").append("clipPath") 64 | .attr("id", "d3_horizon_clip" + id) 65 | .append("rect") 66 | .attr("width", w) 67 | .attr("height", h); 68 | 69 | defs.select("rect").transition() 70 | .duration(duration) 71 | .attr("width", w) 72 | .attr("height", h); 73 | 74 | // We'll use a container to clip all horizon layers at once. 75 | g.selectAll("g") 76 | .data([null]) 77 | .enter().append("g") 78 | .attr("clip-path", "url(#d3_horizon_clip" + id + ")"); 79 | 80 | // Instantiate each copy of the path with different transforms. 81 | var path = g.select("g").selectAll("path") 82 | .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); 83 | 84 | var d0 = d3_horizonArea 85 | .interpolate(interpolate) 86 | .x(function (d) { 87 | return x0(d[0]); 88 | }) 89 | .y0(h * bands) 90 | .y1(function (d) { 91 | return h * bands - y0(d[1]); 92 | }) 93 | (data); 94 | 95 | var d1 = d3_horizonArea 96 | .x(function (d) { 97 | return x1(d[0]); 98 | }) 99 | .y1(function (d) { 100 | return h * bands - y1(d[1]); 101 | }) 102 | (data); 103 | 104 | path.enter().append("path") 105 | .style("fill", color) 106 | .style("fill-opacity", 0.3) 107 | .attr("transform", t0) 108 | .attr("d", d0); 109 | 110 | path.transition() 111 | .duration(duration) 112 | .style("fill", color) 113 | .attr("transform", t1) 114 | .attr("d", d1); 115 | 116 | path.exit().transition() 117 | .duration(duration) 118 | .attr("transform", t1) 119 | .attr("d", d1) 120 | .remove(); 121 | 122 | // Stash the new scales. 123 | this.__chart__ = { 124 | x: x1, 125 | y: y1, 126 | t: t1, 127 | id: id 128 | }; 129 | }); 130 | d3.timer.flush(); 131 | } 132 | 133 | horizon.duration = function (x) { 134 | if (!arguments.length) return duration; 135 | duration = +x; 136 | return horizon; 137 | }; 138 | 139 | horizon.bands = function (x) { 140 | if (!arguments.length) return bands; 141 | bands = +x; 142 | color.domain([-bands, 0, 0, bands]); 143 | return horizon; 144 | }; 145 | 146 | horizon.tag = function (x) { 147 | if (!arguments.length) return bands; 148 | tag = x; 149 | return horizon; 150 | }; 151 | 152 | horizon.mode = function (x) { 153 | if (!arguments.length) return mode; 154 | mode = x + ""; 155 | return horizon; 156 | }; 157 | 158 | horizon.colors = function (x) { 159 | if (!arguments.length) return color.range(); 160 | color.range(x); 161 | return horizon; 162 | }; 163 | 164 | horizon.interpolate = function (x) { 165 | if (!arguments.length) return interpolate; 166 | interpolate = x + ""; 167 | return horizon; 168 | }; 169 | 170 | horizon.x = function (z) { 171 | if (!arguments.length) return x; 172 | x = z; 173 | return horizon; 174 | }; 175 | 176 | horizon.y = function (z) { 177 | if (!arguments.length) return y; 178 | y = z; 179 | return horizon; 180 | }; 181 | 182 | horizon.width = function (x) { 183 | if (!arguments.length) return w; 184 | w = +x; 185 | return horizon; 186 | }; 187 | 188 | horizon.height = function (x) { 189 | if (!arguments.length) return h; 190 | h = +x; 191 | return horizon; 192 | }; 193 | 194 | return horizon; 195 | }; 196 | 197 | var d3_horizonArea = d3.svg.area(), 198 | d3_horizonId = 0; 199 | 200 | function d3_horizonX(d) { 201 | return d[0]; 202 | } 203 | 204 | function d3_horizonY(d) { 205 | return d[1]; 206 | } 207 | 208 | function d3_horizonTransform(bands, h, mode) { 209 | return mode == "offset" ? function (d) { 210 | return "translate(0," + (d + (d < 0) - bands) * h + ")"; 211 | } : function (d) { 212 | return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; 213 | }; 214 | } 215 | })(); -------------------------------------------------------------------------------- /public/js/queue.v1.min.js: -------------------------------------------------------------------------------- 1 | !function(){function n(n){function e(){for(;i=ap;){var u=a++,e=c[u],o=t.call(e,1);o.push(l(u)),++p,e[0].apply(null,o)}}function l(n){return function(u,t){--p,null==s&&(null!=u?(s=u,a=d=0/0,o()):(c[n]=t,--d?i||e():o()))}}function o(){null!=s?m(s):f?m(s,c):m.apply(null,[s].concat(c))}var r,i,f,c=[],a=0,p=0,d=0,s=null,m=u;return n||(n=1/0),r={defer:function(){return s||(c.push(arguments),++d,e()),r},await:function(n){return m=n,f=!1,d||o(),r},awaitAll:function(n){return m=n,f=!0,d||o(),r}}}function u(){}var t=[].slice;n.version="1.0.7","function"==typeof define&&define.amd?define(function(){return n}):"object"==typeof module&&module.exports?module.exports=n:this.queue=n}(); -------------------------------------------------------------------------------- /public/js/raphael.sketchpad.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Raphael SketchPad 3 | * Version 0.5.1 4 | * Copyright (c) 2011 Ian Li (http://ianli.com) 5 | * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. 6 | * 7 | * Requires: 8 | * jQuery http://jquery.com 9 | * Raphael http://raphaeljs.com 10 | * JSON http://www.json.org/js.html 11 | * 12 | * Reference: 13 | * http://ianli.com/sketchpad/ for Usage 14 | * 15 | * Versions: 16 | * 0.5.1 - Fixed extraneous lines when first line is drawn. 17 | * Thanks to http://github.com/peterkeating for the fix! 18 | * 0.5.0 - Added freeze_history. Fixed bug with undoing erase actions. 19 | * 0.4.0 - Support undo/redo of strokes, erase, and clear. 20 | * - Removed input option. To make editors/viewers, set editing option to true/false, respectively. 21 | * To update an input field, listen to change event and update input field with json function. 22 | * - Reduce file size V1. Changed stored path info from array into a string in SVG format. 23 | * 0.3.0 - Added erase, supported initializing data from input field. 24 | * 0.2.0 - Added iPhone/iPod Touch support, onchange event, animate. 25 | * 0.1.0 - Started code. 26 | * 27 | * TODO: 28 | * - Speed up performance. 29 | * - Don't store strokes in two places. _strokes and ActionHistory.current_strokes() 30 | * - Don't rebuild strokes from history with ActionHistory.current_strokes() 31 | * - Reduce file size. 32 | * X V1. Changed stored path info from array into a string in SVG format. 33 | */ 34 | 35 | /** 36 | * We use this wrapper to control global variables. 37 | * The only global variable we expose is Raphael.sketchpad. 38 | */ 39 | (function(Raphael) { 40 | 41 | /** 42 | * Function to create SketchPad object. 43 | */ 44 | Raphael.sketchpad = function(paper, options) { 45 | return new SketchPad(paper, options); 46 | } 47 | 48 | // Current version. 49 | Raphael.sketchpad.VERSION = '0.5.1'; 50 | 51 | /** 52 | * The Sketchpad object. 53 | */ 54 | var SketchPad = function(paper, options) { 55 | // Use self to reduce confusion about this. 56 | var self = this; 57 | 58 | var _options = { 59 | width: 100, 60 | height: 100, 61 | strokes: [], 62 | editing: true 63 | }; 64 | jQuery.extend(_options, options); 65 | 66 | 67 | // The Raphael context to draw on. 68 | var _paper; 69 | if (paper.raphael && paper.raphael.constructor == Raphael.constructor) { 70 | _paper = paper; 71 | } else if (typeof paper == "string") { 72 | _paper = Raphael(paper, _options.width, _options.height); 73 | } else { 74 | throw "first argument must be a Raphael object, an element ID, an array with 3 elements"; 75 | } 76 | 77 | // The Raphael SVG canvas. 78 | var _canvas = _paper.canvas; 79 | 80 | // The HTML element that contains the canvas. 81 | var _container = $(_canvas).parent(); 82 | 83 | // The default pen. 84 | var _pen = new Pen(); 85 | 86 | 87 | // Public Methods 88 | //----------------- 89 | 90 | self.paper = function() { 91 | return _paper; 92 | }; 93 | 94 | self.canvas = function() { 95 | return _canvas; 96 | }; 97 | 98 | self.container = function() { 99 | return _container; 100 | }; 101 | 102 | self.pen = function(value) { 103 | if (value === undefined) { 104 | return _pen; 105 | } 106 | _pen = value; 107 | return self; // function-chaining 108 | }; 109 | 110 | // Convert an SVG path into a string, so that it's smaller when JSONified. 111 | // This function is used by json(). 112 | function svg_path_to_string(path) { 113 | var str = ""; 114 | for (var i = 0, n = path.length; i < n; i++) { 115 | var point = path[i]; 116 | str += point[0] + point[1] + "," + point[2]; 117 | } 118 | return str; 119 | } 120 | 121 | // Convert a string into an SVG path. This reverses the above code. 122 | function string_to_svg_path(str) { 123 | var path = []; 124 | var tokens = str.split("L"); 125 | 126 | if (tokens.length > 0) { 127 | var token = tokens[0].replace("M", ""); 128 | var points = token.split(","); 129 | path.push(["M", parseInt(points[0]), parseInt(points[1])]); 130 | 131 | for (var i = 1, n = tokens.length; i < n; i++) { 132 | token = tokens[i]; 133 | points = token.split(","); 134 | path.push(["L", parseInt(points[0]), parseInt(points[1])]); 135 | } 136 | } 137 | 138 | return path; 139 | } 140 | 141 | self.json = function(value) { 142 | if (value === undefined) { 143 | for (var i = 0, n = _strokes.length; i < n; i++) { 144 | var stroke = _strokes[i]; 145 | if (typeof stroke.path == "object") { 146 | stroke.path = svg_path_to_string(stroke.path); 147 | } 148 | } 149 | return JSON.stringify(_strokes); 150 | } 151 | 152 | return self.strokes(JSON.parse(value)); 153 | }; 154 | 155 | self.strokes = function(value) { 156 | if (value === undefined) { 157 | return _strokes; 158 | } 159 | if (jQuery.isArray(value)) { 160 | _strokes = value; 161 | 162 | for (var i = 0, n = _strokes.length; i < n; i++) { 163 | var stroke = _strokes[i]; 164 | if (typeof stroke.path == "string") { 165 | stroke.path = string_to_svg_path(stroke.path); 166 | } 167 | } 168 | 169 | _action_history.add({ 170 | type: "batch", 171 | strokes: jQuery.merge([], _strokes) // Make a copy. 172 | }) 173 | 174 | _redraw_strokes(); 175 | _fire_change(); 176 | } 177 | return self; // function-chaining 178 | } 179 | 180 | self.freeze_history = function() { 181 | _action_history.freeze(); 182 | }; 183 | 184 | self.undoable = function() { 185 | return _action_history.undoable(); 186 | }; 187 | 188 | self.undo = function() { 189 | if (_action_history.undoable()) { 190 | _action_history.undo(); 191 | _strokes = _action_history.current_strokes(); 192 | _redraw_strokes(); 193 | _fire_change(); 194 | } 195 | return self; // function-chaining 196 | }; 197 | 198 | self.redoable = function() { 199 | return _action_history.redoable(); 200 | }; 201 | 202 | self.redo = function() { 203 | if (_action_history.redoable()) { 204 | _action_history.redo(); 205 | _strokes = _action_history.current_strokes(); 206 | _redraw_strokes(); 207 | _fire_change(); 208 | } 209 | return self; // function-chaining 210 | }; 211 | 212 | self.clear = function() { 213 | _action_history.add({ 214 | type: "clear" 215 | }); 216 | 217 | _strokes = []; 218 | _redraw_strokes(); 219 | _fire_change(); 220 | 221 | return self; // function-chaining 222 | }; 223 | 224 | self.animate = function(ms) { 225 | if (ms === undefined) { 226 | ms = 500; 227 | } 228 | 229 | _paper.clear(); 230 | 231 | if (_strokes.length > 0) { 232 | var i = 0; 233 | 234 | function animate() { 235 | var stroke = _strokes[i]; 236 | var type = stroke.type; 237 | _paper[type]() 238 | .attr(stroke) 239 | .click(_pathclick); 240 | 241 | i++; 242 | if (i < _strokes.length) { 243 | setTimeout(animate, ms); 244 | } 245 | }; 246 | 247 | animate(); 248 | } 249 | 250 | return self; // function-chaining 251 | }; 252 | 253 | self.editing = function(mode) { 254 | if (mode === undefined) { 255 | return _options.editing; 256 | } 257 | 258 | _options.editing = mode; 259 | if (_options.editing) { 260 | if (_options.editing == "erase") { 261 | // Cursor is crosshair, so it looks like we can do something. 262 | $(_container).css("cursor", "crosshair"); 263 | $(_container).unbind("mousedown", _mousedown); 264 | $(_container).unbind("mousemove", _mousemove); 265 | $(_container).unbind("mouseup", _mouseup); 266 | $(document).unbind("mouseup", _mouseup); 267 | 268 | // iPhone Events 269 | var agent = navigator.userAgent; 270 | if (agent.indexOf("iPhone") > 0 || agent.indexOf("iPod") > 0) { 271 | $(_container).unbind("touchstart", _touchstart); 272 | $(_container).unbind("touchmove", _touchmove); 273 | $(_container).unbind("touchend", _touchend); 274 | } 275 | } else { 276 | // Cursor is crosshair, so it looks like we can do something. 277 | $(_container).css("cursor", "crosshair"); 278 | 279 | $(_container).mousedown(_mousedown); 280 | $(_container).mousemove(_mousemove); 281 | $(_container).mouseup(_mouseup); 282 | 283 | // Handle the case when the mouse is released outside the canvas. 284 | $(document).mouseup(_mouseup); 285 | 286 | // iPhone Events 287 | var agent = navigator.userAgent; 288 | if (agent.indexOf("iPhone") > 0 || agent.indexOf("iPod") > 0) { 289 | $(_container).bind("touchstart", _touchstart); 290 | $(_container).bind("touchmove", _touchmove); 291 | $(_container).bind("touchend", _touchend); 292 | } 293 | } 294 | } else { 295 | // Reverse the settings above. 296 | $(_container).attr("style", "cursor:default"); 297 | $(_container).unbind("mousedown", _mousedown); 298 | $(_container).unbind("mousemove", _mousemove); 299 | $(_container).unbind("mouseup", _mouseup); 300 | $(document).unbind("mouseup", _mouseup); 301 | 302 | // iPhone Events 303 | var agent = navigator.userAgent; 304 | if (agent.indexOf("iPhone") > 0 || agent.indexOf("iPod") > 0) { 305 | $(_container).unbind("touchstart", _touchstart); 306 | $(_container).unbind("touchmove", _touchmove); 307 | $(_container).unbind("touchend", _touchend); 308 | } 309 | } 310 | 311 | return self; // function-chaining 312 | } 313 | 314 | // Change events 315 | //---------------- 316 | 317 | var _change_fn = function() {}; 318 | self.change = function(fn) { 319 | if (fn == null || fn === undefined) { 320 | _change_fn = function() {}; 321 | } else if (typeof fn == "function") { 322 | _change_fn = fn; 323 | } 324 | }; 325 | 326 | function _fire_change() { 327 | _change_fn(); 328 | }; 329 | 330 | // Miscellaneous methods 331 | //------------------ 332 | 333 | function _redraw_strokes() { 334 | _paper.clear(); 335 | 336 | for (var i = 0, n = _strokes.length; i < n; i++) { 337 | var stroke = _strokes[i]; 338 | var type = stroke.type; 339 | _paper[type]() 340 | .attr(stroke) 341 | .click(_pathclick); 342 | } 343 | }; 344 | 345 | function _disable_user_select() { 346 | $("*").css("-webkit-user-select", "none"); 347 | $("*").css("-moz-user-select", "none"); 348 | if (jQuery.browser.msie) { 349 | $("body").attr("onselectstart", "return false;"); 350 | } 351 | } 352 | 353 | function _enable_user_select() { 354 | $("*").css("-webkit-user-select", "text"); 355 | $("*").css("-moz-user-select", "text"); 356 | if (jQuery.browser.msie) { 357 | $("body").removeAttr("onselectstart"); 358 | } 359 | } 360 | 361 | // Event handlers 362 | //----------------- 363 | // We can only attach events to the container, so do it. 364 | 365 | function _pathclick(e) { 366 | if (_options.editing == "erase") { 367 | var stroke = this.attr(); 368 | stroke.type = this.type; 369 | 370 | _action_history.add({ 371 | type: "erase", 372 | stroke: stroke 373 | }); 374 | 375 | for (var i = 0, n = _strokes.length; i < n; i++) { 376 | var s = _strokes[i]; 377 | if (equiv(s, stroke)) { 378 | _strokes.splice(i, 1); 379 | } 380 | } 381 | 382 | _fire_change(); 383 | 384 | this.remove(); 385 | } 386 | }; 387 | 388 | function _mousedown(e) { 389 | _disable_user_select(); 390 | 391 | _pen.start(e, self); 392 | }; 393 | 394 | function _mousemove(e) { 395 | _pen.move(e, self); 396 | }; 397 | 398 | function _mouseup(e) { 399 | _enable_user_select(); 400 | 401 | var path = _pen.finish(e, self); 402 | 403 | if (path != null) { 404 | // Add event when clicked. 405 | path.click(_pathclick); 406 | 407 | // Save the stroke. 408 | var stroke = path.attr(); 409 | stroke.type = path.type; 410 | 411 | _strokes.push(stroke); 412 | 413 | _action_history.add({ 414 | type: "stroke", 415 | stroke: stroke 416 | }); 417 | 418 | _fire_change(); 419 | } 420 | }; 421 | 422 | function _touchstart(e) { 423 | e = e.originalEvent; 424 | e.preventDefault(); 425 | 426 | if (e.touches.length == 1) { 427 | var touch = e.touches[0]; 428 | _mousedown(touch); 429 | } 430 | } 431 | 432 | function _touchmove(e) { 433 | e = e.originalEvent; 434 | e.preventDefault(); 435 | 436 | if (e.touches.length == 1) { 437 | var touch = e.touches[0]; 438 | _mousemove(touch); 439 | } 440 | } 441 | 442 | function _touchend(e) { 443 | e = e.originalEvent; 444 | e.preventDefault(); 445 | 446 | _mouseup(e); 447 | } 448 | 449 | // Setup 450 | //-------- 451 | 452 | var _action_history = new ActionHistory(); 453 | 454 | // Path data 455 | var _strokes = _options.strokes; 456 | if (jQuery.isArray(_strokes) && _strokes.length > 0) { 457 | _action_history.add({ 458 | type: "init", 459 | strokes: jQuery.merge([], _strokes) // Make a clone. 460 | }); 461 | _redraw_strokes(); 462 | } else { 463 | _strokes = []; 464 | _redraw_strokes(); 465 | } 466 | 467 | self.editing(_options.editing); 468 | }; 469 | 470 | var ActionHistory = function() { 471 | var self = this; 472 | 473 | var _history = []; 474 | 475 | // Index of the last state. 476 | var _current_state = -1; 477 | 478 | // Index of the freeze state. 479 | // The freeze state is the state where actions cannot be undone. 480 | var _freeze_state = -1; 481 | 482 | // The current set of strokes if strokes were to be rebuilt from history. 483 | // Set to null to force refresh. 484 | var _current_strokes = null; 485 | 486 | self.add = function(action) { 487 | if (_current_state + 1 < _history.length) { 488 | _history.splice(_current_state + 1, _history.length - (_current_state + 1)); 489 | } 490 | 491 | _history.push(action); 492 | _current_state = _history.length - 1; 493 | 494 | // Reset current strokes. 495 | _current_strokes = null; 496 | }; 497 | 498 | self.freeze = function(index) { 499 | if (index === undefined) { 500 | _freeze_state = _current_state; 501 | } else { 502 | _freeze_state = index; 503 | } 504 | }; 505 | 506 | self.undoable = function() { 507 | return (_current_state > -1 && _current_state > _freeze_state); 508 | }; 509 | 510 | self.undo = function() { 511 | if (self.undoable()) { 512 | _current_state--; 513 | 514 | // Reset current strokes. 515 | _current_strokes = null; 516 | } 517 | }; 518 | 519 | self.redoable = function() { 520 | return _current_state < _history.length - 1; 521 | }; 522 | 523 | self.redo = function() { 524 | if (self.redoable()) { 525 | _current_state++; 526 | 527 | // Reset current strokes. 528 | _current_strokes = null; 529 | } 530 | }; 531 | 532 | // Rebuild the strokes from history. 533 | self.current_strokes = function() { 534 | if (_current_strokes == null) { 535 | var strokes = []; 536 | for (var i = 0; i <= _current_state; i++) { 537 | var action = _history[i]; 538 | switch(action.type) { 539 | case "init": 540 | case "json": 541 | case "strokes": 542 | case "batch": 543 | jQuery.merge(strokes, action.strokes); 544 | break; 545 | case "stroke": 546 | strokes.push(action.stroke); 547 | break; 548 | case "erase": 549 | for (var s = 0, n = strokes.length; s < n; s++) { 550 | var stroke = strokes[s]; 551 | if (equiv(stroke, action.stroke)) { 552 | strokes.splice(s, 1); 553 | } 554 | } 555 | break; 556 | case "clear": 557 | strokes = []; 558 | break; 559 | } 560 | } 561 | 562 | _current_strokes = strokes; 563 | } 564 | return _current_strokes; 565 | }; 566 | }; 567 | 568 | /** 569 | * The default Pen object. 570 | */ 571 | var Pen = function() { 572 | var self = this; 573 | 574 | var _color = "#000000"; 575 | var _opacity = 1.0; 576 | var _width = 5; 577 | var _offset = null; 578 | 579 | // Drawing state 580 | var _drawing = false; 581 | var _c = null; 582 | var _points = []; 583 | 584 | self.color = function(value) { 585 | if (value === undefined){ 586 | return _color; 587 | } 588 | 589 | _color = value; 590 | 591 | return self; 592 | }; 593 | 594 | self.width = function(value) { 595 | if (value === undefined) { 596 | return _width; 597 | } 598 | 599 | if (value < Pen.MIN_WIDTH) { 600 | value = Pen.MIN_WIDTH; 601 | } else if (value > Pen.MAX_WIDTH) { 602 | value = Pen.MAX_WIDTH; 603 | } 604 | 605 | _width = value; 606 | 607 | return self; 608 | } 609 | 610 | self.opacity = function(value) { 611 | if (value === undefined) { 612 | return _opacity; 613 | } 614 | 615 | if (value < 0) { 616 | value = 0; 617 | } else if (value > 1) { 618 | value = 1; 619 | } 620 | 621 | _opacity = value; 622 | 623 | return self; 624 | } 625 | 626 | self.start = function(e, sketchpad) { 627 | _drawing = true; 628 | 629 | _offset = $(sketchpad.container()).offset(); 630 | 631 | var x = e.pageX - _offset.left, 632 | y = e.pageY - _offset.top; 633 | _points.push([x, y]); 634 | 635 | _c = sketchpad.paper().path(); 636 | 637 | _c.attr({ 638 | stroke: _color, 639 | "stroke-opacity": _opacity, 640 | "stroke-width": _width, 641 | "stroke-linecap": "round", 642 | "stroke-linejoin": "round" 643 | }); 644 | }; 645 | 646 | self.finish = function(e, sketchpad) { 647 | var path = null; 648 | 649 | if (_c != null) { 650 | if (_points.length <= 1) { 651 | _c.remove(); 652 | } else { 653 | path = _c; 654 | } 655 | } 656 | 657 | _drawing = false; 658 | _c = null; 659 | _points = []; 660 | 661 | return path; 662 | }; 663 | 664 | self.move = function(e, sketchpad) { 665 | if (_drawing == true) { 666 | var x = e.pageX - _offset.left, 667 | y = e.pageY - _offset.top; 668 | _points.push([x, y]); 669 | _c.attr({ path: points_to_svg() }); 670 | } 671 | }; 672 | 673 | function points_to_svg() { 674 | if (_points != null && _points.length > 1) { 675 | var p = _points[0]; 676 | var path = "M" + p[0] + "," + p[1]; 677 | for (var i = 1, n = _points.length; i < n; i++) { 678 | p = _points[i]; 679 | path += "L" + p[0] + "," + p[1]; 680 | } 681 | return path; 682 | } else { 683 | return ""; 684 | } 685 | }; 686 | }; 687 | 688 | Pen.MAX_WIDTH = 1000; 689 | Pen.MIN_WIDTH = 1; 690 | 691 | /** 692 | * Utility to generate string representation of an object. 693 | */ 694 | function inspect(obj) { 695 | var str = ""; 696 | for (var i in obj) { 697 | str += i + "=" + obj[i] + "\n"; 698 | } 699 | return str; 700 | } 701 | 702 | })(window.Raphael); 703 | 704 | Raphael.fn.display = function(elements) { 705 | for (var i = 0, n = elements.length; i < n; i++) { 706 | var e = elements[i]; 707 | var type = e.type; 708 | this[type]().attr(e); 709 | } 710 | }; 711 | 712 | 713 | /** 714 | * Utility functions to compare objects by Phil Rathe. 715 | * http://philrathe.com/projects/equiv 716 | */ 717 | 718 | // Determine what is o. 719 | function hoozit(o) { 720 | if (o.constructor === String) { 721 | return "string"; 722 | 723 | } else if (o.constructor === Boolean) { 724 | return "boolean"; 725 | 726 | } else if (o.constructor === Number) { 727 | 728 | if (isNaN(o)) { 729 | return "nan"; 730 | } else { 731 | return "number"; 732 | } 733 | 734 | } else if (typeof o === "undefined") { 735 | return "undefined"; 736 | 737 | // consider: typeof null === object 738 | } else if (o === null) { 739 | return "null"; 740 | 741 | // consider: typeof [] === object 742 | } else if (o instanceof Array) { 743 | return "array"; 744 | 745 | // consider: typeof new Date() === object 746 | } else if (o instanceof Date) { 747 | return "date"; 748 | 749 | // consider: /./ instanceof Object; 750 | // /./ instanceof RegExp; 751 | // typeof /./ === "function"; // => false in IE and Opera, 752 | // true in FF and Safari 753 | } else if (o instanceof RegExp) { 754 | return "regexp"; 755 | 756 | } else if (typeof o === "object") { 757 | return "object"; 758 | 759 | } else if (o instanceof Function) { 760 | return "function"; 761 | } else { 762 | return undefined; 763 | } 764 | } 765 | 766 | // Call the o related callback with the given arguments. 767 | function bindCallbacks(o, callbacks, args) { 768 | var prop = hoozit(o); 769 | if (prop) { 770 | if (hoozit(callbacks[prop]) === "function") { 771 | return callbacks[prop].apply(callbacks, args); 772 | } else { 773 | return callbacks[prop]; // or undefined 774 | } 775 | } 776 | } 777 | 778 | // Test for equality any JavaScript type. 779 | // Discussions and reference: http://philrathe.com/articles/equiv 780 | // Test suites: http://philrathe.com/tests/equiv 781 | // Author: Philippe Rathé 782 | 783 | var equiv = function () { 784 | 785 | var innerEquiv; // the real equiv function 786 | var callers = []; // stack to decide between skip/abort functions 787 | 788 | 789 | var callbacks = function () { 790 | 791 | // for string, boolean, number and null 792 | function useStrictEquality(b, a) { 793 | if (b instanceof a.constructor || a instanceof b.constructor) { 794 | // to catch short annotaion VS 'new' annotation of a declaration 795 | // e.g. var i = 1; 796 | // var j = new Number(1); 797 | return a == b; 798 | } else { 799 | return a === b; 800 | } 801 | } 802 | 803 | return { 804 | "string": useStrictEquality, 805 | "boolean": useStrictEquality, 806 | "number": useStrictEquality, 807 | "null": useStrictEquality, 808 | "undefined": useStrictEquality, 809 | 810 | "nan": function (b) { 811 | return isNaN(b); 812 | }, 813 | 814 | "date": function (b, a) { 815 | return hoozit(b) === "date" && a.valueOf() === b.valueOf(); 816 | }, 817 | 818 | "regexp": function (b, a) { 819 | return hoozit(b) === "regexp" && 820 | a.source === b.source && // the regex itself 821 | a.global === b.global && // and its modifers (gmi) ... 822 | a.ignoreCase === b.ignoreCase && 823 | a.multiline === b.multiline; 824 | }, 825 | 826 | // - skip when the property is a method of an instance (OOP) 827 | // - abort otherwise, 828 | // initial === would have catch identical references anyway 829 | "function": function () { 830 | var caller = callers[callers.length - 1]; 831 | return caller !== Object && 832 | typeof caller !== "undefined"; 833 | }, 834 | 835 | "array": function (b, a) { 836 | var i; 837 | var len; 838 | 839 | // b could be an object literal here 840 | if ( ! (hoozit(b) === "array")) { 841 | return false; 842 | } 843 | 844 | len = a.length; 845 | if (len !== b.length) { // safe and faster 846 | return false; 847 | } 848 | for (i = 0; i < len; i++) { 849 | if( ! innerEquiv(a[i], b[i])) { 850 | return false; 851 | } 852 | } 853 | return true; 854 | }, 855 | 856 | "object": function (b, a) { 857 | var i; 858 | var eq = true; // unless we can proove it 859 | var aProperties = [], bProperties = []; // collection of strings 860 | 861 | // comparing constructors is more strict than using instanceof 862 | if ( a.constructor !== b.constructor) { 863 | return false; 864 | } 865 | 866 | // stack constructor before traversing properties 867 | callers.push(a.constructor); 868 | 869 | for (i in a) { // be strict: don't ensures hasOwnProperty and go deep 870 | 871 | aProperties.push(i); // collect a's properties 872 | 873 | if ( ! innerEquiv(a[i], b[i])) { 874 | eq = false; 875 | } 876 | } 877 | 878 | callers.pop(); // unstack, we are done 879 | 880 | for (i in b) { 881 | bProperties.push(i); // collect b's properties 882 | } 883 | 884 | // Ensures identical properties name 885 | return eq && innerEquiv(aProperties.sort(), bProperties.sort()); 886 | } 887 | }; 888 | }(); 889 | 890 | innerEquiv = function () { // can take multiple arguments 891 | var args = Array.prototype.slice.apply(arguments); 892 | if (args.length < 2) { 893 | return true; // end transition 894 | } 895 | 896 | return (function (a, b) { 897 | if (a === b) { 898 | return true; // catch the most you can 899 | } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) { 900 | return false; // don't lose time with error prone cases 901 | } else { 902 | return bindCallbacks(a, callbacks, [b, a]); 903 | } 904 | 905 | // apply transition with (1..n) arguments 906 | })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); 907 | }; 908 | 909 | return innerEquiv; 910 | 911 | }(); -------------------------------------------------------------------------------- /public/js/stockVis- neural.js: -------------------------------------------------------------------------------- 1 | 2 | function TemporalNN(options) { 3 | 4 | } 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/js/stockVis-bayesian-temporal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Karthik Badam on Apr 30, 2015 3 | * 4 | */ 5 | 6 | function TemporalPrediction(options) { 7 | var _self = this; 8 | 9 | _self.stockName = options.stock_name; 10 | 11 | _self.stockID = options.stock_id; 12 | 13 | _self.stockObject = stockObjects[_self.stockID]; 14 | 15 | }; 16 | 17 | /* calculates temporal prediction */ 18 | TemporalPrediction.prototype.predict = function(input) { 19 | 20 | var _self = this; 21 | 22 | // get the last 15 values 23 | for (int i = 0; i < _self.stockObject.data.length; i++) { 24 | 25 | } 26 | 27 | 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /public/js/stockVis-correlationviewer.js: -------------------------------------------------------------------------------- 1 | //Correlation Chart class 2 | 3 | function CorrelationChart(options) { 4 | var _self = this; 5 | 6 | // _self.selectedSymbolsData = options.selectedSymbolsData; 7 | // _self.selectedSymbols = options.selectedSymbols; 8 | // _self.color = options.color; 9 | // _self.stocks = options.stocks; 10 | // 11 | 12 | _self.nodes = []; 13 | _self.links = []; 14 | 15 | _self.margin = { 16 | top: 10, 17 | right: 50, 18 | bottom: 10, 19 | left: 10 20 | }; 21 | 22 | _self.width = ($("#correlation-viewer").width() - _self.margin.left - _self.margin.right); 23 | 24 | _self.height = ($("#correlation-viewer").height() - _self.margin.top - _self.margin.bottom); 25 | 26 | _self.force = d3.layout.force() 27 | .charge(-120) 28 | .linkDistance(function (d) { 29 | return _self.width - 20 - _self.width * d.value/2; 30 | }) 31 | .size([_self.width, _self.height]); 32 | 33 | _self.div = d3.select("#correlation-viewer"); 34 | 35 | _self.svg = _self.div.append("svg") 36 | .attr("class", "correlation-svg") 37 | .attr("width", _self.width + _self.margin.left + _self.margin.right) 38 | .attr("height", _self.height + _self.margin.top + _self.margin.bottom) 39 | .append("g") 40 | .attr("transform", "translate(" + _self.margin.left + "," + _self.margin.top + ")"); 41 | 42 | _self.nodes = []; 43 | _self.links = []; 44 | 45 | _self.force.nodes(_self.nodes) 46 | .links(_self.links); 47 | 48 | } 49 | 50 | CorrelationChart.prototype.add = function (options) { 51 | var _self = this; 52 | 53 | _self.selectedSymbolsData = options.selectedSymbolsData; 54 | _self.selectedSymbols = options.selectedSymbols; 55 | _self.color = options.color; 56 | _self.stocks = options.stocks; 57 | } 58 | 59 | CorrelationChart.prototype.refresh = function () { 60 | var _self = this; 61 | var radius = _self.radius = 7; 62 | 63 | //$("#correlation-viewer").empty(); 64 | 65 | _self.nodes = []; 66 | _self.links = []; 67 | 68 | for (var i = 0; i < _self.selectedSymbols.length; i++) { 69 | var node1 = {}; 70 | node1.name = _self.selectedSymbols[i]; 71 | node1.companyName = _self.stocks[i].companyName; 72 | node1.id = i; 73 | _self.nodes.push(node1); 74 | } 75 | 76 | 77 | for (var i = 0; i < _self.selectedSymbols.length; i++) { 78 | for (var j = i + 1; j < _self.selectedSymbols.length; j++) { 79 | var link1 = {}; 80 | link1.source = i; 81 | link1.target = j; 82 | //var data1 = _self.selectedSymbolsData[i]; 83 | //var data2 = _self.selectedSymbolsData[j]; 84 | 85 | var data1 = _self.stocks[i].dataFiltered; 86 | var data2 = _self.stocks[j].dataFiltered; 87 | 88 | link1.value = _self.getCorrelationValue(data1, data2); 89 | //console.log(link1.value); 90 | //var value1 = 100*(data1[0][adjCol] - data1[1][adjCol])/data1[1][adjCol]; 91 | //var value2 = 100*(data2[0][adjCol] - data2[1][adjCol])/data2[1][adjCol]; 92 | //link1.value = Math.pow(value1 - value2, 2); 93 | 94 | _self.links.push(link1); 95 | } 96 | } 97 | 98 | _self.force.nodes(_self.nodes) 99 | .links(_self.links); 100 | 101 | if (_self.link) 102 | _self.link.remove().transition().delay(100); 103 | 104 | _self.link = _self.svg.selectAll(".link") 105 | .data(_self.links) 106 | .enter().append("line") 107 | .attr("class", "link") 108 | .attr("stroke", function (d) { 109 | if (d.value > 0) { 110 | return "#5BB271"; 111 | } else { 112 | return "#F08080"; 113 | } 114 | 115 | }) 116 | .attr("stroke-opacity", function (d) { 117 | return (1 - Math.abs(d.value)) > 0 ? 118 | Math.abs(d.value) : 0.01; 119 | }) 120 | .attr("stroke-width", "1px"); 121 | 122 | 123 | //_self.link.exit().remove(); 124 | 125 | if (_self.node) 126 | _self.node.remove().transition().delay(100); 127 | 128 | // _self.node = _self.svg.selectAll(".node") 129 | // .data(_self.nodes) 130 | // .enter().append("circle") 131 | // .attr("class", "node") 132 | // .attr("r", radius) 133 | // .style("fill", function (d) { 134 | // return color(d.id); 135 | // }); 136 | //.call(_self.force.drag); 137 | 138 | if (_self.svg.selectAll(".node").empty()) { 139 | _self.node = _self.svg.selectAll(".node") 140 | .data(_self.nodes) 141 | .enter() 142 | .append("g") 143 | .attr("class", "node"); 144 | 145 | _self.node.append("circle") 146 | .attr("cx", 0) 147 | .attr("cy", 0) 148 | .attr("r", radius) 149 | .style("fill", function (d) { 150 | //CHANGE COLOR SCHEME 151 | //return color(d.id); 152 | return "#67655D"; 153 | }); 154 | 155 | } else { 156 | _self.node = _self.svg.selectAll(".node") 157 | .data(_self.nodes); 158 | 159 | _self.node.exit().transition().delay(100).remove(); 160 | 161 | _self.node.select("circle") 162 | .transition().delay(100) 163 | .attr("cx", 0) 164 | .attr("cy", 0) 165 | .attr("r", radius) 166 | .style("fill", function (d) { 167 | //CHANGE COLOR SCHEME 168 | //return color(d.id); 169 | return "#67655D"; 170 | }); 171 | 172 | _self.node.enter().select('circle') 173 | .transition().delay(200) 174 | .attr("cx", 0) 175 | .attr("cy", 0) 176 | .attr("r", radius) 177 | .style("fill", function (d) { 178 | //CHANGE COLOR SCHEME 179 | //return color(d.id); 180 | return "#67655D"; 181 | }); 182 | 183 | } 184 | 185 | _self.node.append("text") 186 | .attr("dx", 12) 187 | .attr("dy", ".35em") 188 | .text(function (d) { 189 | return d.companyName; 190 | }); 191 | 192 | // 193 | // _self.node.append("title") 194 | // .text(function (d) { 195 | // return d.name; 196 | // }); 197 | 198 | 199 | //_self.node.exit().remove(); 200 | 201 | _self.force.on("tick", function () { 202 | _self.link.transition().delay(30).attr("x1", function (d) { 203 | return d.source.x; 204 | }) 205 | .attr("y1", function (d) { 206 | return d.source.y; 207 | }) 208 | .attr("x2", function (d) { 209 | return d.target.x; 210 | }) 211 | .attr("y2", function (d) { 212 | return d.target.y; 213 | }); 214 | _self.node.transition().delay(30).attr("transform", function (d) { 215 | d.x = Math.max(radius, Math.min(_self.width - radius, d.x)); 216 | d.y = Math.max(radius, Math.min(_self.height - radius, d.y)); 217 | return "translate(" + d.x + "," + d.y + ")"; 218 | }); 219 | 220 | // _self.node.attr("cx", function (d) { 221 | // return d.x = Math.max(radius, Math.min(_self.width - radius, d.x)); 222 | // }) 223 | // .attr("cy", function (d) { 224 | // return d.y = Math.max(radius, Math.min(_self.height - radius, d.y)); 225 | // }); 226 | //.attr("cx", function(d) { return d.x; }) 227 | //.attr("cy", function(d) { return d.y; }); 228 | }); 229 | 230 | _self.force 231 | .start(); 232 | 233 | var n = _self.nodes.length; 234 | for (var i = n * n; i > 0; --i) 235 | _self.force.tick(); 236 | 237 | _self.force.stop(); 238 | 239 | }; 240 | 241 | CorrelationChart.prototype.getCorrelationValue = function (x, y) { 242 | var shortestArrayLength = 0; 243 | 244 | if (x.length == y.length) { 245 | shortestArrayLength = x.length; 246 | 247 | } else if (x.length > y.length) { 248 | shortestArrayLength = y.length; 249 | 250 | } else { 251 | shortestArrayLength = x.length; 252 | 253 | } 254 | 255 | var xy = []; 256 | var x2 = []; 257 | var y2 = []; 258 | 259 | for (var i = 0; i < shortestArrayLength; i++) { 260 | xy.push(x[i][adjCol] * y[i][adjCol]); 261 | x2.push(x[i][adjCol] * x[i][adjCol]); 262 | y2.push(y[i][adjCol] * y[i][adjCol]); 263 | 264 | } 265 | 266 | var sum_x = 0; 267 | var sum_y = 0; 268 | var sum_xy = 0; 269 | var sum_x2 = 0; 270 | var sum_y2 = 0; 271 | 272 | for (var i = 0; i < shortestArrayLength; i++) { 273 | sum_x += x[i][adjCol]; 274 | sum_y += y[i][adjCol]; 275 | sum_xy += xy[i]; 276 | sum_x2 += x2[i]; 277 | sum_y2 += y2[i]; 278 | 279 | } 280 | 281 | var step1 = (shortestArrayLength * sum_xy) - (sum_x * sum_y); 282 | var step2 = (shortestArrayLength * sum_x2) - (sum_x * sum_x); 283 | var step3 = (shortestArrayLength * sum_y2) - (sum_y * sum_y); 284 | var step4 = Math.sqrt(step2 * step3); 285 | 286 | var correlation = step1 / step4; 287 | 288 | if (isNaN(correlation)) 289 | return 0; 290 | return correlation; 291 | } -------------------------------------------------------------------------------- /public/js/stockVis-overviewchart.js: -------------------------------------------------------------------------------- 1 | //Overview Chart class 2 | 3 | function OverviewChart(options) { 4 | var _self = this; 5 | _self.brush = []; 6 | _self.stockObject = options.stockObject; 7 | _self.data = _self.stockObject.data; 8 | _self.margin = { 9 | top: 10, 10 | right: 30, 11 | bottom: 20, 12 | left: 30 13 | }; 14 | 15 | _self.correlationViewer = options.correlationViewer; 16 | 17 | _self.color = options.color; 18 | _self.linecharts = options.linecharts; 19 | 20 | 21 | // horizon charts 22 | // small multiples 23 | // brushing at the x-axis! 24 | 25 | // cubism plugin 26 | // divide space to redraw charts 27 | // Change the axis lines 28 | 29 | 30 | _self.width = (2*$("#overviewchart-viz").parent().width()/3 - _self.margin.left - _self.margin.right), 31 | _self.height = ($("#overviewchart-viz").parent().height() - _self.margin.top - _self.margin.bottom); 32 | 33 | _self.svg = d3.select("#overviewchart-viz").append("svg").attr("class", "overviewchart") 34 | .attr("width", _self.width + _self.margin.left + _self.margin.right) 35 | .attr("height", _self.height + _self.margin.top + _self.margin.bottom) 36 | .append("g") 37 | .attr("transform", "translate(" + _self.margin.left + "," + _self.margin.top + ")"); 38 | 39 | //Axis x - date -- Axis y - value 40 | _self.x = d3.time.scale() 41 | .range([0, _self.width]); 42 | 43 | _self.y = d3.scale.linear() 44 | .range([_self.height - 20, 0]); 45 | 46 | 47 | _self.x.domain(d3.extent(_self.data, function(stock) { 48 | return stock[dateCol]; 49 | })); 50 | 51 | _self.y.domain([0, 1]); 52 | 53 | //x and y axis 54 | _self.xAxis = d3.svg.axis() 55 | .scale(_self.x) 56 | .orient("bottom"); 57 | //.tickFormat(function(d) { return d3.time.format('%b')(new Date(d)); }); 58 | 59 | _self.yAxis = d3.svg.axis() 60 | .scale(_self.y) 61 | .orient("left"); 62 | 63 | _self.line = d3.svg.line() 64 | .interpolate("monotone") 65 | .x(function(d) { 66 | return _self.x(d[dateCol]); 67 | }) 68 | .y(function(d) { 69 | return _self.y(d.normalized); 70 | }); 71 | 72 | _self.svg.append("g") 73 | .attr("class", "x axis") 74 | .attr("transform", "translate(0," + _self.height + ")") 75 | .call(_self.xAxis); 76 | 77 | 78 | _self.svg.append("defs") 79 | .append("clipPath").attr("id", "clip") 80 | .append("rect") 81 | .attr("width", _self.width).attr("height", _self.height); 82 | 83 | _self.chartContainer = _self.svg.append("g") 84 | .attr("width", _self.width).attr("height", _self.height); 85 | 86 | 87 | var brush = d3.svg.brush().x(_self.x).on("brushend", onBrush); 88 | var context = _self.svg.append("g").attr("class", "context") 89 | .attr("transform", "translate(" + 0 + "," + (0) + ")"); 90 | 91 | 92 | _self.b = _self.x.domain(); 93 | 94 | context.append("g").attr("class", "brush") 95 | .call(brush).selectAll("rect").attr("y", 0) 96 | .attr("height", _self.height) 97 | .attr("z-index", 3); 98 | 99 | /* brush and link to guide interaction from the overview chart */ 100 | function onBrush() { 101 | /* this will return a date range to pass into the chart object */ 102 | _self.b = brush.empty() ? _self.x.domain() : brush.extent(); 103 | var empty = brush.empty() ? 1 : 0; 104 | for (var i = 0; i < _self.linecharts.length; i++) { 105 | try { 106 | _self.linecharts[i].showOnly(_self.b, empty); 107 | } catch (err) { 108 | //console.log("error caught -" + err); 109 | } 110 | } 111 | 112 | _self.correlationViewer.refresh(); 113 | } 114 | 115 | //user study part! 116 | $('#next_button').on('click', function(e) { 117 | var b = _self.b; 118 | if (b === x.domain()) { 119 | return; 120 | } else { 121 | var leftDay = b[0]; 122 | var rightDay = b[1]; 123 | 124 | leftDay.setDate(b[0].getDate() + 1); 125 | if (b[0].getDay() === 6) { 126 | leftDay.setDate(b[0].getDate() + 2); 127 | } 128 | if (b[0].getDay() === 5) { 129 | leftDay.setDate(b[0].getDate() + 3); 130 | } 131 | 132 | rightDay.setDate(b[1].getDate() + 1); 133 | if (b[1].getDay() === 6) { 134 | rightDay.setDate(b[1].getDate() + 2); 135 | } 136 | if (b[1].getDay() === 5) { 137 | rightDay.setDate(b[1].getDate() + 3); 138 | } 139 | 140 | 141 | for (var i = 0; i < _self.linecharts.length; i++) { 142 | try { 143 | _self.linecharts[i].showOnly(b, 1); 144 | } catch (err) { 145 | console.log("error caught -" + err); 146 | } 147 | } 148 | 149 | } 150 | 151 | }); 152 | 153 | } 154 | 155 | OverviewChart.prototype.addLine = function(options) { 156 | var _self = this; 157 | _self.stockObject = options.stockObject; 158 | _self.data = _self.stockObject.data; 159 | _self.id = options.id; 160 | 161 | _self.chartContainer.append("path") 162 | .attr("class", "line") 163 | .attr("clip-path", "url(#clip)") 164 | .data([_self.data]) 165 | .attr("d", _self.line) 166 | //.attr("stroke", _self.color(options.id)) 167 | .attr("stroke", "#444") 168 | .attr("fill", "transparent") 169 | .attr("stroke-width", "1.5px") 170 | .attr("opacity", 0.8).attr("z-index", 1); 171 | 172 | }; 173 | 174 | -------------------------------------------------------------------------------- /public/js/stockVis-overviewhorizonchart.js: -------------------------------------------------------------------------------- 1 | //Overview Chart class 2 | 3 | function OverviewHorizonChart(options) { 4 | 5 | var _self = this; 6 | 7 | _self.brush = []; 8 | _self.stockObject = options.stockObject; 9 | _self.data = _self.stockObject.data; 10 | 11 | _self.margin = { 12 | top: 0, 13 | right: 0, 14 | bottom: 30, 15 | left: 0 16 | }; 17 | 18 | _self.correlationViewer = options.correlationViewer; 19 | 20 | _self.color = options.color; 21 | _self.linecharts = options.linecharts; 22 | 23 | _self.width = $("#overviewchart-viz").parent().width() - 2; 24 | 25 | _self.height = $("#overviewchart-viz").parent().height(); 26 | 27 | _self.x = d3.time.scale().range([0, _self.width]); 28 | 29 | _self.x.domain(d3.extent(_self.data, function (stock) { 30 | return stock[dateCol]; 31 | })); 32 | 33 | _self.axisHeight = 25; 34 | 35 | _self.xAxis = d3.svg.axis() 36 | .scale(_self.x) 37 | .orient("top"); 38 | 39 | _self.svg = d3.select("#overviewchart-viz").append("svg") 40 | .attr("width", _self.width) 41 | .attr("height", _self.axisHeight); 42 | 43 | _self.svg.append("g") 44 | .attr("class", "x axis") 45 | .attr("transform", "translate(0," + (_self.axisHeight - 5) + ")") 46 | .call(_self.xAxis); 47 | 48 | var brush = d3.svg.brush() 49 | .x(_self.x) 50 | .on("brushend", onBrush); 51 | 52 | _self.svg.append("g") 53 | .attr("class", "x brush") 54 | .call(brush) 55 | .selectAll("rect") 56 | .attr("y", 2) 57 | .attr("height", _self.axisHeight - 2); 58 | 59 | function onBrush() { 60 | 61 | _self.b = brush.empty() ? 62 | _self.x.domain() : brush.extent(); 63 | 64 | var empty = brush.empty() ? 1 : 0; 65 | 66 | for (var i = 0; i < _self.linecharts.length; i++) { 67 | 68 | try { 69 | 70 | _self.linecharts[i].showOnly(_self.b, empty); 71 | 72 | } catch (err) { 73 | 74 | console.log("error caught -" + err); 75 | 76 | } 77 | } 78 | 79 | _self.correlationViewer.refresh(); 80 | } 81 | 82 | } 83 | 84 | OverviewHorizonChart.prototype.addHorizon = function (options) { 85 | var _self = this; 86 | 87 | _self.stockObject = options.stockObject; 88 | _self.data = _self.stockObject.data; 89 | _self.id = options.id; 90 | 91 | _self.stockCount = totalSelectedStocks; 92 | 93 | // divide height into segments 94 | _self.horizonHeight = (_self.height - _self.margin.top - _self.margin.bottom) / _self.stockCount; 95 | 96 | _self.horizonWidth = _self.width - _self.margin.left - _self.margin.right; 97 | 98 | var parsedData = _self.data.map(function (d, i) { 99 | return [d[dateCol], d[adjCol]]; 100 | }); 101 | 102 | var chart = d3.horizon() 103 | .width(_self.horizonWidth) 104 | .height(_self.horizonHeight) 105 | .bands(2) 106 | .mode("mirror") 107 | .interpolate("basis"); 108 | 109 | var svg = d3.select("#overviewchart-viz").append("svg") 110 | .attr("width", _self.horizonWidth + _self.margin.left + _self.margin.right) 111 | .attr("height", _self.horizonHeight + _self.margin.top) 112 | .style("border-bottom", "1px solid #000000"); 113 | 114 | // Render the chart. 115 | svg.data([parsedData]).call(chart); 116 | 117 | svg.append("text") 118 | .attr("dx", "1em") 119 | .attr("y", 2*_self.horizonHeight/3) 120 | .text(_self.stockObject.companyName) 121 | .style("font-size", "12px") 122 | .style("fill", "#000000"); 123 | 124 | } -------------------------------------------------------------------------------- /public/js/stockVis-overviewlinechart.js: -------------------------------------------------------------------------------- 1 | //Overview Chart class 2 | 3 | function OverviewLineChart(options) { 4 | var _self = this; 5 | _self.brush = []; 6 | _self.stockObject = options.stockObject; 7 | _self.data = _self.stockObject.data; 8 | _self.margin = { 9 | top: 5, 10 | right: 10, 11 | bottom: 20, 12 | left: 10 13 | }; 14 | 15 | _self.correlationViewer = options.correlationViewer; 16 | 17 | _self.color = options.color; 18 | _self.linecharts = options.linecharts; 19 | 20 | _self.width = $("#overviewchart-viz").parent().width() - _self.margin.left - _self.margin.right; 21 | 22 | _self.height = $("#overviewchart-viz").parent().height() - _self.margin.top - _self.margin.bottom; 23 | 24 | _self.svg = d3.select("#overviewchart-viz").append("svg").attr("class", "overviewchart") 25 | .attr("width", _self.width + _self.margin.left + _self.margin.right) 26 | .attr("height", _self.height + _self.margin.top + _self.margin.bottom) 27 | .append("g") 28 | .attr("transform", "translate(" + _self.margin.left + "," + _self.margin.top + ")"); 29 | 30 | //Axis x - date -- Axis y - value 31 | _self.x = d3.time.scale() 32 | .range([0, _self.width]); 33 | 34 | _self.y = d3.scale.linear() 35 | .range([_self.height - 20, 0]); 36 | 37 | 38 | _self.x.domain(d3.extent(_self.data, function (stock) { 39 | return stock[dateCol]; 40 | })); 41 | 42 | _self.y.domain([0, 1]); 43 | 44 | //x and y axis 45 | _self.xAxis = d3.svg.axis() 46 | .scale(_self.x) 47 | .orient("bottom"); 48 | //.tickFormat(function(d) { return d3.time.format('%b')(new Date(d)); }); 49 | 50 | _self.yAxis = d3.svg.axis() 51 | .scale(_self.y) 52 | .orient("left"); 53 | 54 | _self.line = d3.svg.line() 55 | .interpolate("monotone") 56 | .x(function (d) { 57 | return _self.x(d[dateCol]); 58 | }) 59 | .y(function (d) { 60 | return _self.y(d.normalized); 61 | }); 62 | 63 | _self.svg.append("g") 64 | .attr("class", "x axis") 65 | .attr("transform", "translate(0," + _self.height + ")") 66 | .call(_self.xAxis); 67 | 68 | 69 | _self.svg.append("defs") 70 | .append("clipPath").attr("id", "clip") 71 | .append("rect") 72 | .attr("width", _self.width).attr("height", _self.height); 73 | 74 | _self.chartContainer = _self.svg.append("g") 75 | .attr("width", _self.width).attr("height", _self.height); 76 | 77 | 78 | var brush = d3.svg.brush().x(_self.x).on("brushend", onBrush); 79 | var context = _self.svg.append("g").attr("class", "context") 80 | .attr("transform", "translate(" + 0 + "," + (0) + ")"); 81 | 82 | 83 | _self.b = _self.x.domain(); 84 | 85 | context.append("g").attr("class", "brush") 86 | .call(brush).selectAll("rect").attr("y", 0) 87 | .attr("height", _self.height) 88 | .attr("z-index", 3); 89 | 90 | /* brush and link to guide interaction from the overview chart */ 91 | function onBrush() { 92 | /* this will return a date range to pass into the chart object */ 93 | _self.b = brush.empty() ? _self.x.domain() : brush.extent(); 94 | var empty = brush.empty() ? 1 : 0; 95 | for (var i = 0; i < _self.linecharts.length; i++) { 96 | try { 97 | _self.linecharts[i].showOnly(_self.b, empty); 98 | } catch (err) { 99 | //console.log("error caught -" + err); 100 | } 101 | } 102 | 103 | //_self.correlationViewer.refresh(); 104 | } 105 | 106 | //user study part! 107 | $('#next_button').on('click', function (e) { 108 | var b = _self.b; 109 | if (b === x.domain()) { 110 | return; 111 | } else { 112 | var leftDay = b[0]; 113 | var rightDay = b[1]; 114 | 115 | leftDay.setDate(b[0].getDate() + 1); 116 | if (b[0].getDay() === 6) { 117 | leftDay.setDate(b[0].getDate() + 2); 118 | } 119 | if (b[0].getDay() === 5) { 120 | leftDay.setDate(b[0].getDate() + 3); 121 | } 122 | 123 | rightDay.setDate(b[1].getDate() + 1); 124 | if (b[1].getDay() === 6) { 125 | rightDay.setDate(b[1].getDate() + 2); 126 | } 127 | if (b[1].getDay() === 5) { 128 | rightDay.setDate(b[1].getDate() + 3); 129 | } 130 | 131 | 132 | for (var i = 0; i < _self.linecharts.length; i++) { 133 | try { 134 | _self.linecharts[i].showOnly(b, 1); 135 | } catch (err) { 136 | console.log("error caught -" + err); 137 | } 138 | } 139 | 140 | } 141 | 142 | }); 143 | 144 | } 145 | 146 | OverviewLineChart.prototype.addLine = function (options) { 147 | var _self = this; 148 | _self.stockObject = options.stockObject; 149 | _self.data = _self.stockObject.data; 150 | _self.id = options.id; 151 | 152 | _self.chartContainer.append("path") 153 | .attr("class", "line") 154 | .attr("clip-path", "url(#clip)") 155 | .data([_self.data]) 156 | .attr("d", _self.line) 157 | //.attr("stroke", _self.color(options.id)) 158 | .attr("stroke", "#444") 159 | .attr("fill", "transparent") 160 | .attr("stroke-width", "1.5px") 161 | .attr("opacity", 0.8).attr("z-index", 1); 162 | 163 | }; -------------------------------------------------------------------------------- /public/js/stockVis-prediction.js: -------------------------------------------------------------------------------- 1 | /* Created by Karthik Badam on 06/01/2015 2 | */ 3 | 4 | 5 | function Predictions(options) { 6 | 7 | var _self = this; 8 | 9 | _self.spatialPredictions = []; 10 | 11 | _self.sortedSpatialPredictions = []; 12 | 13 | _self.temporalPredictions = {}; 14 | 15 | _self.stockPresent = {}; 16 | 17 | _self.pastValues = {}; 18 | 19 | _self.TEMPORAL_ALTERNATIVES = 3; 20 | 21 | _self.TEMPORAL_INPUT_SIZE = 6; 22 | 23 | _self.stockPastValues = {}; 24 | 25 | _self.SPATIAL_ALTERNATIVES = 5; 26 | 27 | } 28 | 29 | Predictions.prototype.newSpatialPredictions = function () { 30 | 31 | var _self = this; 32 | 33 | _self.spatialPredictions = []; 34 | 35 | 36 | }; 37 | 38 | Predictions.prototype.newTemporalPredictions = function () { 39 | 40 | var _self = this; 41 | 42 | _self.temporalPredictions = []; 43 | 44 | }; 45 | 46 | 47 | Predictions.prototype.addSpatialPrediction = function (predictions, opacity) { 48 | 49 | var _self = this; 50 | 51 | _self.spatialPredictions.push({ 52 | predictions: predictions, 53 | opacity: opacity 54 | }); 55 | 56 | }; 57 | 58 | Predictions.prototype.sortSpatial = function (predictions, opacity) { 59 | 60 | var _self = this; 61 | 62 | // sort based on opacities 63 | _self.sortedSpatialPredictions = _self.spatialPredictions.sort(function (a, b) { 64 | return b.opacity - a.opacity; 65 | }); 66 | 67 | }; 68 | 69 | Predictions.prototype.getTopSpatialPredictions = function () { 70 | 71 | var _self = this; 72 | 73 | _self.sortSpatial(); 74 | 75 | var sp = _self.sortedSpatialPredictions.splice(0, _self.SPATIAL_ALTERNATIVES); 76 | 77 | if (!sp) 78 | return; 79 | 80 | var min = sp[sp.length - 1].opacity; 81 | 82 | var max = sp[0].opacity; 83 | 84 | for (var i = 0; i < sp.length; i++) { 85 | 86 | sp[i].opacity = (sp[i].opacity - min) / (max - min); 87 | } 88 | 89 | return sp; 90 | } 91 | 92 | Predictions.prototype.getProcessedTemporalInput = function (input) { 93 | 94 | var _self = this; 95 | 96 | var processedInput = []; 97 | 98 | var past = input[0]; 99 | 100 | for (var i = 1; i < input.length; i++) { 101 | 102 | var change = (input[i] - past) * 10 / past; 103 | 104 | if (change > 1) { 105 | 106 | change = 1; 107 | 108 | } else if (change < -1) { 109 | 110 | change = -1; 111 | } 112 | 113 | processedInput.push((1 + change) / 2); 114 | 115 | } 116 | 117 | return processedInput; 118 | 119 | } 120 | 121 | function processPrediction(prediction, previous) { 122 | 123 | prediction = (2 * prediction - 1); 124 | 125 | prediction = prediction * previous / 10 + previous; 126 | 127 | return prediction; 128 | 129 | } 130 | 131 | Predictions.prototype.generateTemporalPredictions = function (stockId, input) { 132 | 133 | var _self = this; 134 | 135 | var actualInput = _self.getProcessedTemporalInput(input); 136 | 137 | var allInputs = []; 138 | 139 | allInputs.push({ 140 | input: input, 141 | opacity: 0.8 142 | }); 143 | 144 | for (var i = 0; i < _self.TEMPORAL_ALTERNATIVES; i++) { 145 | 146 | //var tempInput = actualInput.slice(0); 147 | var tempInput = input.slice(0); 148 | 149 | //get a random number 150 | var alternations = Math.floor(0.7 * _self.TEMPORAL_ALTERNATIVES); //i 151 | 152 | for (var j = 0; j < alternations; j++) { 153 | 154 | //var index = Math.floor(Math.random() * _self.TEMPORAL_ALTERNATIVES); 155 | 156 | var index = _self.TEMPORAL_INPUT_SIZE - 1 - j < 0 ? 2 * _self.TEMPORAL_INPUT_SIZE - 1 - j : _self.TEMPORAL_INPUT_SIZE - 1 - j; 157 | 158 | if (Math.random() < 0.5) { 159 | 160 | tempInput[index] = tempInput[index] + 0.1 * tempInput[index]; 161 | //tempInput[index] = tempInput[index] + 0.4 > 1? 1: tempInput[index] + 0.4; 162 | 163 | } else { 164 | 165 | tempInput[index] = tempInput[index] - 0.1 * tempInput[index]; 166 | //tempInput[index] = tempInput[index] - 0.4 > -1? -1: tempInput[index] - 0.4; 167 | } 168 | } 169 | 170 | allInputs.push({ 171 | input: tempInput, 172 | opacity: (_self.TEMPORAL_ALTERNATIVES - alternations) / _self.TEMPORAL_ALTERNATIVES 173 | }); 174 | 175 | } 176 | 177 | var temporalPredictionArray = []; 178 | var predictor = temporalPredictors[stockId]; 179 | 180 | var allPredictions = []; 181 | 182 | for (var i = 0; i < allInputs.length; i++) { 183 | 184 | var output = predictor.predict(_self.getProcessedTemporalInput(allInputs[i].input)); 185 | 186 | var processedOutput = processPrediction(output[0], input[0]); 187 | 188 | allPredictions.push({ 189 | prediction: processedOutput, 190 | opacity: allInputs[i].opacity, 191 | input: allInputs[i].input 192 | }); 193 | 194 | } 195 | 196 | return allPredictions; 197 | } 198 | 199 | Predictions.prototype.setCurrentPresent = function (stockId, dataFiltered) { 200 | 201 | var _self = this; 202 | 203 | if (dataFiltered.length == 0) 204 | return; 205 | 206 | _self.stockPresent[stockId] = dataFiltered[0][dateCol]; 207 | 208 | var input = new Array(_self.TEMPORAL_INPUT_SIZE + 1); 209 | 210 | //no normalization 211 | for (var i = _self.TEMPORAL_INPUT_SIZE; i >= 0; i--) { 212 | input[i] = dataFiltered[_self.TEMPORAL_INPUT_SIZE - i][adjCol]; 213 | } 214 | 215 | _self.stockPastValues[stockId] = input; 216 | 217 | } 218 | 219 | Predictions.prototype.predictFutureSteps = function (stockId, nTimes, dataFiltered, predictSpatial, userPrediction) { 220 | 221 | var _self = this; 222 | 223 | var predictions = []; 224 | 225 | var previous = 0; 226 | 227 | var spatialPreds = []; 228 | 229 | _self.setCurrentPresent(stockId, dataFiltered); 230 | 231 | var future = new Date(_self.stockPresent[stockId].getTime()); 232 | 233 | var pastBestPredictions = []; 234 | 235 | var temporalBands = []; 236 | 237 | var spatialBands = []; 238 | 239 | temporalBands.push({ 240 | high: dataFiltered[0][adjCol], 241 | low: dataFiltered[0][adjCol], 242 | step: -1 243 | }); 244 | 245 | for (var i = 0; i < nTimes; i++) { 246 | 247 | var presentDate = new Date(future.getTime()); 248 | 249 | future = getFutureDate(future); 250 | 251 | var input = _self.stockPastValues[stockId].slice(i); 252 | 253 | input = input.concat(pastBestPredictions); 254 | 255 | previous = input[input.length - 1]; 256 | 257 | var output = _self.generateTemporalPredictions(stockId, input); 258 | 259 | for (var j = 0; j < output.length; j++) { 260 | 261 | var currPrediction = output[j].prediction; 262 | 263 | predictions.push({ 264 | prediction: currPrediction, 265 | date: presentDate, 266 | past: previous, 267 | opacity: output[j].opacity, 268 | step: i 269 | }); 270 | 271 | } 272 | 273 | // lets fit the high confidence till the last step 274 | var bestPrediction = output[0].prediction; 275 | 276 | // get the band information from output variables 277 | var temporalBandData = getTPredictionBand(output, i); 278 | temporalBands.push(temporalBandData); 279 | 280 | if (i != nTimes - 1) { 281 | 282 | pastBestPredictions.push(bestPrediction); 283 | 284 | } else { 285 | 286 | var minIndex = 0; 287 | var min = 10000000; 288 | 289 | for (var j = 0; j < output.length; j++) { 290 | 291 | var currPrediction = output[j].prediction; 292 | 293 | var difference = Math.abs(currPrediction - userPrediction); 294 | 295 | if (min > difference) { 296 | 297 | min = difference; 298 | 299 | minIndex = j; 300 | } 301 | 302 | } 303 | 304 | bestPrediction = output[minIndex].prediction; 305 | 306 | pastBestPredictions.push(bestPrediction); 307 | } 308 | 309 | 310 | if (predictSpatial && i!=nTimes) { 311 | 312 | //get the list of predicted stocks 313 | var stockIds = Object.keys(currentUserQuery); 314 | 315 | var query = {}; 316 | 317 | for (var stockIdIndex = 0; stockIdIndex < stockIds.length; stockIdIndex++) { 318 | var chart = chartObjects[stockIds[stockIdIndex]]; 319 | var change = (chart.topTemporalPredictions[i+1] 320 | - chart.topTemporalPredictions[i]) * 100/chart.topTemporalPredictions[i]; 321 | 322 | query[stockIds[stockIdIndex]] = change; 323 | } 324 | 325 | var change = (bestPrediction - previous) * 100 / previous; 326 | 327 | //var spatialPred = spatialPrediction.getPredictions(stockId, change); 328 | 329 | var spatialPred = spatialPrediction.getPredictions(stockId, change, query); 330 | 331 | var bands = getSPredictionBand(spatialPred, i); 332 | 333 | spatialPreds.push({ 334 | predictions: spatialPred, 335 | date: presentDate, 336 | past: previous, 337 | step: i, 338 | }); 339 | 340 | spatialBands.push(bands); 341 | } 342 | 343 | } 344 | 345 | return { 346 | temporal: predictions, 347 | temporalband: temporalBands, 348 | spatial: spatialPreds, 349 | spatialband: spatialBands 350 | }; 351 | 352 | } 353 | 354 | function getTPredictionBand(data, step) { 355 | 356 | var band = {}; //{ step: 0, low: 0, high: 0 } 357 | 358 | band.step = step; 359 | 360 | // var low = d3.min(data, function (d) { 361 | // return d.prediction; 362 | // }); 363 | // 364 | // var high = d3.max(data, function (d) { 365 | // return d.prediction; 366 | // }); 367 | // 368 | // band.low = low; 369 | // band.high = high; 370 | 371 | 372 | var wsum = d3.sum(data, function (d) { 373 | return d.prediction * d.opacity; 374 | }); 375 | 376 | var osum = d3.sum(data, function (d) { 377 | return d.opacity; 378 | }); 379 | 380 | var mean = wsum / osum; 381 | 382 | band.mean = mean; 383 | 384 | wsum = d3.sum(data, function (d) { 385 | return Math.pow(d.prediction - mean, 2) * d.opacity; 386 | }); 387 | 388 | osum = d3.sum(data, function (d) { 389 | return d.opacity; 390 | }); 391 | 392 | var stdDev = Math.pow((data.length * wsum) / ((data.length - 1) * osum), 0.5); 393 | 394 | band.low = mean - 2 * stdDev; 395 | band.high = mean + 2 * stdDev; 396 | 397 | return band; 398 | } 399 | 400 | function getSPredictionBand(data, step) { 401 | 402 | var bands = {}; //{ step: 0, low: 0, high: 0 } 403 | 404 | if (!data) { 405 | return; 406 | } 407 | 408 | var numOfStocks = data[0].predictions.length; 409 | 410 | for (var i = 0; i < numOfStocks; i++) { 411 | 412 | // get the actual value at that step 413 | var stockId = stockSymbols[i]; 414 | 415 | if (selectedSymbols.indexOf(stockId) <= -1) { 416 | continue; 417 | } 418 | 419 | if (!bands[stockId]) { 420 | bands[stockId] = {}; 421 | } 422 | 423 | var wsum = d3.sum(data, function (d) { 424 | var value = chartObjects[stockId].topTemporalPredictions[step] + chartObjects[stockId].topTemporalPredictions[step] * d.predictions[i] / 100; 425 | 426 | return value * d.opacity; 427 | }); 428 | 429 | var osum = d3.sum(data, function (d) { 430 | return d.opacity; 431 | }); 432 | 433 | var mean = wsum / osum; 434 | 435 | wsum = d3.sum(data, function (d) { 436 | var value = chartObjects[stockId].topTemporalPredictions[step] + chartObjects[stockId].topTemporalPredictions[step] * d.predictions[i] / 100; 437 | 438 | return Math.pow(value - mean, 2) * d.opacity; 439 | }); 440 | 441 | osum = d3.sum(data, function (d) { 442 | return d.opacity; 443 | }); 444 | 445 | var stdDev = Math.pow((data.length * wsum) / ((data.length - 1) * osum), 0.5); 446 | 447 | 448 | var low = mean - 2 * stdDev; 449 | 450 | var high = mean + 2 * stdDev; 451 | 452 | // var low = d3.min(data, function (d) { 453 | // var value = chartObjects[stockId].topTemporalPredictions[step] + chartObjects[stockId].topTemporalPredictions[step] * d.predictions[i] / 100; 454 | // 455 | // return value; 456 | // }); 457 | // 458 | // var high = d3.max(data, function (d) { 459 | // var value = chartObjects[stockId].topTemporalPredictions[step] + chartObjects[stockId].topTemporalPredictions[step] * d.predictions[i] / 100; 460 | // 461 | // return value; 462 | // }); 463 | // 464 | bands[stockId].high = high; 465 | 466 | bands[stockId].low = low; 467 | 468 | bands[stockId].mean = mean; 469 | 470 | // Need to show branches 471 | 472 | // Add more options -- in terms of machine learning! 473 | 474 | } 475 | 476 | bands.step = step; 477 | 478 | return bands; 479 | } -------------------------------------------------------------------------------- /public/js/stockVis-spatialprediction.js: -------------------------------------------------------------------------------- 1 | //Class Spatial Prediction 2 | 3 | function SpatialPrediction(options) { 4 | var _self = this; 5 | _self.weights = options.weights; 6 | _self.trainingStocks = options.trainingStocks; 7 | 8 | } 9 | 10 | SpatialPrediction.prototype.getPredictions = function (stock_symbol, prediction, query) { 11 | var _self = this; 12 | 13 | _self.predictionArrays = []; 14 | _self.predictionOpacities = []; 15 | 16 | var stockIndex = _self.trainingStocks.indexOf(stock_symbol); 17 | 18 | predictionObject.newSpatialPredictions(); 19 | 20 | 21 | console.log(query); 22 | 23 | 24 | /* Get the weights from the SOM to get the closest values from the history */ 25 | for (var i = 0; i < _self.weights.length; i++) { 26 | 27 | for (var j = 0; j < _self.weights[i].length; j++) { 28 | var weightArray = []; 29 | 30 | for (var k = 0; k < _self.weights[i][j].length; k++) { 31 | 32 | weightArray.push(_self.weights[i][j][k]); 33 | 34 | weightArray[k] = (2 * weightArray[k] - 1) * 100 / 10; 35 | 36 | } 37 | 38 | var stockIndices = Object.keys(query); 39 | 40 | var distance = 0; 41 | 42 | for (var z = 0; z < stockIndices.length; z++) { 43 | 44 | var stockIndex = _self.trainingStocks.indexOf(stockIndices[z]); 45 | 46 | distance += Math.pow(weightArray[stockIndex] - 47 | query[stockIndices[z]], 2); 48 | 49 | } 50 | 51 | if (Math.abs(distance) < 20) { 52 | 53 | var opacity = Math.pow(1 - Math.pow(distance, 0.5) / 100, 2); 54 | predictionObject.addSpatialPrediction(weightArray, 55 | opacity); 56 | 57 | } 58 | } 59 | } 60 | 61 | 62 | return predictionObject.getTopSpatialPredictions(); 63 | 64 | // var returnVal = []; 65 | // 66 | // returnVal.arrays = _self.predictionArrays; 67 | // returnVal.opacities = _self.predictionOpacities; 68 | // 69 | // return returnVal; 70 | }; 71 | 72 | 73 | SpatialPrediction.prototype.getPredictions2 = function (prediction, stock_symbol) { 74 | 75 | var _self = this; 76 | _self.predictionArrays = []; 77 | _self.predictionOpacities = []; 78 | var stockIndex = _self.trainingStocks.indexOf(stock_symbol); 79 | 80 | /* Get the weights from the SOM to get the closest values from the history */ 81 | for (var i = 0; i < _self.weightsSize; i++) { 82 | var distance = Math.abs(_self.weights[i][stockIndex] - prediction); 83 | if (distance < 1) { 84 | var opacity = Math.pow(1 - distance, 2); 85 | _self.predictionArrays.push(_self.weights[i]); 86 | _self.predictionOpacities.push(opacity); 87 | } 88 | } 89 | 90 | var returnVal = []; 91 | returnVal.arrays = _self.predictionArrays; 92 | returnVal.opacities = _self.predictionOpacities; 93 | return returnVal; 94 | }; -------------------------------------------------------------------------------- /public/js/stockVis-stock.js: -------------------------------------------------------------------------------- 1 | //Stock datastructure to store value, volume, and manage operations on the stock 2 | 3 | function Stock(options) { 4 | var _self = this; 5 | _self.data = options.data; 6 | _self.companyName = options.companyName; 7 | _self.symbol = options.symbol; 8 | _self.startDate = options.startDate; 9 | _self.min = 0; 10 | _self.max = 0; 11 | _self.normalization = 0; 12 | _self.dataFiltered = _self.data; 13 | } 14 | 15 | Stock.prototype.normalize = function (close_values) { 16 | var _self = this; 17 | var max = Math.max.apply(Math, close_values) + 0.0001; 18 | var min = Math.min.apply(Math, close_values) - 0.0001; 19 | 20 | for (var i = 0; i < close_values.length; i++) { 21 | _self.data[i].normalized = (_self.data[i].normalized - min) / (max - min); 22 | } 23 | 24 | _self.min = 100000; 25 | _self.max = 0; 26 | for (var i = 0; i < _self.data.length; i++) { 27 | if (_self.min > _self.data[i][adjCol] && _self.data[i][dateCol] > _self.startDate) { 28 | _self.min = _self.data[i][adjCol]; 29 | } 30 | if (_self.max < _self.data[i][adjCol] && _self.data[i][dateCol] > _self.startDate) { 31 | _self.max = _self.data[i][adjCol]; 32 | } 33 | } 34 | 35 | _self.min = _self.min - 0.0002; 36 | _self.normalization = _self.max - _self.min + 0.0003; 37 | console.log(_self.companyName + " min -" + _self.min + " norm -" + _self.normalization); 38 | 39 | }; 40 | 41 | Stock.prototype.deNormalize = function (value) { 42 | var _self = this; 43 | return _self.min + value * _self.normalization; 44 | }; 45 | 46 | Stock.prototype.normalizeValue = function (value) { 47 | var _self = this; 48 | return (value - _self.min) / _self.normalization; 49 | }; 50 | 51 | Stock.prototype.getFilteredData = function (brush) { 52 | var _self = this; 53 | var dataFiltered = _self.dataFiltered = 54 | _self.data.filter(function (d, i) { 55 | if ((d[dateCol] >= brush[0]) && (d[dateCol] <= brush[1])) { 56 | return true; 57 | } 58 | }); 59 | 60 | return dataFiltered; 61 | }; 62 | 63 | Stock.prototype.getRawBandData = function (brush, n) { 64 | var _self = this; 65 | 66 | var index1 = 0, 67 | index2 = 0; 68 | 69 | var val1 = _self.dataFiltered[0]; 70 | var val2 = _self.dataFiltered[_self.dataFiltered.length-1]; 71 | 72 | for (var i = 0; i < _self.data.length; i++) { 73 | 74 | var d = _self.data[i]; 75 | 76 | if (d[dateCol].getTime() === val1[dateCol].getTime()) { 77 | index1 = i; 78 | } 79 | 80 | 81 | if (d[dateCol].getTime() === val2[dateCol].getTime()) { 82 | index2 = i; 83 | } 84 | 85 | } 86 | 87 | index2 = index2 > _self.data.length - 1 - 20 ? 88 | _self.data.length - 1 : index2 + n; 89 | 90 | var rawData = _self.data.slice(index1, index2+1); 91 | 92 | return rawData; 93 | }; -------------------------------------------------------------------------------- /public/js/stockVis-temporalprediction.js: -------------------------------------------------------------------------------- 1 | //Class Temporal Prediction 2 | 3 | function TemporalPrediction(options) { 4 | var _self = this; 5 | // var layers = this.layers = []; 6 | // layers[0] = ENCOG.BasicLayer.create(ENCOG.ActivationTANH.create(), 15, 1); 7 | // layers[1] = ENCOG.BasicLayer.create(ENCOG.ActivationTANH.create(), 41, 1); 8 | // layers[2] = ENCOG.BasicLayer.create(ENCOG.ActivationTANH.create(), 41, 1); 9 | // layers[3] = ENCOG.BasicLayer.create(ENCOG.ActivationTANH.create(), 1, 1); 10 | // 11 | // var network = this.network = ENCOG.BasicNetwork.create(layers); 12 | // 13 | // network.randomize(); 14 | // var result = ENCOG.EGFILE.save(network); 15 | // 16 | 17 | _self.stockName = options.stock_name; 18 | _self.encogFileContent = options.encog_file; 19 | 20 | _self.network = ENCOG.EGFILE.load(_self.encogFileContent); 21 | 22 | var result = ENCOG.EGFILE.save(_self.network); 23 | 24 | console.log("result --"+result); 25 | }; 26 | 27 | /* calculates temporal prediction */ 28 | TemporalPrediction.prototype.predict = function(input) { 29 | var _self = this; 30 | 31 | var output = new Array(1); 32 | _self.network.compute(input, output); 33 | 34 | return output; 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /public/js/stockVis-temporalprediction1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Karthik Badam on Feb 10, 2015 3 | * 4 | */ 5 | 6 | function TemporalPrediction(options) { 7 | var _self = this; 8 | 9 | _self.stockName = options.stock_name; 10 | 11 | _self.network = new brain.NeuralNetwork(); 12 | 13 | d3.json("data/train/train-" + _self.stockName + ".json", function(error, json) { 14 | if (error) return console.warn(error); 15 | _self.network.fromJSON(json); 16 | console.log("Loaded network"); 17 | }); 18 | 19 | 20 | 21 | }; 22 | 23 | /* calculates temporal prediction */ 24 | TemporalPrediction.prototype.predict = function(input) { 25 | var _self = this; 26 | 27 | //console.log(input); 28 | //var output = new Array(1); 29 | 30 | var output = _self.network.run(input); 31 | 32 | //console.log(output); 33 | 34 | return output; 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /public/js/stockVis-tree.js: -------------------------------------------------------------------------------- 1 | // Created by Karthik Badam 06/06/2015 2 | 3 | function Tree(options) { 4 | 5 | var _self = this; 6 | 7 | _self.rootInput = options.rootInput; 8 | 9 | _self.size = options.size; 10 | 11 | _self.branches = options.branches; 12 | 13 | _self.stockId = options.stockId; 14 | 15 | _self.startDate = options.startDate; 16 | 17 | } 18 | 19 | Tree.prototype.getBranchesFromNode = function (parent, currentDate, step) { 20 | 21 | var _self = this; 22 | 23 | //process input from this 24 | var allPredictions = predictionObject.generateTemporalPredictions(parent.shift()); 25 | var branches = []; 26 | 27 | for (var i = 0; i < allPredictions.length; i++) { 28 | 29 | var output = allPredictions[i].prediction; 30 | var raw = allPredictions[i]; 31 | 32 | TreeNode node = new TreeNode({ 33 | input: allPredictions[i].input, 34 | parent: parent, 35 | output: allPredictions[i].prediction, 36 | opacity: allPredictions[i].opacity, 37 | date: currentDate, 38 | step: step 39 | }); 40 | 41 | 42 | } 43 | } 44 | 45 | Tree.prototype.createTree = function () { 46 | 47 | var _self = this; 48 | 49 | var future = new Date(_self.startDate.getTime()); 50 | 51 | var presentDate = new Date(future.getTime()); 52 | 53 | future = getFutureDate(future); 54 | 55 | // create a bunch of branches for each node at the level 56 | var root = new TreeNode({ 57 | input: _self.rootInput, 58 | parent: null, 59 | output: null 60 | }) 61 | 62 | var branchList = []; 63 | 64 | branchList.push(_self.getBranchesFromNode(root, presentDate, 0)); 65 | 66 | // number of tree 67 | for (var i = 0; i < _self.size - 1; i++) { 68 | 69 | var presentDate = new Date(future.getTime()); 70 | 71 | future = getFutureDate(future); 72 | 73 | while(array.length != 0) { 74 | 75 | var node = branchList[0]; 76 | 77 | branchList.push(getBranchesFromNode(node, presentDate, i)); 78 | 79 | branchList = branchList.slice(1); 80 | } 81 | } 82 | } 83 | 84 | function TreeNode (options) { 85 | 86 | var _self = this; 87 | 88 | _self.input = options.input; 89 | 90 | _self.output = options.output; 91 | 92 | _self.parent = options.parent; 93 | 94 | _self.date = options.date; 95 | 96 | _self.opacity = options.opacity; 97 | } 98 | 99 | TreeNode.prototype.setOutput = function (output) { 100 | 101 | var _self = this; 102 | 103 | _self.output = output; 104 | 105 | } 106 | 107 | TreeNode.prototype.setParent = function (parent) { 108 | 109 | var _self = this; 110 | 111 | _self.parent = parent; 112 | 113 | } 114 | 115 | TreeNode.prototype.shift = function () { 116 | 117 | var _self = this; 118 | 119 | if (_self.output == null) { 120 | return _self.input; 121 | } 122 | 123 | var newInput1 = _self.input.slice(1); 124 | 125 | newInput1.concat(_self.output.prediction); 126 | 127 | return newInput1; 128 | } 129 | -------------------------------------------------------------------------------- /public/js/stockVis.client.js: -------------------------------------------------------------------------------- 1 | // Convert this into a framework for any data! 2 | 3 | // one meta data file on the data attributes 4 | 5 | // one file each for all other data 6 | 7 | var stockList = 'data/topInternetStocks.csv'; 8 | 9 | var parseDate = d3.time.format("%Y-%m-%d").parse; 10 | 11 | var stockSymbols = []; 12 | 13 | var companyNames = []; 14 | 15 | var selectedSymbols = []; 16 | 17 | var selectedSymbolsData = []; 18 | 19 | var newlySelectedSymbols = []; 20 | 21 | var totalSelectedStocks = 0; 22 | 23 | var trainingStockList; 24 | 25 | var weightsSOM; 26 | 27 | var spatialPrediction; 28 | 29 | var color = d3.scale.category10(); 30 | 31 | var charts = []; 32 | 33 | var chartObjects = {}; 34 | 35 | var overviewChart; 36 | 37 | var currentUserQuery = {}; 38 | 39 | //var stockColumns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close']; 40 | 41 | // columns 42 | var dateCol = "Date"; 43 | 44 | var openCol = "Open"; 45 | 46 | var highCol = "High"; 47 | 48 | var lowCol = "Low"; 49 | 50 | var closeCol = "Close"; 51 | 52 | var volumeCol = "Volume"; 53 | 54 | var adjCol = "Adj Close"; 55 | 56 | var temporalPredictors = {}; 57 | 58 | var stocks = []; 59 | 60 | var stockObjects = {}; 61 | 62 | var startDate = parseDate("2010-05-06"); 63 | 64 | var correlationViewer; 65 | 66 | var predictionObject; 67 | 68 | var userPredictions = {}; 69 | 70 | var holidays = [parseDate("2013-01-01"), parseDate("2014-01-01"), parseDate("2015-01-01"), parseDate("2013-01-21"), parseDate("2014-01-20"), parseDate("2015-01-19"), parseDate("2013-02-18"), parseDate("2014-02-17"), parseDate("2015-02-16"), parseDate("2013-03-29"), parseDate("2014-04-18"), parseDate("2015-04-03"), parseDate("2013-05-27"), parseDate("2014-05-26"), parseDate("2015-05-25"), parseDate("2013-07-04"), parseDate("2014-07-04"), parseDate("2015-07-04"), parseDate("2013-09-02"), parseDate("2014-09-01"), parseDate("2015-09-07"), parseDate("2013-11-28"), parseDate("2014-11-27"), parseDate("2015-11-26"), parseDate("2013-12-25"), parseDate("2014-12-25"), parseDate("2015-12-25")]; 71 | 72 | // Earnings for the stock market game 73 | var totalEarnings = 100000; 74 | 75 | var investment = {}; 76 | 77 | var step1 = 0, 78 | step2 = 0, 79 | step3 = 0, 80 | step4 = 0; 81 | 82 | function getFutureDate(today) { 83 | 84 | var tomorrow = new Date(today.getTime()); 85 | 86 | tomorrow.setMonth(today.getMonth()); 87 | tomorrow.setFullYear(today.getFullYear()); 88 | 89 | tomorrow.setDate(today.getDate() + 1); 90 | 91 | if (today.getDay() == 6) { 92 | tomorrow.setDate(today.getDate() + 2); 93 | } 94 | 95 | if (today.getDay() == 5) { 96 | tomorrow.setDate(today.getDate() + 3); 97 | } 98 | 99 | for (var i = 0; i < holidays.length; i++) { 100 | if (holidays[i].getTime() == tomorrow.getTime()) { 101 | tomorrow = getFutureDate(tomorrow); 102 | } 103 | } 104 | 105 | return tomorrow; 106 | 107 | } 108 | 109 | $(document).ready(function () { 110 | 111 | 112 | $(window).keypress(function (e) { 113 | if (e.keyCode === 0 || e.keyCode === 32) { 114 | console.log('Space pressed'); 115 | 116 | //clear the predictions 117 | 118 | for (var i = 0; i < charts.length; i++) { 119 | charts[i].showOnly(overviewChart.b, "null"); 120 | } 121 | 122 | e.preventDefault(); 123 | e.stopPropagation(); 124 | e.stopImmediatePropagation(); 125 | 126 | } 127 | }); 128 | 129 | //create Correlation Viewer 130 | correlationViewer = new CorrelationChart(); 131 | 132 | //initialize a predictions object 133 | predictionObject = new Predictions(); 134 | 135 | //Download file for spatial prediction 136 | $.get("data/train/SOM_WEIGHTS.json", function (data) { 137 | console.log("Data: "); 138 | 139 | data = JSON.parse(JSON.stringify(data)); 140 | 141 | trainingStockList = stockSymbols; 142 | spatialPrediction = new SpatialPrediction({ 143 | weights: data.data, 144 | trainingStocks: trainingStockList, 145 | stockSymbols: stockSymbols 146 | }); 147 | 148 | }, "json"); 149 | 150 | //reads the list of stocks first 151 | d3.csv(stockList, function (error, data) { 152 | 153 | //for each string element in the data 154 | data.forEach(function (d) { 155 | 156 | //collects all stock values into a data structure 157 | stockSymbols.push(d.symbols); 158 | companyNames.push(d.company); 159 | 160 | //adds each stock to a list in UI and associate it with a handler 161 | $('#stocklist').append(''); 162 | 163 | }); 164 | 165 | $("#search").autocomplete({ 166 | source: companyNames 167 | }); 168 | 169 | 170 | /* Searches box above the list of stocks */ 171 | $("#search").keyup(function (e) { 172 | //checking for a click of the "Enter" key 173 | if (e.keyCode == 13) { 174 | var selectedCompany = $("#search").val(); 175 | var index = companyNames.indexOf(selectedCompany); 176 | newlySelectedSymbols.push(stockSymbols[index]); 177 | } 178 | }); 179 | 180 | /* Gets all selections in a newlySelectedSymbols list */ 181 | $("select").change(function () { 182 | newlySelectedSymbols.length = 0; 183 | $("select option:selected").each(function () { 184 | theID = $(this).attr('id'); 185 | if (selectedSymbols.indexOf(theID) <= -1) { 186 | newlySelectedSymbols.push(theID); 187 | } 188 | }); 189 | }); 190 | 191 | 192 | /* Adds newly selected stocks to the workspace */ 193 | $("#add_button").on('click', function (e) { 194 | 195 | //queue for handling file reading 196 | var q = queue(); 197 | 198 | totalSelectedStocks += newlySelectedSymbols.length; 199 | 200 | //goes through the newlySelectedSymbols list and download each stock data file 201 | newlySelectedSymbols.forEach(function (stock_id) { 202 | 203 | //stock not already selected 204 | if (selectedSymbols.indexOf(stock_id) <= -1) { 205 | 206 | var stock_name = companyNames[stockSymbols.indexOf(stock_id)]; 207 | 208 | q.defer(function (callback) { 209 | 210 | /* Loads the data for this particular stock */ 211 | // TODO: How about using a browser database? 212 | 213 | $.ajax({ 214 | type: "GET", 215 | url: "/stockData", 216 | data: { 217 | stock: stock_id 218 | } 219 | }).done(function (data) { 220 | 221 | //d3.csv('data/' + stock_id + '.csv', function (error, data) { 222 | //Data downloaded 223 | console.log('Downloaded data for stock ' + stock_id); 224 | 225 | data = JSON.parse(data); 226 | 227 | //add stock to selected list 228 | selectedSymbols.push(stock_id); 229 | selectedSymbolsData.push(data); 230 | 231 | var close_values = []; 232 | 233 | //look inside the csv data 234 | data.forEach(function (stock_instance) { 235 | //convert date format 236 | stock_instance[dateCol] = parseDate(String(stock_instance[dateCol])); 237 | 238 | //convert other column values to numbers 239 | stock_instance[openCol] = +stock_instance[openCol]; 240 | stock_instance[highCol] = +stock_instance[highCol]; 241 | stock_instance[lowCol] = +stock_instance[lowCol]; 242 | stock_instance[closeCol] = +stock_instance[closeCol]; 243 | stock_instance[volumeCol] = +stock_instance[volumeCol]; 244 | stock_instance[adjCol] = +stock_instance[adjCol]; 245 | stock_instance['normalized'] = stock_instance[adjCol]; 246 | close_values.push(stock_instance[adjCol]); 247 | 248 | }); 249 | 250 | //creates a stock object for future reference 251 | var stockObject = new Stock({ 252 | data: data, 253 | companyName: stock_name, 254 | symbol: stock_id, 255 | startDate: startDate, 256 | 257 | }); 258 | 259 | stocks.push(stockObject); 260 | stockObjects[stock_id] = stockObject; 261 | 262 | stockObject.normalize(close_values); 263 | 264 | var lcObject = new LineChart({ 265 | stockObject: stockObject, 266 | id: selectedSymbols.indexOf(stock_id) % 10, 267 | name: stock_name, 268 | symbol: stock_id, 269 | color: color, 270 | trainingStocks: trainingStockList, 271 | charts: charts, 272 | chartObjects: chartObjects, 273 | spatialPrediction: spatialPrediction, 274 | temporalPredictors: temporalPredictors 275 | }); 276 | 277 | charts.push(lcObject); 278 | chartObjects[stock_id] = lcObject; 279 | 280 | /* Checks if there is an overview chart created -- if not -- do it */ 281 | if ($("#overviewchart-viz").contents().length < 1) { 282 | overviewChart = new OverviewHorizonChart({ 283 | stockObject: stockObject, 284 | id: selectedSymbols.indexOf(stock_id) % 10, 285 | name: stock_id, 286 | color: color, 287 | linecharts: charts, 288 | columns: [dateCol, adjCol], 289 | correlationViewer: correlationViewer 290 | }); 291 | } 292 | 293 | overviewChart.addHorizon({ 294 | stockObject: stockObject, 295 | id: selectedSymbols.indexOf(stock_id) % 10 296 | }); 297 | 298 | // document.getElementById(stock_id).style.color = color(selectedSymbols.indexOf(stock_id) % 10); 299 | 300 | document.getElementById(stock_id).style.backgroundColor = "#EEEEEE"; 301 | 302 | callback(null, data); 303 | 304 | }); 305 | 306 | }); 307 | } 308 | }); 309 | //emptying this list because the data has already been downloaded 310 | newlySelectedSymbols = []; 311 | 312 | q.await(createCorrelation); 313 | }); 314 | }); 315 | }); 316 | 317 | function createCorrelation() { 318 | 319 | correlationViewer.add({ 320 | selectedSymbolsData: selectedSymbolsData, 321 | stocks: stocks, 322 | selectedSymbols: selectedSymbols, 323 | color: color, 324 | }); 325 | correlationViewer.refresh(); 326 | } -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index.html', {}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /stock-forest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by karthik on 5/24/15. 3 | */ 4 | 5 | /** 6 | * Classifying into 100 classes 7 | **/ 8 | 9 | function Stock (options) { 10 | var _self = this; 11 | 12 | var csv = _self.csv = require('fast-csv'); 13 | var fs = _self.fs = require('fs'); 14 | var d3 = _self.d3 = require('d3'); 15 | var RandomForestClassifier = _self.RandomForestClassifier = require('random-forest-classifier').RandomForestClassifier; 16 | 17 | _self.parseDate = _self.d3.time.format("%Y%m%d").parse; 18 | 19 | _self.companyName = options.company; 20 | _self.symbol = options.symbol; 21 | _self.data = []; 22 | 23 | _self.min = 100000; 24 | _self.max = -100000; 25 | 26 | _self.FOREST_SIZE = 10; 27 | _self.DECISION_TREE_SIZE = 4; 28 | _self.TEMPORAL_INPUT_SIZE = 5; 29 | _self.TEMPORAL_OUTPUT_SIZE = 1; 30 | 31 | var rf = new RandomForestClassifier({ 32 | n_estimators: _self.FOREST_SIZE 33 | }); 34 | 35 | console.log("I am in child for "+_self.symbol); 36 | 37 | var getTemporalNetworkInput = function () { 38 | 39 | console.log("I am getting training data for "+_self.symbol); 40 | 41 | var trainingData = []; 42 | 43 | var dataSize = _self.data.length; 44 | 45 | if (dataSize < _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE) { 46 | return; 47 | } 48 | 49 | for (var i = dataSize - 1; i >= _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE - 1; i--) { 50 | 51 | var inputDatum = {}; 52 | 53 | for (var j = 0; j < _self.TEMPORAL_OUTPUT_SIZE; j++) { 54 | var d = _self.data[i-j]; 55 | inputDatum["future"] = ""+ Math.round(d["normalized"]*10); 56 | } 57 | 58 | for (var j = _self.TEMPORAL_OUTPUT_SIZE; j < _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE; j++) { 59 | var d = _self.data[i-j]; 60 | inputDatum["f"+j] = d["normalized"]; 61 | } 62 | 63 | trainingData.push(inputDatum); 64 | } 65 | 66 | return trainingData; 67 | } 68 | 69 | 70 | 71 | var trainTemporal = function () { 72 | 73 | console.log("I am training for "+_self.symbol); 74 | 75 | var trainingData = getTemporalNetworkInput(); 76 | 77 | console.log(trainingData); 78 | 79 | if (trainingData) { 80 | 81 | 82 | rf.fit(trainingData, null, "future", function(err, trees){ 83 | 84 | console.log(JSON.stringify(trees, null, 4)); 85 | 86 | var pred = rf.predict(trainingData.slice(1, 10), trees); 87 | 88 | console.log(pred); 89 | 90 | }); 91 | 92 | 93 | //write weights to file 94 | 95 | //console.log(JSON.stringify(_self.temporalNet.weights)); 96 | //_self.fs.writeFileSync("data/train/train-"+_self.symbol+".json", JSON.stringify(_self.temporalNet.toJSON())); 97 | 98 | } 99 | 100 | } 101 | 102 | 103 | console.log("I am reading the stock data for "+_self.symbol); 104 | 105 | var loader = require('csv-load-sync'); 106 | var csv1 = loader("public/data/"+_self.symbol+".csv"); 107 | 108 | // normalizing 109 | for (var i = 0; i < csv1.length; i++) { 110 | var d = csv1[i]; 111 | d["date"] = _self.parseDate(String(d["date"])); 112 | d["close price"] = +d["close price"]; 113 | d["volume"] = +d["volume"]; 114 | 115 | var close = d["close price"]; 116 | 117 | if (_self.min > close) { 118 | _self.min = close; 119 | } 120 | 121 | if (_self.max < close) { 122 | _self.max = close; 123 | } 124 | 125 | _self.data.push(d); 126 | } 127 | 128 | console.log("I am done with reading "+ _self.symbol + " of size " + _self.data.length); 129 | 130 | for (var i = 0; i < _self.data.length; i++) { 131 | if (_self.max - _self.min != 0) 132 | _self.data[i].normalized = (_self.data[i]["close price"] - _self.min)/(_self.max - _self.min); 133 | else 134 | _self.data[i].normalized = 0; 135 | 136 | } 137 | 138 | trainTemporal(); 139 | 140 | } 141 | 142 | module.exports = Stock; 143 | -------------------------------------------------------------------------------- /stock-som.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by karthik on 5/28/15. 3 | */ 4 | 5 | function SOM(options) { 6 | 7 | var _self = this; 8 | 9 | _self.symbols = options.symbols; 10 | 11 | _self.data = []; 12 | 13 | var d3 = _self.d3 = require('d3'); 14 | var SOM = require('ml-som'); 15 | var fs = _self.fs = require('fs'); 16 | 17 | 18 | _self.parseDate = _self.d3.time.format("%Y-%m-%d").parse; 19 | 20 | _self.NETWORK_SIZE = 25; 21 | 22 | if (!_self.symbols) { 23 | return; 24 | } 25 | 26 | console.log("I am in child for " + _self.symbols.toString()); 27 | 28 | _self.som = new SOM(_self.NETWORK_SIZE, _self.NETWORK_SIZE, { 29 | iterations: 10000, 30 | learningRate: 0.3, 31 | method: "random", 32 | gridType: "rect", 33 | fields: _self.symbols.length 34 | }); 35 | 36 | var getSpatialNetworkInput = function () { 37 | 38 | console.log("I am getting training data from " + _self.data.length); 39 | 40 | var trainingData = []; 41 | 42 | var dataSize = _self.data.length; 43 | 44 | for (var i = dataSize - 2; i >= 0; i--) { 45 | 46 | var tempInput = []; 47 | 48 | var past = _self.data[i + 1]; 49 | 50 | var curr = _self.data[i]; 51 | 52 | console.log(curr.toString()); 53 | 54 | for (var j = 0; j < curr.length; j++) { 55 | 56 | var change = (curr[j] - past[j]) * 10 / past[j]; 57 | 58 | if (change > 1) { 59 | 60 | change = 1; 61 | 62 | } else if (change < -1) { 63 | 64 | change = -1; 65 | } 66 | 67 | tempInput.push((1 + change) / 2); 68 | 69 | } 70 | 71 | 72 | trainingData.push(tempInput); 73 | } 74 | 75 | return trainingData; 76 | 77 | } 78 | 79 | 80 | 81 | var trainSpatial = function () { 82 | 83 | console.log("I am training for SOM "); 84 | 85 | var trainingData = getSpatialNetworkInput(); 86 | 87 | console.log(trainingData); 88 | 89 | if (trainingData) { 90 | 91 | //write weights to file 92 | _self.som.train(trainingData); 93 | 94 | //console.log(JSON.stringify(_self.temporalNet.weights)); 95 | _self.fs.writeFileSync("public/data/train/som_weights.json", JSON.stringify(_self.som.export())); 96 | 97 | return 1; 98 | } 99 | 100 | return; 101 | } 102 | 103 | 104 | 105 | _self.csv = {}; 106 | 107 | var loader = require('csv-load-sync'); 108 | 109 | 110 | for (var i = 0; i < _self.symbols.length; i++) { 111 | 112 | console.log("I am reading the stock data for " + _self.symbols[i]); 113 | 114 | //var csv1 = loader("public/data/"+_self.symbols[i]+".csv"); 115 | var csv1 = JSON.parse(fs.readFileSync("public/data/" + _self.symbols[i] + ".json")); 116 | 117 | _self.csv[_self.symbols[i]] = csv1; 118 | } 119 | 120 | var minLength = 100000000; 121 | 122 | for (var i = 0; i < _self.symbols.length; i++) { 123 | if (minLength > _self.csv[_self.symbols[i]].length) { 124 | minLength = _self.csv[_self.symbols[i]].length; 125 | } 126 | } 127 | 128 | for (var i = 0; i < minLength; i++) { 129 | 130 | var priceArray = []; 131 | 132 | for (var j = 0; j < _self.symbols.length; j++) { 133 | 134 | var d = _self.csv[_self.symbols[j]][i]; 135 | 136 | d["Date"] = _self.parseDate(String(d["Date"])); 137 | d["Adj Close"] = +d["Adj Close"]; 138 | d["Volume"] = +d["Volume"]; 139 | 140 | var close = d["Adj Close"]; 141 | priceArray.push(close); 142 | } 143 | 144 | _self.data.push(priceArray); 145 | 146 | } 147 | 148 | console.log("I am done with reading all symbols for SOM"); 149 | 150 | trainSpatial(); 151 | 152 | } 153 | 154 | module.exports = SOM; -------------------------------------------------------------------------------- /stock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by karthik on 1/31/15. 3 | */ 4 | 5 | function Stock (options) { 6 | var _self = this; 7 | 8 | var csv = _self.csv = require('fast-csv'); 9 | var fs = _self.fs = require('fs'); 10 | var d3 = _self.d3 = require('d3'); 11 | var brain = _self.brain = require('brain'); 12 | 13 | _self.companyName = options.company; 14 | _self.symbol = options.symbol; 15 | _self.data = []; 16 | 17 | _self.min = 100000; 18 | _self.max = -100000; 19 | 20 | _self.temporalNet = new _self.brain.NeuralNetwork({ 21 | hiddenLayers: [40, 50, 20], 22 | learningRate: 0.3 // global learning rate, useful when training using streams 23 | }); 24 | 25 | _self.parseDate = _self.d3.time.format("%Y%m%d").parse; 26 | _self.TEMPORAL_INPUT_SIZE = 12; 27 | _self.TEMPORAL_OUTPUT_SIZE = 1; 28 | 29 | 30 | //_self.readStock(); 31 | } 32 | 33 | Stock.prototype.readStock = function () { 34 | 35 | var _self = this; 36 | 37 | var stream = _self.fs.createReadStream("data/"+_self.symbol+".csv"); 38 | 39 | var csvStream = _self.csv 40 | .fromStream(stream, {headers : true}) 41 | .on("data", function(d){ 42 | 43 | d["date"] = _self.parseDate(String(d["date"])); 44 | d["close price"] = +d["close price"]; 45 | d["volume"] = +d["volume"]; 46 | 47 | var close = d["close price"]; 48 | 49 | if (_self.min > close) { 50 | _self.min = close; 51 | } 52 | 53 | if (_self.max < close) { 54 | _self.max = close; 55 | } 56 | 57 | _self.data.push(d); 58 | 59 | 60 | }) 61 | .on("end", function(){ 62 | 63 | console.log("done: "+_self.symbol+", size: "+_self.data.length); 64 | 65 | for (var i = 0; i < _self.data.length; i++) { 66 | if (_self.max - _self.min != 0) 67 | _self.data[i].normalized = (_self.data[i]["close price"] - _self.min)/(_self.max - _self.min); 68 | else 69 | _self.data[i].normalized = 0; 70 | 71 | //console.log(_self.data[i].normalized); 72 | } 73 | 74 | _self.trainTemporal(); 75 | }); 76 | 77 | } 78 | 79 | Stock.prototype.getTemporalNetworkInput = function () { 80 | 81 | //PAST SIX DAYS 82 | 83 | var _self = this; 84 | var trainingData = []; 85 | 86 | var dataSize = _self.data.length; 87 | 88 | if (dataSize < _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE) { 89 | return; 90 | } 91 | 92 | for (var i = dataSize - 1; i >= _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE - 1; i--) { 93 | var tempInput = []; 94 | var tempOutput = []; 95 | 96 | for (var j = 0; j < _self.TEMPORAL_OUTPUT_SIZE; j++) { 97 | var d = _self.data[i-j]; 98 | tempOutput.push(d["normalized"]); 99 | } 100 | 101 | for (var j = _self.TEMPORAL_OUTPUT_SIZE; j < _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE; j++) { 102 | var d = _self.data[i-j]; 103 | tempInput.push(d["normalized"]); 104 | } 105 | 106 | trainingData.push({ 107 | input: tempInput, 108 | output: tempOutput 109 | }) 110 | } 111 | 112 | return trainingData; 113 | } 114 | 115 | Stock.prototype.trainTemporal = function () { 116 | 117 | var _self = this; 118 | 119 | var trainingData = _self.getTemporalNetworkInput(); 120 | 121 | //console.log(trainingData); 122 | 123 | if (trainingData) { 124 | 125 | var info = _self.temporalNet.train(trainingData, { 126 | errorThresh: 0.005, // error threshold to reach 127 | iterations: 40000, // maximum training iterations 128 | log: false, // console.log() progress periodically 129 | logPeriod: 10 // number of iterations between logging 130 | }); 131 | 132 | console.log("training "+_self.companyName+": "+JSON.stringify(info)); 133 | 134 | if (info.error == "null") { 135 | console.log(trainingData); 136 | } 137 | 138 | //write weights to file 139 | 140 | //console.log(JSON.stringify(_self.temporalNet.weights)); 141 | _self.fs.writeFile("data/train/train-"+_self.symbol+".json", JSON.stringify(_self.temporalNet.toJSON()), function(err) { 142 | if(err) { 143 | console.log(err); 144 | } else { 145 | console.log("The file "+"data/train/t"+_self.symbol+".json"+" was saved!"); 146 | } 147 | }); 148 | 149 | return JSON.stringify(info); 150 | } 151 | 152 | return; 153 | 154 | } 155 | 156 | module.exports = Stock; 157 | -------------------------------------------------------------------------------- /stock2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by karthik on 1/31/15. 3 | */ 4 | 5 | function Stock(options) { 6 | var _self = this; 7 | 8 | var csv = _self.csv = require('fast-csv'); 9 | var fs = _self.fs = require('fs'); 10 | var d3 = _self.d3 = require('d3'); 11 | var brain = _self.brain = require('brain'); 12 | 13 | _self.companyName = options.company; 14 | _self.symbol = options.symbol; 15 | _self.data = []; 16 | 17 | _self.min = 100000; 18 | _self.max = -100000; 19 | 20 | _self.temporalNet = new _self.brain.NeuralNetwork({ 21 | hiddenLayers: [50, 60, 70], 22 | learningRate: 0.3 // global learning rate, useful when training using streams 23 | }); 24 | 25 | _self.parseDate = _self.d3.time.format("%Y-%m-%d").parse; 26 | 27 | _self.TEMPORAL_INPUT_SIZE = 6; 28 | _self.TEMPORAL_OUTPUT_SIZE = 1; 29 | 30 | console.log("I am in child for " + _self.symbol); 31 | 32 | var getTemporalNetworkInput = function () { 33 | console.log("I am getting training data for " + _self.symbol); 34 | 35 | var trainingData = []; 36 | 37 | var dataSize = _self.data.length; 38 | 39 | if (dataSize < _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE) { 40 | return; 41 | } 42 | 43 | for (var i = dataSize - 2; i >= _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE - 1; i--) { 44 | var tempInput = []; 45 | var tempOutput = []; 46 | 47 | var past = _self.data[i + 1]; 48 | 49 | for (var j = 0; j < _self.TEMPORAL_INPUT_SIZE; j++) { 50 | var c = _self.data[i - j]; 51 | var change = (c["Adj Close"] - past["Adj Close"]) * 10 / past["Adj Close"]; 52 | if (change > 1) { 53 | 54 | change = 1; 55 | 56 | } else if (change < -1) { 57 | 58 | change = -1; 59 | } 60 | 61 | tempInput.push((1 + change) / 2); 62 | } 63 | 64 | //var past = _self.data[i-j+1]; 65 | 66 | for (var j = _self.TEMPORAL_INPUT_SIZE; j < _self.TEMPORAL_INPUT_SIZE + _self.TEMPORAL_OUTPUT_SIZE; j++) { 67 | 68 | var c = _self.data[i - j]; 69 | var change = (c["Adj Close"] - past["Adj Close"]) * 10 / past["Adj Close"]; 70 | 71 | if (change > 1) { 72 | 73 | change = 1; 74 | 75 | } else if (change < -1) { 76 | 77 | change = -1; 78 | } 79 | 80 | tempOutput.push((1 + change) / 2); 81 | } 82 | 83 | trainingData.push({ 84 | input: tempInput, 85 | output: tempOutput 86 | }) 87 | } 88 | 89 | return trainingData; 90 | } 91 | 92 | 93 | 94 | var trainTemporal = function () { 95 | console.log("I am training for " + _self.symbol); 96 | 97 | var trainingData = getTemporalNetworkInput(); 98 | 99 | if (trainingData) { 100 | 101 | var info = _self.temporalNet.train(trainingData, { 102 | errorThresh: 0.0005, // error threshold to reach 103 | iterations: 10000, // maximum training iterations 104 | log: true, // console.log() progress periodically 105 | logPeriod: 10 // number of iterations between logging 106 | }); 107 | 108 | console.log("training " + _self.companyName + ": " + JSON.stringify(info)); 109 | 110 | if (info.error == "null") { 111 | console.log(trainingData); 112 | } 113 | 114 | //write weights to file 115 | 116 | //console.log(JSON.stringify(_self.temporalNet.weights)); 117 | _self.fs.writeFileSync("public/data/train/train-" + _self.symbol + ".json", JSON.stringify(_self.temporalNet.toJSON())); 118 | 119 | return JSON.stringify(info); 120 | } 121 | 122 | return; 123 | } 124 | 125 | 126 | console.log("I am reading the stock data for " + _self.symbol); 127 | 128 | var loader = require('csv-load-sync'); 129 | //var csv1 = loader("public/data/"+_self.symbol+".csv"); 130 | var csv1 = JSON.parse(fs.readFileSync("public/data/" + _self.symbol + ".json")); 131 | 132 | for (var i = 0; i < csv1.length; i++) { 133 | var d = csv1[i]; 134 | d["Date"] = _self.parseDate(String(d["Date"])); 135 | d["Adj Close"] = +d["Adj Close"]; 136 | d["Volume"] = +d["Volume"]; 137 | 138 | var close = d["Adj Close"]; 139 | 140 | if (_self.min > close) { 141 | _self.min = close; 142 | } 143 | 144 | if (_self.max < close) { 145 | _self.max = close; 146 | } 147 | 148 | _self.data.push(d); 149 | } 150 | 151 | console.log("I am done with reading " + _self.symbol + " of size " + _self.data.length); 152 | 153 | 154 | trainTemporal(); 155 | 156 | } 157 | 158 | module.exports = Stock; -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TimeFork 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 | 35 |
36 | 37 | 40 | 41 |
42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 |
50 | 51 | 52 |
53 | 54 | 66 |
67 |
68 | 69 |
70 |
71 | 72 |
73 | 74 |
75 |
76 | 77 | 78 | -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content --------------------------------------------------------------------------------