Skip to content
No description, website, or topics provided.
JavaScript Ruby CSS HTML CoffeeScript
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app Remove backend loading May 28, 2019
assets
bin Initialize GreenArrow project Feb 26, 2019
config
db Fix seeds file Apr 19, 2019
exchange_data
frontend Remove debuggers Jul 2, 2019
lib
log
public Set Favicon Apr 14, 2019
storage
test Add watch list actions Apr 10, 2019
tmp Initialize GreenArrow project Feb 26, 2019
vendor
.gitignore Initialize GreenArrow project Feb 26, 2019
.ruby-version Initialize GreenArrow project Feb 26, 2019
Gemfile Move BCrypt Gem Feb 28, 2019
Gemfile.lock Move BCrypt Gem Feb 28, 2019
README.md Update readme typo Apr 22, 2019
Rakefile Initialize GreenArrow project Feb 26, 2019
config.ru Initialize GreenArrow project Feb 26, 2019
package-lock.json
package.json
test.jsx Fix Sidecar import Mar 5, 2019
webpack.config.js Add Demo login Feb 27, 2019

README.md

Green Arrow

Green Arrow is a Robinhood clone that allows you to simulate investment strategies with accurate up-to-date stock prices, for free.

Live Link

Technologies

  • Frontend - React/Redux
  • Backend - Ruby on Rails, PostreSQL
  • IEX API - To query real-time stock data
  • Recharts - To interactively visualize stock price fluctuation
  • News API - To provide curated articles for relevant stock

Features

  • Secure user authentication using Bcrypt
  • Portfolio chart communicating performance of stocks owned
  • Search that covers 6564 stocks from the New York Stock Exchange and NASDAQ
  • Transaction system that ensures proper buying power / share ownership
  • News Index that provides the latest stock related news
  • Watchlists allow users to keep track of interested stocks
  • Sidebar lists all stocks in portfolio and watchlist upfront

Dashboard & Portfolio

Upon login, users are greeted with a dashboard hosting information including portfolio performance, stocks owned, stocks watched, and a news feed of recent events.

Stock Show

Several external API queries are made to pull the requisite stock data. Non-dependent API requests are made in parallel via Promise.all

export const fetchStock = symbol => dispatch => {
  const fetchAll = () => Promise.all([
    dispatch(fetchStockData(symbol)),
    dispatch(fetchStockIntradayData(symbol)),
    dispatch(fetchStockInfo(symbol)),
    dispatch(fetchStockNews(symbol))
  ]);

  StockApiUtil.fetchStock(symbol)
    .then(stock => dispatch(receiveStock(stock.tickerSymbol, stock)))
    .then(() => fetchAll());
};

A variety of mathematical calculations are required to communicate stock fluctuation data. The first step is to filter for the section of data relevant to a given interval. This is a necessary preprocessing step in a chosen tradeoff to minimize API calls, as all five years worth of data are pulled every query. Given the relevant slice of data, we choose the opening stock price from the relevant slice as our reference point. This reference point forms the basis of all our calculations, as can be seen in initialStockData.

filterData() {
  const { interval, stock  } = this.props;

  // returns relevant section of data given interval
  let range = INTERVAL_TO_AMOUNT_DATAPOINTS[interval];

  let data;
  if (interval === '1D') {
    data = stock.stockIntradayData;
    // Handle intraday data in 5 minute increments
    return data.filter((stock, i) => { 
      if (i % 5 === 0 && stock.close) return true;
    });
  } else {
    data = stock.stockData.slice(0);
    let end = this.calcEndIndex(data, range);
    return data.reverse().slice(0, end).reverse();
  }
}

findDiffReference() {
  const { interval, stock } = this.props;

  let data;
  if (interval === '1D') {
    data = stock.stockIntradayData;
  } else if (interval === '5Y') {
    data = stock.stockData;
  } else {
    let copy = stock.stockData.slice(0);
    const start = INTERVAL_TO_AMOUNT_DATAPOINTS[interval] + 1;
    const end = 2*INTERVAL_TO_AMOUNT_DATAPOINTS[interval] + 1;
    data = copy.reverse().slice(start, end).reverse(); 
  }
  return this.findReference(data);
}

findReference(data) {
  let values = Object.values(data);
  for (let i = 0; i < data.length - 1; i++) {
    let reference = values[i]
    if (reference) return reference.close;
  }
  return 0;
}

initialStockData(stock, reference) {
  const companyName = stock.companyName;
  const initPrice = this.calcInitPrice(stock);
  const priceDifferential = parseFloat((initPrice - reference).toFixed(2));
  const pctDifferential = ((initPrice - reference) / reference * 100).toFixed(2);
  return [
    companyName, 
    formatMoney(initPrice), 
    formatMoney(priceDifferential), 
    pctDifferential
  ];
}

Transaction System

The transaction system has a series of validation checks in the backend to ensure only proper transactions are carried through. A series of errors inform the users how to modify their transaction to be valid.

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

  transaction_total = @transaction.share_difference*@transaction.share_price
  shares_owned = current_user.portfolio_shares[@transaction.ticker_symbol]
  
  if @transaction.share_difference == 0
    render json: ["Please input valid share amount"], status: 401
  elsif @transaction.share_difference > 0 && transaction_total > current_user.current_buying_power
    render json: ["Insufficient Buying Power"], status: 401
  elsif @transaction.share_difference < 0 && shares_owned < @transaction.share_difference.abs
    render json: ["Insufficient Shares"], status: 401
  else
    if @transaction.save 
      render "api/transactions/show"
    else
      render json: @transaction.errors.full_messages, status: 422
    end
  end
end

Watchlist

Users can keep track of stocks they are interested in via watchlists. The stocks that form the watchlist can be viewed in the dashboard.

You can’t perform that action at this time.