Skip to content
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 - Sara Frandsen - Ada Trader #29

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
00cfe83
import quote view
Dec 12, 2017
f0e9047
create empty history_view.js
Dec 12, 2017
e6b48a5
create empty open_order_view.js
Dec 12, 2017
fa97c21
create quote_view.js, basic setup
Dec 12, 2017
19c25e3
deleted history_view.js
Dec 13, 2017
c34dd5d
made separation of html and templates more clear
Dec 13, 2017
d423c4a
deleted open_order_view.js
Dec 13, 2017
9fdef1a
create trade_view.js
Dec 13, 2017
2a50327
create trade_list_view.js
Dec 13, 2017
3b08c76
create quote_list_view.js
Dec 13, 2017
437fa43
create functions buy() and sell() to increase/decrease the price by 1
Dec 13, 2017
1d24a55
create functions buy() and sell() to increase/decrease the price by 1
Dec 13, 2017
d7e129e
quote data appears, sell/buy buttons do not yet work
Dec 13, 2017
98c6824
fixed bugs, the simulator now WORKS
Dec 13, 2017
0ae260e
create Trade model
Dec 13, 2017
8c44ede
create Trade collection
Dec 13, 2017
9e7e144
import new files for Trade, set up rendering trade list template
Dec 13, 2017
4d29dbd
create trade list view (based on quote list view)
Dec 13, 2017
44fb593
create trade view (based on quote view)
Dec 13, 2017
c9c6eb9
add to buy/sell functions to 'get' symbols and prices
Dec 13, 2017
25d1124
remove temporary history
Dec 13, 2017
f5226b3
adds to history when buy or sell is clicked (just needed to move wher…
Dec 13, 2017
df84801
create files for orders
Dec 13, 2017
2e24458
refactor using bus after Charles review
Dec 14, 2017
6cfb3df
populate order dropdown with symbols using .map
Dec 14, 2017
87a37ab
minor spacing changes and comments before trying to work out the form
Dec 14, 2017
1a2dccf
form collects data, does not yet post to open orders
Dec 15, 2017
eefb9ae
create addOrder and sellOrder, will refactor into newOrder later
Dec 15, 2017
966e4e2
change names of variables
Dec 16, 2017
13a57dd
recognizes buy or sell, need to refactor form values
Dec 17, 2017
8ed6409
'this.model.add in order_list_view to add orders to a list'
Dec 17, 2017
20a67e2
delete order when cancel is clicked
Dec 17, 2017
0237a41
logging shows the order model is listening to the quote model
Dec 17, 2017
f79d84a
order deletes when price matches
Dec 17, 2017
beddb76
adds HABIT order to history. Check bySymbol filter in quote_list for bug
Dec 18, 2017
23fb6a2
adds orders to history when buy or sell is met
Dec 18, 2017
dc8777f
validate price entered with error message
Dec 18, 2017
7d64b5f
create spec file for order test
Dec 18, 2017
a0c2540
prettify text, add comments, remove logs
Dec 18, 2017
869c6a1
comment out test
Dec 18, 2017
9f2c2ef
move around validations
Dec 18, 2017
acc00e5
resolve merge conflicts
Dec 18, 2017
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
19 changes: 13 additions & 6 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<h1 class="logo">Ada Trader</h1>
</header>

<div class="workspace row">
<div id="workspace" class="workspace row">

<div class="columns large-8 small-12">

Expand All @@ -33,6 +33,7 @@ <h2>Quotes</h2>
<h2>Trade History</h2>
<div class="trades-list-container">
<ul id="trades" class="trades">
<!-- prepend history here -->
</ul>
</div>
</div>
Expand All @@ -57,16 +58,16 @@ <h2>Open Orders</h2>
<div class="columns large-4 small-12 trade-column">
<div class="order-entry-form columns small-10 small-offset-1">
<h3>Order Entry Form</h3>
<form>
<form id="order-entry-form">
<label for="symbol">Symbol</label>
<select name="symbol">
<!-- Option entries should be added here using JavaScript -->
</select>
<label for="price-target">Price</label>
<input type="number" name="price-target" step="0.10" min="0.00" placeholder="100.00" />
<label for="target-price">Price</label>
<input type="number" name="target-price" step="0.10" min="0.00" placeholder="100.00" />
<label>Action</label>
<button class="btn-buy alert button">Buy</button>
<button class="btn-sell success button">Sell</button>
<button id="order-buy" class="btn-buy alert button">Buy</button>
<button id="order-sell" class="btn-sell success button">Sell</button>
</form>
<div class="form-errors">
</div>
Expand All @@ -82,6 +83,8 @@ <h3>Order Entry Form</h3>
</div>
</footer>

<!-- ////////////////////////////////////////// -->

<script type="text/template" id="quote-template">
<!-- The provided styles assume that you will insert this template
within an element with the class "quote" applied -->
Expand All @@ -97,6 +100,8 @@ <h3 class="price">$<%- price.toFixed(2) %></h3>
<!-- </li> -->
</script>

<!-- ////////////////////////////////////////// -->

<script type="text/template" id="trade-template">
<li class="trade">
<span>
Expand All @@ -105,6 +110,8 @@ <h3 class="price">$<%- price.toFixed(2) %></h3>
</li>
</script>

<!-- ////////////////////////////////////////// -->

<script type="text/template" id="order-template">
<h3 class="symbol"><%- symbol %></h3>
<div class="detail">
Expand Down
44 changes: 44 additions & 0 deletions spec/models/order_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Order from 'models/order';
import Quote from 'models/quote';

describe('Order spec', () => {
let order;
beforeEach(() => {
order = new Order({
symbol: 'HELLO',
price: 100.00,
});
});

describe('validate', () => {
it('validates there is a target price', () => {
const prices = ['', NaN, undefined, 0]

prices.forEach((price)=> {
const order = new Order({
targetPrice: price
});

expect(order.isValid()).toBeFalsy();
});
});



// it('buy price is not >= to the current market price', () => {
// const prices = [100,1000];
//
// prices.forEach((price)=> {
// const order = new Order({
// targetPrice: price,
// buy: true,
// });
//
// expect(order.isValid()).toBeFalsy();
// });
// });



});
});
51 changes: 50 additions & 1 deletion src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@ import 'foundation-sites/dist/foundation.css';
import 'css/app.css';

import $ from 'jquery';

import _ from 'underscore';
import Backbone from 'backbone';
//models
import Simulator from 'models/simulator';
//collections
import TradeList from 'collections/trade_list';
import QuoteList from 'collections/quote_list';
import OrderList from './collections/order_list';
//views
import QuoteListView from 'views/quote_list_view';
import TradeListView from 'views/trade_list_view';
import OrderListView from 'views/order_list_view';

const quoteData = [
{
Expand All @@ -26,10 +35,50 @@ const quoteData = [
];

$(document).ready(function() {
const bus = _.extend({}, Backbone.Events);
const quotes = new QuoteList(quoteData);
const tradeList = new TradeList();
const orderList = new OrderList();
const simulator = new Simulator({
quotes: quotes,
});

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

///// trade list view /////
const tradeListView = new TradeListView({
model: tradeList,
template: _.template($('#trade-template').html()),
el: '#workspace',
bus: bus,
})
tradeListView.render();

quoteData.map(quote => quote.symbol).forEach((symbol) => {
$("select[name='symbol']").append($('<option>',
{
value: symbol,
text: symbol,
}));
});

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

///// simulator /////
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;
7 changes: 7 additions & 0 deletions src/collections/quote_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import Quote from 'models/quote';

const QuoteList = Backbone.Collection.extend({
model: Quote,

bySymbol: function(symbol) {
const filtered = this.filter(function (quote) {
return quote.get('symbol') === symbol;

Choose a reason for hiding this comment

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

I like that you broke this out as a separate function on the collection. Good organization!

});
return filtered[0];
},
});

export default QuoteList;
8 changes: 8 additions & 0 deletions src/collections/trade_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Backbone from 'backbone';
import Trade from 'models/trade';

const TradeList = Backbone.Collection.extend({
model: Trade,
});

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

const Order = Backbone.Model.extend({
initialize(params) {
this.buy = params.buy;
this.targetPrice = params.targetPrice;
this.symbol = params.symbol;

Choose a reason for hiding this comment

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

Backbone should copy all this data into the attributes automatically, which you can then access with this.get('targetPrice').

this.quotes = params.quotes;
},

validate(params) {
const errors = {};
if (!params.targetPrice) {
errors['price'] = 'Invalid target price!';
}

// if (params.targetPrice >= matchingQuote.attributes.price) {
// errors['buy'] = 'Less than current 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
11 changes: 11 additions & 0 deletions src/models/trade.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Backbone from 'backbone';

const Trade = Backbone.Model.extend({
initialize(params) {
this.symbol = params.symbol;
this.buy = params.buy;
this.price = params.price;

Choose a reason for hiding this comment

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

Again, should not need to explicitly copy attributes here.

},
});

export default Trade;
74 changes: 74 additions & 0 deletions src/views/order_list_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Backbone from 'backbone';
import $ from 'jquery';
import Order from '../models/order';
import OrderView from './order_view';

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() {
const list = this.$('#orders');
list.empty();
this.model.each((order) => {
const orderView = new OrderView({
model: order,
template: this.template,
tagName: 'li',
className: 'order',
quotes: this.quotes,
bus: this.bus,
});
list.prepend(orderView.render().$el);
});
return this;
},

events: {
'click .btn-buy': 'buyOrder',
'click .btn-sell': 'sellOrder',
},

createOrder(buy) {
event.preventDefault();
$('.form-errors').empty(); // clear errors when new order is placed
const symbol = this.$(`[name=symbol]`).val();
const targetPrice = parseFloat(this.$(`[name=target-price]`).val());
const orderData = {
buy, // buy: buy
targetPrice, // targetPrice: targetPrice
symbol, // symbol: symbol
};

const newOrder = new Order(orderData);

if (newOrder.isValid()) {
this.model.add(newOrder); // add to order list
} else {
newOrder.destroy();
this.errorMessage(newOrder.validationError);
}
},

errorMessage(errors) {
Object.entries(errors).forEach((error)=> {
$('.form-errors').prepend(`<h3>${error[1]}</h3>`);
})
},

buyOrder: function(event) {
event.preventDefault();
const order = this.createOrder(true);
},

sellOrder: function(event) {
event.preventDefault();
const order = this.createOrder(false);
},
});

export default OrderListView;
54 changes: 54 additions & 0 deletions src/views/order_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Backbone from 'backbone';
import Trade from '../models/trade';

const OrderView = Backbone.View.extend({
initialize(params) {
this.template = params.template;
this.bus = params.bus;
this.quotes = params.quotes;
this.listenTo(this, 'deleteOrder', this.deleteOrder);
this.listenTo(params.quotes, 'change', this.placeOrder);
},

render() {
const compiledTemplate = this.template(this.model.toJSON());
this.$el.html(compiledTemplate);
return this;
},

events: {
'click button.btn-cancel': 'deleteOrder',
},

deleteOrder() {
this.model.destroy();
this.remove();
},

placeOrder() {
const trade = new Trade({
buy: this.model.buy,
price: this.model.targetPrice,
symbol: this.model.symbol,
});

// bySymbol is custom filter created in quote_list
const matchingQuote = this.quotes.bySymbol(this.model.symbol);

if (this.model.buy) {
if (this.model.targetPrice < matchingQuote.attributes.price) {
matchingQuote.buy();
this.bus.trigger('addTrade', trade); // newTrade() in trade_list_view
this.trigger('deleteOrder'); // deleteOrder() above ^^

Choose a reason for hiding this comment

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

Why not call this.deleteOrder() directly?

}
} else if (!this.model.buy) {
if (this.model.targetPrice > matchingQuote.attributes.price) {
matchingQuote.sell();
this.bus.trigger('addTrade', trade); // newTrade() in trade_list_view
this.trigger('deleteOrder'); // deleteOrder() above ^^
}
}
},
});

export default OrderView;
Loading