├── 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 |
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 |
The page you are looking for cannot be found :(
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(' Username already taken');
7 | $('.create-button').addClass('disabled').attr('disabled', true);
8 | }
9 | else {
10 | $('#imagePlaceHolder').html(' ');
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(' Username already taken');
7 | $('.create-button').addClass('disabled').attr('disabled', true);
8 | }
9 | else {
10 | $('#imagePlaceHolder').html(' ');
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(' Username already taken');
7 | $('.create-button').addClass('disabled').attr('disabled', true);
8 | }
9 | else {
10 | $('#imagePlaceHolder').html(' ');
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(' Username already taken');
7 | $('.create-button').addClass('disabled').attr('disabled', true);
8 | }
9 | else {
10 | $('#imagePlaceHolder').html(' ');
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 |
31 |
32 | Add Stock
33 |
34 |
35 |
36 |
37 | Stock Code
38 | Last Price
39 |
40 |
41 |
42 | <% for (var i=0; i
43 | <%=portfolio[i]%>
44 | <%=prices[i]%>
45 | <% } %>
46 |
47 |
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 |
37 |
38 | Add Stock
39 |
40 |
41 |
42 |
43 | Stock Code
44 | Last Price
45 |
46 |
47 |
48 | <% for (var i=0; i
49 | <%=portfolio[i]%>
50 | <%=prices[i]%>
51 | <% } %>
52 |
53 |
54 |
55 |
56 |
Howdy, I'm in Section 2.
57 |
58 |
59 |
Join Chat
60 |
61 |
62 |
Send
63 |
Online:
64 |
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 |
Welcome to the Node.js
64 | stockmarket
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 |
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 |
Welcome to the Node.js
64 | stockmarket
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 |
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 |
Welcome to the Node.js
64 | stockmarket
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 |
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 |
Welcome to the Node.js
64 | stockmarket
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 |
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 |
57 |
58 |
59 |
60 |
61 | Stock
62 | BID5
63 | BID4
64 | BID3
65 | BID2
66 | BID1
67 | Trade
68 | ASK1
69 | ASK2
70 | ASK3
71 | ASK4
72 | ASK5
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Join Chat
83 |
84 |
85 |
Send
86 |
Online:
87 |
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 |
57 |
58 |
59 |
60 |
61 | Stock
62 | BID5
63 | BID4
64 | BID3
65 | BID2
66 | BID1
67 | Trade
68 | ASK1
69 | ASK2
70 | ASK3
71 | ASK4
72 | ASK5
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Join Chat
83 |
84 |
85 |
Send
86 |
Online:
87 |
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