├── chapter07 ├── .gitignore ├── Procfile ├── Makefile ├── public │ ├── stylesheets │ │ └── nockmarket.css │ ├── js │ │ ├── account.js │ │ ├── nockclient.js │ │ ├── chat.js │ │ ├── portfolio.js │ │ └── trades.js │ └── templates │ │ └── trade-table.ejs ├── dnode │ ├── auth.js │ └── web.js ├── package.json ├── priceFeed.js ├── views │ ├── 404.ejs │ ├── chart.ejs │ ├── index.ejs │ └── portfolio.ejs ├── test │ ├── exchange.test.js │ └── db.test.js ├── lib │ ├── db.js │ ├── exchange.js │ ├── BinaryHeap.js │ └── nocklib.js ├── routes │ └── nockroutes.js └── nockmarket.js ├── .gitignore ├── chapter01 └── authentication │ ├── views │ ├── index.jade │ └── layout.jade │ ├── routes │ └── index.js │ ├── public │ └── stylesheets │ │ └── style.css │ ├── package.json │ ├── form.html │ ├── lib │ └── db.js │ ├── models │ └── User.js │ └── app.js ├── chapter02 ├── Makefile ├── package.json ├── priceFeed.js ├── nockmarket.js ├── test │ └── exchange.test.js └── lib │ ├── nocklib.js │ ├── exchange.js │ └── BinaryHeap.js ├── chapter03 ├── Makefile ├── package.json ├── dnode │ ├── auth.js │ └── web.js ├── priceFeed.js ├── test │ ├── exchange.test.js │ └── db.test.js ├── lib │ ├── db.js │ ├── nocklib.js │ ├── exchange.js │ └── BinaryHeap.js ├── views │ └── chart.ejs └── nockmarket.js ├── chapter04 ├── Makefile ├── dnode │ ├── auth.js │ └── web.js ├── package.json ├── public │ └── js │ │ ├── portfolio.js │ │ └── nockclient.js ├── priceFeed.js ├── test │ ├── exchange.test.js │ └── db.test.js ├── lib │ ├── db.js │ ├── exchange.js │ ├── nocklib.js │ └── BinaryHeap.js ├── routes │ └── nockroutes.js ├── views │ ├── chart.ejs │ ├── portfolio.ejs │ └── index.ejs └── nockmarket.js ├── chapter05 ├── Makefile ├── public │ └── js │ │ ├── trades.js │ │ ├── portfolio.js │ │ ├── account.js │ │ ├── nockclient.js │ │ └── chat.js ├── dnode │ ├── auth.js │ └── web.js ├── package.json ├── priceFeed.js ├── test │ ├── exchange.test.js │ └── db.test.js ├── lib │ ├── db.js │ ├── exchange.js │ ├── BinaryHeap.js │ └── nocklib.js ├── routes │ └── nockroutes.js ├── views │ ├── chart.ejs │ ├── portfolio.ejs │ └── index.ejs └── nockmarket.js └── chapter06 ├── Makefile ├── public ├── stylesheets │ └── nockmarket.css ├── js │ ├── account.js │ ├── nockclient.js │ ├── chat.js │ ├── portfolio.js │ └── trades.js └── templates │ └── trade-table.ejs ├── dnode ├── auth.js └── web.js ├── package.json ├── priceFeed.js ├── test ├── exchange.test.js └── db.test.js ├── lib ├── db.js ├── exchange.js ├── BinaryHeap.js └── nocklib.js ├── routes └── nockroutes.js ├── views ├── chart.ejs ├── index.ejs └── portfolio.ejs └── nockmarket.js /chapter07/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /chapter07/Procfile: -------------------------------------------------------------------------------- 1 | web: node nockmarket.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | $cat .gitignore 2 | .DS_Store 3 | .svn/ 4 | node_modules 5 | -------------------------------------------------------------------------------- /chapter01/authentication/views/index.jade: -------------------------------------------------------------------------------- 1 | h1= title 2 | p Welcome to #{title} -------------------------------------------------------------------------------- /chapter02/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha -u tdd 3 | .PHONY: test -------------------------------------------------------------------------------- /chapter03/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha -u tdd 3 | .PHONY: test -------------------------------------------------------------------------------- /chapter04/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha -u tdd 3 | .PHONY: test -------------------------------------------------------------------------------- /chapter05/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha -u tdd 3 | .PHONY: test -------------------------------------------------------------------------------- /chapter06/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha -u tdd 3 | .PHONY: test -------------------------------------------------------------------------------- /chapter06/public/stylesheets/nockmarket.css: -------------------------------------------------------------------------------- 1 | .trade-button { 2 | width: 24px; 3 | } -------------------------------------------------------------------------------- /chapter07/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha -u tdd 3 | .PHONY: test -------------------------------------------------------------------------------- /chapter07/public/stylesheets/nockmarket.css: -------------------------------------------------------------------------------- 1 | .trade-button { 2 | width: 24px; 3 | } -------------------------------------------------------------------------------- /chapter05/public/js/trades.js: -------------------------------------------------------------------------------- 1 | socket.on('trade', function (data) { 2 | console.log(data); 3 | }); -------------------------------------------------------------------------------- /chapter01/authentication/views/layout.jade: -------------------------------------------------------------------------------- 1 | !!! 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body!= body -------------------------------------------------------------------------------- /chapter01/authentication/routes/index.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * GET home page. 4 | */ 5 | 6 | exports.index = function(req, res){ 7 | res.render('index', { title: 'Express' }) 8 | }; -------------------------------------------------------------------------------- /chapter01/authentication/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 | } -------------------------------------------------------------------------------- /chapter02/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nockmarket" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "jquery" : "1.7.3" 7 | , "mocha": "1.3.0" 8 | , "should": "1.0.0" 9 | } 10 | } -------------------------------------------------------------------------------- /chapter01/authentication/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "authentication" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "express": "2.5.8" 7 | , "jade": "0.26.1" 8 | , "mongoose": "2.6.5" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chapter03/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nockmarket" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "jquery" : "1.7.3" 7 | , "dnode" : "1.0.0" 8 | , "mocha": "1.3.0" 9 | , "should": "1.0.0" 10 | , "ejs" : "0.7.1" 11 | , "express": "2.5.8" 12 | , "mongodb": "1.0.2" 13 | } 14 | } -------------------------------------------------------------------------------- /chapter03/dnode/auth.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode'); 2 | dnode(function(remote, conn) { 3 | this.auth = function(user, pass, cb) { 4 | var users = { 5 | foo: 'bar' 6 | }; 7 | var p = users[user]; 8 | if (p === pass) cb(null, 'AUTHORIZED!'); 9 | else cb('REMOTE ACCESS DENIED'); 10 | }; 11 | }).listen(process.argv[2]); -------------------------------------------------------------------------------- /chapter04/dnode/auth.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode'); 2 | dnode(function(remote, conn) { 3 | this.auth = function(user, pass, cb) { 4 | var users = { 5 | foo: 'bar' 6 | }; 7 | var p = users[user]; 8 | if (p === pass) cb(null, 'AUTHORIZED!'); 9 | else cb('REMOTE ACCESS DENIED'); 10 | }; 11 | }).listen(process.argv[2]); -------------------------------------------------------------------------------- /chapter04/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nockmarket" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "jquery" : "1.7.3" 7 | , "dnode" : "1.0.0" 8 | , "mocha": "1.3.0" 9 | , "should": "1.0.0" 10 | , "ejs" : "0.7.1" 11 | , "express": "2.5.8" 12 | , "mongodb": "1.0.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter04/public/js/portfolio.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add-stock').click(function(e) { 3 | $.ajax({type: 'POST', url: '/add-stock/', data: {stock: $('#stock').val()} 4 | }).done(function(price) { 5 | $('.stock-list').append('' + $('#stock').val() + '' + price + ''); 6 | }); 7 | }); 8 | }); -------------------------------------------------------------------------------- /chapter05/dnode/auth.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode'); 2 | dnode(function(remote, conn) { 3 | this.auth = function(user, pass, cb) { 4 | var users = { 5 | foo: 'bar' 6 | }; 7 | var p = users[user]; 8 | if (p === pass) cb(null, 'AUTHORIZED!'); 9 | else cb('REMOTE ACCESS DENIED'); 10 | }; 11 | }).listen(process.argv[2]); -------------------------------------------------------------------------------- /chapter05/public/js/portfolio.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add-stock').click(function(e) { 3 | $.ajax({type: 'POST', url: '/add-stock/', data: {stock: $('#stock').val()} 4 | }).done(function(price) { 5 | $('.stock-list').append('' + $('#stock').val() + '' + price + ''); 6 | }); 7 | }); 8 | }); -------------------------------------------------------------------------------- /chapter06/dnode/auth.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode'); 2 | dnode(function(remote, conn) { 3 | this.auth = function(user, pass, cb) { 4 | var users = { 5 | foo: 'bar' 6 | }; 7 | var p = users[user]; 8 | if (p === pass) cb(null, 'AUTHORIZED!'); 9 | else cb('REMOTE ACCESS DENIED'); 10 | }; 11 | }).listen(process.argv[2]); -------------------------------------------------------------------------------- /chapter07/dnode/auth.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode'); 2 | dnode(function(remote, conn) { 3 | this.auth = function(user, pass, cb) { 4 | var users = { 5 | foo: 'bar' 6 | }; 7 | var p = users[user]; 8 | if (p === pass) cb(null, 'AUTHORIZED!'); 9 | else cb('REMOTE ACCESS DENIED'); 10 | }; 11 | }).listen(process.argv[2]); -------------------------------------------------------------------------------- /chapter01/authentication/form.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /chapter05/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nockmarket" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "jquery" : "1.7.3" 7 | , "cookie": "0.0.4" 8 | , "dnode" : "1.0.0" 9 | , "mocha": "1.3.0" 10 | , "should": "1.0.0" 11 | , "socket.io": "0.9.10" 12 | , "ejs" : "0.7.1" 13 | , "express": "2.5.8" 14 | , "mongodb": "1.0.2" 15 | } 16 | } -------------------------------------------------------------------------------- /chapter06/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nockmarket" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "jquery" : "1.7.3" 7 | , "cookie": "0.0.4" 8 | , "dnode" : "1.0.0" 9 | , "mocha": "1.3.0" 10 | , "should": "1.0.0" 11 | , "socket.io": "0.9.10" 12 | , "ejs" : "0.7.1" 13 | , "express": "2.5.8" 14 | , "mongodb": "1.0.2" 15 | } 16 | } -------------------------------------------------------------------------------- /chapter05/public/js/account.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#update-account').click(function() { 3 | socket.emit('updateAccount', {email: $('#email').val()}); 4 | }); 5 | }); 6 | socket.on('updateSuccess', function (data) { 7 | var html = "
Email updated!
"; 8 | $(html).hide().appendTo('h3').fadeIn("fast").delay("2000").fadeOut("fast"); 9 | }); -------------------------------------------------------------------------------- /chapter06/public/js/account.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#update-account').click(function() { 3 | socket.emit('updateAccount', {email: $('#email').val()}); 4 | }); 5 | }); 6 | socket.on('updateSuccess', function (data) { 7 | var html = "
Email updated!
"; 8 | $(html).hide().appendTo('h3').fadeIn("fast").delay("2000").fadeOut("fast"); 9 | }); -------------------------------------------------------------------------------- /chapter07/public/js/account.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#update-account').click(function() { 3 | socket.emit('updateAccount', {email: $('#email').val()}); 4 | }); 5 | }); 6 | socket.on('updateSuccess', function (data) { 7 | var html = "
Email updated!
"; 8 | $(html).hide().appendTo('h3').fadeIn("fast").delay("2000").fadeOut("fast"); 9 | }); -------------------------------------------------------------------------------- /chapter07/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nockmarket" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "engines": { 6 | "node": "0.8.8", 7 | "npm": "1.1.49" 8 | } 9 | , "dependencies": { 10 | "cookie": "0.0.4" 11 | , "dnode" : "1.0.0" 12 | , "ejs": "0.8.3" 13 | , "express": "2.5.9" 14 | , "jquery" : "1.7.2" 15 | , "mocha": "1.0.1" 16 | , "mongodb": "1.1.7" 17 | , "should": "0.6.1" 18 | , "socket.io": "0.9.10" 19 | , "underscore": "1.3.3" 20 | } 21 | } -------------------------------------------------------------------------------- /chapter03/dnode/web.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode') 2 | , http = require('http') 3 | , qs = require('querystring'); 4 | 5 | var d = dnode.connect('localhost', 8090); 6 | d.on('remote', function (remote) { 7 | http.createServer(function(req, res) { 8 | if (req.url.match(/^\/login/)) { 9 | var param = qs.parse(req.url.split('?')[1]); 10 | remote.auth(param.user, param.pass, function (err, result) { 11 | res.end(err ? err: result); 12 | }); 13 | } 14 | }).listen(process.argv[2]); 15 | }); -------------------------------------------------------------------------------- /chapter04/dnode/web.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode') 2 | , http = require('http') 3 | , qs = require('querystring'); 4 | 5 | var d = dnode.connect('localhost', 8090); 6 | d.on('remote', function (remote) { 7 | http.createServer(function(req, res) { 8 | if (req.url.match(/^\/login/)) { 9 | var param = qs.parse(req.url.split('?')[1]); 10 | remote.auth(param.user, param.pass, function (err, result) { 11 | res.end(err ? err: result); 12 | }); 13 | } 14 | }).listen(process.argv[2]); 15 | }); -------------------------------------------------------------------------------- /chapter05/dnode/web.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode') 2 | , http = require('http') 3 | , qs = require('querystring'); 4 | 5 | var d = dnode.connect('localhost', 8090); 6 | d.on('remote', function (remote) { 7 | http.createServer(function(req, res) { 8 | if (req.url.match(/^\/login/)) { 9 | var param = qs.parse(req.url.split('?')[1]); 10 | remote.auth(param.user, param.pass, function (err, result) { 11 | res.end(err ? err: result); 12 | }); 13 | } 14 | }).listen(process.argv[2]); 15 | }); -------------------------------------------------------------------------------- /chapter06/dnode/web.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode') 2 | , http = require('http') 3 | , qs = require('querystring'); 4 | 5 | var d = dnode.connect('localhost', 8090); 6 | d.on('remote', function (remote) { 7 | http.createServer(function(req, res) { 8 | if (req.url.match(/^\/login/)) { 9 | var param = qs.parse(req.url.split('?')[1]); 10 | remote.auth(param.user, param.pass, function (err, result) { 11 | res.end(err ? err: result); 12 | }); 13 | } 14 | }).listen(process.argv[2]); 15 | }); -------------------------------------------------------------------------------- /chapter07/dnode/web.js: -------------------------------------------------------------------------------- 1 | var dnode = require('dnode') 2 | , http = require('http') 3 | , qs = require('querystring'); 4 | 5 | var d = dnode.connect('localhost', 8090); 6 | d.on('remote', function (remote) { 7 | http.createServer(function(req, res) { 8 | if (req.url.match(/^\/login/)) { 9 | var param = qs.parse(req.url.split('?')[1]); 10 | remote.auth(param.user, param.pass, function (err, result) { 11 | res.end(err ? err: result); 12 | }); 13 | } 14 | }).listen(process.argv[2]); 15 | }); -------------------------------------------------------------------------------- /chapter02/priceFeed.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var options = { 3 | host: 'download.finance.yahoo.com', 4 | port: 80, 5 | path: '/d/quotes.csv?s=AAPL,FB,GOOG,MSFT&f=sl1c1d1&e=.csv' 6 | }; 7 | 8 | http.get(options, function(res) { 9 | var data = ''; 10 | res.on('data', function(chunk) { 11 | data += chunk.toString(); 12 | }) 13 | .on('error', function(err) { 14 | console.err('Error retrieving Yahoo stock prices'); 15 | throw err; 16 | }) 17 | .on('end', function() { 18 | console.log(data); 19 | }); 20 | }); -------------------------------------------------------------------------------- /chapter03/priceFeed.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var options = { 3 | host: 'download.finance.yahoo.com', 4 | port: 80, 5 | path: '/d/quotes.csv?s=AAPL,FB,GOOG,MSFT&f=sl1c1d1&e=.csv' 6 | }; 7 | 8 | http.get(options, function(res) { 9 | var data = ''; 10 | res.on('data', function(chunk) { 11 | data += chunk.toString(); 12 | }) 13 | .on('error', function(err) { 14 | console.err('Error retrieving Yahoo stock prices'); 15 | throw err; 16 | }) 17 | .on('end', function() { 18 | console.log(data); 19 | }); 20 | }); -------------------------------------------------------------------------------- /chapter04/priceFeed.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var options = { 3 | host: 'download.finance.yahoo.com', 4 | port: 80, 5 | path: '/d/quotes.csv?s=AAPL,FB,GOOG,MSFT&f=sl1c1d1&e=.csv' 6 | }; 7 | 8 | http.get(options, function(res) { 9 | var data = ''; 10 | res.on('data', function(chunk) { 11 | data += chunk.toString(); 12 | }) 13 | .on('error', function(err) { 14 | console.err('Error retrieving Yahoo stock prices'); 15 | throw err; 16 | }) 17 | .on('end', function() { 18 | console.log(data); 19 | }); 20 | }); -------------------------------------------------------------------------------- /chapter05/priceFeed.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var options = { 3 | host: 'download.finance.yahoo.com', 4 | port: 80, 5 | path: '/d/quotes.csv?s=AAPL,FB,GOOG,MSFT&f=sl1c1d1&e=.csv' 6 | }; 7 | 8 | http.get(options, function(res) { 9 | var data = ''; 10 | res.on('data', function(chunk) { 11 | data += chunk.toString(); 12 | }) 13 | .on('error', function(err) { 14 | console.err('Error retrieving Yahoo stock prices'); 15 | throw err; 16 | }) 17 | .on('end', function() { 18 | console.log(data); 19 | }); 20 | }); -------------------------------------------------------------------------------- /chapter06/priceFeed.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var options = { 3 | host: 'download.finance.yahoo.com', 4 | port: 80, 5 | path: '/d/quotes.csv?s=AAPL,FB,GOOG,MSFT&f=sl1c1d1&e=.csv' 6 | }; 7 | 8 | http.get(options, function(res) { 9 | var data = ''; 10 | res.on('data', function(chunk) { 11 | data += chunk.toString(); 12 | }) 13 | .on('error', function(err) { 14 | console.err('Error retrieving Yahoo stock prices'); 15 | throw err; 16 | }) 17 | .on('end', function() { 18 | console.log(data); 19 | }); 20 | }); -------------------------------------------------------------------------------- /chapter07/priceFeed.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var options = { 3 | host: 'download.finance.yahoo.com', 4 | port: 80, 5 | path: '/d/quotes.csv?s=AAPL,FB,GOOG,MSFT&f=sl1c1d1&e=.csv' 6 | }; 7 | 8 | http.get(options, function(res) { 9 | var data = ''; 10 | res.on('data', function(chunk) { 11 | data += chunk.toString(); 12 | }) 13 | .on('error', function(err) { 14 | console.err('Error retrieving Yahoo stock prices'); 15 | throw err; 16 | }) 17 | .on('end', function() { 18 | console.log(data); 19 | }); 20 | }); -------------------------------------------------------------------------------- /chapter01/authentication/lib/db.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | 4 | module.exports.mongoose = mongoose; 5 | module.exports.Schema = Schema; 6 | 7 | // Connect to cloud database 8 | var username = "user"; 9 | var password = "password"; 10 | var address = '@dbh42.mongolab.com:27427/nockmarket'; 11 | connect(); 12 | 13 | // Connect to mongo 14 | function connect() { 15 | var url = 'mongodb://' + username + ':' + password + address; 16 | mongoose.connect(url); 17 | } 18 | 19 | function disconnect() {mongoose.disconnect()} 20 | -------------------------------------------------------------------------------- /chapter07/views/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Whoops

13 | 14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /chapter01/authentication/models/User.js: -------------------------------------------------------------------------------- 1 | var db = require('../lib/db'); 2 | 3 | var UserSchema = new db.Schema({ 4 | username : {type: String, unique: true} 5 | , password : String 6 | }) 7 | 8 | var MyUser = db.mongoose.model('User', UserSchema); 9 | 10 | // Exports 11 | module.exports.addUser = addUser; 12 | 13 | // Add user to database 14 | function addUser(username, password, callback) { 15 | var instance = new MyUser(); 16 | instance.username = username; 17 | instance.password = password; 18 | instance.save(function (err) { 19 | if (err) { 20 | callback(err); 21 | } 22 | else { 23 | callback(null, instance); 24 | } 25 | }); 26 | } -------------------------------------------------------------------------------- /chapter04/public/js/nockclient.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('.uname').blur(function(e) { 3 | $.ajax({type: 'GET', url: '/api/user/' + $('.uname').val() 4 | }).done(function(found) { 5 | if (found == '1') { 6 | $('#imagePlaceHolder').html('cross Username already taken'); 7 | $('.create-button').addClass('disabled').attr('disabled', true); 8 | } 9 | else { 10 | $('#imagePlaceHolder').html('tick'); 11 | $('.create-button').removeClass('disabled').attr('disabled', false); 12 | } 13 | }); 14 | }); 15 | }); -------------------------------------------------------------------------------- /chapter05/public/js/nockclient.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('.uname').blur(function(e) { 3 | $.ajax({type: 'GET', url: '/api/user/' + $('.uname').val() 4 | }).done(function(found) { 5 | if (found == '1') { 6 | $('#imagePlaceHolder').html('cross Username already taken'); 7 | $('.create-button').addClass('disabled').attr('disabled', true); 8 | } 9 | else { 10 | $('#imagePlaceHolder').html('tick'); 11 | $('.create-button').removeClass('disabled').attr('disabled', false); 12 | } 13 | }); 14 | }); 15 | }); -------------------------------------------------------------------------------- /chapter06/public/js/nockclient.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('.uname').blur(function(e) { 3 | $.ajax({type: 'GET', url: '/api/user/' + $('.uname').val() 4 | }).done(function(found) { 5 | if (found == '1') { 6 | $('#imagePlaceHolder').html('cross Username already taken'); 7 | $('.create-button').addClass('disabled').attr('disabled', true); 8 | } 9 | else { 10 | $('#imagePlaceHolder').html('tick'); 11 | $('.create-button').removeClass('disabled').attr('disabled', false); 12 | } 13 | }); 14 | }); 15 | }); -------------------------------------------------------------------------------- /chapter07/public/js/nockclient.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('.uname').blur(function(e) { 3 | $.ajax({type: 'GET', url: '/api/user/' + $('.uname').val() 4 | }).done(function(found) { 5 | if (found == '1') { 6 | $('#imagePlaceHolder').html('cross Username already taken'); 7 | $('.create-button').addClass('disabled').attr('disabled', true); 8 | } 9 | else { 10 | $('#imagePlaceHolder').html('tick'); 11 | $('.create-button').removeClass('disabled').attr('disabled', false); 12 | } 13 | }); 14 | }); 15 | }); -------------------------------------------------------------------------------- /chapter02/nockmarket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchangeData = {} 4 | , exch = require('./lib/exchange') 5 | , nocklib = require('./lib/nocklib') 6 | , timeFloor = 500 7 | , timeRange = 1000; 8 | 9 | function submitRandomOrder() { 10 | // order 11 | var ord = nocklib.generateRandomOrder(exchangeData); 12 | console.log('order', ord); 13 | if (ord.type == exch.BUY) 14 | exchangeData = exch.buy(ord.price, ord.volume, exchangeData); 15 | else 16 | exchangeData = exch.sell(ord.price, ord.volume, exchangeData); 17 | 18 | var pause = Math.floor(Math.random() * timeRange) + timeFloor; 19 | setTimeout(submitRandomOrder, pause); 20 | console.log(exch.getDisplay(exchangeData)); 21 | } 22 | 23 | submitRandomOrder(); -------------------------------------------------------------------------------- /chapter02/test/exchange.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , should = require('should') 5 | , exchange = require('../lib/exchange'); 6 | 7 | var exchangeData = {}; 8 | 9 | suite('exchange', function() { 10 | test('buy should add a BUY nockmarket order', function(done) { 11 | exchangeData = exchange.buy(40, 100, exchangeData); 12 | exchangeData.buys.volumes[40].should.eql(100); 13 | done(); 14 | }); 15 | 16 | test('sell should add a SELL nockmarket order', function(done) { 17 | exchangeData = exchange.sell(41, 200, exchangeData); 18 | exchangeData.sells.volumes['41'].should.eql(200); 19 | done(); 20 | }); 21 | 22 | test('sell should produce trades', function(done) { 23 | exchangeData = exchange.sell(40, 75, exchangeData); 24 | exchangeData.trades[0].price.should.eql(40); 25 | exchangeData.trades[0].volume.should.eql(75); 26 | exchangeData.buys.volumes[40].should.eql(25); 27 | exchangeData.sells.volumes[41].should.eql(200); 28 | done(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /chapter03/test/exchange.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , should = require('should') 5 | , exchange = require('../lib/exchange'); 6 | 7 | var exchangeData = {}; 8 | 9 | suite('exchange', function() { 10 | test('buy should add a BUY nockmarket order', function(done) { 11 | exchangeData = exchange.buy(40, 100, exchangeData); 12 | exchangeData.buys.volumes[40].should.eql(100); 13 | done(); 14 | }); 15 | 16 | test('sell should add a SELL nockmarket order', function(done) { 17 | exchangeData = exchange.sell(41, 200, exchangeData); 18 | exchangeData.sells.volumes['41'].should.eql(200); 19 | done(); 20 | }); 21 | 22 | test('sell should produce trades', function(done) { 23 | exchangeData = exchange.sell(40, 75, exchangeData); 24 | exchangeData.trades[0].price.should.eql(40); 25 | exchangeData.trades[0].volume.should.eql(75); 26 | exchangeData.buys.volumes[40].should.eql(25); 27 | exchangeData.sells.volumes[41].should.eql(200); 28 | done(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /chapter04/test/exchange.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , should = require('should') 5 | , exchange = require('../lib/exchange'); 6 | 7 | var exchangeData = {}; 8 | 9 | suite('exchange', function() { 10 | test('buy should add a BUY nockmarket order', function(done) { 11 | exchangeData = exchange.buy(40, 100, exchangeData); 12 | exchangeData.buys.volumes[40].should.eql(100); 13 | done(); 14 | }); 15 | 16 | test('sell should add a SELL nockmarket order', function(done) { 17 | exchangeData = exchange.sell(41, 200, exchangeData); 18 | exchangeData.sells.volumes['41'].should.eql(200); 19 | done(); 20 | }); 21 | 22 | test('sell should produce trades', function(done) { 23 | exchangeData = exchange.sell(40, 75, exchangeData); 24 | exchangeData.trades[0].price.should.eql(40); 25 | exchangeData.trades[0].volume.should.eql(75); 26 | exchangeData.buys.volumes[40].should.eql(25); 27 | exchangeData.sells.volumes[41].should.eql(200); 28 | done(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /chapter05/test/exchange.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , should = require('should') 5 | , exchange = require('../lib/exchange'); 6 | 7 | var exchangeData = {}; 8 | 9 | suite('exchange', function() { 10 | test('buy should add a BUY nockmarket order', function(done) { 11 | exchangeData = exchange.buy(40, 100, exchangeData); 12 | exchangeData.buys.volumes[40].should.eql(100); 13 | done(); 14 | }); 15 | 16 | test('sell should add a SELL nockmarket order', function(done) { 17 | exchangeData = exchange.sell(41, 200, exchangeData); 18 | exchangeData.sells.volumes['41'].should.eql(200); 19 | done(); 20 | }); 21 | 22 | test('sell should produce trades', function(done) { 23 | exchangeData = exchange.sell(40, 75, exchangeData); 24 | exchangeData.trades[0].price.should.eql(40); 25 | exchangeData.trades[0].volume.should.eql(75); 26 | exchangeData.buys.volumes[40].should.eql(25); 27 | exchangeData.sells.volumes[41].should.eql(200); 28 | done(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /chapter06/test/exchange.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , should = require('should') 5 | , exchange = require('../lib/exchange'); 6 | 7 | var exchangeData = {}; 8 | 9 | suite('exchange', function() { 10 | test('buy should add a BUY nockmarket order', function(done) { 11 | exchangeData = exchange.buy(40, 100, exchangeData); 12 | exchangeData.buys.volumes[40].should.eql(100); 13 | done(); 14 | }); 15 | 16 | test('sell should add a SELL nockmarket order', function(done) { 17 | exchangeData = exchange.sell(41, 200, exchangeData); 18 | exchangeData.sells.volumes['41'].should.eql(200); 19 | done(); 20 | }); 21 | 22 | test('sell should produce trades', function(done) { 23 | exchangeData = exchange.sell(40, 75, exchangeData); 24 | exchangeData.trades[0].price.should.eql(40); 25 | exchangeData.trades[0].volume.should.eql(75); 26 | exchangeData.buys.volumes[40].should.eql(25); 27 | exchangeData.sells.volumes[41].should.eql(200); 28 | done(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /chapter07/test/exchange.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , should = require('should') 5 | , exchange = require('../lib/exchange'); 6 | 7 | var exchangeData = {}; 8 | 9 | suite('exchange', function() { 10 | test('buy should add a BUY nockmarket order', function(done) { 11 | exchangeData = exchange.buy(40, 100, exchangeData); 12 | exchangeData.buys.volumes[40].should.eql(100); 13 | done(); 14 | }); 15 | 16 | test('sell should add a SELL nockmarket order', function(done) { 17 | exchangeData = exchange.sell(41, 200, exchangeData); 18 | exchangeData.sells.volumes['41'].should.eql(200); 19 | done(); 20 | }); 21 | 22 | test('sell should produce trades', function(done) { 23 | exchangeData = exchange.sell(40, 75, exchangeData); 24 | exchangeData.trades[0].price.should.eql(40); 25 | exchangeData.trades[0].volume.should.eql(75); 26 | exchangeData.buys.volumes[40].should.eql(25); 27 | exchangeData.sells.volumes[41].should.eql(200); 28 | done(); 29 | }); 30 | }); -------------------------------------------------------------------------------- /chapter05/public/js/chat.js: -------------------------------------------------------------------------------- 1 | var socket = io.connect('http://localhost'); 2 | $(document).ready(function() { 3 | $('.chat-widget').hide(); 4 | $('#join-chat').click(function() { 5 | $('#join-chat').hide(); 6 | $('.chat-widget').show(); 7 | socket.emit('joined', {}); 8 | }); 9 | $('#send-chat').click(function() { 10 | socket.emit('clientchat', {message: $('#input01').val()}); 11 | }); 12 | 13 | socket.on('chat', function (data) { 14 | $('#textarea').append(data.message); 15 | if (data.username) { 16 | $('#users').append('' + data.username + ''); 17 | } 18 | if (data.users) { 19 | var userHtml = ''; 20 | for (var i=0; i < data.users.length; i++) { 21 | userHtml += '' + data.users[i] + ''; 22 | } 23 | $('#users').html(userHtml); 24 | } 25 | }); 26 | socket.on('disconnect', function (data) { 27 | $('#username-' + data.username).remove(); 28 | }); 29 | }); -------------------------------------------------------------------------------- /chapter06/public/js/chat.js: -------------------------------------------------------------------------------- 1 | var socket = io.connect('http://localhost'); 2 | $(document).ready(function() { 3 | $('.chat-widget').hide(); 4 | $('#join-chat').click(function() { 5 | $('#join-chat').hide(); 6 | $('.chat-widget').show(); 7 | socket.emit('joined', {}); 8 | }); 9 | $('#send-chat').click(function() { 10 | socket.emit('clientchat', {message: $('#input01').val()}); 11 | }); 12 | 13 | socket.on('chat', function (data) { 14 | $('#textarea').append(data.message); 15 | if (data.username) { 16 | $('#users').append('' + data.username + ''); 17 | } 18 | if (data.users) { 19 | var userHtml = ''; 20 | for (var i=0; i < data.users.length; i++) { 21 | userHtml += '' + data.users[i] + ''; 22 | } 23 | $('#users').html(userHtml); 24 | } 25 | }); 26 | socket.on('disconnect', function (data) { 27 | $('#username-' + data.username).remove(); 28 | }); 29 | }); -------------------------------------------------------------------------------- /chapter07/public/js/chat.js: -------------------------------------------------------------------------------- 1 | var socket = io.connect(window.location.hostname); 2 | $(document).ready(function() { 3 | $('.chat-widget').hide(); 4 | $('#join-chat').click(function() { 5 | $('#join-chat').hide(); 6 | $('.chat-widget').show(); 7 | socket.emit('joined', {}); 8 | }); 9 | $('#send-chat').click(function() { 10 | socket.emit('clientchat', {message: $('#input01').val()}); 11 | }); 12 | 13 | socket.on('chat', function (data) { 14 | $('#textarea').append(data.message); 15 | if (data.username) { 16 | $('#users').append('' + data.username + ''); 17 | } 18 | if (data.users) { 19 | var userHtml = ''; 20 | for (var i=0; i < data.users.length; i++) { 21 | userHtml += '' + data.users[i] + ''; 22 | } 23 | $('#users').html(userHtml); 24 | } 25 | }); 26 | socket.on('disconnect', function (data) { 27 | $('#username-' + data.username).remove(); 28 | }); 29 | }); -------------------------------------------------------------------------------- /chapter03/lib/db.js: -------------------------------------------------------------------------------- 1 | var Db = require('mongodb').Db 2 | ,Connection = require('mongodb').Connection 3 | ,Server = require('mongodb').Server; 4 | 5 | var envHost = process.env['MONGO_NODE_DRIVER_HOST'] 6 | ,envPort = process.env['MONGO_NODE_DRIVER_PORT'] 7 | ,host = envHost != null ? envHost: 'localhost' 8 | ,port = envPort != null ? envPort: Connection.DEFAULT_PORT; 9 | 10 | var db = new Db('nockmarket' 11 | ,new Server(host, port, {}) 12 | ,{native_parser:false}); 13 | 14 | module.exports = { 15 | find: function(name, query, limit, callback) { 16 | db.collection(name).find(query).sort({_id: -1}).limit(limit).toArray(callback); 17 | }, 18 | findOne: function(name, query, callback) { 19 | db.collection(name).findOne(query, callback); 20 | }, 21 | insert: function(name, items, callback) { 22 | db.collection(name).insert(items, callback); 23 | }, 24 | insertOne: function(name, item, callback) { 25 | module.exports.insert(name, item, function(err, items) { 26 | callback(err, items[0]); 27 | }); 28 | }, 29 | open: function(callback) { 30 | db.open(callback); 31 | } 32 | } -------------------------------------------------------------------------------- /chapter03/test/db.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , db = require('../lib/db') 5 | , nocklib = require('../lib/nocklib') 6 | , should = require('should'); 7 | 8 | var exchangeData = {}; 9 | 10 | suite('database', function() { 11 | var insertedOrder; 12 | 13 | test('open should open database connection', function(done) { 14 | db.open(done); 15 | }); 16 | test('insertOne should insert a transaction', function(done) { 17 | var ord = nocklib.generateRandomOrder(exchangeData); 18 | db.insertOne('transactions', ord, function(err, order) { 19 | should.not.exist(err); 20 | should.exist(order._id); 21 | insertedOrder = order; 22 | done(); 23 | }); 24 | }); 25 | test('findOne should find a single transaction', function(done) { 26 | var id = insertedOrder._id; 27 | db.findOne('transactions', id, function(err, order) { 28 | should.not.exist(err); 29 | should.exist(order._id); 30 | order.price.should.eql(insertedOrder.price); 31 | order.volume.should.eql(insertedOrder.volume); 32 | done(); 33 | }); 34 | }); 35 | }); -------------------------------------------------------------------------------- /chapter04/test/db.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , db = require('../lib/db') 5 | , nocklib = require('../lib/nocklib') 6 | , should = require('should'); 7 | 8 | var exchangeData = {}; 9 | 10 | suite('database', function() { 11 | var insertedOrder; 12 | 13 | test('open should open database connection', function(done) { 14 | db.open(done); 15 | }); 16 | test('insertOne should insert a transaction', function(done) { 17 | var ord = nocklib.generateRandomOrder(exchangeData); 18 | db.insertOne('transactions', ord, function(err, order) { 19 | should.not.exist(err); 20 | should.exist(order._id); 21 | insertedOrder = order; 22 | done(); 23 | }); 24 | }); 25 | test('findOne should find a single transaction', function(done) { 26 | var id = insertedOrder._id; 27 | db.findOne('transactions', id, function(err, order) { 28 | should.not.exist(err); 29 | should.exist(order._id); 30 | order.price.should.eql(insertedOrder.price); 31 | order.volume.should.eql(insertedOrder.volume); 32 | done(); 33 | }); 34 | }); 35 | }); -------------------------------------------------------------------------------- /chapter05/test/db.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , db = require('../lib/db') 5 | , nocklib = require('../lib/nocklib') 6 | , should = require('should'); 7 | 8 | var exchangeData = {}; 9 | 10 | suite('database', function() { 11 | var insertedOrder; 12 | 13 | test('open should open database connection', function(done) { 14 | db.open(done); 15 | }); 16 | test('insertOne should insert a transaction', function(done) { 17 | var ord = nocklib.generateRandomOrder(exchangeData); 18 | db.insertOne('transactions', ord, function(err, order) { 19 | should.not.exist(err); 20 | should.exist(order._id); 21 | insertedOrder = order; 22 | done(); 23 | }); 24 | }); 25 | test('findOne should find a single transaction', function(done) { 26 | var id = insertedOrder._id; 27 | db.findOne('transactions', id, function(err, order) { 28 | should.not.exist(err); 29 | should.exist(order._id); 30 | order.price.should.eql(insertedOrder.price); 31 | order.volume.should.eql(insertedOrder.volume); 32 | done(); 33 | }); 34 | }); 35 | }); -------------------------------------------------------------------------------- /chapter06/test/db.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , db = require('../lib/db') 5 | , nocklib = require('../lib/nocklib') 6 | , should = require('should'); 7 | 8 | var exchangeData = {}; 9 | 10 | suite('database', function() { 11 | var insertedOrder; 12 | 13 | test('open should open database connection', function(done) { 14 | db.open(done); 15 | }); 16 | test('insertOne should insert a transaction', function(done) { 17 | var ord = nocklib.generateRandomOrder(exchangeData); 18 | db.insertOne('transactions', ord, function(err, order) { 19 | should.not.exist(err); 20 | should.exist(order._id); 21 | insertedOrder = order; 22 | done(); 23 | }); 24 | }); 25 | test('findOne should find a single transaction', function(done) { 26 | var id = insertedOrder._id; 27 | db.findOne('transactions', id, function(err, order) { 28 | should.not.exist(err); 29 | should.exist(order._id); 30 | order.price.should.eql(insertedOrder.price); 31 | order.volume.should.eql(insertedOrder.volume); 32 | done(); 33 | }); 34 | }); 35 | }); -------------------------------------------------------------------------------- /chapter07/test/db.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert') 4 | , db = require('../lib/db') 5 | , nocklib = require('../lib/nocklib') 6 | , should = require('should'); 7 | 8 | var exchangeData = {}; 9 | 10 | suite('database', function() { 11 | var insertedOrder; 12 | 13 | test('open should open database connection', function(done) { 14 | db.open(done); 15 | }); 16 | test('insertOne should insert a transaction', function(done) { 17 | var ord = nocklib.generateRandomOrder(exchangeData); 18 | db.insertOne('transactions', ord, function(err, order) { 19 | should.not.exist(err); 20 | should.exist(order._id); 21 | insertedOrder = order; 22 | done(); 23 | }); 24 | }); 25 | test('findOne should find a single transaction', function(done) { 26 | var id = insertedOrder._id; 27 | db.findOne('transactions', id, function(err, order) { 28 | should.not.exist(err); 29 | should.exist(order._id); 30 | order.price.should.eql(insertedOrder.price); 31 | order.volume.should.eql(insertedOrder.volume); 32 | done(); 33 | }); 34 | }); 35 | }); -------------------------------------------------------------------------------- /chapter04/lib/db.js: -------------------------------------------------------------------------------- 1 | var Db = require('mongodb').Db 2 | ,Connection = require('mongodb').Connection 3 | ,Server = require('mongodb').Server; 4 | 5 | var envHost = process.env['MONGO_NODE_DRIVER_HOST'] 6 | ,envPort = process.env['MONGO_NODE_DRIVER_PORT'] 7 | ,host = envHost != null ? envHost: 'localhost' 8 | ,port = envPort != null ? envPort: Connection.DEFAULT_PORT; 9 | 10 | var db = new Db('nockmarket' 11 | ,new Server(host, port, {}) 12 | ,{native_parser:false}); 13 | 14 | module.exports = { 15 | find: function(name, query, limit, callback) { 16 | db.collection(name).find(query).sort({_id: -1}).limit(limit).toArray(callback); 17 | }, 18 | findOne: function(name, query, callback) { 19 | db.collection(name).findOne(query, callback); 20 | }, 21 | insert: function(name, items, callback) { 22 | db.collection(name).insert(items, callback); 23 | }, 24 | insertOne: function(name, item, callback) { 25 | module.exports.insert(name, item, function(err, items) { 26 | callback(err, items[0]); 27 | }); 28 | }, 29 | open: function(callback) { 30 | db.open(callback); 31 | }, 32 | push: function(name, id, updateQuery, callback) { 33 | db.collection(name).update({_id: id}, {$push: updateQuery}, {safe:true}, callback); 34 | } 35 | } -------------------------------------------------------------------------------- /chapter02/lib/nocklib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchange = require('./exchange') 4 | , priceFloor = 35 5 | , priceRange = 10 6 | , volFloor = 80 7 | , volRange = 40; 8 | 9 | module.exports = { 10 | generateRandomOrder: function(exchangeData) { 11 | var order = {}; 12 | if (Math.random() > 0.5) 13 | order.type = exchange.BUY 14 | else 15 | order.type = exchange.SELL 16 | 17 | var buyExists = exchangeData.buys 18 | && exchangeData.buys.prices.peek(); 19 | var sellExists = exchangeData.sells 20 | && exchangeData.sells.prices.peek(); 21 | 22 | var ran = Math.random(); 23 | if (!buyExists && !sellExists) 24 | order.price = Math.floor(ran * priceRange) + priceFloor; 25 | else if (buyExists && sellExists) { 26 | if (Math.random() > 0.5) 27 | order.price = exchangeData.buys.prices.peek(); 28 | else 29 | order.price = exchangeData.sells.prices.peek(); 30 | } else if (buyExists) { 31 | order.price = exchangeData.buys.prices.peek(); 32 | } else { 33 | order.price = exchangeData.sells.prices.peek(); 34 | } 35 | 36 | var shift = Math.floor(Math.random() * priceRange / 2); 37 | 38 | if (Math.random() > 0.5) 39 | order.price += shift; 40 | else 41 | order.price -= shift; 42 | order.volume = Math.floor(Math.random() * volRange) + volFloor; 43 | return order; 44 | } 45 | } -------------------------------------------------------------------------------- /chapter03/lib/nocklib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchange = require('./exchange') 4 | , priceFloor = 35 5 | , priceRange = 10 6 | , volFloor = 80 7 | , volRange = 40; 8 | 9 | module.exports = { 10 | generateRandomOrder: function(exchangeData) { 11 | var order = {}; 12 | if (Math.random() > 0.5) 13 | order.type = exchange.BUY 14 | else 15 | order.type = exchange.SELL 16 | 17 | var buyExists = exchangeData.buys 18 | && exchangeData.buys.prices.peek(); 19 | var sellExists = exchangeData.sells 20 | && exchangeData.sells.prices.peek(); 21 | 22 | var ran = Math.random(); 23 | if (!buyExists && !sellExists) 24 | order.price = Math.floor(ran * priceRange) + priceFloor; 25 | else if (buyExists && sellExists) { 26 | if (Math.random() > 0.5) 27 | order.price = exchangeData.buys.prices.peek(); 28 | else 29 | order.price = exchangeData.sells.prices.peek(); 30 | } else if (buyExists) { 31 | order.price = exchangeData.buys.prices.peek(); 32 | } else { 33 | order.price = exchangeData.sells.prices.peek(); 34 | } 35 | 36 | var shift = Math.floor(Math.random() * priceRange / 2); 37 | 38 | if (Math.random() > 0.5) 39 | order.price += shift; 40 | else 41 | order.price -= shift; 42 | order.volume = Math.floor(Math.random() * volRange) + volFloor 43 | return order; 44 | } 45 | } -------------------------------------------------------------------------------- /chapter05/lib/db.js: -------------------------------------------------------------------------------- 1 | var Db = require('mongodb').Db 2 | ,Connection = require('mongodb').Connection 3 | ,Server = require('mongodb').Server; 4 | 5 | var envHost = process.env['MONGO_NODE_DRIVER_HOST'] 6 | ,envPort = process.env['MONGO_NODE_DRIVER_PORT'] 7 | ,host = envHost != null ? envHost: 'localhost' 8 | ,port = envPort != null ? envPort: Connection.DEFAULT_PORT; 9 | 10 | var db = new Db('nockmarket' 11 | ,new Server(host, port, {}) 12 | ,{native_parser:false}); 13 | 14 | module.exports = { 15 | find: function(name, query, limit, callback) { 16 | db.collection(name).find(query).sort({_id: -1}).limit(limit).toArray(callback); 17 | }, 18 | findOne: function(name, query, callback) { 19 | db.collection(name).findOne(query, callback); 20 | }, 21 | insert: function(name, items, callback) { 22 | db.collection(name).insert(items, callback); 23 | }, 24 | insertOne: function(name, item, callback) { 25 | module.exports.insert(name, item, function(err, items) { 26 | callback(err, items[0]); 27 | }); 28 | }, 29 | open: function(callback) { 30 | db.open(callback); 31 | }, 32 | push: function(name, id, updateQuery, callback) { 33 | db.collection(name).update({_id: id}, {$push: updateQuery}, {safe:true}, callback); 34 | }, 35 | updateById: function(name, id, updateQuery, callback) { 36 | db.collection(name).update({_id: id}, {$set: updateQuery}, {safe:true}, callback); 37 | } 38 | } -------------------------------------------------------------------------------- /chapter06/lib/db.js: -------------------------------------------------------------------------------- 1 | var Db = require('mongodb').Db 2 | ,Connection = require('mongodb').Connection 3 | ,Server = require('mongodb').Server; 4 | 5 | var envHost = process.env['MONGO_NODE_DRIVER_HOST'] 6 | ,envPort = process.env['MONGO_NODE_DRIVER_PORT'] 7 | ,host = envHost != null ? envHost: 'localhost' 8 | ,port = envPort != null ? envPort: Connection.DEFAULT_PORT; 9 | 10 | var db = new Db('nockmarket' 11 | ,new Server(host, port, {}) 12 | ,{native_parser:false}); 13 | 14 | module.exports = { 15 | find: function(name, query, limit, callback) { 16 | db.collection(name).find(query).sort({_id: -1}).limit(limit).toArray(callback); 17 | }, 18 | findOne: function(name, query, callback) { 19 | db.collection(name).findOne(query, callback); 20 | }, 21 | insert: function(name, items, callback) { 22 | db.collection(name).insert(items, callback); 23 | }, 24 | insertOne: function(name, item, callback) { 25 | module.exports.insert(name, item, function(err, items) { 26 | callback(err, items[0]); 27 | }); 28 | }, 29 | open: function(callback) { 30 | db.open(callback); 31 | }, 32 | push: function(name, id, updateQuery, callback) { 33 | db.collection(name).update({_id: id}, {$push: updateQuery}, {safe:true}, callback); 34 | }, 35 | updateById: function(name, id, updateQuery, callback) { 36 | db.collection(name).update({_id: id}, {$set: updateQuery}, {safe:true}, callback); 37 | } 38 | } -------------------------------------------------------------------------------- /chapter04/routes/nockroutes.js: -------------------------------------------------------------------------------- 1 | var nocklib = require('../lib/nocklib'); 2 | 3 | module.exports = { 4 | addStock: function(req, res) { 5 | if (req.xhr) { 6 | nocklib.addStock(req.session._id, req.body.stock, function(err, price) {res.send(price);}); 7 | } 8 | }, 9 | 10 | getIndex: function(req, res) { 11 | res.render('index'); 12 | }, 13 | 14 | getUser: function(req, res) { 15 | nocklib.getUser(req.params.username, function(err, user) { 16 | if (user) 17 | res.send('1'); 18 | else 19 | res.send('0'); 20 | }); 21 | }, 22 | 23 | portfolio: function(req, res) { 24 | nocklib.getUserById(req.session._id, function(err, user) { 25 | var portfolio = []; 26 | if (user && user.portfolio) portfolio = user.portfolio; 27 | nocklib.getStockPrices(portfolio, function(err, prices) { 28 | res.render('portfolio', {portfolio:portfolio, prices:prices}); 29 | }); 30 | }); 31 | }, 32 | 33 | login: function(req, res) { 34 | nocklib.authenticate(req.body.username, req.body.password, function(err, id) { 35 | if (id) { 36 | req.session._id = id; 37 | res.redirect('/portfolio'); 38 | } 39 | else 40 | res.redirect('/'); 41 | }); 42 | }, 43 | 44 | signup: function(req, res) { 45 | nocklib.createUser(req.body.username, req.body.email, req.body.password, function(err, user) { 46 | console.log(user); 47 | res.redirect('/portfolio'); 48 | }); 49 | } 50 | } -------------------------------------------------------------------------------- /chapter05/routes/nockroutes.js: -------------------------------------------------------------------------------- 1 | var nocklib = require('../lib/nocklib'); 2 | 3 | module.exports = { 4 | addStock: function(req, res) { 5 | if (req.xhr) { 6 | nocklib.addStock(req.session._id, req.body.stock, function(err, price) {res.send(price);}); 7 | } 8 | }, 9 | 10 | getIndex: function(req, res) { 11 | res.render('index'); 12 | }, 13 | 14 | getUser: function(req, res) { 15 | nocklib.getUser(req.params.username, function(err, user) { 16 | if (user) 17 | res.send('1'); 18 | else 19 | res.send('0'); 20 | }); 21 | }, 22 | 23 | portfolio: function(req, res) { 24 | nocklib.getUserById(req.session._id, function(err, user) { 25 | var portfolio = []; 26 | if (user && user.portfolio) portfolio = user.portfolio; 27 | nocklib.getStockPrices(portfolio, function(err, prices) { 28 | res.render('portfolio', {portfolio: portfolio, prices: prices, email: user.email}); 29 | }); 30 | }); 31 | }, 32 | 33 | login: function(req, res) { 34 | nocklib.authenticate(req.body.username, req.body.password, function(err, id) { 35 | if (id) { 36 | req.session._id = id; 37 | req.session.username = req.body.username; 38 | res.redirect('/portfolio'); 39 | } 40 | else 41 | res.redirect('/'); 42 | }); 43 | }, 44 | 45 | signup: function(req, res) { 46 | nocklib.createUser(req.body.username, req.body.email, req.body.password, function(err, user) { 47 | console.log(user); 48 | res.redirect('/portfolio'); 49 | }); 50 | } 51 | } -------------------------------------------------------------------------------- /chapter01/authentication/app.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var express = require('express') 7 | , routes = require('./routes') 8 | , fs = require('fs') 9 | , User = require('./models/User.js'); 10 | 11 | var app = module.exports = express.createServer(); 12 | 13 | // Configuration 14 | 15 | app.configure(function(){ 16 | app.set('views', __dirname + '/views'); 17 | app.set('view engine', 'jade'); 18 | app.use(express.bodyParser()); 19 | app.use(express.methodOverride()); 20 | app.use(app.router); 21 | app.use(express.static(__dirname + '/public')); 22 | }); 23 | 24 | app.configure('development', function(){ 25 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 26 | }); 27 | 28 | app.configure('production', function(){ 29 | app.use(express.errorHandler()); 30 | }); 31 | 32 | // Routes 33 | 34 | app.get('/', routes.index); 35 | app.get('/form', function(req, res) { 36 | fs.readFile('./form.html', function(error, content) { 37 | if (error) { 38 | res.writeHead(500); 39 | res.end(); 40 | } 41 | else { 42 | res.writeHead(200, { 'Content-Type': 'text/html' }); 43 | res.end(content, 'utf-8'); 44 | } 45 | }); 46 | }); 47 | app.post('/signup', function(req, res) { 48 | var username = req.body.username; 49 | var password = req.body.password; 50 | User.addUser(username, password, function(err, user) { 51 | if (err) throw err; 52 | res.redirect('/form'); 53 | }); 54 | }); 55 | 56 | app.listen(3000); 57 | console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); 58 | -------------------------------------------------------------------------------- /chapter06/public/templates/trade-table.ejs: -------------------------------------------------------------------------------- 1 | <%=st%> 2 |
<%=b5p%>
<%=b5v%>
3 |
<%=b4p%>
<%=b4v%>
4 |
<%=b3p%>
<%=b3v%>
5 |
<%=b2p%>
<%=b2v%>
6 |
<%=b1p%>
<%=b1v%>
7 |
<%=tp%>
<%=tv%>
8 |
<%=a1p%>
<%=a1v%>
9 |
<%=a2p%>
<%=a2v%>
10 |
<%=a3p%>
<%=a3v%>
11 |
<%=a4p%>
<%=a4v%>
12 |
<%=a5p%>
<%=a5v%>
-------------------------------------------------------------------------------- /chapter07/public/templates/trade-table.ejs: -------------------------------------------------------------------------------- 1 | <%=st%> 2 |
<%=b5p%>
<%=b5v%>
3 |
<%=b4p%>
<%=b4v%>
4 |
<%=b3p%>
<%=b3v%>
5 |
<%=b2p%>
<%=b2v%>
6 |
<%=b1p%>
<%=b1v%>
7 |
<%=tp%>
<%=tv%>
8 |
<%=a1p%>
<%=a1v%>
9 |
<%=a2p%>
<%=a2v%>
10 |
<%=a3p%>
<%=a3v%>
11 |
<%=a4p%>
<%=a4v%>
12 |
<%=a5p%>
<%=a5v%>
-------------------------------------------------------------------------------- /chapter07/lib/db.js: -------------------------------------------------------------------------------- 1 | var Db = require('mongodb').Db 2 | ,Connection = require('mongodb').Connection 3 | ,Server = require('mongodb').Server; 4 | 5 | var envHost = process.env['MONGO_NODE_DRIVER_HOST'] 6 | ,envPort = process.env['MONGO_NODE_DRIVER_PORT'] 7 | ,host = envHost != null ? envHost: 'localhost' 8 | ,port = envPort != null ? parseInt(envPort, 10) : Connection.DEFAULT_PORT; 9 | 10 | var db = new Db('nockmarket' 11 | ,new Server(host, port, {}) 12 | ,{native_parser:false}); 13 | 14 | module.exports = { 15 | find: function(name, query, limit, callback) { 16 | db.collection(name).find(query).sort({_id: -1}).limit(limit).toArray(callback); 17 | }, 18 | findOne: function(name, query, callback) { 19 | db.collection(name).findOne(query, callback); 20 | }, 21 | insert: function(name, items, callback) { 22 | db.collection(name).insert(items, callback); 23 | }, 24 | insertOne: function(name, item, callback) { 25 | module.exports.insert(name, item, function(err, items) { 26 | callback(err, items[0]); 27 | }); 28 | }, 29 | open: function(callback) { 30 | db.open(function(err, data) { 31 | if (process.env.MONGO_NODE_DRIVER_USER) { 32 | data.authenticate(process.env.MONGO_NODE_DRIVER_USER, process.env.MONGO_NODE_DRIVER_PASS, function(err2, authData) { 33 | if(authData) { callback(); } 34 | else { 35 | console.log(err2); 36 | return; 37 | } 38 | }); 39 | } 40 | else { callback(); } 41 | }); 42 | }, 43 | push: function(name, id, updateQuery, callback) { 44 | db.collection(name).update({_id: id}, {$push: updateQuery}, {safe:true}, callback); 45 | }, 46 | updateById: function(name, id, updateQuery, callback) { 47 | db.collection(name).update({_id: id}, {$set: updateQuery}, {safe:true}, callback); 48 | } 49 | } -------------------------------------------------------------------------------- /chapter06/routes/nockroutes.js: -------------------------------------------------------------------------------- 1 | var nocklib = require('../lib/nocklib'); 2 | 3 | module.exports = { 4 | addStock: function(req, res) { 5 | if (req.xhr) { 6 | nocklib.addStock(req.session._id, req.body.stock, function(err, price) { 7 | res.send(price); 8 | }); 9 | } 10 | }, 11 | 12 | getIndex: function(req, res) { 13 | res.render('index'); 14 | }, 15 | 16 | getUser: function(req, res) { 17 | nocklib.getUser(req.params.username, function(err, user) { 18 | if (user) 19 | res.send('1'); 20 | else 21 | res.send('0'); 22 | }); 23 | }, 24 | 25 | portfolio: function(req, res) { 26 | nocklib.getUserById(req.session._id, function(err, user) { 27 | var portfolio = []; 28 | if (user && user.portfolio) portfolio = user.portfolio; 29 | nocklib.getStockPrices(portfolio, function(err, prices) { 30 | if (req.xhr) { 31 | var data = []; 32 | for (var i = 0; i < portfolio.length; i++) { 33 | data.push({stock: portfolio[i], price: prices[i]}); 34 | } 35 | res.json(data); 36 | } else { 37 | res.render('portfolio', {portfolio: portfolio, prices: prices, email: user.email}); 38 | } 39 | }); 40 | }); 41 | }, 42 | 43 | login: function(req, res) { 44 | nocklib.authenticate(req.body.username, req.body.password, function(err, id) { 45 | if (id) { 46 | req.session._id = id; 47 | req.session.username = req.body.username; 48 | res.redirect('/portfolio'); 49 | } 50 | else 51 | res.redirect('/'); 52 | }); 53 | }, 54 | 55 | signup: function(req, res) { 56 | nocklib.createUser(req.body.username, req.body.email, req.body.password, function(err, user) { 57 | console.log(user); 58 | res.redirect('/portfolio'); 59 | }); 60 | } 61 | } -------------------------------------------------------------------------------- /chapter07/routes/nockroutes.js: -------------------------------------------------------------------------------- 1 | var nocklib = require('../lib/nocklib'); 2 | 3 | module.exports = { 4 | addStock: function(req, res) { 5 | if (req.xhr) { 6 | nocklib.addStock(req.session._id, req.body.stock, function(err, price) { 7 | res.send(price); 8 | }); 9 | } 10 | }, 11 | 12 | getIndex: function(req, res) { 13 | res.render('index'); 14 | }, 15 | 16 | getUser: function(req, res) { 17 | nocklib.getUser(req.params.username, function(err, user) { 18 | if (user) 19 | res.send('1'); 20 | else 21 | res.send('0'); 22 | }); 23 | }, 24 | 25 | portfolio: function(req, res) { 26 | nocklib.getUserById(req.session._id, function(err, user) { 27 | var portfolio = []; 28 | if (user && user.portfolio) portfolio = user.portfolio; 29 | nocklib.getStockPrices(portfolio, function(err, prices) { 30 | if (req.xhr) { 31 | var data = []; 32 | for (var i = 0; i < portfolio.length; i++) { 33 | data.push({stock: portfolio[i], price: prices[i]}); 34 | } 35 | res.json(data); 36 | } else { 37 | res.render('portfolio', {portfolio: portfolio, prices: prices, email: user.email}); 38 | } 39 | }); 40 | }); 41 | }, 42 | 43 | login: function(req, res) { 44 | nocklib.authenticate(req.body.username, req.body.password, function(err, id) { 45 | if (id) { 46 | req.session._id = id; 47 | req.session.username = req.body.username; 48 | res.redirect('/portfolio'); 49 | } 50 | else 51 | res.redirect('/'); 52 | }); 53 | }, 54 | 55 | signup: function(req, res) { 56 | nocklib.createUser(req.body.username, req.body.email, req.body.password, function(err, user) { 57 | console.log(user); 58 | res.redirect('/portfolio'); 59 | }); 60 | } 61 | } -------------------------------------------------------------------------------- /chapter03/views/chart.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 61 |
-------------------------------------------------------------------------------- /chapter04/views/chart.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 61 |
-------------------------------------------------------------------------------- /chapter05/views/chart.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 61 |
-------------------------------------------------------------------------------- /chapter06/views/chart.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 61 |
-------------------------------------------------------------------------------- /chapter07/views/chart.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 61 |
-------------------------------------------------------------------------------- /chapter03/nockmarket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchangeData = {} 4 | , exch = require('./lib/exchange') 5 | , nocklib = require('./lib/nocklib') 6 | , db = require('./lib/db') 7 | , express = require('express') 8 | , timeFloor = 500 9 | , timeRange = 1000; 10 | 11 | function submitRandomOrder() { 12 | // order 13 | var ord = nocklib.generateRandomOrder(exchangeData); 14 | console.log('order', ord); 15 | if (ord.type == exch.BUY) 16 | exchangeData = exch.buy(ord.price, ord.volume, exchangeData); 17 | else 18 | exchangeData = exch.sell(ord.price, ord.volume, exchangeData); 19 | 20 | db.insertOne('transactions', ord, function(err, order) { 21 | if (exchangeData.trades && exchangeData.trades.length > 0) { 22 | var trades = exchangeData.trades.map(function(trade) { 23 | trade.init = (ord.type == exch.BUY) ? 'b' : 's'; 24 | return trade; 25 | }); 26 | db.insert('transactions', trades, function(err, trades) { 27 | pauseThenTrade(); 28 | }); 29 | } 30 | else 31 | pauseThenTrade(); 32 | }); 33 | 34 | function pauseThenTrade() { 35 | var pause = Math.floor(Math.random() * timeRange) + timeFloor; 36 | setTimeout(submitRandomOrder, pause); 37 | console.log(exch.getDisplay(exchangeData)); 38 | } 39 | } 40 | 41 | var app = express.createServer(); 42 | app.configure(function () { 43 | app.set('views', __dirname + '/views'); 44 | app.set('view engine', 'ejs'); 45 | app.use(express.static(__dirname + '/public')); 46 | }); 47 | app.set('view options', { 48 | layout: false 49 | }); 50 | app.get('/', function(req, res) { 51 | res.render('chart'); 52 | }); 53 | 54 | app.get('/api/trades', function(req, res) { 55 | db.find('transactions' 56 | , {init: {$exists: true}} 57 | , 100, function(err, trades) { 58 | if (err) { 59 | console.error(err); 60 | return; 61 | } 62 | var json = []; 63 | var lastTime = 0; 64 | // Highstock expects an array of arrays. Each 65 | // subarray of form [time, price] 66 | trades.reverse().forEach(function(trade) { 67 | var date = new Date(parseInt(trade._id 68 | .toString() 69 | .substring(0,8), 16)*1000); 70 | var dataPoint = [date.getTime(), trade.price]; 71 | if (date - lastTime > 1000) 72 | json.push(dataPoint); 73 | lastTime = date; 74 | }); 75 | 76 | res.json(json); 77 | }); 78 | }); 79 | 80 | db.open(function() { 81 | submitRandomOrder(); 82 | app.listen(3000); 83 | }); -------------------------------------------------------------------------------- /chapter06/public/js/portfolio.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add-stock').click(function(e) { 3 | $.ajax({type: 'POST', url: '/add-stock/', data: {stock: $('#stock').val()} 4 | }).done(function(price) { 5 | $('.stock-list').append('' + $('#stock').val() + '' + price + ''); 6 | }); 7 | }); 8 | 9 | var PortfolioModel = Backbone.Model.extend({ 10 | defaults: { 11 | visible: true 12 | }, 13 | setVisible: function(visible) { 14 | this.set({visible: visible}); 15 | } 16 | }); 17 | 18 | var PortfolioCollection = Backbone.Collection.extend({ 19 | model: PortfolioModel, 20 | url: '/portfolio' 21 | }); 22 | 23 | var PortfolioView = Backbone.View.extend({ 24 | el: 'body', 25 | events: { 26 | 'click .add-filter': 'filter' 27 | }, 28 | filter: function() { 29 | var filterString = $('#filter').val(); 30 | var data = window.portfolioCollection.models; 31 | for (var i=0; i<%=stock%><%=price%>"); 71 | $(this.el).html(template(this.model.toJSON())); 72 | return this; 73 | } 74 | }); 75 | new PortfolioView(); 76 | }); -------------------------------------------------------------------------------- /chapter07/public/js/portfolio.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add-stock').click(function(e) { 3 | $.ajax({type: 'POST', url: '/add-stock/', data: {stock: $('#stock').val()} 4 | }).done(function(price) { 5 | $('.stock-list').append('' + $('#stock').val() + '' + price + ''); 6 | }); 7 | }); 8 | 9 | var PortfolioModel = Backbone.Model.extend({ 10 | defaults: { 11 | visible: true 12 | }, 13 | setVisible: function(visible) { 14 | this.set({visible: visible}); 15 | } 16 | }); 17 | 18 | var PortfolioCollection = Backbone.Collection.extend({ 19 | model: PortfolioModel, 20 | url: '/portfolio' 21 | }); 22 | 23 | var PortfolioView = Backbone.View.extend({ 24 | el: 'body', 25 | events: { 26 | 'click .add-filter': 'filter' 27 | }, 28 | filter: function() { 29 | var filterString = $('#filter').val(); 30 | var data = window.portfolioCollection.models; 31 | for (var i=0; i<%=stock%><%=price%>"); 71 | $(this.el).html(template(this.model.toJSON())); 72 | return this; 73 | } 74 | }); 75 | new PortfolioView(); 76 | }); -------------------------------------------------------------------------------- /chapter06/public/js/trades.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $.get('/templates/trade-table.ejs', function(storedTemplate) { 3 | var loaded = false; 4 | socket.emit('requestData', {}); 5 | var StockModel = Backbone.Model.extend({ 6 | updatePrices: function(deltas) { 7 | this.set({deltas: deltas}); 8 | } 9 | }); 10 | var StockCollection = Backbone.Collection.extend({ 11 | model: StockModel 12 | }); 13 | var StockView = Backbone.View.extend({ 14 | initialize: function() { 15 | var self = this; 16 | self.render(); 17 | }, 18 | render: function() { 19 | for (var i=0; i 0) { 44 | if (attr == 'tp') { 45 | $('#' + prices.st + 'trade-cell').css("backgroundColor", color); 46 | $('#' + prices.st + 'trade-cell').animate({backgroundColor: "white"}, 1000); 47 | } 48 | $('#' + prices.st + attr).html(value); 49 | } 50 | } 51 | } 52 | }); 53 | socket.on('exchangeData', function (deltas) { 54 | if (loaded) { 55 | var model = window.stockCollection.get(deltas.st); 56 | model.updatePrices(deltas); 57 | } 58 | }); 59 | socket.on('initExchangeData', function (data) { 60 | window.stockCollection = new StockCollection(); 61 | for (var stock in data.exchangeData) { 62 | var stockModel = new StockModel(data.exchangeData[stock]); 63 | stockModel.set({id: data.exchangeData[stock].st}); 64 | window.stockCollection.push(stockModel); 65 | } 66 | loaded = true; 67 | new StockView(); 68 | }); 69 | }); 70 | }); -------------------------------------------------------------------------------- /chapter07/public/js/trades.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $.get('/templates/trade-table.ejs', function(storedTemplate) { 3 | var loaded = false; 4 | socket.emit('requestData', {}); 5 | var StockModel = Backbone.Model.extend({ 6 | updatePrices: function(deltas) { 7 | this.set({deltas: deltas}); 8 | } 9 | }); 10 | var StockCollection = Backbone.Collection.extend({ 11 | model: StockModel 12 | }); 13 | var StockView = Backbone.View.extend({ 14 | initialize: function() { 15 | var self = this; 16 | self.render(); 17 | }, 18 | render: function() { 19 | for (var i=0; i 0) { 44 | if (attr == 'tp') { 45 | $('#' + prices.st + 'trade-cell').css("backgroundColor", color); 46 | $('#' + prices.st + 'trade-cell').animate({backgroundColor: "white"}, 1000); 47 | } 48 | $('#' + prices.st + attr).html(value); 49 | } 50 | } 51 | } 52 | }); 53 | socket.on('exchangeData', function (deltas) { 54 | if (loaded) { 55 | var model = window.stockCollection.get(deltas.st); 56 | model.updatePrices(deltas); 57 | } 58 | }); 59 | socket.on('initExchangeData', function (data) { 60 | window.stockCollection = new StockCollection(); 61 | for (var stock in data.exchangeData) { 62 | var stockModel = new StockModel(data.exchangeData[stock]); 63 | stockModel.set({id: data.exchangeData[stock].st}); 64 | window.stockCollection.push(stockModel); 65 | } 66 | loaded = true; 67 | new StockView(); 68 | }); 69 | }); 70 | }); -------------------------------------------------------------------------------- /chapter04/views/portfolio.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

Welcome to the Nockmarket

22 |

Here you can manage your portfolio and view live prices.

23 |
24 | 28 |
29 |
30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | <% for (var i=0; i 43 | 44 | 45 | <% } %> 46 | 47 |
Stock CodeLast Price
<%=portfolio[i]%><%=prices[i]%>
48 |
49 |
50 |

Howdy, I'm in Section 2.

51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 | -------------------------------------------------------------------------------- /chapter04/nockmarket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchangeData = {} 4 | , exch = require('./lib/exchange') 5 | , db = require('./lib/db') 6 | , express = require('express') 7 | , nocklib = require('./lib/nocklib') 8 | , nockroutes = require('./routes/nockroutes.js') 9 | , timeFloor = 500 10 | , timeRange = 1000; 11 | 12 | function submitRandomOrder() { 13 | // order 14 | var ord = nocklib.generateRandomOrder(exchangeData); 15 | //console.log('order', ord); 16 | if (ord.type == exch.BUY) 17 | exchangeData = exch.buy(ord.price, ord.volume, exchangeData); 18 | else 19 | exchangeData = exch.sell(ord.price, ord.volume, exchangeData); 20 | 21 | db.insertOne('transactions', ord, function(err, order) { 22 | if (exchangeData.trades && exchangeData.trades.length > 0) { 23 | var trades = exchangeData.trades.map(function(trade) { 24 | trade.init = (ord.type == exch.BUY) ? 'b' : 's'; 25 | return trade; 26 | }); 27 | db.insert('transactions', trades, function(err, trades) { 28 | pauseThenTrade(); 29 | }); 30 | } 31 | else 32 | pauseThenTrade(); 33 | }); 34 | 35 | function pauseThenTrade() { 36 | var pause = Math.floor(Math.random() * timeRange) + timeFloor; 37 | setTimeout(submitRandomOrder, pause); 38 | //console.log(exch.getDisplay(exchangeData)); 39 | } 40 | } 41 | 42 | var app = express.createServer(); 43 | app.configure(function () { 44 | app.use(express.bodyParser()); 45 | app.use(express.cookieParser()); 46 | app.use(express.session({secret: 'secretpasswordforsessions'})); 47 | app.set('views', __dirname + '/views'); 48 | app.set('view engine', 'ejs'); 49 | app.use(express.static(__dirname + '/public')); 50 | }); 51 | app.set('view options', { 52 | layout: false 53 | }); 54 | app.get('/', nockroutes.getIndex); 55 | app.get('/api/user/:username', nockroutes.getUser); 56 | app.get('/portfolio', nocklib.ensureAuthenticated, nockroutes.portfolio); 57 | app.post('/add-stock', nockroutes.addStock); 58 | app.post('/login', nockroutes.login); 59 | app.post('/signup', nockroutes.signup); 60 | 61 | app.get('/api/trades', function(req, res) { 62 | db.find('transactions' 63 | , {init: {$exists: true}} 64 | , 100, function(err, trades) { 65 | if (err) { 66 | console.error(err); 67 | return; 68 | } 69 | var json = []; 70 | var lastTime = 0; 71 | // Highstock expects an array of arrays. Each 72 | // subarray of form [time, price] 73 | trades.reverse().forEach(function(trade) { 74 | var date = new Date(parseInt(trade._id 75 | .toString() 76 | .substring(0,8), 16)*1000); 77 | var dataPoint = [date.getTime(), trade.price]; 78 | if (date - lastTime > 1000) 79 | json.push(dataPoint); 80 | lastTime = date; 81 | }); 82 | 83 | res.json(json); 84 | }); 85 | }); 86 | 87 | db.open(function() { 88 | submitRandomOrder(); 89 | app.listen(3000); 90 | }); -------------------------------------------------------------------------------- /chapter05/nockmarket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchangeData = {} 4 | , exch = require('./lib/exchange') 5 | , db = require('./lib/db') 6 | , express = require('express') 7 | , nocklib = require('./lib/nocklib') 8 | , nockroutes = require('./routes/nockroutes.js') 9 | , timeFloor = 500 10 | , timeRange = 1000; 11 | 12 | var stocks = ['NOCK1', 'NOCK2', 'NOCK3', 'NOCK4', 'NOCK5']; 13 | var allData = []; 14 | stocks.forEach(function(stock) {allData.push({});}); 15 | 16 | function submitRandomOrder(index) { 17 | // order 18 | var exchangeData = allData[index]; 19 | var ord = nocklib.generateRandomOrder(exchangeData); 20 | ord.stock = stocks[index]; 21 | //console.log('order', ord); 22 | if (ord.type == exch.BUY) 23 | allData[index] = exch.buy(ord.price, ord.volume, exchangeData); 24 | else 25 | allData[index] = exch.sell(ord.price, ord.volume, exchangeData); 26 | 27 | db.insertOne('transactions', ord, function(err, order) { 28 | if (exchangeData.trades && exchangeData.trades.length > 0) { 29 | nocklib.sendTrades(exchangeData.trades); 30 | var trades = exchangeData.trades.map(function(trade) { 31 | trade.init = (ord.type == exch.BUY) ? 'b' : 's'; 32 | trade.stock = stocks[index]; 33 | return trade; 34 | }); 35 | db.insert('transactions', trades, function(err, trades) { 36 | pauseThenTrade(); 37 | }); 38 | } 39 | else 40 | pauseThenTrade(); 41 | }); 42 | 43 | function pauseThenTrade() { 44 | var pause = Math.floor(Math.random() * timeRange) + timeFloor; 45 | setTimeout(submitRandomOrder.bind(this, index), pause); 46 | //console.log(exch.getDisplay(exchangeData)); 47 | } 48 | } 49 | 50 | var app = express.createServer(); 51 | app.configure(function () { 52 | app.use(express.bodyParser()); 53 | app.use(express.cookieParser()); 54 | app.use(express.session({secret: 'secretpasswordforsessions', store: nocklib.getSessionStore()})); 55 | app.set('views', __dirname + '/views'); 56 | app.set('view engine', 'ejs'); 57 | app.use(express.static(__dirname + '/public')); 58 | }); 59 | app.set('view options', { 60 | layout: false 61 | }); 62 | app.get('/', nockroutes.getIndex); 63 | app.get('/api/user/:username', nockroutes.getUser); 64 | app.get('/portfolio', nocklib.ensureAuthenticated, nockroutes.portfolio); 65 | app.post('/add-stock', nockroutes.addStock); 66 | app.post('/login', nockroutes.login); 67 | app.post('/signup', nockroutes.signup); 68 | 69 | app.get('/api/trades', function(req, res) { 70 | db.find('transactions' 71 | , {init: {$exists: true}} 72 | , 100, function(err, trades) { 73 | if (err) { 74 | console.error(err); 75 | return; 76 | } 77 | var json = []; 78 | var lastTime = 0; 79 | // Highstock expects an array of arrays. Each 80 | // subarray of form [time, price] 81 | trades.reverse().forEach(function(trade) { 82 | var date = new Date(parseInt(trade._id 83 | .toString() 84 | .substring(0,8), 16)*1000); 85 | var dataPoint = [date.getTime(), trade.price]; 86 | if (date - lastTime > 1000) 87 | json.push(dataPoint); 88 | lastTime = date; 89 | }); 90 | 91 | res.json(json); 92 | }); 93 | }); 94 | 95 | db.open(function() { 96 | nocklib.createSocket(app); 97 | app.listen(3000); 98 | for (var i = 0; i < stocks.length; i++) { 99 | submitRandomOrder(i); 100 | } 101 | }); -------------------------------------------------------------------------------- /chapter06/nockmarket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchangeData = {} 4 | , exch = require('./lib/exchange') 5 | , db = require('./lib/db') 6 | , express = require('express') 7 | , nocklib = require('./lib/nocklib') 8 | , nockroutes = require('./routes/nockroutes.js') 9 | , timeFloor = 500 10 | , timeRange = 1000; 11 | 12 | var stocks = ['NOCK1', 'NOCK2', 'NOCK3', 'NOCK4', 'NOCK5']; 13 | var allData = []; 14 | stocks.forEach(function(stock) {allData.push({});}); 15 | 16 | function submitRandomOrder(index) { 17 | // order 18 | var exchangeData = allData[index]; 19 | var ord = nocklib.generateRandomOrder(exchangeData); 20 | ord.stock = stocks[index]; 21 | //console.log('order', ord); 22 | if (ord.type == exch.BUY) 23 | allData[index] = exch.buy(ord.price, ord.volume, exchangeData); 24 | else 25 | allData[index] = exch.sell(ord.price, ord.volume, exchangeData); 26 | 27 | db.insertOne('transactions', ord, function(err, order) { 28 | if (exchangeData.trades && exchangeData.trades.length > 0) { 29 | nocklib.sendTrades(exchangeData.trades); 30 | var trades = exchangeData.trades.map(function(trade) { 31 | trade.init = (ord.type == exch.BUY) ? 'b' : 's'; 32 | trade.stock = stocks[index]; 33 | return trade; 34 | }); 35 | nocklib.sendExchangeData(stocks[index], exchangeData); 36 | db.insert('transactions', trades, function(err, trades) { 37 | pauseThenTrade(); 38 | }); 39 | } 40 | else 41 | pauseThenTrade(); 42 | }); 43 | 44 | function pauseThenTrade() { 45 | var pause = Math.floor(Math.random() * timeRange) + timeFloor; 46 | setTimeout(submitRandomOrder.bind(this, index), pause); 47 | //console.log(exch.getDisplay(exchangeData)); 48 | } 49 | } 50 | 51 | var app = express.createServer(); 52 | app.configure(function () { 53 | app.use(express.bodyParser()); 54 | app.use(express.cookieParser()); 55 | app.use(express.session({secret: 'secretpasswordforsessions', store: nocklib.getSessionStore()})); 56 | app.set('views', __dirname + '/views'); 57 | app.set('view engine', 'ejs'); 58 | app.use(express.static(__dirname + '/public')); 59 | }); 60 | app.set('view options', { 61 | layout: false 62 | }); 63 | app.get('/', nockroutes.getIndex); 64 | app.get('/api/user/:username', nockroutes.getUser); 65 | app.get('/portfolio', nocklib.ensureAuthenticated, nockroutes.portfolio); 66 | app.post('/add-stock', nockroutes.addStock); 67 | app.post('/login', nockroutes.login); 68 | app.post('/signup', nockroutes.signup); 69 | 70 | app.get('/api/trades', function(req, res) { 71 | db.find('transactions' 72 | , {init: {$exists: true}} 73 | , 100, function(err, trades) { 74 | if (err) { 75 | console.error(err); 76 | return; 77 | } 78 | var json = []; 79 | var lastTime = 0; 80 | // Highstock expects an array of arrays. Each 81 | // subarray of form [time, price] 82 | trades.reverse().forEach(function(trade) { 83 | var date = new Date(parseInt(trade._id 84 | .toString() 85 | .substring(0,8), 16)*1000); 86 | var dataPoint = [date.getTime(), trade.price]; 87 | if (date - lastTime > 1000) 88 | json.push(dataPoint); 89 | lastTime = date; 90 | }); 91 | 92 | res.json(json); 93 | }); 94 | }); 95 | 96 | db.open(function() { 97 | nocklib.createSocket(app); 98 | app.listen(3000); 99 | for (var i = 0; i < stocks.length; i++) { 100 | submitRandomOrder(i); 101 | } 102 | }); -------------------------------------------------------------------------------- /chapter07/nockmarket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exchangeData = {} 4 | , exch = require('./lib/exchange') 5 | , db = require('./lib/db') 6 | , express = require('express') 7 | , nocklib = require('./lib/nocklib') 8 | , nockroutes = require('./routes/nockroutes.js') 9 | , timeFloor = 500 10 | , timeRange = 1000; 11 | 12 | var stocks = ['NOCK1', 'NOCK2', 'NOCK3', 'NOCK4', 'NOCK5']; 13 | var allData = []; 14 | stocks.forEach(function(stock) {allData.push({});}); 15 | 16 | function submitRandomOrder(index) { 17 | // order 18 | var exchangeData = allData[index]; 19 | var ord = nocklib.generateRandomOrder(exchangeData); 20 | ord.stock = stocks[index]; 21 | //console.log('order', ord); 22 | if (ord.type == exch.BUY) 23 | allData[index] = exch.buy(ord.price, ord.volume, exchangeData); 24 | else 25 | allData[index] = exch.sell(ord.price, ord.volume, exchangeData); 26 | 27 | db.insertOne('transactions', ord, function(err, order) { 28 | if (exchangeData.trades && exchangeData.trades.length > 0) { 29 | nocklib.sendTrades(exchangeData.trades); 30 | var trades = exchangeData.trades.map(function(trade) { 31 | trade.init = (ord.type == exch.BUY) ? 'b' : 's'; 32 | trade.stock = stocks[index]; 33 | return trade; 34 | }); 35 | nocklib.sendExchangeData(stocks[index], exchangeData); 36 | db.insert('transactions', trades, function(err, trades) { 37 | pauseThenTrade(); 38 | }); 39 | } 40 | else 41 | pauseThenTrade(); 42 | }); 43 | 44 | function pauseThenTrade() { 45 | var pause = Math.floor(Math.random() * timeRange) + timeFloor; 46 | setTimeout(submitRandomOrder.bind(this, index), pause); 47 | //console.log(exch.getDisplay(exchangeData)); 48 | } 49 | } 50 | 51 | var app = express.createServer(); 52 | app.configure(function () { 53 | app.use(express.bodyParser()); 54 | app.use(express.cookieParser()); 55 | app.use(express.session({secret: 'secretpasswordforsessions', store: nocklib.getSessionStore()})); 56 | app.set('views', __dirname + '/views'); 57 | app.set('view engine', 'ejs'); 58 | app.use(express.static(__dirname + '/public')); 59 | }); 60 | app.configure('development', function () { 61 | app.use(express.errorHandler({dumpExceptions:true, showStack:true })); 62 | }); 63 | app.configure('production', function () { 64 | app.use(express.errorHandler()); 65 | }); 66 | app.set('view options', { 67 | layout:false 68 | }); 69 | app.get('/', nockroutes.getIndex); 70 | app.get('/api/user/:username', nockroutes.getUser); 71 | app.get('/portfolio', nocklib.ensureAuthenticated, nockroutes.portfolio); 72 | app.post('/add-stock', nockroutes.addStock); 73 | app.post('/login', nockroutes.login); 74 | app.post('/signup', nockroutes.signup); 75 | 76 | app.get('/api/trades', function(req, res) { 77 | db.find('transactions' 78 | , {init: {$exists: true}} 79 | , 100, function(err, trades) { 80 | if (err) { 81 | console.error(err); 82 | return; 83 | } 84 | var json = []; 85 | var lastTime = 0; 86 | // Highstock expects an array of arrays. Each 87 | // subarray of form [time, price] 88 | trades.reverse().forEach(function(trade) { 89 | var date = new Date(parseInt(trade._id 90 | .toString() 91 | .substring(0,8), 16)*1000); 92 | var dataPoint = [date.getTime(), trade.price]; 93 | if (date - lastTime > 1000) 94 | json.push(dataPoint); 95 | lastTime = date; 96 | }); 97 | 98 | res.json(json); 99 | }); 100 | }); 101 | 102 | db.open(function() { 103 | nocklib.createSocket(app); 104 | var port = process.env.PORT || 3000; 105 | app.listen(port); 106 | for (var i = 0; i < stocks.length; i++) { 107 | submitRandomOrder(i); 108 | } 109 | }); 110 | 111 | app.use(function(req, res){ 112 | res.render('404'); 113 | }); -------------------------------------------------------------------------------- /chapter05/views/portfolio.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 |

Welcome to the Nockmarket

26 |

Here you can manage your portfolio and view live prices.

27 |
28 | 34 |
35 |
36 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | <% for (var i=0; i 49 | 50 | 51 | <% } %> 52 | 53 |
Stock CodeLast Price
<%=portfolio[i]%><%=prices[i]%>
54 |
55 |
56 |

Howdy, I'm in Section 2.

57 |
58 |
59 |

60 |

61 |

62 |

63 |

Online:

64 |
65 |

66 |

67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 | -------------------------------------------------------------------------------- /chapter02/lib/exchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery') 4 | , BinaryHeap = require('./BinaryHeap'); 5 | 6 | var BUY = "buys", SELL = "sells"; 7 | 8 | function createBinaryHeap(orderType) { 9 | return new BinaryHeap(function (x) { 10 | return x; 11 | }, orderType); 12 | } 13 | 14 | function createExchange(exchangeData) { 15 | var cloned = $.extend(true, {}, exchangeData); 16 | cloned.trades = []; 17 | init(cloned, BUY); 18 | init(cloned, SELL); 19 | return cloned; 20 | 21 | function init(exchange, orderType) { 22 | if (!exchange[orderType]) { 23 | exchange[orderType] = {}; 24 | exchange[orderType].volumes = {}; 25 | var options = {}; 26 | if (BUY == orderType) options.max = true; 27 | exchange[orderType].prices = createBinaryHeap(options); 28 | } 29 | } 30 | } module.exports = { 31 | BUY: BUY, 32 | SELL: SELL, 33 | buy:function (price, volume, exchangeData) { 34 | return order(BUY, price, volume, exchangeData); 35 | }, 36 | sell:function (price, volume, exchangeData) { 37 | return order(SELL, price, volume, exchangeData); 38 | }, 39 | order: order, 40 | getDisplay: function(exchangeData) { 41 | var options = {max: true}; 42 | var buyPrices = createBinaryHeap(options); 43 | var sellPrices = createBinaryHeap(options); 44 | var buys = exchangeData.buys; 45 | var sells = exchangeData.sells; 46 | 47 | if (sells) { 48 | for (var price in sells.volumes) { 49 | sellPrices.push(price); 50 | } 51 | } 52 | if (buys) { 53 | for (var price in buys.volumes) { 54 | buyPrices.push(price); 55 | } 56 | } 57 | 58 | var padding = " | "; 59 | var stringBook = "\n"; 60 | 61 | while (sellPrices.size() > 0) { 62 | var sellPrice = sellPrices.pop() 63 | stringBook += padding + sellPrice + ", " + sells.volumes[sellPrice] + "\n"; 64 | } 65 | while (buyPrices.size() > 0) { 66 | var buyPrice = buyPrices.pop(); 67 | stringBook += buyPrice + ", " + buys.volumes[buyPrice] + "\n"; 68 | } 69 | stringBook += "\n\n"; 70 | for (var i=0; exchangeData.trades && i < exchangeData.trades.length; i++) { 71 | var trade = exchangeData.trades[i]; 72 | stringBook += "TRADE " + trade.volume + " @ " + trade.price + "\n"; 73 | } 74 | return stringBook; 75 | } 76 | } 77 | 78 | function order(orderType, price, volume, exchangeData) { 79 | // Init 80 | var cloned = createExchange(exchangeData); 81 | var orderBook = cloned[orderType]; 82 | var oldVolume = orderBook.volumes[price]; 83 | 84 | function getOpposite() { 85 | return (BUY == orderType) ? SELL: BUY; 86 | } 87 | function isTrade() { 88 | var opp = cloned[getOpposite()].prices.peek(); 89 | return (BUY == orderType) ? price >= opp : price <= opp; 90 | } 91 | var trade = isTrade(); 92 | var remainingVolume = volume; 93 | var storePrice = true; 94 | 95 | if (trade) { 96 | var oppBook = cloned[BUY] 97 | if (orderType == BUY) oppBook = cloned[SELL] 98 | 99 | while (remainingVolume > 0 && Object.keys(oppBook.volumes).length > 0) { 100 | var bestOppPrice = oppBook.prices.peek(); 101 | var bestOppVol = oppBook.volumes[bestOppPrice]; 102 | 103 | if (bestOppVol > remainingVolume) { 104 | cloned.trades.push({price:bestOppPrice, volume:remainingVolume}); 105 | oppBook.volumes[bestOppPrice] = oppBook.volumes[bestOppPrice] - remainingVolume; 106 | remainingVolume = 0; 107 | storePrice = false; 108 | } 109 | else { 110 | if (bestOppVol == remainingVolume) storePrice = false; 111 | cloned.trades.push({price:bestOppPrice, volume:oppBook.volumes[bestOppPrice]}); 112 | remainingVolume = remainingVolume - oppBook.volumes[bestOppPrice]; 113 | // Pop the best price from the heap 114 | oppBook.prices.pop(); 115 | delete oppBook.volumes[bestOppPrice]; 116 | } 117 | } 118 | } 119 | if (!oldVolume && storePrice) cloned[orderType].prices.push(price); 120 | 121 | var newVolume = remainingVolume; 122 | 123 | // Add to existing volume 124 | if (oldVolume) newVolume += oldVolume; 125 | if (newVolume > 0) orderBook.volumes[price] = newVolume; 126 | return cloned; 127 | } -------------------------------------------------------------------------------- /chapter03/lib/exchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery') 4 | , BinaryHeap = require('./BinaryHeap'); 5 | 6 | var BUY = "buys", SELL = "sells"; 7 | 8 | function createBinaryHeap(orderType) { 9 | return new BinaryHeap(function (x) { 10 | return x; 11 | }, orderType); 12 | } 13 | 14 | function createExchange(exchangeData) { 15 | var cloned = $.extend(true, {}, exchangeData); 16 | cloned.trades = []; 17 | init(cloned, BUY); 18 | init(cloned, SELL); 19 | return cloned; 20 | 21 | function init(exchange, orderType) { 22 | if (!exchange[orderType]) { 23 | exchange[orderType] = {}; 24 | exchange[orderType].volumes = {}; 25 | var options = {}; 26 | if (BUY == orderType) options.max = true; 27 | exchange[orderType].prices = createBinaryHeap(options); 28 | } 29 | } 30 | } module.exports = { 31 | BUY: BUY, 32 | SELL: SELL, 33 | buy:function (price, volume, exchangeData) { 34 | return order(BUY, price, volume, exchangeData); 35 | }, 36 | sell:function (price, volume, exchangeData) { 37 | return order(SELL, price, volume, exchangeData); 38 | }, 39 | order: order, 40 | getDisplay: function(exchangeData) { 41 | var options = {max: true}; 42 | var buyPrices = createBinaryHeap(options); 43 | var sellPrices = createBinaryHeap(options); 44 | var buys = exchangeData.buys; 45 | var sells = exchangeData.sells; 46 | 47 | if (sells) { 48 | for (var price in sells.volumes) { 49 | sellPrices.push(price); 50 | } 51 | } 52 | if (buys) { 53 | for (var price in buys.volumes) { 54 | buyPrices.push(price); 55 | } 56 | } 57 | 58 | var padding = " | "; 59 | var stringBook = "\n"; 60 | 61 | while (sellPrices.size() > 0) { 62 | var sellPrice = sellPrices.pop() 63 | stringBook += padding + sellPrice + ", " + sells.volumes[sellPrice] + "\n"; 64 | } 65 | while (buyPrices.size() > 0) { 66 | var buyPrice = buyPrices.pop(); 67 | stringBook += buyPrice + ", " + buys.volumes[buyPrice] + "\n"; 68 | } 69 | stringBook += "\n\n"; 70 | for (var i=0; exchangeData.trades && i < exchangeData.trades.length; i++) { 71 | var trade = exchangeData.trades[i]; 72 | stringBook += "TRADE " + trade.volume + " @ " + trade.price + "\n"; 73 | } 74 | return stringBook; 75 | } 76 | } 77 | 78 | function order(orderType, price, volume, exchangeData) { 79 | // Init 80 | var cloned = createExchange(exchangeData); 81 | var orderBook = cloned[orderType]; 82 | var oldVolume = orderBook.volumes[price]; 83 | 84 | function getOpposite() { 85 | return (BUY == orderType) ? SELL: BUY; 86 | } 87 | function isTrade() { 88 | var opp = cloned[getOpposite()].prices.peek(); 89 | return (BUY == orderType) ? price >= opp : price <= opp; 90 | } 91 | var trade = isTrade(); 92 | var remainingVolume = volume; 93 | var storePrice = true; 94 | 95 | if (trade) { 96 | var oppBook = cloned[BUY] 97 | if (orderType == BUY) oppBook = cloned[SELL] 98 | 99 | while (remainingVolume > 0 && Object.keys(oppBook.volumes).length > 0) { 100 | var bestOppPrice = oppBook.prices.peek(); 101 | var bestOppVol = oppBook.volumes[bestOppPrice]; 102 | 103 | if (bestOppVol > remainingVolume) { 104 | cloned.trades.push({price:bestOppPrice, volume:remainingVolume}); 105 | oppBook.volumes[bestOppPrice] = oppBook.volumes[bestOppPrice] - remainingVolume; 106 | remainingVolume = 0; 107 | storePrice = false; 108 | } 109 | else { 110 | if (bestOppVol == remainingVolume) storePrice = false; 111 | cloned.trades.push({price:bestOppPrice, volume:oppBook.volumes[bestOppPrice]}); 112 | remainingVolume = remainingVolume - oppBook.volumes[bestOppPrice]; 113 | // Pop the best price from the heap 114 | oppBook.prices.pop(); 115 | delete oppBook.volumes[bestOppPrice]; 116 | } 117 | } 118 | } 119 | if (!oldVolume && storePrice) cloned[orderType].prices.push(price); 120 | 121 | var newVolume = remainingVolume; 122 | 123 | // Add to existing volume 124 | if (oldVolume) newVolume += oldVolume; 125 | if (newVolume > 0) orderBook.volumes[price] = newVolume; 126 | return cloned; 127 | } -------------------------------------------------------------------------------- /chapter04/lib/exchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery') 4 | , BinaryHeap = require('./BinaryHeap'); 5 | 6 | var BUY = "buys", SELL = "sells"; 7 | 8 | function createBinaryHeap(orderType) { 9 | return new BinaryHeap(function (x) { 10 | return x; 11 | }, orderType); 12 | } 13 | 14 | function createExchange(exchangeData) { 15 | var cloned = $.extend(true, {}, exchangeData); 16 | cloned.trades = []; 17 | init(cloned, BUY); 18 | init(cloned, SELL); 19 | return cloned; 20 | 21 | function init(exchange, orderType) { 22 | if (!exchange[orderType]) { 23 | exchange[orderType] = {}; 24 | exchange[orderType].volumes = {}; 25 | var options = {}; 26 | if (BUY == orderType) options.max = true; 27 | exchange[orderType].prices = createBinaryHeap(options); 28 | } 29 | } 30 | } module.exports = { 31 | BUY: BUY, 32 | SELL: SELL, 33 | buy:function (price, volume, exchangeData) { 34 | return order(BUY, price, volume, exchangeData); 35 | }, 36 | sell:function (price, volume, exchangeData) { 37 | return order(SELL, price, volume, exchangeData); 38 | }, 39 | order: order, 40 | getDisplay: function(exchangeData) { 41 | var options = {max: true}; 42 | var buyPrices = createBinaryHeap(options); 43 | var sellPrices = createBinaryHeap(options); 44 | var buys = exchangeData.buys; 45 | var sells = exchangeData.sells; 46 | 47 | if (sells) { 48 | for (var price in sells.volumes) { 49 | sellPrices.push(price); 50 | } 51 | } 52 | if (buys) { 53 | for (var price in buys.volumes) { 54 | buyPrices.push(price); 55 | } 56 | } 57 | 58 | var padding = " | "; 59 | var stringBook = "\n"; 60 | 61 | while (sellPrices.size() > 0) { 62 | var sellPrice = sellPrices.pop() 63 | stringBook += padding + sellPrice + ", " + sells.volumes[sellPrice] + "\n"; 64 | } 65 | while (buyPrices.size() > 0) { 66 | var buyPrice = buyPrices.pop(); 67 | stringBook += buyPrice + ", " + buys.volumes[buyPrice] + "\n"; 68 | } 69 | stringBook += "\n\n"; 70 | for (var i=0; exchangeData.trades && i < exchangeData.trades.length; i++) { 71 | var trade = exchangeData.trades[i]; 72 | stringBook += "TRADE " + trade.volume + " @ " + trade.price + "\n"; 73 | } 74 | return stringBook; 75 | } 76 | } 77 | 78 | function order(orderType, price, volume, exchangeData) { 79 | // Init 80 | var cloned = createExchange(exchangeData); 81 | var orderBook = cloned[orderType]; 82 | var oldVolume = orderBook.volumes[price]; 83 | 84 | function getOpposite() { 85 | return (BUY == orderType) ? SELL: BUY; 86 | } 87 | function isTrade() { 88 | var opp = cloned[getOpposite()].prices.peek(); 89 | return (BUY == orderType) ? price >= opp : price <= opp; 90 | } 91 | var trade = isTrade(); 92 | var remainingVolume = volume; 93 | var storePrice = true; 94 | 95 | if (trade) { 96 | var oppBook = cloned[BUY] 97 | if (orderType == BUY) oppBook = cloned[SELL] 98 | 99 | while (remainingVolume > 0 && Object.keys(oppBook.volumes).length > 0) { 100 | var bestOppPrice = oppBook.prices.peek(); 101 | var bestOppVol = oppBook.volumes[bestOppPrice]; 102 | 103 | if (bestOppVol > remainingVolume) { 104 | cloned.trades.push({price:bestOppPrice, volume:remainingVolume}); 105 | oppBook.volumes[bestOppPrice] = oppBook.volumes[bestOppPrice] - remainingVolume; 106 | remainingVolume = 0; 107 | storePrice = false; 108 | } 109 | else { 110 | if (bestOppVol == remainingVolume) storePrice = false; 111 | cloned.trades.push({price:bestOppPrice, volume:oppBook.volumes[bestOppPrice]}); 112 | remainingVolume = remainingVolume - oppBook.volumes[bestOppPrice]; 113 | // Pop the best price from the heap 114 | oppBook.prices.pop(); 115 | delete oppBook.volumes[bestOppPrice]; 116 | } 117 | } 118 | } 119 | if (!oldVolume && storePrice) cloned[orderType].prices.push(price); 120 | 121 | var newVolume = remainingVolume; 122 | 123 | // Add to existing volume 124 | if (oldVolume) newVolume += oldVolume; 125 | if (newVolume > 0) orderBook.volumes[price] = newVolume; 126 | return cloned; 127 | } -------------------------------------------------------------------------------- /chapter05/lib/exchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery') 4 | , BinaryHeap = require('./BinaryHeap'); 5 | 6 | var BUY = "buys", SELL = "sells"; 7 | 8 | function createBinaryHeap(orderType) { 9 | return new BinaryHeap(function (x) { 10 | return x; 11 | }, orderType); 12 | } 13 | 14 | function createExchange(exchangeData) { 15 | var cloned = $.extend(true, {}, exchangeData); 16 | cloned.trades = []; 17 | init(cloned, BUY); 18 | init(cloned, SELL); 19 | return cloned; 20 | 21 | function init(exchange, orderType) { 22 | if (!exchange[orderType]) { 23 | exchange[orderType] = {}; 24 | exchange[orderType].volumes = {}; 25 | var options = {}; 26 | if (BUY == orderType) options.max = true; 27 | exchange[orderType].prices = createBinaryHeap(options); 28 | } 29 | } 30 | } module.exports = { 31 | BUY: BUY, 32 | SELL: SELL, 33 | buy:function (price, volume, exchangeData) { 34 | return order(BUY, price, volume, exchangeData); 35 | }, 36 | sell:function (price, volume, exchangeData) { 37 | return order(SELL, price, volume, exchangeData); 38 | }, 39 | order: order, 40 | getDisplay: function(exchangeData) { 41 | var options = {max: true}; 42 | var buyPrices = createBinaryHeap(options); 43 | var sellPrices = createBinaryHeap(options); 44 | var buys = exchangeData.buys; 45 | var sells = exchangeData.sells; 46 | 47 | if (sells) { 48 | for (var price in sells.volumes) { 49 | sellPrices.push(price); 50 | } 51 | } 52 | if (buys) { 53 | for (var price in buys.volumes) { 54 | buyPrices.push(price); 55 | } 56 | } 57 | 58 | var padding = " | "; 59 | var stringBook = "\n"; 60 | 61 | while (sellPrices.size() > 0) { 62 | var sellPrice = sellPrices.pop() 63 | stringBook += padding + sellPrice + ", " + sells.volumes[sellPrice] + "\n"; 64 | } 65 | while (buyPrices.size() > 0) { 66 | var buyPrice = buyPrices.pop(); 67 | stringBook += buyPrice + ", " + buys.volumes[buyPrice] + "\n"; 68 | } 69 | stringBook += "\n\n"; 70 | for (var i=0; exchangeData.trades && i < exchangeData.trades.length; i++) { 71 | var trade = exchangeData.trades[i]; 72 | stringBook += "TRADE " + trade.volume + " @ " + trade.price + "\n"; 73 | } 74 | return stringBook; 75 | } 76 | } 77 | 78 | function order(orderType, price, volume, exchangeData) { 79 | // Init 80 | var cloned = createExchange(exchangeData); 81 | var orderBook = cloned[orderType]; 82 | var oldVolume = orderBook.volumes[price]; 83 | 84 | function getOpposite() { 85 | return (BUY == orderType) ? SELL: BUY; 86 | } 87 | function isTrade() { 88 | var opp = cloned[getOpposite()].prices.peek(); 89 | return (BUY == orderType) ? price >= opp : price <= opp; 90 | } 91 | var trade = isTrade(); 92 | var remainingVolume = volume; 93 | var storePrice = true; 94 | 95 | if (trade) { 96 | var oppBook = cloned[BUY] 97 | if (orderType == BUY) oppBook = cloned[SELL] 98 | 99 | while (remainingVolume > 0 && Object.keys(oppBook.volumes).length > 0) { 100 | var bestOppPrice = oppBook.prices.peek(); 101 | var bestOppVol = oppBook.volumes[bestOppPrice]; 102 | 103 | if (bestOppVol > remainingVolume) { 104 | cloned.trades.push({price:bestOppPrice, volume:remainingVolume}); 105 | oppBook.volumes[bestOppPrice] = oppBook.volumes[bestOppPrice] - remainingVolume; 106 | remainingVolume = 0; 107 | storePrice = false; 108 | } 109 | else { 110 | if (bestOppVol == remainingVolume) storePrice = false; 111 | cloned.trades.push({price:bestOppPrice, volume:oppBook.volumes[bestOppPrice]}); 112 | remainingVolume = remainingVolume - oppBook.volumes[bestOppPrice]; 113 | // Pop the best price from the heap 114 | oppBook.prices.pop(); 115 | delete oppBook.volumes[bestOppPrice]; 116 | } 117 | } 118 | } 119 | if (!oldVolume && storePrice) cloned[orderType].prices.push(price); 120 | 121 | var newVolume = remainingVolume; 122 | 123 | // Add to existing volume 124 | if (oldVolume) newVolume += oldVolume; 125 | if (newVolume > 0) orderBook.volumes[price] = newVolume; 126 | return cloned; 127 | } -------------------------------------------------------------------------------- /chapter06/lib/exchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery') 4 | , BinaryHeap = require('./BinaryHeap'); 5 | 6 | var BUY = "buys", SELL = "sells"; 7 | 8 | function createBinaryHeap(orderType) { 9 | return new BinaryHeap(function (x) { 10 | return x; 11 | }, orderType); 12 | } 13 | 14 | function createExchange(exchangeData) { 15 | var cloned = $.extend(true, {}, exchangeData); 16 | cloned.trades = []; 17 | init(cloned, BUY); 18 | init(cloned, SELL); 19 | return cloned; 20 | 21 | function init(exchange, orderType) { 22 | if (!exchange[orderType]) { 23 | exchange[orderType] = {}; 24 | exchange[orderType].volumes = {}; 25 | var options = {}; 26 | if (BUY == orderType) options.max = true; 27 | exchange[orderType].prices = createBinaryHeap(options); 28 | } 29 | } 30 | } module.exports = { 31 | BUY: BUY, 32 | SELL: SELL, 33 | buy:function (price, volume, exchangeData) { 34 | return order(BUY, price, volume, exchangeData); 35 | }, 36 | sell:function (price, volume, exchangeData) { 37 | return order(SELL, price, volume, exchangeData); 38 | }, 39 | order: order, 40 | getDisplay: function(exchangeData) { 41 | var options = {max: true}; 42 | var buyPrices = createBinaryHeap(options); 43 | var sellPrices = createBinaryHeap(options); 44 | var buys = exchangeData.buys; 45 | var sells = exchangeData.sells; 46 | 47 | if (sells) { 48 | for (var price in sells.volumes) { 49 | sellPrices.push(price); 50 | } 51 | } 52 | if (buys) { 53 | for (var price in buys.volumes) { 54 | buyPrices.push(price); 55 | } 56 | } 57 | 58 | var padding = " | "; 59 | var stringBook = "\n"; 60 | 61 | while (sellPrices.size() > 0) { 62 | var sellPrice = sellPrices.pop() 63 | stringBook += padding + sellPrice + ", " + sells.volumes[sellPrice] + "\n"; 64 | } 65 | while (buyPrices.size() > 0) { 66 | var buyPrice = buyPrices.pop(); 67 | stringBook += buyPrice + ", " + buys.volumes[buyPrice] + "\n"; 68 | } 69 | stringBook += "\n\n"; 70 | for (var i=0; exchangeData.trades && i < exchangeData.trades.length; i++) { 71 | var trade = exchangeData.trades[i]; 72 | stringBook += "TRADE " + trade.volume + " @ " + trade.price + "\n"; 73 | } 74 | return stringBook; 75 | } 76 | } 77 | 78 | function order(orderType, price, volume, exchangeData) { 79 | // Init 80 | var cloned = createExchange(exchangeData); 81 | var orderBook = cloned[orderType]; 82 | var oldVolume = orderBook.volumes[price]; 83 | 84 | function getOpposite() { 85 | return (BUY == orderType) ? SELL: BUY; 86 | } 87 | function isTrade() { 88 | var opp = cloned[getOpposite()].prices.peek(); 89 | return (BUY == orderType) ? price >= opp : price <= opp; 90 | } 91 | var trade = isTrade(); 92 | var remainingVolume = volume; 93 | var storePrice = true; 94 | 95 | if (trade) { 96 | var oppBook = cloned[BUY] 97 | if (orderType == BUY) oppBook = cloned[SELL] 98 | 99 | while (remainingVolume > 0 && Object.keys(oppBook.volumes).length > 0) { 100 | var bestOppPrice = oppBook.prices.peek(); 101 | var bestOppVol = oppBook.volumes[bestOppPrice]; 102 | 103 | if (bestOppVol > remainingVolume) { 104 | cloned.trades.push({price:bestOppPrice, volume:remainingVolume}); 105 | oppBook.volumes[bestOppPrice] = oppBook.volumes[bestOppPrice] - remainingVolume; 106 | remainingVolume = 0; 107 | storePrice = false; 108 | } 109 | else { 110 | if (bestOppVol == remainingVolume) storePrice = false; 111 | cloned.trades.push({price:bestOppPrice, volume:oppBook.volumes[bestOppPrice]}); 112 | remainingVolume = remainingVolume - oppBook.volumes[bestOppPrice]; 113 | // Pop the best price from the heap 114 | oppBook.prices.pop(); 115 | delete oppBook.volumes[bestOppPrice]; 116 | } 117 | } 118 | } 119 | if (!oldVolume && storePrice) cloned[orderType].prices.push(price); 120 | 121 | var newVolume = remainingVolume; 122 | 123 | // Add to existing volume 124 | if (oldVolume) newVolume += oldVolume; 125 | if (newVolume > 0) orderBook.volumes[price] = newVolume; 126 | return cloned; 127 | } -------------------------------------------------------------------------------- /chapter07/lib/exchange.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var $ = require('jquery') 4 | , BinaryHeap = require('./BinaryHeap'); 5 | 6 | var BUY = "buys", SELL = "sells"; 7 | 8 | function createBinaryHeap(orderType) { 9 | return new BinaryHeap(function (x) { 10 | return x; 11 | }, orderType); 12 | } 13 | 14 | function createExchange(exchangeData) { 15 | var cloned = $.extend(true, {}, exchangeData); 16 | cloned.trades = []; 17 | init(cloned, BUY); 18 | init(cloned, SELL); 19 | return cloned; 20 | 21 | function init(exchange, orderType) { 22 | if (!exchange[orderType]) { 23 | exchange[orderType] = {}; 24 | exchange[orderType].volumes = {}; 25 | var options = {}; 26 | if (BUY == orderType) options.max = true; 27 | exchange[orderType].prices = createBinaryHeap(options); 28 | } 29 | } 30 | } module.exports = { 31 | BUY: BUY, 32 | SELL: SELL, 33 | buy:function (price, volume, exchangeData) { 34 | return order(BUY, price, volume, exchangeData); 35 | }, 36 | sell:function (price, volume, exchangeData) { 37 | return order(SELL, price, volume, exchangeData); 38 | }, 39 | order: order, 40 | getDisplay: function(exchangeData) { 41 | var options = {max: true}; 42 | var buyPrices = createBinaryHeap(options); 43 | var sellPrices = createBinaryHeap(options); 44 | var buys = exchangeData.buys; 45 | var sells = exchangeData.sells; 46 | 47 | if (sells) { 48 | for (var price in sells.volumes) { 49 | sellPrices.push(price); 50 | } 51 | } 52 | if (buys) { 53 | for (var price in buys.volumes) { 54 | buyPrices.push(price); 55 | } 56 | } 57 | 58 | var padding = " | "; 59 | var stringBook = "\n"; 60 | 61 | while (sellPrices.size() > 0) { 62 | var sellPrice = sellPrices.pop() 63 | stringBook += padding + sellPrice + ", " + sells.volumes[sellPrice] + "\n"; 64 | } 65 | while (buyPrices.size() > 0) { 66 | var buyPrice = buyPrices.pop(); 67 | stringBook += buyPrice + ", " + buys.volumes[buyPrice] + "\n"; 68 | } 69 | stringBook += "\n\n"; 70 | for (var i=0; exchangeData.trades && i < exchangeData.trades.length; i++) { 71 | var trade = exchangeData.trades[i]; 72 | stringBook += "TRADE " + trade.volume + " @ " + trade.price + "\n"; 73 | } 74 | return stringBook; 75 | } 76 | } 77 | 78 | function order(orderType, price, volume, exchangeData) { 79 | // Init 80 | var cloned = createExchange(exchangeData); 81 | var orderBook = cloned[orderType]; 82 | var oldVolume = orderBook.volumes[price]; 83 | 84 | function getOpposite() { 85 | return (BUY == orderType) ? SELL: BUY; 86 | } 87 | function isTrade() { 88 | var opp = cloned[getOpposite()].prices.peek(); 89 | return (BUY == orderType) ? price >= opp : price <= opp; 90 | } 91 | var trade = isTrade(); 92 | var remainingVolume = volume; 93 | var storePrice = true; 94 | 95 | if (trade) { 96 | var oppBook = cloned[BUY] 97 | if (orderType == BUY) oppBook = cloned[SELL] 98 | 99 | while (remainingVolume > 0 && Object.keys(oppBook.volumes).length > 0) { 100 | var bestOppPrice = oppBook.prices.peek(); 101 | var bestOppVol = oppBook.volumes[bestOppPrice]; 102 | 103 | if (bestOppVol > remainingVolume) { 104 | cloned.trades.push({price:bestOppPrice, volume:remainingVolume}); 105 | oppBook.volumes[bestOppPrice] = oppBook.volumes[bestOppPrice] - remainingVolume; 106 | remainingVolume = 0; 107 | storePrice = false; 108 | } 109 | else { 110 | if (bestOppVol == remainingVolume) storePrice = false; 111 | cloned.trades.push({price:bestOppPrice, volume:oppBook.volumes[bestOppPrice]}); 112 | remainingVolume = remainingVolume - oppBook.volumes[bestOppPrice]; 113 | // Pop the best price from the heap 114 | oppBook.prices.pop(); 115 | delete oppBook.volumes[bestOppPrice]; 116 | } 117 | } 118 | } 119 | if (!oldVolume && storePrice) cloned[orderType].prices.push(price); 120 | 121 | var newVolume = remainingVolume; 122 | 123 | // Add to existing volume 124 | if (oldVolume) newVolume += oldVolume; 125 | if (newVolume > 0) orderBook.volumes[price] = newVolume; 126 | return cloned; 127 | } -------------------------------------------------------------------------------- /chapter04/lib/nocklib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto') 4 | , db = require('./db') 5 | , exchange = require('./exchange') 6 | , http = require('http') 7 | , ObjectID = require('mongodb').ObjectID 8 | , priceFloor = 35 9 | , priceRange = 10 10 | , volFloor = 80 11 | , volRange = 40; 12 | 13 | module.exports = { 14 | addStock: function(uid, stock, callback) { 15 | function doCallback() { 16 | counter++; 17 | if (counter == 2) { 18 | callback(null, price); 19 | } 20 | } 21 | 22 | var counter = 0; 23 | var price; 24 | 25 | module.exports.getStockPrices([stock], function(err, retrieved) { 26 | price = retrieved[0]; 27 | doCallback(); 28 | }); 29 | db.push('users', new ObjectID(uid), {portfolio: stock}, doCallback); 30 | }, 31 | 32 | authenticate: function(username, password, callback) { 33 | db.findOne('users', {username: username}, function(err, user) { 34 | if (user && (user.password === encryptPassword(password))) 35 | callback(err, user._id); 36 | else 37 | callback(err, null); 38 | }); 39 | }, 40 | 41 | createUser: function(username, email, password, callback) { 42 | var user = {username: username, email: email, password: encryptPassword(password)}; 43 | db.insertOne('users', user, callback); 44 | }, 45 | 46 | ensureAuthenticated: function (req, res, next) { 47 | if (req.session._id) { 48 | return next(); 49 | } 50 | res.redirect('/'); 51 | }, 52 | 53 | generateRandomOrder: function(exchangeData) { 54 | var order = {}; 55 | if (Math.random() > 0.5) 56 | order.type = exchange.BUY 57 | else 58 | order.type = exchange.SELL 59 | 60 | var buyExists = exchangeData.buys 61 | && exchangeData.buys.prices.peek(); 62 | var sellExists = exchangeData.sells 63 | && exchangeData.sells.prices.peek(); 64 | 65 | var ran = Math.random(); 66 | if (!buyExists && !sellExists) 67 | order.price = Math.floor(ran * priceRange) + priceFloor; 68 | else if (buyExists && sellExists) { 69 | if (Math.random() > 0.5) 70 | order.price = exchangeData.buys.prices.peek(); 71 | else 72 | order.price = exchangeData.sells.prices.peek(); 73 | } else if (buyExists) { 74 | order.price = exchangeData.buys.prices.peek(); 75 | } else { 76 | order.price = exchangeData.sells.prices.peek(); 77 | } 78 | 79 | var shift = Math.floor(Math.random() * priceRange / 2); 80 | 81 | if (Math.random() > 0.5) 82 | order.price += shift; 83 | else 84 | order.price -= shift; 85 | order.volume = Math.floor(Math.random() * volRange) + volFloor 86 | return order; 87 | }, 88 | 89 | getStockPrices: function(stocks, callback) { 90 | var stockList = ''; 91 | stocks.forEach(function(stock) { 92 | stockList += stock + ','; 93 | }); 94 | 95 | var options = { 96 | host: 'download.finance.yahoo.com', 97 | port: 80, 98 | path: '/d/quotes.csv?s=' + stockList + '&f=sl1c1d1&e=.csv' 99 | }; 100 | 101 | http.get(options, function(res) { 102 | var data = ''; 103 | res.on('data', function(chunk) { 104 | data += chunk.toString(); 105 | }) 106 | .on('error', function(err) { 107 | console.err('Error retrieving Yahoo stock prices'); 108 | throw err; 109 | }) 110 | .on('end', function() { 111 | var tokens = data.split('\r\n'); 112 | var prices = []; 113 | tokens.forEach(function(line) { 114 | var price = line.split(",")[1]; 115 | if (price) 116 | prices.push(price); 117 | }); 118 | callback(null, prices); 119 | }); 120 | }); 121 | }, 122 | 123 | getUserById: function(id, callback) { 124 | db.findOne('users', {_id: new ObjectID(id)}, callback); 125 | }, 126 | 127 | getUser: function(username, callback) { 128 | db.findOne('users', {username: username}, callback); 129 | } 130 | } 131 | 132 | function encryptPassword(plainText) { 133 | return crypto.createHash('md5').update(plainText).digest('hex'); 134 | } -------------------------------------------------------------------------------- /chapter04/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 40 | 61 |
62 |

Nockmarket

63 | 65 |
66 |
68 |

Real Prices

69 |

Here you will be able to sign up for an account and 70 | create a stock portfolio with live prices. This will 71 | combine the beauty of Bootstrap with the performance of 72 | Node.js to create a well designed, blazing fast stock 73 | tracking tool. 74 |

75 |
76 |
78 |

Live Data

79 |

We will also construct a dummy portfolio to show you how 80 | to stream real-time data straight into your browser. Once 81 | you have mastered the basics you will be able to stream 82 | any type of data from tweets, to chat to auction data. 83 |

84 |
85 |
86 |
87 |
88 |
89 |

90 | Log In 91 | Sign Up 92 |

93 |
94 |
95 |
96 | 97 | -------------------------------------------------------------------------------- /chapter05/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 40 | 61 |
62 |

Nockmarket

63 | 65 |
66 |
68 |

Real Prices

69 |

Here you will be able to sign up for an account and 70 | create a stock portfolio with live prices. This will 71 | combine the beauty of Bootstrap with the performance of 72 | Node.js to create a well designed, blazing fast stock 73 | tracking tool. 74 |

75 |
76 |
78 |

Live Data

79 |

We will also construct a dummy portfolio to show you how 80 | to stream real-time data straight into your browser. Once 81 | you have mastered the basics you will be able to stream 82 | any type of data from tweets, to chat to auction data. 83 |

84 |
85 |
86 |
87 |
88 |
89 |

90 | Log In 91 | Sign Up 92 |

93 |
94 |
95 |
96 | 97 | -------------------------------------------------------------------------------- /chapter06/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 40 | 61 |
62 |

Nockmarket

63 | 65 |
66 |
68 |

Real Prices

69 |

Here you will be able to sign up for an account and 70 | create a stock portfolio with live prices. This will 71 | combine the beauty of Bootstrap with the performance of 72 | Node.js to create a well designed, blazing fast stock 73 | tracking tool. 74 |

75 |
76 |
78 |

Live Data

79 |

We will also construct a dummy portfolio to show you how 80 | to stream real-time data straight into your browser. Once 81 | you have mastered the basics you will be able to stream 82 | any type of data from tweets, to chat to auction data. 83 |

84 |
85 |
86 |
87 |
88 |
89 |

90 | Log In 91 | Sign Up 92 |

93 |
94 |
95 |
96 | 97 | -------------------------------------------------------------------------------- /chapter07/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 40 | 61 |
62 |

Nockmarket

63 | 65 |
66 |
68 |

Real Prices

69 |

Here you will be able to sign up for an account and 70 | create a stock portfolio with live prices. This will 71 | combine the beauty of Bootstrap with the performance of 72 | Node.js to create a well designed, blazing fast stock 73 | tracking tool. 74 |

75 |
76 |
78 |

Live Data

79 |

We will also construct a dummy portfolio to show you how 80 | to stream real-time data straight into your browser. Once 81 | you have mastered the basics you will be able to stream 82 | any type of data from tweets, to chat to auction data. 83 |

84 |
85 |
86 |
87 |
88 |
89 |

90 | Log In 91 | Sign Up 92 |

93 |
94 |
95 |
96 | 97 | -------------------------------------------------------------------------------- /chapter02/lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var exchange = require('./exchange'); 2 | 3 | module.exports = BinaryHeap; 4 | 5 | function BinaryHeap(scoreFunction, options){ 6 | this.content = []; 7 | if (options) 8 | this.options = options; 9 | else 10 | this.options = {}; 11 | this.scoreFunction = scoreFunction; 12 | } 13 | 14 | BinaryHeap.prototype = { 15 | 16 | all: function() { 17 | return this.content; 18 | }, 19 | peek: function() { 20 | if (this.options.max) 21 | return -this.content[0]; 22 | else 23 | return this.content[0]; 24 | }, 25 | 26 | push: function(element) { 27 | // Add the new element to the end of the array. 28 | if (this.options.max) { 29 | this.content.push(-element); 30 | } 31 | else 32 | this.content.push(element); 33 | // Allow it to bubble up. 34 | this.bubbleUp(this.content.length - 1); 35 | }, 36 | 37 | pop: function() { 38 | // Store the first element so we can return it later. 39 | var result = this.content[0]; 40 | // Get the element at the end of the array. 41 | var end = this.content.pop(); 42 | // If there are any elements left, put the end element at the 43 | // start, and let it sink down. 44 | if (this.content.length > 0) { 45 | this.content[0] = end; 46 | this.sinkDown(0); 47 | } 48 | if (this.options.max) 49 | return -result; 50 | else 51 | return result; 52 | }, 53 | 54 | remove: function(node) { 55 | if (exchange.BUY == this.orderType) node = -node; 56 | var len = this.content.length; 57 | // To remove a value, we must search through the array to find 58 | // it. 59 | for (var i = 0; i < len; i++) { 60 | if (this.content[i] == node) { 61 | // When it is found, the process seen in 'pop' is repeated 62 | // to fill up the hole. 63 | var end = this.content.pop(); 64 | if (i != len - 1) { 65 | this.content[i] = end; 66 | if (this.scoreFunction(end) < this.scoreFunction(node)) 67 | this.bubbleUp(i); 68 | else 69 | this.sinkDown(i); 70 | } 71 | return; 72 | } 73 | } 74 | throw new Error("Node not found."); 75 | }, 76 | 77 | size: function() { 78 | return this.content.length; 79 | }, 80 | 81 | bubbleUp: function(n) { 82 | // Fetch the element that has to be moved. 83 | var element = this.content[n]; 84 | // When at 0, an element can not go up any further. 85 | while (n > 0) { 86 | // Compute the parent element's index, and fetch it. 87 | var parentN = Math.floor((n + 1) / 2) - 1, 88 | parent = this.content[parentN]; 89 | // Swap the elements if the parent is greater. 90 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 91 | this.content[parentN] = element; 92 | this.content[n] = parent; 93 | // Update 'n' to continue at the new position. 94 | n = parentN; 95 | } 96 | // Found a parent that is less, no need to move it further. 97 | else { 98 | break; 99 | } 100 | } 101 | }, 102 | 103 | sinkDown: function(n) { 104 | // Look up the target element and its score. 105 | var length = this.content.length, 106 | element = this.content[n], 107 | elemScore = this.scoreFunction(element); 108 | 109 | while(true) { 110 | // Compute the indices of the child elements. 111 | var child2N = (n + 1) * 2, child1N = child2N - 1; 112 | // This is used to store the new position of the element, 113 | // if any. 114 | var swap = null; 115 | // If the first child exists (is inside the array)... 116 | if (child1N < length) { 117 | // Look it up and compute its score. 118 | var child1 = this.content[child1N], 119 | child1Score = this.scoreFunction(child1); 120 | // If the score is less than our element's, we need to swap. 121 | if (child1Score < elemScore) 122 | swap = child1N; 123 | } 124 | // Do the same checks for the other child. 125 | if (child2N < length) { 126 | var child2 = this.content[child2N], 127 | child2Score = this.scoreFunction(child2); 128 | if (child2Score < (swap == null ? elemScore : child1Score)) 129 | swap = child2N; 130 | } 131 | 132 | // If the element needs to be moved, swap it, and continue. 133 | if (swap != null) { 134 | this.content[n] = this.content[swap]; 135 | this.content[swap] = element; 136 | n = swap; 137 | } 138 | // Otherwise, we are done. 139 | else { 140 | break; 141 | } 142 | } 143 | } 144 | }; -------------------------------------------------------------------------------- /chapter03/lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var exchange = require('./exchange'); 2 | 3 | module.exports = BinaryHeap; 4 | 5 | function BinaryHeap(scoreFunction, options){ 6 | this.content = []; 7 | if (options) 8 | this.options = options; 9 | else 10 | this.options = {}; 11 | this.scoreFunction = scoreFunction; 12 | } 13 | 14 | BinaryHeap.prototype = { 15 | 16 | all: function() { 17 | return this.content; 18 | }, 19 | peek: function() { 20 | if (this.options.max) 21 | return -this.content[0]; 22 | else 23 | return this.content[0]; 24 | }, 25 | 26 | push: function(element) { 27 | // Add the new element to the end of the array. 28 | if (this.options.max) { 29 | this.content.push(-element); 30 | } 31 | else 32 | this.content.push(element); 33 | // Allow it to bubble up. 34 | this.bubbleUp(this.content.length - 1); 35 | }, 36 | 37 | pop: function() { 38 | // Store the first element so we can return it later. 39 | var result = this.content[0]; 40 | // Get the element at the end of the array. 41 | var end = this.content.pop(); 42 | // If there are any elements left, put the end element at the 43 | // start, and let it sink down. 44 | if (this.content.length > 0) { 45 | this.content[0] = end; 46 | this.sinkDown(0); 47 | } 48 | if (this.options.max) 49 | return -result; 50 | else 51 | return result; 52 | }, 53 | 54 | remove: function(node) { 55 | if (exchange.BUY == this.orderType) node = -node; 56 | var len = this.content.length; 57 | // To remove a value, we must search through the array to find 58 | // it. 59 | for (var i = 0; i < len; i++) { 60 | if (this.content[i] == node) { 61 | // When it is found, the process seen in 'pop' is repeated 62 | // to fill up the hole. 63 | var end = this.content.pop(); 64 | if (i != len - 1) { 65 | this.content[i] = end; 66 | if (this.scoreFunction(end) < this.scoreFunction(node)) 67 | this.bubbleUp(i); 68 | else 69 | this.sinkDown(i); 70 | } 71 | return; 72 | } 73 | } 74 | throw new Error("Node not found."); 75 | }, 76 | 77 | size: function() { 78 | return this.content.length; 79 | }, 80 | 81 | bubbleUp: function(n) { 82 | // Fetch the element that has to be moved. 83 | var element = this.content[n]; 84 | // When at 0, an element can not go up any further. 85 | while (n > 0) { 86 | // Compute the parent element's index, and fetch it. 87 | var parentN = Math.floor((n + 1) / 2) - 1, 88 | parent = this.content[parentN]; 89 | // Swap the elements if the parent is greater. 90 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 91 | this.content[parentN] = element; 92 | this.content[n] = parent; 93 | // Update 'n' to continue at the new position. 94 | n = parentN; 95 | } 96 | // Found a parent that is less, no need to move it further. 97 | else { 98 | break; 99 | } 100 | } 101 | }, 102 | 103 | sinkDown: function(n) { 104 | // Look up the target element and its score. 105 | var length = this.content.length, 106 | element = this.content[n], 107 | elemScore = this.scoreFunction(element); 108 | 109 | while(true) { 110 | // Compute the indices of the child elements. 111 | var child2N = (n + 1) * 2, child1N = child2N - 1; 112 | // This is used to store the new position of the element, 113 | // if any. 114 | var swap = null; 115 | // If the first child exists (is inside the array)... 116 | if (child1N < length) { 117 | // Look it up and compute its score. 118 | var child1 = this.content[child1N], 119 | child1Score = this.scoreFunction(child1); 120 | // If the score is less than our element's, we need to swap. 121 | if (child1Score < elemScore) 122 | swap = child1N; 123 | } 124 | // Do the same checks for the other child. 125 | if (child2N < length) { 126 | var child2 = this.content[child2N], 127 | child2Score = this.scoreFunction(child2); 128 | if (child2Score < (swap == null ? elemScore : child1Score)) 129 | swap = child2N; 130 | } 131 | 132 | // If the element needs to be moved, swap it, and continue. 133 | if (swap != null) { 134 | this.content[n] = this.content[swap]; 135 | this.content[swap] = element; 136 | n = swap; 137 | } 138 | // Otherwise, we are done. 139 | else { 140 | break; 141 | } 142 | } 143 | } 144 | }; -------------------------------------------------------------------------------- /chapter04/lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var exchange = require('./exchange'); 2 | 3 | module.exports = BinaryHeap; 4 | 5 | function BinaryHeap(scoreFunction, options){ 6 | this.content = []; 7 | if (options) 8 | this.options = options; 9 | else 10 | this.options = {}; 11 | this.scoreFunction = scoreFunction; 12 | } 13 | 14 | BinaryHeap.prototype = { 15 | 16 | all: function() { 17 | return this.content; 18 | }, 19 | peek: function() { 20 | if (this.options.max) 21 | return -this.content[0]; 22 | else 23 | return this.content[0]; 24 | }, 25 | 26 | push: function(element) { 27 | // Add the new element to the end of the array. 28 | if (this.options.max) { 29 | this.content.push(-element); 30 | } 31 | else 32 | this.content.push(element); 33 | // Allow it to bubble up. 34 | this.bubbleUp(this.content.length - 1); 35 | }, 36 | 37 | pop: function() { 38 | // Store the first element so we can return it later. 39 | var result = this.content[0]; 40 | // Get the element at the end of the array. 41 | var end = this.content.pop(); 42 | // If there are any elements left, put the end element at the 43 | // start, and let it sink down. 44 | if (this.content.length > 0) { 45 | this.content[0] = end; 46 | this.sinkDown(0); 47 | } 48 | if (this.options.max) 49 | return -result; 50 | else 51 | return result; 52 | }, 53 | 54 | remove: function(node) { 55 | if (exchange.BUY == this.orderType) node = -node; 56 | var len = this.content.length; 57 | // To remove a value, we must search through the array to find 58 | // it. 59 | for (var i = 0; i < len; i++) { 60 | if (this.content[i] == node) { 61 | // When it is found, the process seen in 'pop' is repeated 62 | // to fill up the hole. 63 | var end = this.content.pop(); 64 | if (i != len - 1) { 65 | this.content[i] = end; 66 | if (this.scoreFunction(end) < this.scoreFunction(node)) 67 | this.bubbleUp(i); 68 | else 69 | this.sinkDown(i); 70 | } 71 | return; 72 | } 73 | } 74 | throw new Error("Node not found."); 75 | }, 76 | 77 | size: function() { 78 | return this.content.length; 79 | }, 80 | 81 | bubbleUp: function(n) { 82 | // Fetch the element that has to be moved. 83 | var element = this.content[n]; 84 | // When at 0, an element can not go up any further. 85 | while (n > 0) { 86 | // Compute the parent element's index, and fetch it. 87 | var parentN = Math.floor((n + 1) / 2) - 1, 88 | parent = this.content[parentN]; 89 | // Swap the elements if the parent is greater. 90 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 91 | this.content[parentN] = element; 92 | this.content[n] = parent; 93 | // Update 'n' to continue at the new position. 94 | n = parentN; 95 | } 96 | // Found a parent that is less, no need to move it further. 97 | else { 98 | break; 99 | } 100 | } 101 | }, 102 | 103 | sinkDown: function(n) { 104 | // Look up the target element and its score. 105 | var length = this.content.length, 106 | element = this.content[n], 107 | elemScore = this.scoreFunction(element); 108 | 109 | while(true) { 110 | // Compute the indices of the child elements. 111 | var child2N = (n + 1) * 2, child1N = child2N - 1; 112 | // This is used to store the new position of the element, 113 | // if any. 114 | var swap = null; 115 | // If the first child exists (is inside the array)... 116 | if (child1N < length) { 117 | // Look it up and compute its score. 118 | var child1 = this.content[child1N], 119 | child1Score = this.scoreFunction(child1); 120 | // If the score is less than our element's, we need to swap. 121 | if (child1Score < elemScore) 122 | swap = child1N; 123 | } 124 | // Do the same checks for the other child. 125 | if (child2N < length) { 126 | var child2 = this.content[child2N], 127 | child2Score = this.scoreFunction(child2); 128 | if (child2Score < (swap == null ? elemScore : child1Score)) 129 | swap = child2N; 130 | } 131 | 132 | // If the element needs to be moved, swap it, and continue. 133 | if (swap != null) { 134 | this.content[n] = this.content[swap]; 135 | this.content[swap] = element; 136 | n = swap; 137 | } 138 | // Otherwise, we are done. 139 | else { 140 | break; 141 | } 142 | } 143 | } 144 | }; -------------------------------------------------------------------------------- /chapter05/lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var exchange = require('./exchange'); 2 | 3 | module.exports = BinaryHeap; 4 | 5 | function BinaryHeap(scoreFunction, options){ 6 | this.content = []; 7 | if (options) 8 | this.options = options; 9 | else 10 | this.options = {}; 11 | this.scoreFunction = scoreFunction; 12 | } 13 | 14 | BinaryHeap.prototype = { 15 | 16 | all: function() { 17 | return this.content; 18 | }, 19 | peek: function() { 20 | if (this.options.max) 21 | return -this.content[0]; 22 | else 23 | return this.content[0]; 24 | }, 25 | 26 | push: function(element) { 27 | // Add the new element to the end of the array. 28 | if (this.options.max) { 29 | this.content.push(-element); 30 | } 31 | else 32 | this.content.push(element); 33 | // Allow it to bubble up. 34 | this.bubbleUp(this.content.length - 1); 35 | }, 36 | 37 | pop: function() { 38 | // Store the first element so we can return it later. 39 | var result = this.content[0]; 40 | // Get the element at the end of the array. 41 | var end = this.content.pop(); 42 | // If there are any elements left, put the end element at the 43 | // start, and let it sink down. 44 | if (this.content.length > 0) { 45 | this.content[0] = end; 46 | this.sinkDown(0); 47 | } 48 | if (this.options.max) 49 | return -result; 50 | else 51 | return result; 52 | }, 53 | 54 | remove: function(node) { 55 | if (exchange.BUY == this.orderType) node = -node; 56 | var len = this.content.length; 57 | // To remove a value, we must search through the array to find 58 | // it. 59 | for (var i = 0; i < len; i++) { 60 | if (this.content[i] == node) { 61 | // When it is found, the process seen in 'pop' is repeated 62 | // to fill up the hole. 63 | var end = this.content.pop(); 64 | if (i != len - 1) { 65 | this.content[i] = end; 66 | if (this.scoreFunction(end) < this.scoreFunction(node)) 67 | this.bubbleUp(i); 68 | else 69 | this.sinkDown(i); 70 | } 71 | return; 72 | } 73 | } 74 | throw new Error("Node not found."); 75 | }, 76 | 77 | size: function() { 78 | return this.content.length; 79 | }, 80 | 81 | bubbleUp: function(n) { 82 | // Fetch the element that has to be moved. 83 | var element = this.content[n]; 84 | // When at 0, an element can not go up any further. 85 | while (n > 0) { 86 | // Compute the parent element's index, and fetch it. 87 | var parentN = Math.floor((n + 1) / 2) - 1, 88 | parent = this.content[parentN]; 89 | // Swap the elements if the parent is greater. 90 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 91 | this.content[parentN] = element; 92 | this.content[n] = parent; 93 | // Update 'n' to continue at the new position. 94 | n = parentN; 95 | } 96 | // Found a parent that is less, no need to move it further. 97 | else { 98 | break; 99 | } 100 | } 101 | }, 102 | 103 | sinkDown: function(n) { 104 | // Look up the target element and its score. 105 | var length = this.content.length, 106 | element = this.content[n], 107 | elemScore = this.scoreFunction(element); 108 | 109 | while(true) { 110 | // Compute the indices of the child elements. 111 | var child2N = (n + 1) * 2, child1N = child2N - 1; 112 | // This is used to store the new position of the element, 113 | // if any. 114 | var swap = null; 115 | // If the first child exists (is inside the array)... 116 | if (child1N < length) { 117 | // Look it up and compute its score. 118 | var child1 = this.content[child1N], 119 | child1Score = this.scoreFunction(child1); 120 | // If the score is less than our element's, we need to swap. 121 | if (child1Score < elemScore) 122 | swap = child1N; 123 | } 124 | // Do the same checks for the other child. 125 | if (child2N < length) { 126 | var child2 = this.content[child2N], 127 | child2Score = this.scoreFunction(child2); 128 | if (child2Score < (swap == null ? elemScore : child1Score)) 129 | swap = child2N; 130 | } 131 | 132 | // If the element needs to be moved, swap it, and continue. 133 | if (swap != null) { 134 | this.content[n] = this.content[swap]; 135 | this.content[swap] = element; 136 | n = swap; 137 | } 138 | // Otherwise, we are done. 139 | else { 140 | break; 141 | } 142 | } 143 | } 144 | }; -------------------------------------------------------------------------------- /chapter06/lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var exchange = require('./exchange'); 2 | 3 | module.exports = BinaryHeap; 4 | 5 | function BinaryHeap(scoreFunction, options){ 6 | this.content = []; 7 | if (options) 8 | this.options = options; 9 | else 10 | this.options = {}; 11 | this.scoreFunction = scoreFunction; 12 | } 13 | 14 | BinaryHeap.prototype = { 15 | 16 | all: function() { 17 | return this.content; 18 | }, 19 | peek: function() { 20 | if (this.options.max) 21 | return -this.content[0]; 22 | else 23 | return this.content[0]; 24 | }, 25 | 26 | push: function(element) { 27 | // Add the new element to the end of the array. 28 | if (this.options.max) { 29 | this.content.push(-element); 30 | } 31 | else 32 | this.content.push(element); 33 | // Allow it to bubble up. 34 | this.bubbleUp(this.content.length - 1); 35 | }, 36 | 37 | pop: function() { 38 | // Store the first element so we can return it later. 39 | var result = this.content[0]; 40 | // Get the element at the end of the array. 41 | var end = this.content.pop(); 42 | // If there are any elements left, put the end element at the 43 | // start, and let it sink down. 44 | if (this.content.length > 0) { 45 | this.content[0] = end; 46 | this.sinkDown(0); 47 | } 48 | if (this.options.max) 49 | return -result; 50 | else 51 | return result; 52 | }, 53 | 54 | remove: function(node) { 55 | if (exchange.BUY == this.orderType) node = -node; 56 | var len = this.content.length; 57 | // To remove a value, we must search through the array to find 58 | // it. 59 | for (var i = 0; i < len; i++) { 60 | if (this.content[i] == node) { 61 | // When it is found, the process seen in 'pop' is repeated 62 | // to fill up the hole. 63 | var end = this.content.pop(); 64 | if (i != len - 1) { 65 | this.content[i] = end; 66 | if (this.scoreFunction(end) < this.scoreFunction(node)) 67 | this.bubbleUp(i); 68 | else 69 | this.sinkDown(i); 70 | } 71 | return; 72 | } 73 | } 74 | throw new Error("Node not found."); 75 | }, 76 | 77 | size: function() { 78 | return this.content.length; 79 | }, 80 | 81 | bubbleUp: function(n) { 82 | // Fetch the element that has to be moved. 83 | var element = this.content[n]; 84 | // When at 0, an element can not go up any further. 85 | while (n > 0) { 86 | // Compute the parent element's index, and fetch it. 87 | var parentN = Math.floor((n + 1) / 2) - 1, 88 | parent = this.content[parentN]; 89 | // Swap the elements if the parent is greater. 90 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 91 | this.content[parentN] = element; 92 | this.content[n] = parent; 93 | // Update 'n' to continue at the new position. 94 | n = parentN; 95 | } 96 | // Found a parent that is less, no need to move it further. 97 | else { 98 | break; 99 | } 100 | } 101 | }, 102 | 103 | sinkDown: function(n) { 104 | // Look up the target element and its score. 105 | var length = this.content.length, 106 | element = this.content[n], 107 | elemScore = this.scoreFunction(element); 108 | 109 | while(true) { 110 | // Compute the indices of the child elements. 111 | var child2N = (n + 1) * 2, child1N = child2N - 1; 112 | // This is used to store the new position of the element, 113 | // if any. 114 | var swap = null; 115 | // If the first child exists (is inside the array)... 116 | if (child1N < length) { 117 | // Look it up and compute its score. 118 | var child1 = this.content[child1N], 119 | child1Score = this.scoreFunction(child1); 120 | // If the score is less than our element's, we need to swap. 121 | if (child1Score < elemScore) 122 | swap = child1N; 123 | } 124 | // Do the same checks for the other child. 125 | if (child2N < length) { 126 | var child2 = this.content[child2N], 127 | child2Score = this.scoreFunction(child2); 128 | if (child2Score < (swap == null ? elemScore : child1Score)) 129 | swap = child2N; 130 | } 131 | 132 | // If the element needs to be moved, swap it, and continue. 133 | if (swap != null) { 134 | this.content[n] = this.content[swap]; 135 | this.content[swap] = element; 136 | n = swap; 137 | } 138 | // Otherwise, we are done. 139 | else { 140 | break; 141 | } 142 | } 143 | } 144 | }; -------------------------------------------------------------------------------- /chapter07/lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var exchange = require('./exchange'); 2 | 3 | module.exports = BinaryHeap; 4 | 5 | function BinaryHeap(scoreFunction, options){ 6 | this.content = []; 7 | if (options) 8 | this.options = options; 9 | else 10 | this.options = {}; 11 | this.scoreFunction = scoreFunction; 12 | } 13 | 14 | BinaryHeap.prototype = { 15 | 16 | all: function() { 17 | return this.content; 18 | }, 19 | peek: function() { 20 | if (this.options.max) 21 | return -this.content[0]; 22 | else 23 | return this.content[0]; 24 | }, 25 | 26 | push: function(element) { 27 | // Add the new element to the end of the array. 28 | if (this.options.max) { 29 | this.content.push(-element); 30 | } 31 | else 32 | this.content.push(element); 33 | // Allow it to bubble up. 34 | this.bubbleUp(this.content.length - 1); 35 | }, 36 | 37 | pop: function() { 38 | // Store the first element so we can return it later. 39 | var result = this.content[0]; 40 | // Get the element at the end of the array. 41 | var end = this.content.pop(); 42 | // If there are any elements left, put the end element at the 43 | // start, and let it sink down. 44 | if (this.content.length > 0) { 45 | this.content[0] = end; 46 | this.sinkDown(0); 47 | } 48 | if (this.options.max) 49 | return -result; 50 | else 51 | return result; 52 | }, 53 | 54 | remove: function(node) { 55 | if (exchange.BUY == this.orderType) node = -node; 56 | var len = this.content.length; 57 | // To remove a value, we must search through the array to find 58 | // it. 59 | for (var i = 0; i < len; i++) { 60 | if (this.content[i] == node) { 61 | // When it is found, the process seen in 'pop' is repeated 62 | // to fill up the hole. 63 | var end = this.content.pop(); 64 | if (i != len - 1) { 65 | this.content[i] = end; 66 | if (this.scoreFunction(end) < this.scoreFunction(node)) 67 | this.bubbleUp(i); 68 | else 69 | this.sinkDown(i); 70 | } 71 | return; 72 | } 73 | } 74 | throw new Error("Node not found."); 75 | }, 76 | 77 | size: function() { 78 | return this.content.length; 79 | }, 80 | 81 | bubbleUp: function(n) { 82 | // Fetch the element that has to be moved. 83 | var element = this.content[n]; 84 | // When at 0, an element can not go up any further. 85 | while (n > 0) { 86 | // Compute the parent element's index, and fetch it. 87 | var parentN = Math.floor((n + 1) / 2) - 1, 88 | parent = this.content[parentN]; 89 | // Swap the elements if the parent is greater. 90 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 91 | this.content[parentN] = element; 92 | this.content[n] = parent; 93 | // Update 'n' to continue at the new position. 94 | n = parentN; 95 | } 96 | // Found a parent that is less, no need to move it further. 97 | else { 98 | break; 99 | } 100 | } 101 | }, 102 | 103 | sinkDown: function(n) { 104 | // Look up the target element and its score. 105 | var length = this.content.length, 106 | element = this.content[n], 107 | elemScore = this.scoreFunction(element); 108 | 109 | while(true) { 110 | // Compute the indices of the child elements. 111 | var child2N = (n + 1) * 2, child1N = child2N - 1; 112 | // This is used to store the new position of the element, 113 | // if any. 114 | var swap = null; 115 | // If the first child exists (is inside the array)... 116 | if (child1N < length) { 117 | // Look it up and compute its score. 118 | var child1 = this.content[child1N], 119 | child1Score = this.scoreFunction(child1); 120 | // If the score is less than our element's, we need to swap. 121 | if (child1Score < elemScore) 122 | swap = child1N; 123 | } 124 | // Do the same checks for the other child. 125 | if (child2N < length) { 126 | var child2 = this.content[child2N], 127 | child2Score = this.scoreFunction(child2); 128 | if (child2Score < (swap == null ? elemScore : child1Score)) 129 | swap = child2N; 130 | } 131 | 132 | // If the element needs to be moved, swap it, and continue. 133 | if (swap != null) { 134 | this.content[n] = this.content[swap]; 135 | this.content[swap] = element; 136 | n = swap; 137 | } 138 | // Otherwise, we are done. 139 | else { 140 | break; 141 | } 142 | } 143 | } 144 | }; -------------------------------------------------------------------------------- /chapter06/views/portfolio.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |

Welcome to the Nockmarket

29 |

Here you can manage your portfolio and view live prices.

30 |
31 | 37 |
38 |
39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
Stock CodeLast Price
56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
StockBID5BID4BID3BID2BID1TradeASK1ASK2ASK3ASK4ASK5
80 |
81 |
82 |

83 |

84 |

85 |

86 |

Online:

87 |
88 |

89 |

90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | 98 | -------------------------------------------------------------------------------- /chapter07/views/portfolio.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nockmarket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |

Welcome to the Nockmarket

29 |

Here you can manage your portfolio and view live prices.

30 |
31 | 37 |
38 |
39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
Stock CodeLast Price
56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
StockBID5BID4BID3BID2BID1TradeASK1ASK2ASK3ASK4ASK5
80 |
81 |
82 |

83 |

84 |

85 |

86 |

Online:

87 |
88 |

89 |

90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | 98 | -------------------------------------------------------------------------------- /chapter05/lib/nocklib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cookie = require('cookie') 4 | , crypto = require('crypto') 5 | , db = require('./db') 6 | , exchange = require('./exchange') 7 | , express = require('express') 8 | , http = require('http') 9 | , MemoryStore = express.session.MemoryStore 10 | , ObjectID = require('mongodb').ObjectID 11 | , priceFloor = 35 12 | , priceRange = 10 13 | , volFloor = 80 14 | , volRange = 40; 15 | 16 | var sessionStore = new MemoryStore(); 17 | var io; 18 | var online = []; 19 | 20 | module.exports = { 21 | addStock: function(uid, stock, callback) { 22 | function doCallback() { 23 | counter++; 24 | if (counter == 2) { 25 | callback(null, price); 26 | } 27 | } 28 | 29 | var counter = 0; 30 | var price; 31 | 32 | module.exports.getStockPrices([stock], function(err, retrieved) { 33 | price = retrieved[0]; 34 | doCallback(); 35 | }); 36 | db.push('users', new ObjectID(uid), {portfolio: stock}, doCallback); 37 | }, 38 | 39 | authenticate: function(username, password, callback) { 40 | db.findOne('users', {username: username}, function(err, user) { 41 | if (user && (user.password === encryptPassword(password))) 42 | callback(err, user._id); 43 | else 44 | callback(err, null); 45 | }); 46 | }, 47 | 48 | createSocket: function(app) { 49 | io = require('socket.io').listen(app); 50 | io.configure(function (){ 51 | io.set('authorization', function (handshakeData, callback) { 52 | if (handshakeData.headers.cookie) { 53 | handshakeData.cookie = cookie.parse(decodeURIComponent(handshakeData.headers.cookie)); 54 | handshakeData.sessionID = handshakeData.cookie['connect.sid']; 55 | sessionStore.get(handshakeData.sessionID, function (err, session) { 56 | if (err || !session) { 57 | return callback(null, false); 58 | } else { 59 | handshakeData.session = session; 60 | console.log('session data', session); 61 | return callback(null, true); 62 | } 63 | }); 64 | } 65 | else { 66 | return callback(null, false); 67 | } 68 | }); 69 | io.sockets.on('connection', function (socket) { 70 | socket.on('clientchat', function (data) { 71 | var message = socket.handshake.session.username + ': ' 72 | + data.message + '\n'; 73 | socket.emit('chat', { message: message}); 74 | socket.broadcast.emit('chat', { message: message}); 75 | }); 76 | socket.on('disconnect', function (data) { 77 | var username = socket.handshake.session.username; 78 | var index = online.indexOf(username); 79 | online.splice(index, 1); 80 | socket.broadcast.emit('disconnect', { username: username}); 81 | }); 82 | socket.on('joined', function (data) { 83 | online.push(socket.handshake.session.username); 84 | var message = socket.handshake.session.username + ': ' + data.message + '\n'; 85 | socket.emit('chat', { message: message, users: online}); 86 | socket.broadcast.emit('chat', { message: message, username: socket.handshake.session.username}); 87 | }); 88 | socket.on('updateAccount', function (data) { 89 | module.exports.updateEmail(socket.handshake.session._id, data.email, function(err, numUpdates) { 90 | socket.emit('updateSuccess', {}); 91 | }); 92 | }); 93 | }); 94 | }); 95 | }, 96 | 97 | createUser: function(username, email, password, callback) { 98 | var user = {username: username, email: email, password: encryptPassword(password)}; 99 | db.insertOne('users', user, callback); 100 | }, 101 | 102 | ensureAuthenticated: function (req, res, next) { 103 | if (req.session._id) { 104 | return next(); 105 | } 106 | res.redirect('/'); 107 | }, 108 | 109 | generateRandomOrder: function(exchangeData) { 110 | var order = {}; 111 | if (Math.random() > 0.5) 112 | order.type = exchange.BUY 113 | else 114 | order.type = exchange.SELL 115 | 116 | var buyExists = exchangeData.buys 117 | && exchangeData.buys.prices.peek(); 118 | var sellExists = exchangeData.sells 119 | && exchangeData.sells.prices.peek(); 120 | 121 | var ran = Math.random(); 122 | if (!buyExists && !sellExists) 123 | order.price = Math.floor(ran * priceRange) + priceFloor; 124 | else if (buyExists && sellExists) { 125 | if (Math.random() > 0.5) 126 | order.price = exchangeData.buys.prices.peek(); 127 | else 128 | order.price = exchangeData.sells.prices.peek(); 129 | } else if (buyExists) { 130 | order.price = exchangeData.buys.prices.peek(); 131 | } else { 132 | order.price = exchangeData.sells.prices.peek(); 133 | } 134 | 135 | var shift = Math.floor(Math.random() * priceRange / 2); 136 | 137 | if (Math.random() > 0.5) 138 | order.price += shift; 139 | else 140 | order.price -= shift; 141 | order.volume = Math.floor(Math.random() * volRange) + volFloor 142 | return order; 143 | }, 144 | 145 | getSessionStore: function() { 146 | return sessionStore; 147 | }, 148 | 149 | getStockPrices: function(stocks, callback) { 150 | var stockList = ''; 151 | stocks.forEach(function(stock) { 152 | stockList += stock + ','; 153 | }); 154 | 155 | var options = { 156 | host: 'download.finance.yahoo.com', 157 | port: 80, 158 | path: '/d/quotes.csv?s=' + stockList + '&f=sl1c1d1&e=.csv' 159 | }; 160 | 161 | http.get(options, function(res) { 162 | var data = ''; 163 | res.on('data', function(chunk) { 164 | data += chunk.toString(); 165 | }) 166 | .on('error', function(err) { 167 | console.err('Error retrieving Yahoo stock prices'); 168 | throw err; 169 | }) 170 | .on('end', function() { 171 | var tokens = data.split('\r\n'); 172 | var prices = []; 173 | tokens.forEach(function(line) { 174 | var price = line.split(",")[1]; 175 | if (price) 176 | prices.push(price); 177 | }); 178 | callback(null, prices); 179 | }); 180 | }); 181 | }, 182 | 183 | getUserById: function(id, callback) { 184 | db.findOne('users', {_id: new ObjectID(id)}, callback); 185 | }, 186 | 187 | getUser: function(username, callback) { 188 | db.findOne('users', {username: username}, callback); 189 | }, 190 | 191 | sendTrades: function(trades) { 192 | io.sockets.emit('trade', JSON.stringify(trades)); 193 | }, 194 | 195 | updateEmail: function(id, email, callback) { 196 | db.updateById('users', new ObjectID(id), {email: email}, callback); 197 | } 198 | } 199 | 200 | function encryptPassword(plainText) { 201 | return crypto.createHash('md5').update(plainText).digest('hex'); 202 | } -------------------------------------------------------------------------------- /chapter06/lib/nocklib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cookie = require('cookie') 4 | , crypto = require('crypto') 5 | , db = require('./db') 6 | , exchange = require('./exchange') 7 | , express = require('express') 8 | , http = require('http') 9 | , MemoryStore = express.session.MemoryStore 10 | , ObjectID = require('mongodb').ObjectID 11 | , priceFloor = 35 12 | , priceRange = 10 13 | , volFloor = 80 14 | , volRange = 40; 15 | 16 | var sessionStore = new MemoryStore(); 17 | var io; 18 | var online = []; 19 | var lastExchangeData = {}; 20 | 21 | module.exports = { 22 | addStock: function(uid, stock, callback) { 23 | function doCallback() { 24 | counter++; 25 | if (counter == 2) { 26 | callback(null, price); 27 | } 28 | } 29 | 30 | var counter = 0; 31 | var price; 32 | 33 | module.exports.getStockPrices([stock], function(err, retrieved) { 34 | price = retrieved[0]; 35 | doCallback(); 36 | }); 37 | db.push('users', new ObjectID(uid), {portfolio: stock}, doCallback); 38 | }, 39 | 40 | authenticate: function(username, password, callback) { 41 | db.findOne('users', {username: username}, function(err, user) { 42 | if (user && (user.password === encryptPassword(password))) 43 | callback(err, user._id); 44 | else 45 | callback(err, null); 46 | }); 47 | }, 48 | 49 | createSocket: function(app) { 50 | io = require('socket.io').listen(app); 51 | io.configure(function (){ 52 | io.set('authorization', function (handshakeData, callback) { 53 | if (handshakeData.headers.cookie) { 54 | handshakeData.cookie = cookie.parse(decodeURIComponent(handshakeData.headers.cookie)); 55 | handshakeData.sessionID = handshakeData.cookie['connect.sid']; 56 | sessionStore.get(handshakeData.sessionID, function (err, session) { 57 | if (err || !session) { 58 | return callback(null, false); 59 | } else { 60 | handshakeData.session = session; 61 | console.log('session data', session); 62 | return callback(null, true); 63 | } 64 | }); 65 | } 66 | else { 67 | return callback(null, false); 68 | } 69 | }); 70 | io.sockets.on('connection', function (socket) { 71 | socket.on('clientchat', function (data) { 72 | var message = socket.handshake.session.username + ': ' 73 | + data.message + '\n'; 74 | socket.emit('chat', { message: message}); 75 | socket.broadcast.emit('chat', { message: message}); 76 | }); 77 | socket.on('disconnect', function (data) { 78 | var username = socket.handshake.session.username; 79 | var index = online.indexOf(username); 80 | online.splice(index, 1); 81 | socket.broadcast.emit('disconnect', { username: username}); 82 | }); 83 | socket.on('joined', function (data) { 84 | online.push(socket.handshake.session.username); 85 | var message = socket.handshake.session.username + ': ' + data.message + '\n'; 86 | socket.emit('chat', { message: message, users: online}); 87 | socket.broadcast.emit('chat', { message: message, username: socket.handshake.session.username}); 88 | }); 89 | socket.on('requestData', function (data) { 90 | socket.emit('initExchangeData', {exchangeData: transformExchangeData(lastExchangeData)}); 91 | }); 92 | socket.on('updateAccount', function (data) { 93 | module.exports.updateEmail(socket.handshake.session._id, data.email, function(err, numUpdates) { 94 | socket.emit('updateSuccess', {}); 95 | }); 96 | }); 97 | }); 98 | }); 99 | }, 100 | 101 | createUser: function(username, email, password, callback) { 102 | var user = {username: username, email: email, password: encryptPassword(password)}; 103 | db.insertOne('users', user, callback); 104 | }, 105 | 106 | ensureAuthenticated: function (req, res, next) { 107 | if (req.session._id) { 108 | return next(); 109 | } 110 | res.redirect('/'); 111 | }, 112 | 113 | generateRandomOrder: function(exchangeData) { 114 | var order = {}; 115 | if (Math.random() > 0.5) 116 | order.type = exchange.BUY 117 | else 118 | order.type = exchange.SELL 119 | 120 | var buyExists = exchangeData.buys 121 | && exchangeData.buys.prices.peek(); 122 | var sellExists = exchangeData.sells 123 | && exchangeData.sells.prices.peek(); 124 | 125 | var ran = Math.random(); 126 | if (!buyExists && !sellExists) 127 | order.price = Math.floor(ran * priceRange) + priceFloor; 128 | else if (buyExists && sellExists) { 129 | if (Math.random() > 0.5) 130 | order.price = exchangeData.buys.prices.peek(); 131 | else 132 | order.price = exchangeData.sells.prices.peek(); 133 | } else if (buyExists) { 134 | order.price = exchangeData.buys.prices.peek(); 135 | } else { 136 | order.price = exchangeData.sells.prices.peek(); 137 | } 138 | 139 | var shift = Math.floor(Math.random() * priceRange / 2); 140 | 141 | if (Math.random() > 0.5) 142 | order.price += shift; 143 | else 144 | order.price -= shift; 145 | order.volume = Math.floor(Math.random() * volRange) + volFloor 146 | return order; 147 | }, 148 | 149 | getSessionStore: function() { 150 | return sessionStore; 151 | }, 152 | 153 | getStockPrices: function(stocks, callback) { 154 | var stockList = ''; 155 | stocks.forEach(function(stock) { 156 | stockList += stock + ','; 157 | }); 158 | 159 | var options = { 160 | host: 'download.finance.yahoo.com', 161 | port: 80, 162 | path: '/d/quotes.csv?s=' + stockList + '&f=sl1c1d1&e=.csv' 163 | }; 164 | 165 | http.get(options, function(res) { 166 | var data = ''; 167 | res.on('data', function(chunk) { 168 | data += chunk.toString(); 169 | }) 170 | .on('error', function(err) { 171 | console.err('Error retrieving Yahoo stock prices'); 172 | throw err; 173 | }) 174 | .on('end', function() { 175 | var tokens = data.split('\r\n'); 176 | var prices = []; 177 | tokens.forEach(function(line) { 178 | var price = line.split(",")[1]; 179 | if (price) 180 | prices.push(price); 181 | }); 182 | callback(null, prices); 183 | }); 184 | }); 185 | }, 186 | 187 | getUserById: function(id, callback) { 188 | db.findOne('users', {_id: new ObjectID(id)}, callback); 189 | }, 190 | 191 | getUser: function(username, callback) { 192 | db.findOne('users', {username: username}, callback); 193 | }, 194 | 195 | sendExchangeData: function(stock, exchangeData) { 196 | lastExchangeData[stock] = exchangeData; 197 | var current = transformStockData(stock, exchangeData); 198 | io.sockets.emit('exchangeData', current); 199 | }, 200 | 201 | sendTrades: function(trades) { 202 | io.sockets.emit('trade', JSON.stringify(trades)); 203 | }, 204 | 205 | updateEmail: function(id, email, callback) { 206 | db.updateById('users', new ObjectID(id), {email: email}, callback); 207 | } 208 | } 209 | 210 | function encryptPassword(plainText) { 211 | return crypto.createHash('md5').update(plainText).digest('hex'); 212 | } 213 | 214 | function transformExchangeData(data) { 215 | var transformed = []; 216 | for (var stock in data) { 217 | var existingData = data[stock]; 218 | var newData = transformStockData(stock, existingData); 219 | transformed.push(newData); 220 | } 221 | return transformed; 222 | } 223 | 224 | function transformStockData(stock, existingData) { 225 | var newData = {}; 226 | newData.st = stock; 227 | if (existingData && existingData.trades && existingData.trades.length > 0) { 228 | newData.tp = existingData.trades[0].price; 229 | newData.tv = existingData.trades[0].volume; 230 | } 231 | var buyPrices = {}; 232 | var askPrices = {}; 233 | if (existingData && existingData.buys) { 234 | buyPrices = Object.keys(existingData.buys.volumes); 235 | for (var i=buyPrices.length - 5; i 0.5) 118 | order.type = exchange.BUY 119 | else 120 | order.type = exchange.SELL 121 | 122 | var buyExists = exchangeData.buys 123 | && exchangeData.buys.prices.peek(); 124 | var sellExists = exchangeData.sells 125 | && exchangeData.sells.prices.peek(); 126 | 127 | var ran = Math.random(); 128 | if (!buyExists && !sellExists) 129 | order.price = Math.floor(ran * priceRange) + priceFloor; 130 | else if (buyExists && sellExists) { 131 | if (Math.random() > 0.5) 132 | order.price = exchangeData.buys.prices.peek(); 133 | else 134 | order.price = exchangeData.sells.prices.peek(); 135 | } else if (buyExists) { 136 | order.price = exchangeData.buys.prices.peek(); 137 | } else { 138 | order.price = exchangeData.sells.prices.peek(); 139 | } 140 | 141 | var shift = Math.floor(Math.random() * priceRange / 2); 142 | 143 | if (Math.random() > 0.5) 144 | order.price += shift; 145 | else 146 | order.price -= shift; 147 | order.volume = Math.floor(Math.random() * volRange) + volFloor 148 | return order; 149 | }, 150 | 151 | getSessionStore: function() { 152 | return sessionStore; 153 | }, 154 | 155 | getStockPrices: function(stocks, callback) { 156 | var stockList = ''; 157 | stocks.forEach(function(stock) { 158 | stockList += stock + ','; 159 | }); 160 | 161 | var options = { 162 | host: 'download.finance.yahoo.com', 163 | port: 80, 164 | path: '/d/quotes.csv?s=' + stockList + '&f=sl1c1d1&e=.csv' 165 | }; 166 | 167 | http.get(options, function(res) { 168 | var data = ''; 169 | res.on('data', function(chunk) { 170 | data += chunk.toString(); 171 | }) 172 | .on('error', function(err) { 173 | console.err('Error retrieving Yahoo stock prices'); 174 | throw err; 175 | }) 176 | .on('end', function() { 177 | var tokens = data.split('\r\n'); 178 | var prices = []; 179 | tokens.forEach(function(line) { 180 | var price = line.split(",")[1]; 181 | if (price) 182 | prices.push(price); 183 | }); 184 | callback(null, prices); 185 | }); 186 | }); 187 | }, 188 | 189 | getUserById: function(id, callback) { 190 | db.findOne('users', {_id: new ObjectID(id)}, callback); 191 | }, 192 | 193 | getUser: function(username, callback) { 194 | db.findOne('users', {username: username}, callback); 195 | }, 196 | 197 | sendExchangeData: function(stock, exchangeData) { 198 | lastExchangeData[stock] = exchangeData; 199 | var current = transformStockData(stock, exchangeData); 200 | io.sockets.emit('exchangeData', current); 201 | }, 202 | 203 | sendTrades: function(trades) { 204 | io.sockets.emit('trade', JSON.stringify(trades)); 205 | }, 206 | 207 | updateEmail: function(id, email, callback) { 208 | db.updateById('users', new ObjectID(id), {email: email}, callback); 209 | } 210 | } 211 | 212 | function encryptPassword(plainText) { 213 | return crypto.createHash('md5').update(plainText).digest('hex'); 214 | } 215 | 216 | function transformExchangeData(data) { 217 | var transformed = []; 218 | for (var stock in data) { 219 | var existingData = data[stock]; 220 | var newData = transformStockData(stock, existingData); 221 | transformed.push(newData); 222 | } 223 | return transformed; 224 | } 225 | 226 | function transformStockData(stock, existingData) { 227 | var newData = {}; 228 | newData.st = stock; 229 | if (existingData && existingData.trades && existingData.trades.length > 0) { 230 | newData.tp = existingData.trades[0].price; 231 | newData.tv = existingData.trades[0].volume; 232 | } 233 | var buyPrices = {}; 234 | var askPrices = {}; 235 | if (existingData && existingData.buys) { 236 | buyPrices = Object.keys(existingData.buys.volumes); 237 | for (var i=buyPrices.length - 5; i