Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,21 @@ import 'foundation-sites/dist/foundation.css';
import 'css/app.css';

import $ from 'jquery';

import _ from 'underscore';
import Backbone from 'backbone';
import Simulator from 'models/simulator';

// Models & Collections
import Quote from 'models/quote';
import QuoteList from 'collections/quote_list';
import Order from 'models/order';
import OrderList from 'collections/order_list';

// Views
import QuoteView from 'views/quote_view';
import QuoteListView from 'views/quote_list_view';
import OrderView from 'views/order_view';
import OrderListView from 'views/order_list_view';

const quoteData = [
{
Expand All @@ -27,9 +39,41 @@ const quoteData = [

$(document).ready(function() {
const quotes = new QuoteList(quoteData);
const orders = new OrderList();

let bus = {};
bus = _.extend(bus, Backbone.Events);

const simulator = new Simulator({
quotes: quotes,
});

const quoteListView = new QuoteListView({
model: quotes,
template: _.template($('#quote-template').html()),
tradeTemplate: _.template($('#trade-template').html()),
bus: bus,
el: 'main'
});

const formSelect = function formSelect () {
quotes.each((quote) => {
const symbol = quote.get('symbol');
$('select[name="symbol"]').append(`<option value="${symbol}">${symbol}</option>`);
});
};

const orderListView = new OrderListView({
model: orders,
quotes: quotes,
template: _.template($('#order-template').html()),
bus: bus,
el: '#order-workspace'
});


quoteListView.render();
formSelect();
orderListView.render();
simulator.start();
});
8 changes: 8 additions & 0 deletions src/collections/order_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Backbone from 'backbone';
import Order from '../models/order';

const OrderList = Backbone.Collection.extend({
model: Order,
});

export default OrderList;
27 changes: 27 additions & 0 deletions src/models/order.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Backbone from 'backbone';

const Order = Backbone.Model.extend({
validate(attributes) {
const errors = {};

if(!attributes.targetPrice || attributes.targetPrice <= 0) {
errors['price'] = ['Invalid target price'];
}

if (this.get('buy') && attributes.targetPrice >= this.get('quote').get('price')) {
errors['price'] = ['Price higher than market price!'];
}

if (!this.get('buy') && attributes.targetPrice <= this.get('quote').get('price')) {
errors['price'] = ['Price lower than market price!'];
}

if ( Object.keys(errors).length > 0 ) {
return errors;
} else {
return false;
}
}
});

export default Order;
2 changes: 2 additions & 0 deletions src/models/quote.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ const Quote = Backbone.Model.extend({

buy() {
// Implement this function to increase the price by $1.00
this.set('price', this.get('price') + 1.00);
},

sell() {
// Implement this function to decrease the price by $1.00
this.set('price', this.get('price') - 1.00);
},
});

Expand Down
75 changes: 75 additions & 0 deletions src/views/order_list_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Backbone from 'backbone';
// import _ from 'underscore';
// import OrderList from '../collections/order_list';
import $ from 'jquery';
import OrderView from './order_view';
import Order from '../models/order';

const OrderListView = Backbone.View.extend({
initialize(params) {
this.template = params.template;
this.bus = params.bus;
this.quotes = params.quotes;
this.listenTo(this.model, 'update', this.render);
},
render() {
this.$('#orders').empty();

this.model.each((order) => {
const orderView = new OrderView({
model: order,
template: this.template,
bus: this.bus,
tagName: 'li',
quotes: this.quotes,
className: 'order',
});
this.$('#orders').append(orderView.render().$el);
});
return this;
},
events: {
'click .btn-buy': 'buyOrder',
'click .btn-sell': 'sellOrder',
},
createOrder(value) {
this.$('.form-errors').empty();

const orderData = {
buy: value.buy,
symbol: this.$('select[name=symbol]').val(),
quote: this.quotes.find({symbol: this.$('select[name=symbol]').val()}),
targetPrice: parseFloat(this.$('input[name=price-target]').val()),
bus: this.bus
};
return new Order(orderData);
},
buyOrder: function(event) {
event.preventDefault();
const order = this.createOrder({buy: true});
this.validate(order);
},
sellOrder: function(event) {
event.preventDefault();
const order = this.createOrder({buy: false});
this.validate(order);
},
validate(order) {
if (order.isValid()) {
this.model.add(order);
this.$el.find('form').trigger('reset');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat, but you could also do: this.$('form').trigger('reset'); That way you don't need to execute find.

} else {
const errors = order.validationError;
const errorSection = this.$('.form-errors');

Object.keys(errors).forEach((field) => {
errors[field].forEach((error) => {
const html = `<h3>${error}</h3>`;
errorSection.append(html);
});
});
}
},
});

export default OrderListView;
37 changes: 37 additions & 0 deletions src/views/order_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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.get('quote'), 'change', this.tradeOrder);
},
render() {
const compiledTemplate = this.template(this.model.toJSON());
this.$el.html(compiledTemplate);
return this;
},
events: {
'click .btn-cancel': 'cancelOrder',
},
cancelOrder() {
this.model.destroy();
this.remove();
},
tradeOrder() {
if (this.model.get('buy')) {
if (this.model.get('quote').get('price') <= this.model.get('targetPrice')) {
this.bus.trigger('buyOrder', this);
this.cancelOrder();
}
} else {
if (this.model.get('quote').get('price') >= this.model.get('targetPrice')) {
this.bus.trigger('sellOrder', this);
this.cancelOrder();
}
}
},
});

export default OrderView;
34 changes: 34 additions & 0 deletions src/views/quote_list_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Backbone from 'backbone';
// import _ from 'underscore';
// import Quote from '../models/quote';
import QuoteView from './quote_view';


const QuoteListView = Backbone.View.extend({
initialize(params) {
this.template = params.template;
this.tradeTemplate = params.tradeTemplate;
this.bus = params.bus;
this.listenTo(this.model, 'update', this.render);
},
render() {
this.model.each( (quote) => {
const quoteView = new QuoteView({
model: quote,
template: this.template,
bus: this.bus,
tagName: 'li',
className: 'quote',
});
this.listenTo(quoteView, 'quoteAction', this.trade);
this.$('#quotes').append(quoteView.render().$el);
});
return this;
},
trade: function(quoteView) {
const tradeTemplate = this.tradeTemplate(quoteView.model.toJSON());
this.$('#trades').prepend(tradeTemplate);
},
});

export default QuoteListView;
42 changes: 42 additions & 0 deletions src/views/quote_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Backbone from 'backbone';

const QuoteView = Backbone.View.extend({
initialize(params) {
this.template = params.template;
this.bus = params.bus;
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.bus, 'buyOrder', this.buyOrder);
this.listenTo(this.bus, 'sellOrder', this.sellOrder);
},
render() {
const compiledTemplate = this.template(this.model.toJSON());
this.$el.html(compiledTemplate);
return this;
},
events: {
'click button.btn-buy': 'buyQuote',
'click button.btn-sell': 'sellQuote',
},
buyQuote: function(event) {
this.model.set('buy', true);
this.trigger('quoteAction', this);
this.model.buy();
},
sellQuote: function(event) {
this.model.set('buy', false);
this.trigger('quoteAction', this);
this.model.sell();
},
buyOrder: function(orderQuote) {
if(orderQuote.model.get('symbol') === this.model.get('symbol')){
this.buyQuote();
}
},
sellOrder: function(orderQuote) {
if(orderQuote.model.get('symbol') === this.model.get('symbol')){
this.sellQuote();
}
}
});

export default QuoteView;