Stock Market Game - A React & Ruby on Rails Project

Stock Market Game - A React & Ruby on Rails Project

A React & Redux frontend with a Ruby on Rails API

For my final project at Flatiron, I created The Anonymous Stock Trading Game. It was an idea I had several months ago and decided to make it my final project.

My original idea was to show the first half of an anonymous chart before the game starts. Then once the game begins, update the chart with each new day and see how much a player could make (or lose).

I started sketching my idea out and soon realized the game would be more fun if the player was given no historical price context at all! Just a blank chart that only starts to update once the game begins!

stock_game_sketch.png

The project requirements were:

  1. The code should be written in ES6 as much as possible
  2. Use the create-react-app generator to start your project.
  3. Follow the instructions on this repo to setup the generator: create-react-app
  4. Your app should have one HTML page to render your react-redux application
  5. There should be 2 container components
  6. There should be 5 stateless components
  7. There should be 3 routes
  8. The Application must make use of react-router and proper RESTful routing (should you choose to use react-router v3 please refer to the appropriate docs; docs for v4 can be found here)
  9. Use Redux middleware to respond to and modify state change
  10. Make use of async actions to send data to and receive data from a server
  11. Your Rails API should handle the data persistence. You should be using fetch() within your actions to GET and POST data from your API - do not use jQuery methods.
  12. Your client-side application should handle the display of data with minimal data manipulation
  13. Your application should have some minimal styling: feel free to stick to a framework (like react-bootstrap), but if you want to write (additional) CSS yourself, go for it!

Getting Started

I decided to being with the API and the first step was to map out the models and relationships.

stock_game_relationships.png

Next I needed to find a source of historical stock prices. After doing some research I came across the Alpha Vantage API. This seemed like a better approach than scraping or importing data.

I ran into a couple of issues using the Alpha Vantage API.

The first was I didn’t want my API to run into any issues requesting data from another API just for the game to have data to start with. What if their API was down for a period of time; or if my app accessed their API too many times and was throttled/blocked?

I ultimately decided on saving the stock prices since they’re historical anyway. That way I would only need to access their API only once for each stock and I could save the historical data to my Stock model. Their API returned over 5,000 days of historical prices for each stock, so once I had the data, my Rails API could expose an endpoint to the React app and return a random stock and random 100 days of prices!

The second was hitting their API too many times over a certain period. I made GET requests for several stocks and was successfully able to instantiate new Stock objects in my Rails app, but I received error responses from their API after a handful.

After a long debugging/trial and error period, I realized I could only access 5 at a time. My problem was I wanted to be able to pass an array of stock symbols (the Dow 30) to a Stock class method and have it take it from there. Limiting the method to only accept an array of 5 stocks seemed too manual. My solution was to wait for a moment in-between GET requests. The tradeoff I made was that I would take longer, but would be automated.

def get_prices
  response = RestClient.get 'https://www.alphavantage.co/query', {params: {
    function: 'TIME_SERIES_DAILY_ADJUSTED',
    symbol: "#{symbol}",
    outputsize: "full",
    apikey: ENV['ALPHA_VANTAGE_API_KEY']
  }}
  stock = JSON.parse(response)
  self.prices = stock["Time Series (Daily)"].map do |k,v|
    {date: k.to_date, price: v.values[4].to_f}
  end
end

def self.add_stocks(stocks)
  Array(stocks).each do |stock|
    puts("ABOUT TO ADD: #{stock}")
    sleep(10)
    Stock.create(symbol: stock)
    puts("JUST ADDED: #{stock}")
  end
end

React & Redux

Once the functionality of the API was in place, the next step was to work on the React App with Redux.

I came across the spline chart in canvasJS and thought it would be perfect chart to use! I liked the API and how you could customize the data points (green dot for buy and red for sell).

It turned out that updating the chart with the data in Redux was the most difficult part. Keeping the chart in sync with Redux; specifically the player’s buy and sell actions was challenging. Once I realized that every reducer (ex: the buy and sell reducers) does not need to return its own section of state, but could just update the current state in Redux; that’s when things clicked for me!

When a player navigates to the home page; a GET request is made to the API, which returns a random stock with a random 100 days of prices. The stock is persisted in Redux as:

redux_state.png

With stock.prices as an array of the historical prices. I used prices in Redux as the array of data point to draw the chart; which is empty initially.

Once the game starts, I used setInterval() and setTimeout() to quickly increment the days and populate prices in Redux with each price in stock.prices; which updates the chart.

Once the game is in progress, the buy and sell reducers could just update the current state in Redux and add the appropriate attributes to the data point for that day (markerColor, markerSize and markerType)!

Conclusion

This was by far the most challenging project but also the most rewarding to finish! I had no idea how I would make this work when I came up with the concept of the game. There were definitely times during this project where I ran into a wall and though maybe I should come up with something different because this is taking too long. As cliche as it sounds, I learned the most during those frustrating moments.

Feel free to checkout the GitHub repos (Rails API, React/Redux frontend) and view the app.