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

finish implementation #3

Open
wants to merge 70 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
2c35763
add routes and navigation
christianflorez Jul 9, 2017
c747448
add gitignore
christianflorez Jul 9, 2017
259adef
add node modules to gitignore
christianflorez Jul 9, 2017
a588b28
fix repo content
christianflorez Jul 9, 2017
50d7837
add empty datepicker component
christianflorez Jul 10, 2017
99038dd
add basic input slider
christianflorez Jul 10, 2017
77db1ef
add functioning state to date-picker
christianflorez Jul 10, 2017
fd493bc
add form to date picker
christianflorez Jul 10, 2017
5b1a2a2
finish date picker
christianflorez Jul 10, 2017
fa7a033
add style changes
christianflorez Jul 10, 2017
2842521
add close button to datepicker
christianflorez Jul 10, 2017
4e25cf1
add on blur listener for datepicker
christianflorez Jul 10, 2017
b6367d0
begin api endpoint setup
christianflorez Jul 10, 2017
ede5f05
add parsing and error handling to api endpoint
christianflorez Jul 10, 2017
40aced6
begin results data parsing
christianflorez Jul 10, 2017
bff17e5
finish endpoint parsing
christianflorez Jul 10, 2017
2af7cf1
clean up helpers file
christianflorez Jul 10, 2017
862fcbb
make symbols query in api optional
christianflorez Jul 10, 2017
89f4c9f
add reducers
christianflorez Jul 10, 2017
5494837
add actions.js and boilerplate to index.js
christianflorez Jul 10, 2017
2bb133c
add stock data container
christianflorez Jul 10, 2017
df4365b
add functioning stock data table
christianflorez Jul 10, 2017
76baa5a
fix css bug
christianflorez Jul 10, 2017
594724c
add panel to date picker
christianflorez Jul 10, 2017
e227364
add date picker container functionality
christianflorez Jul 10, 2017
9d914c8
add decimal changes
christianflorez Jul 11, 2017
ff43acd
begin filter functionality
christianflorez Jul 11, 2017
686c2db
finish filter
christianflorez Jul 11, 2017
1a6d928
add style changes to buttons
christianflorez Jul 11, 2017
7f2b123
add sorting
christianflorez Jul 11, 2017
d458a6f
improve filter
christianflorez Jul 11, 2017
5acf3e4
fix reducer bug
christianflorez Jul 11, 2017
4626598
make filter case insensitive
christianflorez Jul 11, 2017
873e46a
add overflow settings to stock data panel
christianflorez Jul 11, 2017
ff53226
add actions and reducers for specific stock data
christianflorez Jul 11, 2017
183c464
add working event handler for getting specific stock data
christianflorez Jul 11, 2017
7ebcdd8
modify date picker to update specific stock info
christianflorez Jul 11, 2017
06b7c03
begin trades container implementation
christianflorez Jul 11, 2017
96d831f
dynamically update total
christianflorez Jul 11, 2017
8ce9afe
update coding style
christianflorez Jul 11, 2017
ee4c997
refactor trade form
christianflorez Jul 11, 2017
d9e6b22
update code style for trades components
christianflorez Jul 11, 2017
0f66d9d
add transactions and balance state
christianflorez Jul 11, 2017
94591fa
update loading styles
christianflorez Jul 11, 2017
730f163
add prompt to form
christianflorez Jul 11, 2017
ded84f5
fix form submission
christianflorez Jul 11, 2017
c3b0526
add transactions component boilerplate
christianflorez Jul 11, 2017
c46b240
fix form types bug
christianflorez Jul 11, 2017
050c581
update transactions container
christianflorez Jul 11, 2017
b270a23
add portfolio actions and reducers
christianflorez Jul 11, 2017
7d93f08
add portfolio and trade selling
christianflorez Jul 11, 2017
8713c1a
rename variables for easier reading
christianflorez Jul 11, 2017
a463ccf
add table sorting with queries
christianflorez Jul 11, 2017
4a16249
add links for table sorting
christianflorez Jul 11, 2017
b012487
fixed trading logic
christianflorez Jul 11, 2017
9cb5d05
update coding style with auto formatter
christianflorez Jul 11, 2017
0fcb1ba
update stock data when selected trade stock changes
christianflorez Jul 11, 2017
0426827
add basic filtering
christianflorez Jul 12, 2017
5c5d268
finish filtering
christianflorez Jul 12, 2017
8787183
refactor
christianflorez Jul 12, 2017
7fe9011
modify panel styles
christianflorez Jul 12, 2017
1271765
add portfolio boilerplate
christianflorez Jul 12, 2017
5a42e5a
add portfolio helper function for individual stocks
christianflorez Jul 12, 2017
29c78e2
flesh out portfolio tables with trade button
christianflorez Jul 12, 2017
707be22
create grand totals helper function
christianflorez Jul 12, 2017
1db994c
finish project
christianflorez Jul 12, 2017
83a82c0
add deployment link
christianflorez Jul 12, 2017
fcc8551
fix table overflow
christianflorez Jul 13, 2017
f91b5b2
update readme
christianflorez Jul 24, 2017
8295f76
add img to readme
christianflorez Jul 29, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
/node_modules
mock-data.json
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"**/src/**/*.js": "javascriptreact"
}
}
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,36 @@
# project_fideligard_spa
Buy low, sell high.
# Project Fideligard

[![Project Fideligard](http://i.imgur.com/N1OweUa.png)](https://pure-dawn-86561.herokuapp.com/)

## Introduction
Project Fideligard is a historical stock portfolio simulator. It leverages Quandl's stock API to provide End-of-Day stock prices.

## Technologies Used
Express is used in the back-end to provide a customized API for the client. The front-end is built entirely in React/Redux and uses react-router to provide navigation between different sections of the application. Please view the "Additional Notes" section at the end of this readme for some information regarding the implementation.

## Getting Started
For setting up a local version of the project, first clone the repository to your local machine. Then, install the dependencies using `yarn` or `npm install`. You will need to obtain a (free) Quandl API key from [their website.](https://www.quandl.com/) Then, set up an environment variable (preferably by creating a .env file inside the root directory) and setting your API key in the following manner:

```
QUANDL_API_KEY=YOUR_SECRET_KEY_HERE
```

If you would like to change the minimum date for stock data, simply change the appropriate static variables located in the `DatePicker.js` component. You will need to manually calculate the Unix timestamp for your chosen date.

Finally, simply run `yarn start`/`npm start` to get the application running.

## Deployment Link
A deployed version of this project may be found [here.](https://pure-dawn-86561.herokuapp.com/)

## Additional Notes
Of interest is the helpers.js file in the root directory, which is used to help the back-end manually construct the API from the data provided by Quandl.

Quandl's API returns data in a largely unhelpful format (a giant array of individual stock prices sorted alphabetically and by date). I created several helpful iterators to sort through the data. Since End-of-Day prices are not available for weekends, I also created some helpful functions to deal with these cases. If a user provides a weekend as a date, the API automatically converts this by looking backwards to find the most recent weekday date.

To deal with parsing these dates, I used the js-joda library. Although Moment.js is frequently used for date parsing within the Node.js world, Moment.js provides a thin wrapper around JavaScript's built-in mutable Date library. js-joda is a a fast and immutable library which proved to be a very pleasant alternative to the common options.

This helpers.js file uses js-joda extensively to parse all the dates. In addition to ensuring that the user provided date is not a weekend, it also ensures that the 1 day/7 day/30 day lookback dates are also not weekends.

On the front end, some significant details to note are the date-picker component. Built from the ground up, you can edit the date manually by simply clicking on the date itself. Clicking away automatically cancels the input using the `onBlur` event listener.

The project uses cookies to persist the state of your portfolio. The trade form will automatically reject trades when the user does not have enough balance/stock to make a transaction. There are many sortable fields in the different tables. Sorting/searching results can be saved for the transactions table using url query strings.
21 changes: 21 additions & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.

# dependencies
/node_modules

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
2,138 changes: 2,138 additions & 0 deletions client/README.md

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "client",
"version": "0.1.0",
"private": true,
"license": "ISC",
"proxy": "http://localhost:3001/",
"dependencies": {
"bootswatch": "^3.3.7",
"react": "^15.6.1",
"react-bootstrap": "^0.31.0",
"react-dom": "^15.6.1",
"react-redux": "^5.0.5",
"react-router-bootstrap": "^0.24.2",
"react-router-dom": "^4.1.1",
"react-tap-event-plugin": "^2.0.1",
"redux": "^3.7.1",
"redux-thunk": "^2.2.0"
},
"devDependencies": {
"react-scripts": "1.0.10"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
Binary file added client/public/ajax-loader.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/favicon.ico
Binary file not shown.
42 changes: 42 additions & 0 deletions client/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">

<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Fideligard</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
15 changes: 15 additions & 0 deletions client/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
8 changes: 8 additions & 0 deletions client/src/App.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

it("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(<App />, div);
});
158 changes: 158 additions & 0 deletions client/src/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
export const SET_DATE = "SET_DATE";
export const SET_FILTER = "SET_FILTER";
export const RESET_FILTER = "RESET_FILTER";
export const ADD_STOCK_TO_LIST = "ADD_STOCK_TO_LIST";
export const GET_STOCKS_SUCCESS = "GET_STOCKS_SUCCESS";
export const GET_STOCKS_REQUEST = "GET_STOCKS_REQUEST";
export const GET_STOCKS_FAILURE = "GET_STOCKS_FAILURE";
export const GET_SPECIFIC_STOCK_SUCCESS = "GET_SPECIFIC_STOCK_SUCCESS";
export const GET_SPECIFIC_STOCK_REQUEST = "GET_SPECIFIC_STOCK_REQUEST";
export const GET_SPECIFIC_STOCK_FAILURE = "GET_SPECIFIC_STOCK_FAILURE";
export const SET_SORT_ASCENDING = "SET_SORT_ASCENDING";
export const SET_SORT_DESCENDING = "SET_SORT_DESCENDING";
export const ADD_TRANSACTION = "ADD_TRANSACTION";
export const UPDATE_BALANCE = "UPDATE_BALANCE";
export const UPDATE_PORTFOLIO = "UPDATE_PORTFOLIO";

export function getStocksRequest() {
return {
type: GET_STOCKS_REQUEST
};
}

export function getStocksSuccess(data) {
return {
type: GET_STOCKS_SUCCESS,
data
};
}

export function getStocksFailure(error) {
return {
type: GET_STOCKS_FAILURE,
error
};
}

export function getSpecificStockRequest() {
return {
type: GET_SPECIFIC_STOCK_REQUEST
};
}

export function getSpecificStockSuccess(data) {
return {
type: GET_SPECIFIC_STOCK_SUCCESS,
data
};
}

export function getSpecificStockFailure(error) {
return {
type: GET_SPECIFIC_STOCK_FAILURE,
error
};
}

export function setDate(data) {
return {
type: SET_DATE,
data
};
}

export function setFilter(data = "") {
return {
type: SET_FILTER,
data: data.toUpperCase()
};
}

export function resetFilter(data) {
return {
type: RESET_FILTER
};
}

export function addStockToList(data) {
return {
type: ADD_STOCK_TO_LIST,
data
};
}

export function setSortAscending() {
return {
type: SET_SORT_ASCENDING
};
}

export function setSortDescending() {
return {
type: SET_SORT_DESCENDING
};
}

export function addTransaction(data) {
return {
type: ADD_TRANSACTION,
data
};
}

export function updateBalance(data) {
return {
type: UPDATE_BALANCE,
data
};
}

export function updatePortfolio(data) {
return {
type: UPDATE_PORTFOLIO,
data
};
}

// @param {array} stocks
export function getStocks(stocks, date) {
return dispatch => {
dispatch(getStocksRequest());

fetch(`api/stocks?symbols=${stocks.toString()}&date=${date}`)
.then(response => {
if (!response.ok) {
throw new Error(`${response.status}: ${response.statusText}`);
}

return response.json();
})
.then(json => {
dispatch(getStocksSuccess(json.data));
})
.catch(error => {
dispatch(getStocksFailure(error));
});
};
}

export function getSpecificStock(stock, date) {
return dispatch => {
dispatch(getSpecificStockRequest());

fetch(`api/stocks?symbols=${stock.toString()}&date=${date}`)
.then(response => {
if (!response.ok) {
throw new Error(`${response.status}: ${response.statusText}`);
}

return response.json();
})
.then(json => {
dispatch(getSpecificStockSuccess(json.data[stock]));
})
.catch(error => {
dispatch(getSpecificStockFailure(error));
});
};
}
43 changes: 43 additions & 0 deletions client/src/components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { Grid, Row, Col } from "react-bootstrap";
import ScrollToTop from "./ScrollToTop";
import Navigation from "./Navigation";
import StockDataContainer from "../containers/StockDataContainer";
import DatePickerContainer from "../containers/DatePickerContainer";
import TradesContainer from "../containers/TradesContainer";
import TransactionsContainer from "../containers/TransactionsContainer";
import PortfolioContainer from "../containers/PortfolioContainer";
import Failure from "./Failure";
import Success from "./Success";

class App extends Component {
render() {
return (
<Router>
<ScrollToTop>
<Navigation title={"Fideligard"} />
<Grid fluid>
<Row>
<h1>Fideligard</h1>
<Col md={5} mdOffset={1} xs={12}>
<DatePickerContainer />
<StockDataContainer />
</Col>
<Switch>
<Route path="/trades" component={TradesContainer} />
<Route path="/transactions" component={TransactionsContainer} />
<Route path="/portfolio" component={PortfolioContainer} />
<Route path="/failure" component={Failure} />
<Route path="/success" component={Success} />
<Route exact path="/" component={PortfolioContainer} />
</Switch>
</Row>
</Grid>
</ScrollToTop>
</Router>
);
}
}

export default App;
Loading