| 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))
"""