Overall Statistics
Total Trades
1219
Average Win
8.05%
Average Loss
-7.47%
Compounding Annual Return
121.628%
Drawdown
76.600%
Expectancy
0.353
Net Profit
39887.496%
Sharpe Ratio
1.893
Probabilistic Sharpe Ratio
88.739%
Loss Rate
35%
Win Rate
65%
Profit-Loss Ratio
1.08
Alpha
0.622
Beta
0.419
Annual Standard Deviation
0.504
Annual Variance
0.254
Information Ratio
0.29
Tracking Error
0.562
Treynor Ratio
2.274
Total Fees
$160849.46
Estimated Strategy Capacity
$7200000.00
Lowest Capacity Asset
BTCUSD XJ
Portfolio Turnover
24.29%
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 QuantConnect.Securities import Crypto
from QuantConnect.Brokerages import BrokerageName
from sklearn import preprocessing
from sklearn.model_selection import train_test_split 
from sklearn.neighbors import KNeighborsClassifier
from sklearn import svm
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
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(137) # initial investment
        self.crypto = self.AddCrypto("BTCUSD", Resolution.Daily).Symbol
        self.lookback = 30
        self.minimumOrderSize = 0.000016

        self.Schedule.On(self.DateRules.EveryDay(self.crypto), self.TimeRules.At(0, 0), self.TrainModels)
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.At(0, 0), self.AddWeeklyInvestment) # add weekly investment
        self.models = []
        self.scalers = []
        self.slice = None
        
        self.rsi = RelativeStrengthIndex(14)
        self.macd = MovingAverageConvergenceDivergence(12, 26, 9)
        self.bb = BollingerBands(20, 2)
        
        self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash) # Set brokerage model
        
    def AddWeeklyInvestment(self):
        self.Portfolio.CashBook["USD"].AddAmount(68.5) # add $68.5 every week

    def TrainModels(self):
        history = self.History(self.crypto, self.lookback, Resolution.Daily)
        
        if len(history) < self.lookback:
            return
        
        df = history.loc[self.crypto].copy()
        df['Return'] = df['close'].pct_change()
        df.dropna(inplace=True)
        
        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)
        
        self.models = []
        self.scalers = []
        
        for model in [KNeighborsClassifier(n_neighbors=3), svm.SVC(kernel='poly'), RandomForestClassifier(n_estimators=150), GradientBoostingClassifier(n_estimators=150)]:
            scaler = preprocessing.StandardScaler()
            features = scaler.fit_transform(df[['RSI', 'MACD', 'BB_Upper', 'BB_Middle', 'BB_Lower']])
            model.fit(features, np.sign(df['Return']))
            self.models.append(model)
            self.scalers.append(scaler)
        
    def OnData(self, data):
        self.slice = data
        if not self.models or not self.slice.ContainsKey(self.crypto):
            return

        price = self.slice[self.crypto].Close
        self.rsi.Update(self.Time, price)
        self.macd.Update(self.Time, price)
        self.bb.Update(self.Time, price)

        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]
        })

        predictions = []
        for model, scaler in zip(self.models, self.scalers):
            features_scaled = scaler.transform(df)
            prediction = model.predict(features_scaled)
            predictions.append(prediction)

        if all(prediction == 1 for prediction in predictions):
            orderSize = self.CalculateOrderQuantity(self.crypto, 1.0)
            if abs(orderSize) >= self.minimumOrderSize:
                self.SetHoldings(self.crypto, 1.0)

        elif all(prediction == -1 for prediction in predictions):
            holdings = self.Portfolio[self.crypto].Quantity
            if abs(holdings) >= self.minimumOrderSize:
                self.Liquidate(self.crypto)
        
        holdings = self.Portfolio[self.crypto].Quantity
        if holdings > 0 and self.Portfolio[self.crypto].UnrealizedProfitPercent < -0.10:  # 10% stop-loss
            self.Liquidate(self.crypto)
        elif holdings < 0 and self.Portfolio[self.crypto].UnrealizedProfitPercent < -0.10:  # 10% stop-loss for short position
            self.Liquidate(self.crypto)