In the fast-paced world of cryptocurrencies, staying informed about your investments is crucial. Manually tracking prices, calculating gains and losses, and managing a diverse portfolio can be a daunting task. This is where a web-based cryptocurrency portfolio application built with Node.js comes to the rescue. This tutorial will guide you, step-by-step, through creating a dynamic and interactive portfolio tracker, empowering you to monitor your digital assets with ease.
Why Build a Cryptocurrency Portfolio Tracker?
Imagine having a single, centralized dashboard that provides real-time updates on all your cryptocurrency holdings. This is the power of a portfolio tracker. Building one yourself offers several advantages:
- Real-time Data: Access live price feeds from various exchanges.
- Personalized View: Customize the portfolio to your specific holdings.
- Automated Calculations: Automatically calculate profit/loss, total value, and other key metrics.
- Learning Opportunity: Gain hands-on experience with Node.js, APIs, and front-end development.
This project is perfect for beginners and intermediate developers looking to expand their skills in web development and data integration. You’ll learn how to fetch data from APIs, process it, and display it in a user-friendly interface.
Prerequisites
Before we dive in, ensure you have the following installed:
- Node.js and npm: Download and install Node.js from the official website (https://nodejs.org/). npm (Node Package Manager) comes bundled with Node.js.
- Code Editor: A code editor like Visual Studio Code, Sublime Text, or Atom.
- Basic HTML, CSS, and JavaScript knowledge: Familiarity with these technologies is essential for front-end development.
Project Setup
Let’s get started by setting up our project directory and installing the necessary packages.
- Create a Project Directory: Open your terminal or command prompt and create a new directory for your project:
mkdir crypto-portfolio cd crypto-portfolio - Initialize npm: Initialize a new npm project:
npm init -yThis command creates a
package.jsonfile, which will manage our project dependencies. - Install Dependencies: We’ll need the following packages:
express: A web application framework for Node.js.node-fetch: A module to make HTTP requests (fetching data from APIs).cors: Middleware for enabling Cross-Origin Resource Sharing (CORS).
npm install express node-fetch cors
Building the Backend (Node.js Server)
Our backend will handle API requests, data processing, and serving the front-end files. Create a file named server.js in your project directory.
server.js:
const express = require('express');
const fetch = require('node-fetch');
const cors = require('cors');
const app = express();
const port = process.env.PORT || 3000;
app.use(cors()); // Enable CORS for all origins
app.use(express.static('public')); // Serve static files from the 'public' directory
// Replace with your preferred cryptocurrency API
const API_URL = 'https://api.coingecko.com/api/v3';
// Sample Portfolio Data (Replace with your actual data)
let portfolio = [
{
symbol: 'BTC',
quantity: 0.1,
purchasePrice: 30000,
},
{
symbol: 'ETH',
quantity: 2,
purchasePrice: 2000,
},
];
// Helper function to fetch cryptocurrency prices
async function getCryptoPrice(symbol) {
try {
const response = await fetch(`${API_URL}/coins/markets?vs_currency=usd&symbols=${symbol}&order=market_cap_desc&per_page=1&page=1&sparkline=false`);
const data = await response.json();
if (data && data.length > 0) {
return data[0].current_price;
} else {
console.error(`Price data not found for ${symbol}`);
return null;
}
} catch (error) {
console.error(`Error fetching price for ${symbol}:`, error);
return null;
}
}
// API endpoint to get portfolio data
app.get('/api/portfolio', async (req, res) => {
try {
const portfolioWithPrices = await Promise.all(
portfolio.map(async (holding) => {
const price = await getCryptoPrice(holding.symbol.toLowerCase());
const currentValue = price ? holding.quantity * price : 0;
const profitLoss = price ? currentValue - (holding.quantity * holding.purchasePrice) : 0;
return {
symbol: holding.symbol,
quantity: holding.quantity,
purchasePrice: holding.purchasePrice,
currentPrice: price,
currentValue: currentValue,
profitLoss: profitLoss,
};
})
);
// Calculate total portfolio value and profit/loss
const totalValue = portfolioWithPrices.reduce((sum, holding) => sum + holding.currentValue, 0);
const totalProfitLoss = portfolioWithPrices.reduce((sum, holding) => sum + holding.profitLoss, 0);
res.json({
portfolio: portfolioWithPrices,
totalValue: totalValue,
totalProfitLoss: totalProfitLoss,
});
} catch (error) {
console.error('Error fetching portfolio data:', error);
res.status(500).json({ error: 'Failed to fetch portfolio data' });
}
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
Let’s break down the code:
- Import necessary modules:
expressfor the web server,node-fetchfor making API requests, andcorsfor handling Cross-Origin Resource Sharing. - Initialize Express app: Create an Express application instance and set the port.
- Enable CORS:
app.use(cors())allows requests from any origin (for development purposes). In a production environment, you should restrict origins for security. - Serve static files:
app.use(express.static('public'))serves the static files (HTML, CSS, JavaScript) from thepublicdirectory. - API Endpoint: The
/api/portfolioendpoint handles requests for portfolio data. - API Integration: The code fetches cryptocurrency prices from a public API (CoinGecko). You’ll need to create an account and obtain an API key if the API requires one. The code includes error handling to gracefully handle API failures.
- Portfolio Data: A sample
portfolioarray is provided. This will be replaced with your actual portfolio data (how to do this will be covered later). - Price Fetching: The
getCryptoPricefunction fetches the current price of a cryptocurrency. - Data Calculation: The code calculates the current value, profit/loss, and total portfolio value.
- Response: The server sends a JSON response containing the portfolio data, total value, and total profit/loss.
- Start the server: The server listens on the specified port.
Important: Replace the placeholder API URL with the actual URL from your chosen cryptocurrency API. Also, adapt the API request parameters (e.g., vs_currency, symbols) to match the API’s requirements.
Building the Front-End (HTML, CSS, JavaScript)
Now, let’s create the front-end to display the portfolio data. Create a directory named public in your project directory. Inside the public directory, create the following files:
index.htmlstyle.cssscript.js
public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cryptocurrency Portfolio Tracker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>My Cryptocurrency Portfolio</h1>
</header>
<main>
<div id="portfolio-container">
<table id="portfolio-table">
<thead>
<tr>
<th>Symbol</th>
<th>Quantity</th>
<th>Purchase Price</th>
<th>Current Price</th>
<th>Current Value</th>
<th>Profit/Loss</th>
</tr>
</thead>
<tbody id="portfolio-body">
<!-- Portfolio data will be inserted here -->
</tbody>
</table>
<div id="total-value">
<p>Total Portfolio Value: <span id="total-value-amount">Loading...</span></p>
<p>Total Profit/Loss: <span id="total-profit-loss">Loading...</span></p>
</div>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
public/style.css:
body {
font-family: sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: #fff;
padding: 1em 0;
text-align: center;
}
main {
padding: 20px;
}
#portfolio-container {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.positive {
color: green;
}
.negative {
color: red;
}
#total-value {
font-weight: bold;
margin-top: 10px;
}
public/script.js:
// Function to fetch portfolio data from the server
async function getPortfolioData() {
try {
const response = await fetch('/api/portfolio');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching portfolio data:', error);
return null;
}
}
// Function to display portfolio data in the table
function displayPortfolio(portfolio, totalValue, totalProfitLoss) {
const portfolioBody = document.getElementById('portfolio-body');
const totalValueAmount = document.getElementById('total-value-amount');
const totalProfitLossElement = document.getElementById('total-profit-loss');
// Clear existing table rows
portfolioBody.innerHTML = '';
if (!portfolio) {
portfolioBody.innerHTML = '<tr><td colspan="6">Failed to load portfolio data.</td></tr>';
totalValueAmount.textContent = 'N/A';
totalProfitLossElement.textContent = 'N/A';
return;
}
portfolio.forEach((holding) => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${holding.symbol}</td>
<td>${holding.quantity}</td>
<td>$${holding.purchasePrice.toFixed(2)}</td>
<td>$${holding.currentPrice ? holding.currentPrice.toFixed(2) : 'N/A'}</td>
<td>$${holding.currentValue.toFixed(2)}</td>
<td class="${holding.profitLoss > 0 ? 'positive' : 'negative'}">$${holding.profitLoss.toFixed(2)}</td>
`;
portfolioBody.appendChild(row);
});
totalValueAmount.textContent = `$${totalValue.toFixed(2)}`;
totalProfitLossElement.textContent = `$${totalProfitLoss.toFixed(2)}`;
}
// Function to initialize the app
async function init() {
const portfolioData = await getPortfolioData();
if (portfolioData) {
displayPortfolio(portfolioData.portfolio, portfolioData.totalValue, portfolioData.totalProfitLoss);
}
}
// Call init function when the page loads
window.onload = init;
Let’s break down the front-end code:
- HTML (
index.html):- Sets up the basic HTML structure, including a header and a main section.
- Includes a table to display the portfolio data.
- Includes placeholders for the total portfolio value and profit/loss.
- Links to the
style.cssandscript.jsfiles.
- CSS (
style.css):- Provides basic styling for the page, including the table and the total value/profit-loss display.
- Defines classes for positive and negative profit/loss values.
- JavaScript (
script.js):getPortfolioData(): Fetches portfolio data from the/api/portfolioendpoint using thefetchAPI. Handles potential errors.displayPortfolio(): Takes the portfolio data and populates the HTML table. It dynamically creates table rows for each cryptocurrency holding. It also updates the total portfolio value and profit/loss display.init(): This function is called when the page loads. It fetches the portfolio data and calls thedisplayPortfolio()function to display the data.- Error Handling: Includes basic error handling to display an error message if the data cannot be fetched.
Running the Application
Now that we have both the backend and frontend set up, let’s run the application.
- Start the Server: In your terminal, navigate to your project directory (
crypto-portfolio) and run the following command:node server.jsYou should see a message in the console indicating that the server is listening on the specified port (e.g.,
Server listening on port 3000). - Open in Browser: Open your web browser and go to
http://localhost:3000(or the port your server is running on). You should see your portfolio tracker, displaying the sample data.
Adding Your Cryptocurrency Holdings
Currently, the application displays sample data. To make it useful, you need to replace the sample data with your actual cryptocurrency holdings. There are two primary approaches:
- Hardcoding (For Testing/Small Portfolios):
Modify the
portfolioarray inserver.js. Replace the sample data with your actual holdings. For example:let portfolio = [ { symbol: 'BTC', quantity: 0.25, purchasePrice: 35000, }, { symbol: 'ETH', quantity: 1.5, purchasePrice: 2200, }, { symbol: 'ADA', quantity: 1000, purchasePrice: 0.50, }, ];Remember to restart the server after making changes to
server.jsfor the changes to take effect. - Data Input (For Larger/Dynamic Portfolios):
For a more dynamic and scalable solution, consider these options:
- User Input Form: Add an HTML form to your front-end (
index.html) to allow users to input their holdings. The form data would be sent to the server via an API endpoint (e.g.,/api/addHolding) to update theportfolioarray. - Database Integration: Store the portfolio data in a database (e.g., MongoDB, PostgreSQL). This provides persistence and scalability. You would then modify the server-side code to fetch data from and save data to the database.
- Authentication: Implement user authentication to secure the portfolio data. This is crucial if you’re using a database or allowing users to input their holdings.
- User Input Form: Add an HTML form to your front-end (
For this tutorial, we will focus on hardcoding the data for simplicity. If you want to expand the project, explore the data input and database integration options.
Advanced Features and Enhancements
Once you have a functional portfolio tracker, you can enhance it with these features:
- Real-time Updates: Use WebSockets (e.g., Socket.IO) to receive real-time price updates from the API. This will eliminate the need to refresh the page manually.
- Historical Data: Display historical price charts using a charting library (e.g., Chart.js, ApexCharts) to visualize price trends.
- Portfolio Performance Charts: Create charts to show your portfolio’s performance over time (e.g., daily/weekly/monthly profit/loss).
- Alerts and Notifications: Implement price alerts to notify you when a cryptocurrency reaches a certain price level.
- User Authentication: Implement user login and registration to secure user data and allow multiple users.
- More Advanced Calculations: Add features like calculating the average purchase price, showing the percentage change in value, and showing more detailed profit/loss information.
- Multiple Currencies: Allow users to view their portfolio in different currencies.
- Mobile Responsiveness: Ensure the application is responsive and looks good on different devices.
Common Mistakes and Troubleshooting
Here are some common mistakes and how to fix them:
- CORS Errors: If you see CORS errors in your browser’s console, ensure you’ve enabled CORS in your
server.jsfile usingapp.use(cors()). If you are deploying the application, you may need to configure CORS more specifically. - API Key Issues: If the API you are using requires an API key, make sure you have obtained one and included it correctly in your code. Check the API documentation for proper usage.
- Incorrect API URLs: Double-check the API URLs and parameters to ensure they are correct. Use the API documentation.
- Data Not Displaying: Verify that the data is being fetched from the API correctly by using
console.log()statements to inspect the data in your server and client-side JavaScript code. Check your browser’s developer tools (Network tab) to see if the API requests are succeeding and what data is being returned. - Typos: Carefully check for typos in your code, especially in variable names, function names, and API endpoint URLs.
- Server Not Running: Make sure your Node.js server is running before you try to access the application in your browser.
- Incorrect File Paths: Ensure that your file paths in the HTML, CSS, and JavaScript files are correct.
Key Takeaways
In this tutorial, you learned how to build a basic cryptocurrency portfolio tracker using Node.js, Express, and a public API. You’ve gained practical experience with:
- Setting up a Node.js project.
- Using Express to create a web server.
- Fetching data from a public API.
- Creating a basic front-end with HTML, CSS, and JavaScript.
- Displaying data in a table.
- Performing basic calculations (current value, profit/loss).
- Understanding the basics of CORS.
This project is a solid foundation for building more complex and feature-rich cryptocurrency tracking applications. Remember to replace the sample data with your actual holdings and explore the advanced features to customize and enhance your portfolio tracker. By continuing to experiment and build upon this foundation, you can further develop your skills in Node.js and web development.
FAQ
- Can I use a different cryptocurrency API?
Yes, absolutely! The code is designed to be adaptable. You can easily replace the API URL and modify the data parsing logic in the
getCryptoPricefunction to work with a different API. Just make sure the API provides the necessary data (e.g., current price, symbol). - How do I handle API rate limits?
Many APIs have rate limits that restrict the number of requests you can make in a given time period. If you exceed the rate limit, your requests will be blocked. To handle rate limits, you can implement techniques like:
- Caching: Cache the API responses to reduce the number of requests.
- Request Throttling: Implement a mechanism to limit the number of requests per second/minute.
- API Key: Use an API key (if provided by the API) to increase your rate limits.
- Error Handling: Implement error handling to gracefully handle rate limit errors and retry requests after a delay.
- How can I deploy this application?
You can deploy this application to various platforms, such as:
- Heroku: A popular platform for deploying Node.js applications.
- Netlify: Great for deploying front-end applications.
- AWS, Google Cloud, Azure: Cloud providers offer various deployment options.
- Your own server: You can deploy the application on a server you manage.
The deployment process typically involves pushing your code to the platform and configuring the environment variables (e.g., API key, port).
- How can I improve the security of this application?
Security is crucial, especially when dealing with financial data. Here are some security best practices:
- HTTPS: Use HTTPS to encrypt the communication between the client and the server.
- Input Validation: Validate all user inputs to prevent vulnerabilities like cross-site scripting (XSS) and SQL injection.
- Secure API Keys: Never hardcode API keys in your front-end code. Store them securely on the server-side, and use environment variables.
- Authentication: Implement user authentication to protect user data and prevent unauthorized access.
- Regular Updates: Keep your dependencies (Node.js packages) up to date to patch security vulnerabilities.
- CORS Configuration: Configure CORS properly to restrict access from unauthorized origins.
- Where can I find more cryptocurrency APIs?
There are numerous cryptocurrency APIs available. Some popular options include:
- CoinGecko (https://www.coingecko.com/en/api)
- CoinMarketCap (https://coinmarketcap.com/api/)
- CryptoCompare (https://www.cryptocompare.com/api/)
- Binance API (https://binance-docs.github.io/web-api-v3/) – Requires an account.
Each API has its own documentation, so read the documentation to understand how to use the API and the available endpoints.
Building a cryptocurrency portfolio tracker is a rewarding project that combines practical skills with real-world application. From understanding API integration to front-end design, the experience gained is invaluable. The ability to monitor your investments, visualize performance, and make informed decisions becomes significantly enhanced. By continually refining your skills and embracing the challenges of web development, you’ll be well-equipped to navigate the ever-evolving landscape of cryptocurrency and beyond.
