| Overall Statistics |
|
Total Trades 343 Average Win 10.66% Average Loss -5.20% Compounding Annual Return 85.255% Drawdown 78.000% Expectancy 0.454 Net Profit 622.469% Sharpe Ratio 1.619 Probabilistic Sharpe Ratio 38.471% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 2.05 Alpha 1.97 Beta 0.615 Annual Standard Deviation 1.265 Annual Variance 1.601 Information Ratio 1.522 Tracking Error 1.262 Treynor Ratio 3.333 Total Fees $13456.90 Estimated Strategy Capacity $1900000.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
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 = 10*self.timestamp #testing period table
self.long_quantile = 0.5
self.short_quantile = 0.05
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.Schedule.On(self.DateRules.MonthEnd(),self.TimeRules.At(23, 59) ,self.Regression)
self.er_rebuild_model = 0
self.Schedule.On(self.DateRules.EveryDay(self.BTC.Symbol), self.TimeRules.Every(timedelta(minutes=self.timestamp)), self.Trade)
self.run = 0
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(23, 59), self.handle_error)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(23, 30), self.portfolio_check)
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.OpenInterest, reverse=True)[0]
self.contract_symbol = ideal_contract.Symbol
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("long signal " + str(self.long))
self.Debug("short signal " + str(self.short))
self.Debug(self.MLmodel)
self.Debug(self.Time)
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:
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"]
if self.Portfolio[self.contract_symbol].IsLong:
if predictedReturn < self.closelong or predictedReturn < 0:
self.Liquidate()
if not self.Portfolio[self.contract_symbol].Invested:
if predictedReturn > self.long and predictedReturn > 0:
self.SetHoldings(self.contract_symbol, 1)
'''
# Trading Logic
if self.Portfolio[self.contract_symbol].IsLong:
if predictedReturn < self.short:
self.Liquidate()
#self.Debug("Short")
self.SetHoldings(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.SetHoldings(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.SetHoldings(self.contract_symbol, 1)
#self.Debug("Long")
if predictedReturn < self.short:
self.SetHoldings(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) + " Cash " + str(self.Portfolio.Cash))
self.Debug(self.Portfolio.Keys)
self.Debug(self.Portfolio.Values)
self.Debug(self.contract_symbol)