-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pipes - Kee - Ada Trader #36
base: master
Are you sure you want to change the base?
Changes from all commits
b9894ac
d4c4c43
c78b267
98ba06a
6d32d2b
c99265e
69c5022
b53a938
c526323
3686427
a8a5d9d
8b18733
a814499
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import Order from 'models/order'; | ||
import Quote from 'models/quote'; | ||
|
||
describe('Order spec', () => { | ||
let order; | ||
let quote; | ||
beforeEach(() => { | ||
quote = new Quote({ | ||
symbol: 'DOGS', | ||
price: 66, | ||
}); | ||
}); | ||
|
||
describe('Need a targetPrice', () => { | ||
it('is invalid without a targetPrice', () => { | ||
order = new Order({ | ||
symbol: 'DOGS', | ||
buy: 'buy', | ||
quote: quote, | ||
}) | ||
|
||
expect(order.isValid()).toBeFalsy(); | ||
}); | ||
it('is valid with a targetPrice', () => { | ||
order = new Order({ | ||
symbol: 'DOGS', | ||
targetPrice: 55, | ||
buy: 'buy', | ||
quote: quote, | ||
}) | ||
|
||
expect(order.isValid()).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('Need valid targetPrice', () => { | ||
it('(BUY) must have targetPrice lower than trade price', () => { | ||
order = new Order({ | ||
symbol: 'DOGS', | ||
targetPrice: 999, | ||
buy: "buy", | ||
quote: quote, | ||
}) | ||
expect(order.isValid()).toBeFalsy(); | ||
|
||
order.set('targetPrice', 66) | ||
expect(order.isValid()).toBeFalsy(); | ||
|
||
order.set('targetPrice', 55) | ||
expect(order.isValid()).toBeTruthy(); | ||
}); | ||
|
||
it('(SELL) must have a targetPrice higher than trade price', () => { | ||
order = new Order({ | ||
symbol: 'DOGS', | ||
targetPrice: 44, | ||
buy: "sell", | ||
quote: quote, | ||
}) | ||
expect(order.isValid()).toBeFalsy(); | ||
|
||
order.set('targetPrice', 66) | ||
expect(order.isValid()).toBeFalsy(); | ||
|
||
order.set('targetPrice', 77) | ||
expect(order.isValid()).toBeTruthy(); | ||
}); | ||
|
||
}); | ||
|
||
}); |
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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import Backbone from 'backbone'; | ||
|
||
const Order = Backbone.Model.extend({ | ||
validate(attributes) { | ||
const errors = {}; | ||
|
||
if (!attributes.targetPrice) { | ||
errors['targetPrice'] = 'A target price is required.'; | ||
} | ||
|
||
if(attributes.buy == "buy" && | ||
attributes.targetPrice >= attributes.quote.get('price')) { | ||
errors['targetPrice'] = ['The target price must be lower than the trade price']; | ||
} | ||
|
||
if(attributes.buy === "sell" && | ||
attributes.targetPrice <= attributes.quote.get('price')){ | ||
errors['targetPrice'] = ['The target price must be higher than the trade price']; | ||
} | ||
|
||
if ( Object.keys(errors).length > 0 ) { | ||
return errors; | ||
} else { | ||
return false; | ||
} | ||
|
||
}, | ||
}); | ||
|
||
export default Order; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import Backbone from 'backbone'; | ||
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.quoteList; | ||
|
||
this.listenTo(this.model, 'update', this.render); | ||
this.listenTo(this.bus, 'quote_change', this.buyOrSellOrder); | ||
|
||
this.displayOrderForm(); | ||
}, | ||
|
||
render() { | ||
this.$('#orders').empty(); | ||
|
||
this.model.forEach((order) => { | ||
const orderView = new OrderView({ | ||
model: order, | ||
template: this.template, | ||
tagName: 'li', | ||
className: 'order', | ||
bus: this.bus, | ||
}); | ||
this.$('#orders').append(orderView.render().$el); | ||
}); | ||
|
||
return this; | ||
}, | ||
|
||
events: { | ||
'click button.btn-buy': 'buyOrder', | ||
'click button.btn-sell': 'sellOrder', | ||
}, | ||
|
||
displayOrderForm() { | ||
this.quotes.each((quote) => { | ||
const symbol = quote.get('symbol'); | ||
this.$('.order-entry-form select').append(`<option value="${symbol}">${symbol}</option>`); | ||
}); | ||
}, | ||
|
||
getFormData(action) { | ||
const orderData = {}; | ||
|
||
orderData['symbol'] = this.$('.order-entry-form select[name="symbol"] option:selected').val(); | ||
orderData['quote'] = this.quotes.findWhere({symbol: orderData['symbol']}); | ||
orderData['targetPrice'] = Number(this.$('.order-entry-form input[name="price-target"]').val()); | ||
orderData['buy'] = action; | ||
|
||
return orderData; | ||
}, | ||
|
||
clearFormData() { | ||
this.$('.order-entry-form input[name="price-target"]').val(''); | ||
}, | ||
|
||
buyOrder(event) { | ||
this.addOrder(event, 'buy'); | ||
}, | ||
|
||
sellOrder(event) { | ||
this.addOrder(event, 'sell'); | ||
}, | ||
|
||
addOrder(event, action) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good work using a helper method to DRY up your code here! |
||
event.preventDefault(); | ||
|
||
const formData = this.getFormData(action); | ||
|
||
const newOrder = new Order(formData); | ||
this.model.add(newOrder); | ||
this.clearFormData(); | ||
}, | ||
|
||
buyOrSellOrder(quote) { | ||
let currentOrders = this.model.where({symbol: quote.get('symbol')}); | ||
currentOrders.forEach((order) => { | ||
|
||
if (order.get('buy') == 'buy' && order.get('targetPrice') >= quote.get('price')) { | ||
let tradeItem = { | ||
price: order.get('targetPrice'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit of logic inside the |
||
symbol: order.get('symbol'), | ||
buy: 'bought', | ||
} | ||
this.bus.trigger('boughtOrSold', tradeItem); | ||
order.destroy(); | ||
quote.buy(); | ||
} | ||
|
||
else if (order.get('buy') == 'sell' && order.get('targetPrice') <= quote.get('price')) { | ||
let tradeItem = { | ||
price: order.get('targetPrice'), | ||
symbol: order.get('symbol'), | ||
buy: 'sold', | ||
} | ||
this.bus.trigger('boughtOrSold', tradeItem) | ||
order.destroy(); | ||
quote.sell(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, the workflow for executing an open order looks like this:
But we can simplify this! The key observation is that when you create the
This eliminates dependencies on the bus and the |
||
} | ||
}); | ||
} | ||
}); | ||
|
||
export default OrderListView; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import Backbone from 'backbone'; | ||
import Order from '../models/order'; | ||
|
||
const OrderView = Backbone.View.extend({ | ||
initialize(params) { | ||
this.template = params.template; | ||
}, | ||
|
||
render() { | ||
const compiledTemplate = this.template(this.model.toJSON()); | ||
this.$el.html(compiledTemplate); | ||
|
||
return this; | ||
}, | ||
|
||
events: { | ||
'click button.btn-cancel': 'cancel', | ||
}, | ||
|
||
cancel(event) { | ||
this.model.destroy(); | ||
}, | ||
|
||
}); | ||
|
||
export default OrderView; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that the
OrderListView
knows about theQuoteList
. I've seen several students attempt to achieve the same thing using the bus as an intermediary, but the code ends up being much more complex.