From ef668883c83f0a38257132c7ab49a11dbff59041 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Mon, 11 Dec 2017 15:40:24 -0800 Subject: [PATCH 01/20] Create quote view and quote list view --- src/app.js | 28 ++++++++++++++++++++++++---- src/collections/quote_list.js | 2 +- src/views/quote_list_view.js | 26 ++++++++++++++++++++++++++ src/views/quote_view.js | 16 ++++++++++++++++ 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/views/quote_list_view.js create mode 100644 src/views/quote_view.js diff --git a/src/app.js b/src/app.js index 03ec910..c33c79d 100644 --- a/src/app.js +++ b/src/app.js @@ -1,11 +1,19 @@ +// CSS import 'foundation-sites/dist/foundation.css'; import 'css/app.css'; +// Vendor Modules import $ from 'jquery'; +import _ from 'underscore'; -import Simulator from 'models/simulator'; -import QuoteList from 'collections/quote_list'; +// Models +import Simulator from './models/simulator'; +import QuoteList from './collections/quote_list'; +// Views +import QuoteListView from './views/quote_list_view'; + +// Vars const quoteData = [ { symbol: 'HUMOR', @@ -25,11 +33,23 @@ const quoteData = [ }, ]; +const quoteList = new QuoteList(quoteData); +let quoteTemplate; + $(document).ready(function() { - const quotes = new QuoteList(quoteData); const simulator = new Simulator({ - quotes: quotes, + quotes: quoteList, }); simulator.start(); + + quoteTemplate = _.template($('#quote-template').html()); + + const quoteListView = new QuoteListView({ + model: quoteList, + template: quoteTemplate, + el: 'main' + }); + + quoteListView.render(); }); diff --git a/src/collections/quote_list.js b/src/collections/quote_list.js index 8da08cb..bfa112d 100644 --- a/src/collections/quote_list.js +++ b/src/collections/quote_list.js @@ -1,5 +1,5 @@ import Backbone from 'backbone'; -import Quote from 'models/quote'; +import Quote from '../models/quote'; const QuoteList = Backbone.Collection.extend({ model: Quote, diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js new file mode 100644 index 0000000..f7a7712 --- /dev/null +++ b/src/views/quote_list_view.js @@ -0,0 +1,26 @@ +import Backbone from 'backbone'; +import _ from 'underscore'; +import QuoteView from './quote_view'; +import Quote from '../models/quote'; + +const QuoteListView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, 'update', this.render); + }, + render() { + this.$('#quotes').empty(); + this.model.each((quote) => { + const quoteView = new QuoteView({ + model: quote, + template: this.template, + tagName: 'li', + className: 'quote', + }); + this.$('#quotes').append(quoteView.render().$el); + }); + return this; + }, +}); + +export default QuoteListView; diff --git a/src/views/quote_view.js b/src/views/quote_view.js new file mode 100644 index 0000000..d2ac8ee --- /dev/null +++ b/src/views/quote_view.js @@ -0,0 +1,16 @@ +import Backbone from 'backbone'; +import Quote from '../models/quote'; + +const QuoteView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, "change", this.render); + }, + render() { + const compiledTemplate = this.template(this.model.toJSON()); + this.$el.html(compiledTemplate); + return this; + }, +}) + +export default QuoteView; From ebd20d7e81f07a607a211001a2ce954f3d17081c Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Mon, 11 Dec 2017 15:52:35 -0800 Subject: [PATCH 02/20] Can buy and sell stock --- src/app.js | 11 +++++------ src/models/quote.js | 4 ++-- src/views/quote_view.js | 10 ++++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/app.js b/src/app.js index c33c79d..67633e1 100644 --- a/src/app.js +++ b/src/app.js @@ -13,7 +13,7 @@ import QuoteList from './collections/quote_list'; // Views import QuoteListView from './views/quote_list_view'; -// Vars +// Data const quoteData = [ { symbol: 'HUMOR', @@ -33,21 +33,20 @@ const quoteData = [ }, ]; -const quoteList = new QuoteList(quoteData); -let quoteTemplate; $(document).ready(function() { + + const quoteList = new QuoteList(quoteData); + const simulator = new Simulator({ quotes: quoteList, }); simulator.start(); - quoteTemplate = _.template($('#quote-template').html()); - const quoteListView = new QuoteListView({ model: quoteList, - template: quoteTemplate, + template: _.template($('#quote-template').html()), el: 'main' }); diff --git a/src/models/quote.js b/src/models/quote.js index 4fbf466..63be309 100644 --- a/src/models/quote.js +++ b/src/models/quote.js @@ -7,11 +7,11 @@ const Quote = Backbone.Model.extend({ }, buy() { - // Implement this function to increase the price by $1.00 + return this.set('price', (this.get('price') + 1.00)); }, sell() { - // Implement this function to decrease the price by $1.00 + return this.set('price', (this.get('price') - 1.00)); }, }); diff --git a/src/views/quote_view.js b/src/views/quote_view.js index d2ac8ee..2fa2cfa 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -11,6 +11,16 @@ const QuoteView = Backbone.View.extend({ this.$el.html(compiledTemplate); return this; }, + events: { + 'click button.btn-buy': 'buyQuote', + 'click button.btn-sell': 'sellQuote' + }, + buyQuote: function() { + this.model.buy(); + }, + sellQuote: function() { + this.model.sell(); + }, }) export default QuoteView; From bea0e58e8e7c8fbffdc05fd8644483c3554a502f Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Tue, 12 Dec 2017 15:30:04 -0800 Subject: [PATCH 03/20] Progress on displaying trades --- src/app.js | 1 + src/views/quote_list_view.js | 8 ++++++++ src/views/quote_view.js | 2 ++ 3 files changed, 11 insertions(+) diff --git a/src/app.js b/src/app.js index 67633e1..3b3d010 100644 --- a/src/app.js +++ b/src/app.js @@ -47,6 +47,7 @@ $(document).ready(function() { const quoteListView = new QuoteListView({ model: quoteList, template: _.template($('#quote-template').html()), + tradeTemplate: _.template($('#trade-template').html()), el: 'main' }); diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index f7a7712..64c9522 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -6,6 +6,7 @@ import Quote from '../models/quote'; const QuoteListView = Backbone.View.extend({ initialize(params) { this.template = params.template; + this.tradeTemplate = params.tradeTemplate; this.listenTo(this.model, 'update', this.render); }, render() { @@ -14,13 +15,20 @@ const QuoteListView = Backbone.View.extend({ const quoteView = new QuoteView({ model: quote, template: this.template, + tradeTemplate: this.tradeTemplate, tagName: 'li', className: 'quote', }); + this.listenTo(quoteView, 'buy', this.displayBuy) + this.$('#quotes').append(quoteView.render().$el); + }); return this; }, + displayBuy(quoteView) { + this.$('#trades').append(this.tradeTemplate({buy: true, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) + } }); export default QuoteListView; diff --git a/src/views/quote_view.js b/src/views/quote_view.js index 2fa2cfa..1289886 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -1,4 +1,5 @@ import Backbone from 'backbone'; +import _ from 'underscore'; import Quote from '../models/quote'; const QuoteView = Backbone.View.extend({ @@ -17,6 +18,7 @@ const QuoteView = Backbone.View.extend({ }, buyQuote: function() { this.model.buy(); + this.trigger('buy', this) }, sellQuote: function() { this.model.sell(); From 65f0bcdb65f3841df1679570c1a469bf4c322f91 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Tue, 12 Dec 2017 15:40:05 -0800 Subject: [PATCH 04/20] Trade price reflects price at time button was clicked --- src/views/quote_list_view.js | 12 ++++++++---- src/views/quote_view.js | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index 64c9522..40bc2e4 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -1,7 +1,6 @@ import Backbone from 'backbone'; -import _ from 'underscore'; import QuoteView from './quote_view'; -import Quote from '../models/quote'; +//import Quote from '../models/quote'; const QuoteListView = Backbone.View.extend({ initialize(params) { @@ -19,7 +18,9 @@ const QuoteListView = Backbone.View.extend({ tagName: 'li', className: 'quote', }); - this.listenTo(quoteView, 'buy', this.displayBuy) + this.listenTo(quoteView, 'buy', this.displayBuy); + + this.listenTo(quoteView, 'sell', this.displaySell); this.$('#quotes').append(quoteView.render().$el); @@ -27,7 +28,10 @@ const QuoteListView = Backbone.View.extend({ return this; }, displayBuy(quoteView) { - this.$('#trades').append(this.tradeTemplate({buy: true, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) + this.$('#trades').prepend(this.tradeTemplate({buy: true, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) + }, + displaySell(quoteView) { + this.$('#trades').prepend(this.tradeTemplate({buy: false, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) } }); diff --git a/src/views/quote_view.js b/src/views/quote_view.js index 1289886..6667ea3 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -1,6 +1,5 @@ import Backbone from 'backbone'; -import _ from 'underscore'; -import Quote from '../models/quote'; +//import Quote from '../models/quote'; const QuoteView = Backbone.View.extend({ initialize(params) { @@ -17,10 +16,11 @@ const QuoteView = Backbone.View.extend({ 'click button.btn-sell': 'sellQuote' }, buyQuote: function() { + this.trigger('buy', this); this.model.buy(); - this.trigger('buy', this) }, sellQuote: function() { + this.trigger('sell', this); this.model.sell(); }, }) From a09793794655758ee35ba8cb8ab9d4acb216e896 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Tue, 12 Dec 2017 17:26:05 -0800 Subject: [PATCH 05/20] Can add Order to OrderList via form --- dist/index.html | 207 +++++++++++++++++----------------- src/app.js | 6 +- src/collections/order_list.js | 8 ++ src/models/order.js | 7 ++ src/views/order_list_view.js | 0 src/views/quote_list_view.js | 18 +++ 6 files changed, 143 insertions(+), 103 deletions(-) create mode 100644 src/collections/order_list.js create mode 100644 src/models/order.js create mode 100644 src/views/order_list_view.js diff --git a/dist/index.html b/dist/index.html index 8a046fa..ef7c8be 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,120 +1,123 @@ - - Ada Trader - - - - - - -
-
-

Ada Trader

-
- -
- -
- -
-

Quotes

-
-
    -
-
+ + Ada Trader + + + + + + +
+
+

Ada Trader

+
+ +
+ +
+ +
+

Quotes

+
+
    +
-
-
+
-
-

Trade History

-
-
    -
-
-
+
+
+

Trade History

+
+
    +
+
+
+
+ +
-
- -
- -
-
-

Open Orders

-
-
    -
-
-
-
- -
-
-

Order Entry Form

-
- - - - - - - -
-
-
-
- -
+
+
+
+

Open Orders

+
+
    +
+
+
-
-
-
- -
-
- - - - - - +
+ + + + + + - - + + diff --git a/src/app.js b/src/app.js index 3b3d010..b7b4e53 100644 --- a/src/app.js +++ b/src/app.js @@ -9,6 +9,7 @@ import _ from 'underscore'; // Models import Simulator from './models/simulator'; import QuoteList from './collections/quote_list'; +import OrderList from './collections/order_list'; // Views import QuoteListView from './views/quote_list_view'; @@ -38,6 +39,8 @@ $(document).ready(function() { const quoteList = new QuoteList(quoteData); + const orderList = new OrderList; + const simulator = new Simulator({ quotes: quoteList, }); @@ -48,7 +51,8 @@ $(document).ready(function() { model: quoteList, template: _.template($('#quote-template').html()), tradeTemplate: _.template($('#trade-template').html()), - el: 'main' + el: 'main', + orderList: orderList }); quoteListView.render(); diff --git a/src/collections/order_list.js b/src/collections/order_list.js new file mode 100644 index 0000000..6b77957 --- /dev/null +++ b/src/collections/order_list.js @@ -0,0 +1,8 @@ +import Backbone from 'backbone'; +import Order from '../models/order'; + +const OrderList = Backbone.Collection.extend({ + model: Order, +}); + +export default OrderList; diff --git a/src/models/order.js b/src/models/order.js new file mode 100644 index 0000000..ac00259 --- /dev/null +++ b/src/models/order.js @@ -0,0 +1,7 @@ +import Backbone from 'backbone'; + +const Order = Backbone.Model.extend({ + +}); + +export default Order; diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js new file mode 100644 index 0000000..e69de29 diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index 40bc2e4..aec127c 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -6,6 +6,7 @@ const QuoteListView = Backbone.View.extend({ initialize(params) { this.template = params.template; this.tradeTemplate = params.tradeTemplate; + this.orderList = params.orderList; this.listenTo(this.model, 'update', this.render); }, render() { @@ -32,6 +33,23 @@ const QuoteListView = Backbone.View.extend({ }, displaySell(quoteView) { this.$('#trades').prepend(this.tradeTemplate({buy: false, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) + }, + events: { + 'click .btn-buy': 'makeBuyOrder', + 'click .btn-sell': 'makeSellOrder' + }, + makeBuyOrder: function(event) { + event.preventDefault(); + // validate? when you create a new order? + const orderData = { buy: true }; + orderData['symbol'] = this.$('select :selected').text(); + orderData['targetPrice'] = this.$(`input[name=price-target]`).val(); + //const newOrder = new Order(orderData); + this.orderList.add(orderData); + console.log(this.orderList); + }, + makeSellOrder() { + // validate? when you create a new order? } }); From f88720b3a90906e164e25ba3d3973dce71afe663 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 09:44:22 -0800 Subject: [PATCH 06/20] Can view list of open orders --- src/app.js | 7 +++++++ src/views/order_list_view.js | 24 ++++++++++++++++++++++++ src/views/order_view.js | 15 +++++++++++++++ src/views/quote_list_view.js | 17 +++++++---------- 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 src/views/order_view.js diff --git a/src/app.js b/src/app.js index b7b4e53..a5b5507 100644 --- a/src/app.js +++ b/src/app.js @@ -13,6 +13,7 @@ import OrderList from './collections/order_list'; // Views import QuoteListView from './views/quote_list_view'; +import OrderListView from './views/order_list_view'; // Data const quoteData = [ @@ -55,5 +56,11 @@ $(document).ready(function() { orderList: orderList }); + const orderListView = new OrderListView({ + model: orderList, + template: _.template($('#order-template').html()), + el: 'main' + }); + quoteListView.render(); }); diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index e69de29..dcb4506 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -0,0 +1,24 @@ +import Backbone from 'backbone'; +import OrderView from './order_view'; + +const OrderListView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, 'update', this.render); + }, + render() { + this.$('#orders').empty(); + this.model.each((order) => { + const orderView = new OrderView({ + model: order, + template: this.template, + tagName: 'li', + className: 'order', + }); + this.$('#orders').append(orderView.render().$el); + }); + return this; + } +}) + +export default OrderListView diff --git a/src/views/order_view.js b/src/views/order_view.js new file mode 100644 index 0000000..764d9da --- /dev/null +++ b/src/views/order_view.js @@ -0,0 +1,15 @@ +import Backbone from 'backbone'; + +const OrderView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, 'change', this.render); + }, + render() { + const compiledTemplate = this.template(this.model.toJSON()); + this.$el.html(compiledTemplate); + return this; + } +}) + +export default OrderView; diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index aec127c..1515688 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -35,22 +35,19 @@ const QuoteListView = Backbone.View.extend({ this.$('#trades').prepend(this.tradeTemplate({buy: false, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) }, events: { - 'click .btn-buy': 'makeBuyOrder', - 'click .btn-sell': 'makeSellOrder' + 'click .btn-buy': 'addOrder', + 'click .btn-sell': 'addOrder' }, - makeBuyOrder: function(event) { + addOrder: function(event) { event.preventDefault(); // validate? when you create a new order? - const orderData = { buy: true }; + const orderData = this.$(event.target).attr('class').includes('btn-buy') ? { buy: true } : {buy: false} orderData['symbol'] = this.$('select :selected').text(); - orderData['targetPrice'] = this.$(`input[name=price-target]`).val(); - //const newOrder = new Order(orderData); + const stringTargetPrice = this.$(`input[name=price-target]`).val(); + orderData['targetPrice'] = parseFloat(stringTargetPrice); this.orderList.add(orderData); - console.log(this.orderList); + this.$('.order-entry-form [name=price-target]').val(""); }, - makeSellOrder() { - // validate? when you create a new order? - } }); export default QuoteListView; From 10e625ae3dfa32b25c92f12e36191cf9470c0a5d Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 09:51:32 -0800 Subject: [PATCH 07/20] Can cancel an open order --- src/views/order_view.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/views/order_view.js b/src/views/order_view.js index 764d9da..ec7b559 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -9,6 +9,12 @@ const OrderView = Backbone.View.extend({ const compiledTemplate = this.template(this.model.toJSON()); this.$el.html(compiledTemplate); return this; + }, + events: { + 'click button.btn-cancel': 'deleteOrder', + }, + deleteOrder: function() { + this.model.destroy(); } }) From 74fc2d44199bbcdf9728cc97e662af65e15c97fc Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 10:08:14 -0800 Subject: [PATCH 08/20] Refactor: move addOrder events to order_list_view --- src/views/order_list_view.js | 16 +++++++++++++++- src/views/quote_list_view.js | 14 -------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index dcb4506..67d82fd 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -18,7 +18,21 @@ const OrderListView = Backbone.View.extend({ this.$('#orders').append(orderView.render().$el); }); return this; - } + }, + events: { + 'click .btn-buy': 'addOrder', + 'click .btn-sell': 'addOrder' + }, + addOrder: function(event) { + event.preventDefault(); + // validate? when you create a new order? + const orderData = this.$(event.target).attr('class').includes('btn-buy') ? { buy: true } : {buy: false} + orderData['symbol'] = this.$('select :selected').text(); + const stringTargetPrice = this.$(`input[name=price-target]`).val(); + orderData['targetPrice'] = parseFloat(stringTargetPrice); + this.model.add(orderData); + this.$('.order-entry-form [name=price-target]').val(""); + }, }) export default OrderListView diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index 1515688..1528f18 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -34,20 +34,6 @@ const QuoteListView = Backbone.View.extend({ displaySell(quoteView) { this.$('#trades').prepend(this.tradeTemplate({buy: false, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) }, - events: { - 'click .btn-buy': 'addOrder', - 'click .btn-sell': 'addOrder' - }, - addOrder: function(event) { - event.preventDefault(); - // validate? when you create a new order? - const orderData = this.$(event.target).attr('class').includes('btn-buy') ? { buy: true } : {buy: false} - orderData['symbol'] = this.$('select :selected').text(); - const stringTargetPrice = this.$(`input[name=price-target]`).val(); - orderData['targetPrice'] = parseFloat(stringTargetPrice); - this.orderList.add(orderData); - this.$('.order-entry-form [name=price-target]').val(""); - }, }); export default QuoteListView; From 337840b86fba85d51057b5d98057a64873e9710b Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 10:28:11 -0800 Subject: [PATCH 09/20] Add validations to Order and logic to order form --- src/models/order.js | 14 +++++++++++++- src/views/order_list_view.js | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/models/order.js b/src/models/order.js index ac00259..9098db1 100644 --- a/src/models/order.js +++ b/src/models/order.js @@ -1,7 +1,19 @@ import Backbone from 'backbone'; const Order = Backbone.Model.extend({ - + validate: function(attributes) { + const errors = {}; + if (!attributes.targetPrice) { + errors['Price'] = ['cannot be blank']; + } else if (isNaN(attributes.targetPrice)) { + errors['Price'] = ['must be a number - leave off $'] + } + if (Object.keys(errors).length > 0) { + return errors; + } else { + return false; + } + } }); export default Order; diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index 67d82fd..ff50f14 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -1,5 +1,6 @@ import Backbone from 'backbone'; import OrderView from './order_view'; +import Order from '../models/order'; const OrderListView = Backbone.View.extend({ initialize(params) { @@ -30,8 +31,19 @@ const OrderListView = Backbone.View.extend({ orderData['symbol'] = this.$('select :selected').text(); const stringTargetPrice = this.$(`input[name=price-target]`).val(); orderData['targetPrice'] = parseFloat(stringTargetPrice); - this.model.add(orderData); - this.$('.order-entry-form [name=price-target]').val(""); + const newOrder = new Order(orderData); + if (newOrder.isValid()) { + this.model.add(newOrder); + this.$('.order-entry-form [name=price-target]').val(""); + this.$('.form-errors').empty(); + } else { + this.$('.form-errors').empty(); + for(let key in newOrder.validationError) { + newOrder.validationError[key].forEach((error) => { + this.$('.form-errors').append(`

${key}: ${error}

`); + }) + } + } }, }) From 2c1b19f21330890a7acc2481d60a5c270ff1a66b Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 14:48:27 -0800 Subject: [PATCH 10/20] Order View listening to Quote View through quote list view and order list view --- src/app.js | 3 ++- src/views/order_list_view.js | 2 ++ src/views/order_view.js | 3 +++ src/views/quote_list_view.js | 5 +++++ src/views/quote_view.js | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/app.js b/src/app.js index a5b5507..70b8830 100644 --- a/src/app.js +++ b/src/app.js @@ -59,7 +59,8 @@ $(document).ready(function() { const orderListView = new OrderListView({ model: orderList, template: _.template($('#order-template').html()), - el: 'main' + el: 'main', + quoteListView: quoteListView }); quoteListView.render(); diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index ff50f14..bac49fc 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -6,6 +6,7 @@ const OrderListView = Backbone.View.extend({ initialize(params) { this.template = params.template; this.listenTo(this.model, 'update', this.render); + this.quoteListView = params.quoteListView; }, render() { this.$('#orders').empty(); @@ -17,6 +18,7 @@ const OrderListView = Backbone.View.extend({ className: 'order', }); this.$('#orders').append(orderView.render().$el); + orderView.listenTo(this.quoteListView, 'priceChange', orderView.executeOrder); }); return this; }, diff --git a/src/views/order_view.js b/src/views/order_view.js index ec7b559..bc9ec68 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -15,6 +15,9 @@ const OrderView = Backbone.View.extend({ }, deleteOrder: function() { this.model.destroy(); + }, + executeOrder: function(changeInfo) { + if(this.model.get('symbol') === changeInfo[symbol] && ) } }) diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index 1528f18..41fd587 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -23,6 +23,8 @@ const QuoteListView = Backbone.View.extend({ this.listenTo(quoteView, 'sell', this.displaySell); + this.listenTo(quoteView, 'priceChange', this.alertPriceChange); + this.$('#quotes').append(quoteView.render().$el); }); @@ -34,6 +36,9 @@ const QuoteListView = Backbone.View.extend({ displaySell(quoteView) { this.$('#trades').prepend(this.tradeTemplate({buy: false, symbol: quoteView.model.get('symbol'), price: quoteView.model.get('price')})) }, + alertPriceChange(changeInfo) { + this.trigger('priceChange', changeInfo); + } }); export default QuoteListView; diff --git a/src/views/quote_view.js b/src/views/quote_view.js index 6667ea3..1246b3a 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -9,6 +9,7 @@ const QuoteView = Backbone.View.extend({ render() { const compiledTemplate = this.template(this.model.toJSON()); this.$el.html(compiledTemplate); + this.trigger('priceChange', {symbol: this.model.get('symbol'), currentPrice: this.model.get('price')}); return this; }, events: { From cfa07079b70afc0e7ef80cd9a406d3545d5f65ee Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 15:04:29 -0800 Subject: [PATCH 11/20] Refactor to use bus --- src/app.js | 8 ++++++-- src/models/event_bus.js | 7 +++++++ src/views/order_list_view.js | 3 ++- src/views/order_view.js | 3 ++- src/views/quote_list_view.js | 2 ++ src/views/quote_view.js | 8 ++++++-- 6 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/models/event_bus.js diff --git a/src/app.js b/src/app.js index 70b8830..4225eef 100644 --- a/src/app.js +++ b/src/app.js @@ -10,6 +10,7 @@ import _ from 'underscore'; import Simulator from './models/simulator'; import QuoteList from './collections/quote_list'; import OrderList from './collections/order_list'; +import Bus from './models/event_bus'; // Views import QuoteListView from './views/quote_list_view'; @@ -46,6 +47,8 @@ $(document).ready(function() { quotes: quoteList, }); + const bus = new Bus; + simulator.start(); const quoteListView = new QuoteListView({ @@ -53,14 +56,15 @@ $(document).ready(function() { template: _.template($('#quote-template').html()), tradeTemplate: _.template($('#trade-template').html()), el: 'main', - orderList: orderList + orderList: orderList, + bus: bus }); const orderListView = new OrderListView({ model: orderList, template: _.template($('#order-template').html()), el: 'main', - quoteListView: quoteListView + bus: bus }); quoteListView.render(); diff --git a/src/models/event_bus.js b/src/models/event_bus.js new file mode 100644 index 0000000..b95a159 --- /dev/null +++ b/src/models/event_bus.js @@ -0,0 +1,7 @@ +import Backbone from 'backbone'; + +const Bus = Backbone.Model.extend({ + +}); + +export default Bus; diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index bac49fc..df0d955 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -7,6 +7,7 @@ const OrderListView = Backbone.View.extend({ this.template = params.template; this.listenTo(this.model, 'update', this.render); this.quoteListView = params.quoteListView; + this.bus = params.bus; }, render() { this.$('#orders').empty(); @@ -18,7 +19,7 @@ const OrderListView = Backbone.View.extend({ className: 'order', }); this.$('#orders').append(orderView.render().$el); - orderView.listenTo(this.quoteListView, 'priceChange', orderView.executeOrder); + orderView.listenTo(this.bus, 'priceChange', orderView.executeOrder); }); return this; }, diff --git a/src/views/order_view.js b/src/views/order_view.js index bc9ec68..8683482 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -17,7 +17,8 @@ const OrderView = Backbone.View.extend({ this.model.destroy(); }, executeOrder: function(changeInfo) { - if(this.model.get('symbol') === changeInfo[symbol] && ) + // if(this.model.get('symbol') === changeInfo[symbol] && ) + } }) diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index 41fd587..c4aeded 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -8,6 +8,7 @@ const QuoteListView = Backbone.View.extend({ this.tradeTemplate = params.tradeTemplate; this.orderList = params.orderList; this.listenTo(this.model, 'update', this.render); + this.bus = params.bus; }, render() { this.$('#quotes').empty(); @@ -16,6 +17,7 @@ const QuoteListView = Backbone.View.extend({ model: quote, template: this.template, tradeTemplate: this.tradeTemplate, + bus: this.bus, tagName: 'li', className: 'quote', }); diff --git a/src/views/quote_view.js b/src/views/quote_view.js index 1246b3a..f5a131a 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -4,14 +4,18 @@ import Backbone from 'backbone'; const QuoteView = Backbone.View.extend({ initialize(params) { this.template = params.template; - this.listenTo(this.model, "change", this.render); + this.bus = params.bus; + this.listenTo(this.model, 'change', this.render); + this.listenTo(this.model, 'change', this.alertPriceChange); }, render() { const compiledTemplate = this.template(this.model.toJSON()); this.$el.html(compiledTemplate); - this.trigger('priceChange', {symbol: this.model.get('symbol'), currentPrice: this.model.get('price')}); return this; }, + alertPriceChange() { + this.bus.trigger('priceChange', {symbol: this.model.get('symbol'), currentPrice: this.model.get('price')}); + }, events: { 'click button.btn-buy': 'buyQuote', 'click button.btn-sell': 'sellQuote' From fce1f9a2c91d4e153e99bc376f3b5c153df36110 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Wed, 13 Dec 2017 15:16:10 -0800 Subject: [PATCH 12/20] Order knows when to buy --- src/views/order_view.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/order_view.js b/src/views/order_view.js index 8683482..dc7a737 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -17,7 +17,10 @@ const OrderView = Backbone.View.extend({ this.model.destroy(); }, executeOrder: function(changeInfo) { - // if(this.model.get('symbol') === changeInfo[symbol] && ) + if (this.model.get('symbol') === changeInfo.symbol && this.model.get('buy') === true && changeInfo.currentPrice <= this.model.get('targetPrice')) { + console.log('I should buy'); + } + } }) From cebdbeb993c7b1050ffe3416e489ff3d4c528a1e Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Fri, 15 Dec 2017 10:41:24 -0800 Subject: [PATCH 13/20] Order being executed and displayed in trade history for buy --- src/models/order.js | 9 +++++++++ src/views/order_list_view.js | 4 +++- src/views/order_view.js | 10 ++++------ src/views/quote_view.js | 8 ++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/models/order.js b/src/models/order.js index 9098db1..4e0e522 100644 --- a/src/models/order.js +++ b/src/models/order.js @@ -13,6 +13,15 @@ const Order = Backbone.Model.extend({ } else { return false; } + }, + executeOrder: function(changeInfo) { + if (this.get('symbol') === changeInfo.symbol && this.get('buy') === true && changeInfo.currentPrice <= this.get('targetPrice')) { + this.trigger('buy', changeInfo); + this.destroy(); + } + if (this.get('symbol') === changeInfo.symbol && this.get('buy') === false && changeInfo.currentPrice >= this.get('targetPrice')) { + //this.bus.trigger('sell', changeInfo); + } } }); diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index df0d955..27901da 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -17,9 +17,10 @@ const OrderListView = Backbone.View.extend({ template: this.template, tagName: 'li', className: 'order', + bus: this.bus }); this.$('#orders').append(orderView.render().$el); - orderView.listenTo(this.bus, 'priceChange', orderView.executeOrder); + // orderView.listenTo(this.bus, 'priceChange', orderView.executeOrder); }); return this; }, @@ -37,6 +38,7 @@ const OrderListView = Backbone.View.extend({ const newOrder = new Order(orderData); if (newOrder.isValid()) { this.model.add(newOrder); + newOrder.listenTo(this.bus, 'priceChange', newOrder.executeOrder); this.$('.order-entry-form [name=price-target]').val(""); this.$('.form-errors').empty(); } else { diff --git a/src/views/order_view.js b/src/views/order_view.js index dc7a737..c41c1ff 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -3,7 +3,9 @@ import Backbone from 'backbone'; const OrderView = Backbone.View.extend({ initialize(params) { this.template = params.template; + this.bus = params.bus; this.listenTo(this.model, 'change', this.render); + this.listenTo(this.model, 'buy', this.triggerBuy); }, render() { const compiledTemplate = this.template(this.model.toJSON()); @@ -16,12 +18,8 @@ const OrderView = Backbone.View.extend({ deleteOrder: function() { this.model.destroy(); }, - executeOrder: function(changeInfo) { - if (this.model.get('symbol') === changeInfo.symbol && this.model.get('buy') === true && changeInfo.currentPrice <= this.model.get('targetPrice')) { - console.log('I should buy'); - } - - + triggerBuy: function(changeInfo) { + this.bus.trigger('buyOrder', changeInfo) } }) diff --git a/src/views/quote_view.js b/src/views/quote_view.js index f5a131a..7ccda64 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -7,6 +7,8 @@ const QuoteView = Backbone.View.extend({ this.bus = params.bus; this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'change', this.alertPriceChange); + this.listenTo(this.bus, 'buyOrder', this.buyOrder); + this.listenTo(this.bus, 'sell', this.sellQuote); }, render() { const compiledTemplate = this.template(this.model.toJSON()); @@ -28,6 +30,12 @@ const QuoteView = Backbone.View.extend({ this.trigger('sell', this); this.model.sell(); }, + buyOrder: function(changeInfo) { + if (this.model.get(`symbol`) === changeInfo.symbol) { + this.trigger('buy', this); + this.model.buy(); + } + } }) export default QuoteView; From 342746c61acac9fea6a50b1f755aabc5e27684bc Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Fri, 15 Dec 2017 10:49:31 -0800 Subject: [PATCH 14/20] Order being executed and displayed in trade history fr sell --- src/models/order.js | 3 ++- src/views/order_view.js | 4 ++++ src/views/quote_view.js | 8 +++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/models/order.js b/src/models/order.js index 4e0e522..0f012dc 100644 --- a/src/models/order.js +++ b/src/models/order.js @@ -20,7 +20,8 @@ const Order = Backbone.Model.extend({ this.destroy(); } if (this.get('symbol') === changeInfo.symbol && this.get('buy') === false && changeInfo.currentPrice >= this.get('targetPrice')) { - //this.bus.trigger('sell', changeInfo); + this.trigger('sell', changeInfo); + this.destroy(); } } }); diff --git a/src/views/order_view.js b/src/views/order_view.js index c41c1ff..2d79f63 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -6,6 +6,7 @@ const OrderView = Backbone.View.extend({ this.bus = params.bus; this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'buy', this.triggerBuy); + this.listenTo(this.model, 'sell', this.triggerSell); }, render() { const compiledTemplate = this.template(this.model.toJSON()); @@ -20,6 +21,9 @@ const OrderView = Backbone.View.extend({ }, triggerBuy: function(changeInfo) { this.bus.trigger('buyOrder', changeInfo) + }, + triggerSell: function(changeInfo) { + this.bus.trigger('sellOrder', changeInfo) } }) diff --git a/src/views/quote_view.js b/src/views/quote_view.js index 7ccda64..99c41d5 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -8,7 +8,7 @@ const QuoteView = Backbone.View.extend({ this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'change', this.alertPriceChange); this.listenTo(this.bus, 'buyOrder', this.buyOrder); - this.listenTo(this.bus, 'sell', this.sellQuote); + this.listenTo(this.bus, 'sellOrder', this.sellOrder); }, render() { const compiledTemplate = this.template(this.model.toJSON()); @@ -35,6 +35,12 @@ const QuoteView = Backbone.View.extend({ this.trigger('buy', this); this.model.buy(); } + }, + sellOrder: function(changeInfo) { + if (this.model.get(`symbol`) === changeInfo.symbol) { + this.trigger('sell', this); + this.model.sell(); + } } }) From aae1639f9ba2e60aae2f470c70d53fcb402fd51b Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Fri, 15 Dec 2017 11:40:14 -0800 Subject: [PATCH 15/20] Multiple orders with same target price get exectued correctly --- src/models/order.js | 2 -- src/views/order_view.js | 4 ++-- src/views/quote_view.js | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/models/order.js b/src/models/order.js index 0f012dc..03f42fd 100644 --- a/src/models/order.js +++ b/src/models/order.js @@ -17,11 +17,9 @@ const Order = Backbone.Model.extend({ executeOrder: function(changeInfo) { if (this.get('symbol') === changeInfo.symbol && this.get('buy') === true && changeInfo.currentPrice <= this.get('targetPrice')) { this.trigger('buy', changeInfo); - this.destroy(); } if (this.get('symbol') === changeInfo.symbol && this.get('buy') === false && changeInfo.currentPrice >= this.get('targetPrice')) { this.trigger('sell', changeInfo); - this.destroy(); } } }); diff --git a/src/views/order_view.js b/src/views/order_view.js index 2d79f63..27926df 100644 --- a/src/views/order_view.js +++ b/src/views/order_view.js @@ -20,10 +20,10 @@ const OrderView = Backbone.View.extend({ this.model.destroy(); }, triggerBuy: function(changeInfo) { - this.bus.trigger('buyOrder', changeInfo) + this.bus.trigger('buyOrder', {quote: changeInfo, model: this.model}); }, triggerSell: function(changeInfo) { - this.bus.trigger('sellOrder', changeInfo) + this.bus.trigger('sellOrder', {quote: changeInfo, model: this.model}); } }) diff --git a/src/views/quote_view.js b/src/views/quote_view.js index 99c41d5..264c975 100644 --- a/src/views/quote_view.js +++ b/src/views/quote_view.js @@ -31,15 +31,17 @@ const QuoteView = Backbone.View.extend({ this.model.sell(); }, buyOrder: function(changeInfo) { - if (this.model.get(`symbol`) === changeInfo.symbol) { + if (this.model.get(`symbol`) === changeInfo.quote.symbol && this.model.get('price') <= changeInfo.quote.currentPrice) { this.trigger('buy', this); this.model.buy(); + changeInfo.model.destroy(); } }, sellOrder: function(changeInfo) { - if (this.model.get(`symbol`) === changeInfo.symbol) { + if (this.model.get(`symbol`) === changeInfo.quote.symbol && this.model.get('price') >= changeInfo.quote.currentPrice) { this.trigger('sell', this); this.model.sell(); + changeInfo.model.destroy(); } } }) From 02018a12c49ac218b0f3d9010fc8ac5274afae78 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Sun, 17 Dec 2017 20:21:27 -0800 Subject: [PATCH 16/20] Broke all of the internet --- src/app.js | 1 + src/views/order_list_view.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/app.js b/src/app.js index 4225eef..ecf0520 100644 --- a/src/app.js +++ b/src/app.js @@ -64,6 +64,7 @@ $(document).ready(function() { model: orderList, template: _.template($('#order-template').html()), el: 'main', + quoteList: quoteList, bus: bus }); diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index 27901da..5e3a50b 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -8,6 +8,7 @@ const OrderListView = Backbone.View.extend({ this.listenTo(this.model, 'update', this.render); this.quoteListView = params.quoteListView; this.bus = params.bus; + this.quoteList = params.quoteList; }, render() { this.$('#orders').empty(); @@ -35,6 +36,11 @@ const OrderListView = Backbone.View.extend({ orderData['symbol'] = this.$('select :selected').text(); const stringTargetPrice = this.$(`input[name=price-target]`).val(); orderData['targetPrice'] = parseFloat(stringTargetPrice); + let quote = this.quoteList.where({symbol: orderData['symbol']})[0].attributes + console.log(quote) + // while (orderData['targetPrice'] > quote.price) { + // console.log("bad buy") + // } const newOrder = new Order(orderData); if (newOrder.isValid()) { this.model.add(newOrder); From 98398a0fd089bbb9dcf7292e5f9b5911e5867ae1 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Sun, 17 Dec 2017 20:32:05 -0800 Subject: [PATCH 17/20] Enforce buy low sell high at view level --- src/views/order_list_view.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index 5e3a50b..bc604b5 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -37,22 +37,24 @@ const OrderListView = Backbone.View.extend({ const stringTargetPrice = this.$(`input[name=price-target]`).val(); orderData['targetPrice'] = parseFloat(stringTargetPrice); let quote = this.quoteList.where({symbol: orderData['symbol']})[0].attributes - console.log(quote) - // while (orderData['targetPrice'] > quote.price) { - // console.log("bad buy") - // } - const newOrder = new Order(orderData); - if (newOrder.isValid()) { - this.model.add(newOrder); - newOrder.listenTo(this.bus, 'priceChange', newOrder.executeOrder); - this.$('.order-entry-form [name=price-target]').val(""); - this.$('.form-errors').empty(); + if (orderData['buy'] && orderData['targetPrice'] > quote.price) { + this.$('.form-errors').append(`

Invalid target price: must be less than current market price for buy order

`); + } else if (!orderData['buy'] && orderData['targetPrice'] < quote.price) { + this.$('.form-errors').append(`

Invalid target price: must be greater than current market price for sell order

`); } else { - this.$('.form-errors').empty(); - for(let key in newOrder.validationError) { - newOrder.validationError[key].forEach((error) => { - this.$('.form-errors').append(`

${key}: ${error}

`); - }) + const newOrder = new Order(orderData); + if (newOrder.isValid()) { + this.model.add(newOrder); + newOrder.listenTo(this.bus, 'priceChange', newOrder.executeOrder); + this.$('.order-entry-form [name=price-target]').val(""); + this.$('.form-errors').empty(); + } else { + this.$('.form-errors').empty(); + for(let key in newOrder.validationError) { + newOrder.validationError[key].forEach((error) => { + this.$('.form-errors').append(`

${key}: ${error}

`); + }) + } } } }, From 39a2815befee760b37f4bb1133e92f9650585d69 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Sun, 17 Dec 2017 21:01:09 -0800 Subject: [PATCH 18/20] Refactor so buy low sell high enforced at model level --- src/models/order.js | 6 +++++- src/views/order_list_view.js | 31 ++++++++++++------------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/models/order.js b/src/models/order.js index 03f42fd..f657f6a 100644 --- a/src/models/order.js +++ b/src/models/order.js @@ -6,7 +6,11 @@ const Order = Backbone.Model.extend({ if (!attributes.targetPrice) { errors['Price'] = ['cannot be blank']; } else if (isNaN(attributes.targetPrice)) { - errors['Price'] = ['must be a number - leave off $'] + errors['Price'] = ['must be a number - leave off $']; + } else if (attributes.buy && attributes.targetPrice > attributes.marketPrice) { + errors['Price'] = ['cannot be higher than market price to buy']; + } else if (!attributes.buy && attributes.targetPrice < attributes.marketPrice) { + errors['Price'] = ['cannot be lower than market price to sell']; } if (Object.keys(errors).length > 0) { return errors; diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index bc604b5..4d9a43b 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -31,30 +31,23 @@ const OrderListView = Backbone.View.extend({ }, addOrder: function(event) { event.preventDefault(); - // validate? when you create a new order? const orderData = this.$(event.target).attr('class').includes('btn-buy') ? { buy: true } : {buy: false} orderData['symbol'] = this.$('select :selected').text(); const stringTargetPrice = this.$(`input[name=price-target]`).val(); orderData['targetPrice'] = parseFloat(stringTargetPrice); - let quote = this.quoteList.where({symbol: orderData['symbol']})[0].attributes - if (orderData['buy'] && orderData['targetPrice'] > quote.price) { - this.$('.form-errors').append(`

Invalid target price: must be less than current market price for buy order

`); - } else if (!orderData['buy'] && orderData['targetPrice'] < quote.price) { - this.$('.form-errors').append(`

Invalid target price: must be greater than current market price for sell order

`); + orderData['marketPrice'] = this.quoteList.where({symbol: orderData['symbol']})[0].attributes.price; + const newOrder = new Order(orderData); + if (newOrder.isValid()) { + this.model.add(newOrder); + newOrder.listenTo(this.bus, 'priceChange', newOrder.executeOrder); + this.$('.order-entry-form [name=price-target]').val(""); + this.$('.form-errors').empty(); } else { - const newOrder = new Order(orderData); - if (newOrder.isValid()) { - this.model.add(newOrder); - newOrder.listenTo(this.bus, 'priceChange', newOrder.executeOrder); - this.$('.order-entry-form [name=price-target]').val(""); - this.$('.form-errors').empty(); - } else { - this.$('.form-errors').empty(); - for(let key in newOrder.validationError) { - newOrder.validationError[key].forEach((error) => { - this.$('.form-errors').append(`

${key}: ${error}

`); - }) - } + this.$('.form-errors').empty(); + for(let key in newOrder.validationError) { + newOrder.validationError[key].forEach((error) => { + this.$('.form-errors').append(`

${key}: ${error}

`); + }) } } }, From cbf213353cc06eaf1c970b85db404abd0bcf3f09 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Mon, 18 Dec 2017 07:39:27 -0800 Subject: [PATCH 19/20] Test that executeOrder triggers buy and sell eventss --- spec/models/order_spec.js | 89 ++++++++++++++++++++++++++++++++++++ src/models/order.js | 8 +++- src/views/order_list_view.js | 1 - 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 spec/models/order_spec.js diff --git a/spec/models/order_spec.js b/spec/models/order_spec.js new file mode 100644 index 0000000..9cc8e8d --- /dev/null +++ b/spec/models/order_spec.js @@ -0,0 +1,89 @@ +import Order from 'models/order'; + +describe('Order spec', () => { + let buyOrder; + let sellOrder; + beforeEach(() => { + buyOrder = new Order({ + symbol: 'HELLO', + targetPrice: 90.00, + marketPrice: 100.00, + buy: true + }); + sellOrder = new Order({ + symbol: 'HELLO', + targetPrice: 100.00, + marketPrice: 90.00, + buy: false + }) + }); + + describe('Order validations', () => { + it('is not valid unless it has a symbol', () => { + + expect(buyOrder.isValid()).toBeTruthy(); + + expect(sellOrder.isValid()).toBeTruthy(); + + buyOrder.set('symbol', null); + + expect(buyOrder.isValid()).toBeFalsy(); + + sellOrder.set('symbol', null); + + expect(sellOrder.isValid()).toBeFalsy(); + }) + + it('is not valid unless it has a numerical targetPrice', () => { + expect(buyOrder.isValid()).toBeTruthy(); + + expect(sellOrder.isValid()).toBeTruthy(); + + const badTargetPrices = [null, 'strings', '.', '-1'] + + badTargetPrices.forEach(function(element) { + + buyOrder.set('targetPrice', element); + + sellOrder.set('targetPrice', element); + + expect(buyOrder.isValid()).toBeFalsy(); + + expect(sellOrder.isValid()).toBeFalsy(); + }) + }) + + it('must follow buy low, sell high', () => { + expect(buyOrder.isValid()).toBeTruthy(); + + expect(sellOrder.isValid()).toBeTruthy(); + + buyOrder.set('targetPrice', '110'); + + sellOrder.set('targetPrice', '80'); + + expect(buyOrder.isValid()).toBeFalsy(); + + expect(sellOrder.isValid()).toBeFalsy(); + + }) + }); + + describe('Execute order', () => { + it('triggers a "buy" event on itself if new buyOrder is valid', () => { + const changeInfo = {symbol: 'HELLO', buy: true, currentPrice: 80} + spyOn(buyOrder, "trigger") + buyOrder.executeOrder(changeInfo) + + expect(buyOrder.trigger).toHaveBeenCalledWith('buy', changeInfo); + }); + + it('triggers a "sell" event on itself if new sellOrder is valid', () => { + const changeInfo = {symbol: 'HELLO', buy: false, currentPrice: 110} + spyOn(sellOrder, "trigger") + sellOrder.executeOrder(changeInfo) + + expect(sellOrder.trigger).toHaveBeenCalledWith('sell', changeInfo); + }); + }); +}); diff --git a/src/models/order.js b/src/models/order.js index f657f6a..00f542e 100644 --- a/src/models/order.js +++ b/src/models/order.js @@ -7,10 +7,14 @@ const Order = Backbone.Model.extend({ errors['Price'] = ['cannot be blank']; } else if (isNaN(attributes.targetPrice)) { errors['Price'] = ['must be a number - leave off $']; + } else if (attributes.targetPrice <= 0){ + errors['Price'] = ['must be greater than 0'] } else if (attributes.buy && attributes.targetPrice > attributes.marketPrice) { - errors['Price'] = ['cannot be higher than market price to buy']; + errors['Price'] = ['cannot be higher than market price for buy orders']; } else if (!attributes.buy && attributes.targetPrice < attributes.marketPrice) { - errors['Price'] = ['cannot be lower than market price to sell']; + errors['Price'] = ['cannot be lower than market price for sell orders']; + } else if (!attributes.symbol) { + errors['Symbol'] = ['cannot be blank']; } if (Object.keys(errors).length > 0) { return errors; diff --git a/src/views/order_list_view.js b/src/views/order_list_view.js index 4d9a43b..97c52a4 100644 --- a/src/views/order_list_view.js +++ b/src/views/order_list_view.js @@ -21,7 +21,6 @@ const OrderListView = Backbone.View.extend({ bus: this.bus }); this.$('#orders').append(orderView.render().$el); - // orderView.listenTo(this.bus, 'priceChange', orderView.executeOrder); }); return this; }, From 935e065c0373f921f7366f81c034f582b493f0c9 Mon Sep 17 00:00:00 2001 From: Gale Harrington Date: Mon, 18 Dec 2017 09:01:29 -0800 Subject: [PATCH 20/20] Add dropdown dynamically --- dist/index.html | 4 ++-- src/views/quote_list_view.js | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dist/index.html b/dist/index.html index ef7c8be..ea3ebfa 100644 --- a/dist/index.html +++ b/dist/index.html @@ -60,10 +60,10 @@

Order Entry Form

diff --git a/src/views/quote_list_view.js b/src/views/quote_list_view.js index c4aeded..f0527bf 100644 --- a/src/views/quote_list_view.js +++ b/src/views/quote_list_view.js @@ -12,6 +12,7 @@ const QuoteListView = Backbone.View.extend({ }, render() { this.$('#quotes').empty(); + this.$('select').empty(); this.model.each((quote) => { const quoteView = new QuoteView({ model: quote, @@ -29,6 +30,9 @@ const QuoteListView = Backbone.View.extend({ this.$('#quotes').append(quoteView.render().$el); + this.$('select').append(``) + + }); return this; },