├── .gitignore ├── feed.js ├── package.json ├── server.js └── www ├── css └── styles.css ├── index.html └── js ├── app.js ├── feed-mock.js └── feed-socketio.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /feed.js: -------------------------------------------------------------------------------- 1 | var interval, 2 | onChangeHandler; 3 | 4 | var stocks = [ 5 | {symbol: "GM", open: 38.87}, 6 | {symbol: "GE", open: 25.40}, 7 | {symbol: "MCD", open: 97.05}, 8 | {symbol: "UAL", open: 69.45}, 9 | {symbol: "WMT", open: 83.24}, 10 | {symbol: "AAL", open: 55.76}, 11 | {symbol: "LLY", open: 76.12}, 12 | {symbol: "JPM", open: 61.75}, 13 | {symbol: "BAC", open: 15.84}, 14 | {symbol: "BA", open: 154.50} 15 | ]; 16 | 17 | stocks.forEach(function(stock) { 18 | stock.last = stock.open; 19 | stock.high = stock.open; 20 | stock.low = stock.open; 21 | }); 22 | 23 | function simulateChange() { 24 | 25 | var index = Math.floor(Math.random() * stocks.length), 26 | stock = stocks[index], 27 | 28 | maxChange = stock.open * 0.005, 29 | change = maxChange - Math.random() * maxChange * 2, 30 | last; 31 | 32 | change = Math.round(change * 100) / 100; 33 | change = change === 0 ? 0.01 : change; 34 | 35 | last = stock.last + change; 36 | 37 | if (last > stock.open * 1.15 || last < stock.open * 0.85) 38 | { 39 | change = -change; 40 | last = stock.last + change; 41 | } 42 | 43 | stock.change = change; 44 | stock.last = Math.round(last * 100) / 100; 45 | if (stock.last > stock.high) { 46 | stock.high = stock.last; 47 | } 48 | if (stock.last < stock.low) { 49 | stock.low = stock.last; 50 | } 51 | onChangeHandler(stock.symbol, 'stock', stock); 52 | } 53 | 54 | function start(onChange) { 55 | onChangeHandler = onChange; 56 | interval = setInterval(simulateChange, 200); 57 | } 58 | 59 | function stop() { 60 | clearInterval(interval); 61 | } 62 | 63 | exports.start = start; 64 | exports.stop = stop; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactTrader", 3 | "version": "1.0.0", 4 | "description": "Trader Desktop", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Christophe Coenraets (http://coenraets.org/)", 10 | "license": "MIT", 11 | "dependencies": { 12 | "express": "^4.12.3", 13 | "socket.io": "^1.3.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | app = express(), 3 | path = require('path'), 4 | http = require('http').Server(app), 5 | io = require('socket.io')(http), 6 | feed = require('./feed'); 7 | 8 | app.use(express.static(path.join(__dirname, './www'))); 9 | 10 | io.on('connection', function (socket) { 11 | console.log('User connected. Socket id %s', socket.id); 12 | 13 | socket.on('join', function (rooms) { 14 | console.log('Socket %s subscribed to %s', socket.id, rooms); 15 | if (Array.isArray(rooms)) { 16 | rooms.forEach(function(room) { 17 | socket.join(room); 18 | }); 19 | } else { 20 | socket.join(rooms); 21 | } 22 | }); 23 | 24 | socket.on('leave', function (rooms) { 25 | console.log('Socket %s unsubscribed from %s', socket.id, rooms); 26 | if (Array.isArray(rooms)) { 27 | rooms.forEach(function(room) { 28 | socket.leave(room); 29 | }); 30 | } else { 31 | socket.leave(rooms); 32 | } 33 | }); 34 | 35 | socket.on('disconnect', function () { 36 | console.log('User disconnected. %s. Socket id %s', socket.id); 37 | }); 38 | }); 39 | 40 | feed.start(function(room, type, message) { 41 | io.to(room).emit(type, message); 42 | }); 43 | 44 | http.listen(3000, function () { 45 | console.log('listening on: 3000'); 46 | }); 47 | -------------------------------------------------------------------------------- /www/css/styles.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | } 4 | 5 | th:first-child { 6 | text-align: left; 7 | } 8 | 9 | th:not(:first-child), 10 | td:not(:first-child) { 11 | text-align: right; 12 | } 13 | 14 | th:last-child, 15 | td:last-child { 16 | text-align: center; 17 | } 18 | 19 | th, td { 20 | max-width: 74px; 21 | width: 74px; 22 | border: solid 1px #DDDDDD; 23 | padding: 5px; 24 | } 25 | 26 | th { 27 | font-weight: bolder; 28 | } 29 | 30 | .last-positive { 31 | background: green; 32 | color: #FFFFFF; 33 | } 34 | 35 | .last-negative { 36 | background: red; 37 | color: #FFFFFF; 38 | } 39 | 40 | .change-positive { 41 | color: green; 42 | } 43 | 44 | .change-negative { 45 | color: red; 46 | } 47 | 48 | .row { 49 | margin: 10px 0; 50 | } 51 | 52 | .container { 53 | width: 552px; 54 | } -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Trader Desktop 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /www/js/app.js: -------------------------------------------------------------------------------- 1 | var WatchStock = React.createClass({ 2 | getInitialState: function() { 3 | return {symbol: ""}; 4 | }, 5 | watchStock: function() { 6 | this.props.watchStockHandler(this.state.symbol); 7 | this.setState({symbol: ''}); 8 | }, 9 | handleChange: function(event) { 10 | this.setState({symbol: event.target.value}); 11 | }, 12 | render: function () { 13 | return ( 14 |
15 |

Available stocks for demo: MCD, BA, BAC, LLY, GM, GE, UAL, WMT, AAL, JPM

16 |
17 | 18 | 19 | 22 | 23 |
24 |
25 | ); 26 | } 27 | }); 28 | 29 | var StockRow = React.createClass({ 30 | unwatch: function() { 31 | this.props.unwatchStockHandler(this.props.stock.symbol); 32 | }, 33 | render: function () { 34 | var lastClass = '', 35 | changeClass = 'change-positive', 36 | iconClass = 'glyphicon glyphicon-triangle-top'; 37 | if (this.props.stock === this.props.last) { 38 | lastClass = this.props.stock.change < 0 ? 'last-negative' : 'last-positive'; 39 | } 40 | if (this.props.stock.change < 0) { 41 | changeClass = 'change-negative'; 42 | iconClass = 'glyphicon glyphicon-triangle-bottom'; 43 | } 44 | return ( 45 | 46 | {this.props.stock.symbol} 47 | {this.props.stock.open} 48 | {this.props.stock.last} 49 | {this.props.stock.change} 50 | {this.props.stock.high} 51 | {this.props.stock.low} 52 | 55 | 56 | ); 57 | } 58 | }); 59 | 60 | var StockTable = React.createClass({ 61 | render: function () { 62 | var items = []; 63 | for (var symbol in this.props.stocks) { 64 | var stock = this.props.stocks[symbol]; 65 | items.push(); 66 | } 67 | return ( 68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {items} 83 | 84 |
SymbolOpenLastChangeHighLowUnwatch
85 |
86 | ); 87 | } 88 | }); 89 | 90 | var HomePage = React.createClass({ 91 | getInitialState: function() { 92 | var stocks = {}; 93 | feed.watch(['MCD', 'BA', 'BAC', 'LLY', 'GM', 'GE', 'UAL', 'WMT', 'AAL', 'JPM']); 94 | feed.onChange(function(stock) { 95 | stocks[stock.symbol] = stock; 96 | this.setState({stocks: stocks, last: stock}); 97 | }.bind(this)); 98 | return {stocks: stocks}; 99 | }, 100 | watchStock: function(symbols) { 101 | symbols = symbols.replace(/ /g,''); 102 | var arr = symbols.split(","); 103 | feed.watch(arr); 104 | }, 105 | unwatchStock: function(symbol) { 106 | feed.unwatch(symbol); 107 | var stocks = this.state.stocks; 108 | delete stocks[symbol]; 109 | this.setState({stocks: stocks}); 110 | }, 111 | render: function () { 112 | return ( 113 |
114 | 115 | 116 |
117 |
All stock values are fake and changes are simulated. Do not trade based on the above data.
118 |
119 |
120 | ); 121 | } 122 | }); 123 | 124 | React.render(, document.getElementById('main')); -------------------------------------------------------------------------------- /www/js/feed-mock.js: -------------------------------------------------------------------------------- 1 | feed = (function () { 2 | 3 | var watchList = []; 4 | 5 | var stocks = [ 6 | {symbol: "GM", open: 38.87}, 7 | {symbol: "GE", open: 25.40}, 8 | {symbol: "MCD", open: 97.05}, 9 | {symbol: "UAL", open: 69.45}, 10 | {symbol: "WMT", open: 83.24}, 11 | {symbol: "AAL", open: 55.76}, 12 | {symbol: "LLY", open: 76.12}, 13 | {symbol: "JPM", open: 61.75}, 14 | {symbol: "BAC", open: 15.84}, 15 | {symbol: "BA", open: 154.50} 16 | ]; 17 | 18 | stocks.forEach(function(stock) { 19 | stock.last = stock.open; 20 | stock.high = stock.open; 21 | stock.low = stock.open; 22 | }); 23 | 24 | return { 25 | onChange: function(callback) { 26 | setInterval(function() { 27 | var index = Math.floor(Math.random() * stocks.length), 28 | stock = stocks[index], 29 | maxChange = stock.open * 0.005, 30 | change = maxChange - Math.random() * maxChange * 2, 31 | last; 32 | 33 | change = Math.round(change * 100) / 100; 34 | change = change === 0 ? 0.01 : change; 35 | 36 | last = stock.last + change; 37 | 38 | if (last > stock.open * 1.15 || last < stock.open * 0.85) 39 | { 40 | change = -change; 41 | last = stock.last + change; 42 | } 43 | 44 | stock.change = change; 45 | stock.last = Math.round(last * 100) / 100; 46 | if (stock.last > stock.high) { 47 | stock.high = stock.last; 48 | } 49 | if (stock.last < stock.low) { 50 | stock.low = stock.last; 51 | } 52 | if (watchList.indexOf(stock.symbol) > -1) { 53 | callback(stock); 54 | } 55 | }, 200); 56 | }, 57 | watch: function(symbols) { 58 | console.log(symbols); 59 | symbols.forEach(function(symbol) { 60 | if (watchList.indexOf(symbol) < 0) { 61 | watchList.push(symbol); 62 | } 63 | }); 64 | }, 65 | unwatch: function(symbol) { 66 | var index = watchList.indexOf(symbol); 67 | if (index > -1) { 68 | watchList.splice(index, 1); 69 | } 70 | } 71 | }; 72 | 73 | }()); -------------------------------------------------------------------------------- /www/js/feed-socketio.js: -------------------------------------------------------------------------------- 1 | feed = (function () { 2 | 3 | var socket = io(); 4 | 5 | return { 6 | onChange: function(callback) { 7 | socket.on('stock', callback); 8 | }, 9 | watch: function(symbols) { 10 | socket.emit('join', symbols); 11 | }, 12 | unwatch: function(symbol) { 13 | socket.emit('leave', symbol); 14 | } 15 | }; 16 | 17 | }()); --------------------------------------------------------------------------------