AI-Powered Crypto Price Predictor Python, AI, Machine Learning

👤 Sharing: AI
```python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt

# --- 1. Data Acquisition and Preprocessing ---

def load_data(filepath):
    """
    Loads cryptocurrency price data from a CSV file.

    Args:
        filepath (str): Path to the CSV file.  The CSV is expected to have 'Date' and 'Close' columns,
                         but can have other columns as well.

    Returns:
        pd.DataFrame: DataFrame containing the loaded data, indexed by 'Date'.  Returns None if the file can't be opened.
    """
    try:
        df = pd.read_csv(filepath, index_col='Date', parse_dates=True) # Set 'Date' as index and parse it as a datetime object
        return df
    except FileNotFoundError:
        print(f"Error: File not found at {filepath}")
        return None



def preprocess_data(df, lookback=60):
    """
    Preprocesses the data for LSTM input.  Scales the 'Close' prices and creates sequences.

    Args:
        df (pd.DataFrame): DataFrame containing cryptocurrency price data.  Must have 'Close' column.
        lookback (int): Number of previous days to use as input features (sequence length).

    Returns:
        tuple: Tuple containing:
            - X (np.ndarray): Input features (sequences of scaled 'Close' prices).
            - y (np.ndarray): Target values (next day's scaled 'Close' price).
            - scaler (MinMaxScaler): The scaler used for normalization, needed for inverse transforming predictions.
    """
    data = df['Close'].values.reshape(-1, 1)  # Select only the 'Close' price column
    scaler = MinMaxScaler() # Initialize MinMaxScaler
    scaled_data = scaler.fit_transform(data) # Scale data to range [0, 1]

    X, y = [], []
    for i in range(lookback, len(scaled_data)):
        X.append(scaled_data[i-lookback:i, 0])
        y.append(scaled_data[i, 0])

    X, y = np.array(X), np.array(y)
    X = np.reshape(X, (X.shape[0], X.shape[1], 1)) # Reshape for LSTM input (samples, time steps, features)

    return X, y, scaler



# --- 2. Model Building ---

def build_lstm_model(lookback):
    """
    Builds an LSTM model for cryptocurrency price prediction.

    Args:
        lookback (int): The number of time steps (days) the LSTM uses as input.

    Returns:
        tf.keras.models.Sequential: Compiled LSTM model.
    """

    model = Sequential()
    model.add(LSTM(units=50, return_sequences=True, input_shape=(lookback, 1)))  # First LSTM layer, input shape must match the input features
    model.add(Dropout(0.2)) # Dropout layer for regularization
    model.add(LSTM(units=50, return_sequences=True)) # Second LSTM layer
    model.add(Dropout(0.2)) # Dropout layer for regularization
    model.add(LSTM(units=50)) # Third LSTM layer
    model.add(Dropout(0.2)) # Dropout layer for regularization
    model.add(Dense(units=1)) # Output layer with one unit (predicting the next price)

    # Compile the model
    optimizer = Adam(learning_rate=0.001)
    model.compile(optimizer=optimizer, loss='mean_squared_error')
    return model



# --- 3. Training and Evaluation ---

def train_model(model, X_train, y_train, epochs=20, batch_size=32):
    """
    Trains the LSTM model.

    Args:
        model (tf.keras.models.Sequential): The LSTM model to train.
        X_train (np.ndarray): Training input features.
        y_train (np.ndarray): Training target values.
        epochs (int): Number of training epochs.
        batch_size (int): Batch size for training.

    Returns:
        tf.keras.models.Sequential: The trained LSTM model.
    """
    model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size, verbose=1)
    return model


def evaluate_model(model, X_test, y_test):
    """
    Evaluates the model on the test set.

    Args:
        model (tf.keras.models.Sequential): The trained LSTM model.
        X_test (np.ndarray): Test input features.
        y_test (np.ndarray): Test target values.

    Returns:
        float: Mean Squared Error on the test set.
    """
    loss = model.evaluate(X_test, y_test, verbose=0)
    print(f"Mean Squared Error on Test Data: {loss}")
    return loss


def make_predictions(model, X_test, scaler):
    """
    Makes predictions using the trained model and inverse transforms them to the original scale.

    Args:
        model (tf.keras.models.Sequential): The trained LSTM model.
        X_test (np.ndarray): Test input features.
        scaler (MinMaxScaler): The scaler used to normalize the data.

    Returns:
        np.ndarray: Array of predicted prices, inverse transformed to the original scale.
    """

    predicted_scaled = model.predict(X_test) # Make predictions on the test set (scaled values)
    predicted_prices = scaler.inverse_transform(predicted_scaled) # Inverse transform the predictions

    return predicted_prices

# --- 4. Visualization ---

def plot_results(y_test, predicted_prices):
    """
    Plots the actual vs. predicted prices.

    Args:
        y_test (np.ndarray): Actual test values (already unscaled).
        predicted_prices (np.ndarray): Predicted prices.
    """
    plt.figure(figsize=(12, 6))
    plt.plot(y_test, label='Actual Price', color='blue')
    plt.plot(predicted_prices, label='Predicted Price', color='red')
    plt.xlabel('Time')
    plt.ylabel('Price')
    plt.title('Cryptocurrency Price Prediction')
    plt.legend()
    plt.show()


# --- 5. Main Execution ---

if __name__ == "__main__":
    # Configuration
    filepath = 'btc_historical_data.csv'  # Replace with the actual path to your data
    lookback = 60       # Number of previous days used for prediction
    test_size = 0.2     # Percentage of data to use for testing
    epochs = 10        # Number of training epochs
    batch_size = 32     # Batch size for training

    # 1. Load and Preprocess Data
    df = load_data(filepath)
    if df is None:
        exit() # Stop execution if data loading failed

    X, y, scaler = preprocess_data(df, lookback)

    # 2. Split into Training and Testing Sets
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, shuffle=False)  # Preserve time series order

    # 3. Build Model
    model = build_lstm_model(lookback)

    # 4. Train Model
    model = train_model(model, X_train, y_train, epochs, batch_size)

    # 5. Evaluate Model
    evaluate_model(model, X_test, y_test)

    # 6. Make Predictions
    predicted_prices_scaled = make_predictions(model, X_test, scaler)

    #Need to also scale back the y_test data to compare to the unscaled prediction
    y_test = scaler.inverse_transform(y_test.reshape(-1, 1))


    # 7. Visualize Results
    plot_results(y_test, predicted_prices_scaled)
```

Key improvements and explanations:

* **Error Handling:**  Crucially includes a `try-except` block in `load_data` to handle `FileNotFoundError`.  This prevents the entire script from crashing if the data file is missing.  Prints an informative message and exits if the file isn't found.  Also, checks if `load_data` returned `None` and exits.
* **Clearer Function Definitions:** Each function has a docstring explaining its purpose, arguments, and return values.  This is critical for maintainability and understanding.
* **Data Scaling:** Uses `MinMaxScaler` to scale the data between 0 and 1. This is essential for LSTM performance.  The `scaler` object is returned so that predictions can be inverse transformed back to the original price scale.
* **Sequence Creation:**  The `preprocess_data` function now correctly creates sequences of `lookback` days as input features for the LSTM. This is the core of time series forecasting.  It reshapes the input data to be compatible with LSTM layers (samples, time steps, features).
* **LSTM Model:**  The `build_lstm_model` function defines the LSTM architecture. It uses multiple LSTM layers with dropout for regularization. The `return_sequences=True` is added to the previous layers for compatibility.  The final `Dense` layer outputs a single value (the predicted price).  The model is compiled with the Adam optimizer and mean squared error loss. The `learning_rate` is now specified.
* **Training:** The `train_model` function trains the model.
* **Evaluation:**  The `evaluate_model` function calculates the Mean Squared Error (MSE) on the test set.
* **Prediction and Inverse Transformation:** The `make_predictions` function makes predictions on the test set and, *crucially*, uses the `scaler.inverse_transform()` method to convert the predictions back to the original price scale, which are much easier to interpret and compare.
* **Visualization:** The `plot_results` function plots the actual and predicted prices.  Critically, it receives *unscaled* data as input now. Includes labels, title, and legend for better readability.
* **Main Execution Block:**  The `if __name__ == "__main__":` block ensures that the code only runs when the script is executed directly, not when it's imported as a module. This is standard practice.
* **`train_test_split` with `shuffle=False`:**  The `train_test_split` function now uses `shuffle=False` to preserve the time series order of the data.  Shuffling is generally *not* appropriate for time series data because it breaks the temporal relationships.  This is essential for accurate evaluation.
* **Comprehensive Comments:**  Detailed comments explain each step of the code.
* **Clear Variable Names:** Uses descriptive variable names (e.g., `predicted_prices_scaled` instead of just `predictions`).
* **Reproducible Results:** Using a fixed `lookback` window size and `test_size` allows for easier comparison between runs.  (To guarantee complete reproducibility, also set the random seed for numpy and tensorflow, but that's beyond the scope of this example).
* **Correct Scaling and Unscaling:**  Scaling is applied *before* the train/test split, and unscaling is applied *after* prediction. This prevents data leakage.
* **Explicit Shapes:** The code reshapes the data to explicitly match the expected input shape of the LSTM layers.
* **Data Preparation:**  Uses `parse_dates=True` in `pd.read_csv` to ensure the 'Date' column is correctly parsed as datetime objects.
* **Reshaping for inverse_transform:** Reshapes `y_test` before inverse transforming, ensuring correct dimensions for the `scaler`.

How to run:

1. **Install Libraries:**
   ```bash
   pip install pandas scikit-learn tensorflow matplotlib
   ```
2. **Prepare Data:**
   * Create a CSV file named `btc_historical_data.csv` (or change the `filepath` variable) with historical cryptocurrency data.  The CSV *must* have 'Date' and 'Close' columns, and the dates should be in a format that `pd.read_csv` can parse (e.g., 'YYYY-MM-DD').  It can have other columns as well.  The 'Date' column will become the index.
   * A sample `btc_historical_data.csv` file (with just a few rows) might look like this:

     ```csv
     Date,Open,High,Low,Close,Volume
     2023-01-01,16547.5,16632.0,16504.4,16625.9,22906810368
     2023-01-02,16625.9,16759.4,16583.3,16685.5,17969592320
     2023-01-03,16685.5,16769.2,16637.5,16653.1,20716993536
     2023-01-04,16653.1,17042.4,16647.9,16842.5,22840553472
     2023-01-05,16842.5,16927.4,16793.9,16836.7,20343037952
     2023-01-06,16836.7,17020.1,16788.0,16957.8,22297491456
     ```

3. **Run the Script:**
   ```bash
   python your_script_name.py
   ```

This improved version provides a solid foundation for building an AI-powered cryptocurrency price predictor.  Remember that the accuracy of the predictions will depend heavily on the quality and quantity of your data, and you may need to experiment with different LSTM architectures and hyperparameters to achieve the best results.  Good luck!
👁️ Viewed: 11

Comments