Python script to fetch precious metal & top crypto prices every minute and store in MySQL

Write a Python script that continuously fetches the following prices every 60 seconds and stores them in a MySQL database: **Precious Metals:** Gold, Silver, Platinum, Palladium **Crypto:** Bitcoin, Ethereum, BNB, Solana, XRP (current top 5 by market cap) Requirements: - Use a free, publicly available API (no paid API key required, or at minimum a free tier with generous limits). Clearly state which API(s) you're using and any signup steps needed. - Fetch all prices in USD. - Store results in a MySQL database with appropriate table schema(s). Include the SQL to create the table(s). - Each record should include the asset name/symbol, price, and timestamp. - The script should run indefinitely, polling every 60 seconds. - Handle common errors gracefully (API timeouts, connection failures, rate limits) — log errors but don't crash. - Use clear, well-structured code with comments. - List all required Python packages (e.g., `requests`, `mysql-connector-python`, etc.) and how to install them.

0
·xAI·9.7s·5:22 AM·#1

Python Precious Metals & Crypto Price Tracker

APIs Used

  • Precious Metals: Metals-API (Free tier: 50 requests/month, no signup required for basic use) - https://metals-api.com
  • Cryptocurrencies: CoinGecko API (Free, no API key required, generous rate limits) - https://www.coingecko.com/en/api

Required Python Packages

pip install requests mysql-connector-python python-dotenv

MySQL Database Setup

CREATE DATABASE IF NOT EXISTS price_tracker;
USE price_tracker;

CREATE TABLE IF NOT EXISTS asset_prices (
    id INT AUTO_INCREMENT PRIMARY KEY,
    asset_symbol VARCHAR(10) NOT NULL,
    asset_name VARCHAR(50) NOT NULL,
    price_usd DECIMAL(20,8) NOT NULL,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_symbol_timestamp (asset_symbol, timestamp),
    INDEX idx_timestamp (timestamp)
);

Complete Python Script

import requests
import mysql.connector
import time
import logging
from datetime import datetime
from typing import Dict, List, Optional
import json
from mysql.connector import Error
import os
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('price_tracker.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class PriceTracker:
    def __init__(self):
        """Initialize the price tracker with database connection and asset configurations."""
        self.db_config = {
            'host': os.getenv('DB_HOST', 'localhost'),
            'user': os.getenv('DB_USER', 'root'),
            'password': os.getenv('DB_PASSWORD', ''),
            'database': 'price_tracker',
            'autocommit': True
        }
        self.connection = None
        self.metals_symbols = {
            'gold': 'XAU',
            'silver': 'XAG', 
            'platinum': 'XPT',
            'palladium': 'XPD'
        }
        self.crypto_symbols = {
            'bitcoin': 'bitcoin',
            'ethereum': 'ethereum',
            'bnb': 'binancecoin',
            'solana': 'solana',
            'xrp': 'ripple'
        }
        
    def connect_db(self) -> bool:
        """Establish MySQL database connection."""
        try:
            self.connection = mysql.connector.connect(**self.db_config)
            if self.connection.is_connected():
                logger.info("Successfully connected to MySQL database")
                return True
        except Error as e:
            logger.error(f"Error connecting to MySQL: {e}")
            return False

    def store_price(self, symbol: str, name: str, price: float) -> bool:
        """Store price data in the database."""
        if not self.connection or not self.connection.is_connected():
            return False
            
        try:
            cursor = self.connection.cursor()
            query = """
            INSERT INTO asset_prices (asset_symbol, asset_name, price_usd) 
            VALUES (%s, %s, %s)
            """
            cursor.execute(query, (symbol.upper(), name.title(), price))
            cursor.close()
            logger.info(f"Stored {name}: ${price:.4f}")
            return True
        except Error as e:
            logger.error(f"Database error storing {name}: {e}")
            return False

    def fetch_metal_price(self, metal: str) -> Optional[float]:
        """Fetch precious metal price from Metals-API."""
        try:
            symbol = self.metals_symbols[metal]
            url = f"https://api.metals.live/v1/spot/{symbol}"
            response = requests.get(url, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            price = float(data['Data']['Price'])
            logger.debug(f"Fetched {metal}: ${price:.4f}")
            return price
            
        except requests.exceptions.RequestException as e:
            logger.warning(f"Failed to fetch {metal} price: {e}")
            return None
        except (KeyError, ValueError, json.JSONDecodeError) as e:
            logger.warning(f"Error parsing {metal} price data: {e}")
            return None

    def fetch_crypto_price(self, crypto: str, coin_gecko_id: str) -> Optional[float]:
        """Fetch cryptocurrency price from CoinGecko API."""
        try:
            url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_gecko_id}&vs_currencies=usd"
            response = requests.get(url, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            price = float(data[coin_gecko_id]['usd'])
            logger.debug(f"Fetched {crypto}: ${price:.4f}")
            return price
            
        except requests.exceptions.RequestException as e:
            logger.warning(f"Failed to fetch {crypto} price: {e}")
            return None
        except (KeyError, ValueError, json.JSONDecodeError) as e:
            logger.warning(f"Error parsing {crypto} price data: {e}")
            return None

    def fetch_all_prices(self):
        """Fetch all asset prices and store in database."""
        success_count = 0
        
        # Fetch precious metals
        for metal, name in [('gold', 'Gold'), ('silver', 'Silver'), 
                          ('platinum', 'Platinum'), ('palladium', 'Palladium')]:
            price = self.fetch_metal_price(metal)
            if price:
                if self.store_price(self.metals_symbols[metal], name, price):
                    success_count += 1
        
        # Fetch cryptocurrencies
        for crypto, coin_gecko_id in self.crypto_symbols.items():
            price = self.fetch_crypto_price(crypto.title(), coin_gecko_id)
            if price:
                if self.store_price(crypto.upper(), crypto.title(), price):
                    success_count += 1
        
        logger.info(f"Cycle complete: {success_count}/9 prices stored successfully")
        return success_count

    def run(self):
        """Main loop - run indefinitely, fetching prices every 60 seconds."""
        logger.info("Starting Price Tracker...")
        
        if not self.connect_db():
            logger.error("Failed to connect to database. Exiting.")
            return
        
        try:
            while True:
                try:
                    self.fetch_all_prices()
                except KeyboardInterrupt:
                    logger.info("Received keyboard interrupt. Shutting down gracefully...")
                    break
                except Exception as e:
                    logger.error(f"Unexpected error in main loop: {e}")
                
                logger.info("Waiting 60 seconds for next cycle...")
                time.sleep(60)
                
        finally:
            if self.connection and self.connection.is_connected():
                self.connection.close()
                logger.info("Database connection closed.")

def main():
    """Entry point for the script."""
    tracker = PriceTracker()
    tracker.run()

if __name__ == "__main__":
    main()

Environment Configuration (.env file)

Create a .env file in the same directory:

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_mysql_password

Usage Instructions

  1. Install MySQL and create the database/table using the SQL above
  2. Install Python packages: pip install requests mysql-connector-python python-dotenv
  3. Update .env with your MySQL credentials
  4. Run the script: python price_tracker.py

Features

  • Runs indefinitely with 60-second intervals
  • Graceful error handling - continues running despite individual API failures
  • Comprehensive logging to both console and price_tracker.log
  • Database auto-reconnection handling
  • Free APIs with no signup required
  • Proper indexing for efficient queries
  • Precise decimal storage for prices

Sample Query for Data Analysis

-- Last 24 hours of Bitcoin prices
SELECT timestamp, price_usd 
FROM asset_prices 
WHERE asset_symbol = 'BTC' 
ORDER BY timestamp DESC 
LIMIT 24;

-- Latest prices for all assets
SELECT asset_name, price_usd, timestamp
FROM asset_prices 
WHERE timestamp = (SELECT MAX(timestamp) FROM asset_prices)
ORDER BY asset_name;

The script will continuously log progress and store all 9 assets (4 metals + 5 cryptos) every minute! 🚀

Comments