Overall Statistics
Total Trades
1438
Average Win
4.33%
Average Loss
-2.90%
Compounding Annual Return
91.607%
Drawdown
54.800%
Expectancy
0.291
Net Profit
13267.370%
Sharpe Ratio
1.649
Probabilistic Sharpe Ratio
79.937%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.49
Alpha
0.691
Beta
0.292
Annual Standard Deviation
0.436
Annual Variance
0.19
Information Ratio
1.386
Tracking Error
0.448
Treynor Ratio
2.467
Total Fees
$0.00
Estimated Strategy Capacity
$6000000.00
Lowest Capacity Asset
BTCUSD XJ
Portfolio Turnover
50.62%
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data.Market import TradeBar
from sklearn import preprocessing
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier, VotingClassifier
from sklearn.pipeline import Pipeline
from sklearn import metrics
import pandas as pd
import numpy as np
from datetime import datetime
from QuantConnect.Indicators import RelativeStrengthIndex, MovingAverageConvergenceDivergence, BollingerBands

class CryptoPredictionAlgorithm(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2016, 1, 1)
        self.SetCash(100000)
        self.crypto = self.AddCrypto("BTCUSD", Resolution.Daily).Symbol
        self.lookback = 30

        self.Schedule.On(self.DateRules.EveryDay(self.crypto), self.TimeRules.At(0, 0), self.TrainModels)
        self.model = None
        self.scaler = None
        self.slice = None
        
        # Debugging elements
        self.Debug("Algorithm initialized")
        self.Debug("Symbol: " + str(self.crypto))
        self.Debug("Lookback period: " + str(self.lookback) + " days")

        self.rsi = RelativeStrengthIndex(14)
        self.macd = MovingAverageConvergenceDivergence(12, 26, 9)
        self.bb = BollingerBands(20, 2)

    def TrainModels(self):
        # Get historical data
        history = self.History(self.crypto, self.lookback, Resolution.Daily)
        
        # Ensure we have enough data
        if len(history) < self.lookback:
            self.Debug("Not enough data to train models")
            return
        
        # Debugging element
        self.Debug("Training models")

        # Create dataframe and calculate features
        df = history.loc[self.crypto].copy()
        df['Return'] = df['close'].pct_change()
        df.dropna(inplace=True)
        
        # Insert your feature calculation here
        for i in range(len(df)):
            end_time = df.index[i]
            price = df.loc[end_time, "close"]
            self.rsi.Update(end_time, price)
            self.macd.Update(end_time, price)
            self.bb.Update(end_time, price)
            df.loc[end_time, 'RSI'] = self.rsi.Current.Value
            df.loc[end_time, 'MACD'] = self.macd.Current.Value
            df.loc[end_time, 'BB_Upper'] = self.bb.UpperBand.Current.Value
            df.loc[end_time, 'BB_Middle'] = self.bb.MiddleBand.Current.Value
            df.loc[end_time, 'BB_Lower'] = self.bb.LowerBand.Current.Value
        
        df.dropna(inplace=True)

        # Create models and scalers
        self.model = RandomForestClassifier()
        self.scaler = preprocessing.StandardScaler()
        
        # Hyperparameter tuning using grid search
        parameters = {'n_estimators': [10, 50, 100, 150, 200]}
        clf = GridSearchCV(self.model, parameters)
        
        # Train models and scalers
        features = self.scaler.fit_transform(df[['RSI', 'MACD', 'BB_Upper', 'BB_Middle', 'BB_Lower']])
        clf.fit(features, np.sign(df['Return']))
        
        # Update the model with the best parameters
        self.model = clf.best_estimator_
        
        # Debugging element
        self.Debug("Models trained")
        
    def OnData(self, data):
        self.slice = data
        if not self.model or not self.slice.ContainsKey(self.crypto):
            return

        # Update the indicators with the latest price
        price = self.slice[self.crypto].Close
        self.rsi.Update(self.Time, price)
        self.macd.Update(self.Time, price)
        self.bb.Update(self.Time, price)

        # Create a DataFrame for the latest data
        df = pd.DataFrame({
            'RSI': [self.rsi.Current.Value],
            'MACD': [self.macd.Current.Value],
            'BB_Upper': [self.bb.UpperBand.Current.Value],
            'BB_Middle': [self.bb.MiddleBand.Current.Value],
            'BB_Lower': [self.bb.LowerBand.Current.Value]
        })

        # Use the model to make a prediction
        features_scaled = self.scaler.transform(df)
        prediction = self.model.predict(features_scaled)

        # Decide whether to buy or sell based on the predictions
        if prediction == 1:
            self.SetHoldings(self.crypto, 1.0)
        elif prediction == -1:
            self.Liquidate(self.crypto)

        # Debugging element
        self.Debug("Prediction: " + str(prediction))