Signals began as a lofty idea to develop a platform for users who want to create and implement automated trade strategies, much like trading bots. Being quickly grounded by own coding ability, I shifted my focus on creating a beginner-friendly stock market tool, with room to develop. Just as I am very new to software engineering, there are many stock market novices who want to learn how trade stocks.
Even the entry level market tools available seem intimidating for a trading newbie. After looking through different trading and analysis platforms such as Tradingview, Yahoo Finance, and Bloomberg, I found a few fundamental components and packaged them into a less intimidating tool. I combined these tools, which provide relevant information to analyzing any market or security. These components are stock quotes, a market keyword search function, a watchlist, technical charting, a trade simulator, a portfolio manager, and trade transaction history. Signals.
Signals Homepage
I built signals with Javascript , using the Alpha Vantage API as a data source and Bootstrap 5 CDN for CSS styling. The Alpha Vantage API offers much more financial data than I needed, which allows for future additions and updates to Signals.
The most difficult challenge was creating "My Portfolio" , a stock portfolio based on the user's trade history. Keeping the target user in mind, I developed a simplified trading system. The trading system is simplified to only allow Long and Short trade positions. The user cannot hold opposing positions of the same stock (a form of hedging). Trades are taken at current market price of the traded asset. Here's how I turned a combination of both user inputs and API requests into an automated, up-to-date stock portfolio.
Overview
- Provide the user with relevant information to make educated trading decisions.
- Allow the user to either buy or sell any amount of shares for any given ticker symbol. Collect the trade information every time a user executes a trade and store it somewhere.
- Use the stored data (trade history) to build the user's portfolio, also determining whether the user's position type in a given stock.
- Legibly display the user's portfolio.
Note: Some basic understanding of Javascript is required to follow along with the code snippets provided and technical functionality.
1. Providing the User with A Stock Quote
Signals fetches a quote from the Alpha Vantage API and displays it to the user. Alpha Vantage uses different API functions, which determine the type of data is returned. The Quote Endpoint API function, which returns a detailed stock quote , requires a symbol, formally known as a ticker. A ticker is a letter abbreviation that acts as an identification code, specific to a traded stock or security on the market.
https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=SYMBOLHERE&apikey=demo
Quote Endpoint API URL
This presents challenge 1; what happens if I don't know the symbol? Fortunately, Alpha Vantage has a Search Endpoint API function , which returns the best-matching symbols based on keywords of your choice.
https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=KEYWORDSHERE&apikey=demo
Search Endpoint API Function URL
Capture the user's keywords with a form input named search bar, replace "KEYWORDSHERE" in the Search Endpoint URL with the user input, display a list of best matching symbols, and select the desired symbol from the list. Simple enough.
searchBtn.addEventListener('click',e => {
e.preventDefault()
const searchbarValue = searchbar.value
let url = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${searchbarValue}&apikey=VXY7DLJ0XAQQON66`
marketSearch(url)
})
function marketSearch(url) {
fetch(url)
.then(res => res.json())
.then(searchresults => {
let bestMatchesArray = (Object.values(searchresults))[0]
searchResultsMatches = []
bestMatchesArray.map(bestMatch => simpleResults(bestMatch))
appendResults(searchResultsMatches)
})
}
With an event listener added to the search bar , Signals captures the user input and creates a usable url for the Search Endpoint API fetch request. The best matching companies are found with the marketSearch(url) function.
SimpleResults function cleans up the data from the fetch request and appends each match to the searchResultsMatches array. The appendResults function creates a legible list , which appends each search result stored in the searchResultsMatches array to the DOM (specifically the Toolbox Display), in a legible unordered list. Each list item is given an id , which is the ticker symbol of that specific search result.
searchResultsMatches Array with the user keyword AAPL
A click event listener is added to each list item, and the id of that event target (the ticker symbol) is used to create the Quote Endpoint API URL. Now Signals can use the results of the Search Endpoint API request to build a URL for the Quote Endpoint API Request.
function appendDisplay(e) {
e.stopPropagation()
let url = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${e.target.id}&apikey=VXY7DLJ0XAQQON66`
fetchStockData(url)
}
appendDisplay(e) function is called every time a click event fires from any given list item appended via appendResults function previously mentioned
The appendDisplay(e) function calls 2 other functions , organizeStockData(data) and printToDOM(stockDataObj).
function fetchStockData(url){
fetch(url)
.then(res => res.json())
.then(data => {organizeStockData(data)
printToDOM(stockDataObj)
})
}
organizeStockData cleans up and organizes the data returned from a promise and creates stockDataObj. stockDataObj is then used by printToDOM to display a relevant stock quote to the user.
2. Creating a Trade Simulator
Collecting The User Trade Data
Main Display(left) shows the result of calling printTODOM, Search Results(right) displays the results of calling marketSearch function
The Main Display now displays actionable metrics used to speculate on the market and thus, execute trades. The righthand side of the Main Display shows all final information for a specific stock on the specified trading day. The lefthand graph displays intraday price movement and allows for technical charting with live data via Tradingview Widget.
Making use of an input element and a select element, Signals can capture 2 user inputs, which will later be used to create the trade history. The input allows Signals to capture the quantity of shares for a specific trade , and the select options determine whether the shares are being sold or bought.
Storing The User Trade Data
Every trade is then stored in a local database file db.json array "trade-history".
The variable tradeQuantity selects the input we created with printTODOM , which captures the quantity of shares the user will be trading. Variable tradeOptions refers to the select element with the type of trade the user is exercising (Buy or Sell).
An event listener is added to tradeQuantity , specifically a keypress event. Once the event is fired, object newTrade is created. Using the stockDataObj made earlier, Signals stores a few different key/value pairs in newTrade. newTrade now holds the price of the traded symbol, date of the trade , ticker symbol traded, the trade type, and quantity of shares traded. All of this information is relevant to the user for refining trade strategies.
Not every keypress event creates meaningful trade. I used if then statements to determine when it should post newTrade in the "trade-history".
"trade-history" updates when the keypress event is fired by the enter button , and the quantity of shares is positive. Then, newTrade is posted to the "trade-history" database, and the user is alerted that their trade was successful. Any unsuccessful trade prompts an alert message, informing the user to enter a positive integer in tradeQuantity.
function updateDatabase (newTrade, db = 'trade-history') {
fetch(`http://localhost:3000/${db}`, {
method: 'POST' ,
headers: {
'Content-Type': 'application/json'
} ,
body: JSON.stringify(newTrade)
})
}
updateDatabase sends a post request to a specified database in the JSON array. In this instance, the request is sent to http://localhost:3000/trade-history updating "trade-history" with newTrade, the trade executed by the user. Signals is ready to make a stock portfolio from all the trades, based on the simplified trading system I created.
3. Reducing Trade History to a clean Portfolio.
You may noticed I forgot to mention the function called within the if statement "fetchDatabase("trade-history",makePortfolio)". This function is responsible for turning the data stored in "trade-history" into a Portfolio of current user positions. Argument 2 is a function, which passes the converted JSON object (watchlistObj) as an argument.
function fetchDatabase(db,funct) {
fetch(`http://localhost:3000/${db}`)
.then(res => res.json())
.then(watchlistObj => {
funct(watchlistObj)
})
}
Function makePortfolio uses a reduce method to return the
accumulated results in a new array.
This function accumulates all trades by the symbol , stored in the variable reducer. If the trade's type is "Buy" ,then the quantity of shares is added to the existing shares for that specific symbol. If the trade's type is "Sell" , then the quantity of shares is subtracted. If there is only one trade with for a specific symbol, then it is appended as-is , with all "Sell" trades stored with negative quantities. The accumulated result is a reduced array with only 1 array index per symbol. This will be stored in the db.json file as "portfolio".
First, the existing "portfolio" data is cleared with a DELETE request by calling the function fetchDatabase('portfolio',clearPortfolio).
Again, an array method is used to recategorize each index element in reducer. Every element with a negative quantity is stored as a "Short" trade , and every positive quantity is stored as a "Long" trade.
4. Append My Portfolio to DOM
"portfolio" is displayed as table in the DOM. Function appendPortfolio is another function with one argument. Reusing the fetchDatabase function , appendPortfolio passes the JSON converted object from "portfolio" , and appends each element in a legible manner.
function appendPortfolio(portfolioObj) {
portfolioTableBody.innerHTML = ''
portfolioObj.forEach(stock => {
let tr = document.createElement('tr')
if(stock.quantity > 0) {
tr.innerHTML = `
<th scope="row">${stock.symbol}</th>
<td class="text-success">${stock.type}</td>
<td>${stock.quantity}</td>
<td>${stock.price}</td>
`
} else if(stock.quantity < 0)
tr.innerHTML = `
<th scope="row">${stock.symbol}</th>
<td class="text-danger">${stock.type}</td>
<td>${(stock.quantity * -1)}</td>
<td>${stock.price}</td>
`
portfolioTableBody.appendChild(tr)
})
}
portfolioTableBody is a global variable , which uses a querySelector to select the table in my HTML file that will display the data that makes My Portfolio.
Long position types are styled with green text, and Short position types are style with red text.
I added a refresh button to the table which fires a click event, allowing the user to call appendPortfolio function at will.
And thus, Signals was built.
Feel free to explore Signals for yourself. Leave a comment for any features recommendations , improvements, or code refactors you may have.
Bonus points if you know what I used to make the "i" in the Signals logo.
Top comments (0)