Overall Statistics
Total Trades
385
Average Win
2.71%
Average Loss
-2.55%
Compounding Annual Return
3.801%
Drawdown
43.500%
Expectancy
0.058
Net Profit
12.711%
Sharpe Ratio
0.255
Probabilistic Sharpe Ratio
6.629%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.06
Alpha
0.076
Beta
-0.046
Annual Standard Deviation
0.275
Annual Variance
0.075
Information Ratio
-0.186
Tracking Error
0.346
Treynor Ratio
-1.539
Total Fees
$9490.50
Estimated Strategy Capacity
$62000.00
import clr
clr.AddReference("System")
clr.AddReference("QuantConnect.Algorithm")
clr.AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
import datetime
from datetime import timedelta

import numpy as np
from sklearn import preprocessing
from sklearn.linear_model import Ridge, Lasso

import pandas as pd
from math import floor

class ScikitLearnLinearRegressionAlgorithm(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2018, 1,4 )  # Set Start Date 
        self.SetEndDate(2021, 3, 20) # Set End Date
        self.SetCash(1000000)  # Set Strategy Cash
        #self.Settings.FreePortfolioValuePercentage = 0.30
        
        self.timestamp = 60*24 #1day
        self.lookback = 30*24*60 # 30days, 1 month
        self.testing = 25*self.timestamp  #testing period table
        
        self.long_quantile = 0.5
        self.short_quantile = 0.5
        self.close_quantile = 0
        self.alpha = 0.1

        self.BTC = self.AddFuture(Futures.Currencies.BTC, Resolution.Minute) 
        #self.SetBenchmark("BTCUSD")
        
        #self.BTC.SetFilter(lambda x: x.FrontMonth())
        self.BTC.SetFilter(timedelta(5), timedelta(90))
        #self.BTC.SetFilter(lambda x: x.ExpirationCycle([1, 3]))
        
        self.Schedule.On(self.DateRules.MonthEnd(),self.TimeRules.At(10, 30) ,self.Regression)
        self.er_rebuild_model = 0
        
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(11, 00), self.Trade)
        self.run = 0
            
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(10, 30), self.handle_error)    
        #self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(23, 59), self.portfolio_check) 

        
        self.contract_symbol = None
        self.old_contract_symbol = None
        self.change_symbol = False
        
        self.SMA_Signal = "N"
        self.ML_Signal = "N"
        self.Combined_Signal =  "N"
    
    def OnData(self, slice):
        for chain in slice.FutureChains:
            contracts_list = [contract for contract in chain.Value]
            ideal_contract = sorted(contracts_list, key=lambda k : k.Expiry, reverse=False)[0]
            self.contract_symbol = ideal_contract.Symbol
            if (self.old_contract_symbol != self.contract_symbol):
                self.old_contract_symbol = self.contract_symbol
                self.change_symbol = True
            
    def data_construction(self, look_back_period):
        slices = self.History(look_back_period, Resolution.Minute)
        
        datetime = []
        bidclose = []
        bidsize = []
        askclose = []
        asksize = []
        openprice = []
        close = []

        for s in slices:
            datetime.append(s.Time)
            bidclose.append(s.QuoteBars[self.contract_symbol].Bid.Close)
            bidsize.append(int(s.QuoteBars[self.contract_symbol].LastBidSize))
            askclose.append(s.QuoteBars[self.contract_symbol].Ask.Close)
            asksize.append(int(s.QuoteBars[self.contract_symbol].LastAskSize))
            openprice.append(s.QuoteBars[self.contract_symbol].Open)
            close.append(s.QuoteBars[self.contract_symbol].Close)

        df = pd.DataFrame({"bidclose":bidclose, "bidsize":bidsize, "askclose":askclose, "asksize":asksize, "open":openprice, "close":close}, index=datetime)
        
        if (self.timestamp != 1):
            # Resample the data
            temp_str = str(self.timestamp) + "T"
            df = df.resample(temp_str).last()
            temp_sum = df.resample(temp_str).sum()/self.timestamp
            #temp_sum = temp_sum.astype(int)
            temp_first = df.resample(temp_str).first()
            df[["bidsize", "asksize"]] = temp_sum[["bidsize", "asksize"]]
            df["open"]=temp_first["open"]
        
        df['bidpricechange_lag']=df['bidclose']-df['bidclose'].shift(1)
        df['askpricechange_lag']=df['askclose']-df['askclose'].shift(1)
        df['bidsizediff_lag']=df['bidsize']-df['bidsize'].shift(1)
        df['asksizediff_lag']=df['asksize']-df['asksize'].shift(1)
        df=df.dropna(axis=0)
        
        deltaVolumeBid=[]
        for i in df.index:
            if df.loc[i,'bidpricechange_lag']  > 0:
                deltaVolumeBid.append(df.loc[i,'bidsize'])
            elif df.loc[i,'bidpricechange_lag']  < 0:
                deltaVolumeBid.append(0)
            else:
                deltaVolumeBid.append(df.loc[i,'bidsizediff_lag'])
        
        df['deltaVolumeBid']=deltaVolumeBid
        
        deltaVolumeAsk=[]
        for j in df.index:
            if df.loc[j,'askpricechange_lag']  > 0:
                deltaVolumeAsk.append(0)
            elif df.loc[j,'askpricechange_lag']  < 0:
                deltaVolumeAsk.append(df.loc[j,'asksize'])
            else:
                deltaVolumeAsk.append(df.loc[j,'asksizediff_lag'])
        
        
        df['deltaVolumeAsk']=deltaVolumeAsk
        df['Return']=(df['close'].shift(-1)/df['open'].shift(-1))-1 #open # default trading open?
        df['VOI']=df['deltaVolumeBid']-df['deltaVolumeAsk']
        df['OIR']=(df['bidsize']-df['asksize'])/(df['bidsize']+df['asksize'])
        #df=df.fillna(0)     # As I checked the data and see that bidsize/asksize are 0 in some timestamps
        df['SP']=df['askclose']-df['bidclose']
        #sp_0index = df[df["SP"]==0].index   
        #df.loc[sp_0index, "SP"] = 1       # to ensure that adjusted VOI won't be nan 
        
        df['VOI_SP']=(df['VOI'])/df['SP']
        df['OIR_SP']=(df['OIR'])/df['SP']
        
        df['VOI_SP_lag1']=df['VOI_SP'].shift(1)
        df['VOI_SP_lag2']=df['VOI_SP'].shift(2)
        df['VOI_SP_lag3']=df['VOI_SP'].shift(3)
        df['VOI_SP_lag4']=df['VOI_SP'].shift(4)
        df['VOI_SP_lag5']=df['VOI_SP'].shift(5)
        
        df['OIR_SP_lag1']=df['OIR_SP'].shift(1)
        df['OIR_SP_lag2']=df['OIR_SP'].shift(2)
        df['OIR_SP_lag3']=df['OIR_SP'].shift(3)
        df['OIR_SP_lag4']=df['OIR_SP'].shift(4)
        df['OIR_SP_lag5']=df['OIR_SP'].shift(5)
        df=df.dropna(axis=0)
    
        return df
        
    def Regression(self):
        try:
            df = self.data_construction(self.lookback)
            X = df[["VOI_SP", "VOI_SP_lag1", "VOI_SP_lag2", "VOI_SP_lag3", "VOI_SP_lag4", "VOI_SP_lag5", "OIR_SP", 
                "OIR_SP_lag1", "OIR_SP_lag2", "OIR_SP_lag3", "OIR_SP_lag4", "OIR_SP_lag5"]]
            self.scaler = preprocessing.StandardScaler().fit(X)
            X_scaled = self.scaler.transform(X)
            
            Model = Ridge(alpha = self.alpha).fit(X_scaled, df["Return"])
            df['yhat']= Model.predict(X_scaled)
            
            self.long= df['yhat'].quantile(self.long_quantile)
            self.closelong = df['yhat'].quantile(self.long_quantile-self.close_quantile)
            self.short = df['yhat'].quantile(self.short_quantile)
            self.closeshort = df['yhat'].quantile(self.short_quantile+self.close_quantile)
            self.MLmodel = Model
            self.run=1 
            
            
            self.Debug(str(self.Time)+ " Month End Regression Building: "+ str(self.contract_symbol)+" Contract Name: " + str(self.contract_symbol.Value))
            #self.Debug("Exp Date: " + str(self.contract_symbol.Date))
            self.Debug("L " + str(self.long)+" ; short signal " + str(self.short))

            #self.Debug(self.MLmodel)
  
            
            
            self.er_rebuild_model = 0
        except:
            self.er_rebuild_model = 1
            self.Debug("Model need to be rebuilt in the upcoming day " + str(self.Time))
        
    def handle_error(self):
        if (self.er_rebuild_model == 1):
            self.Regression()

    def Trade(self):
        if self.run == 0:
            self.Regression()
        
        try:
            
            if (self.change_symbol == True):
                self.Liquidate()
                self.change_symbol = False
            
            df = self.data_construction(self.testing)
            X = df[["VOI_SP","VOI_SP_lag1", "VOI_SP_lag2", "VOI_SP_lag3", "VOI_SP_lag4", "VOI_SP_lag5", "OIR_SP", "OIR_SP_lag1", "OIR_SP_lag2", 
                    "OIR_SP_lag3", "OIR_SP_lag4", "OIR_SP_lag5"]]
            X_scaled = self.scaler.transform(X)
            df['yhat'] = self.MLmodel.predict(X_scaled)
            
            predictedReturn = df.iloc[-1]['yhat']
            bid_close = df.iloc[-1]["bidclose"]
            ask_close = df.iloc[-1]["askclose"]
            
            df['fast'] = df['close'].rolling(5).mean()
            df['slow'] = df['close'].rolling(20).mean()
            fast = df.iloc[-1]['fast']
            slow = df.iloc[-1]['slow']
            
            eachamount= 1000000/2
            
            if self.Portfolio.Cash>1000000:
                eachamount=self.Portfolio.Cash/2
        
            if self.Portfolio.TotalPortfolioValue <500000: #stop loss
                self.Liquidate()   
            
            #self.contractsToBuy = floor(self.Portfolio.Cash/ask_close/5)
            #self.contractsToBuy = floor(eachamount/ask_close/5)
            self.contractsToBuy = round(eachamount/ask_close/5)
            
            notionalvalue= self.contractsToBuy*ask_close*5
            
            if fast>slow:
                self.SMA_Signal = "L"
            elif fast<slow:
                self.SMA_Signal = "S"
            else:
                self.SMA_Signal = "N"
            
            if predictedReturn > self.long:
                self.ML_Signal = "L"
            elif predictedReturn < self.short:
                self.ML_Signal = "S"
            else:
                self.ML_Signal = "N"
            
            if predictedReturn > self.long and fast>slow:
                self.Combined_Signal = "L"
            elif predictedReturn < self.short and fast<slow:
                self.Combined_Signal = "S"
            else:
                self.Combined_Signal = "N"
            
           
            
    
            if self.Portfolio.Cash< notionalvalue or self.Portfolio.Cash<500000:
                self.contractsToBuy=0 # just check if we can buy with 50k / each bet
            
            if self.Portfolio[self.contract_symbol].IsLong:
                if predictedReturn < self.closelong or fast<slow:
                    self.Liquidate()
            if self.Portfolio[self.contract_symbol].IsShort:
                if predictedReturn > self.closeshort or fast>slow: 
                    self.Liquidate()
            if not self.Portfolio[self.contract_symbol].Invested:
                if predictedReturn > self.long and fast>slow:
                    marketTicket = self.MarketOrder(self.contract_symbol, self.contractsToBuy)
            
                if predictedReturn < self.short and fast<slow:
                    marketTicket = self.MarketOrder(self.contract_symbol, -self.contractsToBuy)
            
            
            
            #self.Debug("Exp Date: " + str(self.contract_symbol.Date))
            self.Debug(str(self.Time) +" Daily Trading:"+" SMA: " + self.SMA_Signal +" ML: "+ self.ML_Signal+ " Combined: " + self.Combined_Signal)
            self.Debug(str(self.contract_symbol) + " "+ str(self.contract_symbol.Value))
            
            '''
            self.Debug("Contracts bought: " + str(marketTicket.AbsoluteFillQuantity)+ "/"+str(self.contractsToBuy))
            #self.Debug("Fill Price: " + str(marketTicket.AverageFillPrice) + " ; Fill_Quantity:"+ str(marketTicket.AbsoluteFillQuantity)+ " ; Status:" +str(marketTicket.Status))
            self.Debug("Fill Price: " + str(marketTicket.AverageFillPrice))
            self.Debug("Currenct Bid Price: " + str(bid_close))
            self.Debug("Current Ask Price: " + str(ask_close))
            #self.Debug("Currenct Bid Price: " + str(self.Portfolio[self.contract_symbol].BidPrice))
            #self.Debug("Current Ask Price: " + str(self.Portfolio[self.contract_symbol].AskPrice))
            '''
            
            '''
            # Trading Logic
            if self.Portfolio[self.contract_symbol].IsLong:
                if predictedReturn < self.short:
                    self.Liquidate()
                
                    #self.Debug("Short")
                    
                    self.MarketOrder(self.contract_symbol, -1)
                elif predictedReturn < self.closelong:
                    self.Liquidate()
        
            if self.Portfolio[self.contract_symbol].IsShort:
                if predictedReturn > self.long:
                    self.Liquidate()
                
                    #self.Debug("Long")
                
                
                    self.MarketOrder(self.contract_symbol, 1)
                elif predictedReturn > self.closeshort:
                    self.Liquidate()
            
            #if not self.Portfolio.Invested:
            if not self.Portfolio[self.contract_symbol].Invested:
                if predictedReturn > self.long:
                    self.MarketOrder(self.contract_symbol, 1)
                
                    #self.Debug("Long")
                
                
                if predictedReturn < self.short:
                    self.MarketOrder(self.contract_symbol, -1)
                
                    #self.Debug("Short")
            '''
            
            """
            # Reverse Signal
            if self.Portfolio[self.contract_symbol].IsLong:
                if predictedReturn > self.long:
                    self.Liquidate()
                    #self.LimitOrder(self.contract_symbol, -self.Portfolio[self.contract_symbol].Quantity, ask_close)
                    
                    
                    #self.Debug("Short")
                    
                    quantity = self.CalculateOrderQuantity(self.contract_symbol, -1)
                    self.LimitOrder(self.contract_symbol, quantity, ask_close)
                    
                    #self.SetHoldings(self.contract_symbol, -1)
                elif predictedReturn > self.closeshort:
                    #self.Liquidate()
                    self.LimitOrder(self.contract_symbol, -self.Portfolio[self.contract_symbol].Quantity, ask_close)
                    
        
            if self.Portfolio[self.contract_symbol].IsShort:
                if predictedReturn < self.short:
                    self.Liquidate()
                    #self.LimitOrder(self.contract_symbol, -self.Portfolio[self.contract_symbol].Quantity, bid_close)
                    
                    #self.Debug("Long")
                
                    quantity = self.CalculateOrderQuantity(self.contract_symbol, 1)
                    self.LimitOrder(self.contract_symbol, quantity, bid_close)
                    
                    #self.SetHoldings(self.contract_symbol, 1)
                elif predictedReturn < self.closelong:
                    #self.Liquidate()
                    self.LimitOrder(self.contract_symbol, -self.Portfolio[self.contract_symbol].Quantity, bid_close)
            
            #if not self.Portfolio.Invested:
            if not self.Portfolio[self.contract_symbol].Invested:
                if predictedReturn > self.long:
                    #self.SetHoldings(self.contract_symbol, -1)
                    quantity = self.CalculateOrderQuantity(self.contract_symbol, -1)
                    self.LimitOrder(self.contract_symbol, quantity, ask_close)
                    
                    #self.Debug("Long")
                
                
                if predictedReturn < self.short:
                    #self.SetHoldings(self.contract_symbol, 1)
                    quantity = self.CalculateOrderQuantity(self.contract_symbol, 1)
                    self.LimitOrder(self.contract_symbol, quantity, bid_close)
                    
                    #self.Debug("Short")
            """
            
        except:
            self.Liquidate()
            self.Debug("Pass trading on this timestamp due to data error " + str(self.Time))
    
    
    
            
            
            
            
    def portfolio_check(self):
        self.Debug(str(self.Time) + "Daily Portfolio Check at 2359: ")
        self.Debug("Equity: " + str(self.Portfolio.TotalPortfolioValue))
        self.Debug("Cash: " + str(self.Portfolio.Cash))
        self.Debug("Total Profit: " + str(self.Portfolio.TotalProfit))
        self.Debug("Total Unrealized Profit: " + str(self.Portfolio.TotalUnrealizedProfit))
        """
        self.Debug(str(self.contract_symbol))
        self.Debug("Contract Name: " + str(self.contract_symbol.Value))
        #self.Debug("Exp Date: " + str(self.contract_symbol.Date))
        self.Debug("Price: " + str(self.Portfolio[self.contract_symbol].Price))
        self.Debug("Number of contract in the portfolio: " + str(self.Portfolio[self.contract_symbol].Quantity))
        """