Cryptocurrency Portfolio Tracker Python, JavaScript, API
👤 Sharing: AI
```python
# Python (Backend - Flask API)
from flask import Flask, jsonify, request
import requests
import json
app = Flask(__name__)
# Replace with your actual API key (CoinGecko, CoinMarketCap, etc.)
API_KEY = "YOUR_API_KEY_HERE" # A lot of free options require no API key, but some do.
# In-memory portfolio (For simplicity - use a database in a real application)
portfolio = {}
@app.route('/portfolio', methods=['GET'])
def get_portfolio():
"""
Returns the user's cryptocurrency portfolio.
"""
return jsonify(portfolio)
@app.route('/portfolio', methods=['POST'])
def add_to_portfolio():
"""
Adds a cryptocurrency to the user's portfolio.
Expects JSON in the following format:
{
"symbol": "BTC",
"amount": 0.5
}
"""
data = request.get_json()
if not data or 'symbol' not in data or 'amount' not in data:
return jsonify({'error': 'Invalid request. Must include "symbol" and "amount".'}), 400
symbol = data['symbol'].upper() # Ensure uppercase
amount = float(data['amount'])
if symbol in portfolio:
portfolio[symbol] += amount
else:
portfolio[symbol] = amount
return jsonify({'message': f'Added {amount} {symbol} to portfolio.'}), 201 # 201 Created
@app.route('/portfolio/<symbol>', methods=['DELETE'])
def remove_from_portfolio(symbol):
"""
Removes a cryptocurrency from the user's portfolio.
"""
symbol = symbol.upper()
if symbol in portfolio:
del portfolio[symbol]
return jsonify({'message': f'Removed {symbol} from portfolio.'})
else:
return jsonify({'error': f'{symbol} not found in portfolio.'}), 404 # 404 Not Found
@app.route('/price/<symbol>', methods=['GET'])
def get_crypto_price(symbol):
"""
Fetches the current price of a cryptocurrency using the CoinGecko API.
"""
symbol = symbol.lower() # CoinGecko requires lowercase symbols
# CoinGecko API endpoint - a good free option
url = f'https://api.coingecko.com/api/v3/simple/price?ids={symbol}&vs_currencies=usd'
try:
response = requests.get(url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
data = response.json()
if symbol in data and 'usd' in data[symbol]:
price = data[symbol]['usd']
return jsonify({'symbol': symbol.upper(), 'price': price})
else:
return jsonify({'error': f'Could not retrieve price for {symbol.upper()}.'}), 404
except requests.exceptions.RequestException as e:
print(f"Error fetching price: {e}")
return jsonify({'error': f'Error fetching price for {symbol.upper()}.'}), 500
@app.route('/portfolio/value', methods=['GET'])
def get_portfolio_value():
"""
Calculates the total value of the portfolio in USD.
"""
total_value = 0
for symbol, amount in portfolio.items():
# Get the price for each cryptocurrency
response = requests.get(f'/price/{symbol}') # Use the internal API endpoint
if response.status_code == 200:
price_data = response.json()
price = price_data['price']
total_value += amount * price
else:
return jsonify({'error': f'Could not calculate portfolio value. Error fetching price for {symbol}.'}), 500
return jsonify({'total_value': total_value})
if __name__ == '__main__':
app.run(debug=True)
```
```javascript
// JavaScript (Frontend - Example using Fetch API)
// Assuming you have an HTML file with elements like:
// <input type="text" id="symbolInput">
// <input type="number" id="amountInput">
// <button id="addCoinButton">Add Coin</button>
// <ul id="portfolioList"></ul>
// <div id="totalValue"></div>
const apiUrl = 'http://127.0.0.1:5000'; // Flask app's address
document.addEventListener('DOMContentLoaded', () => {
const symbolInput = document.getElementById('symbolInput');
const amountInput = document.getElementById('amountInput');
const addCoinButton = document.getElementById('addCoinButton');
const portfolioList = document.getElementById('portfolioList');
const totalValueDiv = document.getElementById('totalValue');
// Function to fetch and display the portfolio
const displayPortfolio = async () => {
try {
const response = await fetch(`${apiUrl}/portfolio`);
const portfolio = await response.json();
portfolioList.innerHTML = ''; // Clear the list
for (const symbol in portfolio) {
const amount = portfolio[symbol];
const listItem = document.createElement('li');
listItem.textContent = `${symbol}: ${amount}`;
portfolioList.appendChild(listItem);
}
} catch (error) {
console.error('Error fetching portfolio:', error);
alert('Failed to fetch portfolio.');
}
};
// Function to fetch and display total portfolio value
const displayTotalValue = async () => {
try {
const response = await fetch(`${apiUrl}/portfolio/value`);
const data = await response.json();
const totalValue = data.total_value;
totalValueDiv.textContent = `Total Portfolio Value: $${totalValue.toFixed(2)}`; // Format to 2 decimal places
} catch (error) {
console.error('Error fetching total value:', error);
alert('Failed to fetch total portfolio value.');
}
};
// Function to add a coin to the portfolio
const addCoin = async () => {
const symbol = symbolInput.value.trim();
const amount = parseFloat(amountInput.value); // Parse to a number
if (!symbol || isNaN(amount) || amount <= 0) {
alert('Please enter a valid symbol and amount.');
return;
}
try {
const response = await fetch(`${apiUrl}/portfolio`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ symbol: symbol, amount: amount }),
});
if (response.ok) {
symbolInput.value = '';
amountInput.value = '';
displayPortfolio(); // Refresh the portfolio display
displayTotalValue(); // Refresh total value
} else {
const errorData = await response.json(); // Try to get error message from server
alert(`Failed to add coin: ${errorData.error || 'Unknown error'}`); // Use the error message, or a generic one
}
} catch (error) {
console.error('Error adding coin:', error);
alert('Failed to add coin.');
}
};
// Event listener for the "Add Coin" button
addCoinButton.addEventListener('click', addCoin);
// Initial display of the portfolio
displayPortfolio();
displayTotalValue();
});
```
**HTML (Example `index.html`)**
```html
<!DOCTYPE html>
<html>
<head>
<title>Cryptocurrency Portfolio Tracker</title>
</head>
<body>
<h1>Cryptocurrency Portfolio Tracker</h1>
<div>
<label for="symbolInput">Coin Symbol:</label>
<input type="text" id="symbolInput" placeholder="e.g., BTC">
</div>
<div>
<label for="amountInput">Amount:</label>
<input type="number" id="amountInput" placeholder="e.g., 0.5">
</div>
<button id="addCoinButton">Add Coin</button>
<h2>Portfolio</h2>
<ul id="portfolioList"></ul>
<div id="totalValue"></div>
<script src="script.js"></script> <!-- Make sure this points to your JavaScript file -->
</body>
</html>
```
**Explanation:**
1. **Python (Flask API - Backend):**
* **Flask Setup:** Initializes a Flask web application.
* **API Key:** The `API_KEY` variable should be replaced with your actual API key if you are using a service that requires one (many free options do not). Using a service that provides real-time pricing is important for any tracking application.
* **In-Memory Portfolio:** The `portfolio` dictionary stores the user's holdings. This is a very basic example, and in a real application, you would use a database (like PostgreSQL, MySQL, or MongoDB) to persist the portfolio data.
* **API Endpoints:**
* `/portfolio (GET)`: Returns the current portfolio.
* `/portfolio (POST)`: Adds a cryptocurrency and amount to the portfolio. It expects JSON data in the request body.
* `/portfolio/<symbol> (DELETE)`: Removes a cryptocurrency from the portfolio.
* `/price/<symbol> (GET)`: Fetches the current price of a cryptocurrency from CoinGecko (a free API). It handles potential errors from the API request. It's crucial to handle errors gracefully in a production environment.
* `/portfolio/value (GET)`: Calculates the total portfolio value by iterating through the portfolio, fetching the price of each coin, and summing the value.
* **Error Handling:** Includes basic error handling (e.g., checking for valid input, handling API request errors). More robust error handling and logging would be necessary for a production application.
* **CORS:** In a real application, if your frontend and backend are served from different origins, you'll need to configure CORS (Cross-Origin Resource Sharing) to allow the frontend to make requests to the backend. You can use the `flask_cors` extension.
2. **JavaScript (Frontend):**
* **DOM Manipulation:** Uses JavaScript to interact with HTML elements (input fields, buttons, a list for displaying the portfolio, and a div for displaying the total value).
* **Fetch API:** Uses the `fetch` API to make requests to the Flask backend.
* **Asynchronous Operations:** Uses `async/await` to handle asynchronous operations (fetching data from the API).
* **`displayPortfolio()` Function:** Fetches the portfolio data from the `/portfolio` endpoint and updates the HTML list to display the holdings.
* **`displayTotalValue()` Function:** Fetches the total portfolio value from the `/portfolio/value` endpoint and updates the HTML to display the value.
* **`addCoin()` Function:**
* Gets the coin symbol and amount from the input fields.
* Sends a POST request to the `/portfolio` endpoint with the data.
* Handles the response from the backend (success or error).
* Refreshes the portfolio and total value displays.
* **Event Listener:** Attaches a click event listener to the "Add Coin" button to call the `addCoin()` function.
* **Error Handling:** Includes basic error handling (e.g., checking for empty input, handling fetch errors). More comprehensive error handling would be needed in a production app.
3. **HTML:**
* Provides a basic user interface with input fields for the coin symbol and amount, a button to add coins, a list to display the portfolio, and a div to display the total value.
**How to Run:**
1. **Install Dependencies (Backend):**
```bash
pip install flask requests
```
2. **Save the Python code** as `app.py` (or similar). Replace `YOUR_API_KEY_HERE` with a valid API key if needed.
3. **Save the JavaScript code** as `script.js` and the HTML code as `index.html` in the same directory.
4. **Run the Flask app:**
```bash
python app.py
```
This will start the Flask development server (usually on `http://127.0.0.1:5000`).
5. **Open `index.html`** in your web browser.
**Key Improvements and Considerations for a Production Application:**
* **Database:** Replace the in-memory `portfolio` with a proper database (e.g., PostgreSQL, MySQL, MongoDB) for persistent storage. Use an ORM like SQLAlchemy or an ODM like MongoEngine to interact with the database.
* **User Authentication:** Implement user authentication (e.g., using Flask-Login) so that each user has their own portfolio.
* **API Key Security:** Never hardcode API keys in your code. Use environment variables or a configuration file to store sensitive information. Consider using a secrets management service (like AWS Secrets Manager or HashiCorp Vault).
* **Rate Limiting:** Be aware of API rate limits and implement strategies to handle them (e.g., using a retry mechanism with exponential backoff).
* **Error Handling:** Implement comprehensive error handling and logging. Use a logging library (like Python's `logging` module) to record errors and other important events.
* **Input Validation:** Thoroughly validate user input to prevent security vulnerabilities (e.g., SQL injection, cross-site scripting). Use a library like `marshmallow` for serialization and validation.
* **CORS:** Configure CORS properly if your frontend and backend are served from different origins.
* **Testing:** Write unit tests and integration tests to ensure the reliability of your code.
* **Deployment:** Use a production-ready web server (like Gunicorn or uWSGI) to deploy your Flask application. Consider using a containerization technology like Docker for easier deployment.
* **Frontend Framework/Library:** Consider using a more robust frontend framework or library like React, Angular, or Vue.js for a more complex and maintainable user interface.
* **Real-time Updates:** For a more dynamic experience, consider using WebSockets to provide real-time updates to the portfolio value. Libraries like Socket.IO can help with this.
* **Data Visualization:** Add charts and graphs to visualize the portfolio performance. Libraries like Chart.js or D3.js can be used for this.
* **API Documentation:** Use a tool like Swagger to generate API documentation automatically.
* **Security Best Practices:** Follow security best practices throughout the development process to protect your application from vulnerabilities.
This enhanced explanation and the complete code example provide a solid foundation for building a more sophisticated cryptocurrency portfolio tracker. Remember to prioritize security, scalability, and maintainability as you develop your application.
👁️ Viewed: 10
Comments