Git Product home page Git Product logo

easytrade's Introduction

EasyTrade

EasyTrade, a Robinhood clone, is an investing application that allows users to purchase and sell shares of stock in publicly-traded companies

Live Demo

Technologies

Features

  • Secure frontend to backend user authentication using BCrypt
  • Real-time and historical price data of all stocks traded on the NASDAQ and NYSE exchanges
  • Interactive charts displaying stock price fluctuation over time as well as user's portfolio balance fluctuation overtime
  • Dashboard displaying share ownership and current market price of all stocks that make up a user's portfolio
  • Ability to simulate real stock-market trades by buying and selling shares at the most recent market price
  • Ability to search stocks by both their ticker symbol and Company name
  • Relevant news displayed for the general market on home page, and for specific stock on the stock's show page

Dashboard & Portfolio

Once a user logs in, they are immediately redirected to their dashboard, which shows a chart displaying their portfolio balance over time, a list of their holdings, and real-time news.



Portfolio Snapshots (New Feature)

In order to render charts that display a user's portfolio balance over time, daily, a 'snapshot' of the user's portfolio is taken. Through a simple association between the User and PortfolioSnapshot models, all of the user's historical portfolio data is fetched. Daily, at 22:00 UTC, the following rake task is run to scrape portfolio snapshots for every user.

task :add_portfolio_snapshots_for_day => :environment do
  puts "Adding day's portfolio snapshots..."

  # Grab today's date, skip if day is on weekend (markets are closed). Using 'next'
  # because you can't return in rake tasks
  date = Date.today
  next if date.on_weekend?
  
  # Grab all users
  users = User.all

  # Add day's snapshot for each user
  users.each do |user| 
    balance = user.calculate_balance
    PortfolioSnapshot.create({ date: date, balance: balance, user_id: user.id })
  end
  
  puts "done."
end

Stock Show Page

The stock show page contains current and historical price information about the stock, general company information, relevant news, and allows users to purchase and sell shares of the stock at the most recent market price. Colored elements of the page will be rendered in green if the chart being displayed shows a positive price fluctuation, and in red when the price fluctuation is negative.

Fetching Stock Information

When a stock show page is visited, a variety of API calls are made to fetch the necessary information to render the stock's price chart, information ('About' section) and relevant news articles. The following APIs are hit

  • IEX API - 4 separate API calls
    • Stock information 1 - basic info (symbol, company name, CEO, industry, etc.)
    • Stock information 2 - stats (employees, market cap, P/E ratio, etc.)
    • Intraday Price Data
    • Daily (Historical) Price Data
  • News API

A thunk fetchStock is used to perform all of these async API calls and ensure that nothing on the page is loaded until all of this information is received on the front-end. The fetchStock API Util fetches basic information about the stock, and adds its ticker to state - this is done first, and then a series of external API calls are made to fetch all additional information. As these calls do not rely upon each other, Promise.all is used to perform all fetches at the same time, only resolving once all fetches have completed.

export const fetchStock = ticker => dispatch => {
  const performFetches = () => Promise.all([
    dispatch(fetchStockInfo(ticker)),
    dispatch(fetchStockInfo2(ticker)),
    dispatch(fetchStockIntradayData(ticker)),
    dispatch(fetchStockDailyData(ticker)),
    dispatch(fetchStockNews(ticker))
  ]);

  StockApiUtil.fetchStock(ticker)
    .then(stock => dispatch(receiveStock(stock)))
    .then(performFetches);
};

Dynamic Chart Rendering

Charts are dynamic and interactive, allowing users to switch between ranges of 1D, 1W, 1M, 3M, 1Y, and 5Y for individual stocks or their overall portfolio (the 5Y range is replaced by the ALL range for portfolio chart). Buttons for each range appear below the chart with click handlers installed, which serve to update the React component's local state with the relevant chunk of data. The renderChart function takes in one of the aforementioned ranges as a string, using it to key into the RANGES hash to determine the appropriate portion of the dailyData to grab.

const RANGES = {
  '1W': { length: 5, increment: 1},
  '1M': { length: 23, increment: 1},
  '3M': { length: 66, increment: 1},
  '1Y': { length: 251, increment: 1},
  '5Y': { length: 1265, increment: 5},
};
renderChart(range) {
  let { dailyData } = this.state.initialData;
  let data = [];
  let startIdx = RANGES[range].length;
  if (startIdx > dailyData.length) startIdx = dailyData.length;
  let lastIdx;

  for(let i = dailyData.length - startIdx; i < dailyData.length; i+=RANGES[range].increment) {
    if (i < 0) i = 0;
    data.push({
      time: dailyData[i].date,
      price: dailyData[i].close
    });
    lastIdx = i;
  }

  // Set last date as most recent data point regardless
  if (lastIdx !== dailyData.length - 1) {
    data.push({
      time: dailyData[dailyData.length - 1].date,
      price: dailyData[dailyData.length - 1].close
    });
  }

  let { max, min, neg, currPrice, openPrice, priceFlux, priceFluxPercentage } = this.calculateDailyPriceData(data, dailyData.length - startIdx - 1);
  this.setState({
    currData: {
      data,
      currPrice,
      openPrice,
      priceFlux,
      priceFluxPercentage,
      min,
      max,
      neg,
      dailyData,
    },
    active: range
  });
}

A helper function, calculateDailyPriceData is used to calculate key price points that the chart needs to render appropriately including the current price, open price, high(max), low(min), price flux, and price flux percentage.

calculateDailyPriceData(data, startIdx) {
  let { dailyData } = this.state.initialData;
  let neg = "+";
  const prices = [];

  if (startIdx < 0) startIdx = 0;
  for (let i = 0; i < data.length; i++) {
    prices.push(parseFloat(data[i].price));
  }

  // calculate key price data points
  const max = Math.max(...prices);
  const min = Math.min(...prices);
  const currPrice = this.state.initialData.currPrice;
  const openPrice = dailyData[startIdx].close;
  const priceFlux = Math.round((parseFloat(currPrice) - parseFloat(openPrice)) * 100)/100;
  const priceFluxPercentage = Math.round(((parseFloat(currPrice) - parseFloat(openPrice))/parseFloat(openPrice)) * 10000)/100;
  if (priceFlux < 0) { neg = "-" ;}

  return {
    max,
    min,
    neg,
    currPrice,
    openPrice,
    priceFlux,
    priceFluxPercentage
  };
}

Transaction Validation

Users are only allowed to purchase shares of stock if they have adequate buying power. Additionally, they are only allowed to sell, at max, as many shares as they own. These checks are handled by the transactions controller on the back-end, and descriptive error messages will be rendered to the page if a user attempts to make an invalid transaction. The form will only submit and trigger a refresh of the page upon a valid transaction submitted by the user.

def create
  @transaction = Transaction.new(transaction_params)
  @transaction.user_id = current_user.id
  @transaction.transaction_date = Time.now

  transaction_amount = @transaction.price * @transaction.num_shares
  shares_owned = current_user.shares_owned(@transaction.stock_id)

  if transaction_amount > current_user.calculate_buying_power && @transaction.order_type == 'buy'
    render json: ['Not Enough Buying Power'], status: 401
  elsif @transaction.num_shares > shares_owned && @transaction.order_type == 'sell'
    render json: ['Not Enough Shares'], status: 401
  else
    if @transaction.save
      render json: ['success'], status: 200
    else
      render json: @transaction.errors.full_messages, status: 422
    end
  end
end

easytrade's People

Contributors

dependabot[bot] avatar ronilbhatia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easytrade's Issues

PA Review: Dashboard + Portfolio

  • Backend: user model methods to calculate portfolio balance overtime
  • Redux Loop: ajax, actions, reducers
  • Presentational Components
    • Portfolio Chart
    • Stock Dashboard
    • General Market News
  • Styling/Interactivity
    • Various presentational components are appropriately spaced out in line with Robinhood UI
    • Chart is interactive and will create a vertical line to nearest data point no matter where user is in chart box
  • Smooth, bug free navigation
    • Portfolio page renders with no errors
    • Users can easily switch between 1D, 1W, 1M, 3M, 1Y, and ALL charts
  • Adequate and appropriate seeds
    • There is enough data back-dated to render a full chart for all choices (1D, 1W, 1M, 3M, 1Y, ALL)

Asset/Stock Search

  • Backend: fetches all Stock ticker symbols and names to filter as search query is upated
  • Redux Loop: ajax, actions, reducers
  • Presentational Components
    • Stock Search Bar (input field)
    • Stock Search Results (unordered list)
  • Styling/Interactivity
    • Users can search for stocks by either typing in a ticker symbol or actual company name (search will check both automatically)
  • Smooth, bug free navigation
    • When users click outside of search bar, the results list will disappear while keeping user's query in the input field
    • User may navigate to stock page after clicking on search result with no errors
  • Adequate and appropriate seeds
    • Stocks include all assets traded on the NYSE an NASDAQ exchanges

PA Review: Ownerships

  • Backend: transaction data for user used to calculate current ownerships of stocks
  • Redux Loop: ajax, actions, reducers
  • Presentational Components
    • Stock buy/sell component
    • Stock Dashboard update after purchase/sale of stock when user navigates back home
  • Styling/Interactivity
    • Users can choose to buy or sell shares of stock
  • Smooth, bug free navigation
    • Stock page refreshes after purchase/sale of stock updating user's buying power and portfolio displayed on the home screen.
  • Adequate and appropriate seeds

Wiki Pages

Wiki Page Home

  • Is the first page you see upon entering the wiki
  • Contains a welcome message
  • Contains a link/placeholder for a link to the live page
  • All links in the right sidebar should contain each wiki page and link to the correct page
  • Correctly formatted
    • each wiki page is listed in bullet points
    • all links route the correct page

Comments:

Great work!


MVP List

  • Should have 7 MVPs.
    • 3 of those are User Auth, Heroku, and Production README.
    • The other 4 are from the MVP List or they have clarified them with you
  • Contains a description sentence of the app
  • At least one CRUD feature, which states what CRUD operations are planned (creation, reading, updating, deletion)
  • Estimates how long it will take the code each MVP
  • Correctly formatted
    • MVPs are listed in an ordered list
    • Each MVP is broken down into bullet points

Comments:

Great work! I would add more descriptions for each mvp. MVPs 3 to 6 have the same bullet points.


Database Schema

  • Contains correct datatypes

  • Contains appropriate constraints/details

    • primary key
    • not null
    • unique
    • indexed
    • foreign key
  • Contains bullet points after the table that state which foreign keys will reference to which table, or references to the associations which will be made

  • Correctly formatted

    • schema is written in a table format
    • the table's name are back_ticked
    • the table header column names are bolded
    • columns names are lowercased and snaked_cased and back_ticked

Comments:

Great work on your schema! Very thorough with all of the joins tables!

  • If you are using AWS for uploading a user image, you won't need the img_url field. You'll use an association to return an image. (You learn more about this as you learn AWS).
  • Did you want to add a max_shares column in the Stocks Table?
  • What is the purpose of the Stock Ownerships table?
  • What is the difference between Stock Watches and Collections ?

Sample State

  • State shape is flat!
  • State's keys are camelCased
  • All keys within the values in the state are accessible in the schema
  • Correctly formatted
    • Sample state is rendered with triple backticks, and the language ```javascript...```). This will display the state as a code block instead of a giant line of text
    • Top level slices
      • entities
      • session
      • errors (here or in ui)
      • ui (if needed)
    • Should NOT have nested slices, aka comments inside of posts
      • Some info from other tables is ok, for instance:
        • the author username and imageurl for a post. basically any info that the user can't change
        • like count and a boolean on whether the user likes the post instead of a likes slice

Comments:

  • in the users slice, make sure the imgUrls are images from aws, not google.

Routes

  • Contains the following sections: HTML, API Endpoints(Backend), and Frontend Routes
  • Each route has a description
  • API Endpoint routes contains wildcard variables written in snake_case
  • Frontend routes contains wildcard variables written in camelCase
  • Routes does not contain superfluous routes
  • Have API routes that will allow the front end to get all info it needs and does not have unneeded routes:
    • probably doesn't need a GET likes api endpoint bc that info comes through the post show
  • Correctly formatted
    • Routes are displayed with inline coding text (backticks)

Comments:

To add stock to user's watchlist, did you mean POST /api/stockwatches ? We won't need to pass the user's id since rails already knows thecurrent_user from Application Controller.
POST /api/users/:id/stock-watches


Overall

I am very excited about your project!

Let me know when you add more descriptions to your MVP list so I can get a sense of what you are accomplishing because I'm not sure how transactions fit into everything.

PA Review: Asset/Stock Detail

  • Backend: Stock ticker, stock name
  • Redux Loop: ajax, actions, reducers
  • Presentational Components
    • Stock About
    • Stock Chart
    • Stock News
  • Styling/Interactivity
    • Various presentational components are appropriately spaced out in line with Robinhood UI
    • Chart is interactive and will create a vertical line to nearest data point no matter where user is in chart box
  • Smooth, bug free navigation
    • Stock show page renders with no errors
    • Users can easily switch between 1D, 1W, 1M, 3M, 1Y, and 5Y charts
  • Adequate and appropriate seeds
    • Data includes every stock traded in the NYSE and NASDAQ exchanges

PA Review: User Auth

  • Backend: DB, model, controller, views
  • Redux Loop: ajax, actions, reducer
  • Presentational Components
  • Styling
    • When errors are displayed, the form adjusts properly (input boxes move up, sign in/sign up button moves down, and errors are displayed in between)
  • Smooth, bug free navigation
    • Login errors clears when switching from login form to sign up form (and vice-versa)
    • Going to a random route /#/oweiniouewbrviuwebv should redirect to either /, /login, or /signup
    • Errors should display for both /signup and/login.
    • Errors should clear when moving between /signup and /login.
  • Adequate and appropriate seeds
    • Demo Login Works

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.